diff --git a/src/RealTime/Core/ModIds.cs b/src/RealTime/Core/ModIds.cs index fee4dd15..c0fd2497 100644 --- a/src/RealTime/Core/ModIds.cs +++ b/src/RealTime/Core/ModIds.cs @@ -26,5 +26,8 @@ internal static class ModIds /// The Workshop ID of the 'Force Level Up' mod. public const ulong ForceLevelUp = 523818382ul; + + /// The Workshop ID of the 'Realistic Walking Speed' mod. + public const ulong RealisticWalkingSpeed = 1412844620ul; } } diff --git a/src/RealTime/Core/RealTimeCore.cs b/src/RealTime/Core/RealTimeCore.cs index b4a20734..2b61eb76 100644 --- a/src/RealTime/Core/RealTimeCore.cs +++ b/src/RealTime/Core/RealTimeCore.cs @@ -144,7 +144,7 @@ public static RealTimeCore Run( timeInfo, Constants.MaxTravelTime); - if (!SetupCustomAI(timeInfo, configProvider.Configuration, gameConnections, eventManager)) + if (!SetupCustomAI(timeInfo, configProvider.Configuration, gameConnections, eventManager, compatibility)) { Log.Error("The 'Real Time' mod failed to setup the customized AI and will now be deactivated."); patcher.Revert(); @@ -376,7 +376,8 @@ private static bool SetupCustomAI( TimeInfo timeInfo, RealTimeConfig config, GameConnections gameConnections, - RealTimeEventManager eventManager) + RealTimeEventManager eventManager, + Compatibility compatibility) { ResidentAIConnection residentAIConnection = ResidentAIPatch.GetResidentAIConnection(); if (residentAIConnection == null) @@ -384,8 +385,12 @@ private static bool SetupCustomAI( return false; } + float travelDistancePerCycle = compatibility.IsAnyModActive(ModIds.RealisticWalkingSpeed) + ? Constants.AverageTravelDistancePerCycle * 0.583f + : Constants.AverageTravelDistancePerCycle; + var spareTimeBehavior = new SpareTimeBehavior(config, timeInfo); - var travelBehavior = new TravelBehavior(gameConnections.BuildingManager); + var travelBehavior = new TravelBehavior(gameConnections.BuildingManager, travelDistancePerCycle); var workBehavior = new WorkBehavior(config, gameConnections.Random, gameConnections.BuildingManager, timeInfo, travelBehavior); ParkPatches.SpareTimeBehavior = spareTimeBehavior; diff --git a/src/RealTime/CustomAI/Constants.cs b/src/RealTime/CustomAI/Constants.cs index 0ed67ad2..b8cf04c5 100644 --- a/src/RealTime/CustomAI/Constants.cs +++ b/src/RealTime/CustomAI/Constants.cs @@ -48,7 +48,7 @@ internal static class Constants public const float PrepareToWorkHours = 1f; /// An assumed maximum travel time to a target building (in hours). - public const float MaxTravelTime = 3.5f; + public const float MaxTravelTime = 4f; /// An assumed minimum travel time to a target building (in hours). public const float MinTravelTime = 0.5f; @@ -74,10 +74,9 @@ internal static class Constants /// The chance of a young female to get pregnant. public const uint YoungFemalePregnancyChance = 50u; - /// The average distance a citizen can move for (walking, by car, by public transport) during a full simulation - /// cycle at maximum time speed (6). + /// The average distance a citizen can travel for (walking, by car, by public transport) during a single simulation cycle. /// This value was determined empirically. - public const float AverageDistancePerSimulationCycle = 600f; + public const float AverageTravelDistancePerCycle = 450f; /// The maximum number of buildings (of one zone type) that are in construction or upgrading process. public const int MaximumBuildingsInConstruction = 50; diff --git a/src/RealTime/CustomAI/TravelBehavior.cs b/src/RealTime/CustomAI/TravelBehavior.cs index 1e9a6fff..6cf35613 100644 --- a/src/RealTime/CustomAI/TravelBehavior.cs +++ b/src/RealTime/CustomAI/TravelBehavior.cs @@ -4,6 +4,7 @@ namespace RealTime.CustomAI { + using System; using RealTime.GameConnection; using SkyTools.Tools; using static Constants; @@ -14,17 +15,26 @@ namespace RealTime.CustomAI internal sealed class TravelBehavior : ITravelBehavior { private readonly IBuildingManagerConnection buildingManager; - private float averageCitizenSpeed; + private readonly float travelDistancePerCycle; + private float averageTravelSpeedPerHour; /// Initializes a new instance of the class. /// /// A proxy object that provides a way to call the game-specific methods of the class. /// - /// Thrown when the argument is null. - public TravelBehavior(IBuildingManagerConnection buildingManager) + /// The average distance a citizen can travel during a single simulation cycle. + /// Thrown when is null. + /// Thrown when is negative or zero. + public TravelBehavior(IBuildingManagerConnection buildingManager, float travelDistancePerCycle) { - this.buildingManager = buildingManager ?? throw new System.ArgumentNullException(nameof(buildingManager)); - averageCitizenSpeed = AverageDistancePerSimulationCycle; + if (travelDistancePerCycle <= 0) + { + throw new ArgumentException("The travel distance per cycle cannot be negative or zero."); + } + + this.buildingManager = buildingManager ?? throw new ArgumentNullException(nameof(buildingManager)); + this.travelDistancePerCycle = travelDistancePerCycle; + averageTravelSpeedPerHour = travelDistancePerCycle; } /// Sets the duration (in hours) of a full simulation cycle for all citizens. @@ -37,7 +47,7 @@ public void SetSimulationCyclePeriod(float cyclePeriod) cyclePeriod = 1f; } - averageCitizenSpeed = AverageDistancePerSimulationCycle / cyclePeriod; + averageTravelSpeedPerHour = travelDistancePerCycle / cyclePeriod; } /// Gets an estimated travel time (in hours) between two specified buildings. @@ -57,7 +67,7 @@ public float GetEstimatedTravelTime(ushort building1, ushort building2) return MinTravelTime; } - return FastMath.Clamp(distance / averageCitizenSpeed, MinTravelTime, MaxTravelTime); + return FastMath.Clamp(distance / averageTravelSpeedPerHour, MinTravelTime, MaxTravelTime); } } }