import { CrystalAssets } from "../src/game/rom/assets"; import { generatePartyMon } from "../src/game/party/pokemon"; import { GameWorld } from "../src/game/world/world"; export type BattleScenario = "win" | "loss" | "unsupported" | "catch"; /** Test-only encounters on an existing floor: production maps stay gated. * Everything after selection runs through the ROM's real wild-battle script. */ export function battleFixtureWorld(assets: CrystalAssets, scenario: BattleScenario = "win") { assets.grassCollisions = () => [assets.collision(assets.map("24:7"), 3, 2)]; assets.grassWildMons = (id) => id === "24:7" ? { rates: [256, 256, 256], slots: [0, 1, 2].map(() => Array.from({ length: 7 }, () => ({ species: 16, level: 3 }))), } : undefined; let seed = 1; const world = new GameWorld(assets, () => new Date("2026-09-22T12:00:00"), () => { seed = (seed * 1103515245 + 12345) >>> 0; return (seed >>> 16) & 255; }); const mon = generatePartyMon(assets.pokemon, { species: 155, level: 5, item: 0, otId: world.playerId, ot: "CHRIS", dvs: 0x823b, caughtData: 0, }); mon.exp = 178; // One battle away from level 6 and Smokescreen. if (scenario === "loss") mon.hp = 1; if (scenario === "unsupported") { mon.moves = [144, 0, 0, 0]; // Transform is explicitly refused. mon.pp = [10, 0, 0, 0]; } world.state.party.push(mon); if (scenario === "catch") world.state.bag.balls = [{ item: 5, quantity: 10 }]; // POKé BALL world.state.pokedex.seen.add(155); world.state.pokedex.caught.add(155); world.state.money = 3001; return world; } export const ENCOUNTER_WALK = ["up", "right", "right", "right", "right"] as const;