From 75e4f31ccd439b9d49666f28477a8365869a1677 Mon Sep 17 00:00:00 2001 From: Sarahtonin Date: Sat, 5 Sep 2026 16:12:05 +0200 Subject: [PATCH] Made new simulation engine (which fixes dumb mutation bug). --- Pages/Garden.php | 1 + Scripts/Garden/Garden.js | 189 +++++++------ Scripts/Garden/MutationEngine.js | 132 ++++++--- Scripts/Garden/Save.js | 146 +++++++++- Scripts/Garden/Simulation.js | 447 +++++++++++++++++++++++++++++++ Scripts/Garden/Upgrades.js | 12 +- 6 files changed, 800 insertions(+), 127 deletions(-) create mode 100644 Scripts/Garden/Simulation.js diff --git a/Pages/Garden.php b/Pages/Garden.php index 543b7f6..1bb4d18 100644 --- a/Pages/Garden.php +++ b/Pages/Garden.php @@ -294,6 +294,7 @@ $PageSection = "Garden"; + diff --git a/Scripts/Garden/Garden.js b/Scripts/Garden/Garden.js index a24927a..b1d386d 100644 --- a/Scripts/Garden/Garden.js +++ b/Scripts/Garden/Garden.js @@ -65,19 +65,23 @@ async function StartGame() { GameSave = await LoadGame(); + const InitialSimulationResult = + AdvanceGameSimulation( + GameSave, + Date.now() + ); + BindGardenTools(); BindGardenSelector(); BindQuickBuyControls(); EnsureSelectedSeed(); - const InitialMutationResult = - CheckAllGardenMutations( - GameSave - ); - RenderGame(); - if (InitialMutationResult.Attempted) { + if ( + InitialSimulationResult.Advanced || + InitialSimulationResult.Dirty + ) { await SaveGame( GameSave ); @@ -184,6 +188,13 @@ function BindGardenSelector() { async function SwitchGarden( Direction ) { + const ActionTime = Date.now(); + + AdvanceGameSimulation( + GameSave, + ActionTime + ); + const GardenCount = GameSave.Gardens.length; @@ -219,7 +230,8 @@ async function SwitchGarden( const MutationResult = CheckGardenMutations( - GameSave + GameSave, + ActionTime ); RenderGame(); @@ -348,49 +360,15 @@ function RenderGardenSelector() { function CheckAllGardenMutations( - SaveData + SaveData, + AtTime = GetSimulationTime( + SaveData + ) ) { - const OriginalGardenIndex = - SaveData.ActiveGardenIndex; - - const Result = { - Attempted: false, - Changed: false - }; - - - for ( - let GardenIndex = 0; - GardenIndex < - SaveData.Gardens.length; - GardenIndex++ - ) { - SetActiveGardenIndex( - SaveData, - GardenIndex - ); - - const GardenResult = - CheckGardenMutations( - SaveData - ); - - Result.Attempted = - Result.Attempted || - GardenResult.Attempted; - - Result.Changed = - Result.Changed || - GardenResult.Changed; - } - - - SetActiveGardenIndex( + return CheckAllGardenMutationsAtTime( SaveData, - OriginalGardenIndex + AtTime ); - - return Result; } @@ -1832,7 +1810,9 @@ function CreateGardenPlotTimer( 0, Plant.GrowthTime - ( - Date.now() - + GetSimulationTime( + GameSave + ) - Plot.PlantedAt ) ); @@ -1905,6 +1885,13 @@ function FormatGardenRemainingTime( async function PlantSeed( PlotIndex ) { + const ActionTime = Date.now(); + + AdvanceGameSimulation( + GameSave, + ActionTime + ); + if ( GameSave.Garden.Plots[ PlotIndex @@ -1959,7 +1946,7 @@ async function PlantSeed( PlotIndex ] = { Plant: PlantKey, - PlantedAt: Date.now() + PlantedAt: ActionTime }; @@ -1970,7 +1957,8 @@ async function PlantSeed( ); CheckGardenMutations( - GameSave + GameSave, + ActionTime ); RenderGame(); @@ -1982,6 +1970,13 @@ async function PlantSeed( async function PlantManySeeds() { + const ActionTime = Date.now(); + + AdvanceGameSimulation( + GameSave, + ActionTime + ); + EnsureSelectedSeed(); if (SelectedSeedId === null) { @@ -2066,7 +2061,7 @@ async function PlantManySeeds() { } - const PlantedAt = Date.now(); + const PlantedAt = ActionTime; for ( let Index = 0; @@ -2093,7 +2088,8 @@ async function PlantManySeeds() { ); CheckGardenMutations( - GameSave + GameSave, + ActionTime ); RenderGame(); @@ -2107,6 +2103,13 @@ async function PlantManySeeds() { async function HarvestPlant( PlotIndex ) { + const ActionTime = Date.now(); + + AdvanceGameSimulation( + GameSave, + ActionTime + ); + const Plot = GameSave.Garden.Plots[ PlotIndex @@ -2166,7 +2169,8 @@ async function HarvestPlant( ); CheckGardenMutations( - GameSave + GameSave, + ActionTime ); RenderGame(); @@ -2180,6 +2184,13 @@ async function HarvestPlant( async function RemovePlant( PlotIndex ) { + const ActionTime = Date.now(); + + AdvanceGameSimulation( + GameSave, + ActionTime + ); + const Plot = GameSave.Garden.Plots[ PlotIndex @@ -2257,7 +2268,8 @@ async function RemovePlant( ); CheckGardenMutations( - GameSave + GameSave, + ActionTime ); RenderGame(); @@ -2271,6 +2283,13 @@ async function RemovePlant( async function FertilizePlant( PlotIndex ) { + const ActionTime = Date.now(); + + AdvanceGameSimulation( + GameSave, + ActionTime + ); + const Plot = GameSave.Garden.Plots[ PlotIndex @@ -2330,7 +2349,8 @@ async function FertilizePlant( ); CheckGardenMutations( - GameSave + GameSave, + ActionTime ); RenderGame(); @@ -2342,6 +2362,13 @@ async function FertilizePlant( async function UseFutureSight() { + const ActionTime = Date.now(); + + AdvanceGameSimulation( + GameSave, + ActionTime + ); + if ( !IsGardenToolUnlocked( GameSave, @@ -2450,41 +2477,37 @@ function CreateFutureSightPreview() { async function GardenTick() { - RenderGarden(); - RenderTools(); - RenderNextHarvest(); - RenderGardenOverview(); - RenderGardenEconomy(); - RenderGardenInfoLayout(); - - - if ( - SelectedGardenTool === - "FutureSight" && - FutureSightPreview !== null - ) { - return; - } - - if (MutationCheckPending) { return; } - MutationCheckPending = true; - try { - const MutationResult = - CheckAllGardenMutations( - GameSave + const SimulationResult = + AdvanceGameSimulation( + GameSave, + Date.now() ); + if ( + SimulationResult.Attempted && + FutureSightPreview !== null + ) { + FutureSightPreview = null; - if (MutationResult.Attempted) { - RenderGame(); + if ( + SelectedGardenTool === + "FutureSight" + ) { + SelectedGardenTool = + "Trowel"; + } + } + RenderGame(); + + if (SimulationResult.Dirty) { await SaveGame( GameSave ); @@ -2531,7 +2554,9 @@ function GetPlantGrowthProgress( } const Age = - Date.now() - + GetSimulationTime( + GameSave + ) - Plot.PlantedAt; return Math.max( @@ -2733,7 +2758,9 @@ function RenderNextHarvest() { const Remaining = Harvest.Time - - Date.now(); + GetSimulationTime( + GameSave + ); const IncludePlantName = HasOverview; diff --git a/Scripts/Garden/MutationEngine.js b/Scripts/Garden/MutationEngine.js index 47d65b2..aa8ef8c 100644 --- a/Scripts/Garden/MutationEngine.js +++ b/Scripts/Garden/MutationEngine.js @@ -2,7 +2,10 @@ const MaxMutationPasses = 100; function CheckGardenMutations( - SaveData + SaveData, + AtTime = GetSimulationTime( + SaveData + ) ) { SaveData.MutationCooldowns ??= {}; @@ -23,13 +26,15 @@ function CheckGardenMutations( ) { const Candidates = FindGardenMutationCandidates( - SaveData + SaveData, + AtTime ) .filter( Candidate => !IsMutationCandidateOnCooldown( SaveData, - Candidate + Candidate, + AtTime ) ) .sort( @@ -98,7 +103,8 @@ function CheckGardenMutations( if ( !DoesMutationCandidateStillMatch( SaveData, - Candidate + Candidate, + AtTime ) ) { continue; @@ -108,7 +114,8 @@ function CheckGardenMutations( if ( IsMutationCandidateOnCooldown( SaveData, - Candidate + Candidate, + AtTime ) ) { continue; @@ -119,7 +126,8 @@ function CheckGardenMutations( StartMutationCandidateCooldown( SaveData, - Candidate + Candidate, + AtTime ); @@ -136,7 +144,9 @@ function CheckGardenMutations( const Succeeded = - Math.random() < Chance; + GetSimulationRandom( + SaveData + ) < Chance; if (Succeeded) { @@ -151,7 +161,8 @@ function CheckGardenMutations( ApplyMutationResult( SaveData, Candidate, - Candidate.Success + Candidate.Success, + AtTime ); @@ -176,7 +187,8 @@ function CheckGardenMutations( const Changed = ApplyMutationFailure( SaveData, - Candidate + Candidate, + AtTime ); @@ -226,7 +238,10 @@ function CheckGardenMutations( function FindGardenMutationCandidates( - SaveData + SaveData, + AtTime = GetSimulationTime( + SaveData + ) ) { const Candidates = []; @@ -283,7 +298,8 @@ function FindGardenMutationCandidates( Mutation, Orientation, OriginX, - OriginY + OriginY, + AtTime ); @@ -307,7 +323,10 @@ function CreateMutationCandidate( Mutation, Orientation, OriginX, - OriginY + OriginY, + AtTime = GetSimulationTime( + SaveData + ) ) { const Captures = {}; const Cells = []; @@ -357,7 +376,8 @@ function CreateMutationCandidate( Plot, Matcher, Mutation, - Captures + Captures, + AtTime ) ) { return null; @@ -399,7 +419,10 @@ function CreateMutationCandidate( function DoesMutationCandidateStillMatch( SaveData, - Candidate + Candidate, + AtTime = GetSimulationTime( + SaveData + ) ) { const RefreshedCandidate = CreateMutationCandidate( @@ -416,7 +439,8 @@ function DoesMutationCandidateStillMatch( Candidate.Rotation }, Candidate.OriginX, - Candidate.OriginY + Candidate.OriginY, + AtTime ); @@ -437,7 +461,8 @@ function DoesPlotMatchMutationMatcher( Plot, Matcher, Mutation, - Captures + Captures, + AtTime ) { if ( Matcher === null || @@ -470,7 +495,8 @@ function DoesPlotMatchMutationMatcher( true && !IsMutationPlantMature( Plot, - Plant + Plant, + AtTime ) ) { return false; @@ -566,7 +592,8 @@ function DoesPlotMatchMutationMatcher( function IsMutationPlantMature( Plot, - Plant + Plant, + AtTime ) { if ( Number( @@ -578,7 +605,7 @@ function IsMutationPlantMature( return ( - Date.now() - + AtTime - Number( Plot.PlantedAt ?? 0 ) >= @@ -589,7 +616,10 @@ function IsMutationPlantMature( function ApplyMutationFailure( SaveData, - Candidate + Candidate, + AtTime = GetSimulationTime( + SaveData + ) ) { const Failure = Candidate.Mutation.Failure ?? @@ -631,7 +661,8 @@ function ApplyMutationFailure( return ApplyMutationResult( SaveData, Candidate, - Failure + Failure, + AtTime ); } @@ -643,7 +674,10 @@ function ApplyMutationFailure( function ApplyMutationResult( SaveData, Candidate, - ResultMatrix + ResultMatrix, + AtTime = GetSimulationTime( + SaveData + ) ) { if (!Array.isArray(ResultMatrix)) { return false; @@ -706,7 +740,8 @@ function ApplyMutationResult( const NewPlot = CreateMutationResultPlot( ResultValue, - Candidate.Captures + Candidate.Captures, + AtTime ); @@ -749,7 +784,8 @@ function ApplyMutationResult( function CreateMutationResultPlot( ResultValue, - Captures + Captures, + AtTime ) { if ( typeof ResultValue === @@ -785,7 +821,7 @@ function CreateMutationResultPlot( return { Plant: ResultValue, - PlantedAt: Date.now() + PlantedAt: AtTime }; } @@ -841,7 +877,7 @@ function CreateMutationResultPlot( return { Plant: ResultValue.Plant, - PlantedAt: Date.now() + PlantedAt: AtTime }; } @@ -1006,42 +1042,56 @@ function GetMutationPatternSpecificity( } -function IsMutationCandidateOnCooldown( +function GetMutationCandidateCooldownUntil( SaveData, Candidate ) { - const CooldownUntil = - Number( - SaveData.MutationCooldowns[ - GetMutationCooldownKey( - Candidate - ) - ] ?? 0 - ); + return Number( + SaveData.MutationCooldowns[ + GetMutationCooldownKey( + Candidate + ) + ] ?? 0 + ); +} - return CooldownUntil > Date.now(); +function IsMutationCandidateOnCooldown( + SaveData, + Candidate, + AtTime = GetSimulationTime( + SaveData + ) +) { + return ( + GetMutationCandidateCooldownUntil( + SaveData, + Candidate + ) > AtTime + ); } function StartMutationCandidateCooldown( SaveData, - Candidate + Candidate, + AtTime = GetSimulationTime( + SaveData + ) ) { const Cooldown = Math.max( - 0, + SimulationMinimumEventInterval, Number( Candidate.Mutation.Cooldown ?? 0 - ) + ) || 0 ); - SaveData.MutationCooldowns[ GetMutationCooldownKey( Candidate ) - ] = Date.now() + Cooldown; + ] = AtTime + Cooldown; } diff --git a/Scripts/Garden/Save.js b/Scripts/Garden/Save.js index 4777803..19e7486 100644 --- a/Scripts/Garden/Save.js +++ b/Scripts/Garden/Save.js @@ -15,6 +15,138 @@ const MaximumGardenNameLength = 32; const StartingGardenWidth = 4; const StartingGardenHeight = 4; +const CurrentSaveVersion = 6; + + +function CreateSimulationRandomState() { + if ( + typeof crypto !== "undefined" && + typeof crypto.getRandomValues === + "function" + ) { + const Values = + new Uint32Array(1); + + crypto.getRandomValues( + Values + ); + + return Values[0] >>> 0; + } + + return ( + Date.now() ^ + Math.floor( + Math.random() * + 0xFFFFFFFF + ) + ) >>> 0; +} + + +function GetLegacySimulationRandomState( + SaveData +) { + const Source = + String( + SaveData.LastSavedAt ?? 0 + ) + + ":" + + String( + SaveData.Revision ?? 0 + ) + + ":" + + String( + SaveData.Version ?? 0 + ); + + let Hash = 2166136261; + + for ( + let Index = 0; + Index < Source.length; + Index++ + ) { + Hash ^= Source.charCodeAt( + Index + ); + + Hash = Math.imul( + Hash, + 16777619 + ); + } + + return Hash >>> 0; +} + + +function GetSavedSimulationTime( + SaveData +) { + const Time = Number( + SaveData?.Simulation + ?.LastProcessedAt + ); + + return Number.isFinite(Time) + ? Time + : Date.now(); +} + + +function NormalizeSimulationData( + SaveData +) { + const Now = Date.now(); + + if ( + SaveData.Simulation === null || + typeof SaveData.Simulation !== + "object" || + Array.isArray( + SaveData.Simulation + ) + ) { + SaveData.Simulation = {}; + } + + const LastProcessedAt = Number( + SaveData.Simulation + .LastProcessedAt ?? + SaveData.LastSavedAt ?? + Now + ); + + SaveData.Simulation.LastProcessedAt = + Math.min( + Now, + Math.max( + 0, + Math.floor( + Number.isFinite( + LastProcessedAt + ) + ? LastProcessedAt + : Now + ) + ) + ); + + const RandomState = Number( + SaveData.Simulation.RandomState + ); + + SaveData.Simulation.RandomState = + Number.isInteger(RandomState) && + RandomState >= 0 && + RandomState <= 0xFFFFFFFF + ? RandomState >>> 0 + : GetLegacySimulationRandomState( + SaveData + ); +} + function CreateGardenData( Name = DefaultGardenName @@ -392,10 +524,16 @@ function CreateNewSave() { return NormalizeSaveData({ - Version: 5, + Version: CurrentSaveVersion, Revision: 0, LastSavedAt: Date.now(), + Simulation: { + LastProcessedAt: Date.now(), + RandomState: + CreateSimulationRandomState() + }, + Currency: { Dew: StartingDew }, @@ -470,7 +608,7 @@ function NormalizeSaveData( Number( SaveData.Version ?? 1 ), - 5 + CurrentSaveVersion ); SaveData.Revision = Number( @@ -482,6 +620,10 @@ function NormalizeSaveData( Date.now() ); + NormalizeSimulationData( + SaveData + ); + SaveData.Currency ??= {}; SaveData.Currency.Dew = Number( diff --git a/Scripts/Garden/Simulation.js b/Scripts/Garden/Simulation.js new file mode 100644 index 0000000..5f20a1e --- /dev/null +++ b/Scripts/Garden/Simulation.js @@ -0,0 +1,447 @@ +const SimulationMinimumEventInterval = 1000; +const MaximumSimulationSteps = 250000; + + +function GetSimulationTime( + SaveData +) { + return GetSavedSimulationTime( + SaveData + ); +} + + +function SetSimulationTime( + SaveData, + Time +) { + SaveData.Simulation ??= {}; + + SaveData.Simulation.LastProcessedAt = + Math.max( + 0, + Math.floor( + Number(Time) || 0 + ) + ); + + return SaveData.Simulation.LastProcessedAt; +} + + +function GetSimulationRandom( + SaveData +) { + SaveData.Simulation ??= {}; + + let State = Number( + SaveData.Simulation.RandomState ?? 0 + ) >>> 0; + + State = ( + State + 0x6D2B79F5 + ) >>> 0; + + SaveData.Simulation.RandomState = + State; + + let Value = State; + + Value = Math.imul( + Value ^ Value >>> 15, + Value | 1 + ); + + Value ^= + Value + + Math.imul( + Value ^ Value >>> 7, + Value | 61 + ); + + return ( + (Value ^ Value >>> 14) >>> 0 + ) / 4294967296; +} + + +function MergeSimulationMutationResult( + Target, + Source +) { + Target.Attempted = + Target.Attempted || + Source.Attempted; + + Target.Changed = + Target.Changed || + Source.Changed; + + Target.SuccessfulMutations.push( + ...(Source.SuccessfulMutations ?? []) + ); + + Target.NewlyDiscoveredMutations.push( + ...(Source.NewlyDiscoveredMutations ?? []) + ); + + Target.NewlyDiscoveredPlants.push( + ...(Source.NewlyDiscoveredPlants ?? []) + ); +} + + +function CreateSimulationResult( + StartTime, + TargetTime +) { + return { + StartTime, + TargetTime, + Advanced: TargetTime > StartTime, + Attempted: false, + Changed: false, + Dirty: false, + CaughtUp: StartTime >= TargetTime, + EndTime: StartTime, + Steps: 0, + SuccessfulMutations: [], + NewlyDiscoveredMutations: [], + NewlyDiscoveredPlants: [] + }; +} + + +function FinalizeSimulationResult( + Result +) { + Result.SuccessfulMutations = [ + ...new Set( + Result.SuccessfulMutations + ) + ]; + + Result.NewlyDiscoveredMutations = [ + ...new Set( + Result.NewlyDiscoveredMutations + ) + ]; + + Result.NewlyDiscoveredPlants = [ + ...new Set( + Result.NewlyDiscoveredPlants + ) + ]; + + Result.Dirty = + Result.Attempted || + Result.Changed; + + return Result; +} + + +function CheckAllGardenMutationsAtTime( + SaveData, + AtTime +) { + const OriginalGardenIndex = + SaveData.ActiveGardenIndex; + + const Result = { + Attempted: false, + Changed: false, + SuccessfulMutations: [], + NewlyDiscoveredMutations: [], + NewlyDiscoveredPlants: [] + }; + + for ( + let GardenIndex = 0; + GardenIndex < SaveData.Gardens.length; + GardenIndex++ + ) { + SetActiveGardenIndex( + SaveData, + GardenIndex + ); + + const GardenResult = + CheckGardenMutations( + SaveData, + AtTime + ); + + MergeSimulationMutationResult( + Result, + GardenResult + ); + } + + SetActiveGardenIndex( + SaveData, + OriginalGardenIndex + ); + + return Result; +} + + +function GetNextPlantMaturityTime( + SaveData, + CurrentTime, + TargetTime +) { + let NextTime = null; + + for (const Garden of SaveData.Gardens) { + for (const Plot of Garden.Plots) { + if (Plot === null) { + continue; + } + + const Plant = + Plants[Plot.Plant]; + + if (Plant === undefined) { + continue; + } + + const GrowthTime = Math.max( + 0, + Number( + Plant.GrowthTime ?? 0 + ) || 0 + ); + + if (GrowthTime <= 0) { + continue; + } + + const MaturityTime = + Number( + Plot.PlantedAt ?? 0 + ) + + GrowthTime; + + if ( + MaturityTime <= CurrentTime || + MaturityTime > TargetTime + ) { + continue; + } + + if ( + NextTime === null || + MaturityTime < NextTime + ) { + NextTime = MaturityTime; + } + } + } + + return NextTime; +} + + +function GetNextMutationAttemptTime( + SaveData, + CurrentTime, + TargetTime +) { + const OriginalGardenIndex = + SaveData.ActiveGardenIndex; + + let NextTime = null; + + for ( + let GardenIndex = 0; + GardenIndex < SaveData.Gardens.length; + GardenIndex++ + ) { + SetActiveGardenIndex( + SaveData, + GardenIndex + ); + + const Candidates = + FindGardenMutationCandidates( + SaveData, + CurrentTime + ); + + for (const Candidate of Candidates) { + const CooldownUntil = + GetMutationCandidateCooldownUntil( + SaveData, + Candidate + ); + + const AttemptTime = + CooldownUntil > CurrentTime + ? CooldownUntil + : CurrentTime + + SimulationMinimumEventInterval; + + if (AttemptTime > TargetTime) { + continue; + } + + if ( + NextTime === null || + AttemptTime < NextTime + ) { + NextTime = AttemptTime; + } + } + } + + SetActiveGardenIndex( + SaveData, + OriginalGardenIndex + ); + + return NextTime; +} + + +function GetNextSimulationEventTime( + SaveData, + CurrentTime, + TargetTime +) { + const MaturityTime = + GetNextPlantMaturityTime( + SaveData, + CurrentTime, + TargetTime + ); + + const MutationTime = + GetNextMutationAttemptTime( + SaveData, + CurrentTime, + TargetTime + ); + + if (MaturityTime === null) { + return MutationTime; + } + + if (MutationTime === null) { + return MaturityTime; + } + + return Math.min( + MaturityTime, + MutationTime + ); +} + + +function AdvanceGameSimulation( + SaveData, + TargetTime = Date.now() +) { + const SafeTargetTime = Math.max( + 0, + Math.floor( + Number(TargetTime) || + Date.now() + ) + ); + + let CurrentTime = Math.min( + GetSimulationTime( + SaveData + ), + SafeTargetTime + ); + + const Result = + CreateSimulationResult( + CurrentTime, + SafeTargetTime + ); + + SetSimulationTime( + SaveData, + CurrentTime + ); + + for ( + let Step = 0; + Step < MaximumSimulationSteps; + Step++ + ) { + Result.Steps = Step + 1; + + const MutationResult = + CheckAllGardenMutationsAtTime( + SaveData, + CurrentTime + ); + + MergeSimulationMutationResult( + Result, + MutationResult + ); + + if (CurrentTime >= SafeTargetTime) { + Result.CaughtUp = true; + break; + } + + const NextTime = + GetNextSimulationEventTime( + SaveData, + CurrentTime, + SafeTargetTime + ); + + if (NextTime === null) { + CurrentTime = SafeTargetTime; + SetSimulationTime( + SaveData, + CurrentTime + ); + Result.CaughtUp = true; + break; + } + + CurrentTime = Math.min( + SafeTargetTime, + Math.max( + CurrentTime + 1, + NextTime + ) + ); + + SetSimulationTime( + SaveData, + CurrentTime + ); + } + + Result.EndTime = + GetSimulationTime( + SaveData + ); + + const FinalResult = + FinalizeSimulationResult( + Result + ); + + if ( + FinalResult.Dirty && + typeof WriteLocalSave === + "function" + ) { + WriteLocalSave( + SaveData + ); + } + + return FinalResult; +} diff --git a/Scripts/Garden/Upgrades.js b/Scripts/Garden/Upgrades.js index 610fe8e..7762e2a 100644 --- a/Scripts/Garden/Upgrades.js +++ b/Scripts/Garden/Upgrades.js @@ -692,7 +692,9 @@ function TakeFertilizerUse( function GetFutureSightCooldownRemaining( SaveData, - Now = Date.now() + Now = GetSavedSimulationTime( + SaveData + ) ) { return Math.max( 0, @@ -705,7 +707,9 @@ function GetFutureSightCooldownRemaining( function IsFutureSightReady( SaveData, - Now = Date.now() + Now = GetSavedSimulationTime( + SaveData + ) ) { return GetFutureSightCooldownRemaining( SaveData, @@ -716,7 +720,9 @@ function IsFutureSightReady( function StartFutureSightCooldown( SaveData, - Now = Date.now() + Now = GetSavedSimulationTime( + SaveData + ) ) { SaveData.Tools ??= {}; SaveData.Tools.FutureSightCooldownUntil =