mirror of
https://git.srhtnin.garden/sarah/srhtnin.github.io.git
synced 2026-10-14 18:58:11 +02:00
Added lists to mutations for better recipes (more forgiving).
This commit is contained in:
1 parent
d6fa5572ed
commit
d9bdd74b02
11 files changed
+2243
-15
No files matched your search
@@ -177,6 +177,17 @@ $PageSection = "Admin";
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="AdminMassField AdminEditorWideField">
|
||||||
|
<label class="AdminEditorCheckField AdminMassApplyField">
|
||||||
|
<input type="checkbox" data-admin-mass-apply="Lists">
|
||||||
|
<span>Apply</span>
|
||||||
|
</label>
|
||||||
|
<label class="AdminEditorField">
|
||||||
|
<span>Lists JSON</span>
|
||||||
|
<textarea id="AdminMassMutationLists" rows="7" spellcheck="false">[]</textarea>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="AdminMassField AdminEditorWideField">
|
<div class="AdminMassField AdminEditorWideField">
|
||||||
<label class="AdminEditorCheckField AdminMassApplyField">
|
<label class="AdminEditorCheckField AdminMassApplyField">
|
||||||
<input type="checkbox" data-admin-mass-apply="Pattern">
|
<input type="checkbox" data-admin-mass-apply="Pattern">
|
||||||
|
|||||||
@@ -281,6 +281,29 @@ $PageSection = "Admin";
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="AdminMutationListsPanel">
|
||||||
|
<div class="AdminMutationListsHeader">
|
||||||
|
<div>
|
||||||
|
<h3>Lists</h3>
|
||||||
|
<p>
|
||||||
|
Lists are numbered by their position. Every reference to the same list uses that list's Once or Any mode.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
id="AdminMutationAddListButton"
|
||||||
|
class="ActionButton AdminInlineButton"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
Add list
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ol id="AdminMutationLists" class="AdminMutationLists"></ol>
|
||||||
|
|
||||||
|
<datalist id="AdminMutationTagOptions"></datalist>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="AdminMutationRecipeFlow">
|
<div class="AdminMutationRecipeFlow">
|
||||||
<div class="AdminMutationGridPanel">
|
<div class="AdminMutationGridPanel">
|
||||||
<h3>Arrange</h3>
|
<h3>Arrange</h3>
|
||||||
@@ -323,6 +346,7 @@ $PageSection = "Admin";
|
|||||||
<option value="Empty">Empty</option>
|
<option value="Empty">Empty</option>
|
||||||
<option value="Plant">Exact plant</option>
|
<option value="Plant">Exact plant</option>
|
||||||
<option value="Matcher">Tag matcher</option>
|
<option value="Matcher">Tag matcher</option>
|
||||||
|
<option value="List">List</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
@@ -335,6 +359,15 @@ $PageSection = "Admin";
|
|||||||
<select id="AdminMutationPatternPlant"></select>
|
<select id="AdminMutationPatternPlant"></select>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<label
|
||||||
|
id="AdminMutationPatternListRow"
|
||||||
|
class="AdminEditorField"
|
||||||
|
hidden
|
||||||
|
>
|
||||||
|
<span>List</span>
|
||||||
|
<select id="AdminMutationPatternList"></select>
|
||||||
|
</label>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
id="AdminMutationMatcherRows"
|
id="AdminMutationMatcherRows"
|
||||||
class="AdminMatcherFields"
|
class="AdminMatcherFields"
|
||||||
@@ -392,6 +425,7 @@ $PageSection = "Admin";
|
|||||||
<option value="Empty">Empty</option>
|
<option value="Empty">Empty</option>
|
||||||
<option value="Plant">Plant</option>
|
<option value="Plant">Plant</option>
|
||||||
<option value="Capture">Captured plant</option>
|
<option value="Capture">Captured plant</option>
|
||||||
|
<option value="List">List</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
@@ -404,6 +438,15 @@ $PageSection = "Admin";
|
|||||||
<select id="AdminMutationResultPlant"></select>
|
<select id="AdminMutationResultPlant"></select>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<label
|
||||||
|
id="AdminMutationResultListRow"
|
||||||
|
class="AdminEditorField"
|
||||||
|
hidden
|
||||||
|
>
|
||||||
|
<span>List</span>
|
||||||
|
<select id="AdminMutationResultList"></select>
|
||||||
|
</label>
|
||||||
|
|
||||||
<label
|
<label
|
||||||
id="AdminMutationResultCaptureRow"
|
id="AdminMutationResultCaptureRow"
|
||||||
class="AdminEditorField"
|
class="AdminEditorField"
|
||||||
|
|||||||
@@ -132,6 +132,15 @@ $PageSection = "Guide";
|
|||||||
that same area can try mutating again.
|
that same area can try mutating again.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Some recipes use numbered lists instead of one exact
|
||||||
|
plant in a slot. A <strong>Once</strong> list can use
|
||||||
|
each of its entries at most once across the whole
|
||||||
|
recipe, while an <strong>Any</strong> list can reuse a
|
||||||
|
matching entry. This lets a recipe care about a set of
|
||||||
|
plants without caring which order they are arranged in.
|
||||||
|
</p>
|
||||||
|
|
||||||
<div class="GuideTip">
|
<div class="GuideTip">
|
||||||
<strong>
|
<strong>
|
||||||
Since you were good and actually read this...
|
Since you were good and actually read this...
|
||||||
|
|||||||
@@ -744,6 +744,10 @@ function NormalizeAdminTransferMutation(
|
|||||||
AllowImmature:
|
AllowImmature:
|
||||||
Imported.AllowImmature ===
|
Imported.AllowImmature ===
|
||||||
true,
|
true,
|
||||||
|
Lists:
|
||||||
|
CloneAdminTransferValue(
|
||||||
|
Imported.Lists ?? []
|
||||||
|
),
|
||||||
Pattern:
|
Pattern:
|
||||||
CloneAdminTransferValue(
|
CloneAdminTransferValue(
|
||||||
Imported.Pattern
|
Imported.Pattern
|
||||||
@@ -1908,6 +1912,10 @@ function GetAdminTransferPortableMutation(
|
|||||||
AllowImmature:
|
AllowImmature:
|
||||||
Mutation.AllowImmature ===
|
Mutation.AllowImmature ===
|
||||||
true,
|
true,
|
||||||
|
Lists:
|
||||||
|
CloneAdminTransferValue(
|
||||||
|
Mutation.Lists ?? []
|
||||||
|
),
|
||||||
Pattern:
|
Pattern:
|
||||||
CloneAdminTransferValue(
|
CloneAdminTransferValue(
|
||||||
Mutation.Pattern
|
Mutation.Pattern
|
||||||
@@ -2638,10 +2646,106 @@ function CreateAdminTransferMutationRecipe(
|
|||||||
Flow
|
Flow
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const Lists =
|
||||||
|
CreateAdminTransferMutationLists(
|
||||||
|
Mutation.Lists,
|
||||||
|
PlantCatalogue
|
||||||
|
);
|
||||||
|
|
||||||
|
if (Lists !== null) {
|
||||||
|
Section.appendChild(Lists);
|
||||||
|
}
|
||||||
|
|
||||||
return Section;
|
return Section;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function CreateAdminTransferMutationLists(
|
||||||
|
Lists,
|
||||||
|
PlantCatalogue
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
!Array.isArray(Lists) ||
|
||||||
|
Lists.length === 0
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Container =
|
||||||
|
document.createElement("div");
|
||||||
|
|
||||||
|
Container.className =
|
||||||
|
"MutationRecipeLists";
|
||||||
|
|
||||||
|
const Heading =
|
||||||
|
document.createElement("h4");
|
||||||
|
|
||||||
|
Heading.textContent = "Lists";
|
||||||
|
|
||||||
|
const OrderedList =
|
||||||
|
document.createElement("ol");
|
||||||
|
|
||||||
|
for (const List of Lists) {
|
||||||
|
const ListItem =
|
||||||
|
document.createElement("li");
|
||||||
|
|
||||||
|
const Mode =
|
||||||
|
document.createElement("strong");
|
||||||
|
|
||||||
|
Mode.textContent =
|
||||||
|
List?.Mode === "Any"
|
||||||
|
? "Any: "
|
||||||
|
: "Once: ";
|
||||||
|
|
||||||
|
const Labels =
|
||||||
|
Array.isArray(List?.Items)
|
||||||
|
? List.Items.map(
|
||||||
|
Item => {
|
||||||
|
if (Item?.Type === "Plant") {
|
||||||
|
return PlantCatalogue?.[
|
||||||
|
Item.Value
|
||||||
|
]?.Name ??
|
||||||
|
Item.Value ??
|
||||||
|
"Unknown plant";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Item?.Type === "Tag") {
|
||||||
|
return "Tag: " +
|
||||||
|
(Item.Value ?? "");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Item?.Type === "Empty") {
|
||||||
|
return "Empty";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Any";
|
||||||
|
}
|
||||||
|
)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
ListItem.append(
|
||||||
|
Mode,
|
||||||
|
document.createTextNode(
|
||||||
|
Labels.length > 0
|
||||||
|
? Labels.join(", ")
|
||||||
|
: "Empty list"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
OrderedList.appendChild(
|
||||||
|
ListItem
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Container.append(
|
||||||
|
Heading,
|
||||||
|
OrderedList
|
||||||
|
);
|
||||||
|
|
||||||
|
return Container;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function CreateAdminTransferMutationRecipePanel(
|
function CreateAdminTransferMutationRecipePanel(
|
||||||
Heading,
|
Heading,
|
||||||
Matrix,
|
Matrix,
|
||||||
@@ -2746,6 +2850,18 @@ function CreateAdminTransferMutationCell(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ListMatch =
|
||||||
|
/^List:([1-9][0-9]*)$/.exec(
|
||||||
|
PlantValue
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ListMatch !== null) {
|
||||||
|
return CreateAdminTransferTextCell(
|
||||||
|
"List " + ListMatch[1],
|
||||||
|
"MutationRecipeMatcher"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return CreateAdminTransferPlantCell(
|
return CreateAdminTransferPlantCell(
|
||||||
PlantValue,
|
PlantValue,
|
||||||
PlantCatalogue
|
PlantCatalogue
|
||||||
@@ -2770,6 +2886,18 @@ function CreateAdminTransferMutationCell(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (typeof Value === "string") {
|
if (typeof Value === "string") {
|
||||||
|
const ListMatch =
|
||||||
|
/^List:([1-9][0-9]*)$/.exec(
|
||||||
|
Value
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ListMatch !== null) {
|
||||||
|
return CreateAdminTransferTextCell(
|
||||||
|
"List " + ListMatch[1],
|
||||||
|
"MutationRecipeMatcher"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return CreateAdminTransferPlantCell(
|
return CreateAdminTransferPlantCell(
|
||||||
Value,
|
Value,
|
||||||
PlantCatalogue
|
PlantCatalogue
|
||||||
|
|||||||
@@ -161,6 +161,9 @@ function UpdateAdminMassMutationFieldStates() {
|
|||||||
AllowImmature: [
|
AllowImmature: [
|
||||||
"AdminMassMutationAllowImmature"
|
"AdminMassMutationAllowImmature"
|
||||||
],
|
],
|
||||||
|
Lists: [
|
||||||
|
"AdminMassMutationLists"
|
||||||
|
],
|
||||||
Pattern: [
|
Pattern: [
|
||||||
"AdminMassMutationPattern"
|
"AdminMassMutationPattern"
|
||||||
],
|
],
|
||||||
@@ -399,6 +402,18 @@ function GetAdminMassMutationPatch() {
|
|||||||
).value === "true";
|
).value === "true";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
AdminMassMutationFieldEnabled(
|
||||||
|
"Lists"
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Patch.Lists =
|
||||||
|
ParseAdminMassMutationJson(
|
||||||
|
"AdminMassMutationLists",
|
||||||
|
"Lists JSON"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
AdminMassMutationFieldEnabled(
|
AdminMassMutationFieldEnabled(
|
||||||
"Pattern"
|
"Pattern"
|
||||||
@@ -489,6 +504,12 @@ function BuildAdminMassMutationPayload(
|
|||||||
Source.Rotation ?? "None",
|
Source.Rotation ?? "None",
|
||||||
AllowImmature:
|
AllowImmature:
|
||||||
Source.AllowImmature === true,
|
Source.AllowImmature === true,
|
||||||
|
Lists:
|
||||||
|
JSON.parse(
|
||||||
|
JSON.stringify(
|
||||||
|
Source.Lists ?? []
|
||||||
|
)
|
||||||
|
),
|
||||||
Pattern:
|
Pattern:
|
||||||
JSON.parse(
|
JSON.parse(
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
@@ -275,6 +275,40 @@ function GetAdminOverviewPatternPlantKeys(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const Cell of Row) {
|
for (const Cell of Row) {
|
||||||
|
if (
|
||||||
|
typeof Cell === "string"
|
||||||
|
) {
|
||||||
|
const ListMatch =
|
||||||
|
/^List:([1-9][0-9]*)$/.exec(
|
||||||
|
Cell
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ListMatch !== null) {
|
||||||
|
const List =
|
||||||
|
Mutation.Lists?.[
|
||||||
|
Number(ListMatch[1]) - 1
|
||||||
|
];
|
||||||
|
|
||||||
|
for (
|
||||||
|
const Item
|
||||||
|
of List?.Items ?? []
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
Item?.Type === "Plant" &&
|
||||||
|
AdminOverviewPlants[
|
||||||
|
Item.Value
|
||||||
|
] !== undefined
|
||||||
|
) {
|
||||||
|
PlantKeys.add(
|
||||||
|
Item.Value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
typeof Cell === "string" &&
|
typeof Cell === "string" &&
|
||||||
AdminOverviewPlants[
|
AdminOverviewPlants[
|
||||||
|
|||||||
@@ -544,10 +544,293 @@ function ValidateAdminCaptureName(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function GetAdminValidationListReference(
|
||||||
|
Value
|
||||||
|
) {
|
||||||
|
if (typeof Value !== "string") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Match =
|
||||||
|
/^List:([1-9][0-9]*)$/.exec(
|
||||||
|
Value
|
||||||
|
);
|
||||||
|
|
||||||
|
return Match === null
|
||||||
|
? null
|
||||||
|
: Number(Match[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function ValidateAdminMutationLists(
|
||||||
|
Lists,
|
||||||
|
PlantCatalogue
|
||||||
|
) {
|
||||||
|
if (!Array.isArray(Lists)) {
|
||||||
|
throw new Error(
|
||||||
|
"Lists must be an array."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Lists.length > 64) {
|
||||||
|
throw new Error(
|
||||||
|
"A mutation can contain at most 64 lists."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ValidTags =
|
||||||
|
new Set(
|
||||||
|
Object.values(
|
||||||
|
PlantCatalogue ?? {}
|
||||||
|
).flatMap(
|
||||||
|
Plant =>
|
||||||
|
Array.isArray(Plant?.Tags)
|
||||||
|
? Plant.Tags
|
||||||
|
: []
|
||||||
|
).filter(
|
||||||
|
Tag =>
|
||||||
|
typeof Tag === "string" &&
|
||||||
|
Tag.trim() !== ""
|
||||||
|
).map(
|
||||||
|
Tag => Tag.trim()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
for (
|
||||||
|
let ListIndex = 0;
|
||||||
|
ListIndex < Lists.length;
|
||||||
|
ListIndex++
|
||||||
|
) {
|
||||||
|
const List = Lists[ListIndex];
|
||||||
|
const ListNumber = ListIndex + 1;
|
||||||
|
|
||||||
|
if (!IsAdminPlainObject(List)) {
|
||||||
|
throw new Error(
|
||||||
|
"List " +
|
||||||
|
ListNumber +
|
||||||
|
" is invalid."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
List.Mode !== "Once" &&
|
||||||
|
List.Mode !== "Any"
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
"List " +
|
||||||
|
ListNumber +
|
||||||
|
" mode must be Once or Any."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!Array.isArray(List.Items) ||
|
||||||
|
List.Items.length < 1 ||
|
||||||
|
List.Items.length > 64
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
"List " +
|
||||||
|
ListNumber +
|
||||||
|
" must contain between 1 and 64 items."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (
|
||||||
|
let ItemIndex = 0;
|
||||||
|
ItemIndex < List.Items.length;
|
||||||
|
ItemIndex++
|
||||||
|
) {
|
||||||
|
const Item =
|
||||||
|
List.Items[ItemIndex];
|
||||||
|
|
||||||
|
const ItemName =
|
||||||
|
"List " +
|
||||||
|
ListNumber +
|
||||||
|
" item " +
|
||||||
|
(ItemIndex + 1);
|
||||||
|
|
||||||
|
if (!IsAdminPlainObject(Item)) {
|
||||||
|
throw new Error(
|
||||||
|
ItemName +
|
||||||
|
" is invalid."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
![
|
||||||
|
"Any",
|
||||||
|
"Empty",
|
||||||
|
"Plant",
|
||||||
|
"Tag"
|
||||||
|
].includes(Item.Type)
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
ItemName +
|
||||||
|
" has an invalid type."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Item.Type === "Plant") {
|
||||||
|
if (
|
||||||
|
typeof Item.Value !==
|
||||||
|
"string" ||
|
||||||
|
PlantCatalogue?.[
|
||||||
|
Item.Value
|
||||||
|
] === undefined
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
ItemName +
|
||||||
|
" references an unknown plant."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Item.Type === "Tag") {
|
||||||
|
const Tag =
|
||||||
|
typeof Item.Value ===
|
||||||
|
"string"
|
||||||
|
? Item.Value.trim()
|
||||||
|
: "";
|
||||||
|
|
||||||
|
if (
|
||||||
|
Tag.length === 0 ||
|
||||||
|
Tag.length > 64
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
ItemName +
|
||||||
|
" contains an invalid tag."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ValidTags.has(Tag)) {
|
||||||
|
throw new Error(
|
||||||
|
ItemName +
|
||||||
|
" references a tag that no plant has: " +
|
||||||
|
Tag
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function GetAdminMutationListReferenceCounts(
|
||||||
|
Matrix
|
||||||
|
) {
|
||||||
|
const Counts = new Map();
|
||||||
|
|
||||||
|
for (const Row of Matrix ?? []) {
|
||||||
|
if (!Array.isArray(Row)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const Cell of Row) {
|
||||||
|
const ListNumber =
|
||||||
|
GetAdminValidationListReference(
|
||||||
|
Cell
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ListNumber === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Counts.set(
|
||||||
|
ListNumber,
|
||||||
|
(Counts.get(ListNumber) ?? 0) + 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Counts;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function ValidateAdminMutationResultListReferences(
|
||||||
|
Result,
|
||||||
|
Pattern,
|
||||||
|
Name
|
||||||
|
) {
|
||||||
|
const PatternCounts =
|
||||||
|
GetAdminMutationListReferenceCounts(
|
||||||
|
Pattern
|
||||||
|
);
|
||||||
|
|
||||||
|
const ResultCounts =
|
||||||
|
GetAdminMutationListReferenceCounts(
|
||||||
|
Result
|
||||||
|
);
|
||||||
|
|
||||||
|
for (
|
||||||
|
const [ListNumber, Count]
|
||||||
|
of ResultCounts
|
||||||
|
) {
|
||||||
|
const PatternCount =
|
||||||
|
PatternCounts.get(ListNumber) ?? 0;
|
||||||
|
|
||||||
|
if (Count > PatternCount) {
|
||||||
|
throw new Error(
|
||||||
|
Name +
|
||||||
|
" uses List:" +
|
||||||
|
ListNumber +
|
||||||
|
" " +
|
||||||
|
Count +
|
||||||
|
" times, but the pattern only matches it " +
|
||||||
|
PatternCount +
|
||||||
|
" times."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function ValidateAdminMutationOnceListCapacity(
|
||||||
|
Lists,
|
||||||
|
Pattern
|
||||||
|
) {
|
||||||
|
const PatternCounts =
|
||||||
|
GetAdminMutationListReferenceCounts(
|
||||||
|
Pattern
|
||||||
|
);
|
||||||
|
|
||||||
|
for (
|
||||||
|
let ListIndex = 0;
|
||||||
|
ListIndex < Lists.length;
|
||||||
|
ListIndex++
|
||||||
|
) {
|
||||||
|
const List = Lists[ListIndex];
|
||||||
|
|
||||||
|
if (List?.Mode !== "Once") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ListNumber = ListIndex + 1;
|
||||||
|
const ReferenceCount =
|
||||||
|
PatternCounts.get(ListNumber) ?? 0;
|
||||||
|
|
||||||
|
if (
|
||||||
|
ReferenceCount >
|
||||||
|
(List.Items?.length ?? 0)
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
"List:" +
|
||||||
|
ListNumber +
|
||||||
|
" is Once and is referenced " +
|
||||||
|
ReferenceCount +
|
||||||
|
" times, but only has " +
|
||||||
|
(List.Items?.length ?? 0) +
|
||||||
|
" items."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function ValidateAdminMutationPatternCell(
|
function ValidateAdminMutationPatternCell(
|
||||||
Cell,
|
Cell,
|
||||||
PlantCatalogue,
|
PlantCatalogue,
|
||||||
Captures
|
Captures,
|
||||||
|
ListCount
|
||||||
) {
|
) {
|
||||||
if (
|
if (
|
||||||
Cell === null ||
|
Cell === null ||
|
||||||
@@ -558,6 +841,25 @@ function ValidateAdminMutationPatternCell(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (typeof Cell === "string") {
|
if (typeof Cell === "string") {
|
||||||
|
const ListNumber =
|
||||||
|
GetAdminValidationListReference(
|
||||||
|
Cell
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ListNumber !== null) {
|
||||||
|
if (ListNumber > ListCount) {
|
||||||
|
throw new Error(
|
||||||
|
"Pattern references list " +
|
||||||
|
ListNumber +
|
||||||
|
", but only " +
|
||||||
|
ListCount +
|
||||||
|
" lists exist."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
PlantCatalogue?.[Cell] ===
|
PlantCatalogue?.[Cell] ===
|
||||||
undefined
|
undefined
|
||||||
@@ -623,7 +925,8 @@ function ValidateAdminMutationPatternCell(
|
|||||||
function ValidateAdminMutationResultCell(
|
function ValidateAdminMutationResultCell(
|
||||||
Cell,
|
Cell,
|
||||||
PlantCatalogue,
|
PlantCatalogue,
|
||||||
Captures
|
Captures,
|
||||||
|
ListCount
|
||||||
) {
|
) {
|
||||||
if (
|
if (
|
||||||
Cell === null ||
|
Cell === null ||
|
||||||
@@ -636,6 +939,25 @@ function ValidateAdminMutationResultCell(
|
|||||||
let PlantValue = null;
|
let PlantValue = null;
|
||||||
|
|
||||||
if (typeof Cell === "string") {
|
if (typeof Cell === "string") {
|
||||||
|
const ListNumber =
|
||||||
|
GetAdminValidationListReference(
|
||||||
|
Cell
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ListNumber !== null) {
|
||||||
|
if (ListNumber > ListCount) {
|
||||||
|
throw new Error(
|
||||||
|
"Result references list " +
|
||||||
|
ListNumber +
|
||||||
|
", but only " +
|
||||||
|
ListCount +
|
||||||
|
" lists exist."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
PlantValue = Cell;
|
PlantValue = Cell;
|
||||||
} else if (
|
} else if (
|
||||||
IsAdminPlainObject(Cell) &&
|
IsAdminPlainObject(Cell) &&
|
||||||
@@ -882,6 +1204,14 @@ function ValidateAdminMutation(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ValidateAdminMutationLists(
|
||||||
|
Mutation.Lists ?? [],
|
||||||
|
PlantCatalogue
|
||||||
|
);
|
||||||
|
|
||||||
|
const ListCount =
|
||||||
|
(Mutation.Lists ?? []).length;
|
||||||
|
|
||||||
const PatternSize =
|
const PatternSize =
|
||||||
ValidateAdminMutationMatrix(
|
ValidateAdminMutationMatrix(
|
||||||
Mutation.Pattern,
|
Mutation.Pattern,
|
||||||
@@ -896,11 +1226,17 @@ function ValidateAdminMutation(
|
|||||||
ValidateAdminMutationPatternCell(
|
ValidateAdminMutationPatternCell(
|
||||||
Cell,
|
Cell,
|
||||||
PlantCatalogue,
|
PlantCatalogue,
|
||||||
Captures
|
Captures,
|
||||||
|
ListCount
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ValidateAdminMutationOnceListCapacity(
|
||||||
|
Mutation.Lists ?? [],
|
||||||
|
Mutation.Pattern
|
||||||
|
);
|
||||||
|
|
||||||
ValidateAdminMutationMatrix(
|
ValidateAdminMutationMatrix(
|
||||||
Mutation.Success,
|
Mutation.Success,
|
||||||
"Success result",
|
"Success result",
|
||||||
@@ -913,11 +1249,18 @@ function ValidateAdminMutation(
|
|||||||
ValidateAdminMutationResultCell(
|
ValidateAdminMutationResultCell(
|
||||||
Cell,
|
Cell,
|
||||||
PlantCatalogue,
|
PlantCatalogue,
|
||||||
Captures
|
Captures,
|
||||||
|
ListCount
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ValidateAdminMutationResultListReferences(
|
||||||
|
Mutation.Success,
|
||||||
|
Mutation.Pattern,
|
||||||
|
"Success result"
|
||||||
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
Mutation.Failure !== "Keep" &&
|
Mutation.Failure !== "Keep" &&
|
||||||
Mutation.Failure !== "Clear"
|
Mutation.Failure !== "Clear"
|
||||||
@@ -934,10 +1277,17 @@ function ValidateAdminMutation(
|
|||||||
ValidateAdminMutationResultCell(
|
ValidateAdminMutationResultCell(
|
||||||
Cell,
|
Cell,
|
||||||
PlantCatalogue,
|
PlantCatalogue,
|
||||||
Captures
|
Captures,
|
||||||
|
ListCount
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ValidateAdminMutationResultListReferences(
|
||||||
|
Mutation.Failure,
|
||||||
|
Mutation.Pattern,
|
||||||
|
"Failure result"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ValidateAdminMutationRelations(
|
ValidateAdminMutationRelations(
|
||||||
|
|||||||
@@ -458,19 +458,144 @@ function CanAttemptHintedMutation(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return Mutation.Pattern.every(
|
const ListReferenceCounts = {};
|
||||||
Row =>
|
|
||||||
Array.isArray(Row) &&
|
for (const Row of Mutation.Pattern) {
|
||||||
Row.every(
|
if (!Array.isArray(Row)) {
|
||||||
Matcher =>
|
return false;
|
||||||
IsHintMatcherAvailable(
|
}
|
||||||
|
|
||||||
|
for (const Matcher of Row) {
|
||||||
|
const ListNumber =
|
||||||
|
GetMutationDisplayListReference(
|
||||||
|
Matcher
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ListNumber !== null) {
|
||||||
|
ListReferenceCounts[
|
||||||
|
ListNumber
|
||||||
|
] =
|
||||||
|
(ListReferenceCounts[
|
||||||
|
ListNumber
|
||||||
|
] ?? 0) + 1;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!IsHintMatcherAvailable(
|
||||||
Matcher,
|
Matcher,
|
||||||
SaveData
|
SaveData
|
||||||
)
|
)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (
|
||||||
|
const [ListNumberText, Count]
|
||||||
|
of Object.entries(
|
||||||
|
ListReferenceCounts
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
const List =
|
||||||
|
Mutation.Lists?.[
|
||||||
|
Number(ListNumberText) - 1
|
||||||
|
];
|
||||||
|
|
||||||
|
if (
|
||||||
|
List === undefined ||
|
||||||
|
!Array.isArray(List.Items)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AvailableItems =
|
||||||
|
List.Items.filter(
|
||||||
|
Item =>
|
||||||
|
IsHintListItemAvailable(
|
||||||
|
Item,
|
||||||
|
SaveData
|
||||||
|
)
|
||||||
|
).length;
|
||||||
|
|
||||||
|
if (
|
||||||
|
AvailableItems === 0 ||
|
||||||
|
(
|
||||||
|
List.Mode === "Once" &&
|
||||||
|
AvailableItems < Count
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function IsHintListItemAvailable(
|
||||||
|
Item,
|
||||||
|
SaveData
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
Item?.Type === "Any" ||
|
||||||
|
Item?.Type === "Empty"
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Item?.Type === "Plant") {
|
||||||
|
const Plant =
|
||||||
|
Plants[Item.Value];
|
||||||
|
|
||||||
|
return (
|
||||||
|
Plant !== undefined &&
|
||||||
|
HasDiscoveredPlant(
|
||||||
|
SaveData,
|
||||||
|
Plant.Id
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Item?.Type === "Tag") {
|
||||||
|
return Object.values(
|
||||||
|
Plants
|
||||||
|
).some(
|
||||||
|
Plant =>
|
||||||
|
HasDiscoveredPlant(
|
||||||
|
SaveData,
|
||||||
|
Plant.Id
|
||||||
|
) &&
|
||||||
|
Array.isArray(Plant.Tags) &&
|
||||||
|
Plant.Tags.includes(
|
||||||
|
Item.Value
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function GetMutationDisplayListReference(
|
||||||
|
Value
|
||||||
|
) {
|
||||||
|
if (typeof Value !== "string") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Match =
|
||||||
|
/^List:([1-9][0-9]*)$/.exec(
|
||||||
|
Value
|
||||||
|
);
|
||||||
|
|
||||||
|
return Match === null
|
||||||
|
? null
|
||||||
|
: Number(Match[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function CanMutationPatternFitGarden(
|
function CanMutationPatternFitGarden(
|
||||||
Mutation,
|
Mutation,
|
||||||
@@ -1059,11 +1184,107 @@ function CreateMutationRecipeFlow(
|
|||||||
Flow
|
Flow
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const Lists =
|
||||||
|
CreateMutationRecipeLists(
|
||||||
|
Mutation.Lists
|
||||||
|
);
|
||||||
|
|
||||||
|
if (Lists !== null) {
|
||||||
|
Section.appendChild(Lists);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return Section;
|
return Section;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function CreateMutationRecipeLists(
|
||||||
|
Lists
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
!Array.isArray(Lists) ||
|
||||||
|
Lists.length === 0
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Container =
|
||||||
|
document.createElement("div");
|
||||||
|
|
||||||
|
Container.className =
|
||||||
|
"MutationRecipeLists";
|
||||||
|
|
||||||
|
const Heading =
|
||||||
|
document.createElement("h4");
|
||||||
|
|
||||||
|
Heading.textContent = "Lists";
|
||||||
|
|
||||||
|
const OrderedList =
|
||||||
|
document.createElement("ol");
|
||||||
|
|
||||||
|
for (const List of Lists) {
|
||||||
|
const Item =
|
||||||
|
document.createElement("li");
|
||||||
|
|
||||||
|
const Mode =
|
||||||
|
document.createElement("strong");
|
||||||
|
|
||||||
|
Mode.textContent =
|
||||||
|
List?.Mode === "Any"
|
||||||
|
? "Any: "
|
||||||
|
: "Once: ";
|
||||||
|
|
||||||
|
Item.appendChild(Mode);
|
||||||
|
|
||||||
|
const Labels =
|
||||||
|
Array.isArray(List?.Items)
|
||||||
|
? List.Items.map(
|
||||||
|
GetMutationRecipeListItemLabel
|
||||||
|
)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
Item.append(
|
||||||
|
document.createTextNode(
|
||||||
|
Labels.length > 0
|
||||||
|
? Labels.join(", ")
|
||||||
|
: "Empty list"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
OrderedList.appendChild(Item);
|
||||||
|
}
|
||||||
|
|
||||||
|
Container.append(
|
||||||
|
Heading,
|
||||||
|
OrderedList
|
||||||
|
);
|
||||||
|
|
||||||
|
return Container;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function GetMutationRecipeListItemLabel(
|
||||||
|
Item
|
||||||
|
) {
|
||||||
|
if (Item?.Type === "Plant") {
|
||||||
|
return Plants[Item.Value]?.Name ??
|
||||||
|
Item.Value ??
|
||||||
|
"Unknown plant";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Item?.Type === "Tag") {
|
||||||
|
return "Tag: " +
|
||||||
|
(Item.Value ?? "");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Item?.Type === "Empty") {
|
||||||
|
return "Empty";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Any";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function CreateMutationRecipePanel(
|
function CreateMutationRecipePanel(
|
||||||
Heading,
|
Heading,
|
||||||
Matrix,
|
Matrix,
|
||||||
@@ -1191,6 +1412,18 @@ function CreateMutationPatternCell(
|
|||||||
typeof Matcher ===
|
typeof Matcher ===
|
||||||
"string"
|
"string"
|
||||||
) {
|
) {
|
||||||
|
const ListNumber =
|
||||||
|
GetMutationDisplayListReference(
|
||||||
|
Matcher
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ListNumber !== null) {
|
||||||
|
return CreateMutationTextCell(
|
||||||
|
"List " + ListNumber,
|
||||||
|
"MutationRecipeMatcher"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return CreateMutationPlantCell(
|
return CreateMutationPlantCell(
|
||||||
Matcher
|
Matcher
|
||||||
);
|
);
|
||||||
@@ -1320,6 +1553,18 @@ function CreateMutationResultCell(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ListNumber =
|
||||||
|
GetMutationDisplayListReference(
|
||||||
|
Result
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ListNumber !== null) {
|
||||||
|
return CreateMutationTextCell(
|
||||||
|
"List " + ListNumber,
|
||||||
|
"MutationRecipeMatcher"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return CreateMutationPlantCell(
|
return CreateMutationPlantCell(
|
||||||
Result
|
Result
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -355,6 +355,7 @@ function CreateMutationCandidate(
|
|||||||
) {
|
) {
|
||||||
const Captures = {};
|
const Captures = {};
|
||||||
const Cells = [];
|
const Cells = [];
|
||||||
|
const ListReferences = {};
|
||||||
|
|
||||||
|
|
||||||
for (
|
for (
|
||||||
@@ -395,6 +396,24 @@ function CreateMutationCandidate(
|
|||||||
PlotIndex
|
PlotIndex
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const ListNumber =
|
||||||
|
GetMutationListReference(
|
||||||
|
Matcher
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ListNumber !== null) {
|
||||||
|
ListReferences[ListNumber] ??= [];
|
||||||
|
|
||||||
|
ListReferences[ListNumber].push({
|
||||||
|
PlotIndex: PlotIndex,
|
||||||
|
LocalX: LocalX,
|
||||||
|
LocalY: LocalY,
|
||||||
|
Plot: Plot
|
||||||
|
});
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!DoesPlotMatchMutationMatcher(
|
!DoesPlotMatchMutationMatcher(
|
||||||
@@ -411,6 +430,18 @@ function CreateMutationCandidate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const ListMatches =
|
||||||
|
MatchMutationLists(
|
||||||
|
ListReferences,
|
||||||
|
Mutation,
|
||||||
|
AtTime
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ListMatches === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const GardenIndex =
|
const GardenIndex =
|
||||||
Math.max(
|
Math.max(
|
||||||
0,
|
0,
|
||||||
@@ -464,6 +495,7 @@ function CreateMutationCandidate(
|
|||||||
|
|
||||||
Cells: Cells,
|
Cells: Cells,
|
||||||
Captures: Captures,
|
Captures: Captures,
|
||||||
|
ListMatches: ListMatches,
|
||||||
|
|
||||||
Priority:
|
Priority:
|
||||||
Number(
|
Number(
|
||||||
@@ -472,7 +504,8 @@ function CreateMutationCandidate(
|
|||||||
|
|
||||||
Specificity:
|
Specificity:
|
||||||
GetMutationPatternSpecificity(
|
GetMutationPatternSpecificity(
|
||||||
Orientation.Pattern
|
Orientation.Pattern,
|
||||||
|
Mutation
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -513,11 +546,274 @@ function DoesMutationCandidateStillMatch(
|
|||||||
Candidate.Captures =
|
Candidate.Captures =
|
||||||
RefreshedCandidate.Captures;
|
RefreshedCandidate.Captures;
|
||||||
|
|
||||||
|
Candidate.ListMatches =
|
||||||
|
RefreshedCandidate.ListMatches;
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function GetMutationListReference(
|
||||||
|
Value
|
||||||
|
) {
|
||||||
|
if (typeof Value !== "string") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Match =
|
||||||
|
/^List:([1-9][0-9]*)$/.exec(
|
||||||
|
Value
|
||||||
|
);
|
||||||
|
|
||||||
|
return Match === null
|
||||||
|
? null
|
||||||
|
: Number(Match[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function MatchMutationLists(
|
||||||
|
ListReferences,
|
||||||
|
Mutation,
|
||||||
|
AtTime
|
||||||
|
) {
|
||||||
|
const Matches = {};
|
||||||
|
const Lists =
|
||||||
|
Array.isArray(Mutation.Lists)
|
||||||
|
? Mutation.Lists
|
||||||
|
: [];
|
||||||
|
|
||||||
|
for (
|
||||||
|
const [ListNumberText, References]
|
||||||
|
of Object.entries(ListReferences)
|
||||||
|
) {
|
||||||
|
const ListNumber =
|
||||||
|
Number(ListNumberText);
|
||||||
|
|
||||||
|
const List =
|
||||||
|
Lists[ListNumber - 1];
|
||||||
|
|
||||||
|
if (
|
||||||
|
List === undefined ||
|
||||||
|
!Array.isArray(List.Items) ||
|
||||||
|
List.Items.length === 0
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CandidateItems =
|
||||||
|
References.map(
|
||||||
|
Reference =>
|
||||||
|
List.Items.map(
|
||||||
|
(Item, ItemIndex) => ({
|
||||||
|
Item: Item,
|
||||||
|
ItemIndex: ItemIndex
|
||||||
|
})
|
||||||
|
).filter(
|
||||||
|
Candidate =>
|
||||||
|
DoesPlotMatchMutationListItem(
|
||||||
|
Reference.Plot,
|
||||||
|
Candidate.Item,
|
||||||
|
Mutation,
|
||||||
|
AtTime
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
CandidateItems.some(
|
||||||
|
Items => Items.length === 0
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Assignments;
|
||||||
|
|
||||||
|
if (List.Mode === "Once") {
|
||||||
|
Assignments =
|
||||||
|
MatchMutationListOnce(
|
||||||
|
CandidateItems
|
||||||
|
);
|
||||||
|
|
||||||
|
if (Assignments === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Assignments =
|
||||||
|
CandidateItems.map(
|
||||||
|
Items => Items[0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Matches[ListNumber] =
|
||||||
|
References.map(
|
||||||
|
(Reference, Index) => ({
|
||||||
|
PlotIndex:
|
||||||
|
Reference.PlotIndex,
|
||||||
|
LocalX:
|
||||||
|
Reference.LocalX,
|
||||||
|
LocalY:
|
||||||
|
Reference.LocalY,
|
||||||
|
Plot:
|
||||||
|
CloneMutationPlot(
|
||||||
|
Reference.Plot
|
||||||
|
),
|
||||||
|
ItemIndex:
|
||||||
|
Assignments[Index]
|
||||||
|
.ItemIndex
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function MatchMutationListOnce(
|
||||||
|
CandidateItems
|
||||||
|
) {
|
||||||
|
const ItemToReference =
|
||||||
|
new Map();
|
||||||
|
|
||||||
|
const Assignments =
|
||||||
|
new Array(
|
||||||
|
CandidateItems.length
|
||||||
|
);
|
||||||
|
|
||||||
|
function TryAssign(
|
||||||
|
ReferenceIndex,
|
||||||
|
SeenItems
|
||||||
|
) {
|
||||||
|
for (
|
||||||
|
const Candidate
|
||||||
|
of CandidateItems[
|
||||||
|
ReferenceIndex
|
||||||
|
]
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
SeenItems.has(
|
||||||
|
Candidate.ItemIndex
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
SeenItems.add(
|
||||||
|
Candidate.ItemIndex
|
||||||
|
);
|
||||||
|
|
||||||
|
const PreviousReference =
|
||||||
|
ItemToReference.get(
|
||||||
|
Candidate.ItemIndex
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
PreviousReference === undefined ||
|
||||||
|
TryAssign(
|
||||||
|
PreviousReference,
|
||||||
|
SeenItems
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
ItemToReference.set(
|
||||||
|
Candidate.ItemIndex,
|
||||||
|
ReferenceIndex
|
||||||
|
);
|
||||||
|
|
||||||
|
Assignments[
|
||||||
|
ReferenceIndex
|
||||||
|
] = Candidate;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (
|
||||||
|
let ReferenceIndex = 0;
|
||||||
|
ReferenceIndex < CandidateItems.length;
|
||||||
|
ReferenceIndex++
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
!TryAssign(
|
||||||
|
ReferenceIndex,
|
||||||
|
new Set()
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Assignments;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function DoesPlotMatchMutationListItem(
|
||||||
|
Plot,
|
||||||
|
Item,
|
||||||
|
Mutation,
|
||||||
|
AtTime
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
Item === null ||
|
||||||
|
typeof Item !== "object"
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Item.Type === "Any") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Item.Type === "Empty") {
|
||||||
|
return Plot === null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Plot === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Plant =
|
||||||
|
Plants[Plot.Plant];
|
||||||
|
|
||||||
|
if (Plant === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
Mutation.AllowImmature !== true &&
|
||||||
|
!IsMutationPlantMature(
|
||||||
|
Plot,
|
||||||
|
Plant,
|
||||||
|
AtTime
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Item.Type === "Plant") {
|
||||||
|
return (
|
||||||
|
typeof Item.Value === "string" &&
|
||||||
|
Plot.Plant === Item.Value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Item.Type === "Tag") {
|
||||||
|
return (
|
||||||
|
typeof Item.Value === "string" &&
|
||||||
|
Array.isArray(Plant.Tags) &&
|
||||||
|
Plant.Tags.includes(
|
||||||
|
Item.Value
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function DoesPlotMatchMutationMatcher(
|
function DoesPlotMatchMutationMatcher(
|
||||||
Plot,
|
Plot,
|
||||||
Matcher,
|
Matcher,
|
||||||
@@ -756,6 +1052,7 @@ function ApplyMutationResult(
|
|||||||
|
|
||||||
|
|
||||||
let Changed = false;
|
let Changed = false;
|
||||||
|
const UsedListResultMatches = {};
|
||||||
|
|
||||||
|
|
||||||
for (
|
for (
|
||||||
@@ -798,16 +1095,47 @@ function ApplyMutationResult(
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
const NewPlot =
|
const ListNumber =
|
||||||
|
GetMutationListReference(
|
||||||
|
ResultValue
|
||||||
|
);
|
||||||
|
|
||||||
|
let NewPlot;
|
||||||
|
|
||||||
|
if (ListNumber !== null) {
|
||||||
|
const Match =
|
||||||
|
GetMutationResultListMatch(
|
||||||
|
Candidate,
|
||||||
|
ListNumber,
|
||||||
|
LocalX,
|
||||||
|
LocalY,
|
||||||
|
UsedListResultMatches
|
||||||
|
);
|
||||||
|
|
||||||
|
if (Match === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
NewPlot =
|
||||||
|
CloneMutationPlot(
|
||||||
|
Match.Plot
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
NewPlot =
|
||||||
CreateMutationResultPlot(
|
CreateMutationResultPlot(
|
||||||
ResultValue,
|
ResultValue,
|
||||||
Candidate.Captures,
|
Candidate.Captures,
|
||||||
AtTime
|
AtTime
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
ResultValue === "Empty"
|
ResultValue === "Empty" ||
|
||||||
|
(
|
||||||
|
ListNumber !== null &&
|
||||||
|
NewPlot === null
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
if (
|
if (
|
||||||
SaveData.Garden.Plots[
|
SaveData.Garden.Plots[
|
||||||
@@ -881,6 +1209,50 @@ function ApplyMutationResult(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function GetMutationResultListMatch(
|
||||||
|
Candidate,
|
||||||
|
ListNumber,
|
||||||
|
LocalX,
|
||||||
|
LocalY,
|
||||||
|
UsedMatches
|
||||||
|
) {
|
||||||
|
const Matches =
|
||||||
|
Candidate.ListMatches?.[
|
||||||
|
ListNumber
|
||||||
|
] ?? [];
|
||||||
|
|
||||||
|
UsedMatches[ListNumber] ??=
|
||||||
|
new Set();
|
||||||
|
|
||||||
|
const Used =
|
||||||
|
UsedMatches[ListNumber];
|
||||||
|
|
||||||
|
let MatchIndex =
|
||||||
|
Matches.findIndex(
|
||||||
|
(Match, Index) =>
|
||||||
|
!Used.has(Index) &&
|
||||||
|
Match.LocalX === LocalX &&
|
||||||
|
Match.LocalY === LocalY
|
||||||
|
);
|
||||||
|
|
||||||
|
if (MatchIndex < 0) {
|
||||||
|
MatchIndex =
|
||||||
|
Matches.findIndex(
|
||||||
|
(Match, Index) =>
|
||||||
|
!Used.has(Index)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MatchIndex < 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
Used.add(MatchIndex);
|
||||||
|
|
||||||
|
return Matches[MatchIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function CreateMutationResultPlot(
|
function CreateMutationResultPlot(
|
||||||
ResultValue,
|
ResultValue,
|
||||||
Captures,
|
Captures,
|
||||||
@@ -1066,7 +1438,8 @@ function CompareMutationCandidates(
|
|||||||
|
|
||||||
|
|
||||||
function GetMutationPatternSpecificity(
|
function GetMutationPatternSpecificity(
|
||||||
Pattern
|
Pattern,
|
||||||
|
Mutation = null
|
||||||
) {
|
) {
|
||||||
let Specificity = 0;
|
let Specificity = 0;
|
||||||
|
|
||||||
@@ -1097,6 +1470,22 @@ function GetMutationPatternSpecificity(
|
|||||||
typeof Matcher ===
|
typeof Matcher ===
|
||||||
"string"
|
"string"
|
||||||
) {
|
) {
|
||||||
|
const ListNumber =
|
||||||
|
GetMutationListReference(
|
||||||
|
Matcher
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ListNumber !== null) {
|
||||||
|
Specificity +=
|
||||||
|
GetMutationListSpecificity(
|
||||||
|
Mutation?.Lists?.[
|
||||||
|
ListNumber - 1
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
Specificity += 8;
|
Specificity += 8;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1141,6 +1530,46 @@ function GetMutationPatternSpecificity(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function GetMutationListSpecificity(
|
||||||
|
List
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
List === null ||
|
||||||
|
typeof List !== "object" ||
|
||||||
|
!Array.isArray(List.Items) ||
|
||||||
|
List.Items.length === 0
|
||||||
|
) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ItemSpecificities =
|
||||||
|
List.Items.map(
|
||||||
|
Item => {
|
||||||
|
if (Item?.Type === "Empty") {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Item?.Type === "Plant") {
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Item?.Type === "Tag") {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const Base = Math.min(
|
||||||
|
...ItemSpecificities
|
||||||
|
);
|
||||||
|
|
||||||
|
return Base +
|
||||||
|
(List.Mode === "Once" ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function GetMutationCandidateCooldownUntil(
|
function GetMutationCandidateCooldownUntil(
|
||||||
SaveData,
|
SaveData,
|
||||||
Candidate
|
Candidate
|
||||||
|
|||||||
@@ -1840,6 +1840,26 @@ input, button, textarea, select {
|
|||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.MutationRecipeLists {
|
||||||
|
margin-top: 0.9rem;
|
||||||
|
padding-top: 0.75rem;
|
||||||
|
border-top: 2px solid var(--Surface1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.MutationRecipeLists h4 {
|
||||||
|
margin-bottom: 0.4rem;
|
||||||
|
color: var(--Subtext1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.MutationRecipeLists ol {
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MutationRecipeLists li + li {
|
||||||
|
margin-top: 0.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
.MutationRecipeAny {
|
.MutationRecipeAny {
|
||||||
color: var(--Overlay2);
|
color: var(--Overlay2);
|
||||||
background-color: var(--Mantle);
|
background-color: var(--Mantle);
|
||||||
@@ -2058,6 +2078,90 @@ input, button, textarea, select {
|
|||||||
border-top: 3px solid var(--Surface2);
|
border-top: 3px solid var(--Surface2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.AdminMutationListsPanel {
|
||||||
|
margin-top: 1rem;
|
||||||
|
padding: 0.85rem;
|
||||||
|
background-color: var(--Base);
|
||||||
|
border: 3px solid var(--Surface2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminMutationListsHeader {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminMutationListsHeader h3 {
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
color: var(--Peach);
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminMutationListsHeader p {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--Subtext1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminMutationLists {
|
||||||
|
margin: 0.85rem 0 0;
|
||||||
|
padding-left: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminMutationList {
|
||||||
|
margin-bottom: 0.85rem;
|
||||||
|
padding: 0.75rem;
|
||||||
|
background-color: var(--Crust);
|
||||||
|
border: 2px solid var(--Surface1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminMutationList:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminMutationListHeader {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.65rem;
|
||||||
|
align-items: end;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminMutationListMode {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminMutationListMode > span {
|
||||||
|
color: var(--Overlay2);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminMutationListItems {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-top: 0.65rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminMutationListItem {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(7rem, 0.6fr) minmax(10rem, 1.4fr) auto;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminMutationListItemValue[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminMutationListActions {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-top: 0.65rem;
|
||||||
|
}
|
||||||
|
|
||||||
.AdminEditorPreview h2, .AdminMutationRecipeSection h2, .AdminMutationRelationsSection h2, .AdminMutationFailureSection h2 {
|
.AdminEditorPreview h2, .AdminMutationRecipeSection h2, .AdminMutationRelationsSection h2, .AdminMutationFailureSection h2 {
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
@@ -2288,6 +2392,15 @@ input, button, textarea, select {
|
|||||||
.AdminMutationDimensions input {
|
.AdminMutationDimensions input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.AdminMutationListsHeader {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AdminMutationListItem {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.AdminDurationInputs {
|
.AdminDurationInputs {
|
||||||
|
|||||||
Reference in new issue
Block a user