42 lines
4.4 KiB
Markdown
42 lines
4.4 KiB
Markdown
# AGENTS.md
|
|
|
|
## Project
|
|
"Light & Dark" — a 2D light/shade platformer prototype (Python 3.10 + pygame).
|
|
|
|
- Main game: `light_and_dark/main.py` (single large file, ~9500 lines, no tests)
|
|
- Level editor: `light_and_dark/level_editor_v43.html` (standalone HTML/JS, no build step)
|
|
- Levels: `light_and_dark/data/world/rooms/room_0N.json`
|
|
- Data/config: `light_and_dark/data/config/*.json`, `data/items/*.json`, `data/objects/*.json`, `data/enemies/*.json`
|
|
|
|
## Standard session start
|
|
1. `git status` + `git log --oneline -5` to see uncommitted/untracked work (currently `room_08`/`room_09`/`room_boss_fly_refight` and the `Boss_fly_token.png` sprite are untracked/WIP).
|
|
2. Read the specific file(s) the request touches before editing; follow existing style.
|
|
3. After edits, always verify (see below).
|
|
|
|
## Verification
|
|
- `python -m py_compile light_and_dark/main.py` (must pass after any edit)
|
|
- Validate touched JSON: `python -c "import json; json.load(open('<path>', encoding='utf-8'))"`
|
|
- To confirm the game boots: run `python light_and_dark/main.py` with a short timeout; no traceback in the first lines = OK (the window blocks, so it will time out).
|
|
|
|
## Pitfalls / conventions
|
|
- **Python booleans are `True`/`False`** — never `true`/`false` in main.py (this once crashed startup). JSON files use lowercase `true`.
|
|
- Don't add code comments unless asked. Match surrounding style.
|
|
- Rooms load through `normalize_room_json` + `Room` (main.py) which rewrite JSON into the game format; keep new room JSON keys mirrored there (e.g. trigger/ability-door flags).
|
|
|
|
## Key systems (in main.py unless noted)
|
|
- **Saves**: lantern-only. `save_game` / `apply_save_data`. Quest progress, broken doors, defeated bosses persist ONLY on lantern interaction.
|
|
- **Trigger walls** (`trigger_wall_blocking`): meta keys `quest`, `quest_complete`, `quest_turned_in`, `lock_when_pass` (appears once walked through), `open_on_miniboss_dead`, `open_on_boss_dead` (released once the room's boss is slain; also hides/inerts moving platforms while the boss lives).
|
|
- **Exits**: `"requires_boss_dead": true` hides the exit and disables it while the room's boss is alive (used for boss doors and refight rooms). The editor round-trips this flag (checkbox in the Exit properties).
|
|
- **Bosses** (`BOSS_DEFS_DEFAULT`): `boss_id -> {name, room, refight_room, token_name, token_sprite}`. `boss_01` = Gloom Warden (legacy `room_boss_01`, not in the current build); `boss_02` = **Sky Terror** (flying, `room_08`, refight `room_boss_fly_refight`). A boss drops its token in its world room (not during a refight); tokens feed the Boss Diary (`boss_diary`) and the Combat Master upgrades them for the Hard fight. Token art is per-boss via `boss_token_sprite(boss_id)`.
|
|
- **Boss aggro**: once the player first enters a boss's `detection_range` in Light form, `Enemy.boss_engaged` latches and the boss chases forever (ignores range and Shade).
|
|
- **Flying boss** (`behavior: "flyer"`, config `enemy_types.json` → `boss_flyer.flyer`): hovers/bobs, slam-dives (shockwave `poison=False`), summons expiring minions, AOE rings, homing shots. Casts `_flyer_max_casts()` skills per slam-cycle — normal 1 (2 at `phase2_hp_pct`), hard 2 (3 at `hard_phase3_hp_pct`); `self.flyer_hard` is set by `start_refight(..., "hard")`.
|
|
- **Ability doors** (`AbilityDoor`): `kind` = `"ranged"` (shot open, persists in `broken_doors`) or `"shade"` (solid in light, passable in shade + a `shade_door_grace` window after form switch; ejected to nearest edge after grace).
|
|
- **Quests** (`QUEST_DEFS_DEFAULT`): objective `"shade": True` counts kills only in shade form. Rewards via `guaranteed_item` (one-time) or `pool` (repeatable). Items by `item_id` (e.g. `necklace_placeholder` = "Plain Necklace").
|
|
- **Minibosses**: stay defeated per room (`DEFEATED_MINIBOSSES`) until a lantern save in a different room, then respawn.
|
|
- **Enemies**: insta-die on hazard contact with no drops (`hazard_kill` flag); normal enemies respawn on room re-entry.
|
|
- **Chests** (`RewardChest`): spawn coin `CoinPickup`s above the chest top (not center, which clips into the platform).
|
|
- **Shade-kills quest** (`first_kills_shade` in room 6): interactable/prompted only while in shade form (`QuestManager.requires_shade`).
|
|
|
|
## Editor (level_editor_v43.html)
|
|
- Link lines: `drawLinkLinesForCanvas` — exits with a `spawn_id` missing in the target room draw NO line (avoids stale-coordinate artifacts).
|
|
- Keyboard shortcuts live in the `window keydown` handler: V/P/E/N/O switch tools, F fit, Esc deselect, Ctrl+Z/Y/C/X/V/D. |