Files

2290 lines
41 KiB
JavaScript

let MutationEncyclopediaSave;
let MutationEncyclopediaSearchQuery = "";
let MutationEncyclopediaSortMode = "IdAsc";
async function StartMutationEncyclopedia() {
try {
await LoadGameContent();
MutationEncyclopediaSave =
await LoadGame();
BindMutationEncyclopediaControls();
RenderMutationEncyclopedia();
} catch (Error) {
console.error(
"Couldn't load Mutation encyclopedia:",
Error
);
SetMutationEncyclopediaMessage(
"Couldn't load your discovered mutations."
);
}
}
function BindMutationEncyclopediaControls() {
const SearchInput =
document.getElementById(
"MutationEncyclopediaSearchInput"
);
const SortSelect =
document.getElementById(
"MutationEncyclopediaSortSelect"
);
if (SearchInput !== null) {
SearchInput.value =
MutationEncyclopediaSearchQuery;
SearchInput.addEventListener(
"input",
() => {
MutationEncyclopediaSearchQuery =
SearchInput.value;
RenderMutationEncyclopedia();
}
);
}
if (SortSelect !== null) {
SortSelect.value =
MutationEncyclopediaSortMode;
SortSelect.addEventListener(
"change",
() => {
MutationEncyclopediaSortMode =
SortSelect.value;
RenderMutationEncyclopedia();
}
);
}
}
function RenderMutationEncyclopedia() {
const List =
document.getElementById(
"MutationEncyclopediaList"
);
List.replaceChildren();
const AllMutations =
Object.values(
MutationSets
)
.sort(
(A, B) =>
A.Id - B.Id
);
const DiscoveredMutations =
AllMutations.filter(
Mutation =>
HasDiscoveredMutation(
MutationEncyclopediaSave,
Mutation.Id
)
);
const ActiveDiscoveredMutations =
DiscoveredMutations.filter(
Mutation =>
Mutation.Archived !== true
);
const ArchivedDiscoveredMutations =
DiscoveredMutations.filter(
Mutation =>
Mutation.Archived === true
);
const AvailableHints =
HasMutationHintsUpgrade(
MutationEncyclopediaSave
)
? AllMutations.filter(
Mutation =>
Mutation.Archived !== true &&
!HasDiscoveredMutation(
MutationEncyclopediaSave,
Mutation.Id
) &&
HasUsableMutationHint(
Mutation
) &&
CanAttemptHintedMutation(
Mutation,
MutationEncyclopediaSave
)
)
: [];
const HintLimit =
GetMutationHintsDisplayLimit(
MutationEncyclopediaSave
);
const DisplayedHints =
AvailableHints.slice(
0,
HintLimit
);
const SearchQuery =
MutationEncyclopediaSearchQuery
.trim()
.toLocaleLowerCase();
const VisibleHints =
DisplayedHints.filter(
Mutation =>
DoesMutationHintMatchSearch(
Mutation,
SearchQuery
)
);
const VisibleActiveMutations =
ActiveDiscoveredMutations
.filter(
Mutation =>
DoesMutationEncyclopediaMatchSearch(
Mutation,
SearchQuery
)
)
.sort(
CompareMutationEncyclopediaMutations
);
const VisibleArchivedMutations =
ArchivedDiscoveredMutations
.filter(
Mutation =>
DoesMutationEncyclopediaMatchSearch(
Mutation,
SearchQuery
)
)
.sort(
CompareMutationEncyclopediaMutations
);
const VisibleMutations = [
...VisibleActiveMutations,
...VisibleArchivedMutations
];
if (VisibleHints.length > 0) {
List.appendChild(
CreateMutationHintCard(
VisibleHints.map(
Mutation =>
Mutation.Hint
),
GetMutationHintsLevel(
MutationEncyclopediaSave
)
)
);
}
for (
const Mutation
of VisibleActiveMutations
) {
List.appendChild(
CreateMutationEncyclopediaCard(
Mutation
)
);
}
if (VisibleArchivedMutations.length > 0) {
List.appendChild(
CreateMutationEncyclopediaArchivedHeading(
VisibleArchivedMutations.length
)
);
for (
const Mutation
of VisibleArchivedMutations
) {
List.appendChild(
CreateMutationEncyclopediaCard(
Mutation
)
);
}
}
if (
DiscoveredMutations.length === 0 &&
AvailableHints.length === 0
) {
SetMutationEncyclopediaMessage(
"No mutations discovered yet."
);
return;
}
if (
VisibleMutations.length === 0 &&
VisibleHints.length === 0
) {
SetMutationEncyclopediaMessage(
"No mutations or hints match your search."
);
return;
}
const MessageParts = [];
if (SearchQuery.length > 0) {
MessageParts.push(
"Showing " +
VisibleMutations.length
.toLocaleString() +
" of " +
DiscoveredMutations.length
.toLocaleString() +
" discovered mutations."
);
} else {
MessageParts.push(
DiscoveredMutations.length === 1
? "1 mutation discovered."
: DiscoveredMutations.length
.toLocaleString() +
" mutations discovered."
);
}
if (ArchivedDiscoveredMutations.length > 0) {
MessageParts.push(
ArchivedDiscoveredMutations.length === 1
? "1 archived mutation."
: ArchivedDiscoveredMutations.length
.toLocaleString() +
" archived mutations."
);
}
if (AvailableHints.length > 0) {
MessageParts.push(
"Showing " +
VisibleHints.length
.toLocaleString() +
" of " +
AvailableHints.length
.toLocaleString() +
(AvailableHints.length === 1
? " available hint."
: " available hints.")
);
}
SetMutationEncyclopediaMessage(
MessageParts.join(
" "
)
);
}
function CreateMutationEncyclopediaArchivedHeading(
Count
) {
const Header =
document.createElement(
"header"
);
const Heading =
document.createElement(
"h2"
);
Heading.textContent =
"Archived (" +
Count.toLocaleString() +
")";
const Description =
document.createElement(
"p"
);
Description.textContent =
"Retired mutations you discovered remain recorded here.";
Header.append(
Heading,
Description
);
return Header;
}
function DoesMutationHintMatchSearch(
Mutation,
SearchQuery
) {
if (SearchQuery.length === 0) {
return true;
}
return String(
Mutation.Hint ?? ""
)
.toLocaleLowerCase()
.includes(
SearchQuery
);
}
function DoesMutationEncyclopediaMatchSearch(
Mutation,
SearchQuery
) {
if (SearchQuery.length === 0) {
return true;
}
const SearchParts = [
Mutation.Id,
Mutation.Name,
Mutation.Description,
Mutation.Hint,
Mutation.Archived === true
? "Archived"
: "",
FormatMutationChance(
Mutation.Chance
),
FormatMutationEncyclopediaTime(
Mutation.Cooldown
),
Mutation.Rotation
];
const RelationPlantIds = [
...(Mutation.Relations
?.PlantsUsed ?? []),
...(Mutation.Relations
?.PlantsCreated ?? [])
];
for (
const PlantId
of RelationPlantIds
) {
if (
!HasDiscoveredPlant(
MutationEncyclopediaSave,
PlantId
)
) {
continue;
}
const Plant =
GetPlantById(
PlantId
);
if (Plant !== null) {
SearchParts.push(
Plant.Name
);
}
}
return SearchParts
.filter(
Value =>
Value !== null &&
Value !== undefined
)
.join(" ")
.toLocaleLowerCase()
.includes(
SearchQuery
);
}
function CompareMutationEncyclopediaMutations(
A,
B
) {
switch (MutationEncyclopediaSortMode) {
case "NameAsc":
return String(A.Name ?? "")
.localeCompare(
String(B.Name ?? ""),
undefined,
{sensitivity: "base"}
) || A.Id - B.Id;
case "ChanceAsc":
return CompareMutationEncyclopediaNumbers(
A.Chance,
B.Chance,
1
) || A.Id - B.Id;
case "ChanceDesc":
return CompareMutationEncyclopediaNumbers(
A.Chance,
B.Chance,
-1
) || A.Id - B.Id;
case "CooldownAsc":
return CompareMutationEncyclopediaNumbers(
A.Cooldown,
B.Cooldown,
1
) || A.Id - B.Id;
case "CooldownDesc":
return CompareMutationEncyclopediaNumbers(
A.Cooldown,
B.Cooldown,
-1
) || A.Id - B.Id;
case "IdAsc":
default:
return A.Id - B.Id;
}
}
function CompareMutationEncyclopediaNumbers(
A,
B,
Direction
) {
const MissingA =
A === null ||
A === undefined ||
A === "";
const MissingB =
B === null ||
B === undefined ||
B === "";
const NumberA = Number(A);
const NumberB = Number(B);
const ValidA =
!MissingA &&
Number.isFinite(NumberA);
const ValidB =
!MissingB &&
Number.isFinite(NumberB);
if (!ValidA && !ValidB) {
return 0;
}
if (!ValidA) {
return 1;
}
if (!ValidB) {
return -1;
}
return (NumberA - NumberB) *
Direction;
}
function HasUsableMutationHint(
Mutation
) {
return (
typeof Mutation.Hint ===
"string" &&
Mutation.Hint.trim().length > 0
);
}
function CanAttemptHintedMutation(
Mutation,
SaveData
) {
if (
!CanMutationPatternFitGarden(
Mutation,
SaveData
)
) {
return false;
}
if (!Array.isArray(Mutation.Pattern)) {
return false;
}
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 &&
Plant.Archived !== true &&
HasDiscoveredPlant(
SaveData,
Plant.Id
)
);
}
if (Item?.Type === "Tag") {
return Object.values(
Plants
).some(
Plant =>
Plant.Archived !== true &&
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(
Mutation,
SaveData
) {
const Pattern =
Mutation.Pattern;
if (
!Array.isArray(Pattern) ||
Pattern.length === 0
) {
return false;
}
const PatternHeight =
Pattern.length;
const PatternWidth =
Math.max(
0,
...Pattern.map(
Row =>
Array.isArray(Row)
? Row.length
: 0
)
);
if (PatternWidth === 0) {
return false;
}
const GardenWidth =
Number(
SaveData.Garden?.Width ?? 0
);
const GardenHeight =
Number(
SaveData.Garden?.Height ?? 0
);
const FitsNormally =
PatternWidth <= GardenWidth &&
PatternHeight <= GardenHeight;
if (FitsNormally) {
return true;
}
return (
Mutation.Rotation === "Any" &&
PatternHeight <= GardenWidth &&
PatternWidth <= GardenHeight
);
}
function IsHintMatcherAvailable(
Matcher,
SaveData
) {
if (
Matcher === null ||
Matcher === "Any" ||
Matcher === "Empty"
) {
return true;
}
if (typeof Matcher === "string") {
const Plant =
Plants[Matcher];
return (
Plant !== undefined &&
Plant.Archived !== true &&
HasDiscoveredPlant(
SaveData,
Plant.Id
)
);
}
if (
Matcher === null ||
typeof Matcher !== "object" ||
Array.isArray(Matcher)
) {
return false;
}
return Object.entries(
Plants
).some(
([PlantKey, Plant]) =>
Plant.Archived !== true &&
HasDiscoveredPlant(
SaveData,
Plant.Id
) &&
DoesPlantMatchHintMatcher(
PlantKey,
Plant,
Matcher
)
);
}
function DoesPlantMatchHintMatcher(
PlantKey,
Plant,
Matcher
) {
if (
typeof Matcher.Plant ===
"string" &&
Matcher.Plant !== PlantKey
) {
return false;
}
const Tags =
Array.isArray(Plant.Tags)
? Plant.Tags
: [];
if (
Array.isArray(
Matcher.Tags
) &&
!Matcher.Tags.every(
Tag =>
Tags.includes(Tag)
)
) {
return false;
}
if (
Array.isArray(
Matcher.TagsAny
) &&
Matcher.TagsAny.length > 0 &&
!Matcher.TagsAny.some(
Tag =>
Tags.includes(Tag)
)
) {
return false;
}
if (
Array.isArray(
Matcher.TagsNot
) &&
Matcher.TagsNot.some(
Tag =>
Tags.includes(Tag)
)
) {
return false;
}
return true;
}
function CreateMutationHintCard(
Hints,
Level
) {
const Card =
document.createElement(
"article"
);
Card.className =
"Panel MutationEncyclopediaCard MutationHintCard";
const Header =
document.createElement(
"header"
);
Header.className =
"PanelHeader MutationEncyclopediaHeader";
const Name =
document.createElement(
"h2"
);
Name.textContent =
Level >= MutationHintsMaximumLevel
? "Mutation hints"
: "Mutation hint";
Header.appendChild(Name);
const Body =
document.createElement(
"div"
);
Body.className =
"MutationEncyclopediaBody";
if (
Level < MutationHintsMaximumLevel
) {
const HintText =
document.createElement(
"p"
);
HintText.className =
"MutationEncyclopediaDescription";
HintText.textContent =
String(
Hints[0] ?? ""
).trim();
Body.appendChild(
HintText
);
} else {
const HintList =
document.createElement(
"ul"
);
HintList.className =
"MutationHintList";
for (const Hint of Hints) {
const Item =
document.createElement(
"li"
);
Item.textContent =
"- " +
String(Hint).trim();
HintList.appendChild(
Item
);
}
Body.appendChild(
HintList
);
}
Card.append(
Header,
Body
);
return Card;
}
function CreateMutationEncyclopediaCard(
Mutation
) {
const Card =
document.createElement(
"article"
);
Card.className =
"Panel MutationEncyclopediaCard";
Card.id =
"Mutation-" +
Mutation.Id;
const Header =
document.createElement(
"header"
);
Header.className =
"PanelHeader MutationEncyclopediaHeader";
const Number =
document.createElement(
"span"
);
Number.className =
"MutationEncyclopediaNumber";
Number.textContent =
String(
Mutation.Id
).padStart(
3,
"0"
);
const Name =
document.createElement(
"h2"
);
Name.textContent =
Mutation.Name;
Header.append(
Number,
Name
);
const Body =
document.createElement(
"div"
);
Body.className =
"MutationEncyclopediaBody";
const Description =
document.createElement(
"p"
);
Description.className =
"MutationEncyclopediaDescription";
Description.textContent =
Mutation.Description ||
"No description.";
Body.append(
Description,
CreateMutationEncyclopediaStats(
Mutation
),
CreateMutationRecipeFlow(
Mutation
),
CreateMutationEncyclopediaRelations(
Mutation
)
);
Card.append(
Header,
Body
);
return Card;
}
function CreateMutationEncyclopediaStats(
Mutation
) {
const Stats =
document.createElement(
"div"
);
Stats.className =
"MutationEncyclopediaStats";
Stats.append(
CreateMutationEncyclopediaStat(
"Chance",
FormatMutationChance(
Mutation.Chance
)
),
CreateMutationEncyclopediaStat(
"Cooldown",
FormatMutationEncyclopediaTime(
Mutation.Cooldown
)
),
CreateMutationEncyclopediaStat(
"Rotation",
Mutation.Rotation ??
"None"
),
CreateMutationEncyclopediaStat(
"Mature plants",
Mutation.AllowImmature === true
? "Not required"
: "Required"
)
);
if (Mutation.Archived === true) {
Stats.appendChild(
CreateMutationEncyclopediaStat(
"Status",
"Archived"
)
);
}
return Stats;
}
function CreateMutationEncyclopediaStat(
Name,
Value
) {
const Stat =
document.createElement(
"div"
);
Stat.className =
"MutationEncyclopediaStat";
const NameElement =
document.createElement(
"span"
);
NameElement.className =
"MutationEncyclopediaStatName";
NameElement.textContent =
Name;
const ValueElement =
document.createElement(
"span"
);
ValueElement.textContent =
Value;
Stat.append(
NameElement,
ValueElement
);
return Stat;
}
function CreateMutationRecipeFlow(
Mutation
) {
const Section =
document.createElement(
"section"
);
Section.className =
"MutationEncyclopediaRecipeSection";
const Title =
document.createElement(
"h3"
);
Title.textContent =
"Recipe";
const Pattern =
NormalizeMutationDisplayMatrix(
Mutation.Pattern,
null,
null,
"Any"
);
if (Pattern.length === 0) {
const Missing =
document.createElement(
"p"
);
Missing.className =
"MutationEncyclopediaMissingRecipe";
Missing.textContent =
"No recipe data.";
Section.append(
Title,
Missing
);
return Section;
}
const PatternWidth =
Pattern[0].length;
const Success =
NormalizeMutationDisplayMatrix(
Mutation.Success,
PatternWidth,
Pattern.length,
"Keep"
);
const ListOffsets =
CreateMutationRecipePreviewOffsets(
Pattern,
Success
);
const Flow =
document.createElement(
"div"
);
Flow.className =
"MutationEncyclopediaRecipeFlow";
const Input =
CreateMutationRecipePanel(
"Arrange",
Pattern,
"Pattern",
Mutation.Lists,
ListOffsets.Pattern,
Mutation.AllowImmature === true
);
const Arrow =
document.createElement(
"span"
);
Arrow.className =
"MutationEncyclopediaRecipeArrow";
Arrow.setAttribute(
"aria-hidden",
"true"
);
Arrow.textContent =
"→";
const Output =
CreateMutationRecipePanel(
"Result",
Success,
"Result",
Mutation.Lists,
ListOffsets.Result,
false
);
Flow.append(
Input,
Arrow,
Output
);
Section.append(
Title,
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,
Mode,
Lists,
ListOffsets,
CyclePlantStages = false
) {
const Panel =
document.createElement(
"div"
);
Panel.className =
"MutationEncyclopediaRecipePanel";
const Title =
document.createElement(
"h4"
);
Title.textContent =
Heading;
const Grid =
document.createElement(
"div"
);
Grid.className =
"GuideRecipe MutationRecipeGrid";
Grid.style.setProperty(
"--RecipeWidth",
String(
Matrix[0]?.length ?? 1
)
);
const Labels = [];
for (
let Y = 0;
Y < Matrix.length;
Y++
) {
for (
let X = 0;
X < Matrix[Y].length;
X++
) {
const Value =
Matrix[Y][X];
const Cell =
CreateMutationRecipeCell(
Value,
Mode,
Lists,
ListOffsets?.[Y]?.[X],
CyclePlantStages
);
Grid.appendChild(
Cell.Element
);
Labels.push(
Cell.Label
);
}
}
Grid.setAttribute(
"aria-label",
Labels.join(
", "
)
);
Panel.append(
Title,
Grid
);
return Panel;
}
function CreateMutationRecipeCell(
Value,
Mode,
Lists,
ListOffset,
CyclePlantStages = false
) {
if (Mode === "Result") {
return CreateMutationResultCell(
Value,
Lists,
ListOffset
);
}
return CreateMutationPatternCell(
Value,
Lists,
ListOffset,
CyclePlantStages
);
}
function CreateMutationPatternCell(
Matcher,
Lists,
ListOffset,
CyclePlantStages = false
) {
if (
Matcher === null ||
Matcher === "Any"
) {
return CreateMutationTextCell(
"Any",
"MutationRecipeAny"
);
}
if (Matcher === "Empty") {
return CreateMutationTextCell(
"Empty",
"GuideRecipeEmpty"
);
}
if (
typeof Matcher ===
"string"
) {
const ListNumber =
GetMutationDisplayListReference(
Matcher
);
if (ListNumber !== null) {
const Cell =
CreateMutationTextCell(
"List " + ListNumber,
"MutationRecipeMatcher"
);
RegisterMutationRecipePreviewListCell(
Cell.Element,
Lists?.[ListNumber - 1],
ListNumber,
ListOffset,
Plants,
"MutationRecipeText",
CyclePlantStages
);
return Cell;
}
return CreateMutationPlantCell(
Matcher,
CyclePlantStages
);
}
if (
Matcher === null ||
typeof Matcher !==
"object"
) {
return CreateMutationTextCell(
"Any",
"MutationRecipeAny"
);
}
if (
typeof Matcher.Plant ===
"string"
) {
return CreateMutationPlantCell(
Matcher.Plant,
CyclePlantStages
);
}
const Requirements = [];
if (
Array.isArray(
Matcher.Tags
) &&
Matcher.Tags.length > 0
) {
Requirements.push(
Matcher.Tags.join(
" + "
)
);
}
if (
Array.isArray(
Matcher.TagsAny
) &&
Matcher.TagsAny.length > 0
) {
Requirements.push(
"Any: " +
Matcher.TagsAny.join(
" / "
)
);
}
if (
Array.isArray(
Matcher.TagsNot
) &&
Matcher.TagsNot.length > 0
) {
Requirements.push(
"Not: " +
Matcher.TagsNot.join(
", "
)
);
}
if (Requirements.length === 0) {
return CreateMutationTextCell(
"Any",
"MutationRecipeAny"
);
}
return CreateMutationTextCell(
Requirements.join(
"\n"
),
"MutationRecipeMatcher"
);
}
function CreateMutationResultCell(
Result,
Lists,
ListOffset
) {
if (
Result === null ||
Result === "Keep"
) {
return CreateMutationTextCell(
"Keep",
"MutationRecipeKeep"
);
}
if (Result === "Empty") {
return CreateMutationTextCell(
"Empty",
"GuideRecipeEmpty"
);
}
if (
typeof Result ===
"string"
) {
if (
Result.startsWith(
"$"
)
) {
return CreateMutationTextCell(
Result.slice(1),
"MutationRecipeCapture"
);
}
const ListNumber =
GetMutationDisplayListReference(
Result
);
if (ListNumber !== null) {
const Cell =
CreateMutationTextCell(
"List " + ListNumber,
"MutationRecipeMatcher"
);
RegisterMutationRecipePreviewListCell(
Cell.Element,
Lists?.[ListNumber - 1],
ListNumber,
ListOffset,
Plants
);
return Cell;
}
return CreateMutationPlantCell(
Result
);
}
if (
Result !== null &&
typeof Result ===
"object" &&
typeof Result.Plant ===
"string"
) {
if (
Result.Plant.startsWith(
"$"
)
) {
return CreateMutationTextCell(
Result.Plant.slice(1),
"MutationRecipeCapture"
);
}
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
);
}
return CreateMutationTextCell(
"Keep",
"MutationRecipeKeep"
);
}
function CreateMutationPlantCell(
PlantKey,
CyclePlantStages = false
) {
const Plant =
Plants[PlantKey];
if (Plant === undefined) {
return CreateMutationTextCell(
PlantKey,
"MutationRecipeMatcher"
);
}
const Element =
document.createElement(
"div"
);
Element.className =
"PlantTile GuideRecipeCell GuideRecipePlant";
const MatureImage =
GetPlantMatureImageSource(
Plant
);
if (MatureImage !== null) {
const Image =
document.createElement(
"img"
);
Image.className =
"PlantSprite GuideRecipeImage";
Image.src =
MatureImage;
Image.alt =
"";
Element.appendChild(
Image
);
}
const Label =
document.createElement(
"span"
);
Label.className =
"GuideRecipeLabel";
Label.textContent =
Plant.Name;
Element.appendChild(
Label
);
if (CyclePlantStages === true) {
RegisterMutationRecipePreviewPlantStageCell(
Element,
Plant
);
}
return {
Element: Element,
Label: Plant.Name
};
}
function CreateMutationTextCell(
Text,
ClassName
) {
const Element =
document.createElement(
"div"
);
Element.className =
"PlantTile GuideRecipeCell " +
ClassName;
const Label =
document.createElement(
"span"
);
Label.className =
"MutationRecipeText";
Label.textContent =
Text;
Element.appendChild(
Label
);
return {
Element: Element,
Label: Text.replaceAll(
"\n",
" "
)
};
}
function NormalizeMutationDisplayMatrix(
Matrix,
Width = null,
Height = null,
FillValue = "Any"
) {
if (!Array.isArray(Matrix)) {
return [];
}
const TargetHeight =
Height ?? Matrix.length;
const TargetWidth =
Width ?? Math.max(
0,
...Matrix.map(
Row =>
Array.isArray(Row)
? Row.length
: 0
)
);
if (
TargetWidth <= 0 ||
TargetHeight <= 0
) {
return [];
}
const Normalized = [];
for (
let Y = 0;
Y < TargetHeight;
Y++
) {
const SourceRow =
Array.isArray(
Matrix[Y]
)
? Matrix[Y]
: [];
const Row = [];
for (
let X = 0;
X < TargetWidth;
X++
) {
Row.push(
X < SourceRow.length
? SourceRow[X]
: FillValue
);
}
Normalized.push(
Row
);
}
return Normalized;
}
function CreateMutationEncyclopediaRelations(
Mutation
) {
const Relations =
document.createElement(
"div"
);
Relations.className =
"MutationEncyclopediaRelations";
Relations.append(
CreateMutationPlantRelationGroup(
"Plants used",
Mutation.Relations
?.PlantsUsed ?? []
),
CreateMutationPlantRelationGroup(
"Plants created",
Mutation.Relations
?.PlantsCreated ?? []
)
);
return Relations;
}
function CreateMutationPlantRelationGroup(
Heading,
PlantIds
) {
const Group =
document.createElement(
"section"
);
Group.className =
"MutationEncyclopediaRelationGroup";
const Title =
document.createElement(
"h3"
);
Title.textContent =
Heading;
const List =
document.createElement(
"ul"
);
List.className =
"MutationEncyclopediaRelationList";
const SortedPlantIds = [
...new Set(
PlantIds.map(
PlantId =>
Number(PlantId)
)
)
].sort(
(A, B) => A - B
);
if (SortedPlantIds.length === 0) {
const Empty =
document.createElement(
"li"
);
Empty.className =
"MutationEncyclopediaRelationEmpty";
Empty.textContent =
"None";
List.appendChild(
Empty
);
} else {
for (
const PlantId
of SortedPlantIds
) {
List.appendChild(
CreateMutationPlantRelation(
PlantId
)
);
}
}
Group.append(
Title,
List
);
return Group;
}
function CreateMutationPlantRelation(
PlantId
) {
const Item =
document.createElement(
"li"
);
if (
!HasDiscoveredPlant(
MutationEncyclopediaSave,
PlantId
)
) {
Item.className =
"MutationEncyclopediaRelationUnknown";
Item.textContent =
"????????";
return Item;
}
const Plant =
GetPlantById(
PlantId
);
if (Plant === null) {
Item.textContent =
"Plant " +
PlantId;
return Item;
}
const Link =
document.createElement(
"a"
);
Link.href =
"/Pages/Plants.html#Plant-" +
PlantId;
Link.textContent =
Plant.Name +
(
Plant.Archived === true
? " (archived)"
: ""
);
Item.appendChild(
Link
);
return Item;
}
function FormatMutationChance(
Chance
) {
const Percent =
Math.max(
0,
Math.min(
Number(
Chance ?? 1
),
1
)
) * 100;
return Percent.toLocaleString(
undefined,
{
maximumFractionDigits: 2
}
) + "%";
}
function FormatMutationEncyclopediaTime(
Milliseconds
) {
let TotalSeconds =
Math.max(
0,
Math.ceil(
Number(
Milliseconds ?? 0
) /
1000
)
);
if (TotalSeconds === 0) {
return "None";
}
const Hours =
Math.floor(
TotalSeconds /
3600
);
TotalSeconds %= 3600;
const Minutes =
Math.floor(
TotalSeconds /
60
);
const Seconds =
TotalSeconds % 60;
const Parts = [];
if (Hours > 0) {
Parts.push(
Hours + "h"
);
}
if (Minutes > 0) {
Parts.push(
Minutes + "m"
);
}
if (Seconds > 0) {
Parts.push(
Seconds + "s"
);
}
return Parts.join(
" "
);
}
function SetMutationEncyclopediaMessage(
Message
) {
const Element =
document.getElementById(
"MutationEncyclopediaMessage"
);
if (Element !== null) {
Element.textContent =
Message;
}
}
document.addEventListener(
"DOMContentLoaded",
StartMutationEncyclopedia
);