From f9f2b8f1e806e0f5248af1b6a6def2f40f697583 Mon Sep 17 00:00:00 2001 From: chris Date: Fri, 28 Aug 2026 11:14:35 -0400 Subject: [PATCH] Forms: keep formes that differ only by ability (Basculin, Toxtricity) The cosmetic-form filter now also requires identical abilities before dropping a variety, so gameplay-relevant look-alike formes (Basculin Blue/White-Striped, Toxtricity Low Key) are no longer discarded. 274 forms across the snapshot (+10 'other'). Co-Authored-By: Claude Sonnet 5 --- scripts/build-snapshot.mjs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/scripts/build-snapshot.mjs b/scripts/build-snapshot.mjs index 743dec2..6645359 100644 --- a/scripts/build-snapshot.mjs +++ b/scripts/build-snapshot.mjs @@ -192,9 +192,8 @@ async function main() { }; }; const bstOf = (st) => st.hp + st.atk + st.def + st.spa + st.spd + st.spe; - const sameShape = (a, b) => - a.types.join() === b.types.join() && a.bst === b.bst && - a.stats.hp === b.stats.hp && a.stats.spe === b.stats.spe && a.stats.atk === b.stats.atk; + const abilKey = (pk) => pk.abilities.map((a) => a.ability.name).sort().join(','); + const statKey = (st) => `${st.hp}/${st.atk}/${st.def}/${st.spa}/${st.spd}/${st.spe}`; console.log(`Fetching ${ids.length} species (stats, typings, flags, forms) …`); const species = await mapLimit(ids, CONCURRENCY, async (id) => { @@ -204,6 +203,8 @@ async function main() { ]); const stats = statMap(pk); const baseTypes = pk.types.slice().sort((a, b) => a.slot - b.slot).map((t) => t.type.name); + const baseAbil = abilKey(pk); + const baseStatKey = statKey(stats); // Non-default varieties = forms (megas, G-max, regional, alt formes). const variantSlugs = (sp.varieties || []) @@ -231,10 +232,14 @@ async function main() { height: fp.height, weight: fp.weight, }; - // Drop purely-cosmetic forms (identical stats & typing, no named category). - if (cat === 'other' && sameShape(entry, { types: baseTypes, bst: bstOf(stats), stats })) { - continue; - } + // Drop purely-cosmetic forms: same typing, stats AND abilities as the + // base, and not a named category (mega/gmax/regional/primal). + const cosmetic = + cat === 'other' && + ftypes.join() === baseTypes.join() && + statKey(fstats) === baseStatKey && + abilKey(fp) === baseAbil; + if (cosmetic) continue; forms.push(entry); }