diff --git a/src/RealTime/Config/RealTimeConfig.cs b/src/RealTime/Config/RealTimeConfig.cs index eee14169..533e4ddb 100644 --- a/src/RealTime/Config/RealTimeConfig.cs +++ b/src/RealTime/Config/RealTimeConfig.cs @@ -283,6 +283,13 @@ public RealTimeConfig(bool latestVersion) [ConfigItemCheckBox] public bool ShowIncompatibilityNotifications { get; set; } + /// + /// Gets or sets a value indicating whether the mod should use the English-US time and date formats, if the English language is selected. + /// + [ConfigItem("Tools", 1)] + [ConfigItemCheckBox] + public bool UseEnglishUSFormats { get; set; } + /// Checks the version of the deserialized object and migrates it to the latest version when necessary. public void MigrateWhenNecessary() { diff --git a/src/RealTime/Core/RealTimeCore.cs b/src/RealTime/Core/RealTimeCore.cs index 8b173400..5785c7e0 100644 --- a/src/RealTime/Core/RealTimeCore.cs +++ b/src/RealTime/Core/RealTimeCore.cs @@ -26,6 +26,7 @@ namespace RealTime.Core /// The core component of the Real Time mod. Activates and deactivates /// the different parts of the mod's logic. /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "This is the entry point and needs to instantiate all parts")] internal sealed class RealTimeCore { private const string HarmonyId = "com.cities_skylines.dymanoid.realtime"; @@ -117,6 +118,8 @@ public static RealTimeCore Run( LoadStorageData(new[] { configProvider }, StorageBase.CurrentLevelStorage); } + localizationProvider.SetEnglishUSFormatsState(configProvider.Configuration.UseEnglishUSFormats); + var timeInfo = new TimeInfo(configProvider.Configuration); var buildingManager = new BuildingManagerConnection(); var randomizer = new GameRandomizer(); @@ -224,6 +227,22 @@ public void Stop() return; } + ResidentAIPatch.RealTimeAI = null; + TouristAIPatch.RealTimeAI = null; + BuildingAIPatches.RealTimeAI = null; + BuildingAIPatches.WeatherInfo = null; + TransferManagerPatch.RealTimeAI = null; + SimulationHandler.EventManager = null; + SimulationHandler.DayTimeSimulation = null; + SimulationHandler.TimeAdjustment = null; + SimulationHandler.WeatherInfo = null; + SimulationHandler.Buildings = null; + SimulationHandler.CitizenProcessor = null; + SimulationHandler.Statistics?.Close(); + SimulationHandler.Statistics = null; + ParkPatches.SpareTimeBehavior = null; + OutsideConnectionAIPatch.SpareTimeBehavior = null; + Log.Info($"The 'Real Time' mod reverts method patches."); patcher.Revert(); @@ -241,22 +260,6 @@ public void Stop() StorageBase.CurrentLevelStorage.GameSaving -= GameSaving; - ResidentAIPatch.RealTimeAI = null; - TouristAIPatch.RealTimeAI = null; - BuildingAIPatches.RealTimeAI = null; - BuildingAIPatches.WeatherInfo = null; - TransferManagerPatch.RealTimeAI = null; - SimulationHandler.EventManager = null; - SimulationHandler.DayTimeSimulation = null; - SimulationHandler.TimeAdjustment = null; - SimulationHandler.WeatherInfo = null; - SimulationHandler.Buildings = null; - SimulationHandler.CitizenProcessor = null; - SimulationHandler.Statistics?.Close(); - SimulationHandler.Statistics = null; - ParkPatches.SpareTimeBehavior = null; - OutsideConnectionAIPatch.SpareTimeBehavior = null; - WorldInfoPanelPatches.CitizenInfoPanel?.Disable(); WorldInfoPanelPatches.CitizenInfoPanel = null; diff --git a/src/RealTime/Core/RealTimeMod.cs b/src/RealTime/Core/RealTimeMod.cs index 0dcaafb8..22775a3e 100644 --- a/src/RealTime/Core/RealTimeMod.cs +++ b/src/RealTime/Core/RealTimeMod.cs @@ -216,6 +216,7 @@ private void ApplyLanguage() if (localizationProvider.LoadTranslation(LocaleManager.instance.language)) { + localizationProvider.SetEnglishUSFormatsState(configProvider.Configuration.UseEnglishUSFormats); core?.Translate(localizationProvider); } diff --git a/src/RealTime/CustomAI/RealTimeResidentAI.Common.cs b/src/RealTime/CustomAI/RealTimeResidentAI.Common.cs index e17e285d..dffd719a 100644 --- a/src/RealTime/CustomAI/RealTimeResidentAI.Common.cs +++ b/src/RealTime/CustomAI/RealTimeResidentAI.Common.cs @@ -101,10 +101,12 @@ private bool ProcessCitizenSick(TAI instance, uint citizenId, ref TCitizen citiz if (currentLocation == Citizen.Location.Visit) { - ItemClass.Service service = BuildingMgr.GetBuildingService(CitizenProxy.GetVisitBuilding(ref citizen)); - if (service == ItemClass.Service.HealthCare || service == ItemClass.Service.Disaster) + ushort visitBuilding = CitizenProxy.GetVisitBuilding(ref citizen); + switch (BuildingMgr.GetBuildingService(visitBuilding)) { - return true; + case ItemClass.Service.HealthCare: + case ItemClass.Service.Disaster when !BuildingMgr.BuildingHasFlags(visitBuilding, Building.Flags.Downgrading): + return true; } } @@ -115,17 +117,16 @@ private bool ProcessCitizenSick(TAI instance, uint citizenId, ref TCitizen citiz private void DoScheduledEvacuation(ref CitizenSchedule schedule, TAI instance, uint citizenId, ref TCitizen citizen) { + schedule.Schedule(ResidentState.Unknown); ushort building = CitizenProxy.GetCurrentBuilding(ref citizen); if (building == 0) { - schedule.Schedule(ResidentState.AtHome); - return; + Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} is trying to find a shelter from current position"); + TransferMgr.AddOutgoingOfferFromCurrentPosition(citizenId, residentAI.GetEvacuationReason(instance, 0)); } - - schedule.Schedule(ResidentState.InShelter); - if (schedule.CurrentState != ResidentState.InShelter) + else { - Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} is trying to find an evacuation place"); + Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} is trying to find a shelter from {building}"); residentAI.FindEvacuationPlace(instance, citizenId, building, residentAI.GetEvacuationReason(instance, building)); } } @@ -133,13 +134,17 @@ private void DoScheduledEvacuation(ref CitizenSchedule schedule, TAI instance, u private bool ProcessCitizenInShelter(ref CitizenSchedule schedule, ref TCitizen citizen) { ushort shelter = CitizenProxy.GetVisitBuilding(ref citizen); - if (BuildingMgr.BuildingHasFlags(shelter, Building.Flags.Downgrading) && schedule.ScheduledState == ResidentState.InShelter) + if (BuildingMgr.BuildingHasFlags(shelter, Building.Flags.Downgrading)) { CitizenProxy.RemoveFlags(ref citizen, Citizen.Flags.Evacuating); - schedule.Schedule(ResidentState.Unknown); return true; } + if (schedule.ScheduledState != ResidentState.Unknown) + { + schedule.Schedule(ResidentState.Unknown); + } + return false; } @@ -165,11 +170,10 @@ private ScheduleAction UpdateCitizenState(ref TCitizen citizen, ref CitizenSched true)) { // Guided tours are treated as visits - schedule.CurrentState = ResidentState.Visiting; schedule.Hint = ScheduleHint.OnTour; - return ScheduleAction.ProcessState; } + schedule.CurrentState = ResidentState.InTransition; return ScheduleAction.ProcessTransition; } @@ -180,15 +184,13 @@ private ScheduleAction UpdateCitizenState(ref TCitizen citizen, ref CitizenSched return ScheduleAction.ProcessState; } - ItemClass.Service buildingService = BuildingMgr.GetBuildingService(currentBuilding); - if (BuildingMgr.BuildingHasFlags(currentBuilding, Building.Flags.Evacuating) - && buildingService != ItemClass.Service.Disaster) + if (BuildingMgr.BuildingHasFlags(currentBuilding, Building.Flags.Evacuating)) { schedule.CurrentState = ResidentState.Evacuation; - schedule.Schedule(ResidentState.InShelter); return ScheduleAction.ProcessState; } + ItemClass.Service buildingService = BuildingMgr.GetBuildingService(currentBuilding); switch (location) { case Citizen.Location.Home: @@ -196,12 +198,6 @@ private ScheduleAction UpdateCitizenState(ref TCitizen citizen, ref CitizenSched return ScheduleAction.ProcessState; case Citizen.Location.Work: - if (buildingService == ItemClass.Service.Disaster && CitizenProxy.HasFlags(ref citizen, Citizen.Flags.Evacuating)) - { - schedule.CurrentState = ResidentState.InShelter; - return ScheduleAction.ProcessState; - } - if (CitizenProxy.GetVisitBuilding(ref citizen) == currentBuilding && schedule.WorkStatus != WorkStatus.Working) { // A citizen may visit their own work building (e.g. shopping), @@ -209,6 +205,23 @@ private ScheduleAction UpdateCitizenState(ref TCitizen citizen, ref CitizenSched goto case Citizen.Location.Visit; } + switch (buildingService) + { + case ItemClass.Service.Electricity: + case ItemClass.Service.Water: + case ItemClass.Service.HealthCare: + case ItemClass.Service.PoliceDepartment: + case ItemClass.Service.FireDepartment: + case ItemClass.Service.Disaster: + if (BuildingMgr.IsAreaEvacuating(currentBuilding)) + { + schedule.CurrentState = ResidentState.InShelter; + return ScheduleAction.ProcessState; + } + + break; + } + schedule.CurrentState = ResidentState.AtSchoolOrWork; return ScheduleAction.ProcessState; @@ -229,7 +242,7 @@ when BuildingMgr.GetBuildingSubService(currentBuilding) == ItemClass.SubService. schedule.CurrentState = ResidentState.Shopping; return ScheduleAction.ProcessState; - case ItemClass.Service.Disaster when CitizenProxy.HasFlags(ref citizen, Citizen.Flags.Evacuating): + case ItemClass.Service.Disaster: schedule.CurrentState = ResidentState.InShelter; return ScheduleAction.ProcessState; } @@ -356,7 +369,9 @@ private void ExecuteCitizenSchedule(ref CitizenSchedule schedule, TAI instance, return; } - if (schedule.CurrentState == ResidentState.AtHome && IsCitizenVirtual(instance, ref citizen, ShouldRealizeCitizen)) + if (schedule.CurrentState == ResidentState.AtHome + && schedule.ScheduledState != ResidentState.InShelter + && IsCitizenVirtual(instance, ref citizen, ShouldRealizeCitizen)) { Log.Debug(LogCategory.Simulation, $" *** Citizen {citizenId} is virtual this time"); schedule.Schedule(ResidentState.Unknown); @@ -368,18 +383,15 @@ private void ExecuteCitizenSchedule(ref CitizenSchedule schedule, TAI instance, { case ResidentState.AtHome: DoScheduledHome(ref schedule, instance, citizenId, ref citizen); - executed = true; - break; + return; case ResidentState.AtSchoolOrWork: DoScheduledWork(ref schedule, instance, citizenId, ref citizen); - executed = true; - break; + return; case ResidentState.Shopping when schedule.WorkStatus == WorkStatus.Working: DoScheduledLunch(ref schedule, instance, citizenId, ref citizen); - executed = true; - break; + return; case ResidentState.Shopping: executed = DoScheduledShopping(ref schedule, instance, citizenId, ref citizen); @@ -389,10 +401,9 @@ private void ExecuteCitizenSchedule(ref CitizenSchedule schedule, TAI instance, executed = DoScheduledRelaxing(ref schedule, instance, citizenId, ref citizen); break; - case ResidentState.InShelter: + case ResidentState.InShelter when schedule.CurrentState != ResidentState.InShelter: DoScheduledEvacuation(ref schedule, instance, citizenId, ref citizen); - executed = true; - break; + return; default: return; diff --git a/src/RealTime/CustomAI/RealTimeResidentAI.Moving.cs b/src/RealTime/CustomAI/RealTimeResidentAI.Moving.cs index 7215ac9b..5e13653d 100644 --- a/src/RealTime/CustomAI/RealTimeResidentAI.Moving.cs +++ b/src/RealTime/CustomAI/RealTimeResidentAI.Moving.cs @@ -9,7 +9,7 @@ namespace RealTime.CustomAI internal sealed partial class RealTimeResidentAI { - private bool ProcessCitizenMoving(ref CitizenSchedule schedule, TAI instance, uint citizenId, ref TCitizen citizen) + private bool ProcessCitizenMoving(ref CitizenSchedule schedule, uint citizenId, ref TCitizen citizen) { ushort instanceId = CitizenProxy.GetInstance(ref citizen); ushort vehicleId = CitizenProxy.GetVehicle(ref citizen); @@ -39,15 +39,27 @@ private bool ProcessCitizenMoving(ref CitizenSchedule schedule, TAI instance, ui return true; } - if (vehicleId == 0 && !CitizenProxy.HasFlags(ref citizen, Citizen.Flags.Evacuating) && CitizenMgr.IsAreaEvacuating(instanceId)) + bool isEvacuating = CitizenProxy.HasFlags(ref citizen, Citizen.Flags.Evacuating); + if (vehicleId == 0 && !isEvacuating && CitizenMgr.IsAreaEvacuating(instanceId)) { Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} was on the way, but the area evacuates. Finding an evacuation place."); - schedule.Schedule(ResidentState.Unknown); - schedule.DepartureTime = default; - TransferMgr.AddOutgoingOfferFromCurrentPosition(citizenId, residentAI.GetEvacuationReason(instance, 0)); + schedule.CurrentState = ResidentState.Evacuation; + return false; + } + + if (isEvacuating) + { return true; } + if (schedule.Hint == ScheduleHint.OnTour) + { + Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} quits a tour"); + schedule.Schedule(ResidentState.Unknown); + schedule.Hint = ScheduleHint.None; + return false; + } + ushort targetBuilding = CitizenMgr.GetTargetBuilding(instanceId); bool headingToWork = targetBuilding == CitizenProxy.GetWorkBuilding(ref citizen); if (vehicleId != 0 && schedule.DepartureTime != default) @@ -67,7 +79,6 @@ private bool ProcessCitizenMoving(ref CitizenSchedule schedule, TAI instance, ui Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} cancels the trip because of traffic jam"); schedule.Schedule(ResidentState.Relaxing); schedule.Hint = ScheduleHint.RelaxNearbyOnly; - schedule.CurrentState = ResidentState.InTransition; return false; } } @@ -82,7 +93,6 @@ private bool ProcessCitizenMoving(ref CitizenSchedule schedule, TAI instance, ui { Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} cancels the trip to a park due to bad weather"); schedule.Schedule(ResidentState.AtHome); - schedule.CurrentState = ResidentState.InTransition; return false; } diff --git a/src/RealTime/CustomAI/RealTimeResidentAI.Visit.cs b/src/RealTime/CustomAI/RealTimeResidentAI.Visit.cs index 9a30a97f..0743751d 100644 --- a/src/RealTime/CustomAI/RealTimeResidentAI.Visit.cs +++ b/src/RealTime/CustomAI/RealTimeResidentAI.Visit.cs @@ -246,13 +246,6 @@ private bool ProcessCitizenShopping(ref CitizenSchedule schedule, uint citizenId private bool ProcessCitizenVisit(ref CitizenSchedule schedule, uint citizenId, ref TCitizen citizen) { - if (schedule.Hint == ScheduleHint.OnTour) - { - Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} quits a tour"); - schedule.Schedule(ResidentState.Unknown); - return true; - } - return RescheduleVisit(ref schedule, citizenId, ref citizen, CitizenProxy.GetVisitBuilding(ref citizen)); } diff --git a/src/RealTime/CustomAI/RealTimeResidentAI.cs b/src/RealTime/CustomAI/RealTimeResidentAI.cs index 8a991456..199932d7 100644 --- a/src/RealTime/CustomAI/RealTimeResidentAI.cs +++ b/src/RealTime/CustomAI/RealTimeResidentAI.cs @@ -97,15 +97,20 @@ public void UpdateLocation(TAI instance, uint citizenId, ref TCitizen citizen) case ScheduleAction.Ignore: return; - case ScheduleAction.ProcessTransition when ProcessCitizenMoving(ref schedule, instance, citizenId, ref citizen): + case ScheduleAction.ProcessTransition when ProcessCitizenMoving(ref schedule, citizenId, ref citizen): return; } - if (schedule.CurrentState == ResidentState.Unknown) + switch (schedule.CurrentState) { - Log.Debug(LogCategory.State, TimeInfo.Now, $"WARNING: {GetCitizenDesc(citizenId, ref citizen)} is in an UNKNOWN state! Changing to 'moving'"); - CitizenProxy.SetLocation(ref citizen, Citizen.Location.Moving); - return; + case ResidentState.Unknown: + Log.Debug(LogCategory.State, TimeInfo.Now, $"WARNING: {GetCitizenDesc(citizenId, ref citizen)} is in an UNKNOWN state! Changing to 'moving'"); + CitizenProxy.SetLocation(ref citizen, Citizen.Location.Moving); + return; + + case ResidentState.Evacuation: + schedule.Schedule(ResidentState.InShelter); + break; } if (TimeInfo.Now < schedule.ScheduledStateTime) @@ -113,7 +118,8 @@ public void UpdateLocation(TAI instance, uint citizenId, ref TCitizen citizen) return; } - bool updated = UpdateCitizenSchedule(ref schedule, citizenId, ref citizen); + Log.Debug(LogCategory.State, TimeInfo.Now, $"Citizen {citizenId} is in state {schedule.CurrentState}"); + bool updated = schedule.CurrentState != ResidentState.InShelter && UpdateCitizenSchedule(ref schedule, citizenId, ref citizen); ExecuteCitizenSchedule(ref schedule, instance, citizenId, ref citizen, updated); } diff --git a/src/RealTime/CustomAI/RealTimeTouristAI.cs b/src/RealTime/CustomAI/RealTimeTouristAI.cs index 0114133b..4c3f177d 100644 --- a/src/RealTime/CustomAI/RealTimeTouristAI.cs +++ b/src/RealTime/CustomAI/RealTimeTouristAI.cs @@ -109,10 +109,16 @@ private void ProcessMoving(TAI instance, uint citizenId, ref TCitizen citizen) return; } - if (vehicleId == 0 && CitizenMgr.IsAreaEvacuating(instanceId) && !CitizenProxy.HasFlags(ref citizen, Citizen.Flags.Evacuating)) + bool isEvacuating = CitizenProxy.HasFlags(ref citizen, Citizen.Flags.Evacuating); + if (vehicleId == 0 && !isEvacuating && CitizenMgr.IsAreaEvacuating(instanceId)) { Log.Debug(LogCategory.Movement, TimeInfo.Now, $"Tourist {GetCitizenDesc(citizenId, ref citizen)} was on the way, but the area evacuates. Searching for a shelter."); - touristAI.FindVisitPlace(instance, citizenId, CitizenProxy.GetCurrentBuilding(ref citizen), touristAI.GetEvacuationReason(instance, 0)); + TransferMgr.AddOutgoingOfferFromCurrentPosition(citizenId, touristAI.GetEvacuationReason(instance, 0)); + return; + } + + if (isEvacuating) + { return; } diff --git a/src/RealTime/CustomAI/ResidentState.cs b/src/RealTime/CustomAI/ResidentState.cs index 7c11686a..917afe0f 100644 --- a/src/RealTime/CustomAI/ResidentState.cs +++ b/src/RealTime/CustomAI/ResidentState.cs @@ -34,7 +34,7 @@ internal enum ResidentState : byte /// The citizen is in a shelter building. InShelter, - /// The citizen was in transition from one state to another, but must change the decision. + /// The citizen was in transition from one state to another. InTransition, } } \ No newline at end of file diff --git a/src/RealTime/GameConnection/BuildingManagerConnection.cs b/src/RealTime/GameConnection/BuildingManagerConnection.cs index 0e42af78..f3e07dbb 100644 --- a/src/RealTime/GameConnection/BuildingManagerConnection.cs +++ b/src/RealTime/GameConnection/BuildingManagerConnection.cs @@ -418,6 +418,18 @@ public DistrictPolicies.Park GetParkPolicies(byte parkId) : DistrictManager.instance.m_parks.m_buffer[parkId].m_parkPolicies; } + /// + /// Determines whether the area around the building with specified ID is currently being evacuated. + /// + /// The building ID to check. + /// + /// true if the area around the building with specified ID is currently being evacuated.; otherwise, false. + /// + public bool IsAreaEvacuating(ushort buildingId) + { + return buildingId != 0 && DisasterManager.instance.IsEvacuating(BuildingManager.instance.m_buildings.m_buffer[buildingId].m_position); + } + private static bool BuildingCanBeVisited(ushort buildingId) { uint currentUnitId = BuildingManager.instance.m_buildings.m_buffer[buildingId].m_citizenUnits; diff --git a/src/RealTime/GameConnection/IBuildingManagerConnection.cs b/src/RealTime/GameConnection/IBuildingManagerConnection.cs index 57d9e4f0..81e73a66 100644 --- a/src/RealTime/GameConnection/IBuildingManagerConnection.cs +++ b/src/RealTime/GameConnection/IBuildingManagerConnection.cs @@ -178,5 +178,14 @@ ushort FindActiveBuilding( /// The ID of the park to get policies of. /// The policies of the park. DistrictPolicies.Park GetParkPolicies(byte parkId); + + /// + /// Determines whether the area around the building with specified ID is currently being evacuated. + /// + /// The building ID to check. + /// + /// true if the area around the building with specified ID is currently being evacuated.; otherwise, false. + /// + bool IsAreaEvacuating(ushort buildingId); } } \ No newline at end of file diff --git a/src/RealTime/Localization/Translations/de.xml b/src/RealTime/Localization/Translations/de.xml index ae6a4eb8..d8dce311 100644 --- a/src/RealTime/Localization/Translations/de.xml +++ b/src/RealTime/Localization/Translations/de.xml @@ -9,6 +9,7 @@ + diff --git a/src/RealTime/Localization/Translations/en.xml b/src/RealTime/Localization/Translations/en.xml index 4a93a5a9..1916fc8f 100644 --- a/src/RealTime/Localization/Translations/en.xml +++ b/src/RealTime/Localization/Translations/en.xml @@ -8,7 +8,8 @@ - + + diff --git a/src/RealTime/Localization/Translations/es.xml b/src/RealTime/Localization/Translations/es.xml index 79fee0ab..9497720b 100644 --- a/src/RealTime/Localization/Translations/es.xml +++ b/src/RealTime/Localization/Translations/es.xml @@ -9,6 +9,7 @@ + diff --git a/src/RealTime/Localization/Translations/fr.xml b/src/RealTime/Localization/Translations/fr.xml index 65cfa0cb..b64c445d 100644 --- a/src/RealTime/Localization/Translations/fr.xml +++ b/src/RealTime/Localization/Translations/fr.xml @@ -9,6 +9,7 @@ + diff --git a/src/RealTime/Localization/Translations/ko.xml b/src/RealTime/Localization/Translations/ko.xml index f8062a62..c4ef1a75 100644 --- a/src/RealTime/Localization/Translations/ko.xml +++ b/src/RealTime/Localization/Translations/ko.xml @@ -5,9 +5,11 @@ - + + + diff --git a/src/RealTime/Localization/Translations/pl.xml b/src/RealTime/Localization/Translations/pl.xml index c7d3282e..89266b05 100644 --- a/src/RealTime/Localization/Translations/pl.xml +++ b/src/RealTime/Localization/Translations/pl.xml @@ -9,6 +9,7 @@ + diff --git a/src/RealTime/Localization/Translations/pt.xml b/src/RealTime/Localization/Translations/pt.xml index 59b13e58..f45d4436 100644 --- a/src/RealTime/Localization/Translations/pt.xml +++ b/src/RealTime/Localization/Translations/pt.xml @@ -9,6 +9,7 @@ + diff --git a/src/RealTime/Localization/Translations/ru.xml b/src/RealTime/Localization/Translations/ru.xml index ddbd6436..17b51d63 100644 --- a/src/RealTime/Localization/Translations/ru.xml +++ b/src/RealTime/Localization/Translations/ru.xml @@ -9,6 +9,7 @@ + diff --git a/src/RealTime/Localization/Translations/zh.xml b/src/RealTime/Localization/Translations/zh.xml index 9bd8fd27..c084ed2c 100644 --- a/src/RealTime/Localization/Translations/zh.xml +++ b/src/RealTime/Localization/Translations/zh.xml @@ -9,6 +9,7 @@ + diff --git a/src/RealTime/Simulation/TimeAdjustment.cs b/src/RealTime/Simulation/TimeAdjustment.cs index ee6bd073..c197e6df 100644 --- a/src/RealTime/Simulation/TimeAdjustment.cs +++ b/src/RealTime/Simulation/TimeAdjustment.cs @@ -13,6 +13,7 @@ internal sealed class TimeAdjustment { private const int RealtimeSpeed = 23; private readonly uint vanillaFramesPerDay; + private readonly TimeSpan vanillaTimePerFrame; private readonly RealTimeConfig config; private uint dayTimeSpeed; @@ -29,6 +30,7 @@ public TimeAdjustment(RealTimeConfig config) { this.config = config ?? throw new ArgumentNullException(nameof(config)); vanillaFramesPerDay = SimulationManager.DAYTIME_FRAMES; + vanillaTimePerFrame = SimulationManager.instance.m_timePerFrame; } /// Enables the customized time adjustment. @@ -39,16 +41,19 @@ public DateTime Enable(bool setDefaultTime) { dayTimeSpeed = config.DayTimeSpeed; nightTimeSpeed = config.NightTimeSpeed; - isNightTime = SimulationManager.instance.m_isNightTime; isNightEnabled = SimulationManager.instance.m_enableDayNight; + DateTime now = SimulationManager.instance.m_ThreadingWrapper.simulationTime; if (setDefaultTime) { - DateTime currentDate = SimulationManager.instance.m_ThreadingWrapper.simulationTime.Date; - SetGameDateTime(currentDate.AddHours(config.WakeUpHour)); + now = now.Date.AddHours(config.WakeUpHour); + SetGameDateTime(now); } - return UpdateTimeSimulationValues(CalculateFramesPerDay()); + float currentHour = now.TimeOfDay.Hours; + isNightTime = currentHour < config.WakeUpHour || currentHour >= config.GoToSleepHour; + + return UpdateTimeSimulationValues(CalculateFramesPerDay(), useCustomTimePerFrame: true); } /// Updates the time adjustment to be synchronized with the configuration and the daytime. @@ -74,14 +79,14 @@ public bool Update(bool force) uint currentFramesPerDay = SimulationManager.DAYTIME_FRAMES; uint newFramesPerDay = CalculateFramesPerDay(); - UpdateTimeSimulationValues(newFramesPerDay); + UpdateTimeSimulationValues(newFramesPerDay, useCustomTimePerFrame: true); return currentFramesPerDay != newFramesPerDay; } /// Disables the customized time adjustment restoring the default vanilla values. public void Disable() { - UpdateTimeSimulationValues(vanillaFramesPerDay); + UpdateTimeSimulationValues(vanillaFramesPerDay, useCustomTimePerFrame: false); } /// Gets the original time represented by the frame index. @@ -138,7 +143,7 @@ private static void SetGameDateTime(DateTime dateTime) sm.m_dayTimeOffsetFrames = sm.m_dayTimeFrame - sm.m_currentFrameIndex & SimulationManager.DAYTIME_FRAMES - 1; } - private DateTime UpdateTimeSimulationValues(uint framesPerDay) + private DateTime UpdateTimeSimulationValues(uint framesPerDay, bool useCustomTimePerFrame) { SimulationManager.DAYTIME_FRAMES = framesPerDay; SimulationManager.DAYTIME_FRAME_TO_HOUR = 24f / SimulationManager.DAYTIME_FRAMES; @@ -150,7 +155,10 @@ private DateTime UpdateTimeSimulationValues(uint framesPerDay) originalTimeOffsetTicks = sm.m_timeOffsetTicks; DateTime originalDate = sm.m_ThreadingWrapper.simulationTime; - sm.m_timePerFrame = new TimeSpan(24L * 3600L * 10_000_000L / framesPerDay); + sm.m_timePerFrame = useCustomTimePerFrame + ? new TimeSpan(24L * 3600L * 10_000_000L / framesPerDay) + : vanillaTimePerFrame; + SetGameDateTime(originalDate); return sm.m_currentGameTime; diff --git a/src/SkyTools b/src/SkyTools index a0c6bf9c..dfc3a01f 160000 --- a/src/SkyTools +++ b/src/SkyTools @@ -1 +1 @@ -Subproject commit a0c6bf9cf2661b6c3361098f2e387c5caffcc842 +Subproject commit dfc3a01f272061790cd0f08b426668cc2c81ed60