Phase 0: Project setup for agent-driven development

- Phaser 3 + bitECS 0.4 + TypeScript + Vite stack
- BootScene with title screen
- 6 cursor rules (project context, agent workflow, ECS, chemistry, Phaser, data)
- Vitest configured with happy-dom
- GDD, engine analysis, implementation plan, progress tracking

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Денис Шкабатур
2026-02-12 11:59:41 +03:00
commit 10bd67c951
20 changed files with 3256 additions and 0 deletions

View File

@@ -0,0 +1,30 @@
---
description: Agent development workflow — visual feedback, testing, progress tracking
alwaysApply: true
---
# Agent Workflow
## Visual Verification
After ANY visual change, verify via Playwright browser tools:
1. Ensure dev server is running (`npm run dev`)
2. Navigate to `http://localhost:5173`
3. Take screenshot to see result
4. Check browser console for errors
5. Fix issues before moving on
## Testing Strategy
- **Chemistry, ecology, math** — write vitest tests FIRST, then implement
- **Visual/rendering** — verify via Playwright screenshots
- Run `npm run test:run` after logic changes
## After Completing a Task
1. Verify it works (test or screenshot)
2. Update `PROGRESS.md` — mark task complete, add date
3. Check `IMPLEMENTATION-PLAN.md` for what's next
## Code Standards
- TypeScript strict mode — no `any`, no unsafe `as` casts
- Pure functions for ECS systems
- Descriptive variable names (not `x`, `tmp`, `data`)
- Comments explain WHY, not WHAT

View File

@@ -0,0 +1,40 @@
---
description: Chemistry engine — real science constraints and data patterns
globs: src/chemistry/**/*.ts
alwaysApply: false
---
# Chemistry Engine Rules
## Scientific Accuracy
All chemical data must be **real** (simplified, never fabricated):
- Element properties: atomic number, mass, electronegativity, group, period
- Reactions: real products, real conditions (temperature, catalyst)
- Failed reactions return a **reason**: "Requires catalyst", "Insufficient energy"
## Element Interface
```typescript
interface Element {
symbol: string; // "Na"
name: string; // "Sodium"
atomicNumber: number; // 11
atomicMass: number; // 22.99
electronegativity: number;
category: ElementCategory;
state: MatterState; // at room temperature
color: string; // hex for rendering
}
```
## Reaction Lookup
Reactions indexed by **sorted reactant key** for O(1) lookup:
```typescript
// "Cl+Na" → { products: [{ symbol: "NaCl", ... }], ... }
function reactionKey(symbols: string[]): string {
return [...symbols].sort().join('+');
}
```
## Every Reaction Includes
- `description`: human-readable explanation of the science
- `failureReason`: if conditions not met, explain WHY (this is the teaching mechanism)

View File

@@ -0,0 +1,36 @@
---
description: Game data JSON schema and validation conventions
globs: src/data/**/*.json
alwaysApply: false
---
# Data File Conventions
## General Rules
- Every entity has a unique `id` field (string)
- Cross-references use `id` strings, never array indices
- Every entry has a `description` field (feeds the in-game Codex)
- Data must be valid JSON — no comments, no trailing commas
## elements.json
Array of Element objects. Keyed by `symbol`. All values from real periodic table.
```json
{
"symbol": "Na",
"name": "Sodium",
"nameRu": "Натрий",
"atomicNumber": 11,
"atomicMass": 22.99,
"electronegativity": 0.93,
"category": "alkali-metal",
"state": "solid",
"color": "#c8c8c8",
"description": "Мягкий щелочной металл. Бурно реагирует с водой."
}
```
## reactions.json
Array of Reaction objects. Include science explanation and failure reasons.
## Validation
All JSON is loaded and type-checked at boot. Schema mismatches crash with clear errors — never silently ignore bad data.

View File

@@ -0,0 +1,65 @@
---
description: bitECS 0.4 component and system conventions
globs: src/ecs/**/*.ts
alwaysApply: false
---
# ECS Patterns (bitECS 0.4)
## Components — Plain Objects with Arrays
```typescript
// ✅ GOOD: plain object with number arrays
export const Position = { x: [] as number[], y: [] as number[] };
export const Health = { current: [] as number[], max: [] as number[] };
// ❌ BAD: old API (defineComponent, Types) — removed in 0.4
```
## Entity Lifecycle
```typescript
import { createWorld, addEntity, addComponent, removeEntity } from 'bitecs';
const world = createWorld();
const eid = addEntity(world);
addComponent(world, eid, Position);
Position.x[eid] = 100;
Position.y[eid] = 200;
```
## Queries — Direct Function Calls
```typescript
import { query, Not } from 'bitecs';
// ✅ 0.4 API: query(world, components)
const entities = query(world, [Position, Velocity]);
for (const eid of entities) {
Position.x[eid] += Velocity.x[eid] * delta;
}
// Operators: Not, And, Or, Any, None
const stationary = query(world, [Position, Not(Velocity)]);
```
## Observers (replace enterQuery/exitQuery)
```typescript
import { observe, onAdd, onRemove } from 'bitecs';
observe(world, onAdd(Position), (eid) => { /* entity gained Position */ });
observe(world, onRemove(Position), (eid) => { /* entity lost Position */ });
```
## Systems — Pure Functions
```typescript
export const movementSystem = (world: World, delta: number) => {
for (const eid of query(world, [Position, Velocity])) {
Position.x[eid] += Velocity.x[eid] * delta;
Position.y[eid] += Velocity.y[eid] * delta;
}
};
```
## Rules
- Components = plain objects with `[] as number[]` fields
- Never store strings in components — use numeric IDs, look up in registry
- Systems must not create Phaser objects — only the sync bridge does that
- Parameter order: `addComponent(world, eid, component)` (world first, then eid)

View File

@@ -0,0 +1,44 @@
---
description: Phaser scene lifecycle and conventions
globs: src/scenes/**/*.ts
alwaysApply: false
---
# Phaser Scene Conventions
## Lifecycle
```
constructor → init(data) → preload() → create() → update(time, delta)
```
- `init`: receive data from scene transitions
- `preload`: load assets (prefer doing this only in BootScene)
- `create`: set up game objects, physics, input, cameras
- `update`: run ECS systems, sync bridge
## Scene Structure
```typescript
export class GameScene extends Phaser.Scene {
constructor() {
super({ key: 'GameScene' });
}
create(): void {
// 1. Initialize ECS world
// 2. Create tilemap
// 3. Spawn entities
// 4. Set up camera
// 5. Launch UI overlay
this.scene.launch('UIScene');
}
update(time: number, delta: number): void {
// Run ECS systems in defined order
}
}
```
## Rules
- Full transition: `this.scene.start('Name', { data })`
- Overlay: `this.scene.launch('Name')` (for HUD, menus)
- Never put game logic in scenes — delegate to ECS systems
- Scenes manage Phaser objects; ECS manages game state

View File

@@ -0,0 +1,28 @@
---
description: Core project context for Synthesis — scientific roguelike
alwaysApply: true
---
# Synthesis — Project Context
## What Is This
2D scientific roguelike (TypeScript + Phaser 3 + bitECS + Vite). Real science replaces magic. Player combines real chemical elements, builds mechanisms using physics, tames creatures through biology, manipulates NPCs via cognitive biases.
## Architecture
- **Phaser 3** — rendering, physics, input, audio, scenes
- **bitECS** — entity-component-system for game logic (TypedArrays, systems = pure functions)
- **Vite** — build + HMR
- Phaser handles rendering; bitECS handles logic. They sync via a bridge layer.
## Key Files
- `synthesis-gdd.md` — Game Design Document (source of truth for ALL design decisions)
- `engine-analysis.md` — Technical stack rationale
- `IMPLEMENTATION-PLAN.md` — Phased development roadmap
- `PROGRESS.md` — Current status (**update after completing tasks**)
## Design Principles
1. All science must be **real** (simplified, never wrong)
2. Every failed experiment teaches WHY it failed
3. Death is a feature, not punishment — it's a cycle phase
4. Cycles everywhere: gameplay, visuals, audio, narrative
5. Data-driven: game data in `src/data/*.json`, logic in TypeScript