# Context — star-survivor (Incanto)

## Project Overview

**Hollow Horde** — a top-down SURVIVORS-LIKE (Vampire Survivors genre), the
clone-and-modify starter for the survivor/roguelite arena. Move a knight with
WASD/arrows; he AUTO-ATTACKS the nearest enemy. Goblins and faster ghosts pour in
from every side and grow tougher over time; cut them down, vacuum up the XP shards
they drop, and on each LEVEL-UP pick one of three upgrades (damage / fire-rate /
extra bolt / pierce / move-speed / magnet / max-HP). Survive 90 seconds to WIN; let
the horde drain your HP and you fall.

The horde is BUILT-IN gameplay wired in `game.scene.json`; the SURVIVOR feel is the
custom TypeScript.

## Tech Stack

_Exact versions are in `package.json`._

- **Engine**: `incanto` + `incanto/2d` (createGame2D; CharacterBody2D/Area2D,
  AnimatedSprite2D, ColorRect2D, Camera2D, AudioPlayer) + `incanto/gameplay`
  (auto-registered Spawner, Chase, Health, DamageOnContact, ScoreKeeper, …).
- **Art**: built-in animated sheets `medieval-knight` (player), `goblin` + `ghost`
  (enemies), bundler-imported in `App.tsx` and injected into the scene asset urls.
  Arena/bolts/XP shards are `ColorRect2D`.
- **Audio**: procedural SFX presets (shoot/coin/hurt/win/lose). HUD is DOM.
- **Build/Lang**: Vite, TypeScript. **Headless verify**: `incanto/test` (`runScript`).

## Critical Memory

- READ THE SKILLS FIRST: `node_modules/incanto/skills/` —
  `incanto-gameplay-behaviors.md`, `incanto-building-2d-games.md`,
  `incanto-physics-and-input.md`.
- TOP-DOWN: `physics.gravity` is `[0,0]`. The custom `Survivor` integrates its own
  velocity (`moveAndSlide`); enemies move via built-in `Chase` (moveParent).
- ONE SCRIPT PER NODE: the player node's script is `Survivor`, so its `Health` lives
  on a child `Vitals` node — `DamageOnContact` finds it by searching the body's
  descendants. Each enemy's `EnemyDrop`/`EnemyFace` are child scripts reading the
  parent (whose script is `Health`).
- HORDE: the engine clone path (`SpawnSource`) is internal, so two built-in
  `Spawner`s clone hidden goblin/ghost templates and emit `spawned(clone)`; the
  custom `Director` repositions each clone onto a RING around the player and ramps
  spawn rate + enemy HP/speed over time. Spawners sit at the ORIGIN (clone local pos
  = world).
- LEVEL-UP PAUSE: `Survivor` freezes the world with `this.engine.stop()` while the
  DOM upgrade overlay is shown, and `start()`s on pick. Headless (`verify`) drives
  `engine.step()` (not the loop) and auto-applies an upgrade (guarded by `hasDom`).
- MANY ENTITIES: bolts + XP shards are spawned `ColorRect2D`s MANAGED by `Survivor`
  (moved + AABB-tested vs the `enemy`/`xp` groups each frame) — cloned nodes can't use
  scene `connections`, so group + AABB is the pattern. WIN = `Survivor` sets the score
  to `scoreToWin` at the survive-timer → `ScoreKeeper` `won`; LOSE = player `Health`
  `died` → `loseLife` → `lost`.
- `incanto/assets/*` dev imports need an `'incanto/assets'` alias in `vite.config.ts`.
- `this.engine` is unavailable in `onReady` during `loadScene`; defer scene reads.
- Node uids omitted (loader-generated). `window.game` exposes the Game handle.
