From cce44b1b0eb2156499268e12e0fb22a23104e6e8 Mon Sep 17 00:00:00 2001 From: Sarahtonin Date: Thu, 3 Sep 2026 20:31:28 +0200 Subject: [PATCH] Added farm economics upgrades and edited Garden UI to be a bit cleaner. --- Pages/Garden.php | 140 ++++++++++++++------- Scripts/Garden/Economy.js | 80 ++++++++++++ Scripts/Garden/Garden.js | 248 +++++++++++++++++++++++++++++++++---- Scripts/Garden/Save.js | 5 + Scripts/Garden/Shop.js | 173 ++++++++++++++++++++++++++ Scripts/Garden/Upgrades.js | 27 ++++ Styles/Style.css | 70 ++++++++++- 7 files changed, 673 insertions(+), 70 deletions(-) diff --git a/Pages/Garden.php b/Pages/Garden.php index 3811e80..800b4e2 100644 --- a/Pages/Garden.php +++ b/Pages/Garden.php @@ -17,63 +17,117 @@ $PageSection = "Garden";

Garden

-
-
- Dew: - 0 +
+
+
+
+ Dew: + 0 +
+ +
+ Next harvest: + Nothing planted +
+
+ +
- Next harvest: - Nothing planted -
- -
diff --git a/Scripts/Garden/Economy.js b/Scripts/Garden/Economy.js index 2b59a4e..cddc021 100644 --- a/Scripts/Garden/Economy.js +++ b/Scripts/Garden/Economy.js @@ -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( DewPerHour ) { diff --git a/Scripts/Garden/Garden.js b/Scripts/Garden/Garden.js index 3d436be..69c414c 100644 --- a/Scripts/Garden/Garden.js +++ b/Scripts/Garden/Garden.js @@ -6,6 +6,42 @@ let MutationCheckPending = false; 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() { try { @@ -62,6 +98,8 @@ function RenderGame() { RenderGarden(); RenderNextHarvest(); RenderGardenOverview(); + RenderGardenEconomy(); + RenderGardenInfoLayout(); } @@ -217,12 +255,66 @@ function RenderGardenSelector() { const GardenIndex = GameSave.ActiveGardenIndex; + const HasPreviousGarden = + GardenIndex > 0; + + const HasNextGarden = + GardenIndex < + GardenCount - 1; + PreviousButton.disabled = - GardenIndex <= 0; + !HasPreviousGarden; NextButton.disabled = - GardenIndex >= - GardenCount - 1; + !HasNextGarden; + + + 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 ( document.activeElement !== @@ -555,29 +647,11 @@ function RenderGarden() { 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( "--GardenBorderColour", - GardenBorderColours[ - GameSave.ActiveGardenIndex % - GardenBorderColours.length - ] + GetGardenBorderColour( + GameSave.ActiveGardenIndex + ) ); GardenGrid.replaceChildren(); @@ -1196,6 +1270,8 @@ async function GardenTick() { RenderGarden(); RenderNextHarvest(); RenderGardenOverview(); + RenderGardenEconomy(); + RenderGardenInfoLayout(); 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( "DOMContentLoaded", StartGame diff --git a/Scripts/Garden/Save.js b/Scripts/Garden/Save.js index d04063c..12ce33a 100644 --- a/Scripts/Garden/Save.js +++ b/Scripts/Garden/Save.js @@ -420,6 +420,7 @@ function CreateNewSave() { PlotSize: 0, PlantInformation: false, GardenOverview: false, + GardenEconomy: false, MutationHints: false }, @@ -610,6 +611,10 @@ function NormalizeSaveData( SaveData.Upgrades.GardenOverview === true; + SaveData.Upgrades.GardenEconomy = + SaveData.Upgrades.GardenEconomy === + true; + SaveData.Upgrades.MutationHints = SaveData.Upgrades.MutationHints === true; diff --git a/Scripts/Garden/Shop.js b/Scripts/Garden/Shop.js index c11edf6..1598e26 100644 --- a/Scripts/Garden/Shop.js +++ b/Scripts/Garden/Shop.js @@ -127,6 +127,7 @@ function RenderGardenUpgrades() { CreateNewGardenCard(), CreatePlantInformationUpgradeCard(), CreateGardenOverviewUpgradeCard(), + CreateGardenEconomyUpgradeCard(), 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() { const Cost = 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() { if ( ShopPurchasePending || diff --git a/Scripts/Garden/Upgrades.js b/Scripts/Garden/Upgrades.js index e37d893..f12709c 100644 --- a/Scripts/Garden/Upgrades.js +++ b/Scripts/Garden/Upgrades.js @@ -1,6 +1,7 @@ const GardenExpansionBaseCost = 1000; const PlantInformationUpgradeCost = 500; const GardenOverviewUpgradeCost = 1000; +const GardenEconomyUpgradeCost = 1000; const MutationHintsUpgradeCost = 1500; const NewGardenBaseCost = 15000; 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( SaveData ) { diff --git a/Styles/Style.css b/Styles/Style.css index 3fad376..ece30de 100644 --- a/Styles/Style.css +++ b/Styles/Style.css @@ -83,10 +83,40 @@ body { 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 { margin-top: 0.5rem; } +.GardenInfoPanel .GardenOverviewDetails { + margin-top: 0; +} + .GardenStatusLine + .GardenStatusLine { margin-top: 0.1rem; } @@ -327,7 +357,17 @@ input, button, textarea, select { min-width: 0; padding-right: 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 { @@ -336,14 +376,15 @@ input, button, textarea, select { padding: 0.6rem 0.75rem; color: var(--Text); background-color: var(--Surface0); - border: 3px solid var(--Surface2); + border: 3px solid var(--GardenSelectorBorderColour, var(--Surface2)); border-radius: 0; + font-size: 1.5rem; text-align: center; box-shadow: 3px 3px 0 var(--Crust); } .GardenNameInput:hover { - border-color: var(--Sky); + border-color: var(--GardenSelectorBorderColour, var(--Surface2)); } .GardenNameInput:focus { @@ -351,6 +392,29 @@ input, button, textarea, select { 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 { display: grid; grid-template-columns: minmax(0, 1fr) 12rem;