Add per-condition encounter rates to seed data
Some checks failed
CI / backend-lint (pull_request) Successful in 9s
CI / actions-lint (pull_request) Failing after 6s
CI / frontend-lint (pull_request) Successful in 21s

Add a `condition` column to RouteEncounter so encounters can store
per-condition rates (time of day, season, weather) instead of flattening
to max(). Update the seed loader, API schemas, and frontend to support
the new `conditions` dict format in seed JSON.

Port the PoC branch's condition-aware EncounterModal UI with filter
tabs that let players see encounter rates for specific conditions.
Add horde/SOS as distinct encounter methods with their own badges.

Update the import tool to extract per-condition rates instead of
flattening, and add a merge script (tools/merge-conditions.py) that
enriches existing curated seed files with condition data from PokeDB.

Seed data updated for 22 games (5,684 encounters):
- Gen 2: Gold, Silver, Crystal (morning/day/night)
- Gen 4: HG, SS, Diamond, Pearl, Platinum, BD, SP (morning/day/night)
- Gen 5: Black, White, Black 2, White 2 (spring/summer/autumn/winter)
- Gen 7: Sun, Moon, Ultra Sun, Ultra Moon (day/night)
- Gen 8: Sword, Shield (weather)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-17 18:52:20 +01:00
parent d0fff248fe
commit 5240236759
36 changed files with 36715 additions and 11587 deletions

View File

@@ -1,11 +1,11 @@
---
# nuzlocke-tracker-4ni4
title: Fix seed data with encounter conditions
status: todo
status: completed
type: task
priority: high
created_at: 2026-02-17T07:37:25Z
updated_at: 2026-02-17T07:37:28Z
updated_at: 2026-02-17T17:52:29Z
parent: oqfo
---
@@ -15,94 +15,30 @@ Some Pokémon games have different encounter tables depending on time of day, we
The existing seed data has curated route ordering and normalized route names (from beans r48e, qvww, j28y) that must be preserved — only encounter condition data should be added/changed.
## Reference branch
**Branch:** \`feature/encounter-conditions\` (PoC)
Key changes on the PoC branch:
- **Backend model:** \`RouteEncounter\` gains a \`condition\` field (String(30), default \`""\`)
- **Migration:** \`c0d1e2f3a4_add_condition_to_route_encounters.py\` — adds condition column + updated unique constraint
- **Seed loader:** handles \`conditions\` dict format: \`{"morning": 50, "day": 20, "night": 0}\` per encounter
- **API/schema:** \`condition\` field exposed in route encounter responses
- **Frontend types:** \`RouteEncounter\` type gains \`condition: string\`
- **Frontend UI:** condition selector tabs and badges in \`RunEncounters\` and \`EncounterModal\`
## Seed data format
When an encounter has per-condition rates, the JSON uses a \`conditions\` dict instead of a flat \`encounter_rate\`:
\`\`\`json
{
"pokeapi_id": 163,
"pokemon_name": "Hoothoot",
"method": "walk",
"encounter_rate": null,
"conditions": {
"night": 50,
"morning": 10,
"day": 0
},
"min_level": 2,
"max_level": 5
}
\`\`\`
For encounters without variant rates, the existing flat \`encounter_rate\` field remains unchanged.
## Approach
### Phase 1: HeartGold (reference game)
Create complete encounter condition data for HeartGold first. This serves as the reference implementation and validates the full pipeline (seed → DB → API → UI).
HeartGold uses **morning/day/night** conditions for walking encounters. Source: PokeDB data (the import tool at \`tools/import-pokedb\` already has the raw per-condition rates, but \`extract_encounter_rate()\` currently flattens them to \`max()\`).
### Phase 2: All other games with conditions
Update the remaining games' seed data with encounter conditions, without changing route order or route names. Match encounters by route name + Pokémon + method and add the \`conditions\` dict.
## Condition types by game group
- **morning/day/night**: Gold, Silver, Crystal, HeartGold, SoulSilver, Diamond, Pearl, Platinum, Brilliant Diamond, Shining Pearl
- **spring/summer/autumn/winter**: Black, White, Black 2, White 2
- **weather (clear, overcast, rain, thunderstorm, snow, snowstorm, sandstorm, intense-sun, heavy-rain, fog)**: Sword, Shield
- **SOS calls**: Sun, Moon, Ultra Sun, Ultra Moon
- **No conditions (flat rates)**: Red, Blue, Yellow, Ruby, Sapphire, Emerald, FireRed, LeafGreen, X, Y, Omega Ruby, Alpha Sapphire, Let's Go Pikachu, Let's Go Eevee, Legends: Arceus, Scarlet, Violet, Legends: Z-A
## Checklist
### Infrastructure (merge from PoC)
- [ ] Merge backend model + migration for \`condition\` field on \`RouteEncounter\`
- [ ] Merge seed loader changes to handle \`conditions\` dict format
- [ ] Merge API/schema changes to expose \`condition\` field
- [ ] Merge frontend type updates (\`RouteEncounter.condition\`)
- [ ] Merge frontend UI (condition selector tabs/badges in RunEncounters & EncounterModal)
- [x] Merge backend model + migration for `condition` field on `RouteEncounter`
- [x] Merge seed loader changes to handle `conditions` dict format
- [x] Merge API/schema changes to expose `condition` field
- [x] Merge frontend type updates (`RouteEncounter.condition`)
- [x] Merge frontend UI (condition selector tabs/badges in EncounterModal)
- [x] Add horde/SOS method badges to EncounterMethodBadge
- [x] Add condition column to AdminRouteDetail
### Phase 1: HeartGold
- [ ] Update \`tools/import-pokedb\` to extract per-condition rates instead of flattening to \`max()\`
- [ ] Write a merge script that adds condition data to existing seed files without touching route names/order
- [ ] Generate and merge condition data for HeartGold
- [ ] Verify HeartGold seed data loads correctly and conditions display in the frontend
### Import tool updates
- [x] Update `tools/import-pokedb` to extract per-condition rates instead of flattening to `max()`
- [x] Fix encounter method mappings (horde, SOS as distinct methods)
- [x] Write merge script (`tools/merge-conditions.py`)
### Phase 2: Remaining games
- [ ] Gen 2: Gold, Silver, Crystal (morning/day/night)
- [ ] Gen 4: SoulSilver, Diamond, Pearl, Platinum, Brilliant Diamond, Shining Pearl (morning/day/night)
- [ ] Gen 5: Black, White, Black 2, White 2 (spring/summer/autumn/winter)
- [ ] Gen 7: Sun, Moon, Ultra Sun, Ultra Moon (SOS calls)
- [ ] Gen 8: Sword, Shield (weather conditions)
- [ ] Verify all updated games load correctly and show conditions in the UI
### Seed data updates
- [x] Gen 2: Gold, Silver, Crystal (morning/day/night)
- [x] Gen 4: HeartGold, SoulSilver, Diamond, Pearl, Platinum, Brilliant Diamond, Shining Pearl (morning/day/night)
- [x] Gen 5: Black, White, Black 2, White 2 (spring/summer/autumn/winter)
- [x] Gen 7: Sun, Moon, Ultra Sun, Ultra Moon (day/night)
- [x] Gen 8: Sword, Shield (weather conditions)
- [x] Verify all hooks pass (`prek run --all-files`)
## Success criteria
All games that have condition-dependent encounters show those conditions in the UI, so players can see what they can actually catch given their current game state (time of day, season, weather, etc.).
## Key files
- \`backend/src/app/models/route_encounter.py\` — RouteEncounter model
- \`backend/src/app/seeds/loader.py\` — seed loading logic
- \`backend/src/app/seeds/data/*.json\` — game encounter seed files
- \`tools/import-pokedb/import_pokedb/processing.py\` — \`extract_encounter_rate()\` flattens conditions
- \`tools/import-pokedb/import_pokedb/models.py\` — Encounter dataclass
- \`frontend/src/types/game.ts\` — RouteEncounter type
- \`frontend/src/pages/RunEncounters.tsx\` — encounter display with conditions
- \`frontend/src/components/EncounterModal.tsx\` — encounter registration with condition context
## Notes
- X/Y had no condition data in PokeDB (horde encounters already tracked as separate method)
- 5,684 encounters updated across 22 games