# Architecture

```text
packaged first boot
user-provided Pokemon Red ROM
        |
        v
RomImporter + RomExtractor (Lua)
        |
        +--> private data/generated/*.lua
        +--> private assets/generated/**/*.png
        +--> private assets/generated/audio/programs.bin
        |
        v
LÖVE2D engine + ChipAudio
```

The importer validates the ROM SHA-1, decodes tables and graphics using
bundled address/name metadata, and writes a private cache. It releases the ROM
after import and does not copy it into the cache. Normal gameplay reads only
the generated files.

`tools/build_data.py` is a separate Python/Pillow developer path that writes
the same core data and graphics into the source tree for verification.

## Runtime layout

| Area | Files | Role |
| --- | --- | --- |
| import | `src/import/RomImporter.lua` | first-boot UI, ROM validation, cache ownership |
| | `src/import/RomExtractor.lua` | ROM tables, text, pictures, PNGs, audio programs |
| core | `src/core/Game.lua` | service owner: data, input, renderer, stack, save |
| | `src/core/Data.lua` | loads `data/generated/*`, resolves TEXT_* pointers |
| | `src/core/ChipAudio.lua` | streams ROM music programs and synthesizes SFX/cries |
| | `src/core/FixedStep.lua` | 60 Hz fixed-step loop |
| | `src/core/Input.lua` | GB button abstraction, per-step edge detection |
| | `src/core/StateStack.lua` | stack of states; top updates, draws bottom-up from the last opaque state |
| | `src/core/SaveData.lua` | Lua-serialized save in the LÖVE save dir |
| render | `src/render/Renderer.lua` | 160x144 canvas, integer nearest scaling |
| | `src/render/TileRenderer.lua` | one SpriteBatch per map (8x8 quads) + border-block ring |
| | `src/render/SpriteRenderer.lua` | variable-size anchored sprite sheets, 6-frame walkers and flipped right facing |
| | `src/render/Font.lua` | glyph rendering via charmap (greedy longest match) |
| | `src/render/TextBox.lua` | dialogue box: typewriter, `\n` line, `\v` scroll, `\f` page |
| | `src/render/Camera.lua`, `Transition.lua` | follow camera, warp fades |
| world | `src/world/Map.lua` | cell queries: walkable/grass/door/warp/sign (bottom-left-tile rule) |
| | `src/world/MapLoader.lua` | generated def -> runtime Map, cached |
| | `src/world/Player.lua`, `NPC.lua` | grid movement, walk animation, wander AI |
| | `src/world/Collision.lua` | tile + entity + bounds checks |
| | `src/world/Warp.lua` | arrive-on-door and walk-off-edge warp rules, LAST_MAP |
| | `src/world/Encounter.lua` | Gen 1 encounter rate + slot buckets |
| | `src/world/OverworldController.lua` | the overworld state: input, interactions, connections, encounters |
| script | `src/script/ScriptRunner.lua` | coroutine executor for command lists |
| | `src/script/Commands.lua` | show_text, flags, battles, warps, movement, objects... |
| | `src/script/Flags.lua` | named event flags in the save |
| pokemon | `src/pokemon/*` | instances, Gen 1 stat calc, growth curves, party |
| battle | `src/battle/BattleState.lua` | battle flow + menus + message queue |
| | `src/battle/Damage.lua` | Gen 1 damage/crit/accuracy formulas |
| | `src/battle/TypeChart.lua`, `TurnOrder.lua`, `Status.lua`, `MoveEffects.lua` | subsystems |
| | `src/battle/Experience.lua`, `Catching.lua`, `TrainerAI.lua` | exp/levels, Gen 1 catch algorithm, AI |
| | `src/battle/rulesets/` | `gen1_faithful` (default) vs `modern_clean` |
| ui | `src/ui/*` | start menu, generic menu, yes/no box, party/bag lists |
| | `tools/save-editor/` | Save editor: shipped in every build, opened from the launcher's Edit button or standalone with `love . --editor` |

## Map scripts

Map-specific behavior lives in `data/scripts/<map>.lua`, keyed by the
TEXT_* constants from the map's object events. The engine dispatches a
talk interaction to (in order):

1. a hand-ported script in `data/scripts/` (`{ talk = { TEXT_X = {...} } }`),
2. the generic trainer path (object has trainer args from `object_event`),
3. the extracted plain text via the map's text pointer table.

Scripts are arrays of `{ "command", args... }` rows executed by a
coroutine so `show_text`, `ask`, `start_battle`, `warp`, `wait` block
naturally. Every hand-ported script cites its pokered source file.

## Coordinates

- **block**: 32x32 px, the unit of `.blk` layouts (`map.width/height`)
- **cell**: 16x16 px walk grid, the unit of all object/warp coordinates
- **tile**: 8x8 px graphics; a cell is 2x2 tiles, a block 4x4

A cell's behavior (collision, grass, door, warp tile) is decided by its
bottom-left 8x8 tile, matching the original engine's "tile at the
sprite's feet" checks.

## Verification

- `luajit tests/run_tests.lua` - headless behavior suite over real
  generated data (collision, warps, text, stats, damage, growth, type
  chart, encounters, a full scripted battle, save round-trip) using a
  `love` API stub.
- `luajit tests/run_save_editor_tests.lua` (plus the task-specific suites)
  - save editor pure logic and panel click tests.
- `POKEPORT_AUTOPILOT=1 love .` - scripted end-to-end run (walk Pallet
  Town, read the sign, enter Oak's Lab, take a starter, beat the rival,
  exit, cross into Route 1, win a wild battle) that captures screenshots.
- `POKEPORT_DRIVER=tests/drivers/audio_runtime_test.lua love .` - imports
  and queues title music, a sound effect, and a Pokemon cry.
