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:
30
.cursor/rules/agent-workflow.mdc
Normal file
30
.cursor/rules/agent-workflow.mdc
Normal 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
|
||||
40
.cursor/rules/chemistry-engine.mdc
Normal file
40
.cursor/rules/chemistry-engine.mdc
Normal 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)
|
||||
36
.cursor/rules/data-files.mdc
Normal file
36
.cursor/rules/data-files.mdc
Normal 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.
|
||||
65
.cursor/rules/ecs-patterns.mdc
Normal file
65
.cursor/rules/ecs-patterns.mdc
Normal 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)
|
||||
44
.cursor/rules/phaser-scenes.mdc
Normal file
44
.cursor/rules/phaser-scenes.mdc
Normal 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
|
||||
28
.cursor/rules/project-context.mdc
Normal file
28
.cursor/rules/project-context.mdc
Normal 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
|
||||
Reference in New Issue
Block a user