Added neat preview to plots for mutations using lists.

This commit is contained in:
sarah committed 2026-09-07 15:53:37 +02:00
1 parent d9bdd74b02
commit 495e1a230a
7 files changed
+680 -28

No files matched your search

+47 -8
View File
@@ -2604,6 +2604,12 @@ function CreateAdminTransferMutationRecipe(
"Keep"
);
const ListOffsets =
CreateMutationRecipePreviewOffsets(
Pattern,
Success
);
const Flow =
document.createElement(
"div"
@@ -2630,14 +2636,18 @@ function CreateAdminTransferMutationRecipe(
"Arrange",
Pattern,
"Pattern",
PlantCatalogue
PlantCatalogue,
Mutation.Lists,
ListOffsets.Pattern
),
Arrow,
CreateAdminTransferMutationRecipePanel(
"Result",
Success,
"Result",
PlantCatalogue
PlantCatalogue,
Mutation.Lists,
ListOffsets.Result
)
);
@@ -2750,7 +2760,9 @@ function CreateAdminTransferMutationRecipePanel(
Heading,
Matrix,
Mode,
PlantCatalogue
PlantCatalogue,
Lists,
ListOffsets
) {
const Panel =
document.createElement(
@@ -2781,15 +2793,42 @@ function CreateAdminTransferMutationRecipePanel(
)
);
for (const Row of Matrix) {
for (const Value of Row) {
Grid.appendChild(
for (
let Y = 0;
Y < Matrix.length;
Y++
) {
for (
let X = 0;
X < Matrix[Y].length;
X++
) {
const Value =
Matrix[Y][X];
const Cell =
CreateAdminTransferMutationCell(
Value,
Mode,
PlantCatalogue
)
);
);
const ListNumber =
GetMutationRecipePreviewListReference(
Value
);
if (ListNumber !== null) {
RegisterMutationRecipePreviewListCell(
Cell,
Lists?.[ListNumber - 1],
ListNumber,
ListOffsets?.[Y]?.[X],
PlantCatalogue
);
}
Grid.appendChild(Cell);
}
}
+34 -5
View File
@@ -2032,16 +2032,24 @@ function ResizeAdminMutationRecipe() {
function RenderAdminMutationGrids() {
const ListOffsets =
CreateMutationRecipePreviewOffsets(
AdminMutationPattern,
AdminMutationResult
);
RenderAdminMutationGrid(
"AdminMutationPatternGrid",
"Pattern",
AdminMutationPattern
AdminMutationPattern,
ListOffsets.Pattern
);
RenderAdminMutationGrid(
"AdminMutationResultGrid",
"Result",
AdminMutationResult
AdminMutationResult,
ListOffsets.Result
);
}
@@ -2049,7 +2057,8 @@ function RenderAdminMutationGrids() {
function RenderAdminMutationGrid(
ContainerId,
Mode,
Matrix
Matrix,
ListOffsets
) {
const Grid =
document.getElementById(
@@ -2080,7 +2089,8 @@ function RenderAdminMutationGrid(
Mode,
X,
Y,
Matrix[Y][X]
Matrix[Y][X],
ListOffsets?.[Y]?.[X]
)
);
}
@@ -2092,7 +2102,8 @@ function CreateAdminMutationGridCell(
Mode,
X,
Y,
Value
Value,
ListOffset
) {
const Button =
document.createElement(
@@ -2167,6 +2178,24 @@ function CreateAdminMutationGridCell(
Button.title =
Display.Label;
const ListNumber =
GetMutationRecipePreviewListReference(
Value
);
if (ListNumber !== null) {
RegisterMutationRecipePreviewListCell(
Button,
AdminMutationLists[
ListNumber - 1
],
ListNumber,
ListOffset,
AdminMutationPlantCatalogue,
"GuideRecipeLabel"
);
}
Button.addEventListener(
"click",
() => {
+89 -15
View File
@@ -1130,6 +1130,12 @@ function CreateMutationRecipeFlow(
"Keep"
);
const ListOffsets =
CreateMutationRecipePreviewOffsets(
Pattern,
Success
);
const Flow =
document.createElement(
@@ -1144,7 +1150,9 @@ function CreateMutationRecipeFlow(
CreateMutationRecipePanel(
"Arrange",
Pattern,
"Pattern"
"Pattern",
Mutation.Lists,
ListOffsets.Pattern
);
const Arrow =
@@ -1168,7 +1176,9 @@ function CreateMutationRecipeFlow(
CreateMutationRecipePanel(
"Result",
Success,
"Result"
"Result",
Mutation.Lists,
ListOffsets.Result
);
@@ -1288,7 +1298,9 @@ function GetMutationRecipeListItemLabel(
function CreateMutationRecipePanel(
Heading,
Matrix,
Mode
Mode,
Lists,
ListOffsets
) {
const Panel =
document.createElement(
@@ -1328,17 +1340,24 @@ function CreateMutationRecipePanel(
for (
const Row
of Matrix
let Y = 0;
Y < Matrix.length;
Y++
) {
for (
const Value
of Row
let X = 0;
X < Matrix[Y].length;
X++
) {
const Value =
Matrix[Y][X];
const Cell =
CreateMutationRecipeCell(
Value,
Mode
Mode,
Lists,
ListOffsets?.[Y]?.[X]
);
Grid.appendChild(
@@ -1372,22 +1391,30 @@ function CreateMutationRecipePanel(
function CreateMutationRecipeCell(
Value,
Mode
Mode,
Lists,
ListOffset
) {
if (Mode === "Result") {
return CreateMutationResultCell(
Value
Value,
Lists,
ListOffset
);
}
return CreateMutationPatternCell(
Value
Value,
Lists,
ListOffset
);
}
function CreateMutationPatternCell(
Matcher
Matcher,
Lists,
ListOffset
) {
if (
Matcher === null ||
@@ -1418,10 +1445,21 @@ function CreateMutationPatternCell(
);
if (ListNumber !== null) {
return CreateMutationTextCell(
const Cell =
CreateMutationTextCell(
"List " + ListNumber,
"MutationRecipeMatcher"
);
RegisterMutationRecipePreviewListCell(
Cell.Element,
Lists?.[ListNumber - 1],
ListNumber,
ListOffset,
Plants
);
return Cell;
}
return CreateMutationPlantCell(
@@ -1517,7 +1555,9 @@ function CreateMutationPatternCell(
function CreateMutationResultCell(
Result
Result,
Lists,
ListOffset
) {
if (
Result === null ||
@@ -1559,10 +1599,21 @@ function CreateMutationResultCell(
);
if (ListNumber !== null) {
return CreateMutationTextCell(
const Cell =
CreateMutationTextCell(
"List " + ListNumber,
"MutationRecipeMatcher"
);
RegisterMutationRecipePreviewListCell(
Cell.Element,
Lists?.[ListNumber - 1],
ListNumber,
ListOffset,
Plants
);
return Cell;
}
return CreateMutationPlantCell(
@@ -1589,6 +1640,29 @@ function CreateMutationResultCell(
);
}
const ListNumber =
GetMutationDisplayListReference(
Result.Plant
);
if (ListNumber !== null) {
const Cell =
CreateMutationTextCell(
"List " + ListNumber,
"MutationRecipeMatcher"
);
RegisterMutationRecipePreviewListCell(
Cell.Element,
Lists?.[ListNumber - 1],
ListNumber,
ListOffset,
Plants
);
return Cell;
}
return CreateMutationPlantCell(
Result.Plant
);
+507
View File
@@ -0,0 +1,507 @@
const MutationRecipePreviewCycleMilliseconds = 1200;
let MutationRecipePreviewFrame = 0;
let MutationRecipePreviewTimer = null;
const MutationRecipePreviewCells = new Set();
function GetMutationRecipePreviewListReference(
Value
) {
let Reference = Value;
if (
Value !== null &&
typeof Value === "object" &&
!Array.isArray(Value) &&
typeof Value.Plant === "string"
) {
Reference = Value.Plant;
}
if (typeof Reference !== "string") {
return null;
}
const Match =
/^List:([1-9][0-9]*)$/.exec(
Reference
);
return Match === null
? null
: Number(Match[1]);
}
function CreateMutationRecipePreviewOffsets(
Pattern,
Result
) {
const PatternOffsets =
CreateMutationRecipePreviewOffsetMatrix(
Pattern
);
const ResultOffsets =
Array.isArray(Result)
? Result.map(
Row =>
Array.isArray(Row)
? Row.map(() => null)
: []
)
: [];
const PatternCounts = {};
for (
let Y = 0;
Y < PatternOffsets.length;
Y++
) {
for (
let X = 0;
X < PatternOffsets[Y].length;
X++
) {
const Offset =
PatternOffsets[Y][X];
if (Offset === null) {
continue;
}
const ListNumber =
GetMutationRecipePreviewListReference(
Pattern?.[Y]?.[X]
);
if (ListNumber === null) {
continue;
}
PatternCounts[ListNumber] =
Math.max(
PatternCounts[ListNumber] ?? 0,
Offset + 1
);
}
}
const UsedResultOffsets = {};
for (
let Y = 0;
Y < ResultOffsets.length;
Y++
) {
for (
let X = 0;
X < ResultOffsets[Y].length;
X++
) {
const ListNumber =
GetMutationRecipePreviewListReference(
Result?.[Y]?.[X]
);
if (ListNumber === null) {
continue;
}
const PatternListNumber =
GetMutationRecipePreviewListReference(
Pattern?.[Y]?.[X]
);
const PatternOffset =
PatternOffsets?.[Y]?.[X];
if (
PatternListNumber !== ListNumber ||
PatternOffset === null ||
PatternOffset === undefined
) {
continue;
}
ResultOffsets[Y][X] =
PatternOffset;
if (
UsedResultOffsets[ListNumber] ===
undefined
) {
UsedResultOffsets[ListNumber] =
new Set();
}
UsedResultOffsets[ListNumber].add(
PatternOffset
);
}
}
const NextOverflowOffset = {};
for (
let Y = 0;
Y < ResultOffsets.length;
Y++
) {
for (
let X = 0;
X < ResultOffsets[Y].length;
X++
) {
if (ResultOffsets[Y][X] !== null) {
continue;
}
const ListNumber =
GetMutationRecipePreviewListReference(
Result?.[Y]?.[X]
);
if (ListNumber === null) {
continue;
}
if (
UsedResultOffsets[ListNumber] ===
undefined
) {
UsedResultOffsets[ListNumber] =
new Set();
}
const Used =
UsedResultOffsets[ListNumber];
const PatternCount =
PatternCounts[ListNumber] ?? 0;
let Offset = 0;
while (
Offset < PatternCount &&
Used.has(Offset)
) {
Offset++;
}
if (Offset >= PatternCount) {
Offset =
NextOverflowOffset[
ListNumber
] ?? PatternCount;
NextOverflowOffset[ListNumber] =
Offset + 1;
}
Used.add(Offset);
ResultOffsets[Y][X] = Offset;
}
}
return {
Pattern: PatternOffsets,
Result: ResultOffsets
};
}
function CreateMutationRecipePreviewOffsetMatrix(
Matrix
) {
if (!Array.isArray(Matrix)) {
return [];
}
const NextOffsets = {};
return Matrix.map(
Row => {
if (!Array.isArray(Row)) {
return [];
}
return Row.map(
Value => {
const ListNumber =
GetMutationRecipePreviewListReference(
Value
);
if (ListNumber === null) {
return null;
}
const Offset =
NextOffsets[ListNumber] ?? 0;
NextOffsets[ListNumber] =
Offset + 1;
return Offset;
}
);
}
);
}
function RegisterMutationRecipePreviewListCell(
Element,
List,
ListNumber,
Offset,
PlantCatalogue,
TextClassName = "MutationRecipeText"
) {
const Entry = {
Element: Element,
List: List,
ListNumber: ListNumber,
Offset:
Number.isInteger(Offset)
? Offset
: 0,
PlantCatalogue:
PlantCatalogue ?? {},
TextClassName: TextClassName
};
MutationRecipePreviewCells.add(
Entry
);
RenderMutationRecipePreviewListCell(
Entry,
MutationRecipePreviewFrame
);
StartMutationRecipePreviewTimer();
}
function StartMutationRecipePreviewTimer() {
if (
MutationRecipePreviewTimer !== null ||
MutationRecipePreviewCells.size === 0
) {
return;
}
if (
window.matchMedia?.(
"(prefers-reduced-motion: reduce)"
).matches === true
) {
return;
}
MutationRecipePreviewTimer =
window.setInterval(
AdvanceMutationRecipePreviewFrame,
MutationRecipePreviewCycleMilliseconds
);
}
function AdvanceMutationRecipePreviewFrame() {
MutationRecipePreviewFrame++;
for (
const Entry
of Array.from(
MutationRecipePreviewCells
)
) {
if (!Entry.Element.isConnected) {
MutationRecipePreviewCells.delete(
Entry
);
continue;
}
RenderMutationRecipePreviewListCell(
Entry,
MutationRecipePreviewFrame
);
}
if (
MutationRecipePreviewCells.size === 0 &&
MutationRecipePreviewTimer !== null
) {
window.clearInterval(
MutationRecipePreviewTimer
);
MutationRecipePreviewTimer = null;
}
}
function RenderMutationRecipePreviewListCell(
Entry,
Frame
) {
const Items =
Array.isArray(Entry.List?.Items)
? Entry.List.Items
: [];
if (Items.length === 0) {
ApplyMutationRecipePreviewCellDisplay(
Entry,
{
Label:
"List " +
Entry.ListNumber,
Image: null,
ClassName:
"MutationRecipeMatcher"
}
);
return;
}
const ItemIndex =
(
Frame +
Entry.Offset
) % Items.length;
ApplyMutationRecipePreviewCellDisplay(
Entry,
GetMutationRecipePreviewItemDisplay(
Items[ItemIndex],
Entry.PlantCatalogue
)
);
}
function GetMutationRecipePreviewItemDisplay(
Item,
PlantCatalogue
) {
if (Item?.Type === "Plant") {
const Plant =
PlantCatalogue?.[
Item.Value
];
if (Plant === undefined) {
return {
Label:
Item.Value ??
"Unknown plant",
Image: null,
ClassName:
"MutationRecipeMatcher"
};
}
return {
Label:
Plant.Name ??
Item.Value,
Image:
GetPlantMatureImageSource(
Plant
),
ClassName:
"GuideRecipePlant"
};
}
if (Item?.Type === "Tag") {
return {
Label:
Item.Value ?? "",
Image: null,
ClassName:
"MutationRecipeMatcher"
};
}
if (Item?.Type === "Empty") {
return {
Label: "Empty",
Image: null,
ClassName:
"GuideRecipeEmpty"
};
}
return {
Label: "Any",
Image: null,
ClassName:
"MutationRecipeAny"
};
}
function ApplyMutationRecipePreviewCellDisplay(
Entry,
Display
) {
const Element = Entry.Element;
Element.classList.remove(
"GuideRecipePlant",
"GuideRecipeEmpty",
"MutationRecipeAny",
"MutationRecipeMatcher"
);
Element.classList.add(
Display.ClassName
);
Element.replaceChildren();
if (Display.Image !== null) {
const Image =
document.createElement(
"img"
);
Image.className =
"PlantSprite GuideRecipeImage";
Image.src = Display.Image;
Image.alt = "";
Element.appendChild(Image);
}
const Label =
document.createElement(
"span"
);
Label.className =
Display.Image === null
? Entry.TextClassName
: "GuideRecipeLabel";
Label.textContent =
Display.Label;
Element.appendChild(Label);
Element.title =
"List " +
Entry.ListNumber +
": " +
Display.Label;
}