mirror of
https://git.srhtnin.garden/sarah/srhtnin.github.io.git
synced 2026-10-13 18:38:11 +02:00
Added the actual functional effects, not just the base.
This commit is contained in:
1 parent
9de8af0d2b
commit
1452a4a96d
6 files changed
+1102
-88
No files matched your search
@@ -98,13 +98,29 @@ function ValidateAdminFunctionalEffects(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const EffectType = String(
|
||||||
|
Effect.Type ?? ""
|
||||||
|
).trim();
|
||||||
|
|
||||||
ValidateAdminKey(
|
ValidateAdminKey(
|
||||||
String(
|
EffectType,
|
||||||
Effect.Type ?? ""
|
|
||||||
).trim(),
|
|
||||||
"Functional effect type"
|
"Functional effect type"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof IsKnownFunctionalEffectType ===
|
||||||
|
"function" &&
|
||||||
|
!IsKnownFunctionalEffectType(
|
||||||
|
EffectType
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
"Unknown functional effect type: " +
|
||||||
|
EffectType +
|
||||||
|
"."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (Effect.Key !== undefined) {
|
if (Effect.Key !== undefined) {
|
||||||
const EffectKey = String(
|
const EffectKey = String(
|
||||||
Effect.Key ?? ""
|
Effect.Key ?? ""
|
||||||
@@ -175,16 +191,41 @@ function ValidateAdminFunctionalEffects(
|
|||||||
"Functional effect amount must be a number."
|
"Functional effect amount must be a number."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
[
|
||||||
|
"GrowthAdvance",
|
||||||
|
"MutationChanceBonus",
|
||||||
|
"CooldownReduction",
|
||||||
|
"HarvestYieldBonus"
|
||||||
|
].includes(EffectType) &&
|
||||||
|
(Amount < 0 || Amount > 1)
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
"Functional percentage amounts must be between 0 and 1."
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
for (const BooleanField of [
|
||||||
Effect.RequireMature !== undefined &&
|
"RequireMature",
|
||||||
typeof Effect.RequireMature !==
|
"RequireMatureSource",
|
||||||
"boolean"
|
"RandomSource",
|
||||||
) {
|
"RandomTarget",
|
||||||
throw new Error(
|
"ExcludeFunctional",
|
||||||
"Functional effect RequireMature must be On or Off."
|
"ConsumeSeed"
|
||||||
);
|
]) {
|
||||||
|
if (
|
||||||
|
Effect[BooleanField] !== undefined &&
|
||||||
|
typeof Effect[BooleanField] !==
|
||||||
|
"boolean"
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
"Functional effect " +
|
||||||
|
BooleanField +
|
||||||
|
" must be On or Off."
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -180,8 +180,40 @@ function GetResolvedFunctionalEffectCooldown(
|
|||||||
Effect,
|
Effect,
|
||||||
AtTime
|
AtTime
|
||||||
) {
|
) {
|
||||||
return GetFunctionalEffectCooldown(
|
const BaseCooldown =
|
||||||
Effect
|
GetFunctionalEffectCooldown(
|
||||||
|
Effect
|
||||||
|
);
|
||||||
|
|
||||||
|
if (BaseCooldown <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Accelerator =
|
||||||
|
GetStrongestFunctionalEffectAffectingPlot(
|
||||||
|
SaveData,
|
||||||
|
GardenIndex,
|
||||||
|
PlotIndex,
|
||||||
|
"CooldownReduction",
|
||||||
|
AtTime
|
||||||
|
);
|
||||||
|
|
||||||
|
const Reduction = Math.max(
|
||||||
|
0,
|
||||||
|
Math.min(
|
||||||
|
1,
|
||||||
|
GetFunctionalEffectAmount(
|
||||||
|
Accelerator?.Effect
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return Math.max(
|
||||||
|
FunctionalEffectMinimumCooldown,
|
||||||
|
Math.floor(
|
||||||
|
BaseCooldown *
|
||||||
|
(1 - Reduction)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1002,6 +1034,833 @@ function GetStrongestFunctionalEffectAffectingAnyPlot(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function DoesFunctionalPlantHaveEffectType(
|
||||||
|
Plant,
|
||||||
|
Type
|
||||||
|
) {
|
||||||
|
return GetPlantFunctionalEffects(
|
||||||
|
Plant
|
||||||
|
).some(
|
||||||
|
Effect =>
|
||||||
|
String(Effect.Type) ===
|
||||||
|
String(Type)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function CanFunctionalPlantUseAssignedSeed(
|
||||||
|
Plant
|
||||||
|
) {
|
||||||
|
return DoesFunctionalPlantHaveEffectType(
|
||||||
|
Plant,
|
||||||
|
"Plant"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function GetFunctionalPercentageAmount(
|
||||||
|
Effect,
|
||||||
|
Fallback = 0
|
||||||
|
) {
|
||||||
|
return Math.max(
|
||||||
|
0,
|
||||||
|
Math.min(
|
||||||
|
1,
|
||||||
|
GetFunctionalEffectAmount(
|
||||||
|
Effect,
|
||||||
|
Fallback
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function GetFunctionalRandomItem(
|
||||||
|
SaveData,
|
||||||
|
Items
|
||||||
|
) {
|
||||||
|
if (!Array.isArray(Items) || Items.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Items.length === 1) {
|
||||||
|
return Items[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
const Random =
|
||||||
|
typeof GetSimulationRandom === "function"
|
||||||
|
? GetSimulationRandom(
|
||||||
|
SaveData
|
||||||
|
)
|
||||||
|
: Math.random();
|
||||||
|
|
||||||
|
return Items[
|
||||||
|
Math.min(
|
||||||
|
Items.length - 1,
|
||||||
|
Math.floor(
|
||||||
|
Random * Items.length
|
||||||
|
)
|
||||||
|
)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function AddFunctionalCurrencyReward(
|
||||||
|
SaveData,
|
||||||
|
Currency,
|
||||||
|
Amount
|
||||||
|
) {
|
||||||
|
const RewardAmount = Number(
|
||||||
|
Amount
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!Number.isFinite(RewardAmount) ||
|
||||||
|
RewardAmount <= 0
|
||||||
|
) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveData.Currency ??= {};
|
||||||
|
SaveData.Currency[Currency] ??= 0;
|
||||||
|
SaveData.Currency[Currency] += RewardAmount;
|
||||||
|
|
||||||
|
SaveData.Statistics ??= {};
|
||||||
|
SaveData.Statistics.CurrencyEarned ??= {};
|
||||||
|
SaveData.Statistics.CurrencyEarned[
|
||||||
|
Currency
|
||||||
|
] ??= 0;
|
||||||
|
SaveData.Statistics.CurrencyEarned[
|
||||||
|
Currency
|
||||||
|
] += RewardAmount;
|
||||||
|
|
||||||
|
return RewardAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function IsGardenPlotHarvestDisabled(
|
||||||
|
SaveData,
|
||||||
|
GardenIndex,
|
||||||
|
PlotIndex,
|
||||||
|
AtTime
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
GetStrongestFunctionalEffectAffectingPlot(
|
||||||
|
SaveData,
|
||||||
|
GardenIndex,
|
||||||
|
PlotIndex,
|
||||||
|
"DisableHarvest",
|
||||||
|
AtTime
|
||||||
|
) !== null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function IsGardenPlotMutationDisabled(
|
||||||
|
SaveData,
|
||||||
|
GardenIndex,
|
||||||
|
PlotIndex,
|
||||||
|
AtTime
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
GetStrongestFunctionalEffectAffectingPlot(
|
||||||
|
SaveData,
|
||||||
|
GardenIndex,
|
||||||
|
PlotIndex,
|
||||||
|
"DisableMutation",
|
||||||
|
AtTime
|
||||||
|
) !== null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function DoesMutationPatternInvolveDisableMutation(
|
||||||
|
SaveData,
|
||||||
|
GardenIndex,
|
||||||
|
OriginX,
|
||||||
|
OriginY,
|
||||||
|
Pattern
|
||||||
|
) {
|
||||||
|
const Garden =
|
||||||
|
GetFunctionalGarden(
|
||||||
|
SaveData,
|
||||||
|
GardenIndex
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
Garden === null ||
|
||||||
|
!Array.isArray(Pattern)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (
|
||||||
|
let LocalY = 0;
|
||||||
|
LocalY < Pattern.length;
|
||||||
|
LocalY++
|
||||||
|
) {
|
||||||
|
const Row = Pattern[LocalY];
|
||||||
|
|
||||||
|
if (!Array.isArray(Row)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (
|
||||||
|
let LocalX = 0;
|
||||||
|
LocalX < Row.length;
|
||||||
|
LocalX++
|
||||||
|
) {
|
||||||
|
const Matcher = Row[LocalX];
|
||||||
|
|
||||||
|
if (
|
||||||
|
Matcher === null ||
|
||||||
|
Matcher === "Any" ||
|
||||||
|
Matcher === "Empty"
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PlotIndex =
|
||||||
|
GetFunctionalPlotIndex(
|
||||||
|
Garden,
|
||||||
|
OriginX + LocalX,
|
||||||
|
OriginY + LocalY
|
||||||
|
);
|
||||||
|
|
||||||
|
if (PlotIndex === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Plot =
|
||||||
|
Garden.Plots?.[PlotIndex] ?? null;
|
||||||
|
|
||||||
|
if (Plot === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Plant =
|
||||||
|
Plants?.[Plot.Plant];
|
||||||
|
|
||||||
|
if (
|
||||||
|
Plant !== undefined &&
|
||||||
|
DoesFunctionalPlantHaveEffectType(
|
||||||
|
Plant,
|
||||||
|
"DisableMutation"
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function GetGardenPlotGrowthAdvance(
|
||||||
|
SaveData,
|
||||||
|
GardenIndex,
|
||||||
|
PlotIndex,
|
||||||
|
AtTime
|
||||||
|
) {
|
||||||
|
const Effect =
|
||||||
|
GetStrongestFunctionalEffectAffectingPlot(
|
||||||
|
SaveData,
|
||||||
|
GardenIndex,
|
||||||
|
PlotIndex,
|
||||||
|
"GrowthAdvance",
|
||||||
|
AtTime
|
||||||
|
);
|
||||||
|
|
||||||
|
return GetFunctionalPercentageAmount(
|
||||||
|
Effect?.Effect
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function GetGardenPlotHarvestYieldBonus(
|
||||||
|
SaveData,
|
||||||
|
GardenIndex,
|
||||||
|
PlotIndex,
|
||||||
|
AtTime
|
||||||
|
) {
|
||||||
|
const Effect =
|
||||||
|
GetStrongestFunctionalEffectAffectingPlot(
|
||||||
|
SaveData,
|
||||||
|
GardenIndex,
|
||||||
|
PlotIndex,
|
||||||
|
"HarvestYieldBonus",
|
||||||
|
AtTime
|
||||||
|
);
|
||||||
|
|
||||||
|
return GetFunctionalPercentageAmount(
|
||||||
|
Effect?.Effect
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function PlantGardenPlot(
|
||||||
|
SaveData,
|
||||||
|
GardenIndex,
|
||||||
|
PlotIndex,
|
||||||
|
PlantKey,
|
||||||
|
AtTime,
|
||||||
|
Options = {}
|
||||||
|
) {
|
||||||
|
const Garden =
|
||||||
|
GetFunctionalGarden(
|
||||||
|
SaveData,
|
||||||
|
GardenIndex
|
||||||
|
);
|
||||||
|
|
||||||
|
const Plant =
|
||||||
|
Plants?.[PlantKey];
|
||||||
|
|
||||||
|
if (
|
||||||
|
Garden === null ||
|
||||||
|
Plant === undefined ||
|
||||||
|
Garden.Plots?.[PlotIndex] !== null
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
Changed: false,
|
||||||
|
Planted: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const ConsumeSeed =
|
||||||
|
Options.ConsumeSeed === true;
|
||||||
|
|
||||||
|
if (ConsumeSeed) {
|
||||||
|
if (
|
||||||
|
typeof TakeSeed !== "function" ||
|
||||||
|
!TakeSeed(
|
||||||
|
SaveData,
|
||||||
|
Plant.Id,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
Changed: false,
|
||||||
|
Planted: false,
|
||||||
|
MissingSeed: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const GrowthAdvance =
|
||||||
|
Options.ApplyGrowthAdvance === false
|
||||||
|
? 0
|
||||||
|
: GetGardenPlotGrowthAdvance(
|
||||||
|
SaveData,
|
||||||
|
GardenIndex,
|
||||||
|
PlotIndex,
|
||||||
|
AtTime
|
||||||
|
);
|
||||||
|
|
||||||
|
const GrowthTime = Math.max(
|
||||||
|
0,
|
||||||
|
Number(
|
||||||
|
Plant.GrowthTime ?? 0
|
||||||
|
) || 0
|
||||||
|
);
|
||||||
|
|
||||||
|
const PlantedAt =
|
||||||
|
Number(AtTime) -
|
||||||
|
GrowthTime * GrowthAdvance;
|
||||||
|
|
||||||
|
const Rotation =
|
||||||
|
FunctionalDirectionOffsets[
|
||||||
|
Options.Rotation
|
||||||
|
] !== undefined
|
||||||
|
? Options.Rotation
|
||||||
|
: "East";
|
||||||
|
|
||||||
|
Garden.Plots[PlotIndex] = {
|
||||||
|
Plant: PlantKey,
|
||||||
|
PlantedAt,
|
||||||
|
Rotation
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
Changed: true,
|
||||||
|
Planted: true,
|
||||||
|
PlantKey,
|
||||||
|
PlotIndex,
|
||||||
|
GrowthAdvance,
|
||||||
|
PlantedAt
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function HarvestGardenPlot(
|
||||||
|
SaveData,
|
||||||
|
GardenIndex,
|
||||||
|
PlotIndex,
|
||||||
|
AtTime,
|
||||||
|
Options = {}
|
||||||
|
) {
|
||||||
|
const Garden =
|
||||||
|
GetFunctionalGarden(
|
||||||
|
SaveData,
|
||||||
|
GardenIndex
|
||||||
|
);
|
||||||
|
|
||||||
|
const Plot =
|
||||||
|
Garden?.Plots?.[PlotIndex] ?? null;
|
||||||
|
|
||||||
|
if (Plot === null) {
|
||||||
|
return {
|
||||||
|
Changed: false,
|
||||||
|
Harvested: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const Plant =
|
||||||
|
Plants?.[Plot.Plant];
|
||||||
|
|
||||||
|
if (
|
||||||
|
Plant === undefined ||
|
||||||
|
!IsFunctionalPlantMatureAtTime(
|
||||||
|
Plot,
|
||||||
|
Plant,
|
||||||
|
AtTime
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
Changed: false,
|
||||||
|
Harvested: false,
|
||||||
|
Immature: Plant !== undefined
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
Options.ExcludeFunctional === true &&
|
||||||
|
IsFunctionalPlant(
|
||||||
|
Plant
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
Changed: false,
|
||||||
|
Harvested: false,
|
||||||
|
Excluded: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
IsGardenPlotHarvestDisabled(
|
||||||
|
SaveData,
|
||||||
|
GardenIndex,
|
||||||
|
PlotIndex,
|
||||||
|
AtTime
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
Changed: false,
|
||||||
|
Harvested: false,
|
||||||
|
Disabled: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const BaseReward =
|
||||||
|
typeof GetPlantHarvestReward === "function"
|
||||||
|
? GetPlantHarvestReward(
|
||||||
|
SaveData,
|
||||||
|
Plant.Id
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (BaseReward === null) {
|
||||||
|
return {
|
||||||
|
Changed: false,
|
||||||
|
Harvested: false,
|
||||||
|
RewardUnavailable: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const YieldBonus =
|
||||||
|
GetGardenPlotHarvestYieldBonus(
|
||||||
|
SaveData,
|
||||||
|
GardenIndex,
|
||||||
|
PlotIndex,
|
||||||
|
AtTime
|
||||||
|
);
|
||||||
|
|
||||||
|
const RewardAmount = Math.ceil(
|
||||||
|
BaseReward *
|
||||||
|
(1 + YieldBonus)
|
||||||
|
);
|
||||||
|
|
||||||
|
AddFunctionalCurrencyReward(
|
||||||
|
SaveData,
|
||||||
|
"Dew",
|
||||||
|
RewardAmount
|
||||||
|
);
|
||||||
|
|
||||||
|
Garden.Plots[PlotIndex] = null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
Changed: true,
|
||||||
|
Harvested: true,
|
||||||
|
Plant,
|
||||||
|
PlantKey: Plot.Plant,
|
||||||
|
Rotation:
|
||||||
|
GetFunctionalPlotRotation(
|
||||||
|
Plot
|
||||||
|
),
|
||||||
|
RewardAmount,
|
||||||
|
YieldBonus
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function GetFunctionalEligibleSourceTargets(
|
||||||
|
SaveData,
|
||||||
|
Targets,
|
||||||
|
AtTime,
|
||||||
|
RequireMature = true
|
||||||
|
) {
|
||||||
|
return Targets.filter(
|
||||||
|
Target => {
|
||||||
|
const Plot = Target.Plot;
|
||||||
|
|
||||||
|
if (Plot === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Plant =
|
||||||
|
Plants?.[Plot.Plant];
|
||||||
|
|
||||||
|
if (Plant === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
!RequireMature ||
|
||||||
|
IsFunctionalPlantMatureAtTime(
|
||||||
|
Plot,
|
||||||
|
Plant,
|
||||||
|
AtTime
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function SelectFunctionalTarget(
|
||||||
|
SaveData,
|
||||||
|
Targets,
|
||||||
|
Random
|
||||||
|
) {
|
||||||
|
if (Targets.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Random === true
|
||||||
|
? GetFunctionalRandomItem(
|
||||||
|
SaveData,
|
||||||
|
Targets
|
||||||
|
)
|
||||||
|
: Targets[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function HandleFunctionalHarvest(
|
||||||
|
Context
|
||||||
|
) {
|
||||||
|
const Targets =
|
||||||
|
Context.GetTargets();
|
||||||
|
|
||||||
|
let Changed = false;
|
||||||
|
|
||||||
|
for (const Target of Targets) {
|
||||||
|
const Result =
|
||||||
|
HarvestGardenPlot(
|
||||||
|
Context.SaveData,
|
||||||
|
Target.GardenIndex,
|
||||||
|
Target.PlotIndex,
|
||||||
|
Context.AtTime,
|
||||||
|
{
|
||||||
|
ExcludeFunctional:
|
||||||
|
Context.Effect.ExcludeFunctional === true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Changed =
|
||||||
|
Changed ||
|
||||||
|
Result.Changed === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {Changed};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function HandleFunctionalDuplicate(
|
||||||
|
Context
|
||||||
|
) {
|
||||||
|
const SourceArea =
|
||||||
|
GetFunctionalEffectArea(
|
||||||
|
Context.Effect,
|
||||||
|
"Source",
|
||||||
|
"Behind"
|
||||||
|
);
|
||||||
|
|
||||||
|
const TargetArea =
|
||||||
|
GetFunctionalEffectArea(
|
||||||
|
Context.Effect,
|
||||||
|
"Target",
|
||||||
|
"Front"
|
||||||
|
);
|
||||||
|
|
||||||
|
const Sources =
|
||||||
|
GetFunctionalEligibleSourceTargets(
|
||||||
|
Context.SaveData,
|
||||||
|
GetFunctionalAreaTargets(
|
||||||
|
Context.SaveData,
|
||||||
|
Context.GardenIndex,
|
||||||
|
Context.PlotIndex,
|
||||||
|
SourceArea
|
||||||
|
),
|
||||||
|
Context.AtTime,
|
||||||
|
Context.Effect.RequireMatureSource !== false
|
||||||
|
);
|
||||||
|
|
||||||
|
const EmptyTargets =
|
||||||
|
GetFunctionalAreaTargets(
|
||||||
|
Context.SaveData,
|
||||||
|
Context.GardenIndex,
|
||||||
|
Context.PlotIndex,
|
||||||
|
TargetArea
|
||||||
|
).filter(
|
||||||
|
Target => Target.Plot === null
|
||||||
|
);
|
||||||
|
|
||||||
|
const Source =
|
||||||
|
SelectFunctionalTarget(
|
||||||
|
Context.SaveData,
|
||||||
|
Sources,
|
||||||
|
Context.Effect.RandomSource === true
|
||||||
|
);
|
||||||
|
|
||||||
|
const Target =
|
||||||
|
SelectFunctionalTarget(
|
||||||
|
Context.SaveData,
|
||||||
|
EmptyTargets,
|
||||||
|
Context.Effect.RandomTarget === true
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
Source === null ||
|
||||||
|
Target === null
|
||||||
|
) {
|
||||||
|
return {Changed: false};
|
||||||
|
}
|
||||||
|
|
||||||
|
return PlantGardenPlot(
|
||||||
|
Context.SaveData,
|
||||||
|
Context.GardenIndex,
|
||||||
|
Target.PlotIndex,
|
||||||
|
Source.Plot.Plant,
|
||||||
|
Context.AtTime,
|
||||||
|
{
|
||||||
|
ConsumeSeed: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function HandleFunctionalPlant(
|
||||||
|
Context
|
||||||
|
) {
|
||||||
|
const AssignedSeed =
|
||||||
|
typeof Context.Plot.AssignedSeed === "string"
|
||||||
|
? Context.Plot.AssignedSeed
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (
|
||||||
|
AssignedSeed === null ||
|
||||||
|
Plants?.[AssignedSeed] === undefined
|
||||||
|
) {
|
||||||
|
return {Changed: false};
|
||||||
|
}
|
||||||
|
|
||||||
|
const TargetArea =
|
||||||
|
GetFunctionalEffectArea(
|
||||||
|
Context.Effect,
|
||||||
|
"Target",
|
||||||
|
"Front"
|
||||||
|
);
|
||||||
|
|
||||||
|
const EmptyTargets =
|
||||||
|
GetFunctionalAreaTargets(
|
||||||
|
Context.SaveData,
|
||||||
|
Context.GardenIndex,
|
||||||
|
Context.PlotIndex,
|
||||||
|
TargetArea
|
||||||
|
).filter(
|
||||||
|
Target => Target.Plot === null
|
||||||
|
);
|
||||||
|
|
||||||
|
const Target =
|
||||||
|
SelectFunctionalTarget(
|
||||||
|
Context.SaveData,
|
||||||
|
EmptyTargets,
|
||||||
|
Context.Effect.RandomTarget === true
|
||||||
|
);
|
||||||
|
|
||||||
|
if (Target === null) {
|
||||||
|
return {Changed: false};
|
||||||
|
}
|
||||||
|
|
||||||
|
return PlantGardenPlot(
|
||||||
|
Context.SaveData,
|
||||||
|
Context.GardenIndex,
|
||||||
|
Target.PlotIndex,
|
||||||
|
AssignedSeed,
|
||||||
|
Context.AtTime,
|
||||||
|
{
|
||||||
|
ConsumeSeed: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function HandleFunctionalReplant(
|
||||||
|
Context
|
||||||
|
) {
|
||||||
|
const Targets =
|
||||||
|
Context.GetTargets();
|
||||||
|
|
||||||
|
let Changed = false;
|
||||||
|
|
||||||
|
for (const Target of Targets) {
|
||||||
|
const ExistingPlot = Target.Plot;
|
||||||
|
|
||||||
|
if (ExistingPlot === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ExistingPlant =
|
||||||
|
Plants?.[ExistingPlot.Plant];
|
||||||
|
|
||||||
|
if (ExistingPlant === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ConsumeSeed =
|
||||||
|
Context.Effect.ConsumeSeed !== false;
|
||||||
|
|
||||||
|
if (
|
||||||
|
ConsumeSeed &&
|
||||||
|
(
|
||||||
|
typeof GetSeedCount !== "function" ||
|
||||||
|
GetSeedCount(
|
||||||
|
Context.SaveData,
|
||||||
|
ExistingPlant.Id
|
||||||
|
) < 1
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Rotation =
|
||||||
|
GetFunctionalPlotRotation(
|
||||||
|
ExistingPlot
|
||||||
|
);
|
||||||
|
|
||||||
|
const HarvestResult =
|
||||||
|
HarvestGardenPlot(
|
||||||
|
Context.SaveData,
|
||||||
|
Target.GardenIndex,
|
||||||
|
Target.PlotIndex,
|
||||||
|
Context.AtTime,
|
||||||
|
{
|
||||||
|
ExcludeFunctional:
|
||||||
|
Context.Effect.ExcludeFunctional === true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!HarvestResult.Harvested) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PlantResult =
|
||||||
|
PlantGardenPlot(
|
||||||
|
Context.SaveData,
|
||||||
|
Target.GardenIndex,
|
||||||
|
Target.PlotIndex,
|
||||||
|
HarvestResult.PlantKey,
|
||||||
|
Context.AtTime,
|
||||||
|
{
|
||||||
|
ConsumeSeed,
|
||||||
|
Rotation
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Changed =
|
||||||
|
Changed ||
|
||||||
|
HarvestResult.Changed === true ||
|
||||||
|
PlantResult.Changed === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {Changed};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function SetFunctionalPlantAssignedSeed(
|
||||||
|
Plot,
|
||||||
|
PlantKey
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
Plot === null ||
|
||||||
|
typeof Plot !== "object" ||
|
||||||
|
Plants?.[PlantKey] === undefined
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Plot.AssignedSeed = PlantKey;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function ClearFunctionalPlantAssignedSeed(
|
||||||
|
Plot
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
Plot === null ||
|
||||||
|
typeof Plot !== "object"
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete Plot.AssignedSeed;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RegisterFunctionalEffectHandler(
|
||||||
|
"Harvest",
|
||||||
|
HandleFunctionalHarvest
|
||||||
|
);
|
||||||
|
|
||||||
|
RegisterFunctionalEffectHandler(
|
||||||
|
"Duplicate",
|
||||||
|
HandleFunctionalDuplicate
|
||||||
|
);
|
||||||
|
|
||||||
|
RegisterFunctionalEffectHandler(
|
||||||
|
"Plant",
|
||||||
|
HandleFunctionalPlant
|
||||||
|
);
|
||||||
|
|
||||||
|
RegisterFunctionalEffectHandler(
|
||||||
|
"Replant",
|
||||||
|
HandleFunctionalReplant
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
function CreateFunctionalProcessingResult() {
|
function CreateFunctionalProcessingResult() {
|
||||||
return {
|
return {
|
||||||
Attempted: false,
|
Attempted: false,
|
||||||
|
|||||||
+64
-56
@@ -2185,28 +2185,27 @@ async function PlantSeed(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (
|
const PlantResult =
|
||||||
!TakeSeed(
|
PlantGardenPlot(
|
||||||
GameSave,
|
GameSave,
|
||||||
SelectedSeedId
|
GameSave.ActiveGardenIndex,
|
||||||
)
|
PlotIndex,
|
||||||
) {
|
PlantKey,
|
||||||
|
ActionTime,
|
||||||
|
{
|
||||||
|
ConsumeSeed: true,
|
||||||
|
Rotation:
|
||||||
|
GardenPlotDefaultRotation
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!PlantResult.Planted) {
|
||||||
EnsureSelectedSeed();
|
EnsureSelectedSeed();
|
||||||
RenderSeeds();
|
RenderSeeds();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
GameSave.Garden.Plots[
|
|
||||||
PlotIndex
|
|
||||||
] = {
|
|
||||||
Plant: PlantKey,
|
|
||||||
PlantedAt: ActionTime,
|
|
||||||
Rotation: GardenPlotDefaultRotation
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
SetGardenMessage(
|
SetGardenMessage(
|
||||||
"Planted " +
|
"Planted " +
|
||||||
Plant.Name +
|
Plant.Name +
|
||||||
@@ -2307,40 +2306,47 @@ async function PlantManySeeds() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (
|
let PlantedCount = 0;
|
||||||
!TakeSeed(
|
|
||||||
GameSave,
|
|
||||||
SelectedSeedId,
|
|
||||||
PlantCount
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const PlantedAt = ActionTime;
|
|
||||||
|
|
||||||
for (
|
for (
|
||||||
let Index = 0;
|
let Index = 0;
|
||||||
Index < PlantCount;
|
Index < PlantCount;
|
||||||
Index++
|
Index++
|
||||||
) {
|
) {
|
||||||
GameSave.Garden.Plots[
|
const PlantResult =
|
||||||
EmptyPlotIndexes[Index]
|
PlantGardenPlot(
|
||||||
] = {
|
GameSave,
|
||||||
Plant: PlantKey,
|
GameSave.ActiveGardenIndex,
|
||||||
PlantedAt: PlantedAt,
|
EmptyPlotIndexes[Index],
|
||||||
Rotation: GardenPlotDefaultRotation
|
PlantKey,
|
||||||
};
|
ActionTime,
|
||||||
|
{
|
||||||
|
ConsumeSeed: true,
|
||||||
|
Rotation:
|
||||||
|
GardenPlotDefaultRotation
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!PlantResult.Planted) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlantedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PlantedCount <= 0) {
|
||||||
|
EnsureSelectedSeed();
|
||||||
|
RenderSeeds();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SetGardenMessage(
|
SetGardenMessage(
|
||||||
"Magic trowel planted " +
|
"Magic trowel planted " +
|
||||||
PlantCount.toLocaleString() +
|
PlantedCount.toLocaleString() +
|
||||||
" " +
|
" " +
|
||||||
Plant.Name +
|
Plant.Name +
|
||||||
(PlantCount === 1
|
(PlantedCount === 1
|
||||||
? " seed."
|
? " seed."
|
||||||
: " seeds.")
|
: " seeds.")
|
||||||
);
|
);
|
||||||
@@ -2391,38 +2397,40 @@ async function HarvestPlant(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const RewardAmount =
|
const HarvestResult =
|
||||||
GetPlantHarvestReward(
|
HarvestGardenPlot(
|
||||||
GameSave,
|
GameSave,
|
||||||
Plant.Id
|
GameSave.ActiveGardenIndex,
|
||||||
|
PlotIndex,
|
||||||
|
ActionTime
|
||||||
);
|
);
|
||||||
|
|
||||||
if (RewardAmount === null) {
|
if (!HarvestResult.Harvested) {
|
||||||
SetGardenMessage(
|
if (HarvestResult.Disabled) {
|
||||||
"Couldn't calculate the harvest reward for " +
|
SetGardenMessage(
|
||||||
Plant.Name +
|
Plant.Name +
|
||||||
"."
|
" can't be harvested right now."
|
||||||
);
|
);
|
||||||
|
} else if (
|
||||||
|
HarvestResult.RewardUnavailable
|
||||||
|
) {
|
||||||
|
SetGardenMessage(
|
||||||
|
"Couldn't calculate the harvest reward for " +
|
||||||
|
Plant.Name +
|
||||||
|
"."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderGame();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
GiveReward({
|
|
||||||
Currency: "Dew",
|
|
||||||
Amount: RewardAmount
|
|
||||||
});
|
|
||||||
|
|
||||||
GameSave.Garden.Plots[
|
|
||||||
PlotIndex
|
|
||||||
] = null;
|
|
||||||
|
|
||||||
|
|
||||||
SetGardenMessage(
|
SetGardenMessage(
|
||||||
"Harvested " +
|
"Harvested " +
|
||||||
Plant.Name +
|
Plant.Name +
|
||||||
" for " +
|
" for " +
|
||||||
RewardAmount +
|
HarvestResult.RewardAmount +
|
||||||
" Dew."
|
" Dew."
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ function CheckGardenMutations(
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
const Chance = Math.max(
|
const BaseChance = Math.max(
|
||||||
0,
|
0,
|
||||||
Math.min(
|
Math.min(
|
||||||
Number(
|
Number(
|
||||||
@@ -142,6 +142,31 @@ function CheckGardenMutations(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const Catalyst =
|
||||||
|
typeof GetStrongestFunctionalEffectAffectingAnyPlot ===
|
||||||
|
"function"
|
||||||
|
? GetStrongestFunctionalEffectAffectingAnyPlot(
|
||||||
|
SaveData,
|
||||||
|
SaveData.ActiveGardenIndex ?? 0,
|
||||||
|
Candidate.Cells,
|
||||||
|
"MutationChanceBonus",
|
||||||
|
AtTime
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const CatalystBonus =
|
||||||
|
typeof GetFunctionalPercentageAmount ===
|
||||||
|
"function"
|
||||||
|
? GetFunctionalPercentageAmount(
|
||||||
|
Catalyst?.Effect
|
||||||
|
)
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
const Chance = Math.min(
|
||||||
|
1,
|
||||||
|
BaseChance + CatalystBonus
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
const Succeeded =
|
const Succeeded =
|
||||||
GetSimulationRandom(
|
GetSimulationRandom(
|
||||||
@@ -386,6 +411,42 @@ function CreateMutationCandidate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const GardenIndex =
|
||||||
|
Math.max(
|
||||||
|
0,
|
||||||
|
Number(
|
||||||
|
SaveData.ActiveGardenIndex ?? 0
|
||||||
|
) || 0
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof IsGardenPlotMutationDisabled ===
|
||||||
|
"function" &&
|
||||||
|
Cells.some(
|
||||||
|
PlotIndex =>
|
||||||
|
IsGardenPlotMutationDisabled(
|
||||||
|
SaveData,
|
||||||
|
GardenIndex,
|
||||||
|
PlotIndex,
|
||||||
|
AtTime
|
||||||
|
)
|
||||||
|
) &&
|
||||||
|
!(
|
||||||
|
typeof DoesMutationPatternInvolveDisableMutation ===
|
||||||
|
"function" &&
|
||||||
|
DoesMutationPatternInvolveDisableMutation(
|
||||||
|
SaveData,
|
||||||
|
GardenIndex,
|
||||||
|
OriginX,
|
||||||
|
OriginY,
|
||||||
|
Orientation.Pattern
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
Mutation: Mutation,
|
Mutation: Mutation,
|
||||||
|
|
||||||
@@ -789,6 +850,23 @@ function ApplyMutationResult(
|
|||||||
: "East";
|
: "East";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
NewPlot !== null &&
|
||||||
|
typeof NewPlot === "object" &&
|
||||||
|
ExistingPlot !== null &&
|
||||||
|
typeof ExistingPlot === "object" &&
|
||||||
|
typeof ExistingPlot.AssignedSeed ===
|
||||||
|
"string" &&
|
||||||
|
typeof CanFunctionalPlantUseAssignedSeed ===
|
||||||
|
"function" &&
|
||||||
|
CanFunctionalPlantUseAssignedSeed(
|
||||||
|
Plants?.[NewPlot.Plant]
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
NewPlot.AssignedSeed =
|
||||||
|
ExistingPlot.AssignedSeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
SaveData.Garden.Plots[
|
SaveData.Garden.Plots[
|
||||||
PlotIndex
|
PlotIndex
|
||||||
|
|||||||
@@ -288,6 +288,17 @@ function NormalizeGardenData(
|
|||||||
? Plot.Rotation
|
? Plot.Rotation
|
||||||
: "East";
|
: "East";
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof Plot.AssignedSeed ===
|
||||||
|
"string" &&
|
||||||
|
Plot.AssignedSeed.trim().length > 0
|
||||||
|
) {
|
||||||
|
Plot.AssignedSeed =
|
||||||
|
Plot.AssignedSeed.trim();
|
||||||
|
} else {
|
||||||
|
delete Plot.AssignedSeed;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
Plot.FunctionalCooldowns === null ||
|
Plot.FunctionalCooldowns === null ||
|
||||||
typeof Plot.FunctionalCooldowns !==
|
typeof Plot.FunctionalCooldowns !==
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
const SimulationMinimumEventInterval = 1000;
|
const SimulationMinimumEventInterval = 1000;
|
||||||
const MaximumSimulationSteps = 250000;
|
const MaximumSimulationSteps = 250000;
|
||||||
|
const MaximumSimulationSameTimePasses = 100;
|
||||||
|
|
||||||
|
|
||||||
function GetSimulationTime(
|
function GetSimulationTime(
|
||||||
@@ -410,31 +411,47 @@ function AdvanceGameSimulation(
|
|||||||
) {
|
) {
|
||||||
Result.Steps = Step + 1;
|
Result.Steps = Step + 1;
|
||||||
|
|
||||||
const MutationResult =
|
for (
|
||||||
CheckAllGardenMutationsAtTime(
|
let SameTimePass = 0;
|
||||||
SaveData,
|
SameTimePass <
|
||||||
CurrentTime
|
MaximumSimulationSameTimePasses;
|
||||||
);
|
SameTimePass++
|
||||||
|
|
||||||
MergeSimulationMutationResult(
|
|
||||||
Result,
|
|
||||||
MutationResult
|
|
||||||
);
|
|
||||||
|
|
||||||
if (
|
|
||||||
typeof ProcessFunctionalEffectsAtTime ===
|
|
||||||
"function"
|
|
||||||
) {
|
) {
|
||||||
const FunctionalResult =
|
const MutationResult =
|
||||||
ProcessFunctionalEffectsAtTime(
|
CheckAllGardenMutationsAtTime(
|
||||||
SaveData,
|
SaveData,
|
||||||
CurrentTime
|
CurrentTime
|
||||||
);
|
);
|
||||||
|
|
||||||
MergeSimulationFunctionalResult(
|
MergeSimulationMutationResult(
|
||||||
Result,
|
Result,
|
||||||
FunctionalResult
|
MutationResult
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let FunctionalResult = null;
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof ProcessFunctionalEffectsAtTime ===
|
||||||
|
"function"
|
||||||
|
) {
|
||||||
|
FunctionalResult =
|
||||||
|
ProcessFunctionalEffectsAtTime(
|
||||||
|
SaveData,
|
||||||
|
CurrentTime
|
||||||
|
);
|
||||||
|
|
||||||
|
MergeSimulationFunctionalResult(
|
||||||
|
Result,
|
||||||
|
FunctionalResult
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
MutationResult.Changed !== true &&
|
||||||
|
FunctionalResult?.Changed !== true
|
||||||
|
) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CurrentTime >= SafeTargetTime) {
|
if (CurrentTime >= SafeTargetTime) {
|
||||||
|
|||||||
Reference in new issue
Block a user