From 758750b7b86c4f7683880169368169b1ca820ee4 Mon Sep 17 00:00:00 2001 From: Julian Tabel Date: Sun, 8 Feb 2026 15:51:23 +0100 Subject: [PATCH] Add after_route_name to boss battle export/seed pipeline Exports now include after_route_name (resolved from the route FK), and the seed loader resolves it back to an ID on import. Also adds a draft bean for displaying encounter-less locations. Co-Authored-By: Claude Opus 4.6 --- ...counter-less-locations-for-egg-hatching.md | 19 +++++++++++++++++++ backend/src/app/api/export.py | 2 ++ backend/src/app/seeds/loader.py | 11 +++++++++++ backend/src/app/seeds/run.py | 7 ++++++- 4 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 .beans/nuzlocke-tracker-xa5k--display-encounter-less-locations-for-egg-hatching.md diff --git a/.beans/nuzlocke-tracker-xa5k--display-encounter-less-locations-for-egg-hatching.md b/.beans/nuzlocke-tracker-xa5k--display-encounter-less-locations-for-egg-hatching.md new file mode 100644 index 0000000..f3ca553 --- /dev/null +++ b/.beans/nuzlocke-tracker-xa5k--display-encounter-less-locations-for-egg-hatching.md @@ -0,0 +1,19 @@ +--- +# nuzlocke-tracker-xa5k +title: Display encounter-less locations for egg hatching +status: draft +type: feature +created_at: 2026-02-08T14:49:50Z +updated_at: 2026-02-08T14:49:50Z +--- + +Some routes/locations don't have wild encounters but are still relevant for gameplay — particularly for hatching eggs. Currently these locations are hidden or not useful in the run view since they have no encounters to log. + +Add support for displaying locations that have no encounters in the run view, so players can track egg hatches or other location-based events there. + +## Checklist + +- [ ] Determine how encounter-less routes should appear in the run view (e.g. different visual treatment, no encounter status dot) +- [ ] Update route filtering logic to include routes without encounters when relevant +- [ ] Add ability to log an egg hatch at a location (new encounter type or dedicated UI) +- [ ] Consider whether these locations need an admin-side flag (e.g. `show_without_encounters`) or if all routes should always be visible \ No newline at end of file diff --git a/backend/src/app/api/export.py b/backend/src/app/api/export.py index 6d3d01d..6fff59a 100644 --- a/backend/src/app/api/export.py +++ b/backend/src/app/api/export.py @@ -127,6 +127,7 @@ async def export_game_bosses( .where(BossBattle.version_group_id == game.version_group_id) .options( selectinload(BossBattle.pokemon).selectinload(BossPokemon.pokemon), + selectinload(BossBattle.after_route), ) .order_by(BossBattle.order) ) @@ -143,6 +144,7 @@ async def export_game_bosses( "badge_image_url": b.badge_image_url, "level_cap": b.level_cap, "order": b.order, + "after_route_name": b.after_route.name if b.after_route else None, "location": b.location, "section": b.section, "sprite_url": b.sprite_url, diff --git a/backend/src/app/seeds/loader.py b/backend/src/app/seeds/loader.py index e2a288b..04d4f31 100644 --- a/backend/src/app/seeds/loader.py +++ b/backend/src/app/seeds/loader.py @@ -211,10 +211,19 @@ async def upsert_bosses( version_group_id: int, bosses: list[dict], dex_to_id: dict[int, int], + route_name_to_id: dict[str, int] | None = None, ) -> int: """Upsert boss battles for a version group, return count of bosses upserted.""" count = 0 for boss in bosses: + # Resolve after_route_name to an ID + after_route_id = None + after_route_name = boss.get("after_route_name") + if after_route_name and route_name_to_id: + after_route_id = route_name_to_id.get(after_route_name) + if after_route_id is None: + print(f" Warning: route '{after_route_name}' not found for boss '{boss['name']}'") + # Upsert the boss battle on (version_group_id, order) conflict stmt = insert(BossBattle).values( version_group_id=version_group_id, @@ -225,6 +234,7 @@ async def upsert_bosses( badge_image_url=boss.get("badge_image_url"), level_cap=boss["level_cap"], order=boss["order"], + after_route_id=after_route_id, location=boss["location"], section=boss.get("section"), sprite_url=boss.get("sprite_url"), @@ -237,6 +247,7 @@ async def upsert_bosses( "badge_name": boss.get("badge_name"), "badge_image_url": boss.get("badge_image_url"), "level_cap": boss["level_cap"], + "after_route_id": after_route_id, "location": boss["location"], "section": boss.get("section"), "sprite_url": boss.get("sprite_url"), diff --git a/backend/src/app/seeds/run.py b/backend/src/app/seeds/run.py index a742e32..0d7750d 100644 --- a/backend/src/app/seeds/run.py +++ b/backend/src/app/seeds/run.py @@ -68,6 +68,7 @@ async def seed(): # 4. Per version group: upsert routes once, then encounters per game total_routes = 0 total_encounters = 0 + route_maps_by_vg: dict[int, dict[str, int]] = {} for vg_slug, vg_info in vg_data.items(): vg_id = vg_slug_to_id[vg_slug] @@ -87,6 +88,7 @@ async def seed(): # Upsert routes once per version group route_map = await upsert_routes(session, vg_id, routes_data) + route_maps_by_vg[vg_id] = route_map total_routes += len(route_map) print(f" {vg_slug}: {len(route_map)} routes") @@ -147,7 +149,8 @@ async def seed(): if not bosses_data: continue - boss_count = await upsert_bosses(session, vg_id, bosses_data, dex_to_id) + route_name_to_id = route_maps_by_vg.get(vg_id, {}) + boss_count = await upsert_bosses(session, vg_id, bosses_data, dex_to_id, route_name_to_id) total_bosses += boss_count print(f" {vg_slug}: {boss_count} bosses") @@ -416,6 +419,7 @@ async def _export_bosses(session: AsyncSession, vg_data: dict): .where(BossBattle.version_group_id == vg.id) .options( selectinload(BossBattle.pokemon).selectinload(BossPokemon.pokemon), + selectinload(BossBattle.after_route), ) .order_by(BossBattle.order) ) @@ -434,6 +438,7 @@ async def _export_bosses(session: AsyncSession, vg_data: dict): "badge_image_url": b.badge_image_url, "level_cap": b.level_cap, "order": b.order, + "after_route_name": b.after_route.name if b.after_route else None, "location": b.location, "section": b.section, "sprite_url": b.sprite_url,