diff --git a/.planning/codebase/ARCHITECTURE.md b/.planning/codebase/ARCHITECTURE.md new file mode 100644 index 0000000..19b3618 --- /dev/null +++ b/.planning/codebase/ARCHITECTURE.md @@ -0,0 +1,134 @@ +# Architecture + +**Analysis Date:** 2026-02-04 + +## Pattern Overview + +**Overall:** Scene-based state machine with linear progression through game states. + +**Key Characteristics:** +- Phaser 3 game framework with arcade physics +- Scene-based architecture where each major game location is a self-contained scene +- Inventory as shared game state passed between scenes via scene data +- Input-driven gameplay with dual control schemes (mouse/keyboard and touch) +- Responsive scaling for mobile and desktop viewports + +## Layers + +**Presentation Layer (Scenes):** +- Purpose: Render game visuals and handle user interaction +- Location: `src/scenes/` +- Contains: Five scene classes extending Phaser.Scene +- Depends on: Phaser framework, Phaser graphics/text/physics utilities +- Used by: Main application entry point in `src/main.js` + +**Game State Layer (Inventory):** +- Purpose: Track player progress across scenes (fuel, whale oil, penguins) +- Location: Passed as data object between scenes via `scene.start(nextScene, { inventory: ... })` +- Contains: Simple object with three numeric properties: `whaleOil`, `fuel`, `penguins` +- Depends on: Nothing +- Used by: All scenes read/modify inventory during gameplay + +**Framework Configuration Layer:** +- Purpose: Initialize Phaser game with physics, scaling, scene list +- Location: `src/main.js` +- Contains: Phaser.Game configuration and setup +- Depends on: All scene classes +- Used by: Entry point for application startup + +## Data Flow + +**Game Startup:** +1. Browser loads `index.html` which imports `src/main.js` as module +2. `src/main.js` creates Phaser.Game instance with config +3. IntroScene is the first scene in the scene list, auto-starts +4. Player interaction triggers scene transitions + +**Between-Scene Transitions:** +1. Current scene calls `this.scene.start(nextSceneKey, { inventory: this.inventory })` +2. Phaser stops current scene, starts next scene +3. Next scene receives inventory data in `init(data)` lifecycle method +4. Scene reads `data.inventory` and stores it locally +5. Scene's `create()` method renders UI with current inventory state + +**Inventory State:** +- IntroScene initializes: `{ whaleOil: 0, fuel: 100, penguins: 0 }` +- ShipDeckScene receives and passes forward +- MapScene receives, displays, and passes forward +- TransitionScene modifies (deducts fuel if applicable) and passes forward +- HuntingScene receives, displays, modifies (fuel cost, oil gain, penguin discovery) and returns to MapScene +- Penguin discovery (penguins > 0) unlocks penguin cage UI in ShipDeckScene + +## Key Abstractions + +**Scene Class Pattern:** +- Purpose: Encapsulate game logic and rendering for a specific game state +- Examples: `src/scenes/IntroScene.js`, `src/scenes/ShipDeckScene.js`, `src/scenes/MapScene.js`, `src/scenes/TransitionScene.js`, `src/scenes/HuntingScene.js` +- Pattern: Each scene extends `Phaser.Scene`, implements `create()` for setup and `update()` for per-frame logic + +**Interactive UI Elements:** +- Purpose: Handle pointer (mouse/touch) input for clickable objects +- Pattern: Rectangle or circle objects with `.setInteractive()` and `.on('pointerdown')` or `.on('pointerup')` listeners +- Examples in ShipDeckScene: wheel click to open map, barrel zones, penguin cage +- Examples in MapScene: location markers with hover effects and click handlers +- Examples in HuntingScene: crosshair positioning and firing + +**Inventory Display Pattern:** +- Purpose: Show player resources in consistent UI +- Pattern: Text panel with semi-transparent background showing formatted resource strings +- Used in: ShipDeckScene, MapScene, HuntingScene +- Format: Multi-line text with labels and current/max values + +**Message System Pattern:** +- Purpose: Communicate game state changes and feedback to player +- Pattern: Semi-transparent rectangle with text object, updated via `showMessage(text)` method +- Used in: ShipDeckScene, MapScene, TransitionScene, HuntingScene + +## Entry Points + +**Application Entry:** +- Location: `index.html` +- Triggers: Page load +- Responsibilities: Load HTML structure, import main.js as module, render into game-container div + +**Game Initialization:** +- Location: `src/main.js` +- Triggers: Module import by index.html +- Responsibilities: Configure Phaser game (physics, scaling, resolution), instantiate Phaser.Game, list all scenes + +**First Scene:** +- Location: `src/scenes/IntroScene.js` +- Triggers: Phaser auto-starts first scene in config array +- Responsibilities: Display title, draw decorative elements, show SET SAIL button, initialize inventory with defaults + +## Error Handling + +**Strategy:** Silent failures with user messaging; no error boundaries or try-catch blocks in current codebase. + +**Patterns:** +- Fuel depletion check before whale processing (HuntingScene.killWhale): if fuel < 2, whale is discarded without processing +- Whale only spawns if no alive whale exists: `if (this.currentWhale && this.currentWhale.getData('alive')) return;` +- Inventory bounds clamped by Math.min/Math.ceil to prevent negative values +- Crosshair position clamped to screen bounds: `Phaser.Math.Clamp(value, 0, max)` + +## Cross-Cutting Concerns + +**Logging:** None implemented. Debug would require browser console. + +**Validation:** Minimal; mostly relies on initial state setup and boundary clamping. +- Fuel: clamped to [0, 100] via Math.min operations +- Whale oil: clamped to [0, 50] via Math.min operations +- Penguins: clamped to [0, 20] via Math.min operations +- Crosshair X: `Phaser.Math.Clamp(x, 0, 800)` +- Crosshair Y: `Phaser.Math.Clamp(y, 0, 600)` + +**Authentication:** Not applicable; single-player game. + +**Device Detection:** Mobile detection via user agent regex in HuntingScene.create(): +```javascript +this.isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); +``` + +--- + +*Architecture analysis: 2026-02-04* diff --git a/.planning/codebase/CONCERNS.md b/.planning/codebase/CONCERNS.md new file mode 100644 index 0000000..75bed85 --- /dev/null +++ b/.planning/codebase/CONCERNS.md @@ -0,0 +1,205 @@ +# Codebase Concerns + +**Analysis Date:** 2026-02-04 + +## Architecture & State Management + +**Fragile Inventory Passing System:** +- Issue: Inventory object is manually passed between scenes via `data` parameter in every scene transition. No centralized state management. +- Files: `src/scenes/IntroScene.js`, `src/scenes/ShipDeckScene.js`, `src/scenes/MapScene.js`, `src/scenes/HuntingScene.js`, `src/scenes/TransitionScene.js` +- Impact: Each scene maintains its own copy of inventory. A player could lose progress if a scene fails to properly receive or pass inventory data. Adding new inventory items requires changes across all 5 scenes. Risk of inventory corruption during rapid scene transitions. +- Fix approach: Create a centralized GameState or InventoryManager singleton that all scenes access. Example structure: + ```javascript + class GameState { + static instance = null; + static getInstance() { + if (!this.instance) this.instance = new GameState(); + return this.instance; + } + constructor() { + this.inventory = { whaleOil: 0, fuel: 100, penguins: 0 }; + } + } + ``` + Then all scenes reference `GameState.getInstance().inventory` instead of managing copies. + +## Testing Coverage Gaps + +**Unit Tests Don't Test Actual Scene Logic:** +- What's not tested: Game logic tests in `tests/unit/game-logic.test.js` only test pure functions and simple calculations. They don't test the actual scene behavior, event handlers, or UI state changes. +- Files: `tests/unit/game-logic.test.js`, `src/scenes/*.js` +- Specific gaps: + - No tests for barrel creation/destruction logic in ShipDeckScene + - No tests for whale spawning, movement, health tracking + - No tests for harpoon firing and collision detection + - No tests for scene transition data passing + - No tests for inventory display updates +- Risk: Bugs in core game mechanics (whale health, fuel consumption, barrel positioning) can't be caught automatically. Changes to scene code could break functionality without detection. +- Priority: High - Core game loop (hunting scene) is untested + +**E2E Tests Use Hard-coded Click Positions:** +- What's tested: `tests/e2e/game-flow.spec.js` relies on exact pixel coordinates for button clicks. Example: `canvas.click({ position: { x: 400, y: 400 } })` +- Problem: Any change to UI layout, button positioning, or screen resolution will break these tests without actually breaking the game +- Files: `tests/e2e/game-flow.spec.js` (lines 24, 40, 58, 66, 73, 95, 113-117, 125, 136) +- Risk: Tests become maintenance burden; false positives when layout changes +- Fix approach: Use Phaser's scene testing utilities or find elements by ID/class instead of pixel positions + +**No Mobile Touch Testing:** +- What's tested: E2E includes mobile viewport test but only checks canvas visibility and scaling, not touch interaction functionality +- Files: `tests/e2e/game-flow.spec.js` (lines 144-173) +- Missing: Tests for touch event handlers, multi-touch scenarios, long-press interactions +- Risk: Touch controls may break without detection. Buttons may be non-responsive on actual devices. + +## Performance & Complexity Issues + +**HuntingScene is Large and Complex:** +- Issue: Single 637-line file handles whale AI, harpoon physics, collision detection, camera effects, input management, and rendering +- Files: `src/scenes/HuntingScene.js` +- Specific problem areas: + - Lines 316-398: Whale update logic duplicates movement calculation and bounds checking + - Lines 434-450: Harpoon update is simple loop-over-array that could become O(n²) with many harpoons + - Lines 453-489: Collision detection recalculates screen position every frame for every harpoon + - Lines 607-630: Camera sway uses a `time.addEvent()` with permanent loop instead of native `update()` method +- Impact: Difficult to debug, test, or modify. Camera sway timer creates memory leak potential (never cancelled). Whale movement becomes laggy with multiple whales. +- Fix approach: Extract whale logic to separate `Whale` class, harpoons to `Harpoon` class, collision logic to separate method, use `update()` for camera instead of addEvent() + +**Barrel Recreation is Inefficient:** +- Issue: In `ShipDeckScene.js`, `updateInventoryDisplay()` calls `clearBarrels()` then recreates all barrels every time inventory changes (lines 294-297) +- Files: `src/scenes/ShipDeckScene.js` (lines 225-297) +- Impact: Visual flicker; hundreds of objects created/destroyed repeatedly. On low-end devices, could cause jank. +- Fix approach: Reuse barrel objects or update only visual properties (fill color, scale) instead of destroying/recreating + +**Graphics Objects Never Destroyed:** +- Issue: Graphics objects created in multiple scenes are never destroyed - only drawn once +- Files: + - `src/scenes/IntroScene.js` (lines 82-98, 101-116): waves and ship drawn once + - `src/scenes/HuntingScene.js` (lines 144-170): crosshair graphics.generateTexture() leaves original graphics object + - `src/scenes/TransitionScene.js` (lines 286-301): ocean graphics never destroyed +- Impact: Memory leak. Small issue now but accumulates with multiple playthroughs. Old graphics objects persist in memory even after scene changes. +- Fix approach: Call `.destroy()` on graphics objects after `.generateTexture()` or scene cleanup + +## Known Behavioral Issues + +**Incomplete Features Referenced in Code:** +- Issue: Code contains references to unimplemented scenes +- Files: `src/scenes/MapScene.js` (lines 197, 207) +- Problem: Pressing buttons to go to Antarctic or Port just returns to MapScene. Code comments say "Will change to 'AntarcticScene' when implemented" and "PortScene when implemented" +- Impact: Two major gameplay areas not implemented. Players can't sell whale oil or gather penguins as intended +- No fix needed for this sprint; just document that these scenes don't exist + +**Penguin Cage Visibility Logic is Opaque:** +- Issue: Penguin cage shown/hidden based on `inventory.penguins > 0` but there's no way in current code to acquire penguins +- Files: `src/scenes/ShipDeckScene.js` (lines 83-94, 281-291) +- Impact: Code path never executes. Feature is stub/placeholder. +- Severity: Low - intentional incomplete feature, but makes code harder to understand + +**Whale Diving Mechanic is Incomplete:** +- Issue: Whales go transparent when diving but no actual "invulnerability" is enforced - collision detection still runs (line 461 checks `diving` but collision only prevented if whale is visible or alpha check) +- Files: `src/scenes/HuntingScene.js` (lines 304-306, 333-342, 394-410, 460-463) +- Risk: Harpoons might hit whale during dive in edge cases +- Fix approach: Ensure collision detection returns early if `whale.getData('diving')` + +## Security & Validation Issues + +**No Inventory Bounds Checking:** +- Issue: Inventory values can go negative or exceed max without validation +- Files: All scene files, particularly `src/scenes/HuntingScene.js` (lines 586, 589) +- Example problem: If fuel goes below 0, the display shows negative fuel and barrel count calculation breaks +- Current safeguard: Line 547 checks `fuel < 2` before consuming, but nothing prevents other code paths from setting fuel to -5 +- Fix approach: Create Inventory class with setters that enforce bounds: + ```javascript + class Inventory { + setFuel(value) { this.fuel = Math.max(0, Math.min(100, value)); } + } + ``` + +**Hard-coded Game Constants Scattered Throughout:** +- Issue: Magic numbers for inventory limits, costs, and gameplay values are hard-coded in multiple files +- Files: + - Fuel max: `src/scenes/ShipDeckScene.js:276`, `src/scenes/MapScene.js:149`, `src/scenes/HuntingScene.js:234` + - Oil max: `src/scenes/ShipDeckScene.js:277`, `src/scenes/MapScene.js:150`, `src/scenes/HuntingScene.js:235` + - Whale health: `src/scenes/HuntingScene.js:292` (3 hits) + - Fuel cost to process: `src/scenes/HuntingScene.js:547, 586` (2 fuel) + - Barrel size calculations: `src/scenes/ShipDeckScene.js:105-165` (hardcoded ratios) +- Impact: Changing game balance requires finding and updating 10+ locations. High risk of inconsistency. +- Fix approach: Create `GameConstants.js` file: + ```javascript + export const GAME_CONSTANTS = { + INVENTORY: { MAX_FUEL: 100, MAX_OIL: 50, MAX_PENGUINS: 20 }, + WHALE: { HEALTH: 3, PROCESS_FUEL_COST: 2 }, + BARRELS: { FUEL_UNITS_PER_BARREL: 10, OIL_UNITS_PER_BARREL: 10 } + }; + ``` + +## Code Quality Issues + +**Duplicate Inventory Initialization:** +- Issue: Default inventory object defined in 5 different places +- Files: `src/scenes/IntroScene.js:120-124`, `src/scenes/ShipDeckScene.js:6-10`, `src/scenes/HuntingScene.js:9`, `src/scenes/MapScene.js:10`, `src/scenes/TransitionScene.js:10` +- Risk: If inventory structure changes, must update in all 5 places +- Fix: One source of truth (GameState or constants) + +**Inconsistent Data Passing Pattern:** +- Issue: Scene init() method doesn't always match how data is received +- Files: `src/scenes/TransitionScene.js` (line 10) uses OR operator for defaults but other scenes (ShipDeckScene line 15) use if check +- Impact: Minor style inconsistency, slightly reduces readability + +**No Error Handling for Scene Transitions:** +- Issue: All `this.scene.start()` calls don't handle failures +- Files: All scene files +- Risk: If scene key is misspelled or doesn't exist, silently fails +- Fix: Add error handling or validation + +## Missing Features Blocking Full Gameplay + +**No Penguin Collection Mechanic:** +- What's missing: Antarctic Island scene doesn't exist, so penguins can't be collected +- Blocks: Complete game loop where fuel can be managed through penguin burning +- Files: Referenced in `src/scenes/MapScene.js:197` but scene doesn't exist + +**No Port/Trading System:** +- What's missing: Port scene doesn't exist, can't sell whale oil +- Blocks: Economic gameplay loop and victory condition +- Files: Referenced in `src/scenes/MapScene.js:207` but scene doesn't exist + +**No Game Over or Win Condition:** +- What's missing: No way to fail (fuel runs out) or win (enough whale oil collected) +- Impact: Game has no clear objective or end state +- Files: N/A - logic doesn't exist anywhere + +**No Persistence:** +- What's missing: Game state lost on page reload +- Impact: Can't save/load progress +- Not critical for prototype but important for release + +## Scaling & Device Issues + +**Hardcoded Screen Dimensions:** +- Issue: Game assumes 800x600 canvas with many hardcoded position values +- Files: `src/main.js:10-11`, all scene files +- Example: `src/scenes/IntroScene.js:21` (text at x:400, y:150), `src/scenes/HuntingScene.js:139-140` (crosshair clamped to 0-800, 0-600) +- Risk: Resizing game window breaks positioning. Mobile scaling works via Phaser but coordinates are still hardcoded +- Fix: Use relative positioning or percentage-based coordinates + +**Mobile Detection Uses navigator.userAgent:** +- Issue: `src/scenes/HuntingScene.js:23` uses userAgent string matching +- Risk: Can be spoofed, unreliable on some devices +- Better approach: Use Phaser's `this.sys.game.device` API or check touch availability + +## Testing Infrastructure Concerns + +**Vitest Config Exposes Dev Server Publicly:** +- Issue: `vitest.config.js:21-22` sets `host: '0.0.0.0'` allowing external connections +- Files: `vitest.config.js` +- Risk: In production, test server would be exposed to internet +- Fix: Only use in development (check NODE_ENV) + +**E2E Tests Have Long Arbitrary Waits:** +- Issue: Tests use `waitForTimeout(1000)` and `waitForTimeout(2000)` instead of waiting for elements/state +- Files: `tests/e2e/game-flow.spec.js` (almost every test) +- Risk: Tests are slow and flaky; may timeout on slow devices but pass on fast ones +- Fix: Use `waitForSelector()`, `waitForFunction()`, or Phaser's scene ready events + +--- + +*Concerns audit: 2026-02-04* diff --git a/.planning/codebase/CONVENTIONS.md b/.planning/codebase/CONVENTIONS.md new file mode 100644 index 0000000..eb02024 --- /dev/null +++ b/.planning/codebase/CONVENTIONS.md @@ -0,0 +1,139 @@ +# Coding Conventions + +**Analysis Date:** 2026-02-04 + +## Naming Patterns + +**Files:** +- PascalCase for scene classes: `IntroScene.js`, `MapScene.js`, `HuntingScene.js`, `ShipDeckScene.js`, `TransitionScene.js` +- kebab-case for test files: `game-logic.test.js`, `game-flow.spec.js` +- camelCase for configuration files: `vitest.config.js`, `playwright.config.js` + +**Functions:** +- camelCase for all methods: `drawWaves()`, `createLocation()`, `updateInventoryDisplay()`, `spawnWhale()`, `hitWhale()` +- Method names are descriptive and indicate action: `create*` for initialization, `update*` for state changes, `draw*` for graphics +- Private-like pattern: methods starting with event handlers (`on*` callbacks) or utility methods + +**Variables:** +- camelCase for local variables and object properties: `whaleOil`, `currentWhale`, `inventory`, `crosshairX`, `swimSpeedX` +- PascalCase for class names: `IntroScene`, `MapScene`, `HuntingScene` +- SCREAMING_SNAKE_CASE for constants is not used; magic numbers are inline or stored as camelCase object properties +- Abbreviated names in loops: `i`, `x`, `y` for coordinates + +**Types:** +- No TypeScript; vanilla JavaScript with JSDoc comments for complex logic +- Object literals for data structures (e.g., inventory: `{ whaleOil: 0, fuel: 100, penguins: 0 }`) +- No explicit type annotations; types inferred from usage context + +## Code Style + +**Formatting:** +- No explicit formatter configured (no .prettierrc or similar found) +- 4 spaces for indentation (observed consistently across all files) +- Single statements per line; no cramping of expressions +- Line length varies; some lines exceed 100 characters + +**Linting:** +- No ESLint or similar linting tool configured +- No explicit style guide enforced; conventions followed naturally + +## Import Organization + +**Order:** +1. Phaser framework imports: `import Phaser from 'phaser'` +2. Scene exports: `export default class SceneName extends Phaser.Scene` +3. Test framework imports: `import { describe, it, expect } from 'vitest'` and `import { test, expect } from '@playwright/test'` + +**Path Aliases:** +- No path aliases configured +- Relative imports with `.js` extension explicit: `import IntroScene from './scenes/IntroScene.js'` + +## Error Handling + +**Patterns:** +- Defensive checks before operations: `if (this.currentWhale && this.currentWhale.getData('alive'))` +- Boundary checking for game state: `if (this.inventory.fuel < 2)` before processing whale +- No explicit error throwing; invalid states default to safe fallbacks +- Scene transitions use `this.scene.start()` with null coalescing: `this.inventory = data.inventory || { whaleOil: 0, fuel: 100, penguins: 0 }` +- Array bounds checking: loop guards with `.length` checks, array splice after removal + +## Logging + +**Framework:** console (implicit; not found in code) + +**Patterns:** +- No logging statements observed in source code +- Game messaging via `showMessage()` method: text display in game UI +- Debug output would likely use console.log if needed (not observed) + +## Comments + +**When to Comment:** +- Method-level comments for non-obvious logic: `// Ocean blue background`, `// Draw decorative waves` +- Inline comments for complex calculations: `// Can't hit whale while diving`, `// Keep within bounds` +- Comment frequency is moderate; most code is self-documenting through naming + +**JSDoc/TSDoc:** +- No JSDoc comments found +- Vanilla JavaScript with method names that describe intent + +## Function Design + +**Size:** +- Methods range from 10-70 lines +- Larger methods like `create()` (30+ lines) perform initialization with calls to smaller helper methods +- Complex game logic broken into focused methods: `updateWhale()`, `updateHarpoons()`, `checkCollisions()` + +**Parameters:** +- Methods accept 0-3 parameters typically +- Event handlers use single parameter (e.g., `pointerdown` callbacks) +- Data passed through scene initialization: `this.scene.start('NextScene', { inventory: data })` + +**Return Values:** +- Mostly void/no return (Phaser scene lifecycle methods) +- Some utility functions return boolean: `hasOwnProperty()` checks +- Game state propagated through `this` context, not function returns + +## Module Design + +**Exports:** +- Each scene file exports default class: `export default class SceneName extends Phaser.Scene` +- All scenes imported into `src/main.js` as class references +- Scene registration in Phaser config array: `scene: [IntroScene, ShipDeckScene, MapScene, TransitionScene, HuntingScene]` + +**Barrel Files:** +- No index.js or barrel files found +- Each scene is independent; imports are explicit by filename + +## Data Structures + +**Object Literals:** +- Inventory object: `{ whaleOil: 0, fuel: 100, penguins: 0 }` +- Configuration objects: Phaser game config in `src/main.js` +- Destination mapping in `TransitionScene.js`: `destinations` object with title, description, backgroundColor, visualType + +**Phaser-Specific Patterns:** +- `setData(key, value)` / `getData(key)` for storing state on game objects +- Container objects with `add([children])` for grouping (e.g., whale body with parts) +- Scene transitions with inventory passed as data parameter + +## Common Patterns Observed + +**Scene Lifecycle:** +1. `constructor()`: Set scene key +2. `init(data)`: Receive data from previous scene +3. `create()`: Initialize scene graphics and setup +4. `update()`: Game loop updates (only used in HuntingScene) + +**UI Element Creation:** +- Rectangle/circle/text created with `.add.rectangle()`, `.add.circle()`, `.add.text()` +- Interactive zones created with `setInteractive()` and `.on('pointerX', callback)` +- Hover/click feedback with `setScale()`, `setFillStyle()` updates + +**Animation:** +- Phaser tweens for smooth transitions: `this.tweens.add({ targets: obj, property: value, duration: ms })` +- Sine/cosine wave patterns for natural movement: `Math.sin()`, `Math.cos()` for bobbing/sway + +--- + +*Convention analysis: 2026-02-04* diff --git a/.planning/codebase/INTEGRATIONS.md b/.planning/codebase/INTEGRATIONS.md new file mode 100644 index 0000000..82d7a1c --- /dev/null +++ b/.planning/codebase/INTEGRATIONS.md @@ -0,0 +1,161 @@ +# External Integrations + +**Analysis Date:** 2026-02-04 + +## APIs & External Services + +**Not applicable** - No external APIs integrated. Game is fully self-contained. + +## Data Storage + +**Databases:** +- Not applicable - No database required or used +- Game state is managed in-memory during gameplay +- State location: Passed via scene parameters in `src/main.js` + +**File Storage:** +- Local filesystem only - Assets would be bundled with application +- No remote file storage configured +- Asset directories (planned): `assets/sprites/`, `assets/backgrounds/` + +**Caching:** +- Browser caching only - Static assets cached by nginx (1 year TTL) +- No server-side caching layer +- Vite dev server handles hot module replacement during development + +**State Management:** +- Phaser Scene-based state management +- Game state example from `src/scenes/IntroScene.js`: + ```javascript + const inventory = { + whaleOil: 0, + fuel: 100, + penguins: 0 + }; + this.scene.start('ShipDeckScene', { inventory: inventory }); + ``` +- State passed between scenes as parameters +- No persistence layer (state resets on page refresh) + +## Authentication & Identity + +**Not applicable** - No user authentication +- Game is anonymous and stateless +- No user accounts or login required +- All players access the same game instance + +## Monitoring & Observability + +**Error Tracking:** +- Not integrated +- Console errors only (browser developer tools) +- No remote error reporting configured + +**Logs:** +- Browser console logging only +- Nginx access logs available in container at `/var/log/nginx/` +- Docker container logs accessible via: `docker-compose logs -f` +- Vite dev server logs to terminal + +**Health Checks:** +- Docker health check: `wget --quiet --tries=1 --spider http://127.0.0.1/` +- Interval: 30 seconds +- Timeout: 3 seconds +- Retries: 3 before marking unhealthy +- Start period: 5 seconds + +## CI/CD & Deployment + +**Hosting:** +- Docker container on `node03.tricnet.de` +- Served via Nginx (Alpine Linux) +- Manual deployment via `deploy.sh` + +**CI Pipeline:** +- Not detected - No automated CI/CD system configured +- Deployment process: Manual `./deploy.sh` + - Uses rsync to sync files to node03 + - SSH to rebuild Docker container + - Rebuilds docker-compose services + +**Deployment Method:** +- Deployment file: `deploy.sh` +- Sync target: `tho@node03.tricnet.de:/home/tho/whalehunting/` +- Excludes: `node_modules/`, `dist/`, `.git/`, `.DS_Store` +- Build command: `docker-compose down && docker-compose up -d --build` + +## Environment Configuration + +**Required env vars:** +- None - Game requires no environment variables at runtime +- Docker container runs with default environment + +**Secrets location:** +- Not applicable - No secrets, API keys, or credentials required +- SSH key required for deployment (stored on developer machine) + +**Development Environment:** +- Game automatically runs on `http://localhost:5173` (Vite default) +- Playwright configured to use `http://localhost:5173` as baseURL +- No .env file needed for development + +## Webhooks & Callbacks + +**Incoming:** +- Not applicable - No webhooks received + +**Outgoing:** +- Not applicable - No webhooks sent +- Game makes no HTTP requests + +## Network Requirements + +**Development:** +- Must be able to access `localhost:5173` (Vite dev server) +- Must be able to access `localhost:5173` for Playwright E2E tests +- Vitest UI server binds to `0.0.0.0:51204` (all interfaces) + +**Production:** +- Must be accessible on port 8880 (external Docker port) +- Health checks require outbound HTTP to `http://127.0.0.1/` (internal to container) +- No outbound internet access required from game runtime + +## Data Flow + +**Client-Side Only:** +- All game logic runs in browser +- No data sent to any backend or external service +- Game state never persists (no save/load feature implemented) +- Player actions: Click/tap interactions → Phaser input handlers → Scene state changes + +**Example State Flow** (from `src/scenes/IntroScene.js`): +1. User clicks "SET SAIL" button +2. Button click handler creates inventory object +3. Scene transitions: `this.scene.start('ShipDeckScene', { inventory })` +4. ShipDeckScene receives inventory via init parameter +5. Game continues with same inventory state + +## Testing Infrastructure + +**Unit Tests:** +- Framework: Vitest 4.0.16 +- Environment: jsdom +- Test directory: `tests/unit/` +- Run: `npm run test` or `npm run test:ui` (with visual UI) +- Coverage: `npm run test:coverage` + +**E2E Tests:** +- Framework: Playwright 1.57.0 +- Test directory: `tests/e2e/` +- Browsers tested: Chromium, iPhone 12 (mobile) +- Run: `npm run test:e2e` or `npm run test:e2e:ui` +- Screenshots: Captured on failure only +- Traces: Captured on first retry + +**Test Configuration Files:** +- `vitest.config.js` - Unit test setup +- `playwright.config.js` - E2E test setup + +--- + +*Integration audit: 2026-02-04* diff --git a/.planning/codebase/STACK.md b/.planning/codebase/STACK.md new file mode 100644 index 0000000..13a2ee5 --- /dev/null +++ b/.planning/codebase/STACK.md @@ -0,0 +1,135 @@ +# Technology Stack + +**Analysis Date:** 2026-02-04 + +## Languages + +**Primary:** +- JavaScript (ES6 modules) - All source code and configuration + +## Runtime + +**Environment:** +- Node.js 20 (Alpine) - Development and build time +- Browser (Chromium, Safari) - Runtime environment for game + +**Package Manager:** +- npm - Dependency management +- Lockfile: `package-lock.json` (present) + +## Frameworks + +**Core:** +- Phaser 3.80.1 - 2D game framework and physics engine + - Used in: `src/main.js` and all scene files + - Provides game loop, rendering, input handling, physics (arcade) + +**Build/Dev:** +- Vite 5.0.0 - Build tool and development server + - Config: Not detected (uses Vite defaults) + - Dev server: Port 5173 (standard) + - Output directory: `dist/` + +**Testing:** +- Vitest 4.0.16 - Unit test framework + - Config: `vitest.config.js` + - Environment: jsdom (browser-like environment for testing) + - UI: @vitest/ui 4.0.16 for visual test runner + +- Playwright 1.57.0 - E2E testing framework + - Config: `playwright.config.js` + - Test directory: `tests/e2e/` + - Browsers: Chromium, iPhone 12 (mobile simulation) + - Screenshots: Captured on test failure + +## Key Dependencies + +**Critical:** +- phaser (3.80.1) - Complete game development framework + - Why it matters: Core engine for all game logic, rendering, and physics + +**Development Only:** +- @playwright/test (1.57.0) - E2E test runner with browser automation +- @vitest/ui (4.0.16) - Visual UI for viewing test results +- jsdom (27.3.0) - DOM implementation for testing in Node.js environment +- vite (5.0.0) - Lightning-fast build tool and dev server +- vitest (4.0.16) - Unit test framework built on Vite + +## Configuration + +**Environment:** +- No environment variables required for runtime +- Game runs entirely in browser with no backend dependencies + +**Build:** +- `vite.config.js` - Not present (uses defaults) +- `playwright.config.js` - E2E test configuration +- `vitest.config.js` - Unit test configuration with jsdom environment +- Vite configured for module type: module (ES6) + +## Platform Requirements + +**Development:** +- Node.js 20+ +- npm 8+ (included with Node 20) +- Git (for version control) +- Linux/macOS/Windows (any platform with Node.js) + +**Production:** +- Nginx web server (Alpine Linux image) +- Docker (for containerized deployment) +- Static file hosting (game is fully client-side) +- No database required +- No backend API required + +## Build & Deployment + +**Development:** +```bash +npm install # Install dependencies +npm run dev # Start dev server on port 5173 +npm run test # Run unit tests +npm run test:ui # Run tests with visual UI +npm run test:e2e # Run Playwright E2E tests +``` + +**Production:** +```bash +npm run build # Build to dist/ directory +docker build -t whalehunting-game . # Build Docker image +docker-compose up -d # Deploy with Docker Compose +``` + +**Docker Stack:** +- Base image: `nginx:alpine` (production serving) +- Build stage: `node:20-alpine` (build application) +- Port: 8880 (external), 80 (container) +- Health checks: wget ping every 30s +- Network: Docker bridge network `whalehunting-network` + +**Deployment Target:** +- Docker container on node03.tricnet.de +- Deployed via rsync + SSH (see `deploy.sh`) +- Game served at: `http://node03.tricnet.de:8880` + +## Performance Optimizations + +**Build:** +- Vite provides fast module bundling +- Production build is minified and optimized + +**Runtime:** +- Phaser uses WebGL/Canvas rendering (automatic selection via Phaser.AUTO) +- Game configured with fixed dimensions (800x600) with responsive scaling +- Responsive scaling mode: FIT with auto-centering +- Min viewport: 320x240 (mobile support) +- Arcade physics engine configured with no gravity (2D top-down view) + +**Serving:** +- Nginx configured with gzip compression +- Static asset caching: 1 year (as noted in README) +- Health checks ensure container availability + +--- + +*Stack analysis: 2026-02-04* diff --git a/.planning/codebase/STRUCTURE.md b/.planning/codebase/STRUCTURE.md new file mode 100644 index 0000000..38d3f82 --- /dev/null +++ b/.planning/codebase/STRUCTURE.md @@ -0,0 +1,177 @@ +# Codebase Structure + +**Analysis Date:** 2026-02-04 + +## Directory Layout + +``` +whalehunting/ +├── src/ # Game source code +│ ├── main.js # Phaser game initialization and configuration +│ └── scenes/ # Game scene implementations +│ ├── IntroScene.js +│ ├── ShipDeckScene.js +│ ├── MapScene.js +│ ├── TransitionScene.js +│ └── HuntingScene.js +├── tests/ # Test suites +│ ├── unit/ # Unit tests (Vitest) +│ │ └── game-logic.test.js +│ └── e2e/ # End-to-end tests (Playwright) +│ └── game-flow.spec.js +├── .planning/ # GSD planning documents +│ └── codebase/ # Architecture analysis +├── index.html # HTML entry point +├── package.json # Dependencies and scripts +├── vitest.config.js # Unit test configuration +├── playwright.config.js # E2E test configuration +├── nginx.conf # Web server config for deployment +├── Dockerfile # Container image definition +├── docker-compose.yml # Multi-container orchestration +└── deploy.sh # Automated deployment script +``` + +## Directory Purposes + +**src/:** +- Purpose: All game source code +- Contains: Main game initialization and scene implementations +- Key files: `main.js` is the entry point that configures Phaser + +**src/scenes/:** +- Purpose: Game scene implementations +- Contains: Five scene classes, one per major game location/mode +- Key files: IntroScene starts the game, HuntingScene has most complex logic + +**tests/:** +- Purpose: All test code separated from source +- Contains: Unit tests and E2E tests in separate subdirectories +- Key files: `game-logic.test.js` for game mechanics, `game-flow.spec.js` for user flows + +**tests/unit/:** +- Purpose: Logic testing without Phaser +- Contains: Vitest tests for inventory calculations, fuel costs, barrel logic +- Key files: `game-logic.test.js` + +**tests/e2e/:** +- Purpose: Full application flow testing +- Contains: Playwright tests simulating user interactions on actual game canvas +- Key files: `game-flow.spec.js` + +**.planning/codebase/:** +- Purpose: Analysis documents for code navigation +- Contains: ARCHITECTURE.md, STRUCTURE.md, CONVENTIONS.md, TESTING.md, CONCERNS.md +- Generated by: GSD mapping processes + +## Key File Locations + +**Entry Points:** +- `index.html`: Browser loads this, contains game-container div and script import +- `src/main.js`: Phaser game configuration and scene list initialization + +**Configuration:** +- `vitest.config.js`: Unit test runner with jsdom environment, port 51204 for UI server +- `playwright.config.js`: E2E test runner with Chromium and iPhone 12 profiles +- `package.json`: Dependencies (Phaser 3.80.1, Vite, Vitest, Playwright) +- `nginx.conf`: Web server routing (not actively used in dev) +- `docker-compose.yml`: Local dev: port 5173 for Vite server +- `Dockerfile`: Production build with multi-stage for size optimization + +**Core Logic:** +- `src/scenes/IntroScene.js`: Game entry, starts with fuel=100, oil=0, penguins=0 +- `src/scenes/ShipDeckScene.js`: Inventory display, barrel visualization, penguin cage reveal +- `src/scenes/MapScene.js`: Location selection (Hunting Grounds, Antarctic, Port) +- `src/scenes/TransitionScene.js`: Atmospheric journey scenes, destination-specific visuals +- `src/scenes/HuntingScene.js`: Core gameplay - whale spawning, harpoon mechanics, fuel consumption + +**Testing:** +- `tests/unit/game-logic.test.js`: Barrel calculations, inventory limits, fuel costs, whale health, crosshair clamping +- `tests/e2e/game-flow.spec.js`: Full flows (intro→ship→map→hunting), mobile viewport, desktop scaling + +## Naming Conventions + +**Files:** +- PascalCase + Scene suffix: `IntroScene.js`, `ShipDeckScene.js` +- camelCase for non-scene modules: Would apply if utilities existed +- Test files: Matched source with `.test.js` or `.spec.js` suffix +- Config files: kebab-case: `playwright.config.js`, `vitest.config.js` + +**Directories:** +- lowercase plural: `src/scenes/`, `tests/unit/`, `tests/e2e/` +- camelCase for compound: Not currently used + +**Class Names:** +- PascalCase: `IntroScene`, `ShipDeckScene`, `MapScene`, `TransitionScene`, `HuntingScene` +- Extend `Phaser.Scene` directly + +**Methods:** +- camelCase: `create()`, `update()`, `init()`, `createDeck()`, `createBarrels()`, `updateInventoryDisplay()` +- Lifecycle methods (create, update) are Phaser conventions +- Helper methods prefix with action: `create*()`, `update*()`, `show*()`, `draw*()` + +**Variables:** +- camelCase for instance: `this.inventory`, `this.currentWhale`, `this.harpoons` +- camelCase for local: `barrelCount`, `swimSpeedX`, `healthPercent` +- UPPERCASE for constants: `FUEL_COST = 2`, `MAX_HEALTH = 3` + +**Game State Objects:** +- Inventory structure: `{ whaleOil: number, fuel: number, penguins: number }` +- Whale data stored via `setData()`: `health`, `alive`, `diving`, `swimSpeedX`, `swimSpeedY`, `direction` + +## Where to Add New Code + +**New Scene/Location:** +1. Create `src/scenes/NewScene.js` extending Phaser.Scene +2. Implement `init(data)` to receive inventory +3. Implement `create()` for rendering +4. Implement `update()` if frame-by-frame logic needed +5. Call `this.scene.start('MapScene', { inventory: this.inventory })` to transition +6. Add to scene list in `src/main.js` config array +7. Add location marker in `MapScene.js` that routes to new scene + +**New Game Mechanic:** +- Hunting scene has most game logic: HuntingScene.js handles whale spawning, harpoon firing, collision detection, health tracking +- Add mechanics as methods in relevant scene class +- Store persistent state in `this.inventory` object +- Display UI changes via `updateInventoryDisplay()` pattern + +**Utilities (if needed):** +- Create `src/utils/` directory for shared helpers +- Example: `src/utils/inventory.js` for max capacity functions +- Example: `src/utils/collision.js` for distance calculations +- Import in scenes: `import { calculateBarrels } from '../utils/inventory.js';` + +**New UI Component Pattern:** +- Create methods in scene: `createInventoryDisplay()`, `createMessageBox()` +- Return reference to element for later updates +- Update via methods: `updateInventoryDisplay()`, `showMessage(text)` +- Use semi-transparent rectangles for panels with `.setFillStyle(0x000000, 0.7)` + +## Special Directories + +**node_modules/:** +- Purpose: Installed dependencies +- Generated: Yes (via npm install) +- Committed: No (.gitignore) + +**dist/:** +- Purpose: Vite production build output +- Generated: Yes (via npm run build) +- Committed: No (.gitignore) +- Files: Optimized JS/CSS for deployment + +**.git/:** +- Purpose: Git repository metadata +- Generated: Yes (git init) +- Committed: Meta repository +- Notable: Recent commits track feature additions and testing infrastructure + +**.planning/:** +- Purpose: GSD orchestration files and analysis documents +- Generated: Yes (via /gsd commands) +- Committed: Yes (tracked in git) +- Structure: `codebase/` contains ARCHITECTURE.md, STRUCTURE.md, etc. + +--- + +*Structure analysis: 2026-02-04* diff --git a/.planning/codebase/TESTING.md b/.planning/codebase/TESTING.md new file mode 100644 index 0000000..f9ad7da --- /dev/null +++ b/.planning/codebase/TESTING.md @@ -0,0 +1,314 @@ +# Testing Patterns + +**Analysis Date:** 2026-02-04 + +## Test Framework + +**Runner:** +- Vitest 4.0.16 - Unit test runner +- Playwright 1.57.0 - E2E test runner + +**Config Files:** +- Unit tests: `vitest.config.js` +- E2E tests: `playwright.config.js` + +**Run Commands:** +```bash +npm run test # Run unit tests (Vitest) +npm run test:ui # Run tests with Vitest UI +npm run test:coverage # Run tests with coverage report +npm run test:e2e # Run E2E tests (Playwright) +npm run test:e2e:ui # Run E2E tests with Playwright UI +npm run test:all # Run all tests (unit + E2E) +``` + +## Test File Organization + +**Location:** +- Unit tests: `tests/unit/` directory +- E2E tests: `tests/e2e/` directory +- Separate from source code (not co-located) + +**Naming:** +- Unit tests: `.test.js` suffix: `game-logic.test.js` +- E2E tests: `.spec.js` suffix: `game-flow.spec.js` + +**Structure:** +``` +tests/ +├── unit/ +│ └── game-logic.test.js +└── e2e/ + └── game-flow.spec.js +``` + +## Unit Test Structure + +**Framework:** Vitest with globals enabled + +**Pattern from `tests/unit/game-logic.test.js`:** + +```javascript +import { describe, it, expect } from 'vitest'; + +describe('Game Logic - Inventory Management', () => { + describe('Barrel Calculations', () => { + it('should calculate correct fuel barrel count', () => { + const fuel = 100; + const barrelCount = Math.ceil(fuel / 10); + expect(barrelCount).toBe(10); + }); + }); +}); +``` + +**Test Organization:** +- Top-level `describe()` blocks group by feature/system (e.g., "Game Logic - Inventory Management") +- Nested `describe()` blocks organize related tests (e.g., "Barrel Calculations") +- Individual `it()` blocks test single logical assertions +- Test names are descriptive: "should [expected behavior]" + +## E2E Test Structure + +**Framework:** Playwright with page object patterns + +**Pattern from `tests/e2e/game-flow.spec.js`:** + +```javascript +import { test, expect } from '@playwright/test'; + +test.describe('Whale Hunting Game - Main Flow', () => { + test('should load the intro scene', async ({ page }) => { + await page.goto('/'); + await page.waitForTimeout(2000); + + const canvas = page.locator('canvas'); + await expect(canvas).toBeVisible(); + + await expect(page).toHaveTitle(/Whale Hunting/); + }); +}); +``` + +**Test Organization:** +- `test.describe()` blocks group related scenarios +- Individual `test()` blocks test user flows +- Setup patterns: `test.beforeEach()` for common navigation +- Viewport configuration: `test.use()` for device-specific testing +- Async/await pattern for all page interactions + +## Mocking + +**Framework:** None configured or used + +**Patterns:** +- No mocks found in unit tests +- Tests verify pure calculation logic (Math.ceil, Math.min) +- E2E tests work with actual running game instance + +**What to Mock:** +- Not applicable; unit tests use pure functions without external dependencies +- Phaser framework is not mocked (games tested in browser context) + +**What NOT to Mock:** +- Scene transitions (tested via canvas visibility) +- Phaser game instances (tested via E2E) + +## Fixtures and Test Data + +**Test Data:** +```javascript +// Direct inline values +it('should calculate correct fuel barrel count', () => { + const fuel = 100; + const barrelCount = Math.ceil(fuel / 10); + expect(barrelCount).toBe(10); +}); + +// Inventory object template +const inventory = { whaleOil: 0, fuel: 100, penguins: 0 }; +``` + +**Location:** +- No shared fixture files; test data created inline within test functions +- Inventory object structure repeated across multiple tests + +## Coverage + +**Configuration:** +```javascript +// vitest.config.js +coverage: { + provider: 'v8', + reporter: ['text', 'html', 'lcov'], + exclude: [ + 'node_modules/', + 'dist/', + '*.config.js', + 'tests/e2e/**' + ] +} +``` + +**View Coverage:** +```bash +npm run test:coverage +# Creates HTML report in coverage/ +# View: coverage/index.html +``` + +**Excluded from Coverage:** +- E2E tests (`tests/e2e/**`) +- Config files (`*.config.js`) +- Dependencies and build output + +## Test Types + +**Unit Tests:** +- Scope: Pure calculation logic (barrel counts, fuel consumption, bounds checking) +- Approach: Direct function calls with known inputs +- No DOM interaction; test game logic in isolation +- Coverage: `tests/unit/game-logic.test.js` +- Examples: + - Barrel calculation: `Math.ceil(fuel / 10)` + - Inventory limits: `Math.min(currentFuel + 10, maxFuel)` + - Whale health: Health deduction logic + - Mobile detection: User agent regex testing + - Crosshair bounds: Clamp function testing + +**Integration Tests:** +- Not explicitly used +- Closest: E2E tests that verify scene transitions + +**E2E Tests:** +- Scope: Complete user workflows (game startup to hunting) +- Approach: Browser automation via Playwright +- Tests: Canvas visibility, scene transitions, button clicks, viewport scaling +- Coverage: `tests/e2e/game-flow.spec.js` +- Desktop & mobile scenarios tested with viewport configuration +- Examples: + - "should load the intro scene" + - "should navigate to hunting grounds" + - "should work on mobile viewport" + - "should scale properly on desktop" + +## Common Patterns + +**Async Testing (E2E):** +```javascript +test('should start game from intro scene', async ({ page }) => { + await page.goto('/'); + await page.waitForTimeout(2000); + + const canvas = page.locator('canvas'); + await canvas.click({ position: { x: 400, y: 400 } }); + + await page.waitForTimeout(1000); + await expect(canvas).toBeVisible(); +}); +``` + +- `async ({ page })` destructures Playwright fixture +- `await` on page navigation and interactions +- `page.waitForTimeout()` for animation/transition delays +- `page.locator()` for element selection + +**Error Testing (Unit):** +```javascript +it('should not process whale without enough fuel', () => { + let fuel = 1; + const requiredFuel = 2; + const canProcess = fuel >= requiredFuel; + expect(canProcess).toBe(false); +}); +``` + +- Boolean logic to test conditions +- No exception throwing; test state validation + +**Mobile Testing (E2E):** +```javascript +test.describe('Mobile Compatibility', () => { + test.use({ + viewport: { width: 375, height: 667 }, + isMobile: true, + }); + + test('should work on mobile viewport', async ({ page }) => { + await page.goto('/'); + await page.waitForTimeout(2000); + + const canvas = page.locator('canvas'); + await expect(canvas).toBeVisible(); + + const canvasBox = await canvas.boundingBox(); + expect(canvasBox?.width).toBeLessThanOrEqual(375); + }); +}); +``` + +- `test.use()` sets viewport and device context +- Tap interactions: `canvas.tap()` +- BoundingBox assertions: verify canvas scaling + +## Test Isolation + +**Setup:** +- E2E tests use `test.beforeEach()` for repeated navigation setup +- No shared state between tests +- Each test starts fresh from home page + +**Teardown:** +- Playwright automatically closes browser context between tests +- No explicit cleanup required + +## Configuration Details + +**Vitest (`vitest.config.js`):** +- Environment: jsdom (browser-like DOM) +- Globals enabled: `describe`, `it`, `expect` available without imports +- Include pattern: `tests/unit/**/*.test.js` +- Server runs on port 51204 for Vitest UI + +**Playwright (`playwright.config.js`):** +- Projects: Chromium and mobile (iPhone 12) +- Base URL: `http://localhost:5173` (dev server) +- Test directory: `tests/e2e/` +- Retries: 2 in CI, 0 locally +- Screenshots: Only on failure +- Trace: On first retry +- Server: Runs `npm run dev` before tests + +## Test Execution Flow + +1. **Unit Tests (Vitest):** + - Runs isolated game logic tests + - Uses jsdom environment + - No Phaser instance needed + - Fast execution + +2. **E2E Tests (Playwright):** + - Starts dev server via `npm run dev` + - Launches Chromium and mobile browsers + - Navigates to `http://localhost:5173` + - Performs user interactions + - Verifies game state via canvas visibility + - Slower execution but tests full stack + +## Coverage Gaps + +**Not Tested (Unit):** +- Scene rendering and graphics creation +- Phaser animation timing and tweens +- Player input handling (mouse/touch) +- Collision detection logic + +**Not Tested (E2E):** +- Detailed game mechanics (whale health, harpoon trajectory) +- Inventory state persistence +- Exact positioning of game elements +- Performance under load + +--- + +*Testing analysis: 2026-02-04*