Skip to content

Commit

Permalink
Merge branch 'feature-1.19'
Browse files Browse the repository at this point in the history
  • Loading branch information
dymanoid committed May 30, 2019
2 parents 1331eaf + f277f49 commit 89710d0
Show file tree
Hide file tree
Showing 41 changed files with 956 additions and 295 deletions.
13 changes: 12 additions & 1 deletion src/RealTime/Config/RealTimeConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public sealed class RealTimeConfig : IConfiguration
/// <summary>The storage ID for the configuration objects.</summary>
public const string StorageId = "RealTimeConfiguration";

private const int LatestVersion = 2;
private const int LatestVersion = 3;

/// <summary>Initializes a new instance of the <see cref="RealTimeConfig"/> class.</summary>
public RealTimeConfig()
Expand Down Expand Up @@ -105,6 +105,14 @@ public RealTimeConfig(bool latestVersion)
[ConfigItemCheckBox]
public bool SwitchOffLightsAtNight { get; set; }

/// <summary>
/// Gets or sets the maximum height of a residential, commercial, or office building that will switch the lights off
/// at night. All buildings higher than this value will not switch the lights off.
/// </summary>
[ConfigItem("1General", "1Other", 4)]
[ConfigItemSlider(0, 100f, 5f, ValueType = SliderValueType.Default)]
public float SwitchOffLightsMaxHeight { get; set; }

/// <summary>Gets or sets a value indicating whether a citizen can abandon a journey when being too long in
/// a traffic congestion or waiting too long for public transport.</summary>
[ConfigItem("1General", "1Other", 5)]
Expand Down Expand Up @@ -314,6 +322,8 @@ public void Validate()
VirtualCitizens = (VirtualCitizensLevel)FastMath.Clamp((int)VirtualCitizens, (int)VirtualCitizensLevel.None, (int)VirtualCitizensLevel.Vanilla);
ConstructionSpeed = FastMath.Clamp(ConstructionSpeed, 1u, 100u);

SwitchOffLightsMaxHeight = FastMath.Clamp(SwitchOffLightsMaxHeight, 0f, 100f);

SecondShiftQuota = FastMath.Clamp(SecondShiftQuota, 1u, 25u);
NightShiftQuota = FastMath.Clamp(NightShiftQuota, 1u, 25u);
LunchQuota = FastMath.Clamp(LunchQuota, 0u, 100u);
Expand Down Expand Up @@ -363,6 +373,7 @@ public void ResetToDefaults()
StopConstructionAtNight = true;
ConstructionSpeed = 50;
SwitchOffLightsAtNight = true;
SwitchOffLightsMaxHeight = 40f;
CanAbandonJourney = true;

SecondShiftQuota = 13;
Expand Down
3 changes: 3 additions & 0 deletions src/RealTime/Core/ModIds.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,8 @@ internal static class ModIds

/// <summary>The Workshop ID of the 'Force Level Up' mod.</summary>
public const ulong ForceLevelUp = 523818382ul;

/// <summary>The Workshop ID of the 'Realistic Walking Speed' mod.</summary>
public const ulong RealisticWalkingSpeed = 1412844620ul;
}
}
54 changes: 34 additions & 20 deletions src/RealTime/Core/RealTimeCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,10 @@ public static RealTimeCore Run(
new EventManagerConnection(),
buildingManager,
randomizer,
timeInfo);
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();
Expand Down Expand Up @@ -184,18 +185,23 @@ public static RealTimeCore Run(
SimulationHandler.Buildings = BuildingAIPatches.RealTimeAI;
SimulationHandler.Buildings.UpdateFrameDuration();

if (appliedPatches.Contains(CitizenManagerPatch.CreateCitizenPatch1))
{
CitizenManagerPatch.NewCitizenBehavior = new NewCitizenBehavior(randomizer, configProvider.Configuration);
}

if (appliedPatches.Contains(BuildingAIPatches.GetColor))
{
SimulationHandler.Buildings.InitializeLightState();
}

SimulationHandler.Statistics = statistics;

if (appliedPatches.Contains(WorldInfoPanelPatches.UpdateBindings))
if (appliedPatches.Contains(WorldInfoPanelPatch.UpdateBindings))
{
WorldInfoPanelPatches.CitizenInfoPanel = CustomCitizenInfoPanel.Enable(ResidentAIPatch.RealTimeAI, localizationProvider);
WorldInfoPanelPatches.VehicleInfoPanel = CustomVehicleInfoPanel.Enable(ResidentAIPatch.RealTimeAI, localizationProvider);
WorldInfoPanelPatches.CampusWorldInfoPanel = CustomCampusWorldInfoPanel.Enable(localizationProvider);
WorldInfoPanelPatch.CitizenInfoPanel = CustomCitizenInfoPanel.Enable(ResidentAIPatch.RealTimeAI, localizationProvider);
WorldInfoPanelPatch.VehicleInfoPanel = CustomVehicleInfoPanel.Enable(ResidentAIPatch.RealTimeAI, localizationProvider);
WorldInfoPanelPatch.CampusWorldInfoPanel = CustomCampusWorldInfoPanel.Enable(localizationProvider);
}

AwakeSleepSimulation.Install(configProvider.Configuration);
Expand Down Expand Up @@ -243,6 +249,7 @@ public void Stop()
SimulationHandler.Statistics = null;
ParkPatches.SpareTimeBehavior = null;
OutsideConnectionAIPatch.SpareTimeBehavior = null;
CitizenManagerPatch.NewCitizenBehavior = null;

Log.Info("The 'Real Time' mod reverts method patches.");
patcher.Revert();
Expand All @@ -261,14 +268,14 @@ public void Stop()

StorageBase.CurrentLevelStorage.GameSaving -= GameSaving;

WorldInfoPanelPatches.CitizenInfoPanel?.Disable();
WorldInfoPanelPatches.CitizenInfoPanel = null;
WorldInfoPanelPatch.CitizenInfoPanel?.Disable();
WorldInfoPanelPatch.CitizenInfoPanel = null;

WorldInfoPanelPatches.VehicleInfoPanel?.Disable();
WorldInfoPanelPatches.VehicleInfoPanel = null;
WorldInfoPanelPatch.VehicleInfoPanel?.Disable();
WorldInfoPanelPatch.VehicleInfoPanel = null;

WorldInfoPanelPatches.CampusWorldInfoPanel?.Disable();
WorldInfoPanelPatches.CampusWorldInfoPanel = null;
WorldInfoPanelPatch.CampusWorldInfoPanel?.Disable();
WorldInfoPanelPatch.CampusWorldInfoPanel = null;

isEnabled = false;
}
Expand All @@ -289,7 +296,7 @@ public void Translate(ILocalizationProvider localizationProvider)
}

timeBar.Translate(localizationProvider.CurrentCulture);
UIGraphPatches.Translate(localizationProvider.CurrentCulture);
UIGraphPatch.Translate(localizationProvider.CurrentCulture);
}

private static List<IPatch> GetMethodPatches(Compatibility compatibility)
Expand All @@ -309,10 +316,10 @@ private static List<IPatch> GetMethodPatches(Compatibility compatibility)
ResidentAIPatch.InstanceSimulationStep,
TouristAIPatch.Location,
TransferManagerPatch.AddOutgoingOffer,
WorldInfoPanelPatches.UpdateBindings,
UIGraphPatches.MinDataPoints,
UIGraphPatches.VisibleEndTime,
UIGraphPatches.BuildLabels,
WorldInfoPanelPatch.UpdateBindings,
UIGraphPatch.MinDataPoints,
UIGraphPatch.VisibleEndTime,
UIGraphPatch.BuildLabels,
WeatherManagerPatch.SimulationStepImpl,
ParkPatches.DistrictParkSimulation,
OutsideConnectionAIPatch.DummyTrafficProbability,
Expand All @@ -326,6 +333,8 @@ private static List<IPatch> GetMethodPatches(Compatibility compatibility)
{
patches.Add(ResidentAIPatch.UpdateAge);
patches.Add(ResidentAIPatch.CanMakeBabies);
patches.Add(CitizenManagerPatch.CreateCitizenPatch1);
patches.Add(CitizenManagerPatch.CreateCitizenPatch2);
}

if (compatibility.IsAnyModActive(
Expand Down Expand Up @@ -367,16 +376,21 @@ private static bool SetupCustomAI(
TimeInfo timeInfo,
RealTimeConfig config,
GameConnections<Citizen> gameConnections,
RealTimeEventManager eventManager)
RealTimeEventManager eventManager,
Compatibility compatibility)
{
ResidentAIConnection<ResidentAI, Citizen> residentAIConnection = ResidentAIPatch.GetResidentAIConnection();
if (residentAIConnection == null)
{
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;
Expand Down Expand Up @@ -437,7 +451,7 @@ private static void LoadStorageData(IEnumerable<IStorageData> storageData, Stora
}
}

private void CityEventsChanged(object sender, EventArgs e) => timeBar.UpdateEventsDisplay(eventManager.CityEvents);
private void CityEventsChanged(object sender, EventArgs e) => timeBar.UpdateEventsDisplay(eventManager.AllEvents);

private void GameSaving(object sender, EventArgs e)
{
Expand Down
2 changes: 1 addition & 1 deletion src/RealTime/Core/RealTimeMod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public void OnEnabled()
}

Log.Info("The 'Real Time' mod has been enabled, version: " + modVersion);
configProvider = new ConfigurationProvider<RealTimeConfig>(RealTimeConfig.StorageId, Name, () => new RealTimeConfig(true));
configProvider = new ConfigurationProvider<RealTimeConfig>(RealTimeConfig.StorageId, Name, () => new RealTimeConfig(latestVersion: true));
configProvider.LoadDefaultConfiguration();
localizationProvider = new LocalizationProvider(Name, modPath);
}
Expand Down
7 changes: 3 additions & 4 deletions src/RealTime/CustomAI/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ internal static class Constants
public const float PrepareToWorkHours = 1f;

/// <summary>An assumed maximum travel time to a target building (in hours).</summary>
public const float MaxTravelTime = 3.5f;
public const float MaxTravelTime = 4f;

/// <summary>An assumed minimum travel time to a target building (in hours).</summary>
public const float MinTravelTime = 0.5f;
Expand All @@ -74,10 +74,9 @@ internal static class Constants
/// <summary>The chance of a young female to get pregnant.</summary>
public const uint YoungFemalePregnancyChance = 50u;

/// <summary>The average distance a citizen can move for (walking, by car, by public transport) during a full simulation
/// cycle at maximum time speed (6).
/// <summary>The average distance a citizen can travel for (walking, by car, by public transport) during a single simulation cycle.
/// This value was determined empirically.</summary>
public const float AverageDistancePerSimulationCycle = 600f;
public const float AverageTravelDistancePerCycle = 450f;

/// <summary>The maximum number of buildings (of one zone type) that are in construction or upgrading process.</summary>
public const int MaximumBuildingsInConstruction = 50;
Expand Down
29 changes: 29 additions & 0 deletions src/RealTime/CustomAI/INewCitizenBehavior.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// <copyright file="INewCitizenBehavior.cs" company="dymanoid">
// Copyright (c) dymanoid. All rights reserved.
// </copyright>

namespace RealTime.CustomAI
{
/// <summary>
/// An interface for a behavior that determines the creation of new citizens.
/// </summary>
internal interface INewCitizenBehavior
{
/// <summary>
/// Gets the education level of the new citizen based on their <paramref name="age"/>.
/// </summary>
///
/// <param name="age">The citizen's age as raw value (0-255).</param>
/// <param name="currentEducation">The current value of the citizen's education.</param>
///
/// <returns>The education level of the new citizen with the specified age.</returns>
Citizen.Education GetEducation(int age, Citizen.Education currentEducation);

/// <summary>
/// Adjusts the age of the new citizen based on their current <paramref name="age"/>.
/// </summary>
/// <param name="age">The citizen's age as raw value (0-255).</param>
/// <returns>An adjusted raw value (0-255) for the citizen's age.</returns>
int AdjustCitizenAge(int age);
}
}
148 changes: 148 additions & 0 deletions src/RealTime/CustomAI/NewCitizenBehavior.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// <copyright file="NewCitizenBehavior.cs" company="dymanoid">
// Copyright (c) dymanoid. All rights reserved.
// </copyright>

namespace RealTime.CustomAI
{
using System;
using RealTime.Config;
using RealTime.Simulation;

/// <summary>
/// A behavior that determines the creation of new citizens.
/// </summary>
internal sealed class NewCitizenBehavior : INewCitizenBehavior
{
private readonly IRandomizer randomizer;
private readonly RealTimeConfig config;

/// <summary>
/// Initializes a new instance of the <see cref="NewCitizenBehavior"/> class.
/// </summary>
/// <param name="randomizer">The randomizer to use for random decisions.</param>
/// <param name="config">The configuration to run with.</param>
/// <exception cref="ArgumentNullException">Thrown when any argument is null.</exception>
public NewCitizenBehavior(IRandomizer randomizer, RealTimeConfig config)
{
this.randomizer = randomizer ?? throw new ArgumentNullException(nameof(randomizer));
this.config = config ?? throw new ArgumentNullException(nameof(config));
}

/// <summary>
/// Gets the education level of the new citizen based on their <paramref name="age" />.
/// </summary>
/// <param name="age">The citizen's age as raw value (0-255).</param>
/// <param name="currentEducation">The current value of the citizen's education.</param>
/// <returns>The education level of the new citizen with the specified age.</returns>
public Citizen.Education GetEducation(int age, Citizen.Education currentEducation)
{
if (!config.UseSlowAging)
{
return currentEducation;
}

var randomValue = randomizer.GetRandomValue(100u);

// Age:
// 0-14 -> child
// 15-44 -> teen
// 45-89 -> young
// 90-179 -> adult
// 180-255 -> senior
if (age < 10)
{
// little children
return Citizen.Education.Uneducated;
}
else if (age < 40)
{
// children and most of the teens
return randomValue <= 25 ? Citizen.Education.Uneducated : Citizen.Education.OneSchool;
}
else if (age < 80)
{
// few teens and most of the young adults
if (randomValue < 10)
{
return Citizen.Education.Uneducated;
}
else if (randomValue < 50)
{
return Citizen.Education.OneSchool;
}
else
{
return Citizen.Education.TwoSchools;
}
}
else if (age < 120)
{
// few young adults and some adults
if (randomValue < 5)
{
return Citizen.Education.Uneducated;
}
else if (randomValue < 15)
{
return Citizen.Education.OneSchool;
}
else if (randomValue < 50)
{
return Citizen.Education.TwoSchools;
}
else
{
return Citizen.Education.ThreeSchools;
}
}
else
{
// mature adults and all seniors
if (randomValue < 10)
{
return Citizen.Education.Uneducated;
}
else if (randomValue < 20)
{
return Citizen.Education.OneSchool;
}
else if (randomValue < 40)
{
return Citizen.Education.TwoSchools;
}
else
{
return Citizen.Education.ThreeSchools;
}
}
}

/// <summary>
/// Adjusts the age of the new citizen based on their current <paramref name="age"/>.
/// </summary>
/// <param name="age">The citizen's age as raw value (0-255).</param>
/// <returns>An adjusted raw value (0-255) for the citizen's age.</returns>
public int AdjustCitizenAge(int age)
{
// Age:
// 0-14 -> child
// 15-44 -> teen
// 45-89 -> young
// 90-179 -> adult
// 180-255 -> senior
if (!config.UseSlowAging || age <= 1)
{
return age;
}
else if (age <= 15)
{
// children may be teens too
return 4 + randomizer.GetRandomValue(40u);
}
else
{
return 75 + randomizer.GetRandomValue(115u);
}
}
}
}
Loading

0 comments on commit 89710d0

Please sign in to comment.