Add Pokemon form support to seeding (Alolan, regional variants, etc.)
Pokemon forms with dex IDs >= 10000 (e.g., Alolan Rattata = 10091) were being collected in encounter data but missing from pokemon.json, causing them to be silently dropped during DB seeding. Now fetch_all_pokemon() also fetches form entries that appear in encounter data, with clean display names like "Rattata (Alola)" and correct form-specific types. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,11 @@
|
|||||||
---
|
---
|
||||||
# nuzlocke-tracker-f44d
|
# nuzlocke-tracker-f44d
|
||||||
title: Add Pokemon forms support to seeding
|
title: Add Pokemon forms support to seeding
|
||||||
status: todo
|
status: completed
|
||||||
type: task
|
type: task
|
||||||
|
priority: normal
|
||||||
created_at: 2026-02-06T10:11:23Z
|
created_at: 2026-02-06T10:11:23Z
|
||||||
updated_at: 2026-02-06T10:11:23Z
|
updated_at: 2026-02-07T13:30:57Z
|
||||||
---
|
---
|
||||||
|
|
||||||
The current seeding only fetches base Pokemon species. It should also include alternate forms (Alolan, Galarian, Mega, regional variants, etc.) which have different types and stats.
|
The current seeding only fetches base Pokemon species. It should also include alternate forms (Alolan, Galarian, Mega, regional variants, etc.) which have different types and stats.
|
||||||
|
|||||||
@@ -85,71 +85,9 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "Eterna City",
|
|
||||||
"order": 2,
|
|
||||||
"encounters": [
|
|
||||||
{
|
|
||||||
"national_dex": 129,
|
|
||||||
"pokemon_name": "magikarp",
|
|
||||||
"method": "old-rod",
|
|
||||||
"encounter_rate": 100,
|
|
||||||
"min_level": 3,
|
|
||||||
"max_level": 15
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"national_dex": 54,
|
|
||||||
"pokemon_name": "psyduck",
|
|
||||||
"method": "surf",
|
|
||||||
"encounter_rate": 90,
|
|
||||||
"min_level": 20,
|
|
||||||
"max_level": 30
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"national_dex": 130,
|
|
||||||
"pokemon_name": "gyarados",
|
|
||||||
"method": "super-rod",
|
|
||||||
"encounter_rate": 55,
|
|
||||||
"min_level": 30,
|
|
||||||
"max_level": 55
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"national_dex": 129,
|
|
||||||
"pokemon_name": "magikarp",
|
|
||||||
"method": "good-rod",
|
|
||||||
"encounter_rate": 55,
|
|
||||||
"min_level": 10,
|
|
||||||
"max_level": 25
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"national_dex": 339,
|
|
||||||
"pokemon_name": "barboach",
|
|
||||||
"method": "good-rod",
|
|
||||||
"encounter_rate": 45,
|
|
||||||
"min_level": 10,
|
|
||||||
"max_level": 25
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"national_dex": 340,
|
|
||||||
"pokemon_name": "whiscash",
|
|
||||||
"method": "super-rod",
|
|
||||||
"encounter_rate": 45,
|
|
||||||
"min_level": 30,
|
|
||||||
"max_level": 55
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"national_dex": 55,
|
|
||||||
"pokemon_name": "golduck",
|
|
||||||
"method": "surf",
|
|
||||||
"encounter_rate": 10,
|
|
||||||
"min_level": 20,
|
|
||||||
"max_level": 40
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "Pastoria City",
|
"name": "Pastoria City",
|
||||||
"order": 3,
|
"order": 2,
|
||||||
"encounters": [
|
"encounters": [
|
||||||
{
|
{
|
||||||
"national_dex": 129,
|
"national_dex": 129,
|
||||||
@@ -235,7 +173,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Sunyshore City",
|
"name": "Sunyshore City",
|
||||||
"order": 4,
|
"order": 3,
|
||||||
"encounters": [
|
"encounters": [
|
||||||
{
|
{
|
||||||
"national_dex": 129,
|
"national_dex": 129,
|
||||||
@@ -321,7 +259,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Pokemon League",
|
"name": "Pokemon League",
|
||||||
"order": 5,
|
"order": 4,
|
||||||
"encounters": [
|
"encounters": [
|
||||||
{
|
{
|
||||||
"national_dex": 129,
|
"national_dex": 129,
|
||||||
@@ -399,12 +337,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Oreburgh Mine",
|
"name": "Oreburgh Mine",
|
||||||
"order": 6,
|
"order": 5,
|
||||||
"encounters": [],
|
"encounters": [],
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"name": "Oreburgh Mine (1F)",
|
"name": "Oreburgh Mine (1F)",
|
||||||
"order": 7,
|
"order": 6,
|
||||||
"encounters": [
|
"encounters": [
|
||||||
{
|
{
|
||||||
"national_dex": 74,
|
"national_dex": 74,
|
||||||
@@ -434,7 +372,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Oreburgh Mine (B1F)",
|
"name": "Oreburgh Mine (B1F)",
|
||||||
"order": 8,
|
"order": 7,
|
||||||
"encounters": [
|
"encounters": [
|
||||||
{
|
{
|
||||||
"national_dex": 74,
|
"national_dex": 74,
|
||||||
@@ -466,7 +404,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Valley Windworks",
|
"name": "Valley Windworks",
|
||||||
"order": 9,
|
"order": 8,
|
||||||
"encounters": [
|
"encounters": [
|
||||||
{
|
{
|
||||||
"national_dex": 129,
|
"national_dex": 129,
|
||||||
@@ -600,7 +538,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Eterna Forest",
|
"name": "Eterna Forest",
|
||||||
"order": 10,
|
"order": 9,
|
||||||
"encounters": [
|
"encounters": [
|
||||||
{
|
{
|
||||||
"national_dex": 406,
|
"national_dex": 406,
|
||||||
@@ -756,6 +694,68 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Eterna City",
|
||||||
|
"order": 10,
|
||||||
|
"encounters": [
|
||||||
|
{
|
||||||
|
"national_dex": 129,
|
||||||
|
"pokemon_name": "magikarp",
|
||||||
|
"method": "old-rod",
|
||||||
|
"encounter_rate": 100,
|
||||||
|
"min_level": 3,
|
||||||
|
"max_level": 15
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"national_dex": 54,
|
||||||
|
"pokemon_name": "psyduck",
|
||||||
|
"method": "surf",
|
||||||
|
"encounter_rate": 90,
|
||||||
|
"min_level": 20,
|
||||||
|
"max_level": 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"national_dex": 130,
|
||||||
|
"pokemon_name": "gyarados",
|
||||||
|
"method": "super-rod",
|
||||||
|
"encounter_rate": 55,
|
||||||
|
"min_level": 30,
|
||||||
|
"max_level": 55
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"national_dex": 129,
|
||||||
|
"pokemon_name": "magikarp",
|
||||||
|
"method": "good-rod",
|
||||||
|
"encounter_rate": 55,
|
||||||
|
"min_level": 10,
|
||||||
|
"max_level": 25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"national_dex": 339,
|
||||||
|
"pokemon_name": "barboach",
|
||||||
|
"method": "good-rod",
|
||||||
|
"encounter_rate": 45,
|
||||||
|
"min_level": 10,
|
||||||
|
"max_level": 25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"national_dex": 340,
|
||||||
|
"pokemon_name": "whiscash",
|
||||||
|
"method": "super-rod",
|
||||||
|
"encounter_rate": 45,
|
||||||
|
"min_level": 30,
|
||||||
|
"max_level": 55
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"national_dex": 55,
|
||||||
|
"pokemon_name": "golduck",
|
||||||
|
"method": "surf",
|
||||||
|
"encounter_rate": 10,
|
||||||
|
"min_level": 20,
|
||||||
|
"max_level": 40
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Fuego Ironworks",
|
"name": "Fuego Ironworks",
|
||||||
"order": 11,
|
"order": 11,
|
||||||
|
|||||||
@@ -8724,5 +8724,145 @@
|
|||||||
"ghost"
|
"ghost"
|
||||||
],
|
],
|
||||||
"sprite_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/1025.png"
|
"sprite_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/1025.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"national_dex": 10016,
|
||||||
|
"name": "Basculin (Blue Striped)",
|
||||||
|
"types": [
|
||||||
|
"water"
|
||||||
|
],
|
||||||
|
"sprite_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/10016.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"national_dex": 10091,
|
||||||
|
"name": "Rattata (Alola)",
|
||||||
|
"types": [
|
||||||
|
"dark",
|
||||||
|
"normal"
|
||||||
|
],
|
||||||
|
"sprite_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/10091.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"national_dex": 10092,
|
||||||
|
"name": "Raticate (Alola)",
|
||||||
|
"types": [
|
||||||
|
"dark",
|
||||||
|
"normal"
|
||||||
|
],
|
||||||
|
"sprite_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/10092.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"national_dex": 10101,
|
||||||
|
"name": "Sandshrew (Alola)",
|
||||||
|
"types": [
|
||||||
|
"ice",
|
||||||
|
"steel"
|
||||||
|
],
|
||||||
|
"sprite_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/10101.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"national_dex": 10103,
|
||||||
|
"name": "Vulpix (Alola)",
|
||||||
|
"types": [
|
||||||
|
"ice"
|
||||||
|
],
|
||||||
|
"sprite_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/10103.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"national_dex": 10105,
|
||||||
|
"name": "Diglett (Alola)",
|
||||||
|
"types": [
|
||||||
|
"ground",
|
||||||
|
"steel"
|
||||||
|
],
|
||||||
|
"sprite_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/10105.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"national_dex": 10106,
|
||||||
|
"name": "Dugtrio (Alola)",
|
||||||
|
"types": [
|
||||||
|
"ground",
|
||||||
|
"steel"
|
||||||
|
],
|
||||||
|
"sprite_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/10106.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"national_dex": 10107,
|
||||||
|
"name": "Meowth (Alola)",
|
||||||
|
"types": [
|
||||||
|
"dark"
|
||||||
|
],
|
||||||
|
"sprite_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/10107.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"national_dex": 10109,
|
||||||
|
"name": "Geodude (Alola)",
|
||||||
|
"types": [
|
||||||
|
"rock",
|
||||||
|
"electric"
|
||||||
|
],
|
||||||
|
"sprite_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/10109.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"national_dex": 10110,
|
||||||
|
"name": "Graveler (Alola)",
|
||||||
|
"types": [
|
||||||
|
"rock",
|
||||||
|
"electric"
|
||||||
|
],
|
||||||
|
"sprite_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/10110.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"national_dex": 10112,
|
||||||
|
"name": "Grimer (Alola)",
|
||||||
|
"types": [
|
||||||
|
"poison",
|
||||||
|
"dark"
|
||||||
|
],
|
||||||
|
"sprite_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/10112.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"national_dex": 10114,
|
||||||
|
"name": "Exeggutor (Alola)",
|
||||||
|
"types": [
|
||||||
|
"grass",
|
||||||
|
"dragon"
|
||||||
|
],
|
||||||
|
"sprite_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/10114.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"national_dex": 10123,
|
||||||
|
"name": "Oricorio (Pom Pom)",
|
||||||
|
"types": [
|
||||||
|
"electric",
|
||||||
|
"flying"
|
||||||
|
],
|
||||||
|
"sprite_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/10123.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"national_dex": 10124,
|
||||||
|
"name": "Oricorio (Pau)",
|
||||||
|
"types": [
|
||||||
|
"psychic",
|
||||||
|
"flying"
|
||||||
|
],
|
||||||
|
"sprite_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/10124.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"national_dex": 10125,
|
||||||
|
"name": "Oricorio (Sensu)",
|
||||||
|
"types": [
|
||||||
|
"ghost",
|
||||||
|
"flying"
|
||||||
|
],
|
||||||
|
"sprite_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/10125.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"national_dex": 10126,
|
||||||
|
"name": "Lycanroc (Midnight)",
|
||||||
|
"types": [
|
||||||
|
"rock"
|
||||||
|
],
|
||||||
|
"sprite_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/10126.png"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -19,7 +19,8 @@ from app.seeds.special_encounters import SPECIAL_ENCOUNTERS
|
|||||||
|
|
||||||
REPO_ROOT = Path(__file__).parents[4] # backend/src/app/seeds -> repo root
|
REPO_ROOT = Path(__file__).parents[4] # backend/src/app/seeds -> repo root
|
||||||
POKEAPI_DIR = REPO_ROOT / "data" / "pokeapi" / "data" / "api" / "v2"
|
POKEAPI_DIR = REPO_ROOT / "data" / "pokeapi" / "data" / "api" / "v2"
|
||||||
DATA_DIR = Path(__file__).parent / "data"
|
SEEDS_DIR = Path(__file__).parent
|
||||||
|
DATA_DIR = SEEDS_DIR / "data"
|
||||||
|
|
||||||
|
|
||||||
def load_resource(endpoint: str, resource_id: int) -> dict:
|
def load_resource(endpoint: str, resource_id: int) -> dict:
|
||||||
@@ -704,6 +705,170 @@ ROUTE_ORDER: dict[str, list[str]] = {
|
|||||||
"Artisan Cave",
|
"Artisan Cave",
|
||||||
"Altering Cave",
|
"Altering Cave",
|
||||||
],
|
],
|
||||||
|
"platinum": [
|
||||||
|
"Canalave City",
|
||||||
|
"Pastoria City",
|
||||||
|
"Sunyshore City",
|
||||||
|
"Pokemon League",
|
||||||
|
"Oreburgh Mine",
|
||||||
|
"Oreburgh Mine (1F)",
|
||||||
|
"Oreburgh Mine (B1F)",
|
||||||
|
"Valley Windworks",
|
||||||
|
"Eterna Forest",
|
||||||
|
"Eterna City",
|
||||||
|
"Fuego Ironworks",
|
||||||
|
"Mt Coronet",
|
||||||
|
"Mt Coronet (1F Route 207)",
|
||||||
|
"Mt Coronet (2F)",
|
||||||
|
"Mt Coronet (3F)",
|
||||||
|
"Mt Coronet (Exterior Snowfall)",
|
||||||
|
"Mt Coronet (Exterior Blizzard)",
|
||||||
|
"Mt Coronet (4F)",
|
||||||
|
"Mt Coronet (4F Small Room)",
|
||||||
|
"Mt Coronet (5F)",
|
||||||
|
"Mt Coronet (6F)",
|
||||||
|
"Mt Coronet (1F From Exterior)",
|
||||||
|
"Mt Coronet (1F Route 216)",
|
||||||
|
"Mt Coronet (1F Route 211)",
|
||||||
|
"Mt Coronet (B1F)",
|
||||||
|
"Great Marsh",
|
||||||
|
"Great Marsh (Area 1)",
|
||||||
|
"Great Marsh (Area 2)",
|
||||||
|
"Great Marsh (Area 3)",
|
||||||
|
"Great Marsh (Area 4)",
|
||||||
|
"Great Marsh (Area 5)",
|
||||||
|
"Great Marsh (Area 6)",
|
||||||
|
"Solaceon Ruins",
|
||||||
|
"Solaceon Ruins (2F)",
|
||||||
|
"Solaceon Ruins (1F)",
|
||||||
|
"Solaceon Ruins (B1F A)",
|
||||||
|
"Solaceon Ruins (B1F B)",
|
||||||
|
"Solaceon Ruins (B1F C)",
|
||||||
|
"Solaceon Ruins (B2F A)",
|
||||||
|
"Solaceon Ruins (B2F B)",
|
||||||
|
"Solaceon Ruins (B2F C)",
|
||||||
|
"Solaceon Ruins (B3F A)",
|
||||||
|
"Solaceon Ruins (B3F B)",
|
||||||
|
"Solaceon Ruins (B3F C)",
|
||||||
|
"Solaceon Ruins (B3F D)",
|
||||||
|
"Solaceon Ruins (B3F E)",
|
||||||
|
"Solaceon Ruins (B4F A)",
|
||||||
|
"Solaceon Ruins (B4F B)",
|
||||||
|
"Solaceon Ruins (B4F C)",
|
||||||
|
"Solaceon Ruins (B4F D)",
|
||||||
|
"Solaceon Ruins (B5F)",
|
||||||
|
"Victory Road",
|
||||||
|
"Victory Road (1F)",
|
||||||
|
"Victory Road (2F)",
|
||||||
|
"Victory Road (B1F)",
|
||||||
|
"Victory Road (Inside B1F)",
|
||||||
|
"Victory Road (Inside)",
|
||||||
|
"Victory Road (Inside Exit)",
|
||||||
|
"Ravaged Path",
|
||||||
|
"Oreburgh Gate",
|
||||||
|
"Oreburgh Gate (1F)",
|
||||||
|
"Oreburgh Gate (B1F)",
|
||||||
|
"Stark Mountain (Entrance)",
|
||||||
|
"Stark Mountain (Inside)",
|
||||||
|
"Stark Mountain",
|
||||||
|
"Turnback Cave",
|
||||||
|
"Turnback Cave (Pillar 1)",
|
||||||
|
"Turnback Cave (Pillar 2)",
|
||||||
|
"Turnback Cave (Pillar 3)",
|
||||||
|
"Turnback Cave (Before Pillar 1)",
|
||||||
|
"Turnback Cave (Between Pillars 1 And 2)",
|
||||||
|
"Turnback Cave (Between Pillars 2 And 3)",
|
||||||
|
"Turnback Cave (After Pillar 3)",
|
||||||
|
"Snowpoint Temple",
|
||||||
|
"Snowpoint Temple (1F)",
|
||||||
|
"Snowpoint Temple (B1F)",
|
||||||
|
"Snowpoint Temple (B2F)",
|
||||||
|
"Snowpoint Temple (B3F)",
|
||||||
|
"Snowpoint Temple (B4F)",
|
||||||
|
"Snowpoint Temple (B5F)",
|
||||||
|
"Wayward Cave",
|
||||||
|
"Wayward Cave (1F)",
|
||||||
|
"Wayward Cave (B1F)",
|
||||||
|
"Ruin Maniac Cave",
|
||||||
|
"Ruin Maniac Cave (0 9 Different Unown Caught)",
|
||||||
|
"Ruin Maniac Cave (10 25 Different Unown Caught)",
|
||||||
|
"Trophy Garden",
|
||||||
|
"Iron Island (1F)",
|
||||||
|
"Iron Island (B1F Left)",
|
||||||
|
"Iron Island (B1F Right)",
|
||||||
|
"Iron Island (B2F Right)",
|
||||||
|
"Iron Island (B2F Left)",
|
||||||
|
"Iron Island (B3F)",
|
||||||
|
"Iron Island",
|
||||||
|
"Old Chateau",
|
||||||
|
"Old Chateau (Entrance)",
|
||||||
|
"Old Chateau (Dining Room)",
|
||||||
|
"Old Chateau (2F Private Room)",
|
||||||
|
"Old Chateau (2F)",
|
||||||
|
"Old Chateau (2F Leftmost Room)",
|
||||||
|
"Old Chateau (2F Left Room)",
|
||||||
|
"Old Chateau (2F Middle Room)",
|
||||||
|
"Old Chateau (2F Right Room)",
|
||||||
|
"Old Chateau (2F Rightmost Room)",
|
||||||
|
"Lake Verity",
|
||||||
|
"Lake Verity (Before Galactic Intervention)",
|
||||||
|
"Lake Verity (After Galactic Intervention)",
|
||||||
|
"Lake Valor",
|
||||||
|
"Lake Acuity",
|
||||||
|
"Valor Lakefront",
|
||||||
|
"Acuity Lakefront",
|
||||||
|
"Route 201",
|
||||||
|
"Route 202",
|
||||||
|
"Route 203",
|
||||||
|
"Route 204",
|
||||||
|
"Route 204 (South Towards Jubilife City)",
|
||||||
|
"Route 204 (North Towards Floaroma Town)",
|
||||||
|
"Route 205",
|
||||||
|
"Route 205 (South Towards Floaroma Town)",
|
||||||
|
"Route 205 (East Towards Eterna City)",
|
||||||
|
"Route 206",
|
||||||
|
"Route 207",
|
||||||
|
"Route 208",
|
||||||
|
"Route 209",
|
||||||
|
"Lost Tower",
|
||||||
|
"Lost Tower (1F)",
|
||||||
|
"Lost Tower (2F)",
|
||||||
|
"Lost Tower (3F)",
|
||||||
|
"Lost Tower (4F)",
|
||||||
|
"Lost Tower (5F)",
|
||||||
|
"Route 210",
|
||||||
|
"Route 210 (South Towards Solaceon Town)",
|
||||||
|
"Route 210 (West Towards Celestic Town)",
|
||||||
|
"Route 211",
|
||||||
|
"Route 211 (West Towards Eterna City)",
|
||||||
|
"Route 211 (East Towards Celestic Town)",
|
||||||
|
"Route 212",
|
||||||
|
"Route 212 (North Towards Hearthome City)",
|
||||||
|
"Route 212 (East Towards Pastoria City)",
|
||||||
|
"Route 213",
|
||||||
|
"Route 214",
|
||||||
|
"Route 215",
|
||||||
|
"Route 216",
|
||||||
|
"Route 217",
|
||||||
|
"Route 218",
|
||||||
|
"Route 219",
|
||||||
|
"Route 221",
|
||||||
|
"Route 222",
|
||||||
|
"Route 224",
|
||||||
|
"Route 225",
|
||||||
|
"Route 227",
|
||||||
|
"Route 228",
|
||||||
|
"Route 229",
|
||||||
|
"Twinleaf Town",
|
||||||
|
"Celestic Town",
|
||||||
|
"Resort Area",
|
||||||
|
"Sea Route 220",
|
||||||
|
"Sea Route 223",
|
||||||
|
"Sea Route 226",
|
||||||
|
"Sea Route 230",
|
||||||
|
"Sendoff Spring",
|
||||||
|
"Maniac Tunnel"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
# Aliases — version groups sharing same route progression
|
# Aliases — version groups sharing same route progression
|
||||||
@@ -975,22 +1140,42 @@ def process_version(version_name: str, vg_info: dict, vg_key: str) -> list[dict]
|
|||||||
return routes
|
return routes
|
||||||
|
|
||||||
|
|
||||||
|
def format_form_name(poke_data: dict) -> str:
|
||||||
|
"""Convert a PokeAPI pokemon form entry to a clean display name.
|
||||||
|
|
||||||
|
e.g. 'rattata-alola' (species: 'rattata') -> 'Rattata (Alola)'
|
||||||
|
"""
|
||||||
|
species_name = poke_data["species"]["name"]
|
||||||
|
full_name = poke_data["name"]
|
||||||
|
if full_name.startswith(species_name + "-"):
|
||||||
|
form_suffix = full_name[len(species_name) + 1:]
|
||||||
|
base = species_name.title().replace("-", " ")
|
||||||
|
suffix = form_suffix.title().replace("-", " ")
|
||||||
|
return f"{base} ({suffix})"
|
||||||
|
return full_name.title().replace("-", " ")
|
||||||
|
|
||||||
|
|
||||||
def fetch_all_pokemon() -> list[dict]:
|
def fetch_all_pokemon() -> list[dict]:
|
||||||
"""Fetch all Pokemon species from the local PokeAPI data."""
|
"""Fetch all Pokemon species + encountered forms from the local PokeAPI data."""
|
||||||
pokemon_dir = POKEAPI_DIR / "pokemon-species"
|
pokemon_dir = POKEAPI_DIR / "pokemon-species"
|
||||||
|
|
||||||
# Get all species IDs (directories with numeric names, excluding forms 10000+)
|
# Get all base species IDs
|
||||||
all_species = []
|
all_species = []
|
||||||
for entry in pokemon_dir.iterdir():
|
for entry in pokemon_dir.iterdir():
|
||||||
if entry.is_dir() and entry.name.isdigit():
|
if entry.is_dir() and entry.name.isdigit():
|
||||||
dex = int(entry.name)
|
dex = int(entry.name)
|
||||||
if dex < 10000: # Exclude alternate forms
|
if dex < 10000:
|
||||||
all_species.append(dex)
|
all_species.append(dex)
|
||||||
|
|
||||||
|
# Also include form IDs that appear in encounter data
|
||||||
|
form_ids = sorted(d for d in all_pokemon_dex if d >= 10000)
|
||||||
|
|
||||||
all_species.sort()
|
all_species.sort()
|
||||||
print(f"\n--- Fetching {len(all_species)} Pokemon species ---")
|
print(f"\n--- Fetching {len(all_species)} Pokemon species + {len(form_ids)} forms ---")
|
||||||
|
|
||||||
pokemon_list = []
|
pokemon_list = []
|
||||||
|
|
||||||
|
# Fetch base species
|
||||||
for i, dex in enumerate(all_species, 1):
|
for i, dex in enumerate(all_species, 1):
|
||||||
poke = load_resource("pokemon", dex)
|
poke = load_resource("pokemon", dex)
|
||||||
types = [t["type"]["name"] for t in poke["types"]]
|
types = [t["type"]["name"] for t in poke["types"]]
|
||||||
@@ -1000,9 +1185,22 @@ def fetch_all_pokemon() -> list[dict]:
|
|||||||
"types": types,
|
"types": types,
|
||||||
"sprite_url": f"https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/{dex}.png",
|
"sprite_url": f"https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/{dex}.png",
|
||||||
})
|
})
|
||||||
|
|
||||||
if i % 100 == 0 or i == len(all_species):
|
if i % 100 == 0 or i == len(all_species):
|
||||||
print(f" Fetched {i}/{len(all_species)}")
|
print(f" Fetched {i}/{len(all_species)} species")
|
||||||
|
|
||||||
|
# Fetch forms (from pokemon endpoint, with form-aware naming)
|
||||||
|
for form_dex in form_ids:
|
||||||
|
poke = load_resource("pokemon", form_dex)
|
||||||
|
types = [t["type"]["name"] for t in poke["types"]]
|
||||||
|
pokemon_list.append({
|
||||||
|
"national_dex": form_dex,
|
||||||
|
"name": format_form_name(poke),
|
||||||
|
"types": types,
|
||||||
|
"sprite_url": f"https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/{form_dex}.png",
|
||||||
|
})
|
||||||
|
|
||||||
|
if form_ids:
|
||||||
|
print(f" Fetched {len(form_ids)} forms")
|
||||||
|
|
||||||
return sorted(pokemon_list, key=lambda x: x["national_dex"])
|
return sorted(pokemon_list, key=lambda x: x["national_dex"])
|
||||||
|
|
||||||
@@ -1081,8 +1279,9 @@ def fetch_evolution_data(seeded_dex: set[int]) -> list[dict]:
|
|||||||
print(f"\n--- Fetching evolution chains ---")
|
print(f"\n--- Fetching evolution chains ---")
|
||||||
|
|
||||||
# First, get the evolution chain URL for each pokemon species
|
# First, get the evolution chain URL for each pokemon species
|
||||||
|
# Skip form IDs (>= 10000) — they don't have pokemon-species entries
|
||||||
chain_ids: set[int] = set()
|
chain_ids: set[int] = set()
|
||||||
dex_sorted = sorted(seeded_dex)
|
dex_sorted = sorted(d for d in seeded_dex if d < 10000)
|
||||||
|
|
||||||
for i, dex in enumerate(dex_sorted, 1):
|
for i, dex in enumerate(dex_sorted, 1):
|
||||||
species = load_resource("pokemon-species", dex)
|
species = load_resource("pokemon-species", dex)
|
||||||
|
|||||||
Reference in New Issue
Block a user