Skip to content

Commit

Permalink
Merge branch 'feature-parks'
Browse files Browse the repository at this point in the history
  • Loading branch information
dymanoid committed Aug 13, 2018
2 parents d4f4471 + d32d412 commit 95d1553
Show file tree
Hide file tree
Showing 11 changed files with 137 additions and 2 deletions.
4 changes: 4 additions & 0 deletions src/RealTime/Core/RealTimeCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ public void Stop()
SimulationHandler.CitizenProcessor = null;
SimulationHandler.Statistics?.Close();
SimulationHandler.Statistics = null;
ParkPatches.SpareTimeBehavior = null;

WorldInfoPanelPatches.CitizenInfoPanel?.Disable();
WorldInfoPanelPatches.CitizenInfoPanel = null;
Expand Down Expand Up @@ -294,6 +295,7 @@ private static List<IPatch> GetMethodPatches()
UIGraphPatches.VisibleEndTime,
UIGraphPatches.BuildLabels,
WeatherManagerPatch.SimulationStepImpl,
ParkPatches.DistrictParkSimulation,
};

if (Compatibility.IsModActive(Compatibility.CitizenLifecycleRebalanceId))
Expand Down Expand Up @@ -342,6 +344,8 @@ private static bool SetupCustomAI(
var travelBehavior = new TravelBehavior(gameConnections.BuildingManager);
var workBehavior = new WorkBehavior(config, gameConnections.Random, gameConnections.BuildingManager, timeInfo, travelBehavior);

ParkPatches.SpareTimeBehavior = spareTimeBehavior;

var realTimePrivateBuildingAI = new RealTimeBuildingAI(
config,
timeInfo,
Expand Down
3 changes: 3 additions & 0 deletions src/RealTime/CustomAI/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ internal static class Constants
/// <summary>A chance in percent for a citizen to go shopping in the night.</summary>
public const uint NightShoppingChance = 20u;

/// <summary>A chance in percent for a citizen to go to a leisure building in the night instead of usual entertainment.</summary>
public const uint NightLeisureChance = 70u;

/// <summary>A chance in percent for a tourist to find a hotel for sleepover.</summary>
public const uint FindHotelChance = 60;

Expand Down
5 changes: 5 additions & 0 deletions src/RealTime/CustomAI/ISpareTimeBehavior.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ namespace RealTime.CustomAI
/// </summary>
internal interface ISpareTimeBehavior
{
/// <summary>
/// Gets a value indicating whether the fireworks in the parks are allowed at current time.
/// </summary>
bool AreFireworksAllowed { get; }

/// <summary>
/// Gets the probability whether a citizen with specified age would go relaxing on current time.
/// </summary>
Expand Down
9 changes: 9 additions & 0 deletions src/RealTime/CustomAI/RealTimeBuildingAI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,15 @@ private bool ShouldSwitchBuildingLightsOff(ushort buildingId, ItemClass.Service
case ItemClass.Service.Monument:
return false;

case ItemClass.Service.Beautification when subService == ItemClass.SubService.BeautificationParks:
byte parkId = buildingManager.GetParkId(buildingId);
if (parkId == 0 || (buildingManager.GetParkPolicies(parkId) & DistrictPolicies.Park.NightTours) == 0)
{
goto default;
}

return false;

default:
return !workBehavior.IsBuildingWorking(service, subService);
}
Expand Down
15 changes: 14 additions & 1 deletion src/RealTime/CustomAI/RealTimeResidentAI.Visit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ private bool ScheduleRelaxing(ref CitizenSchedule schedule, uint citizenId, ref
Citizen.AgeGroup citizenAge = CitizenProxy.GetAge(ref citizen);

uint relaxChance = spareTimeBehavior.GetRelaxingChance(citizenAge, schedule.WorkShift, schedule.WorkStatus == WorkStatus.OnVacation);
relaxChance = AdjustRelaxChance(relaxChance, ref citizen);

if (!Random.ShouldOccur(relaxChance) || WeatherInfo.IsBadWeather)
{
return false;
Expand All @@ -33,7 +35,7 @@ private bool ScheduleRelaxing(ref CitizenSchedule schedule, uint citizenId, ref
}

schedule.Schedule(ResidentState.Relaxing);
schedule.Hint = TimeInfo.IsNightTime
schedule.Hint = TimeInfo.IsNightTime && Random.ShouldOccur(NightLeisureChance)
? ScheduleHint.RelaxAtLeisureBuilding
: ScheduleHint.None;

Expand Down Expand Up @@ -93,6 +95,8 @@ private bool DoScheduledRelaxing(ref CitizenSchedule schedule, TAI instance, uin
schedule.WorkShift,
schedule.WorkStatus == WorkStatus.OnVacation);

relaxChance = AdjustRelaxChance(relaxChance, ref citizen);

ResidentState nextState = Random.ShouldOccur(relaxChance)
? ResidentState.Relaxing
: ResidentState.Unknown;
Expand Down Expand Up @@ -278,5 +282,14 @@ private bool RescheduleVisit(ref CitizenSchedule schedule, uint citizenId, ref T

return false;
}

private uint AdjustRelaxChance(uint relaxChance, ref TCitizen citizen)
{
ushort visitBuilding = CitizenProxy.GetCurrentBuilding(ref citizen);

return (BuildingMgr.GetBuildingSubService(visitBuilding) == ItemClass.SubService.BeautificationParks)
? relaxChance * 2
: relaxChance;
}
}
}
2 changes: 1 addition & 1 deletion src/RealTime/CustomAI/RealTimeTouristAI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ private uint GetTouristGoingOutChance(ref TCitizen citizen, TouristTarget target
case TouristTarget.Shopping:
return spareTimeBehavior.GetShoppingChance(age);

case TouristTarget.Relaxing when TimeInfo.IsNightTime || WeatherInfo.IsBadWeather:
case TouristTarget.Relaxing when WeatherInfo.IsBadWeather:
return 0u;

case TouristTarget.Party:
Expand Down
5 changes: 5 additions & 0 deletions src/RealTime/CustomAI/SpareTimeBehavior.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ public SpareTimeBehavior(RealTimeConfig config, ITimeInfo timeInfo)
vacationChances = new uint[Enum.GetValues(typeof(Citizen.Wealth)).Length];
}

/// <summary>
/// Gets a value indicating whether the fireworks in the parks are allowed at current time.
/// </summary>
public bool AreFireworksAllowed => timeInfo.CurrentHour >= timeInfo.SunsetHour && timeInfo.CurrentHour < config.GoToSleepHour;

/// <summary>Sets the duration (in hours) of a full simulation cycle for all citizens.
/// The game calls the simulation methods for a particular citizen with this period.</summary>
/// <param name="cyclePeriod">The citizens simulation cycle period, in game hours.</param>
Expand Down
26 changes: 26 additions & 0 deletions src/RealTime/GameConnection/BuildingManagerConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,32 @@ public void DeactivateVisually(ushort buildingId)
building.Info?.m_buildingAI.BuildingDeactivated(buildingId, ref building);
}

/// <summary>Gets the ID of the park area where the building with specified ID is located. Returns 0 if the building
/// is not in a park.</summary>
/// <param name="buildingId">The ID of the building to get the park ID of.</param>
/// <returns>An ID of the park where the building is located, or 0.</returns>
public byte GetParkId(ushort buildingId)
{
if (buildingId == 0)
{
return 0;
}

Vector3 position = BuildingManager.instance.m_buildings.m_buffer[buildingId].m_position;
return DistrictManager.instance.GetPark(position);
}

/// <summary>Gets the policies for a park with specified ID. Returns <see cref="DistrictPolicies.Park.None"/>
/// if the specified park ID is 0 or invalid.</summary>
/// <param name="parkId">The ID of the park to get policies of.</param>
/// <returns>The policies of the park.</returns>
public DistrictPolicies.Park GetParkPolicies(byte parkId)
{
return parkId == 0
? DistrictPolicies.Park.None
: DistrictManager.instance.m_parks.m_buffer[parkId].m_parkPolicies;
}

private static bool BuildingCanBeVisited(ushort buildingId)
{
uint currentUnitId = BuildingManager.instance.m_buildings.m_buffer[buildingId].m_citizenUnits;
Expand Down
12 changes: 12 additions & 0 deletions src/RealTime/GameConnection/IBuildingManagerConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,5 +153,17 @@ ushort FindActiveBuilding(
/// <summary>Visually deactivates the building with specified ID without affecting its production or coverage.</summary>
/// <param name="buildingId">The building ID.</param>
void DeactivateVisually(ushort buildingId);

/// <summary>Gets the ID of the park area where the building with specified ID is located. Returns 0 if the building
/// is not in a park.</summary>
/// <param name="buildingId">The ID of the building to get the park ID of.</param>
/// <returns>An ID of the park where the building is located, or 0.</returns>
byte GetParkId(ushort buildingId);

/// <summary>Gets the policies for a park with specified ID. Returns <see cref="DistrictPolicies.Park.None"/>
/// if the specified park ID is 0 or invalid.</summary>
/// <param name="parkId">The ID of the park to get policies of.</param>
/// <returns>The policies of the park.</returns>
DistrictPolicies.Park GetParkPolicies(byte parkId);
}
}
57 changes: 57 additions & 0 deletions src/RealTime/GameConnection/Patches/ParkPatches.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// <copyright file="ParkPatches.cs" company="dymanoid">
// Copyright (c) dymanoid. All rights reserved.
// </copyright>

namespace RealTime.GameConnection.Patches
{
using System.Reflection;
using RealTime.CustomAI;
using SkyTools.Patching;

/// <summary>
/// A static class that provides the patch objects for the Park Life DLC related methods.
/// </summary>
internal static class ParkPatches
{
/// <summary>Gets the patch for the district park simulation method.</summary>
public static IPatch DistrictParkSimulation { get; } = new DistrictPark_SimulationStep();

/// <summary>Gets or sets the city spare time behavior.</summary>
public static ISpareTimeBehavior SpareTimeBehavior { get; set; }

private sealed class DistrictPark_SimulationStep : PatchBase
{
protected override MethodInfo GetMethod()
{
return typeof(DistrictPark).GetMethod(
"SimulationStep",
BindingFlags.Instance | BindingFlags.Public,
null,
new[] { typeof(byte) },
new ParameterModifier[0]);
}

[System.Diagnostics.CodeAnalysis.SuppressMessage("Redundancy", "RCS1213", Justification = "Harmony patch")]
private static void Postfix(byte parkID)
{
if (SpareTimeBehavior == null)
{
return;
}

ref DistrictPark park = ref DistrictManager.instance.m_parks.m_buffer[parkID];

if (!SpareTimeBehavior.AreFireworksAllowed)
{
park.m_flags &= ~DistrictPark.Flags.SpecialMode;
return;
}

if (park.m_dayNightCount == 6 || (park.m_parkPolicies & DistrictPolicies.Park.FireworksBoost) != 0)
{
park.m_flags |= DistrictPark.Flags.SpecialMode;
}
}
}
}
}
1 change: 1 addition & 0 deletions src/RealTime/RealTime.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
<Compile Include="GameConnection\IToolManagerConnection.cs" />
<Compile Include="GameConnection\ITransferManagerConnection.cs" />
<Compile Include="GameConnection\IWeatherManagerConnection.cs" />
<Compile Include="GameConnection\Patches\ParkPatches.cs" />
<Compile Include="GameConnection\Patches\TransferManagerPatch.cs" />
<Compile Include="GameConnection\Patches\UIGraphPatches.cs" />
<Compile Include="GameConnection\Patches\WeatherManagerPatch.cs" />
Expand Down

0 comments on commit 95d1553

Please sign in to comment.