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 # nuzlocke-tracker-4ni4
title: Fix seed data with encounter conditions title: Fix seed data with encounter conditions
status: todo status: completed
type: task type: task
priority: high priority: high
created_at: 2026-02-17T07:37:25Z created_at: 2026-02-17T07:37:25Z
updated_at: 2026-02-17T07:37:28Z updated_at: 2026-02-17T17:52:29Z
parent: oqfo 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. 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 ## Checklist
### Infrastructure (merge from PoC) ### Infrastructure (merge from PoC)
- [ ] Merge backend model + migration for \`condition\` field on \`RouteEncounter\` - [x] Merge backend model + migration for `condition` field on `RouteEncounter`
- [ ] Merge seed loader changes to handle \`conditions\` dict format - [x] Merge seed loader changes to handle `conditions` dict format
- [ ] Merge API/schema changes to expose \`condition\` field - [x] Merge API/schema changes to expose `condition` field
- [ ] Merge frontend type updates (\`RouteEncounter.condition\`) - [x] Merge frontend type updates (`RouteEncounter.condition`)
- [ ] Merge frontend UI (condition selector tabs/badges in RunEncounters & EncounterModal) - [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 ### Import tool updates
- [ ] Update \`tools/import-pokedb\` to extract per-condition rates instead of flattening to \`max()\` - [x] 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 - [x] Fix encounter method mappings (horde, SOS as distinct methods)
- [ ] Generate and merge condition data for HeartGold - [x] Write merge script (`tools/merge-conditions.py`)
- [ ] Verify HeartGold seed data loads correctly and conditions display in the frontend
### Phase 2: Remaining games ### Seed data updates
- [ ] Gen 2: Gold, Silver, Crystal (morning/day/night) - [x] Gen 2: Gold, Silver, Crystal (morning/day/night)
- [ ] Gen 4: SoulSilver, Diamond, Pearl, Platinum, Brilliant Diamond, Shining Pearl (morning/day/night) - [x] Gen 4: HeartGold, SoulSilver, Diamond, Pearl, Platinum, Brilliant Diamond, Shining Pearl (morning/day/night)
- [ ] Gen 5: Black, White, Black 2, White 2 (spring/summer/autumn/winter) - [x] Gen 5: Black, White, Black 2, White 2 (spring/summer/autumn/winter)
- [ ] Gen 7: Sun, Moon, Ultra Sun, Ultra Moon (SOS calls) - [x] Gen 7: Sun, Moon, Ultra Sun, Ultra Moon (day/night)
- [ ] Gen 8: Sword, Shield (weather conditions) - [x] Gen 8: Sword, Shield (weather conditions)
- [ ] Verify all updated games load correctly and show conditions in the UI - [x] Verify all hooks pass (`prek run --all-files`)
## Success criteria ## Notes
- X/Y had no condition data in PokeDB (horde encounters already tracked as separate method)
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.). - 5,684 encounters updated across 22 games
## 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

View File

@@ -0,0 +1,54 @@
"""add condition to route encounters
Revision ID: h9c0d1e2f3a4
Revises: g8b9c0d1e2f3
Create Date: 2026-02-17 12:00:00.000000
"""
from collections.abc import Sequence
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "h9c0d1e2f3a4"
down_revision: str | Sequence[str] | None = "g8b9c0d1e2f3"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
op.add_column(
"route_encounters",
sa.Column(
"condition",
sa.String(30),
nullable=False,
server_default="",
),
)
op.drop_constraint(
"uq_route_pokemon_method_game",
"route_encounters",
type_="unique",
)
op.create_unique_constraint(
"uq_route_pokemon_method_game_condition",
"route_encounters",
["route_id", "pokemon_id", "encounter_method", "game_id", "condition"],
)
def downgrade() -> None:
op.drop_constraint(
"uq_route_pokemon_method_game_condition",
"route_encounters",
type_="unique",
)
op.create_unique_constraint(
"uq_route_pokemon_method_game",
"route_encounters",
["route_id", "pokemon_id", "encounter_method", "game_id"],
)
op.drop_column("route_encounters", "condition")

View File

@@ -69,8 +69,9 @@ async def export_game_routes(
game_encounters = [ game_encounters = [
enc for enc in route.route_encounters if enc.game_id == game_id enc for enc in route.route_encounters if enc.game_id == game_id
] ]
return [ result = []
{ for enc in sorted(game_encounters, key=lambda e: -e.encounter_rate):
entry: dict = {
"pokeapi_id": enc.pokemon.pokeapi_id, "pokeapi_id": enc.pokemon.pokeapi_id,
"pokemon_name": enc.pokemon.name, "pokemon_name": enc.pokemon.name,
"method": enc.encounter_method, "method": enc.encounter_method,
@@ -78,8 +79,10 @@ async def export_game_routes(
"min_level": enc.min_level, "min_level": enc.min_level,
"max_level": enc.max_level, "max_level": enc.max_level,
} }
for enc in sorted(game_encounters, key=lambda e: -e.encounter_rate) if enc.condition:
] entry["condition"] = enc.condition
result.append(entry)
return result
def format_route(route: Route) -> dict: def format_route(route: Route) -> dict:
data: dict = { data: dict = {

View File

@@ -213,6 +213,7 @@ async def get_pokemon_encounter_locations(
route_name=enc.route.name, route_name=enc.route.name,
encounter_method=enc.encounter_method, encounter_method=enc.encounter_method,
encounter_rate=enc.encounter_rate, encounter_rate=enc.encounter_rate,
condition=enc.condition,
min_level=enc.min_level, min_level=enc.min_level,
max_level=enc.max_level, max_level=enc.max_level,
) )

View File

@@ -12,7 +12,8 @@ class RouteEncounter(Base):
"pokemon_id", "pokemon_id",
"encounter_method", "encounter_method",
"game_id", "game_id",
name="uq_route_pokemon_method_game", "condition",
name="uq_route_pokemon_method_game_condition",
), ),
) )
@@ -22,6 +23,7 @@ class RouteEncounter(Base):
game_id: Mapped[int] = mapped_column(ForeignKey("games.id"), index=True) game_id: Mapped[int] = mapped_column(ForeignKey("games.id"), index=True)
encounter_method: Mapped[str] = mapped_column(String(30)) encounter_method: Mapped[str] = mapped_column(String(30))
encounter_rate: Mapped[int] = mapped_column(SmallInteger) encounter_rate: Mapped[int] = mapped_column(SmallInteger)
condition: Mapped[str] = mapped_column(String(30), default="", server_default="")
min_level: Mapped[int] = mapped_column(SmallInteger) min_level: Mapped[int] = mapped_column(SmallInteger)
max_level: Mapped[int] = mapped_column(SmallInteger) max_level: Mapped[int] = mapped_column(SmallInteger)

View File

@@ -42,6 +42,7 @@ class RouteEncounterResponse(CamelModel):
game_id: int game_id: int
encounter_method: str encounter_method: str
encounter_rate: int encounter_rate: int
condition: str = ""
min_level: int min_level: int
max_level: int max_level: int
@@ -55,6 +56,7 @@ class PokemonEncounterLocationItem(CamelModel):
route_name: str route_name: str
encounter_method: str encounter_method: str
encounter_rate: int encounter_rate: int
condition: str = ""
min_level: int min_level: int
max_level: int max_level: int
@@ -89,6 +91,7 @@ class RouteEncounterCreate(CamelModel):
game_id: int game_id: int
encounter_method: str encounter_method: str
encounter_rate: int encounter_rate: int
condition: str = ""
min_level: int min_level: int
max_level: int max_level: int
@@ -96,6 +99,7 @@ class RouteEncounterCreate(CamelModel):
class RouteEncounterUpdate(CamelModel): class RouteEncounterUpdate(CamelModel):
encounter_method: str | None = None encounter_method: str | None = None
encounter_rate: int | None = None encounter_rate: int | None = None
condition: str | None = None
min_level: int | None = None min_level: int | None = None
max_level: int | None = None max_level: int | None = None
@@ -178,6 +182,7 @@ class BulkRouteEncounterItem(BaseModel):
pokeapi_id: int pokeapi_id: int
method: str method: str
encounter_rate: int encounter_rate: int
condition: str = ""
min_level: int min_level: int
max_level: int max_level: int

View File

@@ -844,17 +844,25 @@
"pokeapi_id": 88, "pokeapi_id": 88,
"pokemon_name": "Grimer", "pokemon_name": "Grimer",
"method": "surf", "method": "surf",
"encounter_rate": 100, "encounter_rate": null,
"min_level": 5, "min_level": 5,
"max_level": 20 "max_level": 20,
"conditions": {
"spring": 100,
"summer": 100
}
}, },
{ {
"pokeapi_id": 88, "pokeapi_id": 88,
"pokemon_name": "Grimer", "pokemon_name": "Grimer",
"method": "fishing", "method": "fishing",
"encounter_rate": 100, "encounter_rate": null,
"min_level": 40, "min_level": 40,
"max_level": 70 "max_level": 70,
"conditions": {
"spring": 100,
"summer": 100
}
}, },
{ {
"pokeapi_id": 19, "pokeapi_id": 19,
@@ -884,17 +892,25 @@
"pokeapi_id": 89, "pokeapi_id": 89,
"pokemon_name": "Muk", "pokemon_name": "Muk",
"method": "fishing", "method": "fishing",
"encounter_rate": 10, "encounter_rate": null,
"min_level": 50, "min_level": 50,
"max_level": 70 "max_level": 70,
"conditions": {
"spring": 10,
"summer": 10
}
}, },
{ {
"pokeapi_id": 89, "pokeapi_id": 89,
"pokemon_name": "Muk", "pokemon_name": "Muk",
"method": "surf", "method": "surf",
"encounter_rate": 5, "encounter_rate": null,
"min_level": 5, "min_level": 5,
"max_level": 20 "max_level": 20,
"conditions": {
"spring": 5,
"summer": 5
}
} }
] ]
}, },
@@ -3294,41 +3310,63 @@
"pokeapi_id": 593, "pokeapi_id": 593,
"pokemon_name": "Jellicent", "pokemon_name": "Jellicent",
"method": "surf", "method": "surf",
"encounter_rate": 60, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 40 "max_level": 40,
"conditions": {
"spring": 60,
"summer": 60,
"autumn": 60
}
}, },
{ {
"pokeapi_id": 320, "pokeapi_id": 320,
"pokemon_name": "Wailmer", "pokemon_name": "Wailmer",
"method": "surf", "method": "surf",
"encounter_rate": 60, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 40 "max_level": 40,
"conditions": {
"spring": 30,
"summer": 30,
"autumn": 30,
"winter": 60
}
}, },
{ {
"pokeapi_id": 458, "pokeapi_id": 458,
"pokemon_name": "Mantyke", "pokemon_name": "Mantyke",
"method": "surf", "method": "surf",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 40 "max_level": 40,
"conditions": {
"spring": 30,
"summer": 30,
"autumn": 30
}
}, },
{ {
"pokeapi_id": 364, "pokeapi_id": 364,
"pokemon_name": "Sealeo", "pokemon_name": "Sealeo",
"method": "surf", "method": "surf",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 40 "max_level": 40,
"conditions": {
"winter": 30
}
}, },
{ {
"pokeapi_id": 363, "pokeapi_id": 363,
"pokemon_name": "Spheal", "pokemon_name": "Spheal",
"method": "surf", "method": "surf",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 40 "max_level": 40,
"conditions": {
"winter": 30
}
}, },
{ {
"pokeapi_id": 171, "pokeapi_id": 171,
@@ -3342,9 +3380,14 @@
"pokeapi_id": 226, "pokeapi_id": 226,
"pokemon_name": "Mantine", "pokemon_name": "Mantine",
"method": "surf", "method": "surf",
"encounter_rate": 5, "encounter_rate": null,
"min_level": 30, "min_level": 30,
"max_level": 40 "max_level": 40,
"conditions": {
"spring": 5,
"summer": 5,
"autumn": 5
}
}, },
{ {
"pokeapi_id": 224, "pokeapi_id": 224,
@@ -3374,9 +3417,12 @@
"pokeapi_id": 365, "pokeapi_id": 365,
"pokemon_name": "Walrein", "pokemon_name": "Walrein",
"method": "surf", "method": "surf",
"encounter_rate": 5, "encounter_rate": null,
"min_level": 30, "min_level": 30,
"max_level": 40 "max_level": 40,
"conditions": {
"winter": 5
}
} }
] ]
}, },
@@ -7642,33 +7688,53 @@
"pokeapi_id": 536, "pokeapi_id": 536,
"pokemon_name": "Palpitoad", "pokemon_name": "Palpitoad",
"method": "walk", "method": "walk",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"spring": 40,
"summer": 40,
"autumn": 40
}
}, },
{ {
"pokeapi_id": 616, "pokeapi_id": 616,
"pokemon_name": "Shelmet", "pokemon_name": "Shelmet",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 54 "max_level": 54,
"conditions": {
"spring": 20,
"summer": 20,
"autumn": 20
}
}, },
{ {
"pokeapi_id": 618, "pokeapi_id": 618,
"pokemon_name": "Stunfisk", "pokemon_name": "Stunfisk",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 55, "min_level": 55,
"max_level": 56 "max_level": 56,
"conditions": {
"spring": 20,
"summer": 20,
"autumn": 20
}
}, },
{ {
"pokeapi_id": 453, "pokeapi_id": 453,
"pokemon_name": "Croagunk", "pokemon_name": "Croagunk",
"method": "walk", "method": "walk",
"encounter_rate": 15, "encounter_rate": null,
"min_level": 55, "min_level": 55,
"max_level": 56 "max_level": 56,
"conditions": {
"spring": 15,
"summer": 15,
"autumn": 15
}
}, },
{ {
"pokeapi_id": 340, "pokeapi_id": 340,
@@ -7682,9 +7748,14 @@
"pokeapi_id": 588, "pokeapi_id": 588,
"pokemon_name": "Karrablast", "pokemon_name": "Karrablast",
"method": "walk", "method": "walk",
"encounter_rate": 5, "encounter_rate": null,
"min_level": 57, "min_level": 57,
"max_level": 57 "max_level": 57,
"conditions": {
"spring": 5,
"summer": 5,
"autumn": 5
}
}, },
{ {
"pokeapi_id": 537, "pokeapi_id": 537,
@@ -7728,33 +7799,53 @@
"pokeapi_id": 536, "pokeapi_id": 536,
"pokemon_name": "Palpitoad", "pokemon_name": "Palpitoad",
"method": "walk", "method": "walk",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"spring": 40,
"summer": 40,
"autumn": 40
}
}, },
{ {
"pokeapi_id": 616, "pokeapi_id": 616,
"pokemon_name": "Shelmet", "pokemon_name": "Shelmet",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 54 "max_level": 54,
"conditions": {
"spring": 20,
"summer": 20,
"autumn": 20
}
}, },
{ {
"pokeapi_id": 618, "pokeapi_id": 618,
"pokemon_name": "Stunfisk", "pokemon_name": "Stunfisk",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 55, "min_level": 55,
"max_level": 56 "max_level": 56,
"conditions": {
"spring": 20,
"summer": 20,
"autumn": 20
}
}, },
{ {
"pokeapi_id": 453, "pokeapi_id": 453,
"pokemon_name": "Croagunk", "pokemon_name": "Croagunk",
"method": "walk", "method": "walk",
"encounter_rate": 15, "encounter_rate": null,
"min_level": 55, "min_level": 55,
"max_level": 56 "max_level": 56,
"conditions": {
"spring": 15,
"summer": 15,
"autumn": 15
}
}, },
{ {
"pokeapi_id": 340, "pokeapi_id": 340,
@@ -7768,9 +7859,14 @@
"pokeapi_id": 588, "pokeapi_id": 588,
"pokemon_name": "Karrablast", "pokemon_name": "Karrablast",
"method": "walk", "method": "walk",
"encounter_rate": 5, "encounter_rate": null,
"min_level": 57, "min_level": 57,
"max_level": 57 "max_level": 57,
"conditions": {
"spring": 5,
"summer": 5,
"autumn": 5
}
}, },
{ {
"pokeapi_id": 537, "pokeapi_id": 537,

View File

@@ -1486,7 +1486,7 @@
] ]
}, },
{ {
"name": "Relic Castle (Volcarona\u2019s Room and Room Outside)", "name": "Relic Castle (Volcaronas Room and Room Outside)",
"order": 30, "order": 30,
"encounters": [ "encounters": [
{ {
@@ -2971,25 +2971,40 @@
"pokeapi_id": 536, "pokeapi_id": 536,
"pokemon_name": "Palpitoad", "pokemon_name": "Palpitoad",
"method": "walk", "method": "walk",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 30, "min_level": 30,
"max_level": 33 "max_level": 33,
"conditions": {
"spring": 40,
"summer": 40,
"autumn": 40
}
}, },
{ {
"pokeapi_id": 616, "pokeapi_id": 616,
"pokemon_name": "Shelmet", "pokemon_name": "Shelmet",
"method": "walk", "method": "walk",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 30, "min_level": 30,
"max_level": 33 "max_level": 33,
"conditions": {
"spring": 40,
"summer": 40,
"autumn": 40
}
}, },
{ {
"pokeapi_id": 618, "pokeapi_id": 618,
"pokemon_name": "Stunfisk", "pokemon_name": "Stunfisk",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 31, "min_level": 31,
"max_level": 32 "max_level": 32,
"conditions": {
"spring": 20,
"summer": 20,
"autumn": 20
}
}, },
{ {
"pokeapi_id": 340, "pokeapi_id": 340,
@@ -3439,25 +3454,40 @@
"pokeapi_id": 536, "pokeapi_id": 536,
"pokemon_name": "Palpitoad", "pokemon_name": "Palpitoad",
"method": "walk", "method": "walk",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 30, "min_level": 30,
"max_level": 33 "max_level": 33,
"conditions": {
"spring": 40,
"summer": 40,
"autumn": 40
}
}, },
{ {
"pokeapi_id": 616, "pokeapi_id": 616,
"pokemon_name": "Shelmet", "pokemon_name": "Shelmet",
"method": "walk", "method": "walk",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 30, "min_level": 30,
"max_level": 33 "max_level": 33,
"conditions": {
"spring": 40,
"summer": 40,
"autumn": 40
}
}, },
{ {
"pokeapi_id": 618, "pokeapi_id": 618,
"pokemon_name": "Stunfisk", "pokemon_name": "Stunfisk",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 31, "min_level": 31,
"max_level": 32 "max_level": 32,
"conditions": {
"spring": 20,
"summer": 20,
"autumn": 20
}
}, },
{ {
"pokeapi_id": 340, "pokeapi_id": 340,
@@ -5630,9 +5660,12 @@
"pokeapi_id": 446, "pokeapi_id": 446,
"pokemon_name": "Munchlax", "pokemon_name": "Munchlax",
"method": "trade", "method": "trade",
"encounter_rate": 100, "encounter_rate": null,
"min_level": 60, "min_level": 60,
"max_level": 60 "max_level": 60,
"conditions": {
"summer": 100
}
}, },
{ {
"pokeapi_id": 90, "pokeapi_id": 90,
@@ -5740,9 +5773,15 @@
"pokeapi_id": 320, "pokeapi_id": 320,
"pokemon_name": "Wailmer", "pokemon_name": "Wailmer",
"method": "surf", "method": "surf",
"encounter_rate": 90, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 60 "max_level": 60,
"conditions": {
"spring": 90,
"summer": 90,
"autumn": 90,
"winter": 60
}
}, },
{ {
"pokeapi_id": 223, "pokeapi_id": 223,
@@ -5772,25 +5811,36 @@
"pokeapi_id": 458, "pokeapi_id": 458,
"pokemon_name": "Mantyke", "pokemon_name": "Mantyke",
"method": "surf", "method": "surf",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 55 "max_level": 55,
"conditions": {
"spring": 30,
"summer": 30,
"autumn": 30
}
}, },
{ {
"pokeapi_id": 364, "pokeapi_id": 364,
"pokemon_name": "Sealeo", "pokemon_name": "Sealeo",
"method": "surf", "method": "surf",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 60 "max_level": 60,
"conditions": {
"winter": 30
}
}, },
{ {
"pokeapi_id": 363, "pokeapi_id": 363,
"pokemon_name": "Spheal", "pokemon_name": "Spheal",
"method": "surf", "method": "surf",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 55 "max_level": 55,
"conditions": {
"winter": 30
}
}, },
{ {
"pokeapi_id": 279, "pokeapi_id": 279,
@@ -5812,9 +5862,14 @@
"pokeapi_id": 226, "pokeapi_id": 226,
"pokemon_name": "Mantine", "pokemon_name": "Mantine",
"method": "surf", "method": "surf",
"encounter_rate": 5, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 60 "max_level": 60,
"conditions": {
"spring": 5,
"summer": 5,
"autumn": 5
}
}, },
{ {
"pokeapi_id": 224, "pokeapi_id": 224,
@@ -5836,9 +5891,12 @@
"pokeapi_id": 365, "pokeapi_id": 365,
"pokemon_name": "Walrein", "pokemon_name": "Walrein",
"method": "surf", "method": "surf",
"encounter_rate": 5, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 70 "max_level": 70,
"conditions": {
"winter": 5
}
} }
] ]
}, },

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -35,7 +35,7 @@
"encounters": [], "encounters": [],
"children": [ "children": [
{ {
"name": "Alola Route 1 (First two fields east of the player\u2019s house)", "name": "Alola Route 1 (First two fields east of the players house)",
"order": 3, "order": 3,
"encounters": [ "encounters": [
{ {
@@ -368,7 +368,7 @@
] ]
}, },
{ {
"name": "Trainer\u2019s School (Alola)", "name": "Trainers School (Alola)",
"order": 8, "order": 8,
"encounters": [ "encounters": [
{ {
@@ -709,17 +709,23 @@
"pokeapi_id": 425, "pokeapi_id": 425,
"pokemon_name": "Drifloon", "pokemon_name": "Drifloon",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 7, "min_level": 7,
"max_level": 10 "max_level": 10,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 200, "pokeapi_id": 200,
"pokemon_name": "Misdreavus", "pokemon_name": "Misdreavus",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 7, "min_level": 7,
"max_level": 10 "max_level": 10,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 41, "pokeapi_id": 41,
@@ -1089,17 +1095,23 @@
"pokeapi_id": 10091, "pokeapi_id": 10091,
"pokemon_name": "Rattata (Alola)", "pokemon_name": "Rattata (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 15, "min_level": 15,
"max_level": 18 "max_level": 18,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 734, "pokeapi_id": 734,
"pokemon_name": "Yungoos", "pokemon_name": "Yungoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 15, "min_level": 15,
"max_level": 18 "max_level": 18,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 79, "pokeapi_id": 79,
@@ -1686,9 +1698,12 @@
"pokeapi_id": 751, "pokeapi_id": 751,
"pokemon_name": "Dewpider", "pokemon_name": "Dewpider",
"method": "surf", "method": "surf",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"day": 40
}
}, },
{ {
"pokeapi_id": 60, "pokeapi_id": 60,
@@ -1702,9 +1717,12 @@
"pokeapi_id": 283, "pokeapi_id": 283,
"pokemon_name": "Surskit", "pokemon_name": "Surskit",
"method": "surf", "method": "surf",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"night": 40
}
}, },
{ {
"pokeapi_id": 54, "pokeapi_id": 54,
@@ -1740,25 +1758,34 @@
"pokeapi_id": 755, "pokeapi_id": 755,
"pokemon_name": "Morelull", "pokemon_name": "Morelull",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"night": 20
}
}, },
{ {
"pokeapi_id": 46, "pokeapi_id": 46,
"pokemon_name": "Paras", "pokemon_name": "Paras",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"day": 20
}
}, },
{ {
"pokeapi_id": 751, "pokeapi_id": 751,
"pokemon_name": "Dewpider", "pokemon_name": "Dewpider",
"method": "walk", "method": "walk",
"encounter_rate": 10, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"day": 10
}
}, },
{ {
"pokeapi_id": 60, "pokeapi_id": 60,
@@ -1772,9 +1799,12 @@
"pokeapi_id": 283, "pokeapi_id": 283,
"pokemon_name": "Surskit", "pokemon_name": "Surskit",
"method": "walk", "method": "walk",
"encounter_rate": 10, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"night": 10
}
}, },
{ {
"pokeapi_id": 278, "pokeapi_id": 278,
@@ -1810,9 +1840,12 @@
"pokeapi_id": 751, "pokeapi_id": 751,
"pokemon_name": "Dewpider", "pokemon_name": "Dewpider",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"day": 20
}
}, },
{ {
"pokeapi_id": 60, "pokeapi_id": 60,
@@ -1826,9 +1859,12 @@
"pokeapi_id": 283, "pokeapi_id": 283,
"pokemon_name": "Surskit", "pokemon_name": "Surskit",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"night": 20
}
} }
] ]
}, },
@@ -2742,17 +2778,23 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 20, "min_level": 20,
"max_level": 23 "max_level": 23,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 20, "min_level": 20,
"max_level": 23 "max_level": 23,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 299, "pokeapi_id": 299,
@@ -2920,33 +2962,45 @@
"pokeapi_id": 752, "pokeapi_id": 752,
"pokemon_name": "Araquanid", "pokemon_name": "Araquanid",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 24, "min_level": 24,
"max_level": 27 "max_level": 27,
"conditions": {
"day": 20
}
}, },
{ {
"pokeapi_id": 168, "pokeapi_id": 168,
"pokemon_name": "Ariados", "pokemon_name": "Ariados",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 24, "min_level": 24,
"max_level": 27 "max_level": 27,
"conditions": {
"night": 20
}
}, },
{ {
"pokeapi_id": 166, "pokeapi_id": 166,
"pokemon_name": "Ledian", "pokemon_name": "Ledian",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 24, "min_level": 24,
"max_level": 27 "max_level": 27,
"conditions": {
"day": 20
}
}, },
{ {
"pokeapi_id": 284, "pokeapi_id": 284,
"pokemon_name": "Masquerain", "pokemon_name": "Masquerain",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 24, "min_level": 24,
"max_level": 27 "max_level": 27,
"conditions": {
"night": 20
}
}, },
{ {
"pokeapi_id": 10107, "pokeapi_id": 10107,
@@ -3076,9 +3130,13 @@
"pokeapi_id": 22, "pokeapi_id": 22,
"pokemon_name": "Fearow", "pokemon_name": "Fearow",
"method": "walk", "method": "walk",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 28 "max_level": 28,
"conditions": {
"day": 40,
"night": 30
}
}, },
{ {
"pokeapi_id": 10136, "pokeapi_id": 10136,
@@ -3100,9 +3158,12 @@
"pokeapi_id": 173, "pokeapi_id": 173,
"pokemon_name": "Cleffa", "pokemon_name": "Cleffa",
"method": "walk", "method": "walk",
"encounter_rate": 10, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 28 "max_level": 28,
"conditions": {
"night": 10
}
}, },
{ {
"pokeapi_id": 132, "pokeapi_id": 132,
@@ -3531,9 +3592,12 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 28, "min_level": 28,
"max_level": 31 "max_level": 31,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 279, "pokeapi_id": 279,
@@ -3547,9 +3611,12 @@
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 28, "min_level": 28,
"max_level": 31 "max_level": 31,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 361, "pokeapi_id": 361,
@@ -3871,17 +3938,23 @@
"pokeapi_id": 168, "pokeapi_id": 168,
"pokemon_name": "Ariados", "pokemon_name": "Ariados",
"method": "walk", "method": "walk",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 31, "min_level": 31,
"max_level": 34 "max_level": 34,
"conditions": {
"night": 40
}
}, },
{ {
"pokeapi_id": 166, "pokeapi_id": 166,
"pokemon_name": "Ledian", "pokemon_name": "Ledian",
"method": "walk", "method": "walk",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 31, "min_level": 31,
"max_level": 34 "max_level": 34,
"conditions": {
"day": 40
}
}, },
{ {
"pokeapi_id": 741, "pokeapi_id": 741,
@@ -4103,9 +4176,12 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 40, "min_level": 40,
"max_level": 43 "max_level": 43,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 279, "pokeapi_id": 279,
@@ -4119,9 +4195,12 @@
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 40, "min_level": 40,
"max_level": 43 "max_level": 43,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 73, "pokeapi_id": 73,
@@ -4213,9 +4292,12 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 40, "min_level": 40,
"max_level": 43 "max_level": 43,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 279, "pokeapi_id": 279,
@@ -4229,9 +4311,12 @@
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 40, "min_level": 40,
"max_level": 43 "max_level": 43,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 210, "pokeapi_id": 210,
@@ -4706,17 +4791,23 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 52, "min_level": 52,
"max_level": 55 "max_level": 55,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 52, "min_level": 52,
"max_level": 55 "max_level": 55,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 732, "pokeapi_id": 732,
@@ -4792,9 +4883,12 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 70, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"day": 70
}
}, },
{ {
"pokeapi_id": 548, "pokeapi_id": 548,
@@ -4808,9 +4902,12 @@
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 70, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"night": 70
}
}, },
{ {
"pokeapi_id": 297, "pokeapi_id": 297,
@@ -4854,17 +4951,23 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 732, "pokeapi_id": 732,
@@ -4908,17 +5011,23 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 97, "pokeapi_id": 97,
@@ -4994,9 +5103,12 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 10, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"day": 10
}
}, },
{ {
"pokeapi_id": 241, "pokeapi_id": 241,
@@ -5010,9 +5122,12 @@
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 10, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"night": 10
}
}, },
{ {
"pokeapi_id": 128, "pokeapi_id": 128,
@@ -5040,17 +5155,23 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 279, "pokeapi_id": 279,
@@ -5220,9 +5341,12 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 56, "min_level": 56,
"max_level": 59 "max_level": 59,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 279, "pokeapi_id": 279,
@@ -5236,9 +5360,12 @@
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 56, "min_level": 56,
"max_level": 59 "max_level": 59,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 210, "pokeapi_id": 210,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -35,7 +35,7 @@
"encounters": [], "encounters": [],
"children": [ "children": [
{ {
"name": "Alola Route 1 (First two fields east of the player\u2019s house)", "name": "Alola Route 1 (First two fields east of the players house)",
"order": 3, "order": 3,
"encounters": [ "encounters": [
{ {
@@ -368,7 +368,7 @@
] ]
}, },
{ {
"name": "Trainer\u2019s School (Alola)", "name": "Trainers School (Alola)",
"order": 8, "order": 8,
"encounters": [ "encounters": [
{ {
@@ -709,17 +709,23 @@
"pokeapi_id": 425, "pokeapi_id": 425,
"pokemon_name": "Drifloon", "pokemon_name": "Drifloon",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 7, "min_level": 7,
"max_level": 10 "max_level": 10,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 200, "pokeapi_id": 200,
"pokemon_name": "Misdreavus", "pokemon_name": "Misdreavus",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 7, "min_level": 7,
"max_level": 10 "max_level": 10,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 41, "pokeapi_id": 41,
@@ -1105,17 +1111,23 @@
"pokeapi_id": 10091, "pokeapi_id": 10091,
"pokemon_name": "Rattata (Alola)", "pokemon_name": "Rattata (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 15, "min_level": 15,
"max_level": 18 "max_level": 18,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 734, "pokeapi_id": 734,
"pokemon_name": "Yungoos", "pokemon_name": "Yungoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 15, "min_level": 15,
"max_level": 18 "max_level": 18,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 79, "pokeapi_id": 79,
@@ -1702,9 +1714,12 @@
"pokeapi_id": 751, "pokeapi_id": 751,
"pokemon_name": "Dewpider", "pokemon_name": "Dewpider",
"method": "surf", "method": "surf",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"day": 40
}
}, },
{ {
"pokeapi_id": 60, "pokeapi_id": 60,
@@ -1718,9 +1733,12 @@
"pokeapi_id": 283, "pokeapi_id": 283,
"pokemon_name": "Surskit", "pokemon_name": "Surskit",
"method": "surf", "method": "surf",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"night": 40
}
}, },
{ {
"pokeapi_id": 54, "pokeapi_id": 54,
@@ -1756,25 +1774,34 @@
"pokeapi_id": 755, "pokeapi_id": 755,
"pokemon_name": "Morelull", "pokemon_name": "Morelull",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"night": 20
}
}, },
{ {
"pokeapi_id": 46, "pokeapi_id": 46,
"pokemon_name": "Paras", "pokemon_name": "Paras",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"day": 20
}
}, },
{ {
"pokeapi_id": 751, "pokeapi_id": 751,
"pokemon_name": "Dewpider", "pokemon_name": "Dewpider",
"method": "walk", "method": "walk",
"encounter_rate": 10, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"day": 10
}
}, },
{ {
"pokeapi_id": 60, "pokeapi_id": 60,
@@ -1788,9 +1815,12 @@
"pokeapi_id": 283, "pokeapi_id": 283,
"pokemon_name": "Surskit", "pokemon_name": "Surskit",
"method": "walk", "method": "walk",
"encounter_rate": 10, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"night": 10
}
}, },
{ {
"pokeapi_id": 278, "pokeapi_id": 278,
@@ -1826,9 +1856,12 @@
"pokeapi_id": 751, "pokeapi_id": 751,
"pokemon_name": "Dewpider", "pokemon_name": "Dewpider",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"day": 20
}
}, },
{ {
"pokeapi_id": 60, "pokeapi_id": 60,
@@ -1842,9 +1875,12 @@
"pokeapi_id": 283, "pokeapi_id": 283,
"pokemon_name": "Surskit", "pokemon_name": "Surskit",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"night": 20
}
} }
] ]
}, },
@@ -2758,17 +2794,23 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 20, "min_level": 20,
"max_level": 23 "max_level": 23,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 20, "min_level": 20,
"max_level": 23 "max_level": 23,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 299, "pokeapi_id": 299,
@@ -2944,33 +2986,45 @@
"pokeapi_id": 752, "pokeapi_id": 752,
"pokemon_name": "Araquanid", "pokemon_name": "Araquanid",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 24, "min_level": 24,
"max_level": 27 "max_level": 27,
"conditions": {
"day": 20
}
}, },
{ {
"pokeapi_id": 168, "pokeapi_id": 168,
"pokemon_name": "Ariados", "pokemon_name": "Ariados",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 24, "min_level": 24,
"max_level": 27 "max_level": 27,
"conditions": {
"night": 20
}
}, },
{ {
"pokeapi_id": 166, "pokeapi_id": 166,
"pokemon_name": "Ledian", "pokemon_name": "Ledian",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 24, "min_level": 24,
"max_level": 27 "max_level": 27,
"conditions": {
"day": 20
}
}, },
{ {
"pokeapi_id": 284, "pokeapi_id": 284,
"pokemon_name": "Masquerain", "pokemon_name": "Masquerain",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 24, "min_level": 24,
"max_level": 27 "max_level": 27,
"conditions": {
"night": 20
}
}, },
{ {
"pokeapi_id": 10107, "pokeapi_id": 10107,
@@ -3100,9 +3154,13 @@
"pokeapi_id": 22, "pokeapi_id": 22,
"pokemon_name": "Fearow", "pokemon_name": "Fearow",
"method": "walk", "method": "walk",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 28 "max_level": 28,
"conditions": {
"day": 40,
"night": 30
}
}, },
{ {
"pokeapi_id": 10136, "pokeapi_id": 10136,
@@ -3124,9 +3182,12 @@
"pokeapi_id": 173, "pokeapi_id": 173,
"pokemon_name": "Cleffa", "pokemon_name": "Cleffa",
"method": "walk", "method": "walk",
"encounter_rate": 10, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 28 "max_level": 28,
"conditions": {
"night": 10
}
}, },
{ {
"pokeapi_id": 132, "pokeapi_id": 132,
@@ -3571,9 +3632,12 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 28, "min_level": 28,
"max_level": 31 "max_level": 31,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 279, "pokeapi_id": 279,
@@ -3587,9 +3651,12 @@
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 28, "min_level": 28,
"max_level": 31 "max_level": 31,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 361, "pokeapi_id": 361,
@@ -3911,17 +3978,23 @@
"pokeapi_id": 168, "pokeapi_id": 168,
"pokemon_name": "Ariados", "pokemon_name": "Ariados",
"method": "walk", "method": "walk",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 31, "min_level": 31,
"max_level": 34 "max_level": 34,
"conditions": {
"night": 40
}
}, },
{ {
"pokeapi_id": 166, "pokeapi_id": 166,
"pokemon_name": "Ledian", "pokemon_name": "Ledian",
"method": "walk", "method": "walk",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 31, "min_level": 31,
"max_level": 34 "max_level": 34,
"conditions": {
"day": 40
}
}, },
{ {
"pokeapi_id": 741, "pokeapi_id": 741,
@@ -4143,9 +4216,12 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 40, "min_level": 40,
"max_level": 43 "max_level": 43,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 279, "pokeapi_id": 279,
@@ -4159,9 +4235,12 @@
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 40, "min_level": 40,
"max_level": 43 "max_level": 43,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 73, "pokeapi_id": 73,
@@ -4253,9 +4332,12 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 40, "min_level": 40,
"max_level": 43 "max_level": 43,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 279, "pokeapi_id": 279,
@@ -4269,9 +4351,12 @@
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 40, "min_level": 40,
"max_level": 43 "max_level": 43,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 210, "pokeapi_id": 210,
@@ -4729,17 +4814,23 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 52, "min_level": 52,
"max_level": 55 "max_level": 55,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 52, "min_level": 52,
"max_level": 55 "max_level": 55,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 732, "pokeapi_id": 732,
@@ -4823,17 +4914,23 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 70, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"day": 70
}
}, },
{ {
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 70, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"night": 70
}
}, },
{ {
"pokeapi_id": 628, "pokeapi_id": 628,
@@ -4877,17 +4974,23 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 732, "pokeapi_id": 732,
@@ -4931,17 +5034,23 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 546, "pokeapi_id": 546,
@@ -5017,9 +5126,12 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 10, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"day": 10
}
}, },
{ {
"pokeapi_id": 241, "pokeapi_id": 241,
@@ -5033,9 +5145,12 @@
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 10, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"night": 10
}
}, },
{ {
"pokeapi_id": 128, "pokeapi_id": 128,
@@ -5063,17 +5178,23 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 546, "pokeapi_id": 546,
@@ -5243,9 +5364,12 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 56, "min_level": 56,
"max_level": 59 "max_level": 59,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 279, "pokeapi_id": 279,
@@ -5259,9 +5383,12 @@
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 56, "min_level": 56,
"max_level": 59 "max_level": 59,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 210, "pokeapi_id": 210,

File diff suppressed because it is too large Load Diff

View File

@@ -60,7 +60,7 @@
], ],
"children": [ "children": [
{ {
"name": "Alola Route 1 (First two fields east of the player\u2019s house)", "name": "Alola Route 1 (First two fields east of the players house)",
"order": 3, "order": 3,
"encounters": [ "encounters": [
{ {
@@ -377,7 +377,7 @@
] ]
}, },
{ {
"name": "Trainer\u2019s School (Alola)", "name": "Trainers School (Alola)",
"order": 8, "order": 8,
"encounters": [ "encounters": [
{ {
@@ -773,7 +773,7 @@
] ]
}, },
{ {
"name": "Alola Route 2 (Two patches of grass southwest of the Pok\u00e9mon Center)", "name": "Alola Route 2 (Two patches of grass southwest of the Pokémon Center)",
"order": 15, "order": 15,
"encounters": [ "encounters": [
{ {
@@ -922,9 +922,12 @@
"pokeapi_id": 425, "pokeapi_id": 425,
"pokemon_name": "Drifloon", "pokemon_name": "Drifloon",
"method": "walk", "method": "walk",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 6, "min_level": 6,
"max_level": 9 "max_level": 9,
"conditions": {
"day": 40
}
}, },
{ {
"pokeapi_id": 92, "pokeapi_id": 92,
@@ -938,9 +941,12 @@
"pokeapi_id": 198, "pokeapi_id": 198,
"pokemon_name": "Murkrow", "pokemon_name": "Murkrow",
"method": "walk", "method": "walk",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 6, "min_level": 6,
"max_level": 9 "max_level": 9,
"conditions": {
"night": 40
}
}, },
{ {
"pokeapi_id": 41, "pokeapi_id": 41,
@@ -1342,17 +1348,23 @@
"pokeapi_id": 10091, "pokeapi_id": 10091,
"pokemon_name": "Rattata (Alola)", "pokemon_name": "Rattata (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 15, "min_level": 15,
"max_level": 18 "max_level": 18,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 734, "pokeapi_id": 734,
"pokemon_name": "Yungoos", "pokemon_name": "Yungoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 15, "min_level": 15,
"max_level": 18 "max_level": 18,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 456, "pokeapi_id": 456,
@@ -1591,9 +1603,13 @@
"pokeapi_id": 366, "pokeapi_id": 366,
"pokemon_name": "Clamperl", "pokemon_name": "Clamperl",
"method": "fishing", "method": "fishing",
"encounter_rate": 10, "encounter_rate": null,
"min_level": 10, "min_level": 10,
"max_level": 17 "max_level": 17,
"conditions": {
"day": 5,
"night": 5
}
}, },
{ {
"pokeapi_id": 222, "pokeapi_id": 222,
@@ -1613,17 +1629,25 @@
"pokeapi_id": 366, "pokeapi_id": 366,
"pokemon_name": "Clamperl", "pokemon_name": "Clamperl",
"method": "fishing", "method": "fishing",
"encounter_rate": 55, "encounter_rate": null,
"min_level": 10, "min_level": 10,
"max_level": 22 "max_level": 22,
"conditions": {
"day": 35,
"night": 20
}
}, },
{ {
"pokeapi_id": 222, "pokeapi_id": 222,
"pokemon_name": "Corsola", "pokemon_name": "Corsola",
"method": "fishing", "method": "fishing",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 10, "min_level": 10,
"max_level": 22 "max_level": 22,
"conditions": {
"day": 5,
"night": 20
}
}, },
{ {
"pokeapi_id": 370, "pokeapi_id": 370,
@@ -1707,9 +1731,13 @@
"pokeapi_id": 366, "pokeapi_id": 366,
"pokemon_name": "Clamperl", "pokemon_name": "Clamperl",
"method": "fishing", "method": "fishing",
"encounter_rate": 10, "encounter_rate": null,
"min_level": 10, "min_level": 10,
"max_level": 22 "max_level": 22,
"conditions": {
"day": 5,
"night": 5
}
} }
] ]
} }
@@ -2096,9 +2124,12 @@
"pokeapi_id": 751, "pokeapi_id": 751,
"pokemon_name": "Dewpider", "pokemon_name": "Dewpider",
"method": "surf", "method": "surf",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"day": 40
}
}, },
{ {
"pokeapi_id": 60, "pokeapi_id": 60,
@@ -2112,33 +2143,45 @@
"pokeapi_id": 283, "pokeapi_id": 283,
"pokemon_name": "Surskit", "pokemon_name": "Surskit",
"method": "surf", "method": "surf",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"night": 40
}
}, },
{ {
"pokeapi_id": 751, "pokeapi_id": 751,
"pokemon_name": "Dewpider", "pokemon_name": "Dewpider",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"day": 20
}
}, },
{ {
"pokeapi_id": 755, "pokeapi_id": 755,
"pokemon_name": "Morelull", "pokemon_name": "Morelull",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"night": 20
}
}, },
{ {
"pokeapi_id": 46, "pokeapi_id": 46,
"pokemon_name": "Paras", "pokemon_name": "Paras",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"day": 20
}
}, },
{ {
"pokeapi_id": 60, "pokeapi_id": 60,
@@ -2168,9 +2211,12 @@
"pokeapi_id": 283, "pokeapi_id": 283,
"pokemon_name": "Surskit", "pokemon_name": "Surskit",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"night": 20
}
}, },
{ {
"pokeapi_id": 278, "pokeapi_id": 278,
@@ -3263,9 +3309,13 @@
"pokeapi_id": 278, "pokeapi_id": 278,
"pokemon_name": "Wingull", "pokemon_name": "Wingull",
"method": "walk", "method": "walk",
"encounter_rate": 50, "encounter_rate": null,
"min_level": 21, "min_level": 21,
"max_level": 24 "max_level": 24,
"conditions": {
"day": 30,
"night": 50
}
}, },
{ {
"pokeapi_id": 170, "pokeapi_id": 170,
@@ -3287,17 +3337,23 @@
"pokeapi_id": 177, "pokeapi_id": 177,
"pokemon_name": "Natu", "pokemon_name": "Natu",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 21, "min_level": 21,
"max_level": 24 "max_level": 24,
"conditions": {
"day": 20
}
}, },
{ {
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 21, "min_level": 21,
"max_level": 24 "max_level": 24,
"conditions": {
"night": 20
}
}, },
{ {
"pokeapi_id": 299, "pokeapi_id": 299,
@@ -3341,9 +3397,13 @@
"pokeapi_id": 771, "pokeapi_id": 771,
"pokemon_name": "Pyukumuku", "pokemon_name": "Pyukumuku",
"method": "surf", "method": "surf",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 22, "min_level": 22,
"max_level": 25 "max_level": 25,
"conditions": {
"day": 30,
"night": 30
}
}, },
{ {
"pokeapi_id": 456, "pokeapi_id": 456,
@@ -3457,33 +3517,45 @@
"pokeapi_id": 752, "pokeapi_id": 752,
"pokemon_name": "Araquanid", "pokemon_name": "Araquanid",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 28 "max_level": 28,
"conditions": {
"day": 20
}
}, },
{ {
"pokeapi_id": 168, "pokeapi_id": 168,
"pokemon_name": "Ariados", "pokemon_name": "Ariados",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 28 "max_level": 28,
"conditions": {
"night": 20
}
}, },
{ {
"pokeapi_id": 166, "pokeapi_id": 166,
"pokemon_name": "Ledian", "pokemon_name": "Ledian",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 28 "max_level": 28,
"conditions": {
"day": 20
}
}, },
{ {
"pokeapi_id": 284, "pokeapi_id": 284,
"pokemon_name": "Masquerain", "pokemon_name": "Masquerain",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 28 "max_level": 28,
"conditions": {
"night": 20
}
}, },
{ {
"pokeapi_id": 10107, "pokeapi_id": 10107,
@@ -3861,9 +3933,13 @@
"pokeapi_id": 737, "pokeapi_id": 737,
"pokemon_name": "Charjabug", "pokemon_name": "Charjabug",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 29, "min_level": 29,
"max_level": 32 "max_level": 32,
"conditions": {
"day": 10,
"night": 10
}
}, },
{ {
"pokeapi_id": 10110, "pokeapi_id": 10110,
@@ -4013,9 +4089,12 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 30, "min_level": 30,
"max_level": 33 "max_level": 33,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 279, "pokeapi_id": 279,
@@ -4029,9 +4108,12 @@
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 30, "min_level": 30,
"max_level": 33 "max_level": 33,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 361, "pokeapi_id": 361,
@@ -4415,9 +4497,12 @@
"pokeapi_id": 168, "pokeapi_id": 168,
"pokemon_name": "Ariados", "pokemon_name": "Ariados",
"method": "walk", "method": "walk",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 33, "min_level": 33,
"max_level": 36 "max_level": 36,
"conditions": {
"night": 40
}
}, },
{ {
"pokeapi_id": 670, "pokeapi_id": 670,
@@ -4431,9 +4516,12 @@
"pokeapi_id": 166, "pokeapi_id": 166,
"pokemon_name": "Ledian", "pokemon_name": "Ledian",
"method": "walk", "method": "walk",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 33, "min_level": 33,
"max_level": 36 "max_level": 36,
"conditions": {
"day": 40
}
}, },
{ {
"pokeapi_id": 741, "pokeapi_id": 741,
@@ -5418,9 +5506,12 @@
"pokeapi_id": 427, "pokeapi_id": 427,
"pokemon_name": "Buneary", "pokemon_name": "Buneary",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 52, "min_level": 52,
"max_level": 55 "max_level": 55,
"conditions": {
"night": 10
}
}, },
{ {
"pokeapi_id": 732, "pokeapi_id": 732,
@@ -5458,9 +5549,12 @@
"pokeapi_id": 447, "pokeapi_id": 447,
"pokemon_name": "Riolu", "pokemon_name": "Riolu",
"method": "walk", "method": "walk",
"encounter_rate": 10, "encounter_rate": null,
"min_level": 52, "min_level": 52,
"max_level": 55 "max_level": 55,
"conditions": {
"day": 10
}
} }
] ]
}, },
@@ -5559,9 +5653,13 @@
"pokeapi_id": 743, "pokeapi_id": 743,
"pokemon_name": "Ribombee", "pokemon_name": "Ribombee",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"day": 30,
"night": 20
}
}, },
{ {
"pokeapi_id": 670, "pokeapi_id": 670,
@@ -5583,9 +5681,12 @@
"pokeapi_id": 200, "pokeapi_id": 200,
"pokemon_name": "Misdreavus", "pokemon_name": "Misdreavus",
"method": "walk", "method": "walk",
"encounter_rate": 10, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"night": 10
}
} }
] ]
}, },
@@ -5621,17 +5722,23 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 70, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"day": 70
}
}, },
{ {
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 70, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"night": 70
}
}, },
{ {
"pokeapi_id": 57, "pokeapi_id": 57,
@@ -5707,17 +5814,23 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 732, "pokeapi_id": 732,
@@ -5761,33 +5874,47 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 297, "pokeapi_id": 297,
"pokemon_name": "Hariyama", "pokemon_name": "Hariyama",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"day": 20,
"night": 10
}
}, },
{ {
"pokeapi_id": 97, "pokeapi_id": 97,
"pokemon_name": "Hypno", "pokemon_name": "Hypno",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"day": 10,
"night": 20
}
}, },
{ {
"pokeapi_id": 241, "pokeapi_id": 241,
@@ -5855,9 +5982,12 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 10, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"day": 10
}
}, },
{ {
"pokeapi_id": 241, "pokeapi_id": 241,
@@ -5871,9 +6001,12 @@
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 10, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"night": 10
}
}, },
{ {
"pokeapi_id": 128, "pokeapi_id": 128,
@@ -5901,17 +6034,23 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 279, "pokeapi_id": 279,
@@ -6696,7 +6835,7 @@
] ]
}, },
{ {
"name": "Team Rocket\u2019s Castle", "name": "Team Rockets Castle",
"order": 124, "order": 124,
"encounters": [ "encounters": [
{ {

View File

@@ -60,7 +60,7 @@
], ],
"children": [ "children": [
{ {
"name": "Alola Route 1 (First two fields east of the player\u2019s house)", "name": "Alola Route 1 (First two fields east of the players house)",
"order": 3, "order": 3,
"encounters": [ "encounters": [
{ {
@@ -377,7 +377,7 @@
] ]
}, },
{ {
"name": "Trainer\u2019s School (Alola)", "name": "Trainers School (Alola)",
"order": 8, "order": 8,
"encounters": [ "encounters": [
{ {
@@ -773,7 +773,7 @@
] ]
}, },
{ {
"name": "Alola Route 2 (Two patches of grass southwest of the Pok\u00e9mon Center)", "name": "Alola Route 2 (Two patches of grass southwest of the Pokémon Center)",
"order": 15, "order": 15,
"encounters": [ "encounters": [
{ {
@@ -922,9 +922,12 @@
"pokeapi_id": 425, "pokeapi_id": 425,
"pokemon_name": "Drifloon", "pokemon_name": "Drifloon",
"method": "walk", "method": "walk",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 6, "min_level": 6,
"max_level": 9 "max_level": 9,
"conditions": {
"day": 40
}
}, },
{ {
"pokeapi_id": 92, "pokeapi_id": 92,
@@ -938,9 +941,12 @@
"pokeapi_id": 198, "pokeapi_id": 198,
"pokemon_name": "Murkrow", "pokemon_name": "Murkrow",
"method": "walk", "method": "walk",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 6, "min_level": 6,
"max_level": 9 "max_level": 9,
"conditions": {
"night": 40
}
}, },
{ {
"pokeapi_id": 41, "pokeapi_id": 41,
@@ -1342,17 +1348,23 @@
"pokeapi_id": 10091, "pokeapi_id": 10091,
"pokemon_name": "Rattata (Alola)", "pokemon_name": "Rattata (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 15, "min_level": 15,
"max_level": 18 "max_level": 18,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 734, "pokeapi_id": 734,
"pokemon_name": "Yungoos", "pokemon_name": "Yungoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 15, "min_level": 15,
"max_level": 18 "max_level": 18,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 456, "pokeapi_id": 456,
@@ -1591,9 +1603,13 @@
"pokeapi_id": 366, "pokeapi_id": 366,
"pokemon_name": "Clamperl", "pokemon_name": "Clamperl",
"method": "fishing", "method": "fishing",
"encounter_rate": 10, "encounter_rate": null,
"min_level": 10, "min_level": 10,
"max_level": 17 "max_level": 17,
"conditions": {
"day": 5,
"night": 5
}
}, },
{ {
"pokeapi_id": 222, "pokeapi_id": 222,
@@ -1613,17 +1629,25 @@
"pokeapi_id": 366, "pokeapi_id": 366,
"pokemon_name": "Clamperl", "pokemon_name": "Clamperl",
"method": "fishing", "method": "fishing",
"encounter_rate": 55, "encounter_rate": null,
"min_level": 10, "min_level": 10,
"max_level": 22 "max_level": 22,
"conditions": {
"day": 35,
"night": 20
}
}, },
{ {
"pokeapi_id": 222, "pokeapi_id": 222,
"pokemon_name": "Corsola", "pokemon_name": "Corsola",
"method": "fishing", "method": "fishing",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 10, "min_level": 10,
"max_level": 22 "max_level": 22,
"conditions": {
"day": 5,
"night": 20
}
}, },
{ {
"pokeapi_id": 370, "pokeapi_id": 370,
@@ -1707,9 +1731,13 @@
"pokeapi_id": 366, "pokeapi_id": 366,
"pokemon_name": "Clamperl", "pokemon_name": "Clamperl",
"method": "fishing", "method": "fishing",
"encounter_rate": 10, "encounter_rate": null,
"min_level": 10, "min_level": 10,
"max_level": 22 "max_level": 22,
"conditions": {
"day": 5,
"night": 5
}
} }
] ]
} }
@@ -2096,9 +2124,12 @@
"pokeapi_id": 751, "pokeapi_id": 751,
"pokemon_name": "Dewpider", "pokemon_name": "Dewpider",
"method": "surf", "method": "surf",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"day": 40
}
}, },
{ {
"pokeapi_id": 60, "pokeapi_id": 60,
@@ -2112,33 +2143,45 @@
"pokeapi_id": 283, "pokeapi_id": 283,
"pokemon_name": "Surskit", "pokemon_name": "Surskit",
"method": "surf", "method": "surf",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"night": 40
}
}, },
{ {
"pokeapi_id": 751, "pokeapi_id": 751,
"pokemon_name": "Dewpider", "pokemon_name": "Dewpider",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"day": 20
}
}, },
{ {
"pokeapi_id": 755, "pokeapi_id": 755,
"pokemon_name": "Morelull", "pokemon_name": "Morelull",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"night": 20
}
}, },
{ {
"pokeapi_id": 46, "pokeapi_id": 46,
"pokemon_name": "Paras", "pokemon_name": "Paras",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"day": 20
}
}, },
{ {
"pokeapi_id": 60, "pokeapi_id": 60,
@@ -2168,9 +2211,12 @@
"pokeapi_id": 283, "pokeapi_id": 283,
"pokemon_name": "Surskit", "pokemon_name": "Surskit",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 14, "min_level": 14,
"max_level": 17 "max_level": 17,
"conditions": {
"night": 20
}
}, },
{ {
"pokeapi_id": 278, "pokeapi_id": 278,
@@ -3263,9 +3309,13 @@
"pokeapi_id": 278, "pokeapi_id": 278,
"pokemon_name": "Wingull", "pokemon_name": "Wingull",
"method": "walk", "method": "walk",
"encounter_rate": 50, "encounter_rate": null,
"min_level": 21, "min_level": 21,
"max_level": 24 "max_level": 24,
"conditions": {
"day": 30,
"night": 50
}
}, },
{ {
"pokeapi_id": 170, "pokeapi_id": 170,
@@ -3287,17 +3337,23 @@
"pokeapi_id": 177, "pokeapi_id": 177,
"pokemon_name": "Natu", "pokemon_name": "Natu",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 21, "min_level": 21,
"max_level": 24 "max_level": 24,
"conditions": {
"day": 20
}
}, },
{ {
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 21, "min_level": 21,
"max_level": 24 "max_level": 24,
"conditions": {
"night": 20
}
}, },
{ {
"pokeapi_id": 299, "pokeapi_id": 299,
@@ -3341,9 +3397,13 @@
"pokeapi_id": 771, "pokeapi_id": 771,
"pokemon_name": "Pyukumuku", "pokemon_name": "Pyukumuku",
"method": "surf", "method": "surf",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 22, "min_level": 22,
"max_level": 25 "max_level": 25,
"conditions": {
"day": 30,
"night": 30
}
}, },
{ {
"pokeapi_id": 456, "pokeapi_id": 456,
@@ -3457,33 +3517,45 @@
"pokeapi_id": 752, "pokeapi_id": 752,
"pokemon_name": "Araquanid", "pokemon_name": "Araquanid",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 28 "max_level": 28,
"conditions": {
"day": 20
}
}, },
{ {
"pokeapi_id": 168, "pokeapi_id": 168,
"pokemon_name": "Ariados", "pokemon_name": "Ariados",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 28 "max_level": 28,
"conditions": {
"night": 20
}
}, },
{ {
"pokeapi_id": 166, "pokeapi_id": 166,
"pokemon_name": "Ledian", "pokemon_name": "Ledian",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 28 "max_level": 28,
"conditions": {
"day": 20
}
}, },
{ {
"pokeapi_id": 284, "pokeapi_id": 284,
"pokemon_name": "Masquerain", "pokemon_name": "Masquerain",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 28 "max_level": 28,
"conditions": {
"night": 20
}
}, },
{ {
"pokeapi_id": 10107, "pokeapi_id": 10107,
@@ -3861,9 +3933,13 @@
"pokeapi_id": 737, "pokeapi_id": 737,
"pokemon_name": "Charjabug", "pokemon_name": "Charjabug",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 29, "min_level": 29,
"max_level": 32 "max_level": 32,
"conditions": {
"day": 10,
"night": 10
}
}, },
{ {
"pokeapi_id": 10110, "pokeapi_id": 10110,
@@ -4021,9 +4097,12 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 30, "min_level": 30,
"max_level": 33 "max_level": 33,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 279, "pokeapi_id": 279,
@@ -4037,9 +4116,12 @@
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 30, "min_level": 30,
"max_level": 33 "max_level": 33,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 361, "pokeapi_id": 361,
@@ -4423,9 +4505,12 @@
"pokeapi_id": 168, "pokeapi_id": 168,
"pokemon_name": "Ariados", "pokemon_name": "Ariados",
"method": "walk", "method": "walk",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 33, "min_level": 33,
"max_level": 36 "max_level": 36,
"conditions": {
"night": 40
}
}, },
{ {
"pokeapi_id": 546, "pokeapi_id": 546,
@@ -4447,9 +4532,12 @@
"pokeapi_id": 166, "pokeapi_id": 166,
"pokemon_name": "Ledian", "pokemon_name": "Ledian",
"method": "walk", "method": "walk",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 33, "min_level": 33,
"max_level": 36 "max_level": 36,
"conditions": {
"day": 40
}
}, },
{ {
"pokeapi_id": 741, "pokeapi_id": 741,
@@ -5427,9 +5515,12 @@
"pokeapi_id": 427, "pokeapi_id": 427,
"pokemon_name": "Buneary", "pokemon_name": "Buneary",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 52, "min_level": 52,
"max_level": 55 "max_level": 55,
"conditions": {
"night": 10
}
}, },
{ {
"pokeapi_id": 732, "pokeapi_id": 732,
@@ -5467,9 +5558,12 @@
"pokeapi_id": 447, "pokeapi_id": 447,
"pokemon_name": "Riolu", "pokemon_name": "Riolu",
"method": "walk", "method": "walk",
"encounter_rate": 10, "encounter_rate": null,
"min_level": 52, "min_level": 52,
"max_level": 55 "max_level": 55,
"conditions": {
"day": 10
}
} }
] ]
}, },
@@ -5568,9 +5662,13 @@
"pokeapi_id": 743, "pokeapi_id": 743,
"pokemon_name": "Ribombee", "pokemon_name": "Ribombee",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"day": 30,
"night": 20
}
}, },
{ {
"pokeapi_id": 670, "pokeapi_id": 670,
@@ -5592,9 +5690,12 @@
"pokeapi_id": 200, "pokeapi_id": 200,
"pokemon_name": "Misdreavus", "pokemon_name": "Misdreavus",
"method": "walk", "method": "walk",
"encounter_rate": 10, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"night": 10
}
} }
] ]
}, },
@@ -5630,17 +5731,23 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 70, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"day": 70
}
}, },
{ {
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 70, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"night": 70
}
}, },
{ {
"pokeapi_id": 57, "pokeapi_id": 57,
@@ -5716,17 +5823,23 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 732, "pokeapi_id": 732,
@@ -5770,33 +5883,47 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 297, "pokeapi_id": 297,
"pokemon_name": "Hariyama", "pokemon_name": "Hariyama",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"day": 20,
"night": 10
}
}, },
{ {
"pokeapi_id": 97, "pokeapi_id": 97,
"pokemon_name": "Hypno", "pokemon_name": "Hypno",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"day": 10,
"night": 20
}
}, },
{ {
"pokeapi_id": 546, "pokeapi_id": 546,
@@ -5864,9 +5991,12 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 10, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"day": 10
}
}, },
{ {
"pokeapi_id": 241, "pokeapi_id": 241,
@@ -5880,9 +6010,12 @@
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 10, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"night": 10
}
}, },
{ {
"pokeapi_id": 128, "pokeapi_id": 128,
@@ -5910,17 +6043,23 @@
"pokeapi_id": 735, "pokeapi_id": 735,
"pokemon_name": "Gumshoos", "pokemon_name": "Gumshoos",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"day": 30
}
}, },
{ {
"pokeapi_id": 10092, "pokeapi_id": 10092,
"pokemon_name": "Raticate (Alola)", "pokemon_name": "Raticate (Alola)",
"method": "walk", "method": "walk",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"night": 30
}
}, },
{ {
"pokeapi_id": 546, "pokeapi_id": 546,
@@ -6705,7 +6844,7 @@
] ]
}, },
{ {
"name": "Team Rocket\u2019s Castle", "name": "Team Rockets Castle",
"order": 124, "order": 124,
"encounters": [ "encounters": [
{ {

View File

@@ -844,17 +844,25 @@
"pokeapi_id": 88, "pokeapi_id": 88,
"pokemon_name": "Grimer", "pokemon_name": "Grimer",
"method": "surf", "method": "surf",
"encounter_rate": 100, "encounter_rate": null,
"min_level": 5, "min_level": 5,
"max_level": 20 "max_level": 20,
"conditions": {
"spring": 100,
"summer": 100
}
}, },
{ {
"pokeapi_id": 88, "pokeapi_id": 88,
"pokemon_name": "Grimer", "pokemon_name": "Grimer",
"method": "fishing", "method": "fishing",
"encounter_rate": 100, "encounter_rate": null,
"min_level": 40, "min_level": 40,
"max_level": 70 "max_level": 70,
"conditions": {
"spring": 100,
"summer": 100
}
}, },
{ {
"pokeapi_id": 19, "pokeapi_id": 19,
@@ -884,17 +892,25 @@
"pokeapi_id": 89, "pokeapi_id": 89,
"pokemon_name": "Muk", "pokemon_name": "Muk",
"method": "fishing", "method": "fishing",
"encounter_rate": 10, "encounter_rate": null,
"min_level": 50, "min_level": 50,
"max_level": 70 "max_level": 70,
"conditions": {
"spring": 10,
"summer": 10
}
}, },
{ {
"pokeapi_id": 89, "pokeapi_id": 89,
"pokemon_name": "Muk", "pokemon_name": "Muk",
"method": "surf", "method": "surf",
"encounter_rate": 5, "encounter_rate": null,
"min_level": 5, "min_level": 5,
"max_level": 20 "max_level": 20,
"conditions": {
"spring": 5,
"summer": 5
}
} }
] ]
}, },
@@ -3294,41 +3310,63 @@
"pokeapi_id": 593, "pokeapi_id": 593,
"pokemon_name": "Jellicent", "pokemon_name": "Jellicent",
"method": "surf", "method": "surf",
"encounter_rate": 60, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 40 "max_level": 40,
"conditions": {
"spring": 60,
"summer": 60,
"autumn": 60
}
}, },
{ {
"pokeapi_id": 320, "pokeapi_id": 320,
"pokemon_name": "Wailmer", "pokemon_name": "Wailmer",
"method": "surf", "method": "surf",
"encounter_rate": 60, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 40 "max_level": 40,
"conditions": {
"spring": 30,
"summer": 30,
"autumn": 30,
"winter": 60
}
}, },
{ {
"pokeapi_id": 458, "pokeapi_id": 458,
"pokemon_name": "Mantyke", "pokemon_name": "Mantyke",
"method": "surf", "method": "surf",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 40 "max_level": 40,
"conditions": {
"spring": 30,
"summer": 30,
"autumn": 30
}
}, },
{ {
"pokeapi_id": 364, "pokeapi_id": 364,
"pokemon_name": "Sealeo", "pokemon_name": "Sealeo",
"method": "surf", "method": "surf",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 40 "max_level": 40,
"conditions": {
"winter": 30
}
}, },
{ {
"pokeapi_id": 363, "pokeapi_id": 363,
"pokemon_name": "Spheal", "pokemon_name": "Spheal",
"method": "surf", "method": "surf",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 40 "max_level": 40,
"conditions": {
"winter": 30
}
}, },
{ {
"pokeapi_id": 171, "pokeapi_id": 171,
@@ -3342,9 +3380,14 @@
"pokeapi_id": 226, "pokeapi_id": 226,
"pokemon_name": "Mantine", "pokemon_name": "Mantine",
"method": "surf", "method": "surf",
"encounter_rate": 5, "encounter_rate": null,
"min_level": 30, "min_level": 30,
"max_level": 40 "max_level": 40,
"conditions": {
"spring": 5,
"summer": 5,
"autumn": 5
}
}, },
{ {
"pokeapi_id": 224, "pokeapi_id": 224,
@@ -3374,9 +3417,12 @@
"pokeapi_id": 365, "pokeapi_id": 365,
"pokemon_name": "Walrein", "pokemon_name": "Walrein",
"method": "surf", "method": "surf",
"encounter_rate": 5, "encounter_rate": null,
"min_level": 30, "min_level": 30,
"max_level": 40 "max_level": 40,
"conditions": {
"winter": 5
}
} }
] ]
}, },
@@ -7642,33 +7688,53 @@
"pokeapi_id": 536, "pokeapi_id": 536,
"pokemon_name": "Palpitoad", "pokemon_name": "Palpitoad",
"method": "walk", "method": "walk",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"spring": 40,
"summer": 40,
"autumn": 40
}
}, },
{ {
"pokeapi_id": 588, "pokeapi_id": 588,
"pokemon_name": "Karrablast", "pokemon_name": "Karrablast",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 57, "min_level": 57,
"max_level": 57 "max_level": 57,
"conditions": {
"spring": 20,
"summer": 20,
"autumn": 20
}
}, },
{ {
"pokeapi_id": 618, "pokeapi_id": 618,
"pokemon_name": "Stunfisk", "pokemon_name": "Stunfisk",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 55, "min_level": 55,
"max_level": 56 "max_level": 56,
"conditions": {
"spring": 20,
"summer": 20,
"autumn": 20
}
}, },
{ {
"pokeapi_id": 453, "pokeapi_id": 453,
"pokemon_name": "Croagunk", "pokemon_name": "Croagunk",
"method": "walk", "method": "walk",
"encounter_rate": 15, "encounter_rate": null,
"min_level": 55, "min_level": 55,
"max_level": 56 "max_level": 56,
"conditions": {
"spring": 15,
"summer": 15,
"autumn": 15
}
}, },
{ {
"pokeapi_id": 340, "pokeapi_id": 340,
@@ -7690,9 +7756,14 @@
"pokeapi_id": 616, "pokeapi_id": 616,
"pokemon_name": "Shelmet", "pokemon_name": "Shelmet",
"method": "walk", "method": "walk",
"encounter_rate": 5, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 54 "max_level": 54,
"conditions": {
"spring": 5,
"summer": 5,
"autumn": 5
}
} }
] ]
}, },
@@ -7728,33 +7799,53 @@
"pokeapi_id": 536, "pokeapi_id": 536,
"pokemon_name": "Palpitoad", "pokemon_name": "Palpitoad",
"method": "walk", "method": "walk",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 57 "max_level": 57,
"conditions": {
"spring": 40,
"summer": 40,
"autumn": 40
}
}, },
{ {
"pokeapi_id": 588, "pokeapi_id": 588,
"pokemon_name": "Karrablast", "pokemon_name": "Karrablast",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 54, "min_level": 54,
"max_level": 54 "max_level": 54,
"conditions": {
"spring": 20,
"summer": 20,
"autumn": 20
}
}, },
{ {
"pokeapi_id": 618, "pokeapi_id": 618,
"pokemon_name": "Stunfisk", "pokemon_name": "Stunfisk",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 55, "min_level": 55,
"max_level": 56 "max_level": 56,
"conditions": {
"spring": 20,
"summer": 20,
"autumn": 20
}
}, },
{ {
"pokeapi_id": 453, "pokeapi_id": 453,
"pokemon_name": "Croagunk", "pokemon_name": "Croagunk",
"method": "walk", "method": "walk",
"encounter_rate": 15, "encounter_rate": null,
"min_level": 55, "min_level": 55,
"max_level": 56 "max_level": 56,
"conditions": {
"spring": 15,
"summer": 15,
"autumn": 15
}
}, },
{ {
"pokeapi_id": 340, "pokeapi_id": 340,
@@ -7776,9 +7867,14 @@
"pokeapi_id": 616, "pokeapi_id": 616,
"pokemon_name": "Shelmet", "pokemon_name": "Shelmet",
"method": "walk", "method": "walk",
"encounter_rate": 5, "encounter_rate": null,
"min_level": 57, "min_level": 57,
"max_level": 57 "max_level": 57,
"conditions": {
"spring": 5,
"summer": 5,
"autumn": 5
}
} }
] ]
}, },

View File

@@ -1486,7 +1486,7 @@
] ]
}, },
{ {
"name": "Relic Castle (Volcarona\u2019s Room and Room Outside)", "name": "Relic Castle (Volcaronas Room and Room Outside)",
"order": 30, "order": 30,
"encounters": [ "encounters": [
{ {
@@ -2971,25 +2971,40 @@
"pokeapi_id": 536, "pokeapi_id": 536,
"pokemon_name": "Palpitoad", "pokemon_name": "Palpitoad",
"method": "walk", "method": "walk",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 30, "min_level": 30,
"max_level": 33 "max_level": 33,
"conditions": {
"spring": 40,
"summer": 40,
"autumn": 40
}
}, },
{ {
"pokeapi_id": 616, "pokeapi_id": 616,
"pokemon_name": "Shelmet", "pokemon_name": "Shelmet",
"method": "walk", "method": "walk",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 30, "min_level": 30,
"max_level": 33 "max_level": 33,
"conditions": {
"spring": 40,
"summer": 40,
"autumn": 40
}
}, },
{ {
"pokeapi_id": 618, "pokeapi_id": 618,
"pokemon_name": "Stunfisk", "pokemon_name": "Stunfisk",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 31, "min_level": 31,
"max_level": 32 "max_level": 32,
"conditions": {
"spring": 20,
"summer": 20,
"autumn": 20
}
}, },
{ {
"pokeapi_id": 340, "pokeapi_id": 340,
@@ -3439,25 +3454,40 @@
"pokeapi_id": 536, "pokeapi_id": 536,
"pokemon_name": "Palpitoad", "pokemon_name": "Palpitoad",
"method": "walk", "method": "walk",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 30, "min_level": 30,
"max_level": 33 "max_level": 33,
"conditions": {
"spring": 40,
"summer": 40,
"autumn": 40
}
}, },
{ {
"pokeapi_id": 616, "pokeapi_id": 616,
"pokemon_name": "Shelmet", "pokemon_name": "Shelmet",
"method": "walk", "method": "walk",
"encounter_rate": 40, "encounter_rate": null,
"min_level": 30, "min_level": 30,
"max_level": 33 "max_level": 33,
"conditions": {
"spring": 40,
"summer": 40,
"autumn": 40
}
}, },
{ {
"pokeapi_id": 618, "pokeapi_id": 618,
"pokemon_name": "Stunfisk", "pokemon_name": "Stunfisk",
"method": "walk", "method": "walk",
"encounter_rate": 20, "encounter_rate": null,
"min_level": 31, "min_level": 31,
"max_level": 32 "max_level": 32,
"conditions": {
"spring": 20,
"summer": 20,
"autumn": 20
}
}, },
{ {
"pokeapi_id": 340, "pokeapi_id": 340,
@@ -5630,9 +5660,12 @@
"pokeapi_id": 446, "pokeapi_id": 446,
"pokemon_name": "Munchlax", "pokemon_name": "Munchlax",
"method": "trade", "method": "trade",
"encounter_rate": 100, "encounter_rate": null,
"min_level": 60, "min_level": 60,
"max_level": 60 "max_level": 60,
"conditions": {
"summer": 100
}
}, },
{ {
"pokeapi_id": 90, "pokeapi_id": 90,
@@ -5740,9 +5773,15 @@
"pokeapi_id": 320, "pokeapi_id": 320,
"pokemon_name": "Wailmer", "pokemon_name": "Wailmer",
"method": "surf", "method": "surf",
"encounter_rate": 90, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 60 "max_level": 60,
"conditions": {
"spring": 90,
"summer": 90,
"autumn": 90,
"winter": 60
}
}, },
{ {
"pokeapi_id": 223, "pokeapi_id": 223,
@@ -5772,25 +5811,36 @@
"pokeapi_id": 458, "pokeapi_id": 458,
"pokemon_name": "Mantyke", "pokemon_name": "Mantyke",
"method": "surf", "method": "surf",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 55 "max_level": 55,
"conditions": {
"spring": 30,
"summer": 30,
"autumn": 30
}
}, },
{ {
"pokeapi_id": 364, "pokeapi_id": 364,
"pokemon_name": "Sealeo", "pokemon_name": "Sealeo",
"method": "surf", "method": "surf",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 60 "max_level": 60,
"conditions": {
"winter": 30
}
}, },
{ {
"pokeapi_id": 363, "pokeapi_id": 363,
"pokemon_name": "Spheal", "pokemon_name": "Spheal",
"method": "surf", "method": "surf",
"encounter_rate": 30, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 55 "max_level": 55,
"conditions": {
"winter": 30
}
}, },
{ {
"pokeapi_id": 279, "pokeapi_id": 279,
@@ -5812,9 +5862,14 @@
"pokeapi_id": 226, "pokeapi_id": 226,
"pokemon_name": "Mantine", "pokemon_name": "Mantine",
"method": "surf", "method": "surf",
"encounter_rate": 5, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 60 "max_level": 60,
"conditions": {
"spring": 5,
"summer": 5,
"autumn": 5
}
}, },
{ {
"pokeapi_id": 224, "pokeapi_id": 224,
@@ -5836,9 +5891,12 @@
"pokeapi_id": 365, "pokeapi_id": 365,
"pokemon_name": "Walrein", "pokemon_name": "Walrein",
"method": "surf", "method": "surf",
"encounter_rate": 5, "encounter_rate": null,
"min_level": 25, "min_level": 25,
"max_level": 70 "max_level": 70,
"conditions": {
"winter": 5
}
} }
] ]
}, },

View File

@@ -192,6 +192,41 @@ async def upsert_routes(
return {row.name: row.id for row in result} return {row.name: row.id for row in result}
async def _upsert_single_encounter(
session: AsyncSession,
route_id: int,
pokemon_id: int,
game_id: int,
method: str,
encounter_rate: int,
min_level: int,
max_level: int,
condition: str = "",
) -> None:
stmt = (
insert(RouteEncounter)
.values(
route_id=route_id,
pokemon_id=pokemon_id,
game_id=game_id,
encounter_method=method,
encounter_rate=encounter_rate,
condition=condition,
min_level=min_level,
max_level=max_level,
)
.on_conflict_do_update(
constraint="uq_route_pokemon_method_game_condition",
set_={
"encounter_rate": encounter_rate,
"min_level": min_level,
"max_level": max_level,
},
)
)
await session.execute(stmt)
async def upsert_route_encounters( async def upsert_route_encounters(
session: AsyncSession, session: AsyncSession,
route_id: int, route_id: int,
@@ -207,27 +242,32 @@ async def upsert_route_encounters(
print(f" Warning: no pokemon_id for pokeapi_id {enc['pokeapi_id']}") print(f" Warning: no pokemon_id for pokeapi_id {enc['pokeapi_id']}")
continue continue
stmt = ( conditions = enc.get("conditions")
insert(RouteEncounter) if conditions:
.values( for condition_name, rate in conditions.items():
route_id=route_id, await _upsert_single_encounter(
pokemon_id=pokemon_id, session,
game_id=game_id, route_id,
encounter_method=enc["method"], pokemon_id,
encounter_rate=enc["encounter_rate"], game_id,
min_level=enc["min_level"], enc["method"],
max_level=enc["max_level"], rate,
enc["min_level"],
enc["max_level"],
condition=condition_name,
) )
.on_conflict_do_update( count += 1
constraint="uq_route_pokemon_method_game", else:
set_={ await _upsert_single_encounter(
"encounter_rate": enc["encounter_rate"], session,
"min_level": enc["min_level"], route_id,
"max_level": enc["max_level"], pokemon_id,
}, game_id,
enc["method"],
enc["encounter_rate"],
enc["min_level"],
enc["max_level"],
) )
)
await session.execute(stmt)
count += 1 count += 1
return count return count

View File

@@ -43,6 +43,14 @@ export const METHOD_CONFIG: Record<string, { label: string; color: string }> = {
label: 'Super Rod', label: 'Super Rod',
color: 'bg-indigo-100 text-indigo-800 dark:bg-indigo-900/40 dark:text-indigo-300', color: 'bg-indigo-100 text-indigo-800 dark:bg-indigo-900/40 dark:text-indigo-300',
}, },
horde: {
label: 'Horde',
color: 'bg-rose-100 text-rose-800 dark:bg-rose-900/40 dark:text-rose-300',
},
sos: {
label: 'SOS',
color: 'bg-violet-100 text-violet-800 dark:bg-violet-900/40 dark:text-violet-300',
},
} }
/** Display order for encounter method groups */ /** Display order for encounter method groups */
@@ -58,6 +66,8 @@ export const METHOD_ORDER = [
'old-rod', 'old-rod',
'good-rod', 'good-rod',
'super-rod', 'super-rod',
'horde',
'sos',
] ]
export function getMethodLabel(method: string): string { export function getMethodLabel(method: string): string {

View File

@@ -62,14 +62,90 @@ const statusOptions: {
const SPECIAL_METHODS = ['starter', 'gift', 'fossil', 'trade'] const SPECIAL_METHODS = ['starter', 'gift', 'fossil', 'trade']
function groupByMethod( interface GroupedEncounter {
pokemon: RouteEncounterDetail[] encounter: RouteEncounterDetail
): { method: string; pokemon: RouteEncounterDetail[] }[] { conditions: string[]
const groups = new Map<string, RouteEncounterDetail[]>() displayRate: number | null
}
function getUniqueConditions(pokemon: RouteEncounterDetail[]): string[] {
const conditions = new Set<string>()
for (const rp of pokemon) { for (const rp of pokemon) {
const list = groups.get(rp.encounterMethod) ?? [] if (rp.condition) conditions.add(rp.condition)
list.push(rp) }
groups.set(rp.encounterMethod, list) return [...conditions].sort()
}
function groupByMethod(
pokemon: RouteEncounterDetail[],
selectedCondition: string | null
): { method: string; pokemon: GroupedEncounter[] }[] {
const groups = new Map<string, Map<number, GroupedEncounter>>()
// Build a lookup: pokemonId+method -> condition -> rate
const rateByCondition = new Map<string, Map<string, number>>()
for (const rp of pokemon) {
if (rp.condition) {
const key = `${rp.pokemonId}:${rp.encounterMethod}`
let condMap = rateByCondition.get(key)
if (!condMap) {
condMap = new Map()
rateByCondition.set(key, condMap)
}
condMap.set(rp.condition, rp.encounterRate)
}
}
for (const rp of pokemon) {
// When a specific condition is selected, skip pokemon with 0% under that condition
if (selectedCondition) {
const key = `${rp.pokemonId}:${rp.encounterMethod}`
const condMap = rateByCondition.get(key)
if (condMap) {
const rate = condMap.get(selectedCondition)
if (rate === 0) continue
// Skip entries for other conditions (we only want one entry per pokemon)
if (rp.condition && rp.condition !== selectedCondition) continue
}
} else {
// "All" mode: skip 0% entries
if (rp.encounterRate === 0 && rp.condition) continue
}
let methodGroup = groups.get(rp.encounterMethod)
if (!methodGroup) {
methodGroup = new Map()
groups.set(rp.encounterMethod, methodGroup)
}
const existing = methodGroup.get(rp.pokemonId)
if (existing) {
if (rp.condition) existing.conditions.push(rp.condition)
} else {
// Determine the display rate
let displayRate: number | null = null
const isSpecial = SPECIAL_METHODS.includes(rp.encounterMethod)
if (!isSpecial) {
if (selectedCondition) {
const key = `${rp.pokemonId}:${rp.encounterMethod}`
const condMap = rateByCondition.get(key)
if (condMap) {
displayRate = condMap.get(selectedCondition) ?? null
} else {
displayRate = rp.encounterRate
}
} else if (!rp.condition) {
// "All" mode: show the base rate for non-condition entries
displayRate = rp.encounterRate
}
}
methodGroup.set(rp.pokemonId, {
encounter: rp,
conditions: rp.condition ? [rp.condition] : [],
displayRate,
})
}
} }
return [...groups.entries()] return [...groups.entries()]
.sort(([a], [b]) => { .sort(([a], [b]) => {
@@ -77,14 +153,25 @@ function groupByMethod(
const bi = METHOD_ORDER.indexOf(b) const bi = METHOD_ORDER.indexOf(b)
return (ai === -1 ? 999 : ai) - (bi === -1 ? 999 : bi) return (ai === -1 ? 999 : ai) - (bi === -1 ? 999 : bi)
}) })
.map(([method, pokemon]) => ({ method, pokemon })) .map(([method, pokemonMap]) => ({
method,
pokemon: [...pokemonMap.values()].sort((a, b) => (b.displayRate ?? 0) - (a.displayRate ?? 0)),
}))
} }
function pickRandomPokemon( function pickRandomPokemon(
pokemon: RouteEncounterDetail[], pokemon: RouteEncounterDetail[],
dupedIds?: Set<number> dupedIds?: Set<number>
): RouteEncounterDetail | null { ): RouteEncounterDetail | null {
const eligible = dupedIds ? pokemon.filter((rp) => !dupedIds.has(rp.pokemonId)) : pokemon // Deduplicate by pokemonId (conditions may create multiple entries)
const seen = new Set<number>()
const unique = pokemon.filter((rp) => {
if (rp.encounterRate === 0) return false
if (seen.has(rp.pokemonId)) return false
seen.add(rp.pokemonId)
return true
})
const eligible = dupedIds ? unique.filter((rp) => !dupedIds.has(rp.pokemonId)) : unique
if (eligible.length === 0) return null if (eligible.length === 0) return null
return eligible[Math.floor(Math.random() * eligible.length)] ?? null return eligible[Math.floor(Math.random() * eligible.length)] ?? null
} }
@@ -112,6 +199,7 @@ export function EncounterModal({
const [faintLevel, setFaintLevel] = useState<string>('') const [faintLevel, setFaintLevel] = useState<string>('')
const [deathCause, setDeathCause] = useState('') const [deathCause, setDeathCause] = useState('')
const [search, setSearch] = useState('') const [search, setSearch] = useState('')
const [selectedCondition, setSelectedCondition] = useState<string | null>(null)
const isEditing = !!existing const isEditing = !!existing
@@ -131,13 +219,18 @@ export function EncounterModal({
} }
}, [existing, routePokemon]) }, [existing, routePokemon])
const availableConditions = useMemo(
() => (routePokemon ? getUniqueConditions(routePokemon) : []),
[routePokemon]
)
const filteredPokemon = routePokemon?.filter((rp) => const filteredPokemon = routePokemon?.filter((rp) =>
rp.pokemon.name.toLowerCase().includes(search.toLowerCase()) rp.pokemon.name.toLowerCase().includes(search.toLowerCase())
) )
const groupedPokemon = useMemo( const groupedPokemon = useMemo(
() => (filteredPokemon ? groupByMethod(filteredPokemon) : []), () => (filteredPokemon ? groupByMethod(filteredPokemon, selectedCondition) : []),
[filteredPokemon] [filteredPokemon, selectedCondition]
) )
const hasMultipleGroups = groupedPokemon.length > 1 const hasMultipleGroups = groupedPokemon.length > 1
@@ -235,6 +328,35 @@ export function EncounterModal({
className="w-full px-3 py-1.5 mb-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500" className="w-full px-3 py-1.5 mb-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
/> />
)} )}
{availableConditions.length > 0 && (
<div className="flex flex-wrap gap-1 mb-2">
<button
type="button"
onClick={() => setSelectedCondition(null)}
className={`px-2.5 py-1 text-xs font-medium rounded-full border transition-colors ${
selectedCondition === null
? 'bg-purple-100 border-purple-300 text-purple-800 dark:bg-purple-900/40 dark:border-purple-600 dark:text-purple-300'
: 'border-gray-300 dark:border-gray-600 text-gray-600 dark:text-gray-400 hover:border-purple-300 dark:hover:border-purple-600'
}`}
>
All
</button>
{availableConditions.map((cond) => (
<button
key={cond}
type="button"
onClick={() => setSelectedCondition(cond)}
className={`px-2.5 py-1 text-xs font-medium rounded-full border transition-colors capitalize ${
selectedCondition === cond
? 'bg-purple-100 border-purple-300 text-purple-800 dark:bg-purple-900/40 dark:border-purple-600 dark:text-purple-300'
: 'border-gray-300 dark:border-gray-600 text-gray-600 dark:text-gray-400 hover:border-purple-300 dark:hover:border-purple-600'
}`}
>
{cond}
</button>
))}
</div>
)}
<div className="max-h-64 overflow-y-auto space-y-3"> <div className="max-h-64 overflow-y-auto space-y-3">
{groupedPokemon.map(({ method, pokemon }, groupIdx) => ( {groupedPokemon.map(({ method, pokemon }, groupIdx) => (
<div key={method}> <div key={method}>
@@ -247,18 +369,21 @@ export function EncounterModal({
</div> </div>
)} )}
<div className="grid grid-cols-3 gap-2"> <div className="grid grid-cols-3 gap-2">
{pokemon.map((rp) => { {pokemon.map(({ encounter: rp, conditions, displayRate }) => {
const isDuped = dupedPokemonIds?.has(rp.pokemonId) ?? false const isDuped = dupedPokemonIds?.has(rp.pokemonId) ?? false
const isSelected =
selectedPokemon?.pokemonId === rp.pokemonId &&
selectedPokemon?.encounterMethod === rp.encounterMethod
return ( return (
<button <button
key={rp.id} key={`${rp.encounterMethod}-${rp.pokemonId}`}
type="button" type="button"
onClick={() => !isDuped && setSelectedPokemon(rp)} onClick={() => !isDuped && setSelectedPokemon(rp)}
disabled={isDuped} disabled={isDuped}
className={`flex flex-col items-center p-2 rounded-lg border text-center transition-colors ${ className={`flex flex-col items-center p-2 rounded-lg border text-center transition-colors ${
isDuped isDuped
? 'opacity-40 cursor-not-allowed border-gray-200 dark:border-gray-700' ? 'opacity-40 cursor-not-allowed border-gray-200 dark:border-gray-700'
: selectedPokemon?.id === rp.id : isSelected
? 'border-blue-500 bg-blue-50 dark:bg-blue-900/30' ? 'border-blue-500 bg-blue-50 dark:bg-blue-900/30'
: 'border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600' : 'border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600'
}`} }`}
@@ -287,6 +412,18 @@ export function EncounterModal({
{!isDuped && SPECIAL_METHODS.includes(rp.encounterMethod) && ( {!isDuped && SPECIAL_METHODS.includes(rp.encounterMethod) && (
<EncounterMethodBadge method={rp.encounterMethod} /> <EncounterMethodBadge method={rp.encounterMethod} />
)} )}
{!isDuped && displayRate !== null && displayRate !== undefined && (
<span className="text-[10px] text-purple-500 dark:text-purple-400 font-medium">
{displayRate}%
</span>
)}
{!isDuped &&
selectedCondition === null &&
conditions.length > 0 && (
<span className="text-[10px] text-purple-500 dark:text-purple-400">
{conditions.join(', ')}
</span>
)}
{!isDuped && ( {!isDuped && (
<span className="text-[10px] text-gray-400"> <span className="text-[10px] text-gray-400">
Lv. {rp.minLevel} Lv. {rp.minLevel}

View File

@@ -63,6 +63,8 @@ export function AdminRouteDetail() {
? Math.max(...childRoutes.map((r) => r.order)) + 1 ? Math.max(...childRoutes.map((r) => r.order)) + 1
: (route?.order ?? 0) * 10 + 1 : (route?.order ?? 0) * 10 + 1
const hasConditions = encounters.some((e) => e.condition !== '')
const columns: Column<RouteEncounterDetail>[] = [ const columns: Column<RouteEncounterDetail>[] = [
{ {
header: 'Pokemon', header: 'Pokemon',
@@ -79,6 +81,14 @@ export function AdminRouteDetail() {
}, },
{ header: 'Method', accessor: (e) => e.encounterMethod }, { header: 'Method', accessor: (e) => e.encounterMethod },
{ header: 'Rate', accessor: (e) => `${e.encounterRate}%` }, { header: 'Rate', accessor: (e) => `${e.encounterRate}%` },
...(hasConditions
? [
{
header: 'Condition',
accessor: (e: RouteEncounterDetail) => e.condition || '\u2014',
} as Column<RouteEncounterDetail>,
]
: []),
{ {
header: 'Levels', header: 'Levels',
accessor: (e) => accessor: (e) =>

View File

@@ -68,6 +68,7 @@ export interface CreateRouteEncounterInput {
gameId: number gameId: number
encounterMethod: string encounterMethod: string
encounterRate: number encounterRate: number
condition?: string
minLevel: number minLevel: number
maxLevel: number maxLevel: number
} }
@@ -75,6 +76,7 @@ export interface CreateRouteEncounterInput {
export interface UpdateRouteEncounterInput { export interface UpdateRouteEncounterInput {
encounterMethod?: string encounterMethod?: string
encounterRate?: number encounterRate?: number
condition?: string
minLevel?: number minLevel?: number
maxLevel?: number maxLevel?: number
} }
@@ -128,6 +130,7 @@ export interface PokemonEncounterLocationItem {
routeName: string routeName: string
encounterMethod: string encounterMethod: string
encounterRate: number encounterRate: number
condition: string
minLevel: number minLevel: number
maxLevel: number maxLevel: number
} }

View File

@@ -56,6 +56,7 @@ export interface RouteEncounter {
gameId: number gameId: number
encounterMethod: string encounterMethod: string
encounterRate: number encounterRate: number
condition: string
minLevel: number minLevel: number
maxLevel: number maxLevel: number
} }

View File

@@ -84,8 +84,8 @@ ENCOUNTER_METHOD_MAP: dict[str, str] = {
"cave-spot": "walk", "cave-spot": "walk",
"bubble-spot": "surf", "bubble-spot": "surf",
"sand-spot": "walk", "sand-spot": "walk",
"horde": "walk", "horde": "horde",
"sos-encounter": "walk", "sos-encounter": "sos",
"ambush": "walk", "ambush": "walk",
# Seaweed / diving # Seaweed / diving
"diving": "surf", "diving": "surf",
@@ -105,7 +105,7 @@ ENCOUNTER_METHOD_MAP: dict[str, str] = {
"dust-cloud": "walk", "dust-cloud": "walk",
"hidden-grotto": "static", "hidden-grotto": "static",
"hidden-encounter": "walk", "hidden-encounter": "walk",
"horde-encounter": "walk", "horde-encounter": "horde",
"shaking-trees": "walk", "shaking-trees": "walk",
"shaking-ore-deposits": "walk", "shaking-ore-deposits": "walk",
"island-scan": "static", "island-scan": "static",

View File

@@ -13,16 +13,22 @@ class Encounter:
encounter_rate: int encounter_rate: int
min_level: int min_level: int
max_level: int max_level: int
conditions: dict[str, int] | None = None
def to_dict(self) -> dict: def to_dict(self) -> dict:
return { d: dict = {
"pokeapi_id": self.pokeapi_id, "pokeapi_id": self.pokeapi_id,
"pokemon_name": self.pokemon_name, "pokemon_name": self.pokemon_name,
"method": self.method, "method": self.method,
"encounter_rate": self.encounter_rate,
"min_level": self.min_level, "min_level": self.min_level,
"max_level": self.max_level, "max_level": self.max_level,
} }
if self.conditions:
d["encounter_rate"] = None
d["conditions"] = self.conditions
else:
d["encounter_rate"] = self.encounter_rate
return d
@dataclass @dataclass

View File

@@ -65,61 +65,75 @@ def parse_rate(value: str | None) -> int | None:
return None return None
def extract_encounter_rate(record: dict[str, Any], generation: int) -> int: def extract_encounter_data(
"""Extract a single encounter_rate from a PokeDB encounter record. record: dict[str, Any],
generation: int,
) -> tuple[int, dict[str, int] | None]:
"""Extract encounter rate and per-condition rates from a PokeDB record.
Flattens generation-specific rate variants into a single value. Returns (rate, conditions) where:
- rate is the max/overall rate (used for sorting and backward compat)
- conditions is a dict of {condition_name: rate} or None for flat rates
""" """
# Gen 1/3/6: rate_overall # Gen 1/3/6: rate_overall — flat rate, no conditions
rate_overall = parse_rate(record.get("rate_overall")) rate_overall = parse_rate(record.get("rate_overall"))
if rate_overall is not None: if rate_overall is not None:
return rate_overall return rate_overall, None
# Gen 2/4: time-of-day rates — take the max # Gen 2/4/7: time-of-day rates
time_rates = [ time_fields = {
parse_rate(record.get("rate_morning")), "morning": parse_rate(record.get("rate_morning")),
parse_rate(record.get("rate_day")), "day": parse_rate(record.get("rate_day")),
parse_rate(record.get("rate_night")), "night": parse_rate(record.get("rate_night")),
] }
time_rates = [r for r in time_rates if r is not None] time_conditions = {k: v for k, v in time_fields.items() if v is not None}
if time_rates: if time_conditions:
return max(time_rates) rate = max(time_conditions.values())
return rate, time_conditions
# Gen 5: seasonal rates — take the max # Gen 5: seasonal rates
season_rates = [ season_fields = {
parse_rate(record.get("rate_spring")), "spring": parse_rate(record.get("rate_spring")),
parse_rate(record.get("rate_summer")), "summer": parse_rate(record.get("rate_summer")),
parse_rate(record.get("rate_autumn")), "autumn": parse_rate(record.get("rate_autumn")),
parse_rate(record.get("rate_winter")), "winter": parse_rate(record.get("rate_winter")),
] }
season_rates = [r for r in season_rates if r is not None] season_conditions = {
if season_rates: k: v for k, v in season_fields.items() if v is not None
return max(season_rates) }
if season_conditions:
rate = max(season_conditions.values())
return rate, season_conditions
# Gen 8 Sw/Sh: weather rates — take the max # Gen 8 Sw/Sh: weather rates
weather_rates = [] weather_conditions: dict[str, int] = {}
for key, val in record.items(): for key, val in record.items():
if key.startswith("weather_") and key.endswith("_rate") and val: if key.startswith("weather_") and key.endswith("_rate") and val:
parsed = parse_rate(val) parsed = parse_rate(val)
if parsed is not None: if parsed is not None:
weather_rates.append(parsed) # "weather_clear_rate" -> "clear"
if weather_rates: condition_name = key[len("weather_"):-len("_rate")]
return max(weather_rates) weather_conditions[condition_name] = parsed
if weather_conditions:
rate = max(weather_conditions.values())
return rate, weather_conditions
# Gen 8 Legends Arceus: boolean conditions presence-based # Gen 8 Legends Arceus: boolean conditions presence-based
if record.get("during_any_time") or record.get("during_morning") or \ if (
record.get("during_day") or record.get("during_evening") or record.get("during_night"): record.get("during_any_time")
return 100 # Present under conditions or record.get("during_morning")
or record.get("during_day")
or record.get("during_evening")
or record.get("during_night")
):
return 100, None
# Gen 9 Sc/Vi: probability weights normalize # Gen 9 Sc/Vi: probability weights normalize
prob_overall = record.get("probability_overall") prob_overall = record.get("probability_overall")
if prob_overall: if prob_overall:
parsed = parse_rate(prob_overall) parsed = parse_rate(prob_overall)
if parsed is not None: if parsed is not None:
# These are spawn weights (e.g. "20", "300"), not percentages. return parsed, None
# We'll normalize them later during aggregation when we have
# all encounters for a location. For now, store the raw weight.
return parsed
# Check time-based probability variants # Check time-based probability variants
prob_rates = [ prob_rates = [
@@ -130,10 +144,10 @@ def extract_encounter_rate(record: dict[str, Any], generation: int) -> int:
] ]
prob_rates = [r for r in prob_rates if r is not None] prob_rates = [r for r in prob_rates if r is not None]
if prob_rates: if prob_rates:
return max(prob_rates) return max(prob_rates), None
# Fallback: gift/trade/static encounters with no rate # Fallback: gift/trade/static encounters with no rate
return 100 return 100, None
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -212,8 +226,8 @@ def process_encounters(
# Parse levels # Parse levels
min_level, max_level = parse_levels(record.get("levels")) min_level, max_level = parse_levels(record.get("levels"))
# Extract rate # Extract rate and conditions
encounter_rate = extract_encounter_rate(record, generation) encounter_rate, conditions = extract_encounter_data(record, generation)
# Location area # Location area
area_id = record.get("location_area_identifier", "") area_id = record.get("location_area_identifier", "")
@@ -227,6 +241,7 @@ def process_encounters(
encounter_rate=encounter_rate, encounter_rate=encounter_rate,
min_level=min_level, min_level=min_level,
max_level=max_level, max_level=max_level,
conditions=conditions,
) )
by_area.setdefault(area_id, []).append(enc) by_area.setdefault(area_id, []).append(enc)
@@ -234,10 +249,28 @@ def process_encounters(
return by_area return by_area
def _merge_conditions(
a: dict[str, int] | None,
b: dict[str, int] | None,
) -> dict[str, int] | None:
"""Merge two condition dicts by summing rates per key."""
if a is None and b is None:
return None
merged = dict(a or {})
for k, v in (b or {}).items():
merged[k] = merged.get(k, 0) + v
return merged
def _cap_conditions(conditions: dict[str, int]) -> dict[str, int]:
"""Cap each condition rate at 100."""
return {k: min(v, 100) for k, v in conditions.items()}
def aggregate_encounters(encounters: list[Encounter]) -> list[Encounter]: def aggregate_encounters(encounters: list[Encounter]) -> list[Encounter]:
"""Aggregate encounters by (pokeapi_id, method), merging level ranges and summing rates. """Aggregate encounters by (pokeapi_id, method), merging level ranges and summing rates.
Replicates the Go tool's aggregation logic. Preserves per-condition rates through aggregation.
""" """
key_type = tuple[int, str] key_type = tuple[int, str]
agg: dict[key_type, Encounter] = {} agg: dict[key_type, Encounter] = {}
@@ -250,8 +283,10 @@ def aggregate_encounters(encounters: list[Encounter]) -> list[Encounter]:
existing.encounter_rate += enc.encounter_rate existing.encounter_rate += enc.encounter_rate
existing.min_level = min(existing.min_level, enc.min_level) existing.min_level = min(existing.min_level, enc.min_level)
existing.max_level = max(existing.max_level, enc.max_level) existing.max_level = max(existing.max_level, enc.max_level)
existing.conditions = _merge_conditions(
existing.conditions, enc.conditions
)
else: else:
# Copy so we don't mutate the original
agg[k] = Encounter( agg[k] = Encounter(
pokeapi_id=enc.pokeapi_id, pokeapi_id=enc.pokeapi_id,
pokemon_name=enc.pokemon_name, pokemon_name=enc.pokemon_name,
@@ -259,6 +294,7 @@ def aggregate_encounters(encounters: list[Encounter]) -> list[Encounter]:
encounter_rate=enc.encounter_rate, encounter_rate=enc.encounter_rate,
min_level=enc.min_level, min_level=enc.min_level,
max_level=enc.max_level, max_level=enc.max_level,
conditions=dict(enc.conditions) if enc.conditions else None,
) )
order.append(k) order.append(k)
@@ -266,6 +302,9 @@ def aggregate_encounters(encounters: list[Encounter]) -> list[Encounter]:
for k in order: for k in order:
e = agg[k] e = agg[k]
e.encounter_rate = min(e.encounter_rate, 100) e.encounter_rate = min(e.encounter_rate, 100)
if e.conditions:
e.conditions = _cap_conditions(e.conditions)
e.encounter_rate = max(e.conditions.values())
result.append(e) result.append(e)
# Sort by rate descending, then name ascending # Sort by rate descending, then name ascending

322
tools/merge-conditions.py Normal file
View File

@@ -0,0 +1,322 @@
#!/usr/bin/env python3
"""Merge per-condition encounter rates from a fresh import into curated seed files.
Usage:
# From repo root (requires PokeDB cache):
python tools/merge-conditions.py --game heartgold
# Process all games that have conditions:
python tools/merge-conditions.py --all
# Dry run (print what would change, don't write):
python tools/merge-conditions.py --game heartgold --dry-run
"""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
# Add tools/import-pokedb to sys.path so we can import the library
REPO_ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(REPO_ROOT / "tools" / "import-pokedb"))
from import_pokedb.loader import load_pokedb_data, load_seed_config # noqa: E402
from import_pokedb.mappings import ( # noqa: E402
LocationMapper,
PokemonMapper,
build_version_map,
)
from import_pokedb.processing import ( # noqa: E402
build_routes,
filter_den_routes,
filter_encounters_for_game,
process_encounters,
)
from import_pokedb.output import merge_special_encounters, sort_routes # noqa: E402
SEEDS_DIR = REPO_ROOT / "backend" / "src" / "app" / "seeds"
DATA_DIR = SEEDS_DIR / "data"
# Games that have per-condition encounter rates
CONDITION_GAMES: dict[str, str] = {
# Gen 2: morning/day/night
"gold": "gold-silver",
"silver": "gold-silver",
"crystal": "crystal",
# Gen 4: morning/day/night
"heartgold": "heartgold-soulsilver",
"soulsilver": "heartgold-soulsilver",
"diamond": "diamond-pearl",
"pearl": "diamond-pearl",
"platinum": "platinum",
"brilliant-diamond": "brilliant-diamond-shining-pearl",
"shining-pearl": "brilliant-diamond-shining-pearl",
# Gen 5: spring/summer/autumn/winter
"black": "black-white",
"white": "black-white",
"black-2": "black-2-white-2",
"white-2": "black-2-white-2",
# Gen 6: horde encounters
"x": "x-y",
"y": "x-y",
# Gen 7: day/night + SOS
"sun": "sun-moon",
"moon": "sun-moon",
"ultra-sun": "ultra-sun-ultra-moon",
"ultra-moon": "ultra-sun-ultra-moon",
# Gen 8: weather
"sword": "sword-shield",
"shield": "sword-shield",
}
def normalize_route_name(name: str) -> str:
"""Normalize a route name for fuzzy matching."""
return name.lower().strip()
def build_fresh_lookup(
game_slug: str,
vg_key: str,
generation: int,
pokedb: object,
config: object,
pokemon_mapper: PokemonMapper,
location_mapper: LocationMapper,
) -> dict[str, dict[tuple[int, str], dict[str, int]]]:
"""Run the import pipeline and build a conditions lookup.
Returns: {normalized_route_name: {(pokeapi_id, method): conditions_dict}}
"""
game_encounters = filter_encounters_for_game(
pokedb.encounters, game_slug
)
if not game_encounters:
return {}
encounters_by_area = process_encounters(
game_encounters, generation, pokemon_mapper, location_mapper
)
routes = build_routes(encounters_by_area, location_mapper)
if vg_key == "sword-shield":
routes = filter_den_routes(routes)
routes = merge_special_encounters(
routes, config, vg_key, pokemon_mapper
)
routes = sort_routes(routes, config, vg_key)
lookup: dict[str, dict[tuple[int, str], dict[str, int]]] = {}
def index_route(route):
key = normalize_route_name(route.name)
enc_map: dict[tuple[int, str], dict[str, int]] = {}
for enc in route.encounters:
if enc.conditions:
enc_map[(enc.pokeapi_id, enc.method)] = enc.conditions
if enc_map:
lookup[key] = enc_map
for route in routes:
index_route(route)
for child in route.children:
index_route(child)
return lookup
def merge_conditions_into_seed(
seed_data: list[dict],
lookup: dict[str, dict[tuple[int, str], dict[str, int]]],
game_slug: str,
dry_run: bool = False,
) -> tuple[list[dict], int]:
"""Merge conditions from lookup into seed data, return (updated_data, count)."""
merged_count = 0
def process_route(route: dict) -> None:
nonlocal merged_count
route_key = normalize_route_name(route["name"])
route_lookup = lookup.get(route_key)
if route_lookup is None:
return
for enc in route.get("encounters", []):
key = (enc["pokeapi_id"], enc["method"])
conditions = route_lookup.get(key)
if conditions:
if dry_run:
print(
f" {route['name']}: "
f"{enc.get('pokemon_name', '?')} ({enc['method']}) "
f"-> {conditions}"
)
enc["conditions"] = conditions
enc["encounter_rate"] = None
merged_count += 1
for child in route.get("children", []):
process_route(child)
for route in seed_data:
process_route(route)
return seed_data, merged_count
def process_game(
game_slug: str,
pokedb,
config,
pokemon_mapper: PokemonMapper,
location_mapper: LocationMapper,
version_map: dict[str, str],
dry_run: bool = False,
) -> int:
"""Process a single game. Returns number of encounters merged."""
vg_key = CONDITION_GAMES.get(game_slug)
if vg_key is None:
print(f" Skipping {game_slug}: not a condition game")
return 0
# Find generation
vg_info = config.version_groups.get(vg_key)
if vg_info is None:
print(f" Warning: version group '{vg_key}' not found")
return 0
generation = vg_info.get("generation", 0)
# Build fresh import lookup
lookup = build_fresh_lookup(
game_slug,
vg_key,
generation,
pokedb,
config,
pokemon_mapper,
location_mapper,
)
if not lookup:
print(" No conditions found in fresh import")
return 0
total_conditions = sum(len(v) for v in lookup.values())
print(
f" Fresh import: {len(lookup)} routes with conditions, "
f"{total_conditions} encounter+condition pairs"
)
# Load existing seed file
seed_path = DATA_DIR / f"{game_slug}.json"
if not seed_path.exists():
print(f" Warning: seed file not found: {seed_path}")
return 0
with open(seed_path) as f:
seed_data = json.load(f)
# Merge
updated_data, merged_count = merge_conditions_into_seed(
seed_data, lookup, game_slug, dry_run=dry_run
)
if merged_count == 0:
print(" No encounters matched for merging")
return 0
print(f" Merged conditions into {merged_count} encounters")
if not dry_run:
with open(seed_path, "w") as f:
json.dump(updated_data, f, indent=2, ensure_ascii=False)
f.write("\n")
print(f" Wrote {seed_path}")
return merged_count
def main() -> None:
parser = argparse.ArgumentParser(
description="Merge per-condition encounter rates into seed files."
)
parser.add_argument(
"--game", type=str, help="Process a specific game slug"
)
parser.add_argument(
"--all",
action="store_true",
help="Process all games with conditions",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Print what would change without writing files",
)
parser.add_argument(
"--pokedb-dir",
type=Path,
default=None,
help="Path to PokeDB data directory",
)
args = parser.parse_args()
if not args.game and not args.all:
parser.error("Specify --game SLUG or --all")
pokedb_dir = args.pokedb_dir or (SEEDS_DIR / ".pokedb_cache")
print(f"PokeDB data: {pokedb_dir}")
print(f"Seed data: {DATA_DIR}")
print()
# Load PokeDB data
pokedb = load_pokedb_data(pokedb_dir)
print(pokedb.summary())
print()
# Load seed config
config = load_seed_config(SEEDS_DIR)
print(f"Loaded {len(config.version_groups)} version groups")
print()
# Build mappings
pokemon_json = DATA_DIR / "pokemon.json"
pokemon_mapper = PokemonMapper(pokemon_json, pokedb)
location_mapper = LocationMapper(pokedb)
version_map = build_version_map(pokedb, config.version_groups)
# Determine games to process
if args.game:
games = [args.game]
else:
games = list(CONDITION_GAMES.keys())
total_merged = 0
for game_slug in games:
print(f"\n--- {game_slug} ---")
count = process_game(
game_slug,
pokedb,
config,
pokemon_mapper,
location_mapper,
version_map,
dry_run=args.dry_run,
)
total_merged += count
print(f"\nTotal: {total_merged} encounters updated across {len(games)} games")
if args.dry_run:
print("(dry run — no files written)")
if __name__ == "__main__":
main()