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);
}
}
}