chore: AGENTS.md updated
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
# slay-the-spire-like
|
||||
|
||||
A Slay the Spire + Backpack Heroes hybrid roguelike sample. Players explore a point-crawl map, manage a tetris-style grid inventory, and fight enemies using cards generated from their equipment.
|
||||
|
||||
## Game Design Docs
|
||||
|
||||
Design docs are in the markdown files at this level:
|
||||
- `01-overview.md` — core game concept, zones, encounter structure, combat rules, buff/debuff system
|
||||
- `02-fighter.md` — Fighter class items (weapons, armor, tools, consumables, relics)
|
||||
- `03-desert.md` — Desert zone enemies (minions, elites, boss)
|
||||
- `data/rules.md` — combat state machine, turn order, effect timing rules
|
||||
|
||||
## Module Structure
|
||||
|
||||
This is **not** a `GameModule` yet — there is no `createInitialState`/`start`/`registry` wired up to `createGameHost`. The code is a library of subsystems that can be composed into a game module.
|
||||
|
||||
### Subsystems
|
||||
|
||||
| Directory | Purpose | Key exports |
|
||||
|-----------|---------|-------------|
|
||||
| `progress/` | Run state, player HP/gold, inventory management, map progression | `createRunState`, `moveToNode`, `resolveEncounter`, `damagePlayer`, `healPlayer`, `addItemFromCsv`, `removeItem`, `getReachableChildren` |
|
||||
| `map/` | Point-crawl map generation and traversal | `generatePointCrawlMap`, `getNode`, `getChildren`, `getParents`, `hasPath`, `findAllPaths` |
|
||||
| `grid-inventory/` | Tetris-style grid placement (place, move, rotate, flip items) | `createGridInventory`, `placeItem`, `removeItem`, `moveItem`, `rotateItem`, `flipItem`, `validatePlacement`, `getAdjacentItems` |
|
||||
| `deck/` | Card/deck system (draw pile, hand, discard, exhaust) | `generateDeckFromInventory`, `createStatusCard`, `createDeckRegions`, `createPlayerDeck` |
|
||||
| `data/` | CSV game data loaded via `inline-schema/csv-loader`. `.d.ts` files are auto-generated by the csv-loader plugin — do not edit by hand. | `heroItemFighter1Data`, `encounterDesertData`, `enemyDesertData`, `enemyIntentDesertData`, `effectDesertData`, `statusCardDesertData` |
|
||||
| `dialogue/` | Yarn Spinner dialogue files (placeholder). Loaded via `yarn-spinner-loader`, a local peer dependency at `../yarn-spinner-loader` (like `inline-schema`, it can be changed and published if needed). | `encounters` yarnproject |
|
||||
| `utils/` | Shape parsing and collision math | `parseShapeString`, `checkCollision`, `checkBounds`, `transformShape`, `rotateTransform`, `flipXTransform`, `flipYTransform` |
|
||||
|
||||
### Data flow
|
||||
|
||||
```
|
||||
CSV files (data/)
|
||||
→ inline-schema/csv-loader → typed JS objects (e.g. HeroItemFighter1)
|
||||
→ parseShapeString() converts shape strings → ParsedShape
|
||||
→ GridInventory<GameItemMeta> holds placed items
|
||||
→ generateDeckFromInventory() generates cards per occupied cell
|
||||
```
|
||||
|
||||
### Key types
|
||||
|
||||
- **`RunState`** — top-level state: seed, map, player, inventory, currentNodeId, encounter state, resolved set. Designed for `MutableSignal.produce()` mutation.
|
||||
- **`GridInventory<TMeta>`** — `items: Map<string, InventoryItem<TMeta>>` + `occupiedCells: Set<CellKey>` for O(1) collision. Mutated directly inside `.produce()`.
|
||||
- **`InventoryItem<TMeta>`** — id, shape (ParsedShape), transform (Transform2D), meta. Shape + transform determines which cells are occupied.
|
||||
- **`GameCard`** — a `Part<GameCardMeta>` bridging inventory items to the deck system. `sourceItemId` links back to the inventory item; `null` for status cards.
|
||||
- **`PointCrawlMap`** — layered DAG: 10 layers (start → wild×2 → settlement → wild×2 → settlement → wild×2 → end). Wild = 3 nodes, Settlement = 4 nodes.
|
||||
- **`MapNode`** — id, type (MapNodeType enum), childIds, optional encounter data from CSV.
|
||||
|
||||
### Map generation
|
||||
|
||||
`generatePointCrawlMap(seed?)` produces a deterministic map:
|
||||
- 10 layers: Start → Wild(3) → Wild(3) → Settlement(4) → Wild(3) → Wild(3) → Settlement(4) → Wild(3) → Wild(3) → End
|
||||
- Settlement layers guarantee ≥1 camp, ≥1 shop, ≥1 curio (4th slot random)
|
||||
- Wild pair types are optimized to minimize same-type repetition
|
||||
- Edge patterns avoid crossings: Start→all wild, Wild→Wild 1:1, Wild↔Settlement 3:4 or 4:3, Wild→all End
|
||||
|
||||
### Shape system
|
||||
|
||||
Items have shapes defined as movement strings parsed by `parseShapeString`:
|
||||
- `o` = origin cell, `n/s/e/w` = move + fill, `r` = return to previous position
|
||||
- Example: `"oesw"` = 2×2 block (origin, east, south, west = full square)
|
||||
- Example: `"oe"` = 1×2 horizontal
|
||||
- Example: `"onrersrw"` = cross/X shape
|
||||
|
||||
Shapes are positioned via `Transform2D` (offset, rotation, flipX, flipY) and validated against the 6×4 grid.
|
||||
|
||||
### Grid inventory
|
||||
|
||||
All mutation functions (`placeItem`, `removeItem`, `moveItem`, `rotateItem`, `flipItem`) mutate the `GridInventory` **directly** — they must be called inside `produce()` callbacks. `validatePlacement` checks bounds + collisions before placement.
|
||||
|
||||
### Card generation
|
||||
|
||||
`generateDeckFromInventory(inventory)` creates one card per occupied cell in each item's shape. Cards carry `GameCardMeta` linking back to the source item and cell position. Status cards (wound, venom, etc.) are created separately via `createStatusCard`.
|
||||
|
||||
## CSV data format
|
||||
|
||||
All CSVs use `inline-schema` typed headers. The first row is a comment header, the second row is the schema row with types and references:
|
||||
- `'energy'|'uses'` — union type
|
||||
- `@enemyDesert` — foreign key reference to another CSV
|
||||
- `[effect: @effectDesert; number][]` — array of structured references
|
||||
|
||||
### heroItemFighter1.csv columns
|
||||
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| type | `'weapon'|'armor'|'consumable'|'tool'` | |
|
||||
| name | string | Display name (Chinese) |
|
||||
| shape | string | Movement string for `parseShapeString` |
|
||||
| costType | `'energy'|'uses'` | Energy = per-turn cost; Uses = limited uses |
|
||||
| costCount | int | Cost amount |
|
||||
| targetType | `'single'|'none'` | |
|
||||
| price | int | Shop price |
|
||||
| desc | string | Ability description (Chinese) |
|
||||
| effects | `['self'|'target'|'all'|'random'; @effectDesert; number][]` | Effect references |
|
||||
|
||||
## Conventions
|
||||
|
||||
- Chinese is used for all user-facing strings (item names, error messages, effect descriptions)
|
||||
- Discriminated union result types: `{ success: true } | { success: false, reason: string }`
|
||||
- Mutation functions mutate state directly (inside `produce()`); validation is separate
|
||||
- `Map` and `Set` are used in `GridInventory` and `PointCrawlMap` (not plain objects) — requires careful handling with `mutative` since it drafts Maps/Sets differently than plain objects
|
||||
- Starter items defined in `progress/index.ts`: `['治疗药剂', '绷带', '水袋', '短刀', '剑']`
|
||||
- Default player stats: 50 HP, 50 gold, 6×4 inventory
|
||||
Reference in New Issue
Block a user