Made new simulation engine (which fixes dumb mutation bug).

This commit is contained in:
sarah committed 2026-09-05 16:12:05 +02:00
1 parent 8ec07731a7
commit 75e4f31ccd
6 files changed
+800 -127

No files matched your search

+1
View File
@@ -294,6 +294,7 @@ $PageSection = "Garden";
<script src="/Scripts/Garden/Content.js"></script> <script src="/Scripts/Garden/Content.js"></script>
<script src="/Scripts/Garden/Economy.js"></script> <script src="/Scripts/Garden/Economy.js"></script>
<script src="/Scripts/Garden/Upgrades.js"></script> <script src="/Scripts/Garden/Upgrades.js"></script>
<script src="/Scripts/Garden/Simulation.js"></script>
<script src="/Scripts/Garden/MutationEngine.js"></script> <script src="/Scripts/Garden/MutationEngine.js"></script>
<script src="/Scripts/Garden/Users.js"></script> <script src="/Scripts/Garden/Users.js"></script>
<script src="/Scripts/Garden/Garden.js"></script> <script src="/Scripts/Garden/Garden.js"></script>
+108 -81
View File
@@ -65,19 +65,23 @@ async function StartGame() {
GameSave = await LoadGame(); GameSave = await LoadGame();
const InitialSimulationResult =
AdvanceGameSimulation(
GameSave,
Date.now()
);
BindGardenTools(); BindGardenTools();
BindGardenSelector(); BindGardenSelector();
BindQuickBuyControls(); BindQuickBuyControls();
EnsureSelectedSeed(); EnsureSelectedSeed();
const InitialMutationResult =
CheckAllGardenMutations(
GameSave
);
RenderGame(); RenderGame();
if (InitialMutationResult.Attempted) { if (
InitialSimulationResult.Advanced ||
InitialSimulationResult.Dirty
) {
await SaveGame( await SaveGame(
GameSave GameSave
); );
@@ -184,6 +188,13 @@ function BindGardenSelector() {
async function SwitchGarden( async function SwitchGarden(
Direction Direction
) { ) {
const ActionTime = Date.now();
AdvanceGameSimulation(
GameSave,
ActionTime
);
const GardenCount = const GardenCount =
GameSave.Gardens.length; GameSave.Gardens.length;
@@ -219,7 +230,8 @@ async function SwitchGarden(
const MutationResult = const MutationResult =
CheckGardenMutations( CheckGardenMutations(
GameSave GameSave,
ActionTime
); );
RenderGame(); RenderGame();
@@ -348,49 +360,15 @@ function RenderGardenSelector() {
function CheckAllGardenMutations( function CheckAllGardenMutations(
SaveData SaveData,
AtTime = GetSimulationTime(
SaveData
)
) { ) {
const OriginalGardenIndex = return CheckAllGardenMutationsAtTime(
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(
SaveData, SaveData,
OriginalGardenIndex AtTime
); );
return Result;
} }
@@ -1832,7 +1810,9 @@ function CreateGardenPlotTimer(
0, 0,
Plant.GrowthTime - Plant.GrowthTime -
( (
Date.now() - GetSimulationTime(
GameSave
) -
Plot.PlantedAt Plot.PlantedAt
) )
); );
@@ -1905,6 +1885,13 @@ function FormatGardenRemainingTime(
async function PlantSeed( async function PlantSeed(
PlotIndex PlotIndex
) { ) {
const ActionTime = Date.now();
AdvanceGameSimulation(
GameSave,
ActionTime
);
if ( if (
GameSave.Garden.Plots[ GameSave.Garden.Plots[
PlotIndex PlotIndex
@@ -1959,7 +1946,7 @@ async function PlantSeed(
PlotIndex PlotIndex
] = { ] = {
Plant: PlantKey, Plant: PlantKey,
PlantedAt: Date.now() PlantedAt: ActionTime
}; };
@@ -1970,7 +1957,8 @@ async function PlantSeed(
); );
CheckGardenMutations( CheckGardenMutations(
GameSave GameSave,
ActionTime
); );
RenderGame(); RenderGame();
@@ -1982,6 +1970,13 @@ async function PlantSeed(
async function PlantManySeeds() { async function PlantManySeeds() {
const ActionTime = Date.now();
AdvanceGameSimulation(
GameSave,
ActionTime
);
EnsureSelectedSeed(); EnsureSelectedSeed();
if (SelectedSeedId === null) { if (SelectedSeedId === null) {
@@ -2066,7 +2061,7 @@ async function PlantManySeeds() {
} }
const PlantedAt = Date.now(); const PlantedAt = ActionTime;
for ( for (
let Index = 0; let Index = 0;
@@ -2093,7 +2088,8 @@ async function PlantManySeeds() {
); );
CheckGardenMutations( CheckGardenMutations(
GameSave GameSave,
ActionTime
); );
RenderGame(); RenderGame();
@@ -2107,6 +2103,13 @@ async function PlantManySeeds() {
async function HarvestPlant( async function HarvestPlant(
PlotIndex PlotIndex
) { ) {
const ActionTime = Date.now();
AdvanceGameSimulation(
GameSave,
ActionTime
);
const Plot = const Plot =
GameSave.Garden.Plots[ GameSave.Garden.Plots[
PlotIndex PlotIndex
@@ -2166,7 +2169,8 @@ async function HarvestPlant(
); );
CheckGardenMutations( CheckGardenMutations(
GameSave GameSave,
ActionTime
); );
RenderGame(); RenderGame();
@@ -2180,6 +2184,13 @@ async function HarvestPlant(
async function RemovePlant( async function RemovePlant(
PlotIndex PlotIndex
) { ) {
const ActionTime = Date.now();
AdvanceGameSimulation(
GameSave,
ActionTime
);
const Plot = const Plot =
GameSave.Garden.Plots[ GameSave.Garden.Plots[
PlotIndex PlotIndex
@@ -2257,7 +2268,8 @@ async function RemovePlant(
); );
CheckGardenMutations( CheckGardenMutations(
GameSave GameSave,
ActionTime
); );
RenderGame(); RenderGame();
@@ -2271,6 +2283,13 @@ async function RemovePlant(
async function FertilizePlant( async function FertilizePlant(
PlotIndex PlotIndex
) { ) {
const ActionTime = Date.now();
AdvanceGameSimulation(
GameSave,
ActionTime
);
const Plot = const Plot =
GameSave.Garden.Plots[ GameSave.Garden.Plots[
PlotIndex PlotIndex
@@ -2330,7 +2349,8 @@ async function FertilizePlant(
); );
CheckGardenMutations( CheckGardenMutations(
GameSave GameSave,
ActionTime
); );
RenderGame(); RenderGame();
@@ -2342,6 +2362,13 @@ async function FertilizePlant(
async function UseFutureSight() { async function UseFutureSight() {
const ActionTime = Date.now();
AdvanceGameSimulation(
GameSave,
ActionTime
);
if ( if (
!IsGardenToolUnlocked( !IsGardenToolUnlocked(
GameSave, GameSave,
@@ -2450,41 +2477,37 @@ function CreateFutureSightPreview() {
async function GardenTick() { async function GardenTick() {
RenderGarden();
RenderTools();
RenderNextHarvest();
RenderGardenOverview();
RenderGardenEconomy();
RenderGardenInfoLayout();
if (
SelectedGardenTool ===
"FutureSight" &&
FutureSightPreview !== null
) {
return;
}
if (MutationCheckPending) { if (MutationCheckPending) {
return; return;
} }
MutationCheckPending = true; MutationCheckPending = true;
try { try {
const MutationResult = const SimulationResult =
CheckAllGardenMutations( AdvanceGameSimulation(
GameSave GameSave,
Date.now()
); );
if (
SimulationResult.Attempted &&
FutureSightPreview !== null
) {
FutureSightPreview = null;
if (MutationResult.Attempted) { if (
RenderGame(); SelectedGardenTool ===
"FutureSight"
) {
SelectedGardenTool =
"Trowel";
}
}
RenderGame();
if (SimulationResult.Dirty) {
await SaveGame( await SaveGame(
GameSave GameSave
); );
@@ -2531,7 +2554,9 @@ function GetPlantGrowthProgress(
} }
const Age = const Age =
Date.now() - GetSimulationTime(
GameSave
) -
Plot.PlantedAt; Plot.PlantedAt;
return Math.max( return Math.max(
@@ -2733,7 +2758,9 @@ function RenderNextHarvest() {
const Remaining = const Remaining =
Harvest.Time - Harvest.Time -
Date.now(); GetSimulationTime(
GameSave
);
const IncludePlantName = const IncludePlantName =
HasOverview; HasOverview;
+91 -41
View File
@@ -2,7 +2,10 @@ const MaxMutationPasses = 100;
function CheckGardenMutations( function CheckGardenMutations(
SaveData SaveData,
AtTime = GetSimulationTime(
SaveData
)
) { ) {
SaveData.MutationCooldowns ??= {}; SaveData.MutationCooldowns ??= {};
@@ -23,13 +26,15 @@ function CheckGardenMutations(
) { ) {
const Candidates = const Candidates =
FindGardenMutationCandidates( FindGardenMutationCandidates(
SaveData SaveData,
AtTime
) )
.filter( .filter(
Candidate => Candidate =>
!IsMutationCandidateOnCooldown( !IsMutationCandidateOnCooldown(
SaveData, SaveData,
Candidate Candidate,
AtTime
) )
) )
.sort( .sort(
@@ -98,7 +103,8 @@ function CheckGardenMutations(
if ( if (
!DoesMutationCandidateStillMatch( !DoesMutationCandidateStillMatch(
SaveData, SaveData,
Candidate Candidate,
AtTime
) )
) { ) {
continue; continue;
@@ -108,7 +114,8 @@ function CheckGardenMutations(
if ( if (
IsMutationCandidateOnCooldown( IsMutationCandidateOnCooldown(
SaveData, SaveData,
Candidate Candidate,
AtTime
) )
) { ) {
continue; continue;
@@ -119,7 +126,8 @@ function CheckGardenMutations(
StartMutationCandidateCooldown( StartMutationCandidateCooldown(
SaveData, SaveData,
Candidate Candidate,
AtTime
); );
@@ -136,7 +144,9 @@ function CheckGardenMutations(
const Succeeded = const Succeeded =
Math.random() < Chance; GetSimulationRandom(
SaveData
) < Chance;
if (Succeeded) { if (Succeeded) {
@@ -151,7 +161,8 @@ function CheckGardenMutations(
ApplyMutationResult( ApplyMutationResult(
SaveData, SaveData,
Candidate, Candidate,
Candidate.Success Candidate.Success,
AtTime
); );
@@ -176,7 +187,8 @@ function CheckGardenMutations(
const Changed = const Changed =
ApplyMutationFailure( ApplyMutationFailure(
SaveData, SaveData,
Candidate Candidate,
AtTime
); );
@@ -226,7 +238,10 @@ function CheckGardenMutations(
function FindGardenMutationCandidates( function FindGardenMutationCandidates(
SaveData SaveData,
AtTime = GetSimulationTime(
SaveData
)
) { ) {
const Candidates = []; const Candidates = [];
@@ -283,7 +298,8 @@ function FindGardenMutationCandidates(
Mutation, Mutation,
Orientation, Orientation,
OriginX, OriginX,
OriginY OriginY,
AtTime
); );
@@ -307,7 +323,10 @@ function CreateMutationCandidate(
Mutation, Mutation,
Orientation, Orientation,
OriginX, OriginX,
OriginY OriginY,
AtTime = GetSimulationTime(
SaveData
)
) { ) {
const Captures = {}; const Captures = {};
const Cells = []; const Cells = [];
@@ -357,7 +376,8 @@ function CreateMutationCandidate(
Plot, Plot,
Matcher, Matcher,
Mutation, Mutation,
Captures Captures,
AtTime
) )
) { ) {
return null; return null;
@@ -399,7 +419,10 @@ function CreateMutationCandidate(
function DoesMutationCandidateStillMatch( function DoesMutationCandidateStillMatch(
SaveData, SaveData,
Candidate Candidate,
AtTime = GetSimulationTime(
SaveData
)
) { ) {
const RefreshedCandidate = const RefreshedCandidate =
CreateMutationCandidate( CreateMutationCandidate(
@@ -416,7 +439,8 @@ function DoesMutationCandidateStillMatch(
Candidate.Rotation Candidate.Rotation
}, },
Candidate.OriginX, Candidate.OriginX,
Candidate.OriginY Candidate.OriginY,
AtTime
); );
@@ -437,7 +461,8 @@ function DoesPlotMatchMutationMatcher(
Plot, Plot,
Matcher, Matcher,
Mutation, Mutation,
Captures Captures,
AtTime
) { ) {
if ( if (
Matcher === null || Matcher === null ||
@@ -470,7 +495,8 @@ function DoesPlotMatchMutationMatcher(
true && true &&
!IsMutationPlantMature( !IsMutationPlantMature(
Plot, Plot,
Plant Plant,
AtTime
) )
) { ) {
return false; return false;
@@ -566,7 +592,8 @@ function DoesPlotMatchMutationMatcher(
function IsMutationPlantMature( function IsMutationPlantMature(
Plot, Plot,
Plant Plant,
AtTime
) { ) {
if ( if (
Number( Number(
@@ -578,7 +605,7 @@ function IsMutationPlantMature(
return ( return (
Date.now() - AtTime -
Number( Number(
Plot.PlantedAt ?? 0 Plot.PlantedAt ?? 0
) >= ) >=
@@ -589,7 +616,10 @@ function IsMutationPlantMature(
function ApplyMutationFailure( function ApplyMutationFailure(
SaveData, SaveData,
Candidate Candidate,
AtTime = GetSimulationTime(
SaveData
)
) { ) {
const Failure = const Failure =
Candidate.Mutation.Failure ?? Candidate.Mutation.Failure ??
@@ -631,7 +661,8 @@ function ApplyMutationFailure(
return ApplyMutationResult( return ApplyMutationResult(
SaveData, SaveData,
Candidate, Candidate,
Failure Failure,
AtTime
); );
} }
@@ -643,7 +674,10 @@ function ApplyMutationFailure(
function ApplyMutationResult( function ApplyMutationResult(
SaveData, SaveData,
Candidate, Candidate,
ResultMatrix ResultMatrix,
AtTime = GetSimulationTime(
SaveData
)
) { ) {
if (!Array.isArray(ResultMatrix)) { if (!Array.isArray(ResultMatrix)) {
return false; return false;
@@ -706,7 +740,8 @@ function ApplyMutationResult(
const NewPlot = const NewPlot =
CreateMutationResultPlot( CreateMutationResultPlot(
ResultValue, ResultValue,
Candidate.Captures Candidate.Captures,
AtTime
); );
@@ -749,7 +784,8 @@ function ApplyMutationResult(
function CreateMutationResultPlot( function CreateMutationResultPlot(
ResultValue, ResultValue,
Captures Captures,
AtTime
) { ) {
if ( if (
typeof ResultValue === typeof ResultValue ===
@@ -785,7 +821,7 @@ function CreateMutationResultPlot(
return { return {
Plant: ResultValue, Plant: ResultValue,
PlantedAt: Date.now() PlantedAt: AtTime
}; };
} }
@@ -841,7 +877,7 @@ function CreateMutationResultPlot(
return { return {
Plant: ResultValue.Plant, Plant: ResultValue.Plant,
PlantedAt: Date.now() PlantedAt: AtTime
}; };
} }
@@ -1006,42 +1042,56 @@ function GetMutationPatternSpecificity(
} }
function IsMutationCandidateOnCooldown( function GetMutationCandidateCooldownUntil(
SaveData, SaveData,
Candidate Candidate
) { ) {
const CooldownUntil = return Number(
Number( SaveData.MutationCooldowns[
SaveData.MutationCooldowns[ GetMutationCooldownKey(
GetMutationCooldownKey( Candidate
Candidate )
) ] ?? 0
] ?? 0 );
); }
return CooldownUntil > Date.now(); function IsMutationCandidateOnCooldown(
SaveData,
Candidate,
AtTime = GetSimulationTime(
SaveData
)
) {
return (
GetMutationCandidateCooldownUntil(
SaveData,
Candidate
) > AtTime
);
} }
function StartMutationCandidateCooldown( function StartMutationCandidateCooldown(
SaveData, SaveData,
Candidate Candidate,
AtTime = GetSimulationTime(
SaveData
)
) { ) {
const Cooldown = Math.max( const Cooldown = Math.max(
0, SimulationMinimumEventInterval,
Number( Number(
Candidate.Mutation.Cooldown ?? Candidate.Mutation.Cooldown ??
0 0
) ) || 0
); );
SaveData.MutationCooldowns[ SaveData.MutationCooldowns[
GetMutationCooldownKey( GetMutationCooldownKey(
Candidate Candidate
) )
] = Date.now() + Cooldown; ] = AtTime + Cooldown;
} }
+144 -2
View File
@@ -15,6 +15,138 @@ const MaximumGardenNameLength = 32;
const StartingGardenWidth = 4; const StartingGardenWidth = 4;
const StartingGardenHeight = 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( function CreateGardenData(
Name = DefaultGardenName Name = DefaultGardenName
@@ -392,10 +524,16 @@ function CreateNewSave() {
return NormalizeSaveData({ return NormalizeSaveData({
Version: 5, Version: CurrentSaveVersion,
Revision: 0, Revision: 0,
LastSavedAt: Date.now(), LastSavedAt: Date.now(),
Simulation: {
LastProcessedAt: Date.now(),
RandomState:
CreateSimulationRandomState()
},
Currency: { Currency: {
Dew: StartingDew Dew: StartingDew
}, },
@@ -470,7 +608,7 @@ function NormalizeSaveData(
Number( Number(
SaveData.Version ?? 1 SaveData.Version ?? 1
), ),
5 CurrentSaveVersion
); );
SaveData.Revision = Number( SaveData.Revision = Number(
@@ -482,6 +620,10 @@ function NormalizeSaveData(
Date.now() Date.now()
); );
NormalizeSimulationData(
SaveData
);
SaveData.Currency ??= {}; SaveData.Currency ??= {};
SaveData.Currency.Dew = Number( SaveData.Currency.Dew = Number(
+447
View File
@@ -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;
}
+9 -3
View File
@@ -692,7 +692,9 @@ function TakeFertilizerUse(
function GetFutureSightCooldownRemaining( function GetFutureSightCooldownRemaining(
SaveData, SaveData,
Now = Date.now() Now = GetSavedSimulationTime(
SaveData
)
) { ) {
return Math.max( return Math.max(
0, 0,
@@ -705,7 +707,9 @@ function GetFutureSightCooldownRemaining(
function IsFutureSightReady( function IsFutureSightReady(
SaveData, SaveData,
Now = Date.now() Now = GetSavedSimulationTime(
SaveData
)
) { ) {
return GetFutureSightCooldownRemaining( return GetFutureSightCooldownRemaining(
SaveData, SaveData,
@@ -716,7 +720,9 @@ function IsFutureSightReady(
function StartFutureSightCooldown( function StartFutureSightCooldown(
SaveData, SaveData,
Now = Date.now() Now = GetSavedSimulationTime(
SaveData
)
) { ) {
SaveData.Tools ??= {}; SaveData.Tools ??= {};
SaveData.Tools.FutureSightCooldownUntil = SaveData.Tools.FutureSightCooldownUntil =