mirror of
https://git.srhtnin.garden/sarah/srhtnin.github.io.git
synced 2026-10-13 18:38:11 +02:00
Added rotation to plots.. Only does stuff for functional plants.
This commit is contained in:
1 parent
75e4f31ccd
commit
132aba0b9a
14 files changed
+1189
-142
No files matched your search
+63
-2
@@ -246,6 +246,17 @@ $PageSection = "Admin";
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<label class="AdminEditorCheckField">
|
||||
<input
|
||||
id="AdminPlantDirectionalSprites"
|
||||
name="DirectionalSprites"
|
||||
type="checkbox"
|
||||
>
|
||||
<span>
|
||||
Directional sprites
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<label class="AdminEditorField AdminEditorWideField">
|
||||
<span>Effects JSON</span>
|
||||
<textarea
|
||||
@@ -323,15 +334,66 @@ $PageSection = "Admin";
|
||||
Optional. You can save a plant with no sprites and add them later.
|
||||
Upload PNG files named by stage, such as
|
||||
<code>1.png</code>, <code>2.png</code>, <code>3.png</code>.
|
||||
Directional plants use one stage set for each direction.
|
||||
</p>
|
||||
|
||||
<div class="AdminPlantImageUpload">
|
||||
<div
|
||||
id="AdminPlantDefaultImageUpload"
|
||||
class="AdminPlantImageUpload"
|
||||
>
|
||||
<input
|
||||
id="AdminPlantImageFiles"
|
||||
type="file"
|
||||
accept="image/png,.png"
|
||||
multiple
|
||||
>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="AdminPlantDirectionalImageUpload"
|
||||
class="AdminPlantDirectionalImageUpload"
|
||||
hidden
|
||||
>
|
||||
<label class="AdminEditorField">
|
||||
<span>North sprites</span>
|
||||
<input
|
||||
id="AdminPlantImageFilesNorth"
|
||||
type="file"
|
||||
accept="image/png,.png"
|
||||
multiple
|
||||
>
|
||||
</label>
|
||||
|
||||
<label class="AdminEditorField">
|
||||
<span>East sprites</span>
|
||||
<input
|
||||
id="AdminPlantImageFilesEast"
|
||||
type="file"
|
||||
accept="image/png,.png"
|
||||
multiple
|
||||
>
|
||||
</label>
|
||||
|
||||
<label class="AdminEditorField">
|
||||
<span>South sprites</span>
|
||||
<input
|
||||
id="AdminPlantImageFilesSouth"
|
||||
type="file"
|
||||
accept="image/png,.png"
|
||||
multiple
|
||||
>
|
||||
</label>
|
||||
|
||||
<label class="AdminEditorField">
|
||||
<span>West sprites</span>
|
||||
<input
|
||||
id="AdminPlantImageFilesWest"
|
||||
type="file"
|
||||
accept="image/png,.png"
|
||||
multiple
|
||||
>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button
|
||||
id="AdminPlantImageUploadButton"
|
||||
@@ -340,7 +402,6 @@ $PageSection = "Admin";
|
||||
>
|
||||
Upload selected sprites
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p
|
||||
id="AdminPlantImageNote"
|
||||
|
||||
@@ -159,6 +159,30 @@ $PageTitle = "Profile";
|
||||
to use these settings.
|
||||
</p>
|
||||
|
||||
<h3>Plot rotation</h3>
|
||||
|
||||
<p>
|
||||
Choose whether plot directions are shown
|
||||
beside plot timers.
|
||||
</p>
|
||||
|
||||
<div class="ProfileButtonRow">
|
||||
<button
|
||||
id="TogglePlotRotationButton"
|
||||
type="button"
|
||||
>
|
||||
Plot rotation: Locked
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p
|
||||
id="PlotRotationDisplayMessage"
|
||||
class="ProfileMessage"
|
||||
>
|
||||
Unlock Plot rotation in the Shop
|
||||
to use this setting.
|
||||
</p>
|
||||
|
||||
<h3>Garden overview</h3>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -402,6 +402,8 @@ function BuildAdminMassPlantPayload(
|
||||
Source.Effects ?? {}
|
||||
)
|
||||
),
|
||||
DirectionalSprites:
|
||||
Source.DirectionalSprites === true,
|
||||
ShopPlant:
|
||||
Source.Shop?.ShopPlant === true,
|
||||
BaseCost:
|
||||
|
||||
@@ -95,10 +95,23 @@ function GetAdminOverviewMissingSprites(
|
||||
)
|
||||
);
|
||||
|
||||
const DirectionalStages =
|
||||
Array.isArray(
|
||||
Plant.DirectionalImageStages
|
||||
)
|
||||
? Plant.DirectionalImageStages
|
||||
: [];
|
||||
|
||||
const HighestApiStage =
|
||||
Math.max(
|
||||
0,
|
||||
...ApiStages
|
||||
...ApiStages,
|
||||
...DirectionalStages.map(
|
||||
Stage =>
|
||||
Number(
|
||||
Stage?.Stage ?? 0
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
const HighestStage =
|
||||
@@ -110,6 +123,51 @@ function GetAdminOverviewMissingSprites(
|
||||
|
||||
const Missing = [];
|
||||
|
||||
if (Plant.DirectionalSprites === true) {
|
||||
const Directions = [
|
||||
["N", "North"],
|
||||
["E", "East"],
|
||||
["S", "South"],
|
||||
["W", "West"]
|
||||
];
|
||||
|
||||
for (const [Code, Name] of Directions) {
|
||||
const Stages = new Set(
|
||||
DirectionalStages
|
||||
.filter(
|
||||
Stage =>
|
||||
String(
|
||||
Stage?.Direction ?? ""
|
||||
).toUpperCase() === Code
|
||||
)
|
||||
.map(
|
||||
Stage =>
|
||||
Number(Stage.Stage)
|
||||
)
|
||||
.filter(
|
||||
Stage =>
|
||||
Number.isInteger(Stage) &&
|
||||
Stage > 0
|
||||
)
|
||||
);
|
||||
|
||||
for (
|
||||
let Stage = 1;
|
||||
Stage <= HighestStage;
|
||||
Stage++
|
||||
) {
|
||||
if (!Stages.has(Stage)) {
|
||||
Missing.push(
|
||||
Name + "/" +
|
||||
Stage + ".png"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Missing;
|
||||
}
|
||||
|
||||
for (
|
||||
let Stage = 1;
|
||||
Stage <= HighestStage;
|
||||
@@ -132,7 +190,6 @@ function GetAdminOverviewMissingSprites(
|
||||
return Missing;
|
||||
}
|
||||
|
||||
|
||||
function CollectAdminOverviewCreatedPlants() {
|
||||
const Created = new Set();
|
||||
|
||||
|
||||
@@ -168,6 +168,16 @@ function BindAdminPlantEditor() {
|
||||
}
|
||||
);
|
||||
|
||||
document.getElementById(
|
||||
"AdminPlantDirectionalSprites"
|
||||
).addEventListener(
|
||||
"change",
|
||||
() => {
|
||||
RenderAdminPlantImageManager();
|
||||
RenderAdminPlantPreview();
|
||||
}
|
||||
);
|
||||
|
||||
document.getElementById(
|
||||
"AdminPlantImageUploadButton"
|
||||
).addEventListener(
|
||||
@@ -540,6 +550,11 @@ function LoadAdminPlantIntoForm(
|
||||
).checked =
|
||||
Plant.Shop?.ShopPlant === true;
|
||||
|
||||
document.getElementById(
|
||||
"AdminPlantDirectionalSprites"
|
||||
).checked =
|
||||
Plant.DirectionalSprites === true;
|
||||
|
||||
SetAdminPlantField(
|
||||
"AdminPlantBaseCost",
|
||||
Plant.Shop?.BaseCost ?? ""
|
||||
@@ -648,6 +663,11 @@ function DuplicateAdminPlant() {
|
||||
).checked =
|
||||
SourcePlant.Shop?.ShopPlant === true;
|
||||
|
||||
document.getElementById(
|
||||
"AdminPlantDirectionalSprites"
|
||||
).checked =
|
||||
SourcePlant.DirectionalSprites === true;
|
||||
|
||||
SetAdminPlantField(
|
||||
"AdminPlantBaseCost",
|
||||
SourcePlant.Shop?.BaseCost ?? ""
|
||||
@@ -734,6 +754,10 @@ function StartNewAdminPlant() {
|
||||
"AdminPlantShopPlant"
|
||||
).checked = false;
|
||||
|
||||
document.getElementById(
|
||||
"AdminPlantDirectionalSprites"
|
||||
).checked = false;
|
||||
|
||||
SetAdminPlantField(
|
||||
"AdminPlantBaseCost",
|
||||
""
|
||||
@@ -910,6 +934,11 @@ function GetAdminPlantFormData() {
|
||||
Effects:
|
||||
ParseAdminPlantEffects(),
|
||||
|
||||
DirectionalSprites:
|
||||
document.getElementById(
|
||||
"AdminPlantDirectionalSprites"
|
||||
).checked,
|
||||
|
||||
ShopPlant: ShopPlant,
|
||||
|
||||
BaseCost:
|
||||
@@ -1249,6 +1278,10 @@ function GetAdminPlantPreviewDefinition() {
|
||||
"AdminPlantHarvestMultiplier"
|
||||
).value
|
||||
),
|
||||
DirectionalSprites:
|
||||
document.getElementById(
|
||||
"AdminPlantDirectionalSprites"
|
||||
).checked,
|
||||
Shop: {
|
||||
ShopPlant: ShopPlant,
|
||||
BaseCost:
|
||||
@@ -1426,16 +1459,43 @@ function RenderAdminPlantImageManager() {
|
||||
"AdminPlantImageUploadButton"
|
||||
);
|
||||
|
||||
const FileInput =
|
||||
const DefaultUpload =
|
||||
document.getElementById(
|
||||
"AdminPlantImageFiles"
|
||||
"AdminPlantDefaultImageUpload"
|
||||
);
|
||||
|
||||
const DirectionalUpload =
|
||||
document.getElementById(
|
||||
"AdminPlantDirectionalImageUpload"
|
||||
);
|
||||
|
||||
const DirectionalToggle =
|
||||
document.getElementById(
|
||||
"AdminPlantDirectionalSprites"
|
||||
);
|
||||
|
||||
const Inputs = [
|
||||
"AdminPlantImageFiles",
|
||||
"AdminPlantImageFilesNorth",
|
||||
"AdminPlantImageFilesEast",
|
||||
"AdminPlantImageFilesSouth",
|
||||
"AdminPlantImageFilesWest"
|
||||
]
|
||||
.map(
|
||||
ElementId =>
|
||||
document.getElementById(
|
||||
ElementId
|
||||
)
|
||||
)
|
||||
.filter(Boolean);
|
||||
|
||||
if (
|
||||
List === null ||
|
||||
Note === null ||
|
||||
UploadButton === null ||
|
||||
FileInput === null
|
||||
DefaultUpload === null ||
|
||||
DirectionalUpload === null ||
|
||||
DirectionalToggle === null
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@@ -1449,6 +1509,15 @@ function RenderAdminPlantImageManager() {
|
||||
AdminPlantEditingKey
|
||||
] ?? null;
|
||||
|
||||
const IsDirectional =
|
||||
DirectionalToggle.checked;
|
||||
|
||||
DefaultUpload.hidden =
|
||||
IsDirectional;
|
||||
|
||||
DirectionalUpload.hidden =
|
||||
!IsDirectional;
|
||||
|
||||
const CanEdit =
|
||||
Plant !== null &&
|
||||
!AdminPlantImagePending;
|
||||
@@ -1456,8 +1525,9 @@ function RenderAdminPlantImageManager() {
|
||||
UploadButton.disabled =
|
||||
!CanEdit;
|
||||
|
||||
FileInput.disabled =
|
||||
!CanEdit;
|
||||
for (const Input of Inputs) {
|
||||
Input.disabled = !CanEdit;
|
||||
}
|
||||
|
||||
if (Plant === null) {
|
||||
Note.textContent =
|
||||
@@ -1466,6 +1536,7 @@ function RenderAdminPlantImageManager() {
|
||||
}
|
||||
|
||||
const StaticImages =
|
||||
typeof PlantImages !== "undefined" &&
|
||||
Array.isArray(
|
||||
PlantImages[
|
||||
AdminPlantEditingKey
|
||||
@@ -1488,6 +1559,44 @@ function RenderAdminPlantImageManager() {
|
||||
)
|
||||
: [];
|
||||
|
||||
const DirectionOrder = {
|
||||
N: 0,
|
||||
E: 1,
|
||||
S: 2,
|
||||
W: 3
|
||||
};
|
||||
|
||||
const DirectionNames = {
|
||||
N: "North",
|
||||
E: "East",
|
||||
S: "South",
|
||||
W: "West"
|
||||
};
|
||||
|
||||
const DirectionalStages =
|
||||
Array.isArray(
|
||||
Plant.DirectionalImageStages
|
||||
)
|
||||
? [...Plant.DirectionalImageStages]
|
||||
.sort(
|
||||
(A, B) =>
|
||||
Number(A.Stage) -
|
||||
Number(B.Stage) ||
|
||||
(
|
||||
DirectionOrder[
|
||||
String(A.Direction)
|
||||
.toUpperCase()
|
||||
] ?? 99
|
||||
) -
|
||||
(
|
||||
DirectionOrder[
|
||||
String(B.Direction)
|
||||
.toUpperCase()
|
||||
] ?? 99
|
||||
)
|
||||
)
|
||||
: [];
|
||||
|
||||
const FallbackText =
|
||||
StaticImages.length > 0
|
||||
? " Repository fallback: " +
|
||||
@@ -1500,28 +1609,45 @@ function RenderAdminPlantImageManager() {
|
||||
)
|
||||
: "";
|
||||
|
||||
if (ApiStages.length === 0) {
|
||||
const DisplayStages =
|
||||
IsDirectional
|
||||
? DirectionalStages
|
||||
: ApiStages;
|
||||
|
||||
if (DisplayStages.length === 0) {
|
||||
Note.textContent =
|
||||
"No API sprites uploaded." +
|
||||
IsDirectional
|
||||
? "No directional API sprites uploaded. Choose stage-numbered PNGs for each direction." +
|
||||
FallbackText
|
||||
: "No API sprites uploaded." +
|
||||
FallbackText;
|
||||
return;
|
||||
}
|
||||
|
||||
Note.textContent =
|
||||
ApiStages.length +
|
||||
" API sprite" +
|
||||
DisplayStages.length +
|
||||
(IsDirectional
|
||||
? " directional API sprite"
|
||||
: " API sprite") +
|
||||
(
|
||||
ApiStages.length === 1
|
||||
DisplayStages.length === 1
|
||||
? " uploaded."
|
||||
: "s uploaded."
|
||||
) +
|
||||
FallbackText;
|
||||
|
||||
for (const ImageStage of ApiStages) {
|
||||
for (const ImageStage of DisplayStages) {
|
||||
const Stage = Number(
|
||||
ImageStage.Stage
|
||||
);
|
||||
|
||||
const Direction =
|
||||
IsDirectional
|
||||
? String(
|
||||
ImageStage.Direction ?? ""
|
||||
).toUpperCase()
|
||||
: null;
|
||||
|
||||
const Row =
|
||||
document.createElement(
|
||||
"div"
|
||||
@@ -1550,13 +1676,24 @@ function RenderAdminPlantImageManager() {
|
||||
GetAdminPlantApiImageUrl(
|
||||
Plant.Id,
|
||||
Stage,
|
||||
ImageStage.Revision
|
||||
ImageStage.Revision,
|
||||
Direction
|
||||
);
|
||||
|
||||
Image.alt =
|
||||
Plant.Name +
|
||||
" stage " +
|
||||
Stage;
|
||||
Stage +
|
||||
(
|
||||
Direction === null
|
||||
? ""
|
||||
: " " +
|
||||
(
|
||||
DirectionNames[
|
||||
Direction
|
||||
] ?? Direction
|
||||
)
|
||||
);
|
||||
|
||||
Preview.appendChild(Image);
|
||||
|
||||
@@ -1566,7 +1703,17 @@ function RenderAdminPlantImageManager() {
|
||||
);
|
||||
|
||||
Label.textContent =
|
||||
"Stage " + Stage;
|
||||
"Stage " + Stage +
|
||||
(
|
||||
Direction === null
|
||||
? ""
|
||||
: " · " +
|
||||
(
|
||||
DirectionNames[
|
||||
Direction
|
||||
] ?? Direction
|
||||
)
|
||||
);
|
||||
|
||||
const RemoveButton =
|
||||
document.createElement(
|
||||
@@ -1586,7 +1733,8 @@ function RenderAdminPlantImageManager() {
|
||||
() =>
|
||||
DeleteAdminPlantImage(
|
||||
Plant.Id,
|
||||
Stage
|
||||
Stage,
|
||||
Direction
|
||||
)
|
||||
);
|
||||
|
||||
@@ -1604,14 +1752,24 @@ function RenderAdminPlantImageManager() {
|
||||
function GetAdminPlantApiImageUrl(
|
||||
PlantId,
|
||||
Stage,
|
||||
Revision = 0
|
||||
Revision = 0,
|
||||
Direction = null
|
||||
) {
|
||||
const Query =
|
||||
new URLSearchParams({
|
||||
const QueryData = {
|
||||
PlantId: String(PlantId),
|
||||
Stage: String(Stage),
|
||||
v: String(Revision ?? 0)
|
||||
});
|
||||
};
|
||||
|
||||
if (Direction !== null) {
|
||||
QueryData.Direction =
|
||||
String(Direction);
|
||||
}
|
||||
|
||||
const Query =
|
||||
new URLSearchParams(
|
||||
QueryData
|
||||
);
|
||||
|
||||
return ApiUrl +
|
||||
"/PlantImage.php?" +
|
||||
@@ -1619,6 +1777,41 @@ function GetAdminPlantApiImageUrl(
|
||||
}
|
||||
|
||||
|
||||
function GetAdminPlantImageUploadSets() {
|
||||
const IsDirectional =
|
||||
document.getElementById(
|
||||
"AdminPlantDirectionalSprites"
|
||||
).checked;
|
||||
|
||||
if (!IsDirectional) {
|
||||
return [
|
||||
{
|
||||
Direction: null,
|
||||
Input:
|
||||
document.getElementById(
|
||||
"AdminPlantImageFiles"
|
||||
)
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
["N", "AdminPlantImageFilesNorth"],
|
||||
["E", "AdminPlantImageFilesEast"],
|
||||
["S", "AdminPlantImageFilesSouth"],
|
||||
["W", "AdminPlantImageFilesWest"]
|
||||
].map(
|
||||
([Direction, ElementId]) => ({
|
||||
Direction: Direction,
|
||||
Input:
|
||||
document.getElementById(
|
||||
ElementId
|
||||
)
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
async function UploadAdminPlantImages() {
|
||||
if (
|
||||
AdminPlantImagePending ||
|
||||
@@ -1636,30 +1829,22 @@ async function UploadAdminPlantImages() {
|
||||
return;
|
||||
}
|
||||
|
||||
const FileInput =
|
||||
document.getElementById(
|
||||
"AdminPlantImageFiles"
|
||||
);
|
||||
|
||||
const Files =
|
||||
Array.from(
|
||||
FileInput.files ?? []
|
||||
);
|
||||
|
||||
if (Files.length === 0) {
|
||||
SetAdminPlantMessage(
|
||||
"Choose one or more PNG sprites first."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const Stages = new Set();
|
||||
const Uploads = [];
|
||||
|
||||
try {
|
||||
for (
|
||||
const UploadSet
|
||||
of GetAdminPlantImageUploadSets()
|
||||
) {
|
||||
const Files = Array.from(
|
||||
UploadSet.Input?.files ?? []
|
||||
);
|
||||
|
||||
const Stages = new Set();
|
||||
|
||||
for (const File of Files) {
|
||||
const Match =
|
||||
/^(\\d+)\\.png$/i.exec(
|
||||
/^(\d+)\.png$/i.exec(
|
||||
File.name
|
||||
);
|
||||
|
||||
@@ -1685,9 +1870,8 @@ async function UploadAdminPlantImages() {
|
||||
|
||||
if (Stages.has(Stage)) {
|
||||
throw new Error(
|
||||
"Stage " +
|
||||
Stage +
|
||||
" was selected more than once."
|
||||
"Stage " + Stage +
|
||||
" was selected more than once for the same direction."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1695,9 +1879,12 @@ async function UploadAdminPlantImages() {
|
||||
|
||||
Uploads.push({
|
||||
Stage: Stage,
|
||||
Direction:
|
||||
UploadSet.Direction,
|
||||
File: File
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (Error) {
|
||||
SetAdminPlantMessage(
|
||||
Error.message
|
||||
@@ -1705,6 +1892,13 @@ async function UploadAdminPlantImages() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Uploads.length === 0) {
|
||||
SetAdminPlantMessage(
|
||||
"Choose one or more PNG sprites first."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
AdminPlantImagePending = true;
|
||||
RenderAdminPlantImageManager();
|
||||
|
||||
@@ -1719,18 +1913,32 @@ async function UploadAdminPlantImages() {
|
||||
Upload.File
|
||||
);
|
||||
|
||||
await AdminPlantRequest(
|
||||
"UploadImage",
|
||||
{
|
||||
const RequestData = {
|
||||
PlantId: Plant.Id,
|
||||
Stage: Upload.Stage,
|
||||
MimeType: "image/png",
|
||||
DataBase64: DataBase64
|
||||
};
|
||||
|
||||
if (Upload.Direction !== null) {
|
||||
RequestData.Direction =
|
||||
Upload.Direction;
|
||||
}
|
||||
|
||||
await AdminPlantRequest(
|
||||
"UploadImage",
|
||||
RequestData
|
||||
);
|
||||
}
|
||||
|
||||
FileInput.value = "";
|
||||
for (
|
||||
const UploadSet
|
||||
of GetAdminPlantImageUploadSets()
|
||||
) {
|
||||
if (UploadSet.Input !== null) {
|
||||
UploadSet.Input.value = "";
|
||||
}
|
||||
}
|
||||
|
||||
localStorage.removeItem(
|
||||
"SarahtoninGardenContent"
|
||||
@@ -1818,7 +2026,8 @@ function ReadAdminPlantFileBase64(
|
||||
|
||||
async function DeleteAdminPlantImage(
|
||||
PlantId,
|
||||
Stage
|
||||
Stage,
|
||||
Direction = null
|
||||
) {
|
||||
if (AdminPlantImagePending) {
|
||||
return;
|
||||
@@ -1830,16 +2039,28 @@ async function DeleteAdminPlantImage(
|
||||
SetAdminPlantMessage(
|
||||
"Removing stage " +
|
||||
Stage +
|
||||
(
|
||||
Direction === null
|
||||
? ""
|
||||
: " " + Direction
|
||||
) +
|
||||
" sprite..."
|
||||
);
|
||||
|
||||
try {
|
||||
await AdminPlantRequest(
|
||||
"DeleteImage",
|
||||
{
|
||||
const RequestData = {
|
||||
PlantId: PlantId,
|
||||
Stage: Stage
|
||||
};
|
||||
|
||||
if (Direction !== null) {
|
||||
RequestData.Direction =
|
||||
Direction;
|
||||
}
|
||||
|
||||
await AdminPlantRequest(
|
||||
"DeleteImage",
|
||||
RequestData
|
||||
);
|
||||
|
||||
localStorage.removeItem(
|
||||
@@ -1853,6 +2074,11 @@ async function DeleteAdminPlantImage(
|
||||
SetAdminPlantMessage(
|
||||
"Stage " +
|
||||
Stage +
|
||||
(
|
||||
Direction === null
|
||||
? ""
|
||||
: " " + Direction
|
||||
) +
|
||||
" sprite removed."
|
||||
);
|
||||
} catch (Error) {
|
||||
@@ -1888,6 +2114,8 @@ function GetAdminPlantPortableData(
|
||||
Plant.Effects ?? {}
|
||||
)
|
||||
),
|
||||
DirectionalSprites:
|
||||
Plant.DirectionalSprites === true,
|
||||
Shop: {
|
||||
ShopPlant:
|
||||
Plant.ShopPlant === true,
|
||||
@@ -2060,6 +2288,9 @@ async function ImportAdminPlantJson(
|
||||
)
|
||||
)
|
||||
: {},
|
||||
DirectionalSprites:
|
||||
Imported.DirectionalSprites ===
|
||||
true,
|
||||
ShopPlant:
|
||||
Shop.ShopPlant === true ||
|
||||
Imported.ShopPlant === true,
|
||||
@@ -2120,6 +2351,11 @@ async function ImportAdminPlantJson(
|
||||
"AdminPlantShopPlant"
|
||||
).checked = Plant.ShopPlant;
|
||||
|
||||
document.getElementById(
|
||||
"AdminPlantDirectionalSprites"
|
||||
).checked =
|
||||
Plant.DirectionalSprites;
|
||||
|
||||
SetAdminPlantField(
|
||||
"AdminPlantBaseCost",
|
||||
Plant.BaseCost ?? ""
|
||||
|
||||
@@ -205,6 +205,15 @@ function ValidateAdminPlant(
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
Plant.DirectionalSprites !== undefined &&
|
||||
typeof Plant.DirectionalSprites !== "boolean"
|
||||
) {
|
||||
throw new Error(
|
||||
"Directional sprites must be On or Off."
|
||||
);
|
||||
}
|
||||
|
||||
if (Plant.ShopPlant === true) {
|
||||
ValidateAdminInteger(
|
||||
Number(Plant.BaseCost),
|
||||
|
||||
+293
-75
@@ -26,6 +26,20 @@ const GardenBorderColours = [
|
||||
"var(--Lavender)"
|
||||
];
|
||||
|
||||
const GardenPlotDefaultRotation = "East";
|
||||
const GardenPlotRotations = [
|
||||
"North",
|
||||
"East",
|
||||
"South",
|
||||
"West"
|
||||
];
|
||||
const GardenPlotRotationLabels = {
|
||||
North: "^",
|
||||
East: "->",
|
||||
South: "v",
|
||||
West: "<-"
|
||||
};
|
||||
|
||||
|
||||
function GetGardenBorderColour(
|
||||
GardenIndex
|
||||
@@ -1383,7 +1397,9 @@ function AreGardenPlotsEqual(
|
||||
return (
|
||||
A.Plant === B.Plant &&
|
||||
Number(A.PlantedAt) ===
|
||||
Number(B.PlantedAt)
|
||||
Number(B.PlantedAt) &&
|
||||
GetGardenPlotRotation(A) ===
|
||||
GetGardenPlotRotation(B)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1405,44 +1421,135 @@ function GetGardenDisplaySettings() {
|
||||
HasPlantInformation &&
|
||||
GameSave.Preferences
|
||||
.ShowGrowthTimers !==
|
||||
false,
|
||||
|
||||
ShowPlotRotation:
|
||||
HasRotationUpgrade(
|
||||
GameSave
|
||||
) &&
|
||||
GameSave.Preferences
|
||||
.ShowPlotRotation !==
|
||||
false
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function GetGardenPlotRotation(
|
||||
Plot
|
||||
) {
|
||||
return GardenPlotRotations.includes(
|
||||
Plot?.Rotation
|
||||
)
|
||||
? Plot.Rotation
|
||||
: GardenPlotDefaultRotation;
|
||||
}
|
||||
|
||||
|
||||
function GetGardenPlotRotationLabel(
|
||||
Plot
|
||||
) {
|
||||
return GardenPlotRotationLabels[
|
||||
GetGardenPlotRotation(
|
||||
Plot
|
||||
)
|
||||
] ?? "->";
|
||||
}
|
||||
|
||||
|
||||
async function RotateGardenPlot(
|
||||
PlotIndex
|
||||
) {
|
||||
if (
|
||||
!HasRotationUpgrade(
|
||||
GameSave
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ActionTime = Date.now();
|
||||
|
||||
AdvanceGameSimulation(
|
||||
GameSave,
|
||||
ActionTime
|
||||
);
|
||||
|
||||
const Plot =
|
||||
GameSave.Garden.Plots[
|
||||
PlotIndex
|
||||
];
|
||||
|
||||
if (
|
||||
Plot === null ||
|
||||
typeof Plot !== "object"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const CurrentIndex =
|
||||
GardenPlotRotations.indexOf(
|
||||
GetGardenPlotRotation(
|
||||
Plot
|
||||
)
|
||||
);
|
||||
|
||||
Plot.Rotation =
|
||||
GardenPlotRotations[
|
||||
(CurrentIndex + 1) %
|
||||
GardenPlotRotations.length
|
||||
];
|
||||
|
||||
const Plant =
|
||||
Plants[Plot.Plant];
|
||||
|
||||
SetGardenMessage(
|
||||
"Rotated " +
|
||||
(Plant?.Name ?? "plot") +
|
||||
" " +
|
||||
Plot.Rotation.toLowerCase() +
|
||||
"."
|
||||
);
|
||||
|
||||
RenderGame();
|
||||
|
||||
await SaveGame(
|
||||
GameSave
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function CreatePlotElement(
|
||||
Plot,
|
||||
PlotIndex,
|
||||
DisplaySettings,
|
||||
IsPreview = false
|
||||
) {
|
||||
const Button =
|
||||
const Tile =
|
||||
document.createElement(
|
||||
"div"
|
||||
);
|
||||
|
||||
Tile.className =
|
||||
"PlantTile GardenPlot";
|
||||
|
||||
const MainButton =
|
||||
document.createElement(
|
||||
"button"
|
||||
);
|
||||
|
||||
Button.className =
|
||||
"PlantTile GardenPlot";
|
||||
|
||||
Button.type = "button";
|
||||
MainButton.className =
|
||||
"GardenPlotMain";
|
||||
MainButton.type = "button";
|
||||
|
||||
|
||||
if (
|
||||
DisplaySettings.ShowPlantNames
|
||||
) {
|
||||
Button.classList.add(
|
||||
Tile.classList.add(
|
||||
"GardenPlotWithName"
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
DisplaySettings.ShowGrowthTimers
|
||||
) {
|
||||
Button.classList.add(
|
||||
"GardenPlotWithTimer"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const Plant =
|
||||
Plot === null
|
||||
@@ -1452,7 +1559,7 @@ function CreatePlotElement(
|
||||
if (
|
||||
DisplaySettings.ShowPlantNames
|
||||
) {
|
||||
Button.appendChild(
|
||||
MainButton.appendChild(
|
||||
CreateGardenPlotName(
|
||||
Plot,
|
||||
Plant
|
||||
@@ -1469,43 +1576,45 @@ function CreatePlotElement(
|
||||
Visual.className =
|
||||
"GardenPlotVisual";
|
||||
|
||||
Button.appendChild(
|
||||
MainButton.appendChild(
|
||||
Visual
|
||||
);
|
||||
|
||||
Tile.appendChild(
|
||||
MainButton
|
||||
);
|
||||
|
||||
|
||||
if (Plot === null) {
|
||||
Button.classList.add(
|
||||
Tile.classList.add(
|
||||
"GardenPlotEmpty"
|
||||
);
|
||||
|
||||
Button.title =
|
||||
MainButton.title =
|
||||
IsPreview
|
||||
? "Future Sight: empty plot"
|
||||
: "Empty plot";
|
||||
|
||||
if (
|
||||
DisplaySettings
|
||||
.ShowGrowthTimers
|
||||
) {
|
||||
Button.appendChild(
|
||||
CreateGardenPlotTimer(
|
||||
AppendGardenPlotFooter(
|
||||
Tile,
|
||||
null,
|
||||
null,
|
||||
0
|
||||
)
|
||||
0,
|
||||
PlotIndex,
|
||||
DisplaySettings,
|
||||
IsPreview
|
||||
);
|
||||
}
|
||||
|
||||
if (IsPreview) {
|
||||
return Button;
|
||||
MainButton.disabled = true;
|
||||
return Tile;
|
||||
}
|
||||
|
||||
if (
|
||||
SelectedGardenTool ===
|
||||
"Trowel"
|
||||
) {
|
||||
Button.addEventListener(
|
||||
MainButton.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
PlantSeed(
|
||||
@@ -1517,40 +1626,40 @@ function CreatePlotElement(
|
||||
SelectedGardenTool ===
|
||||
"MagicTrowel"
|
||||
) {
|
||||
Button.classList.add(
|
||||
Tile.classList.add(
|
||||
"GardenPlotMagicPlant"
|
||||
);
|
||||
|
||||
Button.title =
|
||||
MainButton.title =
|
||||
"Fill empty plots with the selected seed";
|
||||
|
||||
Button.addEventListener(
|
||||
MainButton.addEventListener(
|
||||
"click",
|
||||
PlantManySeeds
|
||||
);
|
||||
} else {
|
||||
MainButton.disabled = true;
|
||||
}
|
||||
|
||||
return Button;
|
||||
return Tile;
|
||||
}
|
||||
|
||||
|
||||
if (Plant === undefined) {
|
||||
Visual.textContent = "?";
|
||||
|
||||
if (
|
||||
DisplaySettings
|
||||
.ShowGrowthTimers
|
||||
) {
|
||||
Button.appendChild(
|
||||
CreateGardenPlotTimer(
|
||||
AppendGardenPlotFooter(
|
||||
Tile,
|
||||
Plot,
|
||||
null,
|
||||
0
|
||||
)
|
||||
0,
|
||||
PlotIndex,
|
||||
DisplaySettings,
|
||||
IsPreview
|
||||
);
|
||||
}
|
||||
|
||||
return Button;
|
||||
MainButton.disabled = true;
|
||||
return Tile;
|
||||
}
|
||||
|
||||
|
||||
@@ -1560,7 +1669,7 @@ function CreatePlotElement(
|
||||
Plant
|
||||
);
|
||||
|
||||
Button.style.setProperty(
|
||||
Tile.style.setProperty(
|
||||
"--GrowthProgress",
|
||||
`${Progress * 100}%`
|
||||
);
|
||||
@@ -1608,25 +1717,24 @@ function CreatePlotElement(
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
DisplaySettings.ShowGrowthTimers
|
||||
) {
|
||||
Button.appendChild(
|
||||
CreateGardenPlotTimer(
|
||||
AppendGardenPlotFooter(
|
||||
Tile,
|
||||
Plot,
|
||||
Plant,
|
||||
Progress
|
||||
)
|
||||
Progress,
|
||||
PlotIndex,
|
||||
DisplaySettings,
|
||||
IsPreview
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (IsPreview) {
|
||||
Button.title =
|
||||
MainButton.title =
|
||||
"Future Sight: " +
|
||||
Plant.Name;
|
||||
|
||||
return Button;
|
||||
MainButton.disabled = true;
|
||||
return Tile;
|
||||
}
|
||||
|
||||
|
||||
@@ -1634,15 +1742,15 @@ function CreatePlotElement(
|
||||
SelectedGardenTool ===
|
||||
"Shovel"
|
||||
) {
|
||||
Button.classList.add(
|
||||
Tile.classList.add(
|
||||
"GardenPlotRemove"
|
||||
);
|
||||
|
||||
Button.title =
|
||||
MainButton.title =
|
||||
"Remove " +
|
||||
Plant.Name;
|
||||
|
||||
Button.addEventListener(
|
||||
MainButton.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
RemovePlant(
|
||||
@@ -1651,7 +1759,7 @@ function CreatePlotElement(
|
||||
}
|
||||
);
|
||||
|
||||
return Button;
|
||||
return Tile;
|
||||
}
|
||||
|
||||
|
||||
@@ -1660,18 +1768,19 @@ function CreatePlotElement(
|
||||
"Fertilizer"
|
||||
) {
|
||||
if (Progress >= 1) {
|
||||
Button.title =
|
||||
MainButton.title =
|
||||
Plant.Name +
|
||||
" is already fully grown";
|
||||
|
||||
return Button;
|
||||
MainButton.disabled = true;
|
||||
return Tile;
|
||||
}
|
||||
|
||||
Button.classList.add(
|
||||
Tile.classList.add(
|
||||
"GardenPlotFertilize"
|
||||
);
|
||||
|
||||
Button.title =
|
||||
MainButton.title =
|
||||
"Fertilize " +
|
||||
Plant.Name +
|
||||
" (" +
|
||||
@@ -1680,7 +1789,7 @@ function CreatePlotElement(
|
||||
) +
|
||||
" minutes)";
|
||||
|
||||
Button.addEventListener(
|
||||
MainButton.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
FertilizePlant(
|
||||
@@ -1689,7 +1798,7 @@ function CreatePlotElement(
|
||||
}
|
||||
);
|
||||
|
||||
return Button;
|
||||
return Tile;
|
||||
}
|
||||
|
||||
|
||||
@@ -1697,7 +1806,7 @@ function CreatePlotElement(
|
||||
SelectedGardenTool !==
|
||||
"Trowel"
|
||||
) {
|
||||
Button.title =
|
||||
MainButton.title =
|
||||
Plant.Name +
|
||||
" - " +
|
||||
Math.floor(
|
||||
@@ -1705,22 +1814,23 @@ function CreatePlotElement(
|
||||
) +
|
||||
"% grown";
|
||||
|
||||
return Button;
|
||||
MainButton.disabled = true;
|
||||
return Tile;
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
Progress >= 1
|
||||
) {
|
||||
Button.classList.add(
|
||||
Tile.classList.add(
|
||||
"GardenPlotMature"
|
||||
);
|
||||
|
||||
Button.title =
|
||||
MainButton.title =
|
||||
"Harvest " +
|
||||
Plant.Name;
|
||||
|
||||
Button.addEventListener(
|
||||
MainButton.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
HarvestPlant(
|
||||
@@ -1729,17 +1839,114 @@ function CreatePlotElement(
|
||||
}
|
||||
);
|
||||
} else {
|
||||
Button.title =
|
||||
MainButton.title =
|
||||
Plant.Name +
|
||||
" - " +
|
||||
Math.floor(
|
||||
Progress * 100
|
||||
) +
|
||||
"% grown";
|
||||
|
||||
MainButton.disabled = true;
|
||||
}
|
||||
|
||||
|
||||
return Button;
|
||||
return Tile;
|
||||
}
|
||||
|
||||
|
||||
function AppendGardenPlotFooter(
|
||||
Tile,
|
||||
Plot,
|
||||
Plant,
|
||||
Progress,
|
||||
PlotIndex,
|
||||
DisplaySettings,
|
||||
IsPreview
|
||||
) {
|
||||
const ShowTimer =
|
||||
DisplaySettings.ShowGrowthTimers;
|
||||
|
||||
const ShowRotation =
|
||||
Plot !== null &&
|
||||
DisplaySettings.ShowPlotRotation &&
|
||||
!IsPreview;
|
||||
|
||||
if (
|
||||
!ShowTimer &&
|
||||
!ShowRotation
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
Tile.classList.add(
|
||||
"GardenPlotWithFooter"
|
||||
);
|
||||
|
||||
const Footer =
|
||||
document.createElement(
|
||||
"span"
|
||||
);
|
||||
|
||||
Footer.className =
|
||||
"GardenPlotFooter";
|
||||
|
||||
if (
|
||||
ShowTimer &&
|
||||
ShowRotation
|
||||
) {
|
||||
Footer.classList.add(
|
||||
"GardenPlotFooterSplit"
|
||||
);
|
||||
}
|
||||
|
||||
if (ShowTimer) {
|
||||
Footer.appendChild(
|
||||
CreateGardenPlotTimer(
|
||||
Plot,
|
||||
Plant,
|
||||
Progress
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (ShowRotation) {
|
||||
const RotateButton =
|
||||
document.createElement(
|
||||
"button"
|
||||
);
|
||||
|
||||
RotateButton.className =
|
||||
"GardenPlotRotationButton";
|
||||
RotateButton.type = "button";
|
||||
RotateButton.textContent =
|
||||
GetGardenPlotRotationLabel(
|
||||
Plot
|
||||
);
|
||||
RotateButton.title =
|
||||
"Rotate plot clockwise";
|
||||
RotateButton.setAttribute(
|
||||
"aria-label",
|
||||
"Rotate " +
|
||||
(Plant?.Name ?? "plot") +
|
||||
" clockwise"
|
||||
);
|
||||
|
||||
RotateButton.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
RotateGardenPlot(
|
||||
PlotIndex
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Footer.appendChild(
|
||||
RotateButton
|
||||
);
|
||||
}
|
||||
|
||||
Tile.appendChild(Footer);
|
||||
}
|
||||
|
||||
|
||||
@@ -1946,7 +2153,8 @@ async function PlantSeed(
|
||||
PlotIndex
|
||||
] = {
|
||||
Plant: PlantKey,
|
||||
PlantedAt: ActionTime
|
||||
PlantedAt: ActionTime,
|
||||
Rotation: GardenPlotDefaultRotation
|
||||
};
|
||||
|
||||
|
||||
@@ -2072,7 +2280,8 @@ async function PlantManySeeds() {
|
||||
EmptyPlotIndexes[Index]
|
||||
] = {
|
||||
Plant: PlantKey,
|
||||
PlantedAt: PlantedAt
|
||||
PlantedAt: PlantedAt,
|
||||
Rotation: GardenPlotDefaultRotation
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2597,8 +2806,17 @@ function GetPlantImages(
|
||||
Plot,
|
||||
Plant
|
||||
) {
|
||||
const Direction =
|
||||
Plot !== null &&
|
||||
Plant?.DirectionalSprites === true
|
||||
? GetGardenPlotRotation(
|
||||
Plot
|
||||
)
|
||||
: null;
|
||||
|
||||
return GetPlantImageSources(
|
||||
Plant ?? Plot.Plant
|
||||
Plant ?? Plot.Plant,
|
||||
Direction
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -768,6 +768,27 @@ function ApplyMutationResult(
|
||||
continue;
|
||||
}
|
||||
|
||||
const ExistingPlot =
|
||||
SaveData.Garden.Plots[
|
||||
PlotIndex
|
||||
];
|
||||
|
||||
if (
|
||||
NewPlot !== null &&
|
||||
typeof NewPlot === "object" &&
|
||||
typeof NewPlot.Rotation !==
|
||||
"string"
|
||||
) {
|
||||
NewPlot.Rotation =
|
||||
ExistingPlot !== null &&
|
||||
typeof ExistingPlot ===
|
||||
"object" &&
|
||||
typeof ExistingPlot.Rotation ===
|
||||
"string"
|
||||
? ExistingPlot.Rotation
|
||||
: "East";
|
||||
}
|
||||
|
||||
|
||||
SaveData.Garden.Plots[
|
||||
PlotIndex
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
let Plants = {};
|
||||
|
||||
const PlantDirectionCodes = {
|
||||
North: "N",
|
||||
East: "E",
|
||||
South: "S",
|
||||
West: "W"
|
||||
};
|
||||
|
||||
|
||||
|
||||
async function FetchPlantsFromApi() {
|
||||
const Response =
|
||||
@@ -50,7 +58,8 @@ async function FetchPlantsFromApi() {
|
||||
}
|
||||
|
||||
function GetPlantImageSources(
|
||||
PlantOrKey
|
||||
PlantOrKey,
|
||||
Direction = null
|
||||
) {
|
||||
const PlantKey =
|
||||
typeof PlantOrKey === "string"
|
||||
@@ -86,10 +95,43 @@ function GetPlantImageSources(
|
||||
? Plant.ImageStages
|
||||
: [];
|
||||
|
||||
const DirectionCode =
|
||||
typeof Direction === "string"
|
||||
? (
|
||||
PlantDirectionCodes[
|
||||
Direction
|
||||
] ??
|
||||
Direction.toUpperCase()
|
||||
)
|
||||
: null;
|
||||
|
||||
const DirectionalStages =
|
||||
Plant?.DirectionalSprites === true &&
|
||||
DirectionCode !== null &&
|
||||
Array.isArray(
|
||||
Plant?.DirectionalImageStages
|
||||
)
|
||||
? Plant.DirectionalImageStages
|
||||
.filter(
|
||||
ImageStage =>
|
||||
String(
|
||||
ImageStage?.Direction ??
|
||||
""
|
||||
).toUpperCase() ===
|
||||
DirectionCode
|
||||
)
|
||||
: [];
|
||||
|
||||
let HighestStage =
|
||||
StaticImages.length;
|
||||
|
||||
for (const ImageStage of ApiStages) {
|
||||
for (
|
||||
const ImageStage
|
||||
of [
|
||||
...ApiStages,
|
||||
...DirectionalStages
|
||||
]
|
||||
) {
|
||||
const Stage = Number(
|
||||
typeof ImageStage === "object" &&
|
||||
ImageStage !== null
|
||||
@@ -156,10 +198,46 @@ function GetPlantImageSources(
|
||||
Query.toString();
|
||||
}
|
||||
|
||||
for (
|
||||
const ImageStage
|
||||
of DirectionalStages
|
||||
) {
|
||||
const Stage = Number(
|
||||
ImageStage?.Stage
|
||||
);
|
||||
|
||||
if (
|
||||
!Number.isInteger(Stage) ||
|
||||
Stage < 1
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const Revision = Number(
|
||||
ImageStage?.Revision ?? 0
|
||||
);
|
||||
|
||||
const Query =
|
||||
new URLSearchParams({
|
||||
PlantId: String(Plant.Id),
|
||||
Stage: String(Stage),
|
||||
Direction: DirectionCode,
|
||||
v: String(
|
||||
Number.isFinite(Revision)
|
||||
? Revision
|
||||
: 0
|
||||
)
|
||||
});
|
||||
|
||||
Images[Stage - 1] =
|
||||
ApiUrl +
|
||||
"/PlantImage.php?" +
|
||||
Query.toString();
|
||||
}
|
||||
|
||||
return Images;
|
||||
}
|
||||
|
||||
|
||||
function GetPlantMatureImageSource(
|
||||
PlantOrKey
|
||||
) {
|
||||
|
||||
@@ -92,6 +92,10 @@ function BindGardenDisplayPreferences() {
|
||||
"ToggleGrowthTimersButton",
|
||||
"ShowGrowthTimers"
|
||||
],
|
||||
[
|
||||
"TogglePlotRotationButton",
|
||||
"ShowPlotRotation"
|
||||
],
|
||||
[
|
||||
"ToggleNextHarvestButton",
|
||||
"ShowNextHarvest"
|
||||
@@ -150,6 +154,7 @@ function BindGardenDisplayPreferences() {
|
||||
function RenderGardenDisplayPreferences() {
|
||||
RenderToolSelectionPreferences();
|
||||
RenderPlantInformationPreferences();
|
||||
RenderPlotRotationPreference();
|
||||
RenderGardenOverviewPreferences();
|
||||
}
|
||||
|
||||
@@ -262,6 +267,44 @@ function RenderPlantInformationPreferences() {
|
||||
}
|
||||
|
||||
|
||||
function RenderPlotRotationPreference() {
|
||||
const Button =
|
||||
document.getElementById(
|
||||
"TogglePlotRotationButton"
|
||||
);
|
||||
|
||||
const Message =
|
||||
document.getElementById(
|
||||
"PlotRotationDisplayMessage"
|
||||
);
|
||||
|
||||
if (
|
||||
Button === null ||
|
||||
Message === null
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const IsOwned =
|
||||
HasRotationUpgrade(
|
||||
ProfileSave
|
||||
);
|
||||
|
||||
SetPreferenceButtonState(
|
||||
Button,
|
||||
"Plot rotation",
|
||||
ProfileSave.Preferences
|
||||
.ShowPlotRotation !== false,
|
||||
!IsOwned
|
||||
);
|
||||
|
||||
Message.textContent =
|
||||
IsOwned
|
||||
? "Plot directions can be hidden without changing their saved rotation."
|
||||
: "Unlock Plot rotation in the Shop to use this setting.";
|
||||
}
|
||||
|
||||
|
||||
function RenderGardenOverviewPreferences() {
|
||||
const Settings = [
|
||||
[
|
||||
@@ -378,6 +421,10 @@ async function ToggleGardenDisplayPreference(
|
||||
"ShowGrowthTimers"
|
||||
];
|
||||
|
||||
const RotationPreferences = [
|
||||
"ShowPlotRotation"
|
||||
];
|
||||
|
||||
const GardenOverviewPreferences = [
|
||||
"ShowNextHarvest",
|
||||
"ShowGardenSize",
|
||||
@@ -398,6 +445,11 @@ async function ToggleGardenDisplayPreference(
|
||||
PreferenceName
|
||||
);
|
||||
|
||||
const IsRotationPreference =
|
||||
RotationPreferences.includes(
|
||||
PreferenceName
|
||||
);
|
||||
|
||||
const IsGardenOverviewPreference =
|
||||
GardenOverviewPreferences.includes(
|
||||
PreferenceName
|
||||
@@ -407,6 +459,7 @@ async function ToggleGardenDisplayPreference(
|
||||
if (
|
||||
!IsToolSelectionPreference &&
|
||||
!IsPlantInformationPreference &&
|
||||
!IsRotationPreference &&
|
||||
!IsGardenOverviewPreference
|
||||
) {
|
||||
return;
|
||||
@@ -421,6 +474,15 @@ async function ToggleGardenDisplayPreference(
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
IsRotationPreference &&
|
||||
!HasRotationUpgrade(
|
||||
ProfileSave
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
IsGardenOverviewPreference &&
|
||||
!HasGardenOverviewUpgrade(
|
||||
|
||||
+26
-3
@@ -15,7 +15,7 @@ const MaximumGardenNameLength = 32;
|
||||
const StartingGardenWidth = 4;
|
||||
const StartingGardenHeight = 4;
|
||||
|
||||
const CurrentSaveVersion = 6;
|
||||
const CurrentSaveVersion = 7;
|
||||
|
||||
|
||||
function CreateSimulationRandomState() {
|
||||
@@ -274,6 +274,20 @@ function NormalizeGardenData(
|
||||
Date.now()
|
||||
);
|
||||
|
||||
const ValidRotations = [
|
||||
"North",
|
||||
"East",
|
||||
"South",
|
||||
"West"
|
||||
];
|
||||
|
||||
Plot.Rotation =
|
||||
ValidRotations.includes(
|
||||
Plot.Rotation
|
||||
)
|
||||
? Plot.Rotation
|
||||
: "East";
|
||||
|
||||
delete Plot.VisualVariant;
|
||||
delete Plot.AddedTags;
|
||||
}
|
||||
@@ -571,7 +585,8 @@ function CreateNewSave() {
|
||||
GardenOverview: false,
|
||||
GardenEconomy: false,
|
||||
MutationHints: false,
|
||||
MutationHintsLevel: 0
|
||||
MutationHintsLevel: 0,
|
||||
Rotation: false
|
||||
},
|
||||
|
||||
Preferences: {
|
||||
@@ -584,7 +599,8 @@ function CreateNewSave() {
|
||||
ShowGrowingPlots: true,
|
||||
ShowReadyPlots: true,
|
||||
SelectQuickBoughtPlant: true,
|
||||
SelectTrowelWithInventoryPlant: true
|
||||
SelectTrowelWithInventoryPlant: true,
|
||||
ShowPlotRotation: true
|
||||
},
|
||||
|
||||
Gardens: [
|
||||
@@ -831,6 +847,9 @@ function NormalizeSaveData(
|
||||
SaveData.Upgrades.MutationHints ===
|
||||
true;
|
||||
|
||||
SaveData.Upgrades.Rotation =
|
||||
SaveData.Upgrades.Rotation === true;
|
||||
|
||||
SaveData.Upgrades.MutationHintsLevel =
|
||||
Math.max(
|
||||
0,
|
||||
@@ -888,6 +907,10 @@ function NormalizeSaveData(
|
||||
SaveData.Preferences.SelectTrowelWithInventoryPlant !==
|
||||
false;
|
||||
|
||||
SaveData.Preferences.ShowPlotRotation =
|
||||
SaveData.Preferences.ShowPlotRotation !==
|
||||
false;
|
||||
|
||||
|
||||
const LegacyGarden =
|
||||
SaveData.Garden ??
|
||||
|
||||
@@ -1114,6 +1114,7 @@ function RenderGardenUpgrades() {
|
||||
"Row"
|
||||
),
|
||||
CreateNewGardenCard(),
|
||||
CreateRotationUpgradeCard(),
|
||||
CreatePlantInformationUpgradeCard(),
|
||||
CreateGardenOverviewUpgradeCard(),
|
||||
CreateGardenEconomyUpgradeCard(),
|
||||
@@ -1377,6 +1378,117 @@ function CreateNewGardenCard() {
|
||||
}
|
||||
|
||||
|
||||
function CreateRotationUpgradeCard() {
|
||||
const Cost =
|
||||
GetRotationUpgradeCost();
|
||||
|
||||
const IsOwned =
|
||||
HasRotationUpgrade(
|
||||
ShopSave
|
||||
);
|
||||
|
||||
const Card =
|
||||
document.createElement(
|
||||
"article"
|
||||
);
|
||||
|
||||
Card.className =
|
||||
"Panel ShopItem";
|
||||
|
||||
const Header =
|
||||
document.createElement(
|
||||
"div"
|
||||
);
|
||||
|
||||
Header.className =
|
||||
"PanelHeader PanelHeaderInset ShopItemHeader" +
|
||||
(
|
||||
IsOwned
|
||||
? " ShopItemHeaderOwned"
|
||||
: ""
|
||||
);
|
||||
|
||||
const Name =
|
||||
document.createElement(
|
||||
"h3"
|
||||
);
|
||||
|
||||
Name.textContent =
|
||||
"Plot rotation";
|
||||
|
||||
Header.appendChild(Name);
|
||||
|
||||
const Description =
|
||||
document.createElement(
|
||||
"p"
|
||||
);
|
||||
|
||||
Description.textContent =
|
||||
"Rotate planted plots clockwise. Direction is stored per plot and can be shown beside the plot timer.";
|
||||
|
||||
const Details =
|
||||
document.createElement(
|
||||
"div"
|
||||
);
|
||||
|
||||
Details.className =
|
||||
"ShopItemDetails";
|
||||
|
||||
Details.append(
|
||||
CreateShopStat(
|
||||
"Current",
|
||||
IsOwned
|
||||
? "Rotation unlocked"
|
||||
: "Rotation locked"
|
||||
),
|
||||
CreateShopStat(
|
||||
"Next",
|
||||
IsOwned
|
||||
? "Purchased"
|
||||
: "Rotation controls"
|
||||
)
|
||||
);
|
||||
|
||||
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",
|
||||
BuyRotationUpgrade
|
||||
);
|
||||
}
|
||||
|
||||
Card.append(
|
||||
Header,
|
||||
Description,
|
||||
Details,
|
||||
BuyButton
|
||||
);
|
||||
|
||||
return Card;
|
||||
}
|
||||
|
||||
|
||||
function CreatePlantInformationUpgradeCard() {
|
||||
const Cost =
|
||||
GetPlantInformationUpgradeCost();
|
||||
@@ -2854,6 +2966,55 @@ async function BuyNewGarden() {
|
||||
}
|
||||
|
||||
|
||||
async function BuyRotationUpgrade() {
|
||||
if (
|
||||
ShopPurchasePending ||
|
||||
HasRotationUpgrade(
|
||||
ShopSave
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Cost =
|
||||
GetRotationUpgradeCost();
|
||||
|
||||
if (ShopSave.Currency.Dew < Cost) {
|
||||
SetShopMessage(
|
||||
"You don't have enough Dew for Plot rotation."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
ShopPurchasePending = true;
|
||||
|
||||
try {
|
||||
ShopSave.Currency.Dew -= Cost;
|
||||
ShopSave.Statistics.CurrencySpent.Dew +=
|
||||
Cost;
|
||||
|
||||
UnlockRotation(
|
||||
ShopSave
|
||||
);
|
||||
|
||||
SetShopMessage(
|
||||
"Bought Plot rotation for " +
|
||||
Cost.toLocaleString() +
|
||||
" Dew. Rotation controls are now visible on planted plots."
|
||||
);
|
||||
|
||||
RenderShop();
|
||||
|
||||
await SaveGame(
|
||||
ShopSave
|
||||
);
|
||||
} finally {
|
||||
ShopPurchasePending = false;
|
||||
RenderShop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function BuyPlantInformationUpgrade() {
|
||||
if (
|
||||
ShopPurchasePending ||
|
||||
|
||||
@@ -27,6 +27,7 @@ const MutationHintsUnlockCost = 1500;
|
||||
const MutationHintsUpgradeCostMultiplier = 2;
|
||||
const MutationHintsMaximumLevel = 1;
|
||||
const MutationHintsMaximumVisible = 10;
|
||||
const RotationUnlockCost = 1200;
|
||||
const NewGardenBaseCost = 4000;
|
||||
const NewGardenCostMultiplier = 1.5;
|
||||
|
||||
@@ -175,6 +176,34 @@ function AddNewGarden(
|
||||
}
|
||||
|
||||
|
||||
function HasRotationUpgrade(
|
||||
SaveData
|
||||
) {
|
||||
return (
|
||||
SaveData
|
||||
?.Upgrades
|
||||
?.Rotation === true
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function GetRotationUpgradeCost() {
|
||||
return RotationUnlockCost;
|
||||
}
|
||||
|
||||
|
||||
function UnlockRotation(
|
||||
SaveData
|
||||
) {
|
||||
SaveData.Upgrades ??= {};
|
||||
SaveData.Preferences ??= {};
|
||||
|
||||
SaveData.Upgrades.Rotation = true;
|
||||
SaveData.Preferences.ShowPlotRotation =
|
||||
true;
|
||||
}
|
||||
|
||||
|
||||
function HasPlantInformationUpgrade(
|
||||
SaveData
|
||||
) {
|
||||
|
||||
+74
-8
@@ -634,10 +634,30 @@ input, button, textarea, select {
|
||||
}
|
||||
|
||||
.GardenPlotWithName,
|
||||
.GardenPlotWithTimer {
|
||||
.GardenPlotWithFooter {
|
||||
aspect-ratio: auto;
|
||||
}
|
||||
|
||||
.GardenPlotMain {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
cursor: inherit;
|
||||
color: inherit;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
font: inherit;
|
||||
appearance: none;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.GardenPlotMain:disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.GardenPlotVisual {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
@@ -650,7 +670,8 @@ input, button, textarea, select {
|
||||
}
|
||||
|
||||
.GardenPlotName,
|
||||
.GardenPlotTimer {
|
||||
.GardenPlotTimer,
|
||||
.GardenPlotRotationButton {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
min-height: 1.5rem;
|
||||
@@ -663,6 +684,10 @@ input, button, textarea, select {
|
||||
font-size: 0.75rem;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.GardenPlotName,
|
||||
.GardenPlotTimer {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
@@ -671,10 +696,41 @@ input, button, textarea, select {
|
||||
border-bottom: 2px solid var(--Surface2);
|
||||
}
|
||||
|
||||
.GardenPlotTimer {
|
||||
.GardenPlotFooter {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
min-width: 0;
|
||||
background-color: var(--Mantle);
|
||||
border-top: 2px solid var(--Surface2);
|
||||
}
|
||||
|
||||
.GardenPlotFooterSplit {
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
}
|
||||
|
||||
.GardenPlotRotationButton {
|
||||
padding-inline: 0.45rem;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
.GardenPlotFooterSplit .GardenPlotRotationButton {
|
||||
border-left: 2px solid var(--Surface2);
|
||||
}
|
||||
|
||||
.GardenPlotRotationButton:hover {
|
||||
color: var(--Sky);
|
||||
background-color: var(--Surface0);
|
||||
}
|
||||
|
||||
.GardenPlotRotationButton:focus-visible,
|
||||
.GardenPlotMain:focus-visible {
|
||||
outline: 3px solid var(--Yellow);
|
||||
outline-offset: -3px;
|
||||
}
|
||||
|
||||
.GardenPlotMature .GardenPlotTimer {
|
||||
color: var(--Green);
|
||||
}
|
||||
@@ -686,11 +742,6 @@ input, button, textarea, select {
|
||||
box-shadow: 2px 2px 0 var(--Crust);
|
||||
}
|
||||
|
||||
.GardenPlot:focus-visible {
|
||||
outline: 3px solid var(--Yellow);
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
.GardenPlotEmpty {
|
||||
--TileBackground: var(--Mantle);
|
||||
}
|
||||
@@ -2304,6 +2355,17 @@ input, button, textarea, select {
|
||||
flex: 1 1 18rem;
|
||||
}
|
||||
|
||||
.AdminPlantDirectionalImageUpload {
|
||||
display: grid;
|
||||
grid-template-columns: repeat( 2, minmax(0, 1fr) );
|
||||
gap: 0.75rem;
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.AdminPlantDirectionalImageUpload .AdminEditorField {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.AdminPlantImageNote {
|
||||
color: var(--Overlay2);
|
||||
}
|
||||
@@ -2350,6 +2412,10 @@ input, button, textarea, select {
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.AdminPlantDirectionalImageUpload {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.AdminPlantImageRemoveButton {
|
||||
grid-column: 1 / -1;
|
||||
width: 100%;
|
||||
|
||||
Reference in new issue
Block a user