mirror of
https://git.srhtnin.garden/sarah/srhtnin.github.io.git
synced 2026-10-14 02:38:12 +02:00
Added effects engine.
This commit is contained in:
1 parent
132aba0b9a
commit
9de8af0d2b
9 files changed
+1684
-12
No files matched your search
@@ -58,6 +58,153 @@ function ValidateAdminKey(
|
||||
}
|
||||
|
||||
|
||||
function ValidateAdminFunctionalEffects(
|
||||
Effects
|
||||
) {
|
||||
const Functional =
|
||||
Effects?.Functional;
|
||||
|
||||
if (Functional === undefined) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Array.isArray(Functional)) {
|
||||
throw new Error(
|
||||
"Effects.Functional must be an array."
|
||||
);
|
||||
}
|
||||
|
||||
if (Functional.length > 32) {
|
||||
throw new Error(
|
||||
"Effects.Functional cannot contain more than 32 effects."
|
||||
);
|
||||
}
|
||||
|
||||
const SeenKeys = new Set();
|
||||
|
||||
for (
|
||||
let EffectIndex = 0;
|
||||
EffectIndex < Functional.length;
|
||||
EffectIndex++
|
||||
) {
|
||||
const Effect =
|
||||
Functional[EffectIndex];
|
||||
|
||||
if (!IsAdminPlainObject(Effect)) {
|
||||
throw new Error(
|
||||
"Functional effect " +
|
||||
(EffectIndex + 1) +
|
||||
" must be an object."
|
||||
);
|
||||
}
|
||||
|
||||
ValidateAdminKey(
|
||||
String(
|
||||
Effect.Type ?? ""
|
||||
).trim(),
|
||||
"Functional effect type"
|
||||
);
|
||||
|
||||
if (Effect.Key !== undefined) {
|
||||
const EffectKey = String(
|
||||
Effect.Key ?? ""
|
||||
).trim();
|
||||
|
||||
ValidateAdminKey(
|
||||
EffectKey,
|
||||
"Functional effect key"
|
||||
);
|
||||
|
||||
if (SeenKeys.has(EffectKey)) {
|
||||
throw new Error(
|
||||
"Functional effect keys must be unique within a plant."
|
||||
);
|
||||
}
|
||||
|
||||
SeenKeys.add(EffectKey);
|
||||
}
|
||||
|
||||
for (const Field of [
|
||||
"Area",
|
||||
"Source",
|
||||
"Target"
|
||||
]) {
|
||||
if (Effect[Field] === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const Area = String(
|
||||
Effect[Field]
|
||||
);
|
||||
|
||||
if (
|
||||
typeof FunctionalEffectAreas !==
|
||||
"undefined" &&
|
||||
!FunctionalEffectAreas.includes(
|
||||
Area
|
||||
)
|
||||
) {
|
||||
throw new Error(
|
||||
"Functional effect " +
|
||||
Field.toLowerCase() +
|
||||
" must be Front, Behind, Cardinal, Adjacent or Farm."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (Effect.Cooldown !== undefined) {
|
||||
ValidateAdminInteger(
|
||||
Number(
|
||||
Effect.Cooldown
|
||||
),
|
||||
"Functional effect cooldown",
|
||||
typeof FunctionalEffectMinimumCooldown !==
|
||||
"undefined"
|
||||
? FunctionalEffectMinimumCooldown
|
||||
: 1000
|
||||
);
|
||||
}
|
||||
|
||||
if (Effect.Amount !== undefined) {
|
||||
const Amount = Number(
|
||||
Effect.Amount
|
||||
);
|
||||
|
||||
if (!Number.isFinite(Amount)) {
|
||||
throw new Error(
|
||||
"Functional effect amount must be a number."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
Effect.RequireMature !== undefined &&
|
||||
typeof Effect.RequireMature !==
|
||||
"boolean"
|
||||
) {
|
||||
throw new Error(
|
||||
"Functional effect RequireMature must be On or Off."
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
typeof IsFunctionalEffectActive ===
|
||||
"function" &&
|
||||
IsFunctionalEffectActive(
|
||||
Effect
|
||||
) &&
|
||||
Effect.Cooldown === undefined
|
||||
) {
|
||||
throw new Error(
|
||||
"Active functional effects need a cooldown."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function ValidateAdminPlant(
|
||||
Plant,
|
||||
Catalogue,
|
||||
@@ -205,6 +352,10 @@ function ValidateAdminPlant(
|
||||
);
|
||||
}
|
||||
|
||||
ValidateAdminFunctionalEffects(
|
||||
Plant.Effects
|
||||
);
|
||||
|
||||
if (
|
||||
Plant.DirectionalSprites !== undefined &&
|
||||
typeof Plant.DirectionalSprites !== "boolean"
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1669,11 +1669,35 @@ function CreatePlotElement(
|
||||
Plant
|
||||
);
|
||||
|
||||
const CooldownState =
|
||||
Progress >= 1 &&
|
||||
typeof GetFunctionalPlantCooldownDisplayState ===
|
||||
"function"
|
||||
? GetFunctionalPlantCooldownDisplayState(
|
||||
Plot,
|
||||
Plant,
|
||||
GetSimulationTime(
|
||||
GameSave
|
||||
)
|
||||
)
|
||||
: null;
|
||||
|
||||
const BorderProgress =
|
||||
CooldownState === null
|
||||
? Progress
|
||||
: CooldownState.Progress;
|
||||
|
||||
Tile.style.setProperty(
|
||||
"--GrowthProgress",
|
||||
`${Progress * 100}%`
|
||||
`${BorderProgress * 100}%`
|
||||
);
|
||||
|
||||
if (CooldownState !== null) {
|
||||
Tile.classList.add(
|
||||
"GardenPlotFunctionalCooldown"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const ImagePath =
|
||||
GetPlantImage(
|
||||
@@ -2004,6 +2028,31 @@ function CreateGardenPlotTimer(
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
Progress >= 1 &&
|
||||
typeof GetFunctionalPlantCooldownDisplayState ===
|
||||
"function"
|
||||
) {
|
||||
const CooldownState =
|
||||
GetFunctionalPlantCooldownDisplayState(
|
||||
Plot,
|
||||
Plant,
|
||||
GetSimulationTime(
|
||||
GameSave
|
||||
)
|
||||
);
|
||||
|
||||
if (CooldownState !== null) {
|
||||
Timer.textContent =
|
||||
FormatGardenRemainingTime(
|
||||
CooldownState.Remaining
|
||||
);
|
||||
|
||||
return Timer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (Progress >= 1) {
|
||||
Timer.textContent =
|
||||
"Ready";
|
||||
|
||||
+72
-1
@@ -15,7 +15,7 @@ const MaximumGardenNameLength = 32;
|
||||
const StartingGardenWidth = 4;
|
||||
const StartingGardenHeight = 4;
|
||||
|
||||
const CurrentSaveVersion = 7;
|
||||
const CurrentSaveVersion = 8;
|
||||
|
||||
|
||||
function CreateSimulationRandomState() {
|
||||
@@ -288,6 +288,77 @@ function NormalizeGardenData(
|
||||
? Plot.Rotation
|
||||
: "East";
|
||||
|
||||
if (
|
||||
Plot.FunctionalCooldowns === null ||
|
||||
typeof Plot.FunctionalCooldowns !==
|
||||
"object" ||
|
||||
Array.isArray(
|
||||
Plot.FunctionalCooldowns
|
||||
)
|
||||
) {
|
||||
Plot.FunctionalCooldowns = {};
|
||||
}
|
||||
|
||||
for (
|
||||
const [CooldownKey, CooldownValue]
|
||||
of Object.entries(
|
||||
Plot.FunctionalCooldowns
|
||||
)
|
||||
) {
|
||||
let StartedAt = 0;
|
||||
let Until = 0;
|
||||
|
||||
if (
|
||||
CooldownValue !== null &&
|
||||
typeof CooldownValue ===
|
||||
"object" &&
|
||||
!Array.isArray(
|
||||
CooldownValue
|
||||
)
|
||||
) {
|
||||
StartedAt = Math.max(
|
||||
0,
|
||||
Math.floor(
|
||||
Number(
|
||||
CooldownValue.StartedAt ?? 0
|
||||
) || 0
|
||||
)
|
||||
);
|
||||
|
||||
Until = Math.max(
|
||||
StartedAt,
|
||||
Math.floor(
|
||||
Number(
|
||||
CooldownValue.Until ?? 0
|
||||
) || 0
|
||||
)
|
||||
);
|
||||
} else {
|
||||
Until = Math.max(
|
||||
0,
|
||||
Math.floor(
|
||||
Number(
|
||||
CooldownValue
|
||||
) || 0
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (Until <= 0) {
|
||||
delete Plot.FunctionalCooldowns[
|
||||
CooldownKey
|
||||
];
|
||||
continue;
|
||||
}
|
||||
|
||||
Plot.FunctionalCooldowns[
|
||||
CooldownKey
|
||||
] = {
|
||||
StartedAt,
|
||||
Until
|
||||
};
|
||||
}
|
||||
|
||||
delete Plot.VisualVariant;
|
||||
delete Plot.AddedTags;
|
||||
}
|
||||
|
||||
@@ -91,6 +91,28 @@ function MergeSimulationMutationResult(
|
||||
}
|
||||
|
||||
|
||||
function MergeSimulationFunctionalResult(
|
||||
Target,
|
||||
Source
|
||||
) {
|
||||
if (Source === null || Source === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
Target.FunctionalAttempted =
|
||||
Target.FunctionalAttempted ||
|
||||
Source.Attempted === true;
|
||||
|
||||
Target.Changed =
|
||||
Target.Changed ||
|
||||
Source.Changed === true;
|
||||
|
||||
Target.FunctionalActivations.push(
|
||||
...(Source.Activations ?? [])
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function CreateSimulationResult(
|
||||
StartTime,
|
||||
TargetTime
|
||||
@@ -100,6 +122,7 @@ function CreateSimulationResult(
|
||||
TargetTime,
|
||||
Advanced: TargetTime > StartTime,
|
||||
Attempted: false,
|
||||
FunctionalAttempted: false,
|
||||
Changed: false,
|
||||
Dirty: false,
|
||||
CaughtUp: StartTime >= TargetTime,
|
||||
@@ -107,7 +130,8 @@ function CreateSimulationResult(
|
||||
Steps: 0,
|
||||
SuccessfulMutations: [],
|
||||
NewlyDiscoveredMutations: [],
|
||||
NewlyDiscoveredPlants: []
|
||||
NewlyDiscoveredPlants: [],
|
||||
FunctionalActivations: []
|
||||
};
|
||||
}
|
||||
|
||||
@@ -135,6 +159,7 @@ function FinalizeSimulationResult(
|
||||
|
||||
Result.Dirty =
|
||||
Result.Attempted ||
|
||||
Result.FunctionalAttempted ||
|
||||
Result.Changed;
|
||||
|
||||
return Result;
|
||||
@@ -324,18 +349,27 @@ function GetNextSimulationEventTime(
|
||||
TargetTime
|
||||
);
|
||||
|
||||
if (MaturityTime === null) {
|
||||
return MutationTime;
|
||||
}
|
||||
const FunctionalTime =
|
||||
typeof GetNextFunctionalEffectEventTime ===
|
||||
"function"
|
||||
? GetNextFunctionalEffectEventTime(
|
||||
SaveData,
|
||||
CurrentTime,
|
||||
TargetTime
|
||||
)
|
||||
: null;
|
||||
|
||||
if (MutationTime === null) {
|
||||
return MaturityTime;
|
||||
}
|
||||
|
||||
return Math.min(
|
||||
const EventTimes = [
|
||||
MaturityTime,
|
||||
MutationTime
|
||||
MutationTime,
|
||||
FunctionalTime
|
||||
].filter(
|
||||
Time => Time !== null
|
||||
);
|
||||
|
||||
return EventTimes.length === 0
|
||||
? null
|
||||
: Math.min(...EventTimes);
|
||||
}
|
||||
|
||||
|
||||
@@ -387,6 +421,22 @@ function AdvanceGameSimulation(
|
||||
MutationResult
|
||||
);
|
||||
|
||||
if (
|
||||
typeof ProcessFunctionalEffectsAtTime ===
|
||||
"function"
|
||||
) {
|
||||
const FunctionalResult =
|
||||
ProcessFunctionalEffectsAtTime(
|
||||
SaveData,
|
||||
CurrentTime
|
||||
);
|
||||
|
||||
MergeSimulationFunctionalResult(
|
||||
Result,
|
||||
FunctionalResult
|
||||
);
|
||||
}
|
||||
|
||||
if (CurrentTime >= SafeTargetTime) {
|
||||
Result.CaughtUp = true;
|
||||
break;
|
||||
|
||||
Reference in new issue
Block a user