Files
srhtnin.github.io/Scripts/Garden/Garden.js
T
2026-08-30 15:36:16 +02:00

559 lines
8.5 KiB
JavaScript

let GameSave;
let SelectedSeed = "Rose";
const GardenUpdateInterval = 1000;
async function StartGame() {
GameSave = await LoadGame();
NormalizeSave();
RenderGame();
setInterval(
RenderGarden,
GardenUpdateInterval
);
}
function NormalizeSave() {
GameSave.Currency ??= {};
GameSave.Currency.Dew ??= 0;
GameSave.Seeds ??= {};
GameSave.Seeds.Unlocked ??= [
"Rose"
];
GameSave.Garden ??= {
Width: 3,
Height: 3,
Plots: []
};
const RequiredPlots =
GameSave.Garden.Width *
GameSave.Garden.Height;
while (
GameSave.Garden.Plots.length <
RequiredPlots
) {
GameSave.Garden.Plots.push(null);
}
}
function RenderGame() {
RenderCurrency();
RenderSeeds();
RenderGarden();
}
function RenderCurrency() {
const DewAmount =
document.getElementById(
"DewAmount"
);
DewAmount.textContent =
GameSave.Currency.Dew;
}
function RenderSeeds() {
const SeedList =
document.getElementById(
"SeedList"
);
SeedList.replaceChildren();
for (
const PlantId
of GameSave.Seeds.Unlocked
) {
const Plant =
Plants[PlantId];
if (Plant === undefined) {
continue;
}
const Button =
document.createElement(
"button"
);
Button.className =
"SeedButton";
Button.type = "button";
Button.textContent =
Plant.Name;
if (
PlantId ===
SelectedSeed
) {
Button.setAttribute(
"aria-current",
"true"
);
}
Button.addEventListener(
"click",
() => {
SelectedSeed =
PlantId;
RenderSeeds();
SetGardenMessage(
"Selected " +
Plant.Name +
"."
);
}
);
SeedList.appendChild(
Button
);
}
}
function RenderGarden() {
const GardenGrid =
document.getElementById(
"GardenGrid"
);
GardenGrid.style.setProperty(
"--GardenWidth",
GameSave.Garden.Width
);
GardenGrid.replaceChildren();
GameSave.Garden.Plots.forEach(
(Plot, PlotIndex) => {
const PlotElement =
CreatePlotElement(
Plot,
PlotIndex
);
GardenGrid.appendChild(
PlotElement
);
}
);
}
function CreatePlotElement(
Plot,
PlotIndex
) {
const Button =
document.createElement(
"button"
);
Button.className =
"GardenPlot";
Button.type = "button";
if (Plot === null) {
Button.classList.add(
"GardenPlotEmpty"
);
Button.title =
"Empty plot";
Button.addEventListener(
"click",
() => {
PlantSeed(
PlotIndex
);
}
);
return Button;
}
const Plant =
Plants[Plot.Plant];
if (Plant === undefined) {
Button.textContent = "?";
return Button;
}
const Stage =
GetPlantStage(
Plot,
Plant
);
const Image =
document.createElement(
"img"
);
Image.className =
"GardenPlant";
Image.alt =
Plant.Name +
" - " +
Stage;
SetPlantImage(
Image,
Plot,
Plant,
Stage
);
Button.appendChild(
Image
);
if (Stage === "Mature") {
Button.classList.add(
"GardenPlotMature"
);
Button.title =
"Harvest " +
Plant.Name;
Button.addEventListener(
"click",
() => {
HarvestPlant(
PlotIndex
);
}
);
} else {
const Progress =
GetPlantGrowthProgress(
Plot,
Plant
);
Button.title =
Plant.Name +
" - " +
Math.floor(
Progress * 100
) +
"% grown";
}
return Button;
}
async function PlantSeed(
PlotIndex
) {
if (
GameSave.Garden.Plots[
PlotIndex
] !== null
) {
return;
}
if (
!GameSave.Seeds.Unlocked
.includes(
SelectedSeed
)
) {
return;
}
GameSave.Garden.Plots[
PlotIndex
] = {
Plant: SelectedSeed,
AddedTags: [],
VisualVariant: null,
PlantedAt: Date.now()
};
SetGardenMessage(
"Planted " +
Plants[SelectedSeed].Name +
"."
);
RenderGarden();
await SaveGame(
GameSave
);
}
async function HarvestPlant(
PlotIndex
) {
const Plot =
GameSave.Garden.Plots[
PlotIndex
];
if (Plot === null) {
return;
}
const Plant =
Plants[Plot.Plant];
if (
GetPlantStage(
Plot,
Plant
) !== "Mature"
) {
return;
}
GiveReward(
Plant.Reward
);
GameSave.Garden.Plots[
PlotIndex
] = null;
SetGardenMessage(
"Harvested " +
Plant.Name +
" for " +
Plant.Reward.Amount +
" " +
Plant.Reward.Currency +
"."
);
RenderGame();
await SaveGame(
GameSave
);
}
function GiveReward(
Reward
) {
GameSave.Currency[
Reward.Currency
] ??= 0;
GameSave.Currency[
Reward.Currency
] += Reward.Amount;
}
function GetPlantGrowthProgress(
Plot,
Plant
) {
if (
Plant.GrowthTime <= 0
) {
return 1;
}
const Age =
Date.now() -
Plot.PlantedAt;
return Math.min(
Age /
Plant.GrowthTime,
1
);
}
function GetPlantStage(
Plot,
Plant
) {
const Progress =
GetPlantGrowthProgress(
Plot,
Plant
);
if (Progress < 1 / 3) {
return "Seed";
}
if (Progress < 2 / 3) {
return "Sprout";
}
return "Mature";
}
function GetPlantTags(
Plot,
Plant
) {
return [
...Plant.Tags,
...(Plot.AddedTags ?? [])
];
}
function GetPlantVariant(
Plot,
Plant
) {
if (
Plot.VisualVariant !==
null
) {
return Plot.VisualVariant;
}
const Tags =
GetPlantTags(
Plot,
Plant
);
const Variants = [
...Plant.Variants
].sort(
(A, B) =>
B.Priority -
A.Priority
);
for (
const Variant
of Variants
) {
const Matches =
Variant.Tags.every(
Tag =>
Tags.includes(
Tag
)
);
if (Matches) {
return Variant.Name;
}
}
return "Default";
}
function SetPlantImage(
Image,
Plot,
Plant,
Stage
) {
const Variant =
GetPlantVariant(
Plot,
Plant
);
const BasePath =
"/Assets/Img/Garden/Plants/" +
Plot.Plant +
"/" +
Variant +
"/";
/*
* Try the requested stage first.
*
* If that image doesn't exist,
* Mature.png becomes the fallback.
*/
const Candidates = [
BasePath + Stage + ".png",
BasePath + "Mature.png"
];
let CandidateIndex = 0;
Image.src =
Candidates[
CandidateIndex
];
Image.addEventListener(
"error",
() => {
CandidateIndex++;
if (
CandidateIndex <
Candidates.length
) {
Image.src =
Candidates[
CandidateIndex
];
return;
}
Image.remove();
}
);
}
function SetGardenMessage(
Message
) {
document.getElementById(
"GardenMessage"
).textContent = Message;
}
document.addEventListener(
"DOMContentLoaded",
StartGame
);