From 3f161913290cc536d54e55d176cebe4f6cc59039 Mon Sep 17 00:00:00 2001 From: Alex Mooney Date: Mon, 26 Jan 2026 13:56:06 -0800 Subject: [PATCH 1/6] Add list of monster that upgrade into this monster --- src/types/Monster.svelte | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/types/Monster.svelte b/src/types/Monster.svelte index 10ff08ee..77ba5f02 100644 --- a/src/types/Monster.svelte +++ b/src/types/Monster.svelte @@ -309,6 +309,27 @@ let upgrades = : [], } : null; + +// Find monsters that upgrade to this one +let upgradesFrom: string[] = []; +for (const monster of data.byType("monster")) { + if (!monster.upgrades || !monster.id) continue; + + // Check if this monster upgrades directly to the current item + if (monster.upgrades.into === item.id) { + upgradesFrom.push(monster.id); + } + + // Check if this monster upgrades to a group that includes the current item + if (monster.upgrades.into_group) { + const groupMonsters = flattenGroup( + data.byId("monstergroup", monster.upgrades.into_group) + ); + if (groupMonsters.includes(item.id)) { + upgradesFrom.push(monster.id); + } + } +}

{singularName(item)}

@@ -526,6 +547,16 @@ let upgrades = {/if} {/if} + {#if upgradesFrom.length > 0} +
{t("Upgrades From", { _context })}
+
+ +
+ {/if} {#if deathDrops.size} From 461823613abbf15f65b2f36a65a6207e3caad28c Mon Sep 17 00:00:00 2001 From: Alex Mooney Date: Mon, 26 Jan 2026 13:58:15 -0800 Subject: [PATCH 2/6] Sort Upgrades lists alphabetically --- src/types/Monster.svelte | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/types/Monster.svelte b/src/types/Monster.svelte index 77ba5f02..662cad9f 100644 --- a/src/types/Monster.svelte +++ b/src/types/Monster.svelte @@ -5,6 +5,7 @@ import { getContext } from "svelte"; import { asKilograms, asLiters, + byName, CddaData, i18n, normalizeDamageInstance, @@ -302,11 +303,14 @@ let upgrades = item.upgrades && (item.upgrades.into || item.upgrades.into_group) ? { ...item.upgrades, - monsters: item.upgrades.into + monsters: (item.upgrades.into ? [item.upgrades.into] : item.upgrades.into_group ? flattenGroup(data.byId("monstergroup", item.upgrades.into_group)) - : [], + : [] + ).sort((a, b) => + byName(data.byIdMaybe("monster", a), data.byIdMaybe("monster", b)) + ), } : null; @@ -330,6 +334,10 @@ for (const monster of data.byType("monster")) { } } } +// Sort alphabetically +upgradesFrom.sort((a, b) => + byName(data.byIdMaybe("monster", a), data.byIdMaybe("monster", b)) +);

{singularName(item)}

From 68bf9a3fb86d1431df7b95188c5d1c5ce662e130 Mon Sep 17 00:00:00 2001 From: Alex Mooney Date: Tue, 27 Jan 2026 10:45:56 -0800 Subject: [PATCH 3/6] Add timing info to upgrades from --- src/types/Monster.svelte | 88 +++++++++++++++++++++++++++++++++------- 1 file changed, 74 insertions(+), 14 deletions(-) diff --git a/src/types/Monster.svelte b/src/types/Monster.svelte index 662cad9f..e033b764 100644 --- a/src/types/Monster.svelte +++ b/src/types/Monster.svelte @@ -315,29 +315,71 @@ let upgrades = : null; // Find monsters that upgrade to this one -let upgradesFrom: string[] = []; +interface UpgradeFromInfo { + monsterId: string; + age_grow?: number; + half_life?: number; +} + +let upgradesFromRaw: UpgradeFromInfo[] = []; for (const monster of data.byType("monster")) { if (!monster.upgrades || !monster.id) continue; + let matches = false; // Check if this monster upgrades directly to the current item if (monster.upgrades.into === item.id) { - upgradesFrom.push(monster.id); + matches = true; } // Check if this monster upgrades to a group that includes the current item - if (monster.upgrades.into_group) { + if (!matches && monster.upgrades.into_group) { const groupMonsters = flattenGroup( data.byId("monstergroup", monster.upgrades.into_group) ); if (groupMonsters.includes(item.id)) { - upgradesFrom.push(monster.id); + matches = true; } } + + if (matches) { + upgradesFromRaw.push({ + monsterId: monster.id, + age_grow: monster.upgrades.age_grow, + half_life: monster.upgrades.half_life, + }); + } +} + +// Group by timing and sort +const upgradesFromGrouped = new Map(); +for (const upgrade of upgradesFromRaw) { + let timingKey = ""; + if (upgrade.age_grow) { + timingKey = `age_grow:${upgrade.age_grow}`; + } else if (upgrade.half_life) { + timingKey = `half_life:${upgrade.half_life}`; + } else { + timingKey = "no_timing"; + } + + if (!upgradesFromGrouped.has(timingKey)) { + upgradesFromGrouped.set(timingKey, []); + } + upgradesFromGrouped.get(timingKey)!.push(upgrade.monsterId); +} + +// Sort monsters within each group alphabetically +for (const [_, monsters] of upgradesFromGrouped) { + monsters.sort((a, b) => + byName(data.byIdMaybe("monster", a), data.byIdMaybe("monster", b)) + ); +} + +function getTimingValue(key: string): number { + if (key.startsWith("age_grow:")) return parseInt(key.split(":")[1]); + if (key.startsWith("half_life:")) return parseInt(key.split(":")[1]); + return Number.MAX_SAFE_INTEGER; // no timing comes last } -// Sort alphabetically -upgradesFrom.sort((a, b) => - byName(data.byIdMaybe("monster", a), data.byIdMaybe("monster", b)) -);

{singularName(item)}

@@ -555,14 +597,32 @@ upgradesFrom.sort((a, b) => {/if} {/if} - {#if upgradesFrom.length > 0} + {#if upgradesFromGrouped.size > 0}
{t("Upgrades From", { _context })}
-
    - {#each upgradesFrom as monId} -
  • - {/each} -
+ {#each [...upgradesFromGrouped.entries()].sort(([a], [b]) => { + return getTimingValue(a) - getTimingValue(b); + }) as [timingKey, monsterIds], i} + {#if i > 0}; {/if} + + {#each monsterIds as monId, j} + {#if j > 0}, {/if} + {/each} + + {#if timingKey.startsWith("age_grow:")} + {@const days = parseInt(timingKey.split(":")[1])} + {t("in {days} {days, plural, =1 {day} other {days}}", { + _context, + days, + })} + {:else if timingKey.startsWith("half_life:")} + {@const half_life = parseInt(timingKey.split(":")[1])} + {t( + "with a half-life of {half_life} {half_life, plural, =1 {day} other {days}}", + { _context, half_life } + )} + {/if} + {/each}
{/if} From 0c9221fae209694ec072d1acda9299f805b3a053 Mon Sep 17 00:00:00 2001 From: Alex Mooney Date: Thu, 19 Feb 2026 13:03:26 -0800 Subject: [PATCH 4/6] Rename key from monsterId to id --- src/types/Monster.svelte | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/types/Monster.svelte b/src/types/Monster.svelte index e033b764..5d425544 100644 --- a/src/types/Monster.svelte +++ b/src/types/Monster.svelte @@ -316,7 +316,7 @@ let upgrades = // Find monsters that upgrade to this one interface UpgradeFromInfo { - monsterId: string; + id: string; age_grow?: number; half_life?: number; } @@ -343,7 +343,7 @@ for (const monster of data.byType("monster")) { if (matches) { upgradesFromRaw.push({ - monsterId: monster.id, + id: monster.id, age_grow: monster.upgrades.age_grow, half_life: monster.upgrades.half_life, }); @@ -365,7 +365,7 @@ for (const upgrade of upgradesFromRaw) { if (!upgradesFromGrouped.has(timingKey)) { upgradesFromGrouped.set(timingKey, []); } - upgradesFromGrouped.get(timingKey)!.push(upgrade.monsterId); + upgradesFromGrouped.get(timingKey)!.push(upgrade.id); } // Sort monsters within each group alphabetically From e3471c9c20f214abed7ed7afc21eb2b6d9edb385 Mon Sep 17 00:00:00 2001 From: Alex Mooney Date: Thu, 19 Feb 2026 13:08:31 -0800 Subject: [PATCH 5/6] Extract upgradesFrom into a function --- src/types/Monster.svelte | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/types/Monster.svelte b/src/types/Monster.svelte index 5d425544..1e473968 100644 --- a/src/types/Monster.svelte +++ b/src/types/Monster.svelte @@ -321,27 +321,23 @@ interface UpgradeFromInfo { half_life?: number; } -let upgradesFromRaw: UpgradeFromInfo[] = []; -for (const monster of data.byType("monster")) { - if (!monster.upgrades || !monster.id) continue; +function upgradesFrom(other: Monster) { + if (!other.id || !other.upgrades) return false; + if (other.upgrades.into === item.id) return true; - let matches = false; - // Check if this monster upgrades directly to the current item - if (monster.upgrades.into === item.id) { - matches = true; + if (other.upgrades.into_group) { + return flattenGroup( + data.byId("monstergroup", other.upgrades.into_group) + ).includes(item.id); } + return false; +} - // Check if this monster upgrades to a group that includes the current item - if (!matches && monster.upgrades.into_group) { - const groupMonsters = flattenGroup( - data.byId("monstergroup", monster.upgrades.into_group) - ); - if (groupMonsters.includes(item.id)) { - matches = true; - } - } +const upgradesFromRaw: UpgradeFromInfo[] = []; +for (const monster of data.byType("monster")) { + if (!monster.id || !monster.upgrades) continue; - if (matches) { + if (upgradesFrom(monster)) { upgradesFromRaw.push({ id: monster.id, age_grow: monster.upgrades.age_grow, From 2f06e117481081ceadedd91602445866feb6072f Mon Sep 17 00:00:00 2001 From: Alex Mooney Date: Thu, 19 Feb 2026 13:38:10 -0800 Subject: [PATCH 6/6] Refactor sorting to avoid string parsing --- src/types/Monster.svelte | 69 ++++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/src/types/Monster.svelte b/src/types/Monster.svelte index 1e473968..bc73318e 100644 --- a/src/types/Monster.svelte +++ b/src/types/Monster.svelte @@ -346,35 +346,64 @@ for (const monster of data.byType("monster")) { } } -// Group by timing and sort -const upgradesFromGrouped = new Map(); +// Group monsters into their group based on upgrade type and timing +const upgradesFromByHalfLife = new Map(); +const upgradesFromByAgeGrow = new Map(); +const upgradesFromNoTiming = []; for (const upgrade of upgradesFromRaw) { - let timingKey = ""; if (upgrade.age_grow) { - timingKey = `age_grow:${upgrade.age_grow}`; + if (!upgradesFromByAgeGrow.has(upgrade.age_grow)) { + upgradesFromByAgeGrow.set(upgrade.age_grow, []); + } + upgradesFromByAgeGrow.get(upgrade.age_grow)!.push(upgrade.id); } else if (upgrade.half_life) { - timingKey = `half_life:${upgrade.half_life}`; + if (!upgradesFromByHalfLife.has(upgrade.half_life)) { + upgradesFromByHalfLife.set(upgrade.half_life, []); + } + upgradesFromByHalfLife.get(upgrade.half_life)!.push(upgrade.id); } else { - timingKey = "no_timing"; + upgradesFromNoTiming.push(upgrade.id); } +} - if (!upgradesFromGrouped.has(timingKey)) { - upgradesFromGrouped.set(timingKey, []); +// Sort monsters with the same timing alphabetically +for (const monsterByTime of [upgradesFromByHalfLife, upgradesFromByAgeGrow]) { + for (const [_, monsters] of monsterByTime) { + monsters.sort((a, b) => + byName(data.byIdMaybe("monster", a), data.byIdMaybe("monster", b)) + ); } - upgradesFromGrouped.get(timingKey)!.push(upgrade.id); } +upgradesFromNoTiming.sort((a, b) => + byName(data.byIdMaybe("monster", a), data.byIdMaybe("monster", b)) +); -// Sort monsters within each group alphabetically -for (const [_, monsters] of upgradesFromGrouped) { - monsters.sort((a, b) => - byName(data.byIdMaybe("monster", a), data.byIdMaybe("monster", b)) +// Sort each group by timing and insert into one upgradesFromGrouped for display +const upgradesFromGrouped = new Map(); +if (upgradesFromByHalfLife.size > 0) { + const sortedHalfLifeKeys = Array.from(upgradesFromByHalfLife.keys()).sort( + (a, b) => a - b ); + for (const half_life of sortedHalfLifeKeys) { + upgradesFromGrouped.set( + `half_life:${half_life}`, + upgradesFromByHalfLife.get(half_life)! + ); + } } - -function getTimingValue(key: string): number { - if (key.startsWith("age_grow:")) return parseInt(key.split(":")[1]); - if (key.startsWith("half_life:")) return parseInt(key.split(":")[1]); - return Number.MAX_SAFE_INTEGER; // no timing comes last +if (upgradesFromByAgeGrow.size > 0) { + const sortedAgeGrowKeys = Array.from(upgradesFromByAgeGrow.keys()).sort( + (a, b) => a - b + ); + for (const age_grow of sortedAgeGrowKeys) { + upgradesFromGrouped.set( + `age_grow:${age_grow}`, + upgradesFromByAgeGrow.get(age_grow)! + ); + } +} +if (upgradesFromNoTiming.length > 0) { + upgradesFromGrouped.set("no_timing", upgradesFromNoTiming); } @@ -596,9 +625,7 @@ function getTimingValue(key: string): number { {#if upgradesFromGrouped.size > 0}
{t("Upgrades From", { _context })}
- {#each [...upgradesFromGrouped.entries()].sort(([a], [b]) => { - return getTimingValue(a) - getTimingValue(b); - }) as [timingKey, monsterIds], i} + {#each [...upgradesFromGrouped.entries()] as [timingKey, monsterIds], i} {#if i > 0}; {/if} {#each monsterIds as monId, j}