mirror of
https://git.srhtnin.garden/sarah/srhtnin.github.io.git
synced 2026-10-13 18:38:11 +02:00
559 lines
8.5 KiB
JavaScript
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
|
|
);
|