Added farm economics upgrades and edited Garden UI to be a bit cleaner.

This commit is contained in:
sarah committed 2026-09-03 20:31:28 +02:00
1 parent b0bc6ecc03
commit cce44b1b0e
7 files changed
+673 -70

No files matched your search

+97 -43
View File
@@ -17,63 +17,117 @@ $PageSection = "Garden";
<main class="MainContainer"> <main class="MainContainer">
<h1>Garden</h1> <h1>Garden</h1>
<div class="StatusPanel"> <div
<div class="GardenStatusLine"> id="GardenInfoPanel"
<strong>Dew:</strong> class="StatusPanel GardenInfoPanel"
<span id="DewAmount">0</span> >
<div class="GardenInfoRow GardenInfoTopRow">
<div class="GardenInfoSection GardenInfoPrimarySection">
<div class="GardenStatusLine">
<strong>Dew:</strong>
<span id="DewAmount">0</span>
</div>
<div
id="NextHarvestLine"
class="GardenStatusLine"
>
<strong>Next harvest:</strong>
<span id="NextHarvest">Nothing planted</span>
</div>
</div>
<div
class="GardenInfoSection GardenInfoReservedSection"
aria-hidden="true"
></div>
</div> </div>
<div <div
id="NextHarvestLine" id="GardenInfoDetailRow"
class="GardenStatusLine" class="GardenInfoRow GardenInfoDetailRow"
>
<strong>Next harvest:</strong>
<span id="NextHarvest">Nothing planted</span>
</div>
<div
id="GardenOverviewDetails"
class="GardenOverviewDetails"
hidden hidden
> >
<div <div
id="GardenSizeOverviewLine" id="GardenOverviewSection"
class="GardenStatusLine" class="GardenInfoSection GardenOverviewSection"
> >
<strong>Garden size:</strong> <div
<span id="GardenSizeOverview">3×3 (9)</span> id="GardenOverviewDetails"
class="GardenOverviewDetails"
hidden
>
<div
id="GardenSizeOverviewLine"
class="GardenStatusLine"
>
<strong>Garden size:</strong>
<span id="GardenSizeOverview">3×3 (9)</span>
</div>
<div
id="EmptyPlotsOverviewLine"
class="GardenStatusLine"
>
<strong>Empty plots:</strong>
<span id="EmptyPlotsOverview">9/9</span>
</div>
<div
id="PlantedPlotsOverviewLine"
class="GardenStatusLine"
>
<strong>Planted plots:</strong>
<span id="PlantedPlotsOverview">0/9</span>
</div>
<div
id="GrowingPlotsOverviewLine"
class="GardenStatusLine"
>
<strong>Growing:</strong>
<span id="GrowingPlotsOverview">0/9</span>
</div>
<div
id="ReadyPlotsOverviewLine"
class="GardenStatusLine"
>
<strong>Ready to harvest:</strong>
<span id="ReadyPlotsOverview">0/9</span>
</div>
</div>
</div> </div>
<div <div
id="EmptyPlotsOverviewLine" id="GardenEconomySection"
class="GardenStatusLine" class="GardenInfoSection GardenEconomySection"
> >
<strong>Empty plots:</strong> <div
<span id="EmptyPlotsOverview">9/9</span> id="GardenEconomyDetails"
</div> class="GardenEconomyDetails"
hidden
>
<div class="GardenStatusLine">
<strong>Dew invested:</strong>
<span id="GardenDewInvested">0</span>
</div>
<div <div class="GardenStatusLine">
id="PlantedPlotsOverviewLine" <strong>Harvest value:</strong>
class="GardenStatusLine" <span id="GardenHarvestValue">0</span>
> </div>
<strong>Planted plots:</strong>
<span id="PlantedPlotsOverview">0/9</span>
</div>
<div <div class="GardenStatusLine">
id="GrowingPlotsOverviewLine" <strong>Net profit:</strong>
class="GardenStatusLine" <span id="GardenNetProfit">0</span>
> </div>
<strong>Growing:</strong>
<span id="GrowingPlotsOverview">0/9</span>
</div>
<div <div class="GardenStatusLine">
id="ReadyPlotsOverviewLine" <strong>Farm DPH:</strong>
class="GardenStatusLine" <span id="GardenDewPerHour">0 Dew</span>
> </div>
<strong>Ready to harvest:</strong> </div>
<span id="ReadyPlotsOverview">0/9</span>
</div> </div>
</div> </div>
</div> </div>
+80
View File
@@ -717,6 +717,86 @@ function GetPlantDewPerHour(
} }
function GetGardenEconomySummary(
SaveData,
Garden = SaveData?.Garden
) {
if (
Garden === null ||
Garden === undefined ||
!Array.isArray(
Garden.Plots
)
) {
return null;
}
let DewInvested = 0;
let HarvestValue = 0;
let NetProfit = 0;
let DewPerHour = 0;
for (
const Plot
of Garden.Plots
) {
if (Plot === null) {
continue;
}
const Plant =
Plants[Plot.Plant];
if (Plant === undefined) {
return null;
}
const Cost =
GetPlantShopCost(
SaveData,
Plant.Id
);
const Reward =
GetPlantHarvestReward(
SaveData,
Plant.Id
);
const PlantDewPerHour =
GetPlantDewPerHour(
SaveData,
Plant.Id
);
if (
Cost === null ||
Reward === null ||
PlantDewPerHour === null
) {
return null;
}
DewInvested += Cost;
HarvestValue += Reward;
NetProfit +=
Reward - Cost;
DewPerHour +=
PlantDewPerHour;
}
return {
DewInvested,
HarvestValue,
NetProfit,
DewPerHour
};
}
function FormatDewPerHour( function FormatDewPerHour(
DewPerHour DewPerHour
) { ) {
+224 -24
View File
@@ -6,6 +6,42 @@ let MutationCheckPending = false;
const GardenUpdateInterval = 1000; const GardenUpdateInterval = 1000;
const GardenBorderColours = [
"var(--Rosewater)",
"var(--Flamingo)",
"var(--Pink)",
"var(--Mauve)",
"var(--Red)",
"var(--Maroon)",
"var(--Peach)",
"var(--Yellow)",
"var(--Green)",
"var(--Teal)",
"var(--Sky)",
"var(--Sapphire)",
"var(--Blue)",
"var(--Lavender)"
];
function GetGardenBorderColour(
GardenIndex
) {
const ColourCount =
GardenBorderColours.length;
const NormalizedIndex =
(
Number(GardenIndex) %
ColourCount +
ColourCount
) % ColourCount;
return GardenBorderColours[
NormalizedIndex
];
}
async function StartGame() { async function StartGame() {
try { try {
@@ -62,6 +98,8 @@ function RenderGame() {
RenderGarden(); RenderGarden();
RenderNextHarvest(); RenderNextHarvest();
RenderGardenOverview(); RenderGardenOverview();
RenderGardenEconomy();
RenderGardenInfoLayout();
} }
@@ -217,12 +255,66 @@ function RenderGardenSelector() {
const GardenIndex = const GardenIndex =
GameSave.ActiveGardenIndex; GameSave.ActiveGardenIndex;
const HasPreviousGarden =
GardenIndex > 0;
const HasNextGarden =
GardenIndex <
GardenCount - 1;
PreviousButton.disabled = PreviousButton.disabled =
GardenIndex <= 0; !HasPreviousGarden;
NextButton.disabled = NextButton.disabled =
GardenIndex >= !HasNextGarden;
GardenCount - 1;
PreviousButton.style.setProperty(
"--GardenSelectorBorderColour",
HasPreviousGarden
? GetGardenBorderColour(
GardenIndex - 1
)
: "var(--Surface2)"
);
NameInput.style.setProperty(
"--GardenSelectorBorderColour",
GetGardenBorderColour(
GardenIndex
)
);
NextButton.style.setProperty(
"--GardenSelectorBorderColour",
HasNextGarden
? GetGardenBorderColour(
GardenIndex + 1
)
: "var(--Surface2)"
);
PreviousButton.setAttribute(
"aria-label",
HasPreviousGarden
? "Previous Garden: " +
GameSave.Gardens[
GardenIndex - 1
].Name
: "No previous Garden"
);
NextButton.setAttribute(
"aria-label",
HasNextGarden
? "Next Garden: " +
GameSave.Gardens[
GardenIndex + 1
].Name
: "No next Garden"
);
if ( if (
document.activeElement !== document.activeElement !==
@@ -555,29 +647,11 @@ function RenderGarden() {
GameSave.Garden.Width GameSave.Garden.Width
); );
const GardenBorderColours = [
"var(--Rosewater)",
"var(--Flamingo)",
"var(--Pink)",
"var(--Mauve)",
"var(--Red)",
"var(--Maroon)",
"var(--Peach)",
"var(--Yellow)",
"var(--Green)",
"var(--Teal)",
"var(--Sky)",
"var(--Sapphire)",
"var(--Blue)",
"var(--Lavender)"
];
GardenPanel.style.setProperty( GardenPanel.style.setProperty(
"--GardenBorderColour", "--GardenBorderColour",
GardenBorderColours[ GetGardenBorderColour(
GameSave.ActiveGardenIndex % GameSave.ActiveGardenIndex
GardenBorderColours.length )
]
); );
GardenGrid.replaceChildren(); GardenGrid.replaceChildren();
@@ -1196,6 +1270,8 @@ async function GardenTick() {
RenderGarden(); RenderGarden();
RenderNextHarvest(); RenderNextHarvest();
RenderGardenOverview(); RenderGardenOverview();
RenderGardenEconomy();
RenderGardenInfoLayout();
if (MutationCheckPending) { if (MutationCheckPending) {
@@ -1770,6 +1846,130 @@ function FormatRemainingTime(
} }
function RenderGardenEconomy() {
const Details =
document.getElementById(
"GardenEconomyDetails"
);
if (Details === null) {
return;
}
if (
!HasGardenEconomyUpgrade(
GameSave
)
) {
Details.hidden = true;
return;
}
Details.hidden = false;
const Summary =
GetGardenEconomySummary(
GameSave
);
const Values = {
GardenDewInvested:
Summary === null
? "Unavailable"
: Summary.DewInvested
.toLocaleString(),
GardenHarvestValue:
Summary === null
? "Unavailable"
: Summary.HarvestValue
.toLocaleString(),
GardenNetProfit:
Summary === null
? "Unavailable"
: Summary.NetProfit
.toLocaleString(),
GardenDewPerHour:
Summary === null
? "Unavailable"
: FormatDewPerHour(
Summary.DewPerHour
)
};
for (
const [ElementId, Value]
of Object.entries(Values)
) {
const Element =
document.getElementById(
ElementId
);
if (Element !== null) {
Element.textContent =
Value;
}
}
}
function RenderGardenInfoLayout() {
const DetailRow =
document.getElementById(
"GardenInfoDetailRow"
);
const OverviewSection =
document.getElementById(
"GardenOverviewSection"
);
const EconomySection =
document.getElementById(
"GardenEconomySection"
);
if (
DetailRow === null ||
OverviewSection === null ||
EconomySection === null
) {
return;
}
const HasOverview =
HasGardenOverviewUpgrade(
GameSave
);
const HasEconomy =
HasGardenEconomyUpgrade(
GameSave
);
DetailRow.hidden =
!HasOverview &&
!HasEconomy;
OverviewSection.classList.toggle(
"GardenInfoSectionEmpty",
!HasOverview
);
EconomySection.classList.toggle(
"GardenInfoSectionEmpty",
!HasEconomy
);
}
document.addEventListener( document.addEventListener(
"DOMContentLoaded", "DOMContentLoaded",
StartGame StartGame
+5
View File
@@ -420,6 +420,7 @@ function CreateNewSave() {
PlotSize: 0, PlotSize: 0,
PlantInformation: false, PlantInformation: false,
GardenOverview: false, GardenOverview: false,
GardenEconomy: false,
MutationHints: false MutationHints: false
}, },
@@ -610,6 +611,10 @@ function NormalizeSaveData(
SaveData.Upgrades.GardenOverview === SaveData.Upgrades.GardenOverview ===
true; true;
SaveData.Upgrades.GardenEconomy =
SaveData.Upgrades.GardenEconomy ===
true;
SaveData.Upgrades.MutationHints = SaveData.Upgrades.MutationHints =
SaveData.Upgrades.MutationHints === SaveData.Upgrades.MutationHints ===
true; true;
+173
View File
@@ -127,6 +127,7 @@ function RenderGardenUpgrades() {
CreateNewGardenCard(), CreateNewGardenCard(),
CreatePlantInformationUpgradeCard(), CreatePlantInformationUpgradeCard(),
CreateGardenOverviewUpgradeCard(), CreateGardenOverviewUpgradeCard(),
CreateGardenEconomyUpgradeCard(),
CreateMutationHintsUpgradeCard() CreateMutationHintsUpgradeCard()
); );
} }
@@ -619,6 +620,120 @@ function CreateGardenOverviewUpgradeCard() {
} }
function CreateGardenEconomyUpgradeCard() {
const Cost =
GetGardenEconomyUpgradeCost();
const IsOwned =
HasGardenEconomyUpgrade(
ShopSave
);
const Card =
document.createElement(
"article"
);
Card.className =
"Panel ShopItem";
const Header =
document.createElement(
"div"
);
Header.className =
"ShopItemHeader";
const Name =
document.createElement(
"h3"
);
Name.textContent =
"Garden economy";
Header.appendChild(Name);
const Description =
document.createElement(
"p"
);
Description.textContent =
"Add live economy information for the active Garden: Dew invested, total harvest value, net profit after replanting, and Farm DPH.";
const Details =
document.createElement(
"div"
);
Details.className =
"ShopItemDetails";
Details.append(
CreateShopStat(
"Garden economics",
IsOwned
? "Unlocked"
: "Locked"
),
CreateShopStat(
"Scope",
"Active Garden"
)
);
const BuyButton =
document.createElement(
"button"
);
BuyButton.className =
"ActionButton ShopBuyButton";
BuyButton.type = "button";
if (IsOwned) {
BuyButton.textContent =
"Purchased";
BuyButton.disabled = true;
} else {
BuyButton.textContent =
"Buy - " +
Cost.toLocaleString() +
" Dew";
BuyButton.disabled =
ShopSave.Currency.Dew < Cost ||
ShopPurchasePending;
BuyButton.addEventListener(
"click",
BuyGardenEconomyUpgrade
);
}
Card.append(
Header,
Description,
Details,
BuyButton
);
return Card;
}
function CreateMutationHintsUpgradeCard() { function CreateMutationHintsUpgradeCard() {
const Cost = const Cost =
GetMutationHintsUpgradeCost(); GetMutationHintsUpgradeCost();
@@ -1463,6 +1578,64 @@ async function BuyGardenOverviewUpgrade() {
} }
async function BuyGardenEconomyUpgrade() {
if (
ShopPurchasePending ||
HasGardenEconomyUpgrade(
ShopSave
)
) {
return;
}
const Cost =
GetGardenEconomyUpgradeCost();
if (
ShopSave.Currency.Dew <
Cost
) {
SetShopMessage(
"You don't have enough Dew for Garden economy."
);
return;
}
ShopPurchasePending = true;
try {
ShopSave.Currency.Dew -=
Cost;
ShopSave.Statistics.CurrencySpent.Dew +=
Cost;
UnlockGardenEconomy(
ShopSave
);
SetShopMessage(
"Bought Garden economy for " +
Cost.toLocaleString() +
" Dew. Economy information is now visible on the Garden page."
);
RenderShop();
await SaveGame(
ShopSave
);
} finally {
ShopPurchasePending = false;
RenderShop();
}
}
async function BuyMutationHintsUpgrade() { async function BuyMutationHintsUpgrade() {
if ( if (
ShopPurchasePending || ShopPurchasePending ||
+27
View File
@@ -1,6 +1,7 @@
const GardenExpansionBaseCost = 1000; const GardenExpansionBaseCost = 1000;
const PlantInformationUpgradeCost = 500; const PlantInformationUpgradeCost = 500;
const GardenOverviewUpgradeCost = 1000; const GardenOverviewUpgradeCost = 1000;
const GardenEconomyUpgradeCost = 1000;
const MutationHintsUpgradeCost = 1500; const MutationHintsUpgradeCost = 1500;
const NewGardenBaseCost = 15000; const NewGardenBaseCost = 15000;
const NewGardenCostMultiplier = 1.5; const NewGardenCostMultiplier = 1.5;
@@ -240,6 +241,32 @@ function UnlockGardenOverview(
} }
function HasGardenEconomyUpgrade(
SaveData
) {
return (
SaveData
?.Upgrades
?.GardenEconomy ===
true
);
}
function GetGardenEconomyUpgradeCost() {
return GardenEconomyUpgradeCost;
}
function UnlockGardenEconomy(
SaveData
) {
SaveData.Upgrades ??= {};
SaveData.Upgrades.GardenEconomy =
true;
}
function HasMutationHintsUpgrade( function HasMutationHintsUpgrade(
SaveData SaveData
) { ) {
+67 -3
View File
@@ -83,10 +83,40 @@ body {
border: 3px solid var(--Surface2); border: 3px solid var(--Surface2);
} }
.GardenInfoPanel {
padding: 0;
}
.GardenInfoRow {
display: grid;
grid-template-columns: repeat( 2, minmax(0, 1fr) );
}
.GardenInfoRow + .GardenInfoRow {
border-top: 3px solid var(--Surface2);
}
.GardenInfoSection {
min-width: 0;
padding: 0.75rem 1rem;
}
.GardenInfoSection + .GardenInfoSection {
border-left: 3px solid var(--Surface2);
}
.GardenInfoReservedSection {
min-height: 100%;
}
.GardenOverviewDetails { .GardenOverviewDetails {
margin-top: 0.5rem; margin-top: 0.5rem;
} }
.GardenInfoPanel .GardenOverviewDetails {
margin-top: 0;
}
.GardenStatusLine + .GardenStatusLine { .GardenStatusLine + .GardenStatusLine {
margin-top: 0.1rem; margin-top: 0.1rem;
} }
@@ -327,7 +357,17 @@ input, button, textarea, select {
min-width: 0; min-width: 0;
padding-right: 0; padding-right: 0;
padding-left: 0; padding-left: 0;
font-size: 1.25rem; border-color: var(--GardenSelectorBorderColour, var(--Surface2));
font-size: 1.5rem;
}
.GardenSelectorButton:hover:not(:disabled) {
border-color: var(--GardenSelectorBorderColour, var(--Surface2));
}
.GardenSelectorButton:disabled {
cursor: default;
border-color: var(--Surface2);
} }
.GardenNameInput { .GardenNameInput {
@@ -336,14 +376,15 @@ input, button, textarea, select {
padding: 0.6rem 0.75rem; padding: 0.6rem 0.75rem;
color: var(--Text); color: var(--Text);
background-color: var(--Surface0); background-color: var(--Surface0);
border: 3px solid var(--Surface2); border: 3px solid var(--GardenSelectorBorderColour, var(--Surface2));
border-radius: 0; border-radius: 0;
font-size: 1.5rem;
text-align: center; text-align: center;
box-shadow: 3px 3px 0 var(--Crust); box-shadow: 3px 3px 0 var(--Crust);
} }
.GardenNameInput:hover { .GardenNameInput:hover {
border-color: var(--Sky); border-color: var(--GardenSelectorBorderColour, var(--Surface2));
} }
.GardenNameInput:focus { .GardenNameInput:focus {
@@ -351,6 +392,29 @@ input, button, textarea, select {
outline-offset: 3px; outline-offset: 3px;
} }
@media (max-width: 700px) {
.GardenInfoTopRow {
grid-template-columns: 1fr;
}
.GardenInfoReservedSection {
display: none;
}
.GardenInfoDetailRow {
grid-template-columns: 1fr;
}
.GardenInfoDetailRow .GardenInfoSection + .GardenInfoSection {
border-top: 3px solid var(--Surface2);
border-left: 0;
}
.GardenInfoDetailRow .GardenInfoSectionEmpty {
display: none;
}
}
.GardenLayout { .GardenLayout {
display: grid; display: grid;
grid-template-columns: minmax(0, 1fr) 12rem; grid-template-columns: minmax(0, 1fr) 12rem;