mirror of
https://git.srhtnin.garden/sarah/srhtnin.github.io.git
synced 2026-10-13 18:38: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>
|
||||
</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">
|
||||
<label class="AdminEditorCheckField AdminMassApplyField">
|
||||
<input type="checkbox" data-admin-mass-apply="Pattern">
|
||||
|
||||
@@ -281,6 +281,29 @@ $PageSection = "Admin";
|
||||
</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="AdminMutationGridPanel">
|
||||
<h3>Arrange</h3>
|
||||
@@ -323,6 +346,7 @@ $PageSection = "Admin";
|
||||
<option value="Empty">Empty</option>
|
||||
<option value="Plant">Exact plant</option>
|
||||
<option value="Matcher">Tag matcher</option>
|
||||
<option value="List">List</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
@@ -335,6 +359,15 @@ $PageSection = "Admin";
|
||||
<select id="AdminMutationPatternPlant"></select>
|
||||
</label>
|
||||
|
||||
<label
|
||||
id="AdminMutationPatternListRow"
|
||||
class="AdminEditorField"
|
||||
hidden
|
||||
>
|
||||
<span>List</span>
|
||||
<select id="AdminMutationPatternList"></select>
|
||||
</label>
|
||||
|
||||
<div
|
||||
id="AdminMutationMatcherRows"
|
||||
class="AdminMatcherFields"
|
||||
@@ -392,6 +425,7 @@ $PageSection = "Admin";
|
||||
<option value="Empty">Empty</option>
|
||||
<option value="Plant">Plant</option>
|
||||
<option value="Capture">Captured plant</option>
|
||||
<option value="List">List</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
@@ -404,6 +438,15 @@ $PageSection = "Admin";
|
||||
<select id="AdminMutationResultPlant"></select>
|
||||
</label>
|
||||
|
||||
<label
|
||||
id="AdminMutationResultListRow"
|
||||
class="AdminEditorField"
|
||||
hidden
|
||||
>
|
||||
<span>List</span>
|
||||
<select id="AdminMutationResultList"></select>
|
||||
</label>
|
||||
|
||||
<label
|
||||
id="AdminMutationResultCaptureRow"
|
||||
class="AdminEditorField"
|
||||
|
||||
@@ -132,6 +132,15 @@ $PageSection = "Guide";
|
||||
that same area can try mutating again.
|
||||
</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">
|
||||
<strong>
|
||||
Since you were good and actually read this...
|
||||
|
||||
@@ -744,6 +744,10 @@ function NormalizeAdminTransferMutation(
|
||||
AllowImmature:
|
||||
Imported.AllowImmature ===
|
||||
true,
|
||||
Lists:
|
||||
CloneAdminTransferValue(
|
||||
Imported.Lists ?? []
|
||||
),
|
||||
Pattern:
|
||||
CloneAdminTransferValue(
|
||||
Imported.Pattern
|
||||
@@ -1908,6 +1912,10 @@ function GetAdminTransferPortableMutation(
|
||||
AllowImmature:
|
||||
Mutation.AllowImmature ===
|
||||
true,
|
||||
Lists:
|
||||
CloneAdminTransferValue(
|
||||
Mutation.Lists ?? []
|
||||
),
|
||||
Pattern:
|
||||
CloneAdminTransferValue(
|
||||
Mutation.Pattern
|
||||
@@ -2638,10 +2646,106 @@ function CreateAdminTransferMutationRecipe(
|
||||
Flow
|
||||
);
|
||||
|
||||
const Lists =
|
||||
CreateAdminTransferMutationLists(
|
||||
Mutation.Lists,
|
||||
PlantCatalogue
|
||||
);
|
||||
|
||||
if (Lists !== null) {
|
||||
Section.appendChild(Lists);
|
||||
}
|
||||
|
||||
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(
|
||||
Heading,
|
||||
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(
|
||||
PlantValue,
|
||||
PlantCatalogue
|
||||
@@ -2770,6 +2886,18 @@ function CreateAdminTransferMutationCell(
|
||||
}
|
||||
|
||||
if (typeof Value === "string") {
|
||||
const ListMatch =
|
||||
/^List:([1-9][0-9]*)$/.exec(
|
||||
Value
|
||||
);
|
||||
|
||||
if (ListMatch !== null) {
|
||||
return CreateAdminTransferTextCell(
|
||||
"List " + ListMatch[1],
|
||||
"MutationRecipeMatcher"
|
||||
);
|
||||
}
|
||||
|
||||
return CreateAdminTransferPlantCell(
|
||||
Value,
|
||||
PlantCatalogue
|
||||
|
||||
@@ -161,6 +161,9 @@ function UpdateAdminMassMutationFieldStates() {
|
||||
AllowImmature: [
|
||||
"AdminMassMutationAllowImmature"
|
||||
],
|
||||
Lists: [
|
||||
"AdminMassMutationLists"
|
||||
],
|
||||
Pattern: [
|
||||
"AdminMassMutationPattern"
|
||||
],
|
||||
@@ -399,6 +402,18 @@ function GetAdminMassMutationPatch() {
|
||||
).value === "true";
|
||||
}
|
||||
|
||||
if (
|
||||
AdminMassMutationFieldEnabled(
|
||||
"Lists"
|
||||
)
|
||||
) {
|
||||
Patch.Lists =
|
||||
ParseAdminMassMutationJson(
|
||||
"AdminMassMutationLists",
|
||||
"Lists JSON"
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
AdminMassMutationFieldEnabled(
|
||||
"Pattern"
|
||||
@@ -489,6 +504,12 @@ function BuildAdminMassMutationPayload(
|
||||
Source.Rotation ?? "None",
|
||||
AllowImmature:
|
||||
Source.AllowImmature === true,
|
||||
Lists:
|
||||
JSON.parse(
|
||||
JSON.stringify(
|
||||
Source.Lists ?? []
|
||||
)
|
||||
),
|
||||
Pattern:
|
||||
JSON.parse(
|
||||
JSON.stringify(
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -275,6 +275,40 @@ function GetAdminOverviewPatternPlantKeys(
|
||||
}
|
||||
|
||||
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 (
|
||||
typeof Cell === "string" &&
|
||||
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(
|
||||
Cell,
|
||||
PlantCatalogue,
|
||||
Captures
|
||||
Captures,
|
||||
ListCount
|
||||
) {
|
||||
if (
|
||||
Cell === null ||
|
||||
@@ -558,6 +841,25 @@ function ValidateAdminMutationPatternCell(
|
||||
}
|
||||
|
||||
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 (
|
||||
PlantCatalogue?.[Cell] ===
|
||||
undefined
|
||||
@@ -623,7 +925,8 @@ function ValidateAdminMutationPatternCell(
|
||||
function ValidateAdminMutationResultCell(
|
||||
Cell,
|
||||
PlantCatalogue,
|
||||
Captures
|
||||
Captures,
|
||||
ListCount
|
||||
) {
|
||||
if (
|
||||
Cell === null ||
|
||||
@@ -636,6 +939,25 @@ function ValidateAdminMutationResultCell(
|
||||
let PlantValue = null;
|
||||
|
||||
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;
|
||||
} else if (
|
||||
IsAdminPlainObject(Cell) &&
|
||||
@@ -882,6 +1204,14 @@ function ValidateAdminMutation(
|
||||
);
|
||||
}
|
||||
|
||||
ValidateAdminMutationLists(
|
||||
Mutation.Lists ?? [],
|
||||
PlantCatalogue
|
||||
);
|
||||
|
||||
const ListCount =
|
||||
(Mutation.Lists ?? []).length;
|
||||
|
||||
const PatternSize =
|
||||
ValidateAdminMutationMatrix(
|
||||
Mutation.Pattern,
|
||||
@@ -896,11 +1226,17 @@ function ValidateAdminMutation(
|
||||
ValidateAdminMutationPatternCell(
|
||||
Cell,
|
||||
PlantCatalogue,
|
||||
Captures
|
||||
Captures,
|
||||
ListCount
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ValidateAdminMutationOnceListCapacity(
|
||||
Mutation.Lists ?? [],
|
||||
Mutation.Pattern
|
||||
);
|
||||
|
||||
ValidateAdminMutationMatrix(
|
||||
Mutation.Success,
|
||||
"Success result",
|
||||
@@ -913,11 +1249,18 @@ function ValidateAdminMutation(
|
||||
ValidateAdminMutationResultCell(
|
||||
Cell,
|
||||
PlantCatalogue,
|
||||
Captures
|
||||
Captures,
|
||||
ListCount
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ValidateAdminMutationResultListReferences(
|
||||
Mutation.Success,
|
||||
Mutation.Pattern,
|
||||
"Success result"
|
||||
);
|
||||
|
||||
if (
|
||||
Mutation.Failure !== "Keep" &&
|
||||
Mutation.Failure !== "Clear"
|
||||
@@ -934,10 +1277,17 @@ function ValidateAdminMutation(
|
||||
ValidateAdminMutationResultCell(
|
||||
Cell,
|
||||
PlantCatalogue,
|
||||
Captures
|
||||
Captures,
|
||||
ListCount
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ValidateAdminMutationResultListReferences(
|
||||
Mutation.Failure,
|
||||
Mutation.Pattern,
|
||||
"Failure result"
|
||||
);
|
||||
}
|
||||
|
||||
ValidateAdminMutationRelations(
|
||||
|
||||
@@ -458,17 +458,142 @@ function CanAttemptHintedMutation(
|
||||
}
|
||||
|
||||
|
||||
return Mutation.Pattern.every(
|
||||
Row =>
|
||||
Array.isArray(Row) &&
|
||||
Row.every(
|
||||
Matcher =>
|
||||
IsHintMatcherAvailable(
|
||||
const ListReferenceCounts = {};
|
||||
|
||||
for (const Row of Mutation.Pattern) {
|
||||
if (!Array.isArray(Row)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const Matcher of Row) {
|
||||
const ListNumber =
|
||||
GetMutationDisplayListReference(
|
||||
Matcher
|
||||
);
|
||||
|
||||
if (ListNumber !== null) {
|
||||
ListReferenceCounts[
|
||||
ListNumber
|
||||
] =
|
||||
(ListReferenceCounts[
|
||||
ListNumber
|
||||
] ?? 0) + 1;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
!IsHintMatcherAvailable(
|
||||
Matcher,
|
||||
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]);
|
||||
}
|
||||
|
||||
|
||||
@@ -1059,11 +1184,107 @@ function CreateMutationRecipeFlow(
|
||||
Flow
|
||||
);
|
||||
|
||||
const Lists =
|
||||
CreateMutationRecipeLists(
|
||||
Mutation.Lists
|
||||
);
|
||||
|
||||
if (Lists !== null) {
|
||||
Section.appendChild(Lists);
|
||||
}
|
||||
|
||||
|
||||
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(
|
||||
Heading,
|
||||
Matrix,
|
||||
@@ -1191,6 +1412,18 @@ function CreateMutationPatternCell(
|
||||
typeof Matcher ===
|
||||
"string"
|
||||
) {
|
||||
const ListNumber =
|
||||
GetMutationDisplayListReference(
|
||||
Matcher
|
||||
);
|
||||
|
||||
if (ListNumber !== null) {
|
||||
return CreateMutationTextCell(
|
||||
"List " + ListNumber,
|
||||
"MutationRecipeMatcher"
|
||||
);
|
||||
}
|
||||
|
||||
return CreateMutationPlantCell(
|
||||
Matcher
|
||||
);
|
||||
@@ -1320,6 +1553,18 @@ function CreateMutationResultCell(
|
||||
);
|
||||
}
|
||||
|
||||
const ListNumber =
|
||||
GetMutationDisplayListReference(
|
||||
Result
|
||||
);
|
||||
|
||||
if (ListNumber !== null) {
|
||||
return CreateMutationTextCell(
|
||||
"List " + ListNumber,
|
||||
"MutationRecipeMatcher"
|
||||
);
|
||||
}
|
||||
|
||||
return CreateMutationPlantCell(
|
||||
Result
|
||||
);
|
||||
|
||||
@@ -355,6 +355,7 @@ function CreateMutationCandidate(
|
||||
) {
|
||||
const Captures = {};
|
||||
const Cells = [];
|
||||
const ListReferences = {};
|
||||
|
||||
|
||||
for (
|
||||
@@ -395,6 +396,24 @@ function CreateMutationCandidate(
|
||||
PlotIndex
|
||||
);
|
||||
|
||||
const ListNumber =
|
||||
GetMutationListReference(
|
||||
Matcher
|
||||
);
|
||||
|
||||
if (ListNumber !== null) {
|
||||
ListReferences[ListNumber] ??= [];
|
||||
|
||||
ListReferences[ListNumber].push({
|
||||
PlotIndex: PlotIndex,
|
||||
LocalX: LocalX,
|
||||
LocalY: LocalY,
|
||||
Plot: Plot
|
||||
});
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
!DoesPlotMatchMutationMatcher(
|
||||
@@ -411,6 +430,18 @@ function CreateMutationCandidate(
|
||||
}
|
||||
|
||||
|
||||
const ListMatches =
|
||||
MatchMutationLists(
|
||||
ListReferences,
|
||||
Mutation,
|
||||
AtTime
|
||||
);
|
||||
|
||||
if (ListMatches === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
const GardenIndex =
|
||||
Math.max(
|
||||
0,
|
||||
@@ -464,6 +495,7 @@ function CreateMutationCandidate(
|
||||
|
||||
Cells: Cells,
|
||||
Captures: Captures,
|
||||
ListMatches: ListMatches,
|
||||
|
||||
Priority:
|
||||
Number(
|
||||
@@ -472,7 +504,8 @@ function CreateMutationCandidate(
|
||||
|
||||
Specificity:
|
||||
GetMutationPatternSpecificity(
|
||||
Orientation.Pattern
|
||||
Orientation.Pattern,
|
||||
Mutation
|
||||
)
|
||||
};
|
||||
}
|
||||
@@ -513,11 +546,274 @@ function DoesMutationCandidateStillMatch(
|
||||
Candidate.Captures =
|
||||
RefreshedCandidate.Captures;
|
||||
|
||||
Candidate.ListMatches =
|
||||
RefreshedCandidate.ListMatches;
|
||||
|
||||
|
||||
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(
|
||||
Plot,
|
||||
Matcher,
|
||||
@@ -756,6 +1052,7 @@ function ApplyMutationResult(
|
||||
|
||||
|
||||
let Changed = false;
|
||||
const UsedListResultMatches = {};
|
||||
|
||||
|
||||
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(
|
||||
ResultValue,
|
||||
Candidate.Captures,
|
||||
AtTime
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
ResultValue === "Empty"
|
||||
ResultValue === "Empty" ||
|
||||
(
|
||||
ListNumber !== null &&
|
||||
NewPlot === null
|
||||
)
|
||||
) {
|
||||
if (
|
||||
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(
|
||||
ResultValue,
|
||||
Captures,
|
||||
@@ -1066,7 +1438,8 @@ function CompareMutationCandidates(
|
||||
|
||||
|
||||
function GetMutationPatternSpecificity(
|
||||
Pattern
|
||||
Pattern,
|
||||
Mutation = null
|
||||
) {
|
||||
let Specificity = 0;
|
||||
|
||||
@@ -1097,6 +1470,22 @@ function GetMutationPatternSpecificity(
|
||||
typeof Matcher ===
|
||||
"string"
|
||||
) {
|
||||
const ListNumber =
|
||||
GetMutationListReference(
|
||||
Matcher
|
||||
);
|
||||
|
||||
if (ListNumber !== null) {
|
||||
Specificity +=
|
||||
GetMutationListSpecificity(
|
||||
Mutation?.Lists?.[
|
||||
ListNumber - 1
|
||||
]
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
Specificity += 8;
|
||||
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(
|
||||
SaveData,
|
||||
Candidate
|
||||
|
||||
@@ -1840,6 +1840,26 @@ input, button, textarea, select {
|
||||
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 {
|
||||
color: var(--Overlay2);
|
||||
background-color: var(--Mantle);
|
||||
@@ -2058,6 +2078,90 @@ input, button, textarea, select {
|
||||
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 {
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: 1.4rem;
|
||||
@@ -2288,6 +2392,15 @@ input, button, textarea, select {
|
||||
.AdminMutationDimensions input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.AdminMutationListsHeader {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.AdminMutationListItem {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.AdminDurationInputs {
|
||||
|
||||
Reference in new issue
Block a user