mirror of
https://git.srhtnin.garden/sarah/srhtnin.github.io.git
synced 2026-10-13 18:38:11 +02:00
bleh
This commit is contained in:
1 parent
17c29270cb
commit
b0bc6ecc03
13 files changed
+1809
-168
No files matched your search
@@ -132,17 +132,53 @@ $PageSection = "Admin";
|
|||||||
>
|
>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="AdminEditorField">
|
<div class="AdminEditorField">
|
||||||
<span>Cooldown (ms)</span>
|
<span>Cooldown</span>
|
||||||
|
|
||||||
|
<div class="AdminDurationInputs">
|
||||||
|
<label>
|
||||||
|
<span>Hours</span>
|
||||||
|
<input
|
||||||
|
id="AdminMutationCooldownHours"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="1"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<span>Minutes</span>
|
||||||
|
<input
|
||||||
|
id="AdminMutationCooldownMinutes"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
max="59"
|
||||||
|
step="1"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<span>Seconds</span>
|
||||||
|
<input
|
||||||
|
id="AdminMutationCooldownSeconds"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
max="59"
|
||||||
|
step="1"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
id="AdminMutationCooldown"
|
id="AdminMutationCooldown"
|
||||||
type="number"
|
type="hidden"
|
||||||
min="0"
|
|
||||||
step="1000"
|
|
||||||
required
|
|
||||||
>
|
>
|
||||||
|
|
||||||
<small id="AdminMutationCooldownHint"></small>
|
<small id="AdminMutationCooldownHint"></small>
|
||||||
</label>
|
</div>
|
||||||
|
|
||||||
<label class="AdminEditorField">
|
<label class="AdminEditorField">
|
||||||
<span>Rotation</span>
|
<span>Rotation</span>
|
||||||
|
|||||||
+131
-5
@@ -80,7 +80,7 @@ $PageSection = "Admin";
|
|||||||
required
|
required
|
||||||
>
|
>
|
||||||
<small>
|
<small>
|
||||||
Used by saves, recipes and the sprite folder.
|
Used by saves, recipes and repository sprite fallbacks.
|
||||||
</small>
|
</small>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
@@ -118,17 +118,69 @@ $PageSection = "Admin";
|
|||||||
</small>
|
</small>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="AdminEditorField">
|
<div class="AdminEditorField">
|
||||||
<span>Growth time (ms)</span>
|
<span>Growth time</span>
|
||||||
|
|
||||||
|
<div class="AdminDurationInputs">
|
||||||
|
<label>
|
||||||
|
<span>Hours</span>
|
||||||
|
<input
|
||||||
|
id="AdminPlantGrowthHours"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="1"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<span>Minutes</span>
|
||||||
|
<input
|
||||||
|
id="AdminPlantGrowthMinutes"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
max="59"
|
||||||
|
step="1"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<span>Seconds</span>
|
||||||
|
<input
|
||||||
|
id="AdminPlantGrowthSeconds"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
max="59"
|
||||||
|
step="1"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
id="AdminPlantGrowthTime"
|
id="AdminPlantGrowthTime"
|
||||||
name="GrowthTime"
|
name="GrowthTime"
|
||||||
|
type="hidden"
|
||||||
|
>
|
||||||
|
|
||||||
|
<small id="AdminPlantGrowthTimeHint"></small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="AdminEditorField">
|
||||||
|
<span>Harvest multiplier</span>
|
||||||
|
<input
|
||||||
|
id="AdminPlantHarvestMultiplier"
|
||||||
|
name="HarvestMultiplier"
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
step="1000"
|
max="9999.9999"
|
||||||
|
step="0.0001"
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
<small id="AdminPlantGrowthTimeHint"></small>
|
<small>
|
||||||
|
Defaults to 1.5. Reward is seed cost × this multiplier.
|
||||||
|
</small>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="AdminEditorField">
|
<label class="AdminEditorField">
|
||||||
@@ -189,6 +241,78 @@ $PageSection = "Admin";
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="AdminPlantEconomyPreview">
|
||||||
|
<h3>Economy</h3>
|
||||||
|
|
||||||
|
<dl>
|
||||||
|
<div>
|
||||||
|
<dt>Seed cost</dt>
|
||||||
|
<dd id="AdminPlantEconomyCost">Unavailable</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt>Harvest multiplier</dt>
|
||||||
|
<dd id="AdminPlantEconomyMultiplier">1.5×</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt>Harvest reward</dt>
|
||||||
|
<dd id="AdminPlantEconomyReward">Unavailable</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt>Net profit</dt>
|
||||||
|
<dd id="AdminPlantEconomyProfit">Unavailable</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt>Growth time</dt>
|
||||||
|
<dd id="AdminPlantEconomyGrowth">Unavailable</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt>DPH</dt>
|
||||||
|
<dd id="AdminPlantEconomyDph">Unavailable</dd>
|
||||||
|
</div>
|
||||||
|
<div class="AdminPlantEconomySource">
|
||||||
|
<dt>Price source</dt>
|
||||||
|
<dd id="AdminPlantEconomySource">Unavailable</dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="AdminPlantImagesSection">
|
||||||
|
<h2>Sprites</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
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>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="AdminPlantImageUpload">
|
||||||
|
<input
|
||||||
|
id="AdminPlantImageFiles"
|
||||||
|
type="file"
|
||||||
|
accept="image/png,.png"
|
||||||
|
multiple
|
||||||
|
>
|
||||||
|
|
||||||
|
<button
|
||||||
|
id="AdminPlantImageUploadButton"
|
||||||
|
class="ActionButton AdminInlineButton"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
Upload selected sprites
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p
|
||||||
|
id="AdminPlantImageNote"
|
||||||
|
class="AdminPlantImageNote"
|
||||||
|
></p>
|
||||||
|
|
||||||
|
<div
|
||||||
|
id="AdminPlantImageList"
|
||||||
|
class="AdminPlantImageList"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="AdminEditorActions">
|
<div class="AdminEditorActions">
|
||||||
@@ -219,7 +343,9 @@ $PageSection = "Admin";
|
|||||||
|
|
||||||
<script src="/Scripts/Garden/PlantImages.js"></script>
|
<script src="/Scripts/Garden/PlantImages.js"></script>
|
||||||
<script src="/Scripts/Garden/Plants.js"></script>
|
<script src="/Scripts/Garden/Plants.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/Economy.js"></script>
|
||||||
<script src="/Scripts/Garden/AdminPlantEditor.js"></script>
|
<script src="/Scripts/Garden/AdminPlantEditor.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -175,12 +175,34 @@ function BindAdminMutationEditor() {
|
|||||||
RenderAdminMutationJsonPreview
|
RenderAdminMutationJsonPreview
|
||||||
);
|
);
|
||||||
|
|
||||||
document.getElementById(
|
for (
|
||||||
"AdminMutationCooldown"
|
const ElementId
|
||||||
).addEventListener(
|
of [
|
||||||
"input",
|
"AdminMutationCooldownHours",
|
||||||
UpdateAdminMutationCooldownHint
|
"AdminMutationCooldownMinutes",
|
||||||
);
|
"AdminMutationCooldownSeconds"
|
||||||
|
]
|
||||||
|
) {
|
||||||
|
document.getElementById(
|
||||||
|
ElementId
|
||||||
|
).addEventListener(
|
||||||
|
"input",
|
||||||
|
() => {
|
||||||
|
SyncAdminMutationCooldown();
|
||||||
|
RenderAdminMutationJsonPreview();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
document.getElementById(
|
||||||
|
ElementId
|
||||||
|
).addEventListener(
|
||||||
|
"change",
|
||||||
|
() => {
|
||||||
|
SyncAdminMutationCooldown();
|
||||||
|
RenderAdminMutationJsonPreview();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -645,8 +667,7 @@ function LoadAdminMutationIntoForm(
|
|||||||
) * 100
|
) * 100
|
||||||
);
|
);
|
||||||
|
|
||||||
SetAdminMutationField(
|
SetAdminMutationDurationFields(
|
||||||
"AdminMutationCooldown",
|
|
||||||
Number(
|
Number(
|
||||||
Mutation.Cooldown ?? 0
|
Mutation.Cooldown ?? 0
|
||||||
)
|
)
|
||||||
@@ -769,8 +790,7 @@ function StartNewAdminMutation() {
|
|||||||
100
|
100
|
||||||
);
|
);
|
||||||
|
|
||||||
SetAdminMutationField(
|
SetAdminMutationDurationFields(
|
||||||
"AdminMutationCooldown",
|
|
||||||
120000
|
120000
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1403,21 +1423,11 @@ function GetAdminPlantName(
|
|||||||
function GetAdminMutationPlantImage(
|
function GetAdminMutationPlantImage(
|
||||||
PlantKey
|
PlantKey
|
||||||
) {
|
) {
|
||||||
const Images =
|
return GetPlantMatureImageSource(
|
||||||
PlantImages[
|
AdminMutationPlantCatalogue[
|
||||||
PlantKey
|
PlantKey
|
||||||
] ?? [];
|
] ?? PlantKey
|
||||||
|
);
|
||||||
if (
|
|
||||||
!Array.isArray(Images) ||
|
|
||||||
Images.length === 0
|
|
||||||
) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Images[
|
|
||||||
Images.length - 1
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -2179,6 +2189,101 @@ function RenderAdminMutationJsonPreview() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function SetAdminMutationDurationFields(
|
||||||
|
Milliseconds
|
||||||
|
) {
|
||||||
|
Milliseconds = Math.max(
|
||||||
|
0,
|
||||||
|
Number(Milliseconds) || 0
|
||||||
|
);
|
||||||
|
|
||||||
|
const TotalSeconds =
|
||||||
|
Math.round(
|
||||||
|
Milliseconds / 1000
|
||||||
|
);
|
||||||
|
|
||||||
|
SetAdminMutationField(
|
||||||
|
"AdminMutationCooldownHours",
|
||||||
|
Math.floor(
|
||||||
|
TotalSeconds / 3600
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
SetAdminMutationField(
|
||||||
|
"AdminMutationCooldownMinutes",
|
||||||
|
Math.floor(
|
||||||
|
(TotalSeconds % 3600) / 60
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
SetAdminMutationField(
|
||||||
|
"AdminMutationCooldownSeconds",
|
||||||
|
TotalSeconds % 60
|
||||||
|
);
|
||||||
|
|
||||||
|
SetAdminMutationField(
|
||||||
|
"AdminMutationCooldown",
|
||||||
|
TotalSeconds * 1000
|
||||||
|
);
|
||||||
|
|
||||||
|
UpdateAdminMutationCooldownHint();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function SyncAdminMutationCooldown() {
|
||||||
|
const Hours = Number(
|
||||||
|
document.getElementById(
|
||||||
|
"AdminMutationCooldownHours"
|
||||||
|
).value
|
||||||
|
);
|
||||||
|
|
||||||
|
const Minutes = Number(
|
||||||
|
document.getElementById(
|
||||||
|
"AdminMutationCooldownMinutes"
|
||||||
|
).value
|
||||||
|
);
|
||||||
|
|
||||||
|
const Seconds = Number(
|
||||||
|
document.getElementById(
|
||||||
|
"AdminMutationCooldownSeconds"
|
||||||
|
).value
|
||||||
|
);
|
||||||
|
|
||||||
|
const Values = [
|
||||||
|
Hours,
|
||||||
|
Minutes,
|
||||||
|
Seconds
|
||||||
|
];
|
||||||
|
|
||||||
|
if (
|
||||||
|
Values.some(
|
||||||
|
Value =>
|
||||||
|
!Number.isFinite(Value)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
SetAdminMutationField(
|
||||||
|
"AdminMutationCooldown",
|
||||||
|
""
|
||||||
|
);
|
||||||
|
|
||||||
|
UpdateAdminMutationCooldownHint();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Milliseconds =
|
||||||
|
Math.max(0, Hours) * 3600000 +
|
||||||
|
Math.max(0, Minutes) * 60000 +
|
||||||
|
Math.max(0, Seconds) * 1000;
|
||||||
|
|
||||||
|
SetAdminMutationField(
|
||||||
|
"AdminMutationCooldown",
|
||||||
|
Milliseconds
|
||||||
|
);
|
||||||
|
|
||||||
|
UpdateAdminMutationCooldownHint();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function UpdateAdminMutationCooldownHint() {
|
function UpdateAdminMutationCooldownHint() {
|
||||||
const Cooldown = Number(
|
const Cooldown = Number(
|
||||||
document.getElementById(
|
document.getElementById(
|
||||||
@@ -2190,76 +2295,12 @@ function UpdateAdminMutationCooldownHint() {
|
|||||||
"AdminMutationCooldownHint"
|
"AdminMutationCooldownHint"
|
||||||
).textContent =
|
).textContent =
|
||||||
Number.isFinite(Cooldown)
|
Number.isFinite(Cooldown)
|
||||||
? FormatAdminMutationDuration(
|
? Cooldown.toLocaleString() +
|
||||||
Cooldown
|
" ms stored"
|
||||||
)
|
|
||||||
: "";
|
: "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function FormatAdminMutationDuration(
|
|
||||||
Milliseconds
|
|
||||||
) {
|
|
||||||
Milliseconds = Math.max(
|
|
||||||
0,
|
|
||||||
Number(Milliseconds)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (Milliseconds < 1000) {
|
|
||||||
return Milliseconds + "ms";
|
|
||||||
}
|
|
||||||
|
|
||||||
const TotalSeconds =
|
|
||||||
Math.round(
|
|
||||||
Milliseconds /
|
|
||||||
1000
|
|
||||||
);
|
|
||||||
|
|
||||||
const Hours =
|
|
||||||
Math.floor(
|
|
||||||
TotalSeconds /
|
|
||||||
3600
|
|
||||||
);
|
|
||||||
|
|
||||||
const Minutes =
|
|
||||||
Math.floor(
|
|
||||||
(
|
|
||||||
TotalSeconds %
|
|
||||||
3600
|
|
||||||
) /
|
|
||||||
60
|
|
||||||
);
|
|
||||||
|
|
||||||
const Seconds =
|
|
||||||
TotalSeconds % 60;
|
|
||||||
|
|
||||||
const Parts = [];
|
|
||||||
|
|
||||||
if (Hours > 0) {
|
|
||||||
Parts.push(
|
|
||||||
Hours + "h"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Minutes > 0) {
|
|
||||||
Parts.push(
|
|
||||||
Minutes + "m"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
Seconds > 0 ||
|
|
||||||
Parts.length === 0
|
|
||||||
) {
|
|
||||||
Parts.push(
|
|
||||||
Seconds + "s"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Parts.join(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function SetAdminMutationMessage(
|
function SetAdminMutationMessage(
|
||||||
Message
|
Message
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
let AdminPlantCatalogue = {};
|
let AdminPlantCatalogue = {};
|
||||||
|
let AdminPlantMutationCatalogue = {};
|
||||||
let AdminPlantEditingKey = null;
|
let AdminPlantEditingKey = null;
|
||||||
let AdminPlantSavePending = false;
|
let AdminPlantSavePending = false;
|
||||||
|
let AdminPlantImagePending = false;
|
||||||
|
|
||||||
|
|
||||||
async function StartAdminPlantEditor() {
|
async function StartAdminPlantEditor() {
|
||||||
@@ -75,7 +77,7 @@ function BindAdminPlantEditor() {
|
|||||||
"AdminPlantId",
|
"AdminPlantId",
|
||||||
"AdminPlantKey",
|
"AdminPlantKey",
|
||||||
"AdminPlantName",
|
"AdminPlantName",
|
||||||
"AdminPlantGrowthTime",
|
"AdminPlantHarvestMultiplier",
|
||||||
"AdminPlantShopPlant",
|
"AdminPlantShopPlant",
|
||||||
"AdminPlantBaseCost"
|
"AdminPlantBaseCost"
|
||||||
]
|
]
|
||||||
@@ -95,11 +97,50 @@ function BindAdminPlantEditor() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (
|
||||||
|
const ElementId
|
||||||
|
of [
|
||||||
|
"AdminPlantGrowthHours",
|
||||||
|
"AdminPlantGrowthMinutes",
|
||||||
|
"AdminPlantGrowthSeconds"
|
||||||
|
]
|
||||||
|
) {
|
||||||
|
document.getElementById(
|
||||||
|
ElementId
|
||||||
|
).addEventListener(
|
||||||
|
"input",
|
||||||
|
() => {
|
||||||
|
SyncAdminPlantGrowthTime();
|
||||||
|
RenderAdminPlantPreview();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
document.getElementById(
|
||||||
|
ElementId
|
||||||
|
).addEventListener(
|
||||||
|
"change",
|
||||||
|
() => {
|
||||||
|
SyncAdminPlantGrowthTime();
|
||||||
|
RenderAdminPlantPreview();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById(
|
document.getElementById(
|
||||||
"AdminPlantShopPlant"
|
"AdminPlantShopPlant"
|
||||||
).addEventListener(
|
).addEventListener(
|
||||||
"change",
|
"change",
|
||||||
UpdateAdminPlantShopFields
|
() => {
|
||||||
|
UpdateAdminPlantShopFields();
|
||||||
|
RenderAdminPlantPreview();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
document.getElementById(
|
||||||
|
"AdminPlantImageUploadButton"
|
||||||
|
).addEventListener(
|
||||||
|
"click",
|
||||||
|
UploadAdminPlantImages
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,6 +208,47 @@ async function AdminPlantRequest(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function AdminPlantMutationRequest() {
|
||||||
|
const SaveKey =
|
||||||
|
localStorage.getItem(
|
||||||
|
SaveKeyName
|
||||||
|
);
|
||||||
|
|
||||||
|
const Response = await fetch(
|
||||||
|
ApiUrl +
|
||||||
|
"/AdminMutations.php",
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
|
||||||
|
headers: {
|
||||||
|
"Content-Type":
|
||||||
|
"application/json"
|
||||||
|
},
|
||||||
|
|
||||||
|
body: JSON.stringify({
|
||||||
|
SaveKey: SaveKey,
|
||||||
|
Action: "List"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const Result =
|
||||||
|
await Response.json();
|
||||||
|
|
||||||
|
if (
|
||||||
|
!Response.ok ||
|
||||||
|
Result.Success !== true
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
Result.Error ??
|
||||||
|
"Couldn't load mutations for the Plant editor."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async function LoadAdminPlants(
|
async function LoadAdminPlants(
|
||||||
PreferredPlantKey = null
|
PreferredPlantKey = null
|
||||||
) {
|
) {
|
||||||
@@ -174,17 +256,28 @@ async function LoadAdminPlants(
|
|||||||
"Loading plants..."
|
"Loading plants..."
|
||||||
);
|
);
|
||||||
|
|
||||||
const Result =
|
const [
|
||||||
await AdminPlantRequest(
|
PlantResult,
|
||||||
|
MutationResult
|
||||||
|
] = await Promise.all([
|
||||||
|
AdminPlantRequest(
|
||||||
"List"
|
"List"
|
||||||
);
|
),
|
||||||
|
AdminPlantMutationRequest()
|
||||||
|
]);
|
||||||
|
|
||||||
AdminPlantCatalogue =
|
AdminPlantCatalogue =
|
||||||
Result.Plants ?? {};
|
PlantResult.Plants ?? {};
|
||||||
|
|
||||||
|
AdminPlantMutationCatalogue =
|
||||||
|
MutationResult.Mutations ?? {};
|
||||||
|
|
||||||
Plants =
|
Plants =
|
||||||
AdminPlantCatalogue;
|
AdminPlantCatalogue;
|
||||||
|
|
||||||
|
MutationSets =
|
||||||
|
AdminPlantMutationCatalogue;
|
||||||
|
|
||||||
RenderAdminPlantSelect();
|
RenderAdminPlantSelect();
|
||||||
|
|
||||||
const Keys =
|
const Keys =
|
||||||
@@ -331,13 +424,20 @@ function LoadAdminPlantIntoForm(
|
|||||||
: ""
|
: ""
|
||||||
);
|
);
|
||||||
|
|
||||||
SetAdminPlantField(
|
SetAdminPlantDurationFields(
|
||||||
"AdminPlantGrowthTime",
|
|
||||||
Number(
|
Number(
|
||||||
Plant.GrowthTime ?? 0
|
Plant.GrowthTime ?? 0
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
SetAdminPlantField(
|
||||||
|
"AdminPlantHarvestMultiplier",
|
||||||
|
Number(
|
||||||
|
Plant.HarvestMultiplier ??
|
||||||
|
1.5
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
document.getElementById(
|
document.getElementById(
|
||||||
"AdminPlantShopPlant"
|
"AdminPlantShopPlant"
|
||||||
).checked =
|
).checked =
|
||||||
@@ -407,11 +507,15 @@ function StartNewAdminPlant() {
|
|||||||
""
|
""
|
||||||
);
|
);
|
||||||
|
|
||||||
SetAdminPlantField(
|
SetAdminPlantDurationFields(
|
||||||
"AdminPlantGrowthTime",
|
|
||||||
3600000
|
3600000
|
||||||
);
|
);
|
||||||
|
|
||||||
|
SetAdminPlantField(
|
||||||
|
"AdminPlantHarvestMultiplier",
|
||||||
|
1.5
|
||||||
|
);
|
||||||
|
|
||||||
document.getElementById(
|
document.getElementById(
|
||||||
"AdminPlantShopPlant"
|
"AdminPlantShopPlant"
|
||||||
).checked = false;
|
).checked = false;
|
||||||
@@ -583,6 +687,12 @@ function GetAdminPlantFormData() {
|
|||||||
).value
|
).value
|
||||||
),
|
),
|
||||||
|
|
||||||
|
HarvestMultiplier: Number(
|
||||||
|
document.getElementById(
|
||||||
|
"AdminPlantHarvestMultiplier"
|
||||||
|
).value
|
||||||
|
),
|
||||||
|
|
||||||
Effects:
|
Effects:
|
||||||
ParseAdminPlantEffects(),
|
ParseAdminPlantEffects(),
|
||||||
|
|
||||||
@@ -705,15 +815,14 @@ function RenderAdminPlantPreview() {
|
|||||||
|
|
||||||
Tile.replaceChildren();
|
Tile.replaceChildren();
|
||||||
|
|
||||||
const Images =
|
const MatureImage =
|
||||||
PlantImages[
|
GetPlantMatureImageSource(
|
||||||
PlantKey
|
AdminPlantCatalogue[
|
||||||
] ?? [];
|
PlantKey
|
||||||
|
] ?? PlantKey
|
||||||
|
);
|
||||||
|
|
||||||
if (
|
if (MatureImage !== null) {
|
||||||
Array.isArray(Images) &&
|
|
||||||
Images.length > 0
|
|
||||||
) {
|
|
||||||
const Image =
|
const Image =
|
||||||
document.createElement(
|
document.createElement(
|
||||||
"img"
|
"img"
|
||||||
@@ -723,9 +832,7 @@ function RenderAdminPlantPreview() {
|
|||||||
"PlantSprite";
|
"PlantSprite";
|
||||||
|
|
||||||
Image.src =
|
Image.src =
|
||||||
Images[
|
MatureImage;
|
||||||
Images.length - 1
|
|
||||||
];
|
|
||||||
|
|
||||||
Image.alt = Name;
|
Image.alt = Name;
|
||||||
|
|
||||||
@@ -771,11 +878,769 @@ function RenderAdminPlantPreview() {
|
|||||||
document.getElementById(
|
document.getElementById(
|
||||||
"AdminPlantGrowthTimeHint"
|
"AdminPlantGrowthTimeHint"
|
||||||
).textContent =
|
).textContent =
|
||||||
|
Number.isFinite(GrowthTime)
|
||||||
|
? GrowthTime.toLocaleString() +
|
||||||
|
" ms stored"
|
||||||
|
: "";
|
||||||
|
|
||||||
|
RenderAdminPlantEconomyPreview();
|
||||||
|
RenderAdminPlantImageManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function SetAdminPlantDurationFields(
|
||||||
|
Milliseconds
|
||||||
|
) {
|
||||||
|
Milliseconds = Math.max(
|
||||||
|
0,
|
||||||
|
Number(Milliseconds) || 0
|
||||||
|
);
|
||||||
|
|
||||||
|
const TotalSeconds =
|
||||||
|
Math.round(
|
||||||
|
Milliseconds / 1000
|
||||||
|
);
|
||||||
|
|
||||||
|
SetAdminPlantField(
|
||||||
|
"AdminPlantGrowthHours",
|
||||||
|
Math.floor(
|
||||||
|
TotalSeconds / 3600
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
SetAdminPlantField(
|
||||||
|
"AdminPlantGrowthMinutes",
|
||||||
|
Math.floor(
|
||||||
|
(TotalSeconds % 3600) / 60
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
SetAdminPlantField(
|
||||||
|
"AdminPlantGrowthSeconds",
|
||||||
|
TotalSeconds % 60
|
||||||
|
);
|
||||||
|
|
||||||
|
SetAdminPlantField(
|
||||||
|
"AdminPlantGrowthTime",
|
||||||
|
TotalSeconds * 1000
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function SyncAdminPlantGrowthTime() {
|
||||||
|
const Hours = Number(
|
||||||
|
document.getElementById(
|
||||||
|
"AdminPlantGrowthHours"
|
||||||
|
).value
|
||||||
|
);
|
||||||
|
|
||||||
|
const Minutes = Number(
|
||||||
|
document.getElementById(
|
||||||
|
"AdminPlantGrowthMinutes"
|
||||||
|
).value
|
||||||
|
);
|
||||||
|
|
||||||
|
const Seconds = Number(
|
||||||
|
document.getElementById(
|
||||||
|
"AdminPlantGrowthSeconds"
|
||||||
|
).value
|
||||||
|
);
|
||||||
|
|
||||||
|
const Values = [
|
||||||
|
Hours,
|
||||||
|
Minutes,
|
||||||
|
Seconds
|
||||||
|
];
|
||||||
|
|
||||||
|
if (
|
||||||
|
Values.some(
|
||||||
|
Value =>
|
||||||
|
!Number.isFinite(Value)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
SetAdminPlantField(
|
||||||
|
"AdminPlantGrowthTime",
|
||||||
|
""
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetAdminPlantField(
|
||||||
|
"AdminPlantGrowthTime",
|
||||||
|
Math.max(0, Hours) * 3600000 +
|
||||||
|
Math.max(0, Minutes) * 60000 +
|
||||||
|
Math.max(0, Seconds) * 1000
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function GetAdminPlantPreviewDefinition() {
|
||||||
|
const PlantId = Number(
|
||||||
|
document.getElementById(
|
||||||
|
"AdminPlantId"
|
||||||
|
).value
|
||||||
|
);
|
||||||
|
|
||||||
|
const PlantKey =
|
||||||
|
document.getElementById(
|
||||||
|
"AdminPlantKey"
|
||||||
|
).value.trim();
|
||||||
|
|
||||||
|
const Existing =
|
||||||
|
AdminPlantCatalogue[
|
||||||
|
PlantKey
|
||||||
|
] ?? {};
|
||||||
|
|
||||||
|
const ShopPlant =
|
||||||
|
document.getElementById(
|
||||||
|
"AdminPlantShopPlant"
|
||||||
|
).checked;
|
||||||
|
|
||||||
|
const BaseCostValue =
|
||||||
|
document.getElementById(
|
||||||
|
"AdminPlantBaseCost"
|
||||||
|
).value;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...Existing,
|
||||||
|
Id: PlantId,
|
||||||
|
Name:
|
||||||
|
document.getElementById(
|
||||||
|
"AdminPlantName"
|
||||||
|
).value.trim() ||
|
||||||
|
"New plant",
|
||||||
|
Tags: ParseAdminPlantTags(),
|
||||||
|
GrowthTime: Number(
|
||||||
|
document.getElementById(
|
||||||
|
"AdminPlantGrowthTime"
|
||||||
|
).value
|
||||||
|
),
|
||||||
|
HarvestMultiplier: Number(
|
||||||
|
document.getElementById(
|
||||||
|
"AdminPlantHarvestMultiplier"
|
||||||
|
).value
|
||||||
|
),
|
||||||
|
Shop: {
|
||||||
|
ShopPlant: ShopPlant,
|
||||||
|
BaseCost:
|
||||||
|
ShopPlant &&
|
||||||
|
BaseCostValue !== ""
|
||||||
|
? Number(BaseCostValue)
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function RenderAdminPlantEconomyPreview() {
|
||||||
|
const Plant =
|
||||||
|
GetAdminPlantPreviewDefinition();
|
||||||
|
|
||||||
|
const PlantKey =
|
||||||
|
document.getElementById(
|
||||||
|
"AdminPlantKey"
|
||||||
|
).value.trim();
|
||||||
|
|
||||||
|
const PreviewCatalogue = {
|
||||||
|
...AdminPlantCatalogue,
|
||||||
|
[
|
||||||
|
PlantKey ||
|
||||||
|
"__AdminPlantPreview"
|
||||||
|
]: Plant
|
||||||
|
};
|
||||||
|
|
||||||
|
const CostInfo =
|
||||||
|
Number.isFinite(Plant.Id) &&
|
||||||
|
Plant.Id > 0
|
||||||
|
? GetCataloguePlantCostInfo(
|
||||||
|
Plant.Id,
|
||||||
|
PreviewCatalogue,
|
||||||
|
AdminPlantMutationCatalogue
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const Multiplier =
|
||||||
|
GetPlantHarvestMultiplier(
|
||||||
|
Plant
|
||||||
|
);
|
||||||
|
|
||||||
|
const GrowthTime = Number(
|
||||||
|
Plant.GrowthTime
|
||||||
|
);
|
||||||
|
|
||||||
|
const Cost =
|
||||||
|
CostInfo?.Cost ?? null;
|
||||||
|
|
||||||
|
const Reward =
|
||||||
|
Cost === null
|
||||||
|
? null
|
||||||
|
: Math.ceil(
|
||||||
|
Cost * Multiplier
|
||||||
|
);
|
||||||
|
|
||||||
|
const Profit =
|
||||||
|
Cost === null ||
|
||||||
|
Reward === null
|
||||||
|
? null
|
||||||
|
: Reward - Cost;
|
||||||
|
|
||||||
|
const Dph =
|
||||||
|
Profit === null ||
|
||||||
|
!Number.isFinite(GrowthTime) ||
|
||||||
|
GrowthTime <= 0
|
||||||
|
? null
|
||||||
|
: Profit * 3600000 /
|
||||||
|
GrowthTime;
|
||||||
|
|
||||||
|
SetAdminPlantEconomyText(
|
||||||
|
"AdminPlantEconomyCost",
|
||||||
|
FormatAdminPlantDew(Cost)
|
||||||
|
);
|
||||||
|
|
||||||
|
SetAdminPlantEconomyText(
|
||||||
|
"AdminPlantEconomyMultiplier",
|
||||||
|
Multiplier.toLocaleString(
|
||||||
|
undefined,
|
||||||
|
{maximumFractionDigits: 4}
|
||||||
|
) + "×"
|
||||||
|
);
|
||||||
|
|
||||||
|
SetAdminPlantEconomyText(
|
||||||
|
"AdminPlantEconomyReward",
|
||||||
|
FormatAdminPlantDew(Reward)
|
||||||
|
);
|
||||||
|
|
||||||
|
SetAdminPlantEconomyText(
|
||||||
|
"AdminPlantEconomyProfit",
|
||||||
|
FormatAdminPlantDew(Profit)
|
||||||
|
);
|
||||||
|
|
||||||
|
SetAdminPlantEconomyText(
|
||||||
|
"AdminPlantEconomyGrowth",
|
||||||
Number.isFinite(GrowthTime)
|
Number.isFinite(GrowthTime)
|
||||||
? FormatAdminDuration(
|
? FormatAdminDuration(
|
||||||
GrowthTime
|
GrowthTime
|
||||||
)
|
)
|
||||||
|
: "Unavailable"
|
||||||
|
);
|
||||||
|
|
||||||
|
SetAdminPlantEconomyText(
|
||||||
|
"AdminPlantEconomyDph",
|
||||||
|
Dph === null
|
||||||
|
? "Unavailable"
|
||||||
|
: Dph.toLocaleString(
|
||||||
|
undefined,
|
||||||
|
{maximumFractionDigits: 2}
|
||||||
|
) + " Dew/h"
|
||||||
|
);
|
||||||
|
|
||||||
|
let Source = "Unavailable";
|
||||||
|
|
||||||
|
if (CostInfo?.SourceType === "Base") {
|
||||||
|
Source = "Base seed cost";
|
||||||
|
} else if (
|
||||||
|
CostInfo?.SourceType ===
|
||||||
|
"Mutation"
|
||||||
|
) {
|
||||||
|
Source =
|
||||||
|
CostInfo.SourceMutation
|
||||||
|
?.Name ??
|
||||||
|
CostInfo.SourceMutation
|
||||||
|
?.MutationKey ??
|
||||||
|
"Mutation";
|
||||||
|
}
|
||||||
|
|
||||||
|
SetAdminPlantEconomyText(
|
||||||
|
"AdminPlantEconomySource",
|
||||||
|
Source
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function SetAdminPlantEconomyText(
|
||||||
|
ElementId,
|
||||||
|
Value
|
||||||
|
) {
|
||||||
|
document.getElementById(
|
||||||
|
ElementId
|
||||||
|
).textContent = Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function FormatAdminPlantDew(
|
||||||
|
Value
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
Value === null ||
|
||||||
|
!Number.isFinite(Value)
|
||||||
|
)
|
||||||
|
? "Unavailable"
|
||||||
|
: Math.ceil(Value)
|
||||||
|
.toLocaleString() +
|
||||||
|
" Dew";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function RenderAdminPlantImageManager() {
|
||||||
|
const List =
|
||||||
|
document.getElementById(
|
||||||
|
"AdminPlantImageList"
|
||||||
|
);
|
||||||
|
|
||||||
|
const Note =
|
||||||
|
document.getElementById(
|
||||||
|
"AdminPlantImageNote"
|
||||||
|
);
|
||||||
|
|
||||||
|
const UploadButton =
|
||||||
|
document.getElementById(
|
||||||
|
"AdminPlantImageUploadButton"
|
||||||
|
);
|
||||||
|
|
||||||
|
const FileInput =
|
||||||
|
document.getElementById(
|
||||||
|
"AdminPlantImageFiles"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
List === null ||
|
||||||
|
Note === null ||
|
||||||
|
UploadButton === null ||
|
||||||
|
FileInput === null
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List.replaceChildren();
|
||||||
|
|
||||||
|
const Plant =
|
||||||
|
AdminPlantEditingKey === null
|
||||||
|
? null
|
||||||
|
: AdminPlantCatalogue[
|
||||||
|
AdminPlantEditingKey
|
||||||
|
] ?? null;
|
||||||
|
|
||||||
|
const CanEdit =
|
||||||
|
Plant !== null &&
|
||||||
|
!AdminPlantImagePending;
|
||||||
|
|
||||||
|
UploadButton.disabled =
|
||||||
|
!CanEdit;
|
||||||
|
|
||||||
|
FileInput.disabled =
|
||||||
|
!CanEdit;
|
||||||
|
|
||||||
|
if (Plant === null) {
|
||||||
|
Note.textContent =
|
||||||
|
"Save the plant first, then upload sprites whenever you have them.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const StaticImages =
|
||||||
|
Array.isArray(
|
||||||
|
PlantImages[
|
||||||
|
AdminPlantEditingKey
|
||||||
|
]
|
||||||
|
)
|
||||||
|
? PlantImages[
|
||||||
|
AdminPlantEditingKey
|
||||||
|
]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const ApiStages =
|
||||||
|
Array.isArray(
|
||||||
|
Plant.ImageStages
|
||||||
|
)
|
||||||
|
? [...Plant.ImageStages]
|
||||||
|
.sort(
|
||||||
|
(A, B) =>
|
||||||
|
Number(A.Stage) -
|
||||||
|
Number(B.Stage)
|
||||||
|
)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const FallbackText =
|
||||||
|
StaticImages.length > 0
|
||||||
|
? " Repository fallback: " +
|
||||||
|
StaticImages.length +
|
||||||
|
" stage" +
|
||||||
|
(
|
||||||
|
StaticImages.length === 1
|
||||||
|
? "."
|
||||||
|
: "s."
|
||||||
|
)
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
|
if (ApiStages.length === 0) {
|
||||||
|
Note.textContent =
|
||||||
|
"No API sprites uploaded." +
|
||||||
|
FallbackText;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Note.textContent =
|
||||||
|
ApiStages.length +
|
||||||
|
" API sprite" +
|
||||||
|
(
|
||||||
|
ApiStages.length === 1
|
||||||
|
? " uploaded."
|
||||||
|
: "s uploaded."
|
||||||
|
) +
|
||||||
|
FallbackText;
|
||||||
|
|
||||||
|
for (const ImageStage of ApiStages) {
|
||||||
|
const Stage = Number(
|
||||||
|
ImageStage.Stage
|
||||||
|
);
|
||||||
|
|
||||||
|
const Row =
|
||||||
|
document.createElement(
|
||||||
|
"div"
|
||||||
|
);
|
||||||
|
|
||||||
|
Row.className =
|
||||||
|
"AdminPlantImageRow";
|
||||||
|
|
||||||
|
const Preview =
|
||||||
|
document.createElement(
|
||||||
|
"div"
|
||||||
|
);
|
||||||
|
|
||||||
|
Preview.className =
|
||||||
|
"PlantTile AdminPlantImagePreview";
|
||||||
|
|
||||||
|
const Image =
|
||||||
|
document.createElement(
|
||||||
|
"img"
|
||||||
|
);
|
||||||
|
|
||||||
|
Image.className =
|
||||||
|
"PlantSprite";
|
||||||
|
|
||||||
|
Image.src =
|
||||||
|
GetAdminPlantApiImageUrl(
|
||||||
|
Plant.Id,
|
||||||
|
Stage,
|
||||||
|
ImageStage.Revision
|
||||||
|
);
|
||||||
|
|
||||||
|
Image.alt =
|
||||||
|
Plant.Name +
|
||||||
|
" stage " +
|
||||||
|
Stage;
|
||||||
|
|
||||||
|
Preview.appendChild(Image);
|
||||||
|
|
||||||
|
const Label =
|
||||||
|
document.createElement(
|
||||||
|
"strong"
|
||||||
|
);
|
||||||
|
|
||||||
|
Label.textContent =
|
||||||
|
"Stage " + Stage;
|
||||||
|
|
||||||
|
const RemoveButton =
|
||||||
|
document.createElement(
|
||||||
|
"button"
|
||||||
|
);
|
||||||
|
|
||||||
|
RemoveButton.type = "button";
|
||||||
|
RemoveButton.className =
|
||||||
|
"ActionButton AdminInlineButton AdminPlantImageRemoveButton";
|
||||||
|
RemoveButton.textContent =
|
||||||
|
"Remove";
|
||||||
|
RemoveButton.disabled =
|
||||||
|
AdminPlantImagePending;
|
||||||
|
|
||||||
|
RemoveButton.addEventListener(
|
||||||
|
"click",
|
||||||
|
() =>
|
||||||
|
DeleteAdminPlantImage(
|
||||||
|
Plant.Id,
|
||||||
|
Stage
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
Row.append(
|
||||||
|
Preview,
|
||||||
|
Label,
|
||||||
|
RemoveButton
|
||||||
|
);
|
||||||
|
|
||||||
|
List.appendChild(Row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function GetAdminPlantApiImageUrl(
|
||||||
|
PlantId,
|
||||||
|
Stage,
|
||||||
|
Revision = 0
|
||||||
|
) {
|
||||||
|
const Query =
|
||||||
|
new URLSearchParams({
|
||||||
|
PlantId: String(PlantId),
|
||||||
|
Stage: String(Stage),
|
||||||
|
v: String(Revision ?? 0)
|
||||||
|
});
|
||||||
|
|
||||||
|
return ApiUrl +
|
||||||
|
"/PlantImage.php?" +
|
||||||
|
Query.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function UploadAdminPlantImages() {
|
||||||
|
if (
|
||||||
|
AdminPlantImagePending ||
|
||||||
|
AdminPlantEditingKey === null
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Plant =
|
||||||
|
AdminPlantCatalogue[
|
||||||
|
AdminPlantEditingKey
|
||||||
|
];
|
||||||
|
|
||||||
|
if (Plant === undefined) {
|
||||||
|
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 File of Files) {
|
||||||
|
const Match =
|
||||||
|
/^(\\d+)\\.png$/i.exec(
|
||||||
|
File.name
|
||||||
|
);
|
||||||
|
|
||||||
|
if (Match === null) {
|
||||||
|
throw new Error(
|
||||||
|
"Sprite filenames must be stage numbers such as 1.png or 3.png."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Stage = Number(
|
||||||
|
Match[1]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!Number.isInteger(Stage) ||
|
||||||
|
Stage < 1 ||
|
||||||
|
Stage > 255
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
"Sprite stages must be between 1 and 255."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Stages.has(Stage)) {
|
||||||
|
throw new Error(
|
||||||
|
"Stage " +
|
||||||
|
Stage +
|
||||||
|
" was selected more than once."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Stages.add(Stage);
|
||||||
|
|
||||||
|
Uploads.push({
|
||||||
|
Stage: Stage,
|
||||||
|
File: File
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (Error) {
|
||||||
|
SetAdminPlantMessage(
|
||||||
|
Error.message
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AdminPlantImagePending = true;
|
||||||
|
RenderAdminPlantImageManager();
|
||||||
|
|
||||||
|
SetAdminPlantMessage(
|
||||||
|
"Uploading sprites..."
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (const Upload of Uploads) {
|
||||||
|
const DataBase64 =
|
||||||
|
await ReadAdminPlantFileBase64(
|
||||||
|
Upload.File
|
||||||
|
);
|
||||||
|
|
||||||
|
await AdminPlantRequest(
|
||||||
|
"UploadImage",
|
||||||
|
{
|
||||||
|
PlantId: Plant.Id,
|
||||||
|
Stage: Upload.Stage,
|
||||||
|
MimeType: "image/png",
|
||||||
|
DataBase64: DataBase64
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileInput.value = "";
|
||||||
|
|
||||||
|
localStorage.removeItem(
|
||||||
|
"SarahtoninGardenContent"
|
||||||
|
);
|
||||||
|
|
||||||
|
await LoadAdminPlants(
|
||||||
|
AdminPlantEditingKey
|
||||||
|
);
|
||||||
|
|
||||||
|
SetAdminPlantMessage(
|
||||||
|
Uploads.length === 1
|
||||||
|
? "Sprite uploaded."
|
||||||
|
: Uploads.length +
|
||||||
|
" sprites uploaded."
|
||||||
|
);
|
||||||
|
} catch (Error) {
|
||||||
|
console.error(
|
||||||
|
"Couldn't upload plant sprites:",
|
||||||
|
Error
|
||||||
|
);
|
||||||
|
|
||||||
|
SetAdminPlantMessage(
|
||||||
|
Error.message ??
|
||||||
|
"Couldn't upload plant sprites."
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
AdminPlantImagePending = false;
|
||||||
|
RenderAdminPlantImageManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function ReadAdminPlantFileBase64(
|
||||||
|
File
|
||||||
|
) {
|
||||||
|
return new Promise(
|
||||||
|
(Resolve, Reject) => {
|
||||||
|
const Reader =
|
||||||
|
new FileReader();
|
||||||
|
|
||||||
|
Reader.addEventListener(
|
||||||
|
"load",
|
||||||
|
() => {
|
||||||
|
const Result =
|
||||||
|
String(
|
||||||
|
Reader.result ??
|
||||||
|
""
|
||||||
|
);
|
||||||
|
|
||||||
|
const CommaIndex =
|
||||||
|
Result.indexOf(",");
|
||||||
|
|
||||||
|
if (CommaIndex < 0) {
|
||||||
|
Reject(
|
||||||
|
new Error(
|
||||||
|
"Couldn't read sprite file."
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Resolve(
|
||||||
|
Result.slice(
|
||||||
|
CommaIndex + 1
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Reader.addEventListener(
|
||||||
|
"error",
|
||||||
|
() =>
|
||||||
|
Reject(
|
||||||
|
new Error(
|
||||||
|
"Couldn't read sprite file."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
Reader.readAsDataURL(File);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function DeleteAdminPlantImage(
|
||||||
|
PlantId,
|
||||||
|
Stage
|
||||||
|
) {
|
||||||
|
if (AdminPlantImagePending) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AdminPlantImagePending = true;
|
||||||
|
RenderAdminPlantImageManager();
|
||||||
|
|
||||||
|
SetAdminPlantMessage(
|
||||||
|
"Removing stage " +
|
||||||
|
Stage +
|
||||||
|
" sprite..."
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await AdminPlantRequest(
|
||||||
|
"DeleteImage",
|
||||||
|
{
|
||||||
|
PlantId: PlantId,
|
||||||
|
Stage: Stage
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
localStorage.removeItem(
|
||||||
|
"SarahtoninGardenContent"
|
||||||
|
);
|
||||||
|
|
||||||
|
await LoadAdminPlants(
|
||||||
|
AdminPlantEditingKey
|
||||||
|
);
|
||||||
|
|
||||||
|
SetAdminPlantMessage(
|
||||||
|
"Stage " +
|
||||||
|
Stage +
|
||||||
|
" sprite removed."
|
||||||
|
);
|
||||||
|
} catch (Error) {
|
||||||
|
console.error(
|
||||||
|
"Couldn't remove plant sprite:",
|
||||||
|
Error
|
||||||
|
);
|
||||||
|
|
||||||
|
SetAdminPlantMessage(
|
||||||
|
Error.message ??
|
||||||
|
"Couldn't remove plant sprite."
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
AdminPlantImagePending = false;
|
||||||
|
RenderAdminPlantImageManager();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const GameContentCacheName =
|
const GameContentCacheName =
|
||||||
"SarahtoninGardenContent";
|
"SarahtoninGardenContent";
|
||||||
|
|
||||||
const GameContentCacheSchemaVersion = 2;
|
const GameContentCacheSchemaVersion = 3;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Once content has been checked, page loads for the next
|
* Once content has been checked, page loads for the next
|
||||||
|
|||||||
+327
-1
@@ -269,6 +269,14 @@ function GetPlantShopCost(
|
|||||||
Plant.Shop?.ShopPlant ===
|
Plant.Shop?.ShopPlant ===
|
||||||
true
|
true
|
||||||
) {
|
) {
|
||||||
|
if (
|
||||||
|
Plant.Shop.BaseCost === null ||
|
||||||
|
Plant.Shop.BaseCost === undefined ||
|
||||||
|
Plant.Shop.BaseCost === ""
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const BaseCost = Number(
|
const BaseCost = Number(
|
||||||
Plant.Shop.BaseCost
|
Plant.Shop.BaseCost
|
||||||
);
|
);
|
||||||
@@ -599,6 +607,35 @@ function GetMutationTimeFee(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function GetPlantHarvestMultiplier(
|
||||||
|
PlantId
|
||||||
|
) {
|
||||||
|
const Plant =
|
||||||
|
typeof PlantId === "object" &&
|
||||||
|
PlantId !== null
|
||||||
|
? PlantId
|
||||||
|
: GetPlantById(
|
||||||
|
PlantId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (Plant === null) {
|
||||||
|
return 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Multiplier = Number(
|
||||||
|
Plant.HarvestMultiplier ??
|
||||||
|
1.5
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
Number.isFinite(Multiplier) &&
|
||||||
|
Multiplier >= 0
|
||||||
|
)
|
||||||
|
? Multiplier
|
||||||
|
: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function GetPlantHarvestReward(
|
function GetPlantHarvestReward(
|
||||||
SaveData,
|
SaveData,
|
||||||
PlantId
|
PlantId
|
||||||
@@ -614,7 +651,10 @@ function GetPlantHarvestReward(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Math.ceil(
|
return Math.ceil(
|
||||||
Cost * 1.5
|
Cost *
|
||||||
|
GetPlantHarvestMultiplier(
|
||||||
|
PlantId
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -699,3 +739,289 @@ function FormatDewPerHour(
|
|||||||
" Dew"
|
" Dew"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function GetCataloguePlantById(
|
||||||
|
PlantId,
|
||||||
|
PlantCatalogue = Plants
|
||||||
|
) {
|
||||||
|
PlantId = Number(PlantId);
|
||||||
|
|
||||||
|
for (
|
||||||
|
const Plant
|
||||||
|
of Object.values(
|
||||||
|
PlantCatalogue
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
if (Number(Plant.Id) === PlantId) {
|
||||||
|
return Plant;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function GetCataloguePlantCostInfo(
|
||||||
|
PlantId,
|
||||||
|
PlantCatalogue = Plants,
|
||||||
|
MutationCatalogue = MutationSets,
|
||||||
|
Cache = new Map(),
|
||||||
|
Visiting = new Set()
|
||||||
|
) {
|
||||||
|
PlantId = Number(PlantId);
|
||||||
|
|
||||||
|
if (Cache.has(PlantId)) {
|
||||||
|
return Cache.get(PlantId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Plant =
|
||||||
|
GetCataloguePlantById(
|
||||||
|
PlantId,
|
||||||
|
PlantCatalogue
|
||||||
|
);
|
||||||
|
|
||||||
|
if (Plant === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Plant.Shop?.ShopPlant === true) {
|
||||||
|
if (
|
||||||
|
Plant.Shop.BaseCost === null ||
|
||||||
|
Plant.Shop.BaseCost === undefined ||
|
||||||
|
Plant.Shop.BaseCost === ""
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BaseCost = Number(
|
||||||
|
Plant.Shop.BaseCost
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!Number.isFinite(BaseCost) ||
|
||||||
|
BaseCost < 0
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Info = {
|
||||||
|
Cost: Math.ceil(BaseCost),
|
||||||
|
SourceType: "Base",
|
||||||
|
SourceMutation: null
|
||||||
|
};
|
||||||
|
|
||||||
|
Cache.set(PlantId, Info);
|
||||||
|
return Info;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Visiting.has(PlantId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Visiting.add(PlantId);
|
||||||
|
|
||||||
|
let CheapestInfo = null;
|
||||||
|
|
||||||
|
for (
|
||||||
|
const [MutationKey, Mutation]
|
||||||
|
of Object.entries(
|
||||||
|
MutationCatalogue
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
if (Mutation.Archived === true) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!Mutation.Relations
|
||||||
|
?.PlantsCreated
|
||||||
|
?.includes(PlantId)
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const IngredientCost =
|
||||||
|
GetCatalogueMutationIngredientCost(
|
||||||
|
Mutation,
|
||||||
|
PlantCatalogue,
|
||||||
|
MutationCatalogue,
|
||||||
|
Cache,
|
||||||
|
Visiting
|
||||||
|
);
|
||||||
|
|
||||||
|
if (IngredientCost === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Cost =
|
||||||
|
IngredientCost +
|
||||||
|
GetMutationTimeFee(
|
||||||
|
Mutation
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
CheapestInfo === null ||
|
||||||
|
Cost < CheapestInfo.Cost
|
||||||
|
) {
|
||||||
|
CheapestInfo = {
|
||||||
|
Cost: Cost,
|
||||||
|
SourceType: "Mutation",
|
||||||
|
SourceMutation: {
|
||||||
|
Id: Number(
|
||||||
|
Mutation.Id
|
||||||
|
),
|
||||||
|
MutationKey: MutationKey,
|
||||||
|
Name:
|
||||||
|
Mutation.Name ??
|
||||||
|
MutationKey
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Visiting.delete(PlantId);
|
||||||
|
Cache.set(PlantId, CheapestInfo);
|
||||||
|
|
||||||
|
return CheapestInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function GetCatalogueMutationIngredientCost(
|
||||||
|
Mutation,
|
||||||
|
PlantCatalogue,
|
||||||
|
MutationCatalogue,
|
||||||
|
Cache,
|
||||||
|
Visiting
|
||||||
|
) {
|
||||||
|
let TotalCost = 0;
|
||||||
|
|
||||||
|
for (const Row of Mutation.Pattern ?? []) {
|
||||||
|
for (const Matcher of Row) {
|
||||||
|
if (
|
||||||
|
Matcher === null ||
|
||||||
|
Matcher === "Empty" ||
|
||||||
|
Matcher === "Any"
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MatcherCost =
|
||||||
|
GetCatalogueMatcherSeedCost(
|
||||||
|
Matcher,
|
||||||
|
PlantCatalogue,
|
||||||
|
MutationCatalogue,
|
||||||
|
Cache,
|
||||||
|
Visiting
|
||||||
|
);
|
||||||
|
|
||||||
|
if (MatcherCost === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
TotalCost += MatcherCost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TotalCost;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function GetCatalogueMatcherSeedCost(
|
||||||
|
Matcher,
|
||||||
|
PlantCatalogue,
|
||||||
|
MutationCatalogue,
|
||||||
|
Cache,
|
||||||
|
Visiting
|
||||||
|
) {
|
||||||
|
if (typeof Matcher === "string") {
|
||||||
|
const Plant =
|
||||||
|
PlantCatalogue[Matcher];
|
||||||
|
|
||||||
|
if (
|
||||||
|
Plant === undefined ||
|
||||||
|
Plant.Archived === true
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetCataloguePlantCostInfo(
|
||||||
|
Plant.Id,
|
||||||
|
PlantCatalogue,
|
||||||
|
MutationCatalogue,
|
||||||
|
Cache,
|
||||||
|
Visiting
|
||||||
|
)?.Cost ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof Matcher !== "object" ||
|
||||||
|
Matcher === null
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof Matcher.Plant === "string") {
|
||||||
|
const Plant =
|
||||||
|
PlantCatalogue[Matcher.Plant];
|
||||||
|
|
||||||
|
if (
|
||||||
|
Plant === undefined ||
|
||||||
|
Plant.Archived === true
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetCataloguePlantCostInfo(
|
||||||
|
Plant.Id,
|
||||||
|
PlantCatalogue,
|
||||||
|
MutationCatalogue,
|
||||||
|
Cache,
|
||||||
|
Visiting
|
||||||
|
)?.Cost ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let CheapestCost = null;
|
||||||
|
|
||||||
|
for (
|
||||||
|
const Plant
|
||||||
|
of Object.values(
|
||||||
|
PlantCatalogue
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
if (Plant.Archived === true) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!DoesPlantMatchRecipeMatcher(
|
||||||
|
Plant,
|
||||||
|
Matcher
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Cost =
|
||||||
|
GetCataloguePlantCostInfo(
|
||||||
|
Plant.Id,
|
||||||
|
PlantCatalogue,
|
||||||
|
MutationCatalogue,
|
||||||
|
Cache,
|
||||||
|
Visiting
|
||||||
|
)?.Cost ?? null;
|
||||||
|
|
||||||
|
if (Cost === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
CheapestCost === null ||
|
||||||
|
Cost < CheapestCost
|
||||||
|
) {
|
||||||
|
CheapestCost = Cost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CheapestCost;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1303,9 +1303,9 @@ function GetPlantImages(
|
|||||||
Plot,
|
Plot,
|
||||||
Plant
|
Plant
|
||||||
) {
|
) {
|
||||||
return PlantImages[
|
return GetPlantImageSources(
|
||||||
Plot.Plant
|
Plant ?? Plot.Plant
|
||||||
] ?? [];
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -28,13 +28,17 @@ function RenderGuideRecipePlants() {
|
|||||||
const Plant =
|
const Plant =
|
||||||
Plants[PlantKey];
|
Plants[PlantKey];
|
||||||
|
|
||||||
const Images =
|
const MatureImage =
|
||||||
PlantImages[PlantKey] ?? [];
|
Plant === undefined
|
||||||
|
? null
|
||||||
|
: GetPlantMatureImageSource(
|
||||||
|
Plant
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
Plant === undefined ||
|
Plant === undefined ||
|
||||||
Images.length === 0
|
MatureImage === null
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -49,9 +53,7 @@ function RenderGuideRecipePlants() {
|
|||||||
"PlantSprite GuideRecipeImage";
|
"PlantSprite GuideRecipeImage";
|
||||||
|
|
||||||
Image.src =
|
Image.src =
|
||||||
Images[
|
MatureImage;
|
||||||
Images.length - 1
|
|
||||||
];
|
|
||||||
|
|
||||||
Image.alt = "";
|
Image.alt = "";
|
||||||
|
|
||||||
|
|||||||
@@ -1051,11 +1051,13 @@ function CreateMutationPlantCell(
|
|||||||
"PlantTile GuideRecipeCell GuideRecipePlant";
|
"PlantTile GuideRecipeCell GuideRecipePlant";
|
||||||
|
|
||||||
|
|
||||||
const Images =
|
const MatureImage =
|
||||||
PlantImages[PlantKey] ?? [];
|
GetPlantMatureImageSource(
|
||||||
|
Plant
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
if (Images.length > 0) {
|
if (MatureImage !== null) {
|
||||||
const Image =
|
const Image =
|
||||||
document.createElement(
|
document.createElement(
|
||||||
"img"
|
"img"
|
||||||
@@ -1065,9 +1067,7 @@ function CreateMutationPlantCell(
|
|||||||
"PlantSprite GuideRecipeImage";
|
"PlantSprite GuideRecipeImage";
|
||||||
|
|
||||||
Image.src =
|
Image.src =
|
||||||
Images[
|
MatureImage;
|
||||||
Images.length - 1
|
|
||||||
];
|
|
||||||
|
|
||||||
Image.alt =
|
Image.alt =
|
||||||
"";
|
"";
|
||||||
|
|||||||
@@ -178,20 +178,13 @@ function CreatePlantEncyclopediaVisual(
|
|||||||
"PlantTile PlantEncyclopediaVisual";
|
"PlantTile PlantEncyclopediaVisual";
|
||||||
|
|
||||||
|
|
||||||
const PlantKey =
|
const MatureImage =
|
||||||
GetPlantKeyById(
|
GetPlantMatureImageSource(
|
||||||
Plant.Id
|
Plant
|
||||||
);
|
);
|
||||||
|
|
||||||
const Images =
|
|
||||||
PlantKey === null
|
|
||||||
? []
|
|
||||||
: PlantImages[
|
|
||||||
PlantKey
|
|
||||||
] ?? [];
|
|
||||||
|
|
||||||
|
if (MatureImage === null) {
|
||||||
if (Images.length === 0) {
|
|
||||||
const Missing =
|
const Missing =
|
||||||
document.createElement(
|
document.createElement(
|
||||||
"span"
|
"span"
|
||||||
@@ -220,9 +213,7 @@ function CreatePlantEncyclopediaVisual(
|
|||||||
"PlantSprite";
|
"PlantSprite";
|
||||||
|
|
||||||
Image.src =
|
Image.src =
|
||||||
Images[
|
MatureImage;
|
||||||
Images.length - 1
|
|
||||||
];
|
|
||||||
|
|
||||||
Image.alt =
|
Image.alt =
|
||||||
Plant.Name;
|
Plant.Name;
|
||||||
|
|||||||
@@ -48,3 +48,140 @@ async function FetchPlantsFromApi() {
|
|||||||
|
|
||||||
return Result.Plants;
|
return Result.Plants;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function GetPlantImageSources(
|
||||||
|
PlantOrKey
|
||||||
|
) {
|
||||||
|
const PlantKey =
|
||||||
|
typeof PlantOrKey === "string"
|
||||||
|
? PlantOrKey
|
||||||
|
: Object.entries(
|
||||||
|
Plants
|
||||||
|
).find(
|
||||||
|
([, Candidate]) =>
|
||||||
|
Candidate ===
|
||||||
|
PlantOrKey ||
|
||||||
|
Number(Candidate?.Id) ===
|
||||||
|
Number(PlantOrKey?.Id)
|
||||||
|
)?.[0] ?? null;
|
||||||
|
|
||||||
|
const Plant =
|
||||||
|
typeof PlantOrKey === "string"
|
||||||
|
? Plants[PlantOrKey]
|
||||||
|
: PlantOrKey;
|
||||||
|
|
||||||
|
const StaticImages =
|
||||||
|
PlantKey !== null &&
|
||||||
|
typeof PlantKey === "string" &&
|
||||||
|
Array.isArray(
|
||||||
|
PlantImages[PlantKey]
|
||||||
|
)
|
||||||
|
? PlantImages[PlantKey]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const ApiStages =
|
||||||
|
Array.isArray(
|
||||||
|
Plant?.ImageStages
|
||||||
|
)
|
||||||
|
? Plant.ImageStages
|
||||||
|
: [];
|
||||||
|
|
||||||
|
let HighestStage =
|
||||||
|
StaticImages.length;
|
||||||
|
|
||||||
|
for (const ImageStage of ApiStages) {
|
||||||
|
const Stage = Number(
|
||||||
|
typeof ImageStage === "object" &&
|
||||||
|
ImageStage !== null
|
||||||
|
? ImageStage.Stage
|
||||||
|
: ImageStage
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
Number.isInteger(Stage) &&
|
||||||
|
Stage > HighestStage
|
||||||
|
) {
|
||||||
|
HighestStage = Stage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Images =
|
||||||
|
Array.from(
|
||||||
|
{length: HighestStage},
|
||||||
|
(_, Index) =>
|
||||||
|
StaticImages[Index] ?? null
|
||||||
|
);
|
||||||
|
|
||||||
|
if (Plant === undefined || Plant === null) {
|
||||||
|
return Images;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const ImageStage of ApiStages) {
|
||||||
|
const Stage = Number(
|
||||||
|
typeof ImageStage === "object" &&
|
||||||
|
ImageStage !== null
|
||||||
|
? ImageStage.Stage
|
||||||
|
: ImageStage
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!Number.isInteger(Stage) ||
|
||||||
|
Stage < 1
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Revision =
|
||||||
|
typeof ImageStage === "object" &&
|
||||||
|
ImageStage !== null
|
||||||
|
? Number(
|
||||||
|
ImageStage.Revision ?? 0
|
||||||
|
)
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
const Query =
|
||||||
|
new URLSearchParams({
|
||||||
|
PlantId: String(Plant.Id),
|
||||||
|
Stage: String(Stage),
|
||||||
|
v: String(
|
||||||
|
Number.isFinite(Revision)
|
||||||
|
? Revision
|
||||||
|
: 0
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Images[Stage - 1] =
|
||||||
|
ApiUrl +
|
||||||
|
"/PlantImage.php?" +
|
||||||
|
Query.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Images;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function GetPlantMatureImageSource(
|
||||||
|
PlantOrKey
|
||||||
|
) {
|
||||||
|
const Images =
|
||||||
|
GetPlantImageSources(
|
||||||
|
PlantOrKey
|
||||||
|
);
|
||||||
|
|
||||||
|
for (
|
||||||
|
let Index = Images.length - 1;
|
||||||
|
Index >= 0;
|
||||||
|
Index--
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
typeof Images[Index] ===
|
||||||
|
"string" &&
|
||||||
|
Images[Index].length > 0
|
||||||
|
) {
|
||||||
|
return Images[Index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
+5
-14
@@ -951,20 +951,13 @@ function CreateShopPlantVisual(
|
|||||||
"PlantTile ShopPlantImageFrame";
|
"PlantTile ShopPlantImageFrame";
|
||||||
|
|
||||||
|
|
||||||
const PlantKey =
|
const MatureImage =
|
||||||
GetPlantKeyById(
|
GetPlantMatureImageSource(
|
||||||
Plant.Id
|
Plant
|
||||||
);
|
);
|
||||||
|
|
||||||
const Images =
|
|
||||||
PlantKey === null
|
|
||||||
? []
|
|
||||||
: PlantImages[
|
|
||||||
PlantKey
|
|
||||||
] ?? [];
|
|
||||||
|
|
||||||
|
if (MatureImage === null) {
|
||||||
if (Images.length === 0) {
|
|
||||||
const Missing =
|
const Missing =
|
||||||
document.createElement(
|
document.createElement(
|
||||||
"span"
|
"span"
|
||||||
@@ -989,9 +982,7 @@ function CreateShopPlantVisual(
|
|||||||
"PlantSprite";
|
"PlantSprite";
|
||||||
|
|
||||||
Image.src =
|
Image.src =
|
||||||
Images[
|
MatureImage;
|
||||||
Images.length - 1
|
|
||||||
];
|
|
||||||
|
|
||||||
Image.alt =
|
Image.alt =
|
||||||
Plant.Name;
|
Plant.Name;
|
||||||
|
|||||||
@@ -1884,3 +1884,129 @@ input, button, textarea, select {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.AdminDurationInputs {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat( 3, minmax(0, 1fr) );
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminDurationInputs label {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.25rem;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminDurationInputs label > span {
|
||||||
|
color: var(--Overlay2);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminPlantEconomyPreview {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminPlantEconomyPreview h3 {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
color: var(--Peach);
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminPlantEconomyPreview dl {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat( 3, minmax(0, 1fr) );
|
||||||
|
gap: 0.65rem 1rem;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminPlantEconomyPreview dl > div {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminPlantEconomyPreview dt {
|
||||||
|
color: var(--Overlay2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminPlantEconomyPreview dd {
|
||||||
|
margin: 0.15rem 0 0;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminPlantEconomySource {
|
||||||
|
grid-column: span 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminPlantImagesSection {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
padding-top: 1rem;
|
||||||
|
border-top: 3px solid var(--Surface2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminPlantImagesSection h2 {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminPlantImageUpload {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.75rem;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminPlantImageUpload input[type="file"] {
|
||||||
|
flex: 1 1 18rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminPlantImageNote {
|
||||||
|
color: var(--Overlay2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminPlantImageList {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.65rem;
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminPlantImageRow {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||||
|
gap: 0.75rem;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.65rem;
|
||||||
|
border: 3px solid var(--Surface1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminPlantImagePreview {
|
||||||
|
display: flex;
|
||||||
|
width: 4rem;
|
||||||
|
height: 4rem;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminPlantImageRemoveButton {
|
||||||
|
--ActionAccent: var(--Red);
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
.AdminDurationInputs,
|
||||||
|
.AdminPlantEconomyPreview dl {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminPlantEconomySource {
|
||||||
|
grid-column: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminPlantImageRow {
|
||||||
|
grid-template-columns: auto minmax(0, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminPlantImageRemoveButton {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user