From 1a1dddef1613da4304e9eda98d43cebfe5424158 Mon Sep 17 00:00:00 2001 From: dymanoid <9433345+dymanoid@users.noreply.github.com> Date: Sun, 1 Jul 2018 03:04:58 +0200 Subject: [PATCH 1/3] Improve the UI slider attribute and default display format --- src/RealTime/UI/ConfigItemSliderAttribute.cs | 29 ++++++++++++++++---- src/RealTime/UI/UnitySliderItem.cs | 3 +- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/RealTime/UI/ConfigItemSliderAttribute.cs b/src/RealTime/UI/ConfigItemSliderAttribute.cs index c057364a..286d8e53 100644 --- a/src/RealTime/UI/ConfigItemSliderAttribute.cs +++ b/src/RealTime/UI/ConfigItemSliderAttribute.cs @@ -26,7 +26,7 @@ internal sealed class ConfigItemSliderAttribute : ConfigItemUIBaseAttribute /// /// Thrown when the value is less or equal to zero. /// - public ConfigItemSliderAttribute(float min, float max, float step = 1f, SliderValueType valueType = SliderValueType.Percentage) + public ConfigItemSliderAttribute(float min, float max, float step, SliderValueType valueType) { if (max <= min) { @@ -44,16 +44,35 @@ public ConfigItemSliderAttribute(float min, float max, float step = 1f, SliderVa ValueType = valueType; } + /// + /// Initializes a new instance of the class. + /// + /// The minimum slider value. + /// The maximum slider value. + /// The slider step value. Default is 1. + /// The type of the value to display. Default is . + /// + /// Thrown when the value is less or equal to the + /// value. + /// + /// + /// Thrown when the value is less or equal to zero. + /// + public ConfigItemSliderAttribute(float min, float max) + : this(min, max, 1.0f, SliderValueType.Percentage) + { + } + /// Gets the slider minimum value. public float Min { get; } /// Gets the slider maximum value. public float Max { get; } - /// Gets the slider step value. - public float Step { get; } + /// Gets or sets the slider step value. + public float Step { get; set; } - /// Gets the type of the value that will be displayed. - public SliderValueType ValueType { get; } + /// Gets or sets the type of the value that will be displayed. + public SliderValueType ValueType { get; set; } } } \ No newline at end of file diff --git a/src/RealTime/UI/UnitySliderItem.cs b/src/RealTime/UI/UnitySliderItem.cs index 8ba9369c..c817add7 100644 --- a/src/RealTime/UI/UnitySliderItem.cs +++ b/src/RealTime/UI/UnitySliderItem.cs @@ -145,7 +145,8 @@ private void UpdateValueLabel(float value) break; default: - return; + valueString = value.ToString(); + break; } valueLabel.text = valueString; From ad23c971f69e7ab0a97bbf7bb4c09ff4728efafd Mon Sep 17 00:00:00 2001 From: dymanoid <9433345+dymanoid@users.noreply.github.com> Date: Sun, 1 Jul 2018 03:06:33 +0200 Subject: [PATCH 2/3] Add configuration validation --- src/RealTime/Config/ConfigurationProvider.cs | 2 +- src/RealTime/Config/RealTimeConfig.cs | 41 ++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/RealTime/Config/ConfigurationProvider.cs b/src/RealTime/Config/ConfigurationProvider.cs index d57547c3..dc99b7e4 100644 --- a/src/RealTime/Config/ConfigurationProvider.cs +++ b/src/RealTime/Config/ConfigurationProvider.cs @@ -70,7 +70,7 @@ private static RealTimeConfig Deserialize() var serializer = new XmlSerializer(typeof(RealTimeConfig)); using (var sr = new StreamReader(SettingsFileName)) { - return (RealTimeConfig)serializer.Deserialize(sr); + return ((RealTimeConfig)serializer.Deserialize(sr)).Validate(); } } diff --git a/src/RealTime/Config/RealTimeConfig.cs b/src/RealTime/Config/RealTimeConfig.cs index 80ca57da..7406bf22 100644 --- a/src/RealTime/Config/RealTimeConfig.cs +++ b/src/RealTime/Config/RealTimeConfig.cs @@ -4,6 +4,7 @@ namespace RealTime.Config { + using System.Collections.Generic; using RealTime.UI; /// @@ -149,5 +150,45 @@ public sealed class RealTimeConfig [ConfigItem("4Time", 6)] [ConfigItemSlider(11, 16, 0.25f, SliderValueType.Time)] public float SchoolEnd { get; set; } = 14f; + + /// Validates this instance and corrects possible invalid property values. + /// This instance. + public RealTimeConfig Validate() + { + VirtualCitizens = (VirtualCitizensLevel)Clamp((int)VirtualCitizens, (int)VirtualCitizensLevel.None, (int)VirtualCitizensLevel.Many); + ConstructionSpeed = Clamp(ConstructionSpeed, 0u, 100u); + LunchQuota = Clamp(LunchQuota, 0u, 100u); + LocalBuildingSearchQuota = Clamp(LocalBuildingSearchQuota, 0u, 100u); + OnTimeQuota = Clamp(OnTimeQuota, 0u, 100u); + EarliestHourEventStartWeekday = Clamp(EarliestHourEventStartWeekday, 0f, 23.75f); + LatestHourEventStartWeekday = Clamp(LatestHourEventStartWeekday, 0f, 23.75f); + EarliestHourEventStartWeekend = Clamp(EarliestHourEventStartWeekend, 0f, 23.75f); + LatestHourEventStartWeekend = Clamp(LatestHourEventStartWeekend, 0f, 23.75f); + WorkBegin = Clamp(WorkBegin, 4f, 11f); + WorkEnd = Clamp(WorkEnd, 12f, 20f); + LunchBegin = Clamp(LunchBegin, 11f, 13f); + LunchEnd = Clamp(LunchEnd, 13f, 15f); + SchoolBegin = Clamp(SchoolBegin, 4f, 10f); + SchoolEnd = Clamp(SchoolEnd, 11f, 16f); + MaxOvertime = Clamp(MaxOvertime, 0f, 4f); + return this; + } + + private static T Clamp(T value, T min, T max) + where T : struct + { + Comparer comparer = Comparer.Default; + if (comparer.Compare(value, min) < 0) + { + return min; + } + + if (comparer.Compare(value, max) > 0) + { + return max; + } + + return value; + } } } From 17274bc15a93c9ec2d245cd20b45b8759e62c810 Mon Sep 17 00:00:00 2001 From: dymanoid <9433345+dymanoid@users.noreply.github.com> Date: Sun, 1 Jul 2018 03:08:13 +0200 Subject: [PATCH 3/3] Implement #20 by enabling of manual time speed changes (day and night) --- src/RealTime/Config/RealTimeConfig.cs | 26 +++++-- src/RealTime/Core/RealTimeCore.cs | 4 +- src/RealTime/Localization/Translations/de.xml | 4 ++ src/RealTime/Localization/Translations/en.xml | 4 ++ src/RealTime/Localization/Translations/es.xml | 4 ++ src/RealTime/Localization/Translations/fr.xml | 4 ++ src/RealTime/Localization/Translations/ko.xml | 4 ++ src/RealTime/Localization/Translations/pl.xml | 4 ++ src/RealTime/Localization/Translations/pt.xml | 4 ++ src/RealTime/Localization/Translations/ru.xml | 4 ++ src/RealTime/Localization/Translations/zh.xml | 4 ++ src/RealTime/Simulation/SimulationHandler.cs | 4 ++ src/RealTime/Simulation/TimeAdjustment.cs | 68 +++++++++++-------- 13 files changed, 104 insertions(+), 34 deletions(-) diff --git a/src/RealTime/Config/RealTimeConfig.cs b/src/RealTime/Config/RealTimeConfig.cs index 7406bf22..f84d3ece 100644 --- a/src/RealTime/Config/RealTimeConfig.cs +++ b/src/RealTime/Config/RealTimeConfig.cs @@ -13,37 +13,51 @@ namespace RealTime.Config public sealed class RealTimeConfig { /// - /// Gets or sets the virtual citizens mode. + /// Gets or sets the speed of the time flow on daytime. Valid values are 1..7. /// [ConfigItem("1General", 0)] + [ConfigItemSlider(1, 7, ValueType = SliderValueType.Default)] + public uint DayTimeSpeed { get; set; } = 5; + + /// + /// Gets or sets the speed of the time flow on night time. Valid values are 1..7. + /// + [ConfigItem("1General", 1)] + [ConfigItemSlider(1, 7, ValueType = SliderValueType.Default)] + public uint NightTimeSpeed { get; set; } = 5; + + /// + /// Gets or sets the virtual citizens mode. + /// + [ConfigItem("1General", 2)] [ConfigItemComboBox] public VirtualCitizensLevel VirtualCitizens { get; set; } = VirtualCitizensLevel.Few; /// /// Gets or sets a value indicating whether the weekends are enabled. Cims don't go to work on weekends. /// - [ConfigItem("1General", 1)] + [ConfigItem("1General", 3)] [ConfigItemCheckBox] public bool IsWeekendEnabled { get; set; } = true; /// /// Gets or sets a value indicating whether Cims should go out at lunch for food. /// - [ConfigItem("1General", 2)] + [ConfigItem("1General", 4)] [ConfigItemCheckBox] public bool IsLunchtimeEnabled { get; set; } = true; /// /// Gets or sets a value indicating whether the construction sites should pause at night time. /// - [ConfigItem("1General", 3)] + [ConfigItem("1General", 5)] [ConfigItemCheckBox] public bool StopConstructionAtNight { get; set; } = true; /// /// Gets or sets the percentage value of the building construction speed. Valid values are 1..100. /// - [ConfigItem("1General", 4)] + [ConfigItem("1General", 6)] [ConfigItemSlider(1, 100)] public uint ConstructionSpeed { get; set; } = 50; @@ -155,6 +169,8 @@ public sealed class RealTimeConfig /// This instance. public RealTimeConfig Validate() { + DayTimeSpeed = Clamp(DayTimeSpeed, 1u, 7u); + NightTimeSpeed = Clamp(NightTimeSpeed, 1u, 7u); VirtualCitizens = (VirtualCitizensLevel)Clamp((int)VirtualCitizens, (int)VirtualCitizensLevel.None, (int)VirtualCitizensLevel.Many); ConstructionSpeed = Clamp(ConstructionSpeed, 0u, 100u); LunchQuota = Clamp(LunchQuota, 0u, 100u); diff --git a/src/RealTime/Core/RealTimeCore.cs b/src/RealTime/Core/RealTimeCore.cs index c995714b..385a8da0 100644 --- a/src/RealTime/Core/RealTimeCore.cs +++ b/src/RealTime/Core/RealTimeCore.cs @@ -80,8 +80,9 @@ public static RealTimeCore Run(RealTimeConfig config, string rootPath, Localizat return null; } - var timeAdjustment = new TimeAdjustment(); + var timeAdjustment = new TimeAdjustment(config); DateTime gameDate = timeAdjustment.Enable(); + SimulationHandler.TimeAdjustment = timeAdjustment; var timeInfo = new TimeInfo(); var buildingManager = new BuildingManagerConnection(); @@ -155,6 +156,7 @@ public void Stop() SimulationHandler.EventManager = null; SimulationHandler.DayTimeSimulation = null; SimulationHandler.CommercialAI = null; + SimulationHandler.TimeAdjustment = null; try { diff --git a/src/RealTime/Localization/Translations/de.xml b/src/RealTime/Localization/Translations/de.xml index 3a8682cc..acf41ae4 100644 --- a/src/RealTime/Localization/Translations/de.xml +++ b/src/RealTime/Localization/Translations/de.xml @@ -2,6 +2,10 @@ + + + + diff --git a/src/RealTime/Localization/Translations/en.xml b/src/RealTime/Localization/Translations/en.xml index 4eced2e3..5e9891ee 100644 --- a/src/RealTime/Localization/Translations/en.xml +++ b/src/RealTime/Localization/Translations/en.xml @@ -2,6 +2,10 @@ + + + + diff --git a/src/RealTime/Localization/Translations/es.xml b/src/RealTime/Localization/Translations/es.xml index 59fe7ad7..6f2e0b6c 100644 --- a/src/RealTime/Localization/Translations/es.xml +++ b/src/RealTime/Localization/Translations/es.xml @@ -2,6 +2,10 @@ + + + + diff --git a/src/RealTime/Localization/Translations/fr.xml b/src/RealTime/Localization/Translations/fr.xml index a50951e4..fb60bb8d 100644 --- a/src/RealTime/Localization/Translations/fr.xml +++ b/src/RealTime/Localization/Translations/fr.xml @@ -2,6 +2,10 @@ + + + + diff --git a/src/RealTime/Localization/Translations/ko.xml b/src/RealTime/Localization/Translations/ko.xml index 89721a2a..86fd5a2a 100644 --- a/src/RealTime/Localization/Translations/ko.xml +++ b/src/RealTime/Localization/Translations/ko.xml @@ -2,6 +2,10 @@ + + + + diff --git a/src/RealTime/Localization/Translations/pl.xml b/src/RealTime/Localization/Translations/pl.xml index da87a949..776c4323 100644 --- a/src/RealTime/Localization/Translations/pl.xml +++ b/src/RealTime/Localization/Translations/pl.xml @@ -2,6 +2,10 @@ + + + + diff --git a/src/RealTime/Localization/Translations/pt.xml b/src/RealTime/Localization/Translations/pt.xml index 65467097..d59393b6 100644 --- a/src/RealTime/Localization/Translations/pt.xml +++ b/src/RealTime/Localization/Translations/pt.xml @@ -2,6 +2,10 @@ + + + + diff --git a/src/RealTime/Localization/Translations/ru.xml b/src/RealTime/Localization/Translations/ru.xml index d188c8a1..55338206 100644 --- a/src/RealTime/Localization/Translations/ru.xml +++ b/src/RealTime/Localization/Translations/ru.xml @@ -2,6 +2,10 @@ + + + + diff --git a/src/RealTime/Localization/Translations/zh.xml b/src/RealTime/Localization/Translations/zh.xml index 63dbff06..a12d2eba 100644 --- a/src/RealTime/Localization/Translations/zh.xml +++ b/src/RealTime/Localization/Translations/zh.xml @@ -2,6 +2,10 @@ + + + + diff --git a/src/RealTime/Simulation/SimulationHandler.cs b/src/RealTime/Simulation/SimulationHandler.cs index e13aa93f..1837076a 100644 --- a/src/RealTime/Simulation/SimulationHandler.cs +++ b/src/RealTime/Simulation/SimulationHandler.cs @@ -37,6 +37,9 @@ public sealed class SimulationHandler : ThreadingExtensionBase /// internal static RealTimeCommercialBuildingAI CommercialAI { get; set; } + /// Gets or sets the time adjustment simulation class instance. + internal static TimeAdjustment TimeAdjustment { get; set; } + /// /// Called after each game simulation tick. A tick contains multiple frames. /// Performs the dispatching for this simulation phase. @@ -44,6 +47,7 @@ public sealed class SimulationHandler : ThreadingExtensionBase public override void OnAfterSimulationTick() { EventManager?.ProcessEvents(); + TimeAdjustment?.Update(); DateTime currentDate = SimulationManager.instance.m_currentGameTime.Date; if (currentDate != lastHandledDate) diff --git a/src/RealTime/Simulation/TimeAdjustment.cs b/src/RealTime/Simulation/TimeAdjustment.cs index 8e1e09a9..7a05b1f3 100644 --- a/src/RealTime/Simulation/TimeAdjustment.cs +++ b/src/RealTime/Simulation/TimeAdjustment.cs @@ -1,56 +1,62 @@ -// -// Copyright (c) dymanoid. All rights reserved. -// +// Copyright (c) dymanoid. All rights reserved. namespace RealTime.Simulation { using System; - using RealTime.Tools; + using RealTime.Config; /// /// Manages the customized time adjustment. This class depends on the class. /// internal sealed class TimeAdjustment { - private const int CustomFramesPerDay = 1 << 17; - private static readonly TimeSpan CustomTimePerFrame = new TimeSpan(24L * 3600L * 10_000_000L / CustomFramesPerDay); - + private const int RealtimeSpeed = 23; private readonly uint vanillaFramesPerDay; - private readonly TimeSpan vanillaTimePerFrame; + private readonly RealTimeConfig config; + private uint lastDayTimeSpeed; + private uint lastNightTimeSpeed; + private bool isDayTime; - /// - /// Initializes a new instance of the class. - /// - public TimeAdjustment() + /// Initializes a new instance of the class. + /// The configuration to run with. + /// Thrown when the argument is null. + public TimeAdjustment(RealTimeConfig config) { + this.config = config ?? throw new ArgumentNullException(nameof(config)); + lastDayTimeSpeed = config.DayTimeSpeed; + lastNightTimeSpeed = config.NightTimeSpeed; vanillaFramesPerDay = SimulationManager.DAYTIME_FRAMES; - vanillaTimePerFrame = SimulationManager.instance.m_timePerFrame; } - /// - /// Enables the customized time adjustment. - /// - /// + /// Enables the customized time adjustment. /// The current game date and time. public DateTime Enable() { - if (vanillaTimePerFrame == CustomTimePerFrame) + isDayTime = !SimulationManager.instance.m_isNightTime; + return UpdateTimeSimulationValues(CalculateFramesPerDay()); + } + + /// Updates the time adjustment to be synchronized with the configuration and the daytime. + public void Update() + { + if (SimulationManager.instance.m_isNightTime == isDayTime + || lastDayTimeSpeed != config.DayTimeSpeed + || lastNightTimeSpeed != config.NightTimeSpeed) { - Log.Warning("The 'Real Time' mod has not been properly deactivated! Check the TimeAdjustment.Disable() calls."); + isDayTime = !SimulationManager.instance.m_isNightTime; + lastDayTimeSpeed = config.DayTimeSpeed; + lastNightTimeSpeed = config.NightTimeSpeed; + UpdateTimeSimulationValues(CalculateFramesPerDay()); } - - return UpdateTimeSimulationValues(CustomFramesPerDay, CustomTimePerFrame); } - /// - /// Disables the customized time adjustment restoring the default vanilla values. - /// + /// Disables the customized time adjustment restoring the default vanilla values. public void Disable() { - UpdateTimeSimulationValues(vanillaFramesPerDay, vanillaTimePerFrame); + UpdateTimeSimulationValues(vanillaFramesPerDay); } - private static DateTime UpdateTimeSimulationValues(uint framesPerDay, TimeSpan timePerFrame) + private static DateTime UpdateTimeSimulationValues(uint framesPerDay) { SimulationManager sm = SimulationManager.instance; DateTime originalDate = sm.m_ThreadingWrapper.simulationTime; @@ -59,7 +65,7 @@ private static DateTime UpdateTimeSimulationValues(uint framesPerDay, TimeSpan t SimulationManager.DAYTIME_FRAME_TO_HOUR = 24f / SimulationManager.DAYTIME_FRAMES; SimulationManager.DAYTIME_HOUR_TO_FRAME = SimulationManager.DAYTIME_FRAMES / 24f; - sm.m_timePerFrame = timePerFrame; + sm.m_timePerFrame = new TimeSpan(24L * 3600L * 10_000_000L / framesPerDay); sm.m_timeOffsetTicks = originalDate.Ticks - (sm.m_currentFrameIndex * sm.m_timePerFrame.Ticks); sm.m_currentGameTime = originalDate; @@ -69,5 +75,11 @@ private static DateTime UpdateTimeSimulationValues(uint framesPerDay, TimeSpan t return sm.m_currentGameTime; } + + private uint CalculateFramesPerDay() + { + uint offset = isDayTime ? lastDayTimeSpeed : lastNightTimeSpeed; + return 1u << (int)(RealtimeSpeed - offset); + } } -} +} \ No newline at end of file