# Structure — star-survivor (Incanto)

## `index.html`

Full-window page: loading overlay, one `#game` canvas, the DOM HUD (`#hp-track`/
`#hp-fill`/`#hp-text`, `#xp-track`/`#xp-fill`, `#level-text`, `#kills-text`,
`#timer-text`, `#hint`), the LEVEL-UP overlay (`#levelup` → `#levelup-level` +
`#levelup-cards`, populated by `Survivor`), a center `#banner`, and the iframe
GAME_SIZE script. `Survivor` writes all of these (guarded for headless).

## `src/game.scene.json`

The arena + horde, mostly BUILT-INS:

- header — `background`, `viewport` (`design [960,540]`, `fit: expand`),
  `physics.gravity [0,0]` (top-down), `move` input (WASD+arrows), and the
  `knight`/`goblin`/`ghost` spritesheet `assets` (urls injected in `App.tsx`).
- `Game` root — `ScoreKeeper` (`scoreToWin 1`, `lives 1`).
- `Arena`/`ArenaPad`/`ArenaRing*` — dark `ColorRect2D` floor + a centre marker.
- `Player` — `CharacterBody2D` (group `player`) + `Survivor`; children `Skin`
  (`AnimatedSprite2D` knight) + `Vitals` (`Health`, the body's HP via a child).
- `GoblinSpawner` / `GhostSpawner` — built-in `Spawner` (interval, max, `total 0`
  = infinite) at the ORIGIN, each with a hidden `visible:false` template child:
  - `GoblinTemplate` / `GhostTemplate` — `CharacterBody2D` (group `enemy`) +
    `Health(freeOnDeath)`; children `Skin` (`AnimatedSprite2D`), `AI` (`Chase`
    target `/root/Player`, `moveParent`), `Face` (`EnemyFace`), `Hit` (`Area2D` +
    `DamageOnContact` targetGroup `player`), `Drop` (`EnemyDrop`).
- `Director` — custom: listens to both spawners' `spawned`, ring-scatters each
  clone around the player, ramps interval down + enemy HP/speed up.
- `Camera` — `Camera2D` + `FollowCamera` (target player).
- `SfxShoot/Xp/Hurt/Win/Lose` — `AudioPlayer` presets.
- `connections` — `Player/Vitals died → /root loseLife`; `damaged → SfxHurt`;
  root `won/lost → SfxWin/SfxLose`. (Auto-attack/XP/level/win-trigger are in code.)

## `src/behaviors.ts`

- `Survivor` — the player brain: top-down move + facing/anim, AUTO-ATTACK (managed
  bolt list, AABB vs `enemy`), XP pickup + magnet (AABB vs `xp`), level-up (pause +
  3 random upgrade cards + `engine.stop/start`), survive-timer win, and the DOM HUD.
- `Director` — ring-spawn around the player + time escalation (see above).
- `EnemyDrop` — on the parent enemy's `died`, drop an XP shard (`ColorRect2D` in
  group `xp` with an `_xp` value) + `Survivor.addKill()`.
- `EnemyFace` — flip the enemy sprite toward travel + play `move`.

## `src/main.tsx` and `src/App.tsx`

Inject the knight/goblin/ghost urls, `createGame2D({ canvas, scene, behaviors })`,
optional music, remove the loader, expose `window.game`.

## `vite.config.ts`

Aliases `incanto`, `incanto/2d`, AND `incanto/assets` (the last is REQUIRED for the
built-in sprite imports to resolve in `bun run dev`).

## `verify.ts`

Headless `runScript` proof: move, auto-attack kills an enemy → XP shard drop + kill
tally, XP → level-up, survive-timer → `won`, HP→0 → `lost`. Run with `bun run verify`.

## `docs/project-2d-rules.md`

The vibe-coding rules for this survivor template (one-script-per-node, horde-without-
clone-API, engine pause, AABB-in-one-owner, the `incanto/assets` vite alias, …).
