docs: map existing codebase

- STACK.md - Technologies and dependencies
- ARCHITECTURE.md - System design and patterns
- STRUCTURE.md - Directory layout
- CONVENTIONS.md - Code style and patterns
- TESTING.md - Test structure
- INTEGRATIONS.md - External services
- CONCERNS.md - Technical debt and issues
This commit is contained in:
Thomas Richter
2026-02-04 23:16:04 +01:00
parent 24a44583ef
commit 576799ae0e
7 changed files with 1265 additions and 0 deletions

View File

@@ -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*

View File

@@ -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*

View File

@@ -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*

View File

@@ -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*

135
.planning/codebase/STACK.md Normal file
View File

@@ -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*

View File

@@ -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*

View File

@@ -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*