Added plant info upgrade.

This commit is contained in:
sarah committed 2026-09-02 22:40:58 +02:00
1 parent 23cc94dc69
commit 367e34ef18
8 files changed
+733 -10

No files matched your search

+1
View File
@@ -137,6 +137,7 @@ $PageSection = "Garden";
<script src="/Scripts/Garden/Save.js"></script> <script src="/Scripts/Garden/Save.js"></script>
<script src="/Scripts/Garden/Content.js"></script> <script src="/Scripts/Garden/Content.js"></script>
<script src="/Scripts/Garden/Economy.js"></script> <script src="/Scripts/Garden/Economy.js"></script>
<script src="/Scripts/Garden/Upgrades.js"></script>
<script src="/Scripts/Garden/MutationEngine.js"></script> <script src="/Scripts/Garden/MutationEngine.js"></script>
<script src="/Scripts/Garden/Users.js"></script> <script src="/Scripts/Garden/Users.js"></script>
<script src="/Scripts/Garden/Leaderboard.js"></script> <script src="/Scripts/Garden/Leaderboard.js"></script>
+37
View File
@@ -99,6 +99,42 @@ $PageTitle = "Profile";
</form> </form>
</section> </section>
<section
id="GardenDisplay"
class="Panel ProfileSection"
>
<h2>Garden display</h2>
<p>
Choose which Plant information details are
shown on Garden plots.
</p>
<div class="ProfileButtonRow">
<button
id="TogglePlantNamesButton"
type="button"
>
Plant names: Locked
</button>
<button
id="ToggleGrowthTimersButton"
type="button"
>
Growth timers: Locked
</button>
</div>
<p
id="GardenDisplayMessage"
class="ProfileMessage"
>
Unlock Plant information in the Shop
to use these settings.
</p>
</section>
<section <section
id="Statistics" id="Statistics"
class="Panel ProfileSection" class="Panel ProfileSection"
@@ -294,6 +330,7 @@ $PageTitle = "Profile";
<script src="/Scripts/Garden/Mutations.js"></script> <script src="/Scripts/Garden/Mutations.js"></script>
<script src="/Scripts/Garden/Save.js"></script> <script src="/Scripts/Garden/Save.js"></script>
<script src="/Scripts/Garden/Content.js"></script> <script src="/Scripts/Garden/Content.js"></script>
<script src="/Scripts/Garden/Upgrades.js"></script>
<script src="/Scripts/Garden/Users.js"></script> <script src="/Scripts/Garden/Users.js"></script>
<script src="/Scripts/Garden/Profile.js"></script> <script src="/Scripts/Garden/Profile.js"></script>
<script src="/Scripts/Garden/Account.js"></script> <script src="/Scripts/Garden/Account.js"></script>
+257 -8
View File
@@ -327,12 +327,18 @@ function RenderGarden() {
GardenGrid.replaceChildren(); GardenGrid.replaceChildren();
const DisplaySettings =
GetGardenDisplaySettings();
GameSave.Garden.Plots.forEach( GameSave.Garden.Plots.forEach(
(Plot, PlotIndex) => { (Plot, PlotIndex) => {
const PlotElement = const PlotElement =
CreatePlotElement( CreatePlotElement(
Plot, Plot,
PlotIndex PlotIndex,
DisplaySettings
); );
GardenGrid.appendChild( GardenGrid.appendChild(
@@ -343,9 +349,32 @@ function RenderGarden() {
} }
function GetGardenDisplaySettings() {
const HasPlantInformation =
HasPlantInformationUpgrade(
GameSave
);
return {
ShowPlantNames:
HasPlantInformation &&
GameSave.Preferences
.ShowPlantNames !==
false,
ShowGrowthTimers:
HasPlantInformation &&
GameSave.Preferences
.ShowGrowthTimers !==
false
};
}
function CreatePlotElement( function CreatePlotElement(
Plot, Plot,
PlotIndex PlotIndex,
DisplaySettings
) { ) {
const Button = const Button =
document.createElement( document.createElement(
@@ -358,6 +387,53 @@ function CreatePlotElement(
Button.type = "button"; Button.type = "button";
if (
DisplaySettings.ShowPlantNames
) {
Button.classList.add(
"GardenPlotWithName"
);
}
if (
DisplaySettings.ShowGrowthTimers
) {
Button.classList.add(
"GardenPlotWithTimer"
);
}
const Plant =
Plot === null
? null
: Plants[Plot.Plant];
if (
DisplaySettings.ShowPlantNames
) {
Button.appendChild(
CreateGardenPlotName(
Plot,
Plant
)
);
}
const Visual =
document.createElement(
"span"
);
Visual.className =
"GardenPlotVisual";
Button.appendChild(
Visual
);
if (Plot === null) { if (Plot === null) {
Button.classList.add( Button.classList.add(
"GardenPlotEmpty" "GardenPlotEmpty"
@@ -366,6 +442,19 @@ function CreatePlotElement(
Button.title = Button.title =
"Empty plot"; "Empty plot";
if (
DisplaySettings
.ShowGrowthTimers
) {
Button.appendChild(
CreateGardenPlotTimer(
null,
null,
0
)
);
}
if ( if (
SelectedGardenTool === SelectedGardenTool ===
"Seed" "Seed"
@@ -384,11 +473,21 @@ function CreatePlotElement(
} }
const Plant =
Plants[Plot.Plant];
if (Plant === undefined) { if (Plant === undefined) {
Button.textContent = "?"; Visual.textContent = "?";
if (
DisplaySettings
.ShowGrowthTimers
) {
Button.appendChild(
CreateGardenPlotTimer(
Plot,
null,
0
)
);
}
return Button; return Button;
} }
@@ -427,7 +526,7 @@ function CreatePlotElement(
Image.src = Image.src =
ImagePath; ImagePath;
Button.appendChild( Visual.appendChild(
Image Image
); );
} else { } else {
@@ -442,12 +541,25 @@ function CreatePlotElement(
MissingImage.textContent = MissingImage.textContent =
Plant.Name; Plant.Name;
Button.appendChild( Visual.appendChild(
MissingImage MissingImage
); );
} }
if (
DisplaySettings.ShowGrowthTimers
) {
Button.appendChild(
CreateGardenPlotTimer(
Plot,
Plant,
Progress
)
);
}
if ( if (
SelectedGardenTool === SelectedGardenTool ===
"Shovel" "Shovel"
@@ -507,6 +619,143 @@ function CreatePlotElement(
} }
function CreateGardenPlotName(
Plot,
Plant
) {
const Name =
document.createElement(
"span"
);
Name.className =
"GardenPlotName";
if (Plot === null) {
Name.textContent =
"Empty";
} else if (Plant === undefined) {
Name.textContent =
"Unknown";
} else {
Name.textContent =
Plant.Name;
Name.title =
Plant.Name;
}
return Name;
}
function CreateGardenPlotTimer(
Plot,
Plant,
Progress
) {
const Timer =
document.createElement(
"span"
);
Timer.className =
"GardenPlotTimer";
if (
Plot === null ||
Plant === null ||
Plant === undefined
) {
Timer.textContent = "";
return Timer;
}
if (Progress >= 1) {
Timer.textContent =
"Ready";
return Timer;
}
const RemainingTime =
Math.max(
0,
Plant.GrowthTime -
(
Date.now() -
Plot.PlantedAt
)
);
Timer.textContent =
FormatGardenRemainingTime(
RemainingTime
);
return Timer;
}
function FormatGardenRemainingTime(
Milliseconds
) {
const TotalSeconds =
Math.max(
0,
Math.ceil(
Number(
Milliseconds
) / 1000
)
);
const Hours =
Math.floor(
TotalSeconds / 3600
);
const Minutes =
Math.floor(
(
TotalSeconds % 3600
) / 60
);
const Seconds =
TotalSeconds % 60;
if (Hours > 0) {
return (
Hours +
":" +
String(Minutes).padStart(
2,
"0"
) +
":" +
String(Seconds).padStart(
2,
"0"
)
);
}
return (
Minutes +
":" +
String(Seconds).padStart(
2,
"0"
)
);
}
async function PlantSeed( async function PlantSeed(
PlotIndex PlotIndex
) { ) {
+151
View File
@@ -11,6 +11,8 @@ async function StartProfile() {
await LoadGame(); await LoadGame();
RenderProfileStatistics(); RenderProfileStatistics();
RenderGardenDisplayPreferences();
BindGardenDisplayPreferences();
} catch (Error) { } catch (Error) {
console.error( console.error(
"Couldn't load profile statistics:", "Couldn't load profile statistics:",
@@ -72,6 +74,155 @@ async function StartProfile() {
} }
function BindGardenDisplayPreferences() {
const PlantNamesButton =
document.getElementById(
"TogglePlantNamesButton"
);
const GrowthTimersButton =
document.getElementById(
"ToggleGrowthTimersButton"
);
if (
PlantNamesButton === null ||
GrowthTimersButton === null
) {
return;
}
PlantNamesButton.addEventListener(
"click",
() => {
ToggleGardenDisplayPreference(
"ShowPlantNames"
);
}
);
GrowthTimersButton.addEventListener(
"click",
() => {
ToggleGardenDisplayPreference(
"ShowGrowthTimers"
);
}
);
}
function RenderGardenDisplayPreferences() {
const PlantNamesButton =
document.getElementById(
"TogglePlantNamesButton"
);
const GrowthTimersButton =
document.getElementById(
"ToggleGrowthTimersButton"
);
const Message =
document.getElementById(
"GardenDisplayMessage"
);
if (
PlantNamesButton === null ||
GrowthTimersButton === null ||
Message === null
) {
return;
}
const IsOwned =
HasPlantInformationUpgrade(
ProfileSave
);
if (!IsOwned) {
PlantNamesButton.textContent =
"Plant names: Locked";
GrowthTimersButton.textContent =
"Growth timers: Locked";
PlantNamesButton.disabled = true;
GrowthTimersButton.disabled = true;
Message.textContent =
"Unlock Plant information in the Shop to use these settings.";
return;
}
const ShowPlantNames =
ProfileSave.Preferences
.ShowPlantNames !== false;
const ShowGrowthTimers =
ProfileSave.Preferences
.ShowGrowthTimers !== false;
PlantNamesButton.disabled = false;
GrowthTimersButton.disabled = false;
PlantNamesButton.textContent =
"Plant names: " +
(ShowPlantNames
? "On"
: "Off");
GrowthTimersButton.textContent =
"Growth timers: " +
(ShowGrowthTimers
? "On"
: "Off");
Message.textContent =
"Plant names and growth timers can be changed independently.";
}
async function ToggleGardenDisplayPreference(
PreferenceName
) {
if (
!HasPlantInformationUpgrade(
ProfileSave
)
) {
return;
}
if (
PreferenceName !==
"ShowPlantNames" &&
PreferenceName !==
"ShowGrowthTimers"
) {
return;
}
ProfileSave.Preferences[
PreferenceName
] = !ProfileSave.Preferences[
PreferenceName
];
RenderGardenDisplayPreferences();
await SaveGame(
ProfileSave
);
}
async function RenderProfilePlayer() { async function RenderProfilePlayer() {
const UsernameInput = const UsernameInput =
document.getElementById( document.getElementById(
+21 -1
View File
@@ -66,7 +66,13 @@ function CreateNewSave() {
}, },
Upgrades: { Upgrades: {
PlotSize: 0 PlotSize: 0,
PlantInformation: false
},
Preferences: {
ShowPlantNames: true,
ShowGrowthTimers: true
}, },
Garden: { Garden: {
@@ -239,6 +245,20 @@ function NormalizeSaveData(
SaveData.Upgrades ??= {}; SaveData.Upgrades ??= {};
SaveData.Upgrades.PlotSize ??= 0; SaveData.Upgrades.PlotSize ??= 0;
SaveData.Upgrades.PlantInformation =
SaveData.Upgrades.PlantInformation ===
true;
SaveData.Preferences ??= {};
SaveData.Preferences.ShowPlantNames =
SaveData.Preferences.ShowPlantNames !==
false;
SaveData.Preferences.ShowGrowthTimers =
SaveData.Preferences.ShowGrowthTimers !==
false;
SaveData.Garden ??= { SaveData.Garden ??= {
+176 -1
View File
@@ -123,7 +123,8 @@ function RenderGardenUpgrades() {
), ),
CreateGardenExpansionCard( CreateGardenExpansionCard(
"Row" "Row"
) ),
CreatePlantInformationUpgradeCard()
); );
} }
@@ -270,6 +271,122 @@ function CreateGardenExpansionCard(
} }
function CreatePlantInformationUpgradeCard() {
const Cost =
GetPlantInformationUpgradeCost();
const IsOwned =
HasPlantInformationUpgrade(
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 =
"Plant information";
Header.appendChild(Name);
const Description =
document.createElement(
"p"
);
Description.textContent =
"Show each plant's name above its sprite and exact time remaining below it. Both displays can be toggled on or off from your Profile.";
const Details =
document.createElement(
"div"
);
Details.className =
"ShopItemDetails";
Details.append(
CreateShopStat(
"Plant names",
IsOwned
? "Unlocked"
: "Locked"
),
CreateShopStat(
"Growth timers",
IsOwned
? "Unlocked"
: "Locked"
)
);
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",
BuyPlantInformationUpgrade
);
}
Card.append(
Header,
Description,
Details,
BuyButton
);
return Card;
}
function CreateShopSeedCard( function CreateShopSeedCard(
Plant Plant
) { ) {
@@ -839,6 +956,64 @@ async function BuyGardenExpansion(
} }
async function BuyPlantInformationUpgrade() {
if (
ShopPurchasePending ||
HasPlantInformationUpgrade(
ShopSave
)
) {
return;
}
const Cost =
GetPlantInformationUpgradeCost();
if (
ShopSave.Currency.Dew <
Cost
) {
SetShopMessage(
"You don't have enough Dew for Plant information."
);
return;
}
ShopPurchasePending = true;
try {
ShopSave.Currency.Dew -=
Cost;
ShopSave.Statistics.CurrencySpent.Dew +=
Cost;
UnlockPlantInformation(
ShopSave
);
SetShopMessage(
"Bought Plant information for " +
Cost.toLocaleString() +
" Dew. Plant names and growth timers are now visible."
);
RenderShop();
await SaveGame(
ShopSave
);
} finally {
ShopPurchasePending = false;
RenderShop();
}
}
function SetShopMessage( function SetShopMessage(
Message Message
) { ) {
+35
View File
@@ -1,4 +1,5 @@
const GardenExpansionBaseCost = 1000; const GardenExpansionBaseCost = 1000;
const PlantInformationUpgradeCost = 500;
function GetGardenColumnUpgradeCost( function GetGardenColumnUpgradeCost(
@@ -107,3 +108,37 @@ function ResetGardenMutationCooldowns(
) { ) {
SaveData.MutationCooldowns = {}; SaveData.MutationCooldowns = {};
} }
function HasPlantInformationUpgrade(
SaveData
) {
return (
SaveData
?.Upgrades
?.PlantInformation ===
true
);
}
function GetPlantInformationUpgradeCost() {
return PlantInformationUpgradeCost;
}
function UnlockPlantInformation(
SaveData
) {
SaveData.Upgrades ??= {};
SaveData.Preferences ??= {};
SaveData.Upgrades.PlantInformation =
true;
SaveData.Preferences.ShowPlantNames =
true;
SaveData.Preferences.ShowGrowthTimers =
true;
}
+55
View File
@@ -328,6 +328,7 @@ input, button, textarea, select {
--PlotBorderColour: var(--Surface2); --PlotBorderColour: var(--Surface2);
--TileBackground: var(--PlantBackground); --TileBackground: var(--PlantBackground);
position: relative; position: relative;
display: grid;
width: 100%; width: 100%;
aspect-ratio: 1; aspect-ratio: 1;
padding: 0; padding: 0;
@@ -337,6 +338,52 @@ input, button, textarea, select {
box-shadow: 3px 3px 0 var(--Crust); box-shadow: 3px 3px 0 var(--Crust);
} }
.GardenPlotWithName,
.GardenPlotWithTimer {
aspect-ratio: auto;
}
.GardenPlotVisual {
display: flex;
width: 100%;
min-width: 0;
aspect-ratio: 1;
align-items: center;
justify-content: center;
overflow: hidden;
background-color: var(--TileBackground);
}
.GardenPlotName,
.GardenPlotTimer {
display: flex;
min-width: 0;
min-height: 1.5rem;
padding: 0.2rem 0.3rem;
align-items: center;
justify-content: center;
overflow: hidden;
color: var(--Subtext1);
background-color: var(--Mantle);
font-size: 0.75rem;
line-height: 1;
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
}
.GardenPlotName {
border-bottom: 2px solid var(--Surface2);
}
.GardenPlotTimer {
border-top: 2px solid var(--Surface2);
}
.GardenPlotMature .GardenPlotTimer {
color: var(--Green);
}
.GardenPlot:hover { .GardenPlot:hover {
--TileBackground: var(--Surface1); --TileBackground: var(--Surface1);
--PlotBorderColour: var(--Sky); --PlotBorderColour: var(--Sky);
@@ -645,6 +692,14 @@ input, button, textarea, select {
box-shadow: 2px 2px 0 var(--Crust); box-shadow: 2px 2px 0 var(--Crust);
} }
.ProfileSection button:disabled {
cursor: not-allowed;
color: var(--Overlay1);
background-color: var(--Surface0);
border-color: var(--Surface1);
box-shadow: none;
}
.ProfileSection button:active { .ProfileSection button:active {
color: var(--Green); color: var(--Green);
background-color: var(--Surface2); background-color: var(--Surface2);