Added archiving and fixed list economy tracing bug.

This commit is contained in:
sarah committed 2026-09-07 21:54:53 +02:00
1 parent 8a6d3b27e3
commit bf630894e1
6 files changed
+580 -19

No files matched your search

+8
View File
@@ -205,6 +205,14 @@ $PageSection = "Guide";
<a href="/Pages/Mutations.html">Mutation encyclopedia</a>. <a href="/Pages/Mutations.html">Mutation encyclopedia</a>.
</p> </p>
<p>
Retired content may be archived instead of deleted.
Archived plants and mutations no longer take part in
normal progression, but anything you already discovered
stays part of your Garden history and remains identified
as archived in the encyclopedias.
</p>
<p> <p>
Experiment with different plants, empty spaces, and Experiment with different plants, empty spaces, and
arrangements. Not every recipe has to look like the arrangements. Not every recipe has to look like the
+302 -4
View File
@@ -177,6 +177,10 @@ function IsPlantAvailableInShop(
SaveData, SaveData,
Plant Plant
) { ) {
if (Plant.Archived === true) {
return false;
}
if ( if (
Plant.Shop?.ShopPlant === Plant.Shop?.ShopPlant ===
true true
@@ -325,6 +329,10 @@ function GetPlantShopCost(
MutationSets MutationSets
) )
) { ) {
if (Mutation.Archived === true) {
continue;
}
if ( if (
!HasDiscoveredMutation( !HasDiscoveredMutation(
SaveData, SaveData,
@@ -392,6 +400,10 @@ function GetMutationIngredientCost(
Visiting Visiting
) { ) {
let TotalCost = 0; let TotalCost = 0;
const ListReferenceCounts =
GetEconomyMutationListReferenceCounts(
Mutation.Pattern
);
for ( for (
@@ -405,7 +417,10 @@ function GetMutationIngredientCost(
if ( if (
Matcher === null || Matcher === null ||
Matcher === "Empty" || Matcher === "Empty" ||
Matcher === "Any" Matcher === "Any" ||
GetEconomyMutationListReference(
Matcher
) !== null
) { ) {
continue; continue;
} }
@@ -428,10 +443,240 @@ function GetMutationIngredientCost(
} }
for (
const [
ListNumber,
ReferenceCount
]
of ListReferenceCounts
) {
const List =
Mutation.Lists?.[
ListNumber - 1
];
const ListCost =
GetMutationListSeedCost(
List,
ReferenceCount,
Item =>
GetMutationListItemSeedCost(
Item,
Plants,
Plant =>
GetPlantShopCost(
SaveData,
Plant.Id,
Cache,
Visiting
),
Plant =>
IsPlantAvailableInShop(
SaveData,
Plant
)
)
);
if (ListCost === null) {
return null;
}
TotalCost += ListCost;
}
return TotalCost; return TotalCost;
} }
function GetEconomyMutationListReference(
Matcher
) {
if (typeof Matcher !== "string") {
return null;
}
const Match =
/^List:([1-9][0-9]*)$/.exec(
Matcher
);
return Match === null
? null
: Number(Match[1]);
}
function GetEconomyMutationListReferenceCounts(
Pattern
) {
const Counts = new Map();
for (const Row of Pattern ?? []) {
if (!Array.isArray(Row)) {
continue;
}
for (const Matcher of Row) {
const ListNumber =
GetEconomyMutationListReference(
Matcher
);
if (ListNumber === null) {
continue;
}
Counts.set(
ListNumber,
(Counts.get(ListNumber) ?? 0) + 1
);
}
}
return Counts;
}
function GetMutationListSeedCost(
List,
ReferenceCount,
GetItemCost
) {
if (
List === null ||
typeof List !== "object" ||
!Array.isArray(List.Items) ||
List.Items.length === 0 ||
!Number.isInteger(ReferenceCount) ||
ReferenceCount <= 0
) {
return null;
}
const ItemCosts = [];
for (const Item of List.Items) {
const Cost = GetItemCost(Item);
if (
Cost === null ||
!Number.isFinite(Cost) ||
Cost < 0
) {
continue;
}
ItemCosts.push(Cost);
}
if (ItemCosts.length === 0) {
return null;
}
if (List.Mode !== "Once") {
return (
Math.min(...ItemCosts) *
ReferenceCount
);
}
if (ItemCosts.length < ReferenceCount) {
return null;
}
ItemCosts.sort(
(A, B) => A - B
);
return ItemCosts
.slice(0, ReferenceCount)
.reduce(
(Total, Cost) =>
Total + Cost,
0
);
}
function GetMutationListItemSeedCost(
Item,
PlantCatalogue,
GetPlantCost,
IsPlantCandidateAvailable
) {
if (
Item === null ||
typeof Item !== "object" ||
Array.isArray(Item)
) {
return null;
}
if (
Item.Type === "Any" ||
Item.Type === "Empty"
) {
return 0;
}
if (Item.Type === "Plant") {
const Plant =
PlantCatalogue[Item.Value];
if (
Plant === undefined ||
Plant.Archived === true
) {
return null;
}
return GetPlantCost(Plant);
}
if (Item.Type !== "Tag") {
return null;
}
let CheapestCost = null;
for (
const Plant
of Object.values(
PlantCatalogue
)
) {
if (
!IsPlantCandidateAvailable(
Plant
) ||
!Array.isArray(Plant.Tags) ||
!Plant.Tags.includes(
Item.Value
)
) {
continue;
}
const Cost = GetPlantCost(Plant);
if (Cost === null) {
continue;
}
if (
CheapestCost === null ||
Cost < CheapestCost
) {
CheapestCost = Cost;
}
}
return CheapestCost;
}
function GetMatcherSeedCost( function GetMatcherSeedCost(
SaveData, SaveData,
Matcher, Matcher,
@@ -442,7 +687,10 @@ function GetMatcherSeedCost(
const Plant = const Plant =
Plants[Matcher]; Plants[Matcher];
if (Plant === undefined) { if (
Plant === undefined ||
Plant.Archived === true
) {
return null; return null;
} }
@@ -470,7 +718,10 @@ function GetMatcherSeedCost(
const Plant = const Plant =
Plants[Matcher.Plant]; Plants[Matcher.Plant];
if (Plant === undefined) { if (
Plant === undefined ||
Plant.Archived === true
) {
return null; return null;
} }
@@ -974,13 +1225,20 @@ function GetCatalogueMutationIngredientCost(
Visiting Visiting
) { ) {
let TotalCost = 0; let TotalCost = 0;
const ListReferenceCounts =
GetEconomyMutationListReferenceCounts(
Mutation.Pattern
);
for (const Row of Mutation.Pattern ?? []) { for (const Row of Mutation.Pattern ?? []) {
for (const Matcher of Row) { for (const Matcher of Row) {
if ( if (
Matcher === null || Matcher === null ||
Matcher === "Empty" || Matcher === "Empty" ||
Matcher === "Any" Matcher === "Any" ||
GetEconomyMutationListReference(
Matcher
) !== null
) { ) {
continue; continue;
} }
@@ -1002,6 +1260,46 @@ function GetCatalogueMutationIngredientCost(
} }
} }
for (
const [
ListNumber,
ReferenceCount
]
of ListReferenceCounts
) {
const List =
Mutation.Lists?.[
ListNumber - 1
];
const ListCost =
GetMutationListSeedCost(
List,
ReferenceCount,
Item =>
GetMutationListItemSeedCost(
Item,
PlantCatalogue,
Plant =>
GetCataloguePlantCostInfo(
Plant.Id,
PlantCatalogue,
MutationCatalogue,
Cache,
Visiting
)?.Cost ?? null,
Plant =>
Plant.Archived !== true
)
);
if (ListCost === null) {
return null;
}
TotalCost += ListCost;
}
return TotalCost; return TotalCost;
} }
+121 -4
View File
@@ -97,12 +97,25 @@ function RenderMutationEncyclopedia() {
) )
); );
const ActiveDiscoveredMutations =
DiscoveredMutations.filter(
Mutation =>
Mutation.Archived !== true
);
const ArchivedDiscoveredMutations =
DiscoveredMutations.filter(
Mutation =>
Mutation.Archived === true
);
const AvailableHints = const AvailableHints =
HasMutationHintsUpgrade( HasMutationHintsUpgrade(
MutationEncyclopediaSave MutationEncyclopediaSave
) )
? AllMutations.filter( ? AllMutations.filter(
Mutation => Mutation =>
Mutation.Archived !== true &&
!HasDiscoveredMutation( !HasDiscoveredMutation(
MutationEncyclopediaSave, MutationEncyclopediaSave,
Mutation.Id Mutation.Id
@@ -142,8 +155,8 @@ function RenderMutationEncyclopedia() {
) )
); );
const VisibleMutations = const VisibleActiveMutations =
DiscoveredMutations ActiveDiscoveredMutations
.filter( .filter(
Mutation => Mutation =>
DoesMutationEncyclopediaMatchSearch( DoesMutationEncyclopediaMatchSearch(
@@ -155,6 +168,24 @@ function RenderMutationEncyclopedia() {
CompareMutationEncyclopediaMutations CompareMutationEncyclopediaMutations
); );
const VisibleArchivedMutations =
ArchivedDiscoveredMutations
.filter(
Mutation =>
DoesMutationEncyclopediaMatchSearch(
Mutation,
SearchQuery
)
)
.sort(
CompareMutationEncyclopediaMutations
);
const VisibleMutations = [
...VisibleActiveMutations,
...VisibleArchivedMutations
];
if (VisibleHints.length > 0) { if (VisibleHints.length > 0) {
List.appendChild( List.appendChild(
@@ -173,7 +204,7 @@ function RenderMutationEncyclopedia() {
for ( for (
const Mutation const Mutation
of VisibleMutations of VisibleActiveMutations
) { ) {
List.appendChild( List.appendChild(
CreateMutationEncyclopediaCard( CreateMutationEncyclopediaCard(
@@ -183,6 +214,26 @@ function RenderMutationEncyclopedia() {
} }
if (VisibleArchivedMutations.length > 0) {
List.appendChild(
CreateMutationEncyclopediaArchivedHeading(
VisibleArchivedMutations.length
)
);
for (
const Mutation
of VisibleArchivedMutations
) {
List.appendChild(
CreateMutationEncyclopediaCard(
Mutation
)
);
}
}
if ( if (
DiscoveredMutations.length === 0 && DiscoveredMutations.length === 0 &&
AvailableHints.length === 0 AvailableHints.length === 0
@@ -229,6 +280,16 @@ function RenderMutationEncyclopedia() {
); );
} }
if (ArchivedDiscoveredMutations.length > 0) {
MessageParts.push(
ArchivedDiscoveredMutations.length === 1
? "1 archived mutation."
: ArchivedDiscoveredMutations.length
.toLocaleString() +
" archived mutations."
);
}
if (AvailableHints.length > 0) { if (AvailableHints.length > 0) {
MessageParts.push( MessageParts.push(
"Showing " + "Showing " +
@@ -251,6 +312,41 @@ function RenderMutationEncyclopedia() {
} }
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( function DoesMutationHintMatchSearch(
Mutation, Mutation,
SearchQuery SearchQuery
@@ -282,6 +378,9 @@ function DoesMutationEncyclopediaMatchSearch(
Mutation.Name, Mutation.Name,
Mutation.Description, Mutation.Description,
Mutation.Hint, Mutation.Hint,
Mutation.Archived === true
? "Archived"
: "",
FormatMutationChance( FormatMutationChance(
Mutation.Chance Mutation.Chance
), ),
@@ -552,6 +651,7 @@ function IsHintListItemAvailable(
return ( return (
Plant !== undefined && Plant !== undefined &&
Plant.Archived !== true &&
HasDiscoveredPlant( HasDiscoveredPlant(
SaveData, SaveData,
Plant.Id Plant.Id
@@ -564,6 +664,7 @@ function IsHintListItemAvailable(
Plants Plants
).some( ).some(
Plant => Plant =>
Plant.Archived !== true &&
HasDiscoveredPlant( HasDiscoveredPlant(
SaveData, SaveData,
Plant.Id Plant.Id
@@ -677,6 +778,7 @@ function IsHintMatcherAvailable(
return ( return (
Plant !== undefined && Plant !== undefined &&
Plant.Archived !== true &&
HasDiscoveredPlant( HasDiscoveredPlant(
SaveData, SaveData,
Plant.Id Plant.Id
@@ -698,6 +800,7 @@ function IsHintMatcherAvailable(
Plants Plants
).some( ).some(
([PlantKey, Plant]) => ([PlantKey, Plant]) =>
Plant.Archived !== true &&
HasDiscoveredPlant( HasDiscoveredPlant(
SaveData, SaveData,
Plant.Id Plant.Id
@@ -1019,6 +1122,15 @@ function CreateMutationEncyclopediaStats(
) )
); );
if (Mutation.Archived === true) {
Stats.appendChild(
CreateMutationEncyclopediaStat(
"Status",
"Archived"
)
);
}
return Stats; return Stats;
} }
@@ -2049,7 +2161,12 @@ function CreateMutationPlantRelation(
PlantId; PlantId;
Link.textContent = Link.textContent =
Plant.Name; Plant.Name +
(
Plant.Archived === true
? " (archived)"
: ""
);
Item.appendChild( Item.appendChild(
+4
View File
@@ -277,6 +277,10 @@ function FindGardenMutationCandidates(
MutationSets MutationSets
) )
) { ) {
if (Mutation.Archived === true) {
continue;
}
for ( for (
const Orientation const Orientation
of GetMutationOrientations( of GetMutationOrientations(
+3
View File
@@ -33,6 +33,9 @@ function DoesPlantCatalogueMatchSearch(
Plant.Id, Plant.Id,
Plant.Name, Plant.Name,
Plant.Description, Plant.Description,
Plant.Archived === true
? "Archived"
: "",
...(Array.isArray(Plant.Tags) ...(Array.isArray(Plant.Tags)
? Plant.Tags ? Plant.Tags
: []) : [])
+142 -11
View File
@@ -88,13 +88,25 @@ function RenderPlantEncyclopedia() {
) )
); );
const ActivePlants =
DiscoveredPlants.filter(
Plant =>
Plant.Archived !== true
);
const ArchivedPlants =
DiscoveredPlants.filter(
Plant =>
Plant.Archived === true
);
const SearchQuery = const SearchQuery =
PlantEncyclopediaSearchQuery PlantEncyclopediaSearchQuery
.trim() .trim()
.toLocaleLowerCase(); .toLocaleLowerCase();
const VisiblePlants = const VisibleActivePlants =
DiscoveredPlants ActivePlants
.filter( .filter(
Plant => Plant =>
DoesPlantEncyclopediaMatchSearch( DoesPlantEncyclopediaMatchSearch(
@@ -106,6 +118,24 @@ function RenderPlantEncyclopedia() {
ComparePlantEncyclopediaPlants ComparePlantEncyclopediaPlants
); );
const VisibleArchivedPlants =
ArchivedPlants
.filter(
Plant =>
DoesPlantEncyclopediaMatchSearch(
Plant,
SearchQuery
)
)
.sort(
ComparePlantEncyclopediaPlants
);
const VisiblePlants = [
...VisibleActivePlants,
...VisibleArchivedPlants
];
if (DiscoveredPlants.length === 0) { if (DiscoveredPlants.length === 0) {
SetPlantEncyclopediaMessage( SetPlantEncyclopediaMessage(
@@ -127,7 +157,7 @@ function RenderPlantEncyclopedia() {
for ( for (
const Plant const Plant
of VisiblePlants of VisibleActivePlants
) { ) {
List.appendChild( List.appendChild(
CreatePlantEncyclopediaCard( CreatePlantEncyclopediaCard(
@@ -137,6 +167,36 @@ function RenderPlantEncyclopedia() {
} }
if (VisibleArchivedPlants.length > 0) {
List.appendChild(
CreatePlantEncyclopediaArchivedHeading(
VisibleArchivedPlants.length
)
);
for (
const Plant
of VisibleArchivedPlants
) {
List.appendChild(
CreatePlantEncyclopediaCard(
Plant
)
);
}
}
const ArchivedSummary =
ArchivedPlants.length > 0
? " " +
ArchivedPlants.length
.toLocaleString() +
(ArchivedPlants.length === 1
? " archived plant."
: " archived plants.")
: "";
if (SearchQuery.length > 0) { if (SearchQuery.length > 0) {
SetPlantEncyclopediaMessage( SetPlantEncyclopediaMessage(
"Showing " + "Showing " +
@@ -145,7 +205,8 @@ function RenderPlantEncyclopedia() {
" of " + " of " +
DiscoveredPlants.length DiscoveredPlants.length
.toLocaleString() + .toLocaleString() +
" discovered plants." " discovered plants." +
ArchivedSummary
); );
return; return;
@@ -153,15 +214,52 @@ function RenderPlantEncyclopedia() {
SetPlantEncyclopediaMessage( SetPlantEncyclopediaMessage(
DiscoveredPlants.length === 1 (
? "1 plant discovered." DiscoveredPlants.length === 1
: DiscoveredPlants.length ? "1 plant discovered."
.toLocaleString() + : DiscoveredPlants.length
" plants discovered." .toLocaleString() +
" plants discovered."
) + ArchivedSummary
); );
} }
function CreatePlantEncyclopediaArchivedHeading(
Count
) {
const Header =
document.createElement(
"header"
);
const Heading =
document.createElement(
"h2"
);
Heading.textContent =
"Archived (" +
Count.toLocaleString() +
")";
const Description =
document.createElement(
"p"
);
Description.textContent =
"Retired plants you discovered remain recorded here.";
Header.append(
Heading,
Description
);
return Header;
}
function DoesPlantEncyclopediaMatchSearch( function DoesPlantEncyclopediaMatchSearch(
Plant, Plant,
SearchQuery SearchQuery
@@ -384,6 +482,16 @@ function CreatePlantEncyclopediaDetails(
); );
if (Plant.Archived === true) {
Details.appendChild(
CreatePlantEncyclopediaStat(
"Status",
"Archived"
)
);
}
Details.appendChild( Details.appendChild(
CreatePlantEncyclopediaStat( CreatePlantEncyclopediaStat(
"Growth", "Growth",
@@ -545,6 +653,15 @@ function CreatePlantMutationRelationGroup(
Heading, Heading,
Mutations Mutations
) { ) {
Mutations = Mutations.filter(
Mutation =>
Mutation.Archived !== true ||
HasDiscoveredMutation(
PlantEncyclopediaSave,
Mutation.Id
)
);
const Group = const Group =
document.createElement( document.createElement(
"section" "section"
@@ -646,7 +763,12 @@ function CreatePlantMutationRelation(
Mutation.Id; Mutation.Id;
Link.textContent = Link.textContent =
Mutation.Name; Mutation.Name +
(
Mutation.Archived === true
? " (archived)"
: ""
);
Item.appendChild( Item.appendChild(
@@ -684,7 +806,16 @@ function GetPlantRecipeProgress(
Number(PlantId) Number(PlantId)
); );
if (IsRelated) { if (
IsRelated &&
(
Mutation.Archived !== true ||
HasDiscoveredMutation(
PlantEncyclopediaSave,
Mutation.Id
)
)
) {
RelatedMutationIds.add( RelatedMutationIds.add(
Mutation.Id Mutation.Id
); );