From 286edc3c1ddb546df9e690378defa0033800dbc9 Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Tue, 14 Apr 2026 11:43:03 +0700 Subject: [PATCH] remove completed plan and report files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plans for Svelte 5 rewrite and gameplay improvements have been fully implemented — removing the now-obsolete plan phases and research report. --- .../phase-01-scaffold-project.md | 50 --- .../phase-02-port-game-logic.md | 63 ---- .../phase-03-game-board-components.md | 87 ------ .../phase-04-scene-components.md | 85 ----- .../phase-05-game-scene-input.md | 96 ------ .../phase-06-polish-verify.md | 60 ---- plans/260412-1113-svelte5-rewrite/plan.md | 124 -------- .../phase-01-wait-action.md | 54 ---- .../phase-02-star-rating.md | 89 ------ .../phase-03-level-redesign.md | 89 ------ .../phase-04-vision-preview.md | 49 --- .../260413-1416-gameplay-improvements/plan.md | 57 ---- ...er-260412-1127-svelte5-grid-puzzle-game.md | 293 ------------------ 13 files changed, 1196 deletions(-) delete mode 100644 plans/260412-1113-svelte5-rewrite/phase-01-scaffold-project.md delete mode 100644 plans/260412-1113-svelte5-rewrite/phase-02-port-game-logic.md delete mode 100644 plans/260412-1113-svelte5-rewrite/phase-03-game-board-components.md delete mode 100644 plans/260412-1113-svelte5-rewrite/phase-04-scene-components.md delete mode 100644 plans/260412-1113-svelte5-rewrite/phase-05-game-scene-input.md delete mode 100644 plans/260412-1113-svelte5-rewrite/phase-06-polish-verify.md delete mode 100644 plans/260412-1113-svelte5-rewrite/plan.md delete mode 100644 plans/260413-1416-gameplay-improvements/phase-01-wait-action.md delete mode 100644 plans/260413-1416-gameplay-improvements/phase-02-star-rating.md delete mode 100644 plans/260413-1416-gameplay-improvements/phase-03-level-redesign.md delete mode 100644 plans/260413-1416-gameplay-improvements/phase-04-vision-preview.md delete mode 100644 plans/260413-1416-gameplay-improvements/plan.md delete mode 100644 plans/reports/researcher-260412-1127-svelte5-grid-puzzle-game.md diff --git a/plans/260412-1113-svelte5-rewrite/phase-01-scaffold-project.md b/plans/260412-1113-svelte5-rewrite/phase-01-scaffold-project.md deleted file mode 100644 index d20a15a..0000000 --- a/plans/260412-1113-svelte5-rewrite/phase-01-scaffold-project.md +++ /dev/null @@ -1,50 +0,0 @@ -# Phase 1: Scaffold Svelte 5 Project - -**Priority:** Critical | **Status:** pending | **Effort:** Small - -## Overview -Set up a fresh Svelte 5 + Vite project structure alongside existing code. Replace Phaser dependency with Svelte. - -## Steps - -1. Remove Phaser and related deps from `package.json` -2. Install Svelte 5 + vite-plugin-svelte - ```bash - npm install svelte && npm install -D @sveltejs/vite-plugin-svelte - ``` -3. Replace Vite config files (`vite/config.dev.mjs`, `vite/config.prod.mjs`) with single `vite.config.js` using svelte plugin -4. Create directory structure: - ``` - src/lib/ # Pure JS game logic - src/lib/locales/ # Locale JSON files - src/lib/levels/ # Level data - src/lib/game/ # Game logic classes - src/components/ # Reusable Svelte components - src/scenes/ # Scene components - src/styles/ # CSS - ``` -5. Create `src/main.js` — mount Svelte app to `#app` -6. Create minimal `src/App.svelte` — renders "Hello NNTV" placeholder -7. Create `src/styles/theme.css` — port color/font constants from `theme.js` as CSS variables -8. Update `index.html` — remove Phaser-specific meta, keep basic structure -9. Update `package.json` scripts to use new vite config -10. Verify `npm run dev` serves the placeholder app - -## Files to Create -- `vite.config.js` -- `src/main.js` (replace existing) -- `src/App.svelte` -- `src/styles/theme.css` - -## Files to Delete -- `vite/config.dev.mjs` -- `vite/config.prod.mjs` -- `vite/` directory -- `log.js` -- `src/game/theme.js` - -## Success Criteria -- `npm run dev` opens browser with "Hello NNTV" -- `npm run build` produces working dist/ -- Zero Phaser imports remain -- CSS variables available globally diff --git a/plans/260412-1113-svelte5-rewrite/phase-02-port-game-logic.md b/plans/260412-1113-svelte5-rewrite/phase-02-port-game-logic.md deleted file mode 100644 index 9f4d446..0000000 --- a/plans/260412-1113-svelte5-rewrite/phase-02-port-game-logic.md +++ /dev/null @@ -1,63 +0,0 @@ -# Phase 2: Port Pure Game Logic - -**Priority:** Critical | **Status:** pending | **Effort:** Medium - -## Overview -Move all framework-agnostic game logic into `src/lib/`. Strip Phaser imports. Separate data/logic from rendering. - -## Steps - -1. **Copy as-is** (just move files): - - `locales/en.json` → `src/lib/locales/en.json` - - `locales/vi.json` → `src/lib/locales/vi.json` - - `levels/Levels.js` → `src/lib/levels/levels.js` - - `progress.js` → `src/lib/progress.js` - - `localization.js` → `src/lib/localization.js` - -2. **Port TurnManager** → `src/lib/game/turn-manager.js` - - Remove `import Phaser` (line 1) - - Remove `this.scene` references - - Make it accept callbacks instead of calling scene methods directly - - Constructor takes `{ onLevelComplete, onDetected, getPlayerPos, getGrid, getGuards, getLightSystem }` - -3. **Port GridSystem** → `src/lib/game/grid-system.js` - - Keep: grid data model, isValidPosition, setWall/setGoal/setLight, gridToPixel, pixelToGrid - - Remove: `scene.add.graphics()`, `render()` method, all Phaser graphics calls - - Merge LightingSystem's `clearAllLight()` into GridSystem - - Add `getCellState(row, col)` returning `{ isWall, isGoal, isLight }` - -4. **Port Guards** → `src/lib/game/guards.js` - - Keep: all guard logic (updateLight, onTurnChange, patrol path, rotation, blinking) - - Remove: `createSprite()`, `update()` (sprite positioning), `scene.add.circle/line` - - Remove: `destroy()` sprite cleanup (no sprites to destroy) - - Each guard exposes: `{ row, col, color, type, direction, isOn }` for rendering - -5. **Port Player** → `src/lib/game/player.js` - - Keep: `moveTo()`, `move()`, `isInLitCell()`, `isAtGoal()` - - Remove: `createSprite()`, `update()` (sprite), tweens - - Pure data: `{ row, col }` position tracking - -6. **Port LevelManager** → `src/lib/game/level-manager.js` - - Rewrite as a pure function: `loadLevel(levelId, grid)` → returns `{ player, guards, isFinalLevel }` - - No scene references - - Creates guard instances, sets up grid walls/goals - -## Files to Create -- `src/lib/game/grid-system.js` -- `src/lib/game/guards.js` -- `src/lib/game/player.js` -- `src/lib/game/turn-manager.js` -- `src/lib/game/level-manager.js` -- `src/lib/localization.js` -- `src/lib/progress.js` -- `src/lib/locales/en.json` -- `src/lib/locales/vi.json` -- `src/lib/levels/levels.js` - -## Files to Delete (after all phases complete) -- Entire `src/game/` directory - -## Success Criteria -- All game logic files import zero Phaser code -- Guard logic can be tested independently (no DOM/canvas needed) -- `loadLevel()` returns a complete game state object diff --git a/plans/260412-1113-svelte5-rewrite/phase-03-game-board-components.md b/plans/260412-1113-svelte5-rewrite/phase-03-game-board-components.md deleted file mode 100644 index 9d664ac..0000000 --- a/plans/260412-1113-svelte5-rewrite/phase-03-game-board-components.md +++ /dev/null @@ -1,87 +0,0 @@ -# Phase 3: Build Game Board + Sprite Components - -**Priority:** Critical | **Status:** pending | **Effort:** Medium - -## Overview -Create Svelte components that render the game grid, player, and guards using HTML/CSS instead of Phaser canvas. - -## Steps - -1. **Button.svelte** — reusable styled button - - Props: `text`, `onclick`, `small` (boolean for compact variant) - - Styled with CSS variables from theme.css - - Hover/active states via CSS - -2. **GameBoard.svelte** — renders the grid - - Props: `grid` (2D array of cell states), `rows`, `cols`, `cellSize` - - CSS Grid layout: `grid-template-columns: repeat(cols, cellSize)` - - Each cell is a `
` with class based on state: `.wall`, `.goal`, `.lit`, `.empty` - - Click handler on cells for tap-to-move (emits `oncellclick` with row, col) - - Cell colors from CSS variables - -3. **PlayerSprite.svelte** — renders the player - - Props: `row`, `col`, `cellSize`, `gridOffset` - - Positioned absolutely over the grid - - CSS `transition: top 100ms, left 100ms` for smooth movement (replaces Phaser tweens) - - Black circle via `border-radius: 50%` - -4. **GuardSprite.svelte** — renders a guard - - Props: `guard` object `{ row, col, type, direction, isOn }` - - Props: `cellSize`, `gridOffset` - - Positioned absolutely over the grid - - Circle colored by type (CSS class: `.guard-static`, `.guard-rotating`, etc.) - - Direction indicator for rotating/patrolling: rotated CSS triangle or line - - Blinking guard dim state: reduced opacity via class toggle - -5. **GameHud.svelte** — top HUD bar - - Props: `lives`, `level`, `turns` - - Events: `onpause`, `onmenu` - - Fixed position, not part of game board - - Uses Button.svelte for pause/menu - -6. **DetectionPopup.svelte** — detection overlay - - Props: `visible` - - Events: `onplayagain` - - Dark overlay with centered message + button - - Uses `transition:fade` - -7. **PauseMenu.svelte** — pause overlay - - Props: `visible` - - Events: `onresume`, `onrestart`, `onmainmenu` - - Dark overlay with 3 buttons - - Uses `transition:fade` - -## Rendering Strategy - -``` -┌─ GameBoard.svelte (CSS Grid) ─────────┐ -│ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ │ -│ │ │ │ │ │ │ │ │ │ │ │ │ ... │ -│ └──┘ └──┘ └──┘ └──┘ └──┘ └──┘ │ -│ (each cell = div with bg color) │ -│ │ -│ PlayerSprite (absolute positioned) │ -│ GuardSprite × N (absolute positioned)│ -└────────────────────────────────────────┘ -``` - -## CSS Class Map - -| Game State | CSS Class | Color Variable | -|---|---|---| -| Empty cell | `.cell` | `--grid-empty` | -| Wall | `.cell.wall` | `--grid-wall` | -| Goal | `.cell.goal` | `--grid-goal` | -| Lit | `.cell.lit` | `--grid-lit` | -| Static guard | `.guard.static` | `--guard-static` | -| Rotating guard | `.guard.rotating` | `--guard-rotating` | -| Blinking guard (on) | `.guard.blinking` | `--guard-blinking` | -| Blinking guard (off) | `.guard.blinking.off` | `--guard-blinking-off` | -| Patrolling guard | `.guard.patrolling` | `--guard-patrolling` | - -## Success Criteria -- GameBoard renders correct grid for any level (6x6 through 10x10) -- Player circle moves smoothly with CSS transitions -- Guards display correct colors and direction indicators -- All overlays (detection, pause) fade in/out -- Components accept plain JS data — no Phaser objects diff --git a/plans/260412-1113-svelte5-rewrite/phase-04-scene-components.md b/plans/260412-1113-svelte5-rewrite/phase-04-scene-components.md deleted file mode 100644 index ba527af..0000000 --- a/plans/260412-1113-svelte5-rewrite/phase-04-scene-components.md +++ /dev/null @@ -1,85 +0,0 @@ -# Phase 4: Build Scene Components - -**Priority:** High | **Status:** pending | **Effort:** Medium - -## Overview -Replace all Phaser Scene classes with Svelte components. App.svelte acts as the scene router using reactive state + transitions. - -## Steps - -1. **App.svelte** — scene router - - Module-level `$state` for `currentScene` and `sceneData` - - `{#if currentScene === 'MainMenu'}` blocks for each scene - - Each block wrapped in `transition:fade={{ duration: 300 }}` - - Export `navigate(scene, data)` function for child components - - Initialize with `currentScene = 'MainMenu'` - -2. **MainMenu.svelte** - - Starfield background (random positioned dots via `{#each}`) - - Title, ninja icon, 4 buttons (Start, Level Select, Guide, Settings) - - Buttons call `navigate('StoryIntro')` etc. - -3. **StoryIntro.svelte** - - Dark background, scrolling story text - - CSS animation `@keyframes scroll` moves text upward - - Skip button → navigates to LevelIntro - - Auto-advance after scroll completes - -4. **LevelIntro.svelte** - - Props via sceneData: `{ level, lives }` - - Shows level number, name (from levels.js), story text (from localization) - - Continue button → navigates to Game - -5. **LevelSelect.svelte** - - Grid of 12 level buttons (CSS Grid, 4 columns) - - Reads progress from `getProgress()` - - Locked/unlocked/completed states via CSS classes - - Checkmark on completed levels - - Click → navigate to LevelIntro with level data - -6. **GameOver.svelte** - - Props via sceneData: `{ level, isLastLevel }` - - Different message for final level vs normal game over - - Try Again + Main Menu buttons - -7. **Settings.svelte** - - Language toggle (EN/VI) - - Calls `setLanguage()` from localization.js - - Re-renders text reactively via `$derived` on language - -8. **Guide.svelte** - - Scrollable content area (native CSS `overflow-y: auto`) - - Sections: objectives, controls, enemy types - - Back button - -## Scene Navigation Pattern - -```svelte - - - -{#if currentScene === 'MainMenu'} -
- -
-{:else if currentScene === 'Game'} -
- -
-{/if} -``` - -## Success Criteria -- All 8 scenes render correctly -- Scene transitions fade smoothly -- Language switching updates all visible text -- Level select shows correct progress state -- Story intro scrolls and auto-advances diff --git a/plans/260412-1113-svelte5-rewrite/phase-05-game-scene-input.md b/plans/260412-1113-svelte5-rewrite/phase-05-game-scene-input.md deleted file mode 100644 index de228a6..0000000 --- a/plans/260412-1113-svelte5-rewrite/phase-05-game-scene-input.md +++ /dev/null @@ -1,96 +0,0 @@ -# Phase 5: Wire Game Scene + Input - -**Priority:** Critical | **Status:** pending | **Effort:** Large - -## Overview -The main Game.svelte scene — connects game logic (grid, guards, turns, player) to the visual components and handles keyboard/click input. - -## Steps - -1. **Game.svelte** — main game orchestrator - - `$state` for: grid, player, guards, lives, level, turns, isPaused, detected, isFinalLevel - - `onMount`: call `loadLevel()` to get initial state, set up keyboard listener - - `onDestroy`: clean up keyboard listener - - Render: GameBoard + PlayerSprite + GuardSprites + GameHud + overlays - -2. **Keyboard input** - ```js - function onKeyDown(e) { - if (isPaused || !inputEnabled) return; - const dirMap = { - ArrowUp: 'up', ArrowDown: 'down', ArrowLeft: 'left', ArrowRight: 'right', - w: 'up', s: 'down', a: 'left', d: 'right', - }; - const dir = dirMap[e.key]; - if (dir) handleMove(dir); - } - ``` - - Bind to `window` via `svelte:window` directive: `` - -3. **Click-to-move input** - - GameBoard emits `oncellclick(row, col)` - - Game.svelte checks if cell is adjacent to player, calls `handleMove()` - -4. **Turn flow** (handleMove function) - ``` - 1. player.move(direction) - 2. If moved successfully: - a. Check if player at goal → handleLevelComplete() - b. clearAllLight() - c. Each guard.onTurnChange() - d. grid.render state update (triggers Svelte reactivity) - e. Check if player in lit cell → showDetection() - f. turns++ - ``` - -5. **Level 12 special mechanic** - - On each move, check `isFinalLevel && Manhattan distance to goal <= 2` - - If triggered: set ALL non-wall cells to `isLight = true` - - Show princess detected message - - This looks like normal gameplay — player won't know it's impossible until they experience it - -6. **Detection flow** - - Set `detected = true` → shows DetectionPopup - - On "Play Again": `lives--`, if lives > 0 reload level, else navigate to GameOver - -7. **Level complete flow** - - Call `completeLevel()` from progress.js - - Brief CSS flash animation on board - - Navigate to LevelIntro for next level (or GameOver for final) - -8. **Pause flow** - - Toggle `isPaused` → shows PauseMenu - - Resume / Restart / Main Menu actions - -## Reactive State Shape - -```js -// In Game.svelte -let grid = $state(null); // GridSystem instance -let player = $state(null); // Player instance -let guards = $state([]); // Guard instances -let lives = $state(3); -let currentLevel = $state(1); -let turns = $state(0); -let isPaused = $state(false); -let detected = $state(false); -let isFinalLevel = $state(false); - -// Derived for rendering -let cells = $derived(grid ? grid.getAllCells() : []); -let playerPos = $derived(player ? { row: player.row, col: player.col } : null); -let guardData = $derived(guards.map(g => ({ - row: g.row, col: g.col, type: g.constructor.name, - direction: g.direction, isOn: g.isOn, -}))); -``` - -## Success Criteria -- Arrow keys / WASD move player through grid -- Click adjacent cell moves player -- Guards update correctly each turn (static lit, rotating beam, blinking toggle, patrol movement) -- Detection popup shows when stepping on lit cell -- Level completes when reaching goal -- Level 12 lights up entire map near goal -- Pause menu works -- Lives decrement and game over triggers at 0 diff --git a/plans/260412-1113-svelte5-rewrite/phase-06-polish-verify.md b/plans/260412-1113-svelte5-rewrite/phase-06-polish-verify.md deleted file mode 100644 index 7d1ee6d..0000000 --- a/plans/260412-1113-svelte5-rewrite/phase-06-polish-verify.md +++ /dev/null @@ -1,60 +0,0 @@ -# Phase 6: Polish + Verify - -**Priority:** High | **Status:** pending | **Effort:** Small - -## Overview -Final pass — visual polish, cleanup old Phaser code, update docs, verify all 12 levels. - -## Steps - -1. **Delete old Phaser code** - - Remove entire `src/game/` directory - - Remove `vite/` directory - - Remove `log.js` - - Remove `public/style.css` (styles now in Svelte components + theme.css) - - Remove `phaser` from package.json dependencies - - Remove `terser` from devDependencies (Vite handles minification) - - Run `npm install` to clean lockfile - -2. **Visual polish** - - Verify dark theme renders correctly on all scenes - - Test responsive scaling (Scale.FIT equivalent via CSS `max-width` + `aspect-ratio`) - - Ensure grid is centered on all level sizes (6x6 through 10x10) - - Test direction indicators on rotating/patrolling guards - - Verify blinking guard color toggle - -3. **Test all 12 levels** - - Level 1: move freely, reach goal - - Levels 2-5: static guard lit cells block correct positions - - Level 6: rotating guard beam sweeps and blocks cells - - Levels 7-8: mixed guard interactions - - Level 9: blinking guard toggles on/off - - Level 10: patrolling guard moves along path - - Level 11: all 4 guard types simultaneously - - Level 12: approach goal → entire map lights up (unbeatable) - -4. **Test game flows** - - Lives system: lose 3 lives → game over screen - - Level progression: complete level → intro → next level - - Level select: locked/unlocked/completed states correct - - Progress persistence: refresh browser, progress retained - - Language switch: EN ↔ VI updates all text - - Pause menu: resume, restart, main menu all work - -5. **Update docs** - - Update `docs/system-architecture.md` — Svelte 5 architecture - - Update `docs/codebase-summary.md` — new file listing - - Update `docs/code-standards.md` — Svelte conventions - - Update `README.md` — remove Phaser references, add Svelte info - -6. **Build verification** - - `npm run build` produces working dist/ - - Serve dist/ locally, test full game - - Check bundle size (target: <50KB app code, down from ~1MB with Phaser) - -## Success Criteria -- Zero Phaser code remains -- All 12 levels playable with correct guard behavior -- Level 12 secret mechanic works -- Bundle size < 50KB (excluding assets) -- Docs updated to reflect new stack diff --git a/plans/260412-1113-svelte5-rewrite/plan.md b/plans/260412-1113-svelte5-rewrite/plan.md deleted file mode 100644 index 4214959..0000000 --- a/plans/260412-1113-svelte5-rewrite/plan.md +++ /dev/null @@ -1,124 +0,0 @@ ---- -title: Rewrite NNTV from Phaser 3 to Svelte 5 -status: ready -created: 2026-04-12 -branch: main -blockedBy: [] -blocks: [] ---- - -# Rewrite NNTV: Phaser 3 → Svelte 5 + Vite - -## Overview - -Replace Phaser 3 game engine (~1MB) with plain Svelte 5 + Vite for a turn-based grid puzzle game that only uses rectangles, circles, and text. All game logic (guards, turns, levels, lighting) ports as-is. Only the rendering/UI layer changes. - -## Architecture: Before → After - -``` -BEFORE (Phaser 3) AFTER (Svelte 5) -───────────────── ──────────────── -Phaser.Game config → App.svelte (scene router) -Phaser.Scene classes → Svelte components per scene -scene.add.circle/rect/text → HTML/CSS elements -scene.input.keyboard → window keydown events -scene.tweens.add → CSS transitions -this.scene.start() → reactive scene state -camera.fadeIn/fadeOut → svelte/transition (fade) -Phaser physics (unused) → DELETE -``` - -## File Migration Map - -| Current File | Action | New Location | -|---|---|---| -| `locales/en.json`, `vi.json` | PORT AS-IS | `src/lib/locales/` | -| `levels/Levels.js` | PORT AS-IS | `src/lib/levels/levels.js` | -| `progress.js` | PORT AS-IS | `src/lib/progress.js` | -| `localization.js` | PORT AS-IS | `src/lib/localization.js` | -| `TurnManager.js` | PORT (remove Phaser import) | `src/lib/game/turn-manager.js` | -| `GridSystem.js` | SPLIT: data logic → pure JS, rendering → Svelte | `src/lib/game/grid-system.js` + `GameBoard.svelte` | -| `Guard.js` | SPLIT: logic → pure JS, rendering → Svelte | `src/lib/game/guards.js` + `GuardSprite.svelte` | -| `Player.js` | SPLIT: logic → pure JS, rendering → Svelte | `src/lib/game/player.js` + `PlayerSprite.svelte` | -| `LightingSystem.js` | MERGE into grid-system.js | `src/lib/game/grid-system.js` | -| `LevelManager.js` | REWRITE as pure function | `src/lib/game/level-manager.js` | -| `theme.js` | REWRITE as CSS variables | `src/styles/theme.css` | -| All scenes/*.js | REWRITE as Svelte components | `src/scenes/*.svelte` | -| `main.js` | REWRITE | `src/main.js` + `src/App.svelte` | -| `LevelTester.js` | DELETE | — | - -## New Project Structure - -``` -src/ -├── main.js # Mount Svelte app -├── App.svelte # Scene router + transitions -├── styles/ -│ └── theme.css # CSS variables (colors, fonts) -├── lib/ -│ ├── localization.js # getText, setLanguage (ported) -│ ├── progress.js # localStorage persistence (ported) -│ ├── locales/ -│ │ ├── en.json # English (ported) -│ │ └── vi.json # Vietnamese (ported) -│ ├── levels/ -│ │ └── levels.js # Level data (ported) -│ └── game/ -│ ├── grid-system.js # Grid data model + lighting (pure JS) -│ ├── guards.js # Guard classes, logic only (pure JS) -│ ├── player.js # Player position + movement (pure JS) -│ ├── turn-manager.js # Turn logic (pure JS) -│ └── level-manager.js # Level loading (pure JS) -├── components/ -│ ├── GameBoard.svelte # CSS Grid rendering of cells -│ ├── PlayerSprite.svelte # Player circle element -│ ├── GuardSprite.svelte # Guard circle + direction indicator -│ ├── GameHud.svelte # Lives, level, turns, pause/menu buttons -│ ├── DetectionPopup.svelte # "You've been detected" overlay -│ ├── PauseMenu.svelte # Pause overlay -│ └── Button.svelte # Reusable styled button -└── scenes/ - ├── MainMenu.svelte - ├── StoryIntro.svelte - ├── LevelSelect.svelte - ├── LevelIntro.svelte - ├── Game.svelte # Main game scene - ├── GameOver.svelte - ├── Settings.svelte - └── Guide.svelte -``` - -## Key Design Decisions - -1. **Plain Svelte + Vite** (not SvelteKit) — no routing needed for SPA game -2. **Svelte 5 runes** (`$state`, `$derived`, `$effect`) — reactive game state -3. **CSS Grid** for game board — native, responsive, zero dependencies -4. **CSS transitions** for player movement — replace Phaser tweens -5. **`svelte/transition`** for scene switching — fade between screens -6. **Pure JS game logic** — all guard/turn/level logic stays framework-agnostic -7. **Game state as module-level `$state`** in `.svelte.js` files — shared across components - -## Phases - -| # | Phase | Status | File | -|---|-------|--------|------| -| 1 | Scaffold Svelte 5 project | pending | [phase-01](phase-01-scaffold-project.md) | -| 2 | Port pure game logic | pending | [phase-02](phase-02-port-game-logic.md) | -| 3 | Build game board + sprites | pending | [phase-03](phase-03-game-board-components.md) | -| 4 | Build scene components | pending | [phase-04](phase-04-scene-components.md) | -| 5 | Wire game scene + input | pending | [phase-05](phase-05-game-scene-input.md) | -| 6 | Polish + verify | pending | [phase-06](phase-06-polish-verify.md) | - -## Risk Assessment - -| Risk | Impact | Mitigation | -|---|---|---| -| Grid reactivity perf on 10x10 | Low | Svelte fine-grained updates; only changed cells re-render | -| CSS transition timing mismatch with game turns | Medium | Use fixed duration, test each guard type | -| Level 12 special mechanic (lightUpEntireMap) | Low | Direct DOM class toggle on all cells | - -## Dependencies - -- Svelte 5 (latest) -- Vite 6.x (already used) -- No other runtime dependencies diff --git a/plans/260413-1416-gameplay-improvements/phase-01-wait-action.md b/plans/260413-1416-gameplay-improvements/phase-01-wait-action.md deleted file mode 100644 index 691cef3..0000000 --- a/plans/260413-1416-gameplay-improvements/phase-01-wait-action.md +++ /dev/null @@ -1,54 +0,0 @@ -## Phase 1: Wait Action - -**Priority:** P1 | **Status:** Pending | **Effort:** 15min - -### Overview - -Spacebar (and tap-on-player for mobile) skips the player's move but still advances guards. -Creates timing puzzles — player must wait for a safe window. - -### Data Flow - -1. User presses Space / taps player cell -2. `handleMove` is NOT called; instead new `handleWait()` fires -3. `handleWait` calls `turnManager.nextTurn(grid, player, guards)` without moving player -4. Same detection/completion checks run -5. `renderVersion++` triggers re-render - -### Files to Modify - -**`src/scenes/Game.svelte`** (input handler only) - -### Implementation Steps - -- [ ] 1. In `onKeyDown`, add `' '` (Space) key check before direction map lookup -- [ ] 2. When Space detected, call `handleWait()` and `e.preventDefault()` -- [ ] 3. Create `handleWait()` function: - ```js - function handleWait() { - if (!player || !grid) return; - const result = turnManager.nextTurn(grid, player, guards); - if (isFinalLevel && checkFinalLevel()) { renderVersion++; return; } - renderVersion++; - if (result.levelComplete) handleLevelComplete(); - else if (result.detected) detected = true; - } - ``` -- [ ] 4. In `onCellClick`, if clicked cell === player position, call `handleWait()` - -### Edge Cases - -- Wait on level 12 still triggers princess detection expansion (covered — same `checkFinalLevel` path) -- Wait when already at goal: `nextTurn` returns `levelComplete: true` (correct) -- Wait counts as a turn for move counter (correct — `turnManager.turnCount++` happens in `nextTurn`) - -### Success Criteria - -- Space key advances turn without moving player -- Guards rotate/patrol/blink as normal -- Turn counter increments -- Detection still works if guard light reaches player after wait - -### Rollback - -Revert single commit. No schema changes. diff --git a/plans/260413-1416-gameplay-improvements/phase-02-star-rating.md b/plans/260413-1416-gameplay-improvements/phase-02-star-rating.md deleted file mode 100644 index fb1d757..0000000 --- a/plans/260413-1416-gameplay-improvements/phase-02-star-rating.md +++ /dev/null @@ -1,89 +0,0 @@ -## Phase 2: Star Rating + Par Moves - -**Priority:** P1 | **Status:** Pending | **Effort:** 2h - -### Overview - -Each level gets a `parMoves` value. On completion, show 1-3 star rating. -Persist best stars + best moves in localStorage. Display stars on level select. - -### Data Flow - -``` -Level complete → turnManager.turnCount vs level.parMoves → star rating - → completeLevel(levelNum, totalLevels, moves) → localStorage update - → LevelSelect reads stars from progress -``` - -### Star Calculation - -| Condition | Stars | -|-----------|-------| -| moves <= parMoves | 3 | -| moves <= parMoves + 3 | 2 | -| completed | 1 | - -### Files to Modify - -#### 1. `src/lib/levels/levels.js` -- [ ] Add `parMoves` field to each level definition -- [ ] Placeholder values (will be finalized in Phase 3 after redesign): - - L1: 10, L2: 12, L3: 14, L4: 14, L5: 14, L6: 16 - - L7: 16, L8: 18, L9: 18, L10: 20, L11: 22, L12: 99 (unbeatable) - -#### 2. `src/lib/progress.js` -- [ ] Extend schema: add `levelStars: {}` and `levelBestMoves: {}` to DEFAULT_PROGRESS -- [ ] `getProgress()`: merge missing keys for backwards compat -- [ ] Update `completeLevel(levelNum, totalLevels, moves, parMoves)`: - - Calculate stars - - Store best stars (only if better than existing) - - Store best moves (only if fewer than existing) - - Return `{ ...progress, stars }` so caller can display - -#### 3. `src/scenes/Game.svelte` -- [ ] Import `LEVELS` to access `parMoves` for current level -- [ ] Pass `turnManager.turnCount` and `parMoves` to `completeLevel()` -- [ ] After level complete, show star rating overlay before navigating -- [ ] Add `LevelComplete` popup component (inline or separate small component) - - Shows 1-3 stars with animation - - Shows move count vs par - - "Next Level" button - -#### 4. `src/scenes/LevelSelect.svelte` -- [ ] Read `progress.levelStars` in addition to `completedLevels` -- [ ] Replace checkmark with star display (1-3 stars shown as filled/empty) -- [ ] Show best move count under level number - -#### 5. `src/components/GameHud.svelte` -- [ ] No changes needed — already shows turn count - -### localStorage Schema (backwards compatible) - -```js -// Old format still works — missing keys get defaults -{ - maxLevel: 5, - completedLevels: [1, 2, 3, 4], - // NEW — absent in old saves, defaults to {} - levelStars: { "1": 3, "2": 2, "3": 1 }, - levelBestMoves: { "1": 8, "2": 14, "3": 20 } -} -``` - -### Edge Cases - -- Old localStorage without `levelStars`/`levelBestMoves` → defaults to `{}` -- Level 12 unbeatable → `parMoves: 99` never matters -- Player completes level with worse score → keep existing best -- `completeLevel` called with no moves param (old code path) → skip star calc - -### Success Criteria - -- Star rating appears on level complete -- Best stars persist in localStorage -- Level select shows stars per level -- Old saves don't break (backwards compat) - -### Rollback - -Revert commit. Old localStorage data unaffected (new keys simply disappear). diff --git a/plans/260413-1416-gameplay-improvements/phase-03-level-redesign.md b/plans/260413-1416-gameplay-improvements/phase-03-level-redesign.md deleted file mode 100644 index c123cc2..0000000 --- a/plans/260413-1416-gameplay-improvements/phase-03-level-redesign.md +++ /dev/null @@ -1,89 +0,0 @@ -## Phase 3: Level Redesign + Chaser Guard - -**Priority:** P1 | **Status:** Pending | **Effort:** 3h - -### Overview - -Redesign existing 12 levels for more challenge. Add **Chaser guard** type. -Finalize `parMoves` values after redesign. Level 12 remains unbeatable. - -### New Guard Type: ChaserGuard - -**Behavior:** Dormant until player enters Manhattan distance <= `detectionRadius` (default 3). -Once triggered, moves 1 cell toward player's **last known position** each turn. -Lights its own cell + adjacent cell in movement direction. Returns to start if reaches -last known position and player not visible. - -**Why Chaser over Sentry:** Chaser creates dynamic panic — player must change route. -Sentry is just a fancy static guard (cone vision = static pattern). Chaser adds genuine -reactive gameplay with minimal code. - -#### Implementation in `src/lib/game/guards.js` - -- [ ] Add `ChaserGuard` class extending `Guard`: - ``` - Properties: detectionRadius, startRow, startCol, targetRow, targetCol, - isChasing, lastKnownRow, lastKnownCol - Methods: - - onTurnChange(allGuards, player): check distance to player - - If within radius and not blocked by wall: set isChasing, record lastKnownPos - - If chasing: move 1 step toward lastKnownPos (prefer row then col) - - If reached lastKnownPos and player not in radius: return to start - - updateLight(): light own cell + 1 cell in facing direction - ``` -- [ ] Key: `onTurnChange` needs player reference — update signature - -#### Turn Manager Change (`src/lib/game/turn-manager.js`) - -- [ ] Pass `player` to `guard.onTurnChange(guards, player)` — non-breaking since - existing guards ignore extra params - -#### Level Manager Change (`src/lib/game/level-manager.js`) - -- [ ] Add `case 'chaser'` to guard factory switch - -#### Guard Sprite (`src/components/GuardSprite.svelte`) - -- [ ] Add chaser visual (red-tinted sprite, or different emoji/icon) -- [ ] Show "alert" state when `isChasing` is true - -### Level Redesign Principles - -1. **Tighter corridors** — reduce open space, force specific paths -2. **More guards per level** — current levels are sparse -3. **Timing windows** — require waiting (Phase 1) for safe passage -4. **Chaser placement** — put chasers near obvious paths to force detours -5. **Level 12** — keep walls + guard placement, only verify light wave still covers all paths - -### Level-by-Level Notes - -- [ ] **L1-2** (tutorial): Keep simple but add 1 more wall each to reduce trivial paths -- [ ] **L3-4** (rotating intro): Add extra static guard, narrow corridors -- [ ] **L5-6** (blinking): Tighter timing — reduce safe windows from 2 turns to 1 -- [ ] **L7-8** (patrolling): Add walls to force player into patrol paths, require waiting -- [ ] **L9** (decoy): Add chaser near the "obvious" short path — punishes rushing -- [ ] **L10** (mirrors): Add more mirror bounces, tighter spaces -- [ ] **L11** (throne room): Add chaser + extra patrolling guard -- [ ] **L12** (princess): DO NOT CHANGE core mechanic. May add 1-2 more guards in upper sections -- [ ] **Finalize parMoves** for each level after redesign (playtest manually) - -### Edge Cases / Risks - -| Risk | Likelihood | Impact | Mitigation | -|------|-----------|--------|------------| -| Level becomes unsolvable | Medium | High | Playtest each level; keep at least 1 valid path | -| Chaser pathfinding hits wall | Medium | Medium | Simple Manhattan step; skip move if blocked by wall | -| Level 12 becomes beatable | Low | High | Verify: expanding light wave from (9,9) covers all reachable cells within player's max possible steps | -| ChaserGuard makes levels too hard | Medium | Low | Tune detectionRadius per level (2-4 range) | - -### Success Criteria - -- All levels 1-11 are solvable but require thought -- At least 3 levels benefit from wait action -- Chaser guard creates noticeable tension when triggered -- Level 12 remains provably unbeatable -- parMoves set to optimal-path length for each level - -### Rollback - -Revert commit restores old level definitions and removes ChaserGuard class. diff --git a/plans/260413-1416-gameplay-improvements/phase-04-vision-preview.md b/plans/260413-1416-gameplay-improvements/phase-04-vision-preview.md deleted file mode 100644 index 255b325..0000000 --- a/plans/260413-1416-gameplay-improvements/phase-04-vision-preview.md +++ /dev/null @@ -1,49 +0,0 @@ -## Phase 4: Guard Vision Preview (Optional) - -**Priority:** P3 | **Status:** Pending | **Effort:** 45min - -### Overview - -Show semi-transparent overlay of cells that will be lit NEXT turn. -Helps player plan without trial-and-error. Toggle via button or hotkey. - -### Data Flow - -1. Before rendering, simulate next turn's guard positions/lights on a **cloned grid** -2. Diff current lit cells vs next-turn lit cells -3. Render "preview" cells with distinct CSS class (e.g., `cell-preview` with 20% opacity warning color) - -### Implementation - -#### `src/lib/game/turn-manager.js` -- [ ] Add `previewNextTurn(grid, guards)` method: - - Deep-clone guard states (row, col, direction, isOn, currentPathIndex, isReversing) - - Create temporary grid clone - - Run `onTurnChange` on clones - - Return Set of `"row,col"` strings for next-turn lit cells - -#### `src/scenes/Game.svelte` -- [ ] Add `showPreview` toggle state (default false) -- [ ] Bind `v` key or HUD button to toggle -- [ ] Compute `previewCells` derived from `turnManager.previewNextTurn()` when toggle on -- [ ] Pass `previewCells` to `GameBoard` - -#### `src/components/GameBoard.svelte` -- [ ] Accept optional `previewCells` prop -- [ ] Add `cell-preview` CSS class to cells in preview set - -### Edge Cases - -- Chaser guard preview needs player position — pass current player pos to preview -- Level 12 expanding wave — preview should show next ring expansion -- Performance: cloning 5-10 guards per frame is trivial for grid sizes <= 10x10 - -### Success Criteria - -- Toggle shows/hides next-turn danger zones -- Preview visually distinct from current lit cells (different opacity/color) -- No performance impact on turn processing - -### Rollback - -Revert commit. Preview is purely additive UI — no game logic changes. diff --git a/plans/260413-1416-gameplay-improvements/plan.md b/plans/260413-1416-gameplay-improvements/plan.md deleted file mode 100644 index 9bf2fba..0000000 --- a/plans/260413-1416-gameplay-improvements/plan.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -title: "Gameplay Improvements: Wait Action, Star Rating, Level Redesign" -description: "Add wait mechanic, star/par rating system, and redesign levels for challenge" -status: done -priority: P1 -effort: 6h -branch: main -tags: [gameplay, levels, ux] -created: 2026-04-13 ---- - -# Gameplay Improvements Plan - -## Phases - -| # | Phase | Effort | Status | Files Modified | -|---|-------|--------|--------|----------------| -| 1 | [Wait Action](phase-01-wait-action.md) | 15min | Done | Game.svelte | -| 2 | [Star Rating + Par Moves](phase-02-star-rating.md) | 2h | Done | levels.js, progress.js, Game.svelte, LevelSelect.svelte, LevelCompletePopup.svelte | -| 3 | [Level Redesign + Chaser Guard](phase-03-level-redesign.md) | 3h | Done | levels.js, guards.js, level-manager.js, GuardSprite.svelte, theme.css | -| 4 | [Guard Vision Preview](phase-04-vision-preview.md) | 45min | Done | GameBoard.svelte, Game.svelte, turn-manager.js, GameHud.svelte | - -## Dependency Graph - -``` -Phase 1 (Wait) ── no deps, standalone -Phase 2 (Stars) ── no deps, standalone -Phase 3 (Levels) ── after Phase 1 + 2 (par values depend on wait mechanic existing) -Phase 4 (Preview) ── after Phase 3 (needs final guard configs) -``` - -## File Ownership (no parallel conflicts) - -- Phase 1: Game.svelte only (input handler) -- Phase 2: progress.js, LevelSelect.svelte, GameHud.svelte, + level complete flow in Game.svelte -- Phase 3: levels.js, guards.js, level-manager.js, GuardSprite.svelte -- Phase 4: GameBoard.svelte (new overlay layer) - -## Key Constraints - -- Level 12 MUST remain unbeatable (expanding light wave from goal covers all paths) -- No new npm dependencies -- All state stays in pure JS classes; Svelte 5 reactivity via renderVersion bump pattern -- localStorage schema must be backwards-compatible (existing `nntv-progress` key) - -## Rollback - -Each phase is a single commit. Revert commit to rollback. No data migrations needed -(localStorage schema only adds optional fields, old data still works). - -## Success Criteria - -1. Spacebar wait works, guards advance, player stays -2. Stars show on level complete + level select; persist across sessions -3. Levels require more thought; at least 3 levels need wait action to solve optimally -4. Level 12 still unbeatable (manual verification) -5. (Optional) Ghost overlay shows next-turn danger zones diff --git a/plans/reports/researcher-260412-1127-svelte5-grid-puzzle-game.md b/plans/reports/researcher-260412-1127-svelte5-grid-puzzle-game.md deleted file mode 100644 index ae480b3..0000000 --- a/plans/reports/researcher-260412-1127-svelte5-grid-puzzle-game.md +++ /dev/null @@ -1,293 +0,0 @@ -# Svelte 5 Research: Grid Puzzle Game Rewrite - -**Status:** Complete | **Focus:** Runes, setup, transitions, grid patterns, state management -**Date:** 2026-04-12 | **Scope:** Actionable patterns for turn-based grid game - ---- - -## 1. Svelte 5 Runes Syntax — Reactive State - -### Core Pattern: $state for Grid -Runes are compiler keywords (not functions), prefixed with `$`. Use `$state` to declare reactive cell data. - -**Grid state pattern (array of cells):** -```javascript -let grid = $state([ - { id: 0, type: 'wall' }, - { id: 1, type: 'floor' }, - // ... 16+ cells -]); -``` - -**For object-based grid:** -```javascript -let gridMap = $state(new Map([ - ['0,0', { type: 'wall', solved: false }], - ['0,1', { type: 'puzzle', value: 5 }], -])); -``` - -### $derived — Computed Grid State -Auto-updates when dependencies change (memoized). Zero side-effects allowed. - -```javascript -let solvedCount = $derived(grid.filter(c => c.solved).length); -let isWon = $derived(solvedCount === grid.length); -let gridDisplay = $derived(grid.map(c => c.type)); // safe memo -``` - -### $effect — Side Effects (Subscriptions, DOM, Analytics) -Runs after DOM update. Return cleanup function for teardown. - -```javascript -$effect(() => { - console.log('Game state changed:', grid); - localStorage.setItem('game', JSON.stringify(grid)); -}); - -$effect(() => { - return () => console.log('Component unmounting'); // cleanup -}); -``` - -### Key Gotchas -- Avoid mutations inside `$derived`: `$derived(count++)` **fails**. -- `$state` works outside `.svelte` files (in `.svelte.js` / `.svelte.ts`). -- Runes replace old `let x = 0; $: x = x + 1` reactive declarations. - ---- - -## 2. Svelte 5 + Vite Setup - -### Recommended: Use `sv` CLI (SvelteKit) -```bash -npx sv create my-puzzle-game -# Select: Svelte, TypeScript, Vitest, Prettier -cd my-puzzle-game -npm run dev -``` - -Scaffold includes: Vite (pre-configured), TypeScript, dev server at `localhost:5173`. - -### Alternative: Bare Svelte + Vite -```bash -npm create vite@latest my-game -- --template svelte-ts -npm install -npm run dev -``` - -Simpler but misses SvelteKit's routing/build optimizations. **For a static SPA game, either works.** - -### Vite Config (if bare Svelte) -`vite.config.ts` auto-includes `vite-plugin-svelte`. No manual config needed for basic setup. - ---- - -## 3. Scene Transitions (Fade/Fly/Slide) - -### Pattern: Conditional Scenes -```svelte - - -{#if currentScene === 'intro'} -
-

Intro Scene

- -
-{:else if currentScene === 'game'} -
- -
-{:else if currentScene === 'win'} -
-

You Won!

-
-{/if} -``` - -### Available Transitions -| Name | Use | Params | -|------|-----|--------| -| `fade` | Opacity 0→1 | `duration`, `delay`, `easing` | -| `fly` | Position + opacity | `x`, `y`, `duration` | -| `slide` | Axis-based slide | `axis` ('x'/'y'), `duration` | -| `scale`, `blur` | Also available | config object | - -### Bidirectional -`transition:` works both in and out. Reverses smoothly mid-animation if toggled. - ---- - -## 4. CSS Grid for Game Board - -### Reactive Grid Layout -```svelte - - -
- {#each grid as cell (cell.id)} -
handleCellClick(cell.id)}> - {cell.id} -
- {/each} -
- - -``` - -### Performance Note -Svelte's fine-grained reactivity means updating a single cell **doesn't re-render all others**. Safe for responsive grids (6x6, 8x8). - ---- - -## 5. State Management: Stores vs. Runes - -### For Game Progress + Localization: Use Runes in `.svelte.js` - -**gameState.svelte.js:** -```javascript -export const game = $state({ - level: 1, - moves: 0, - isWon: false, - grid: [], -}); - -export const i18n = $state({ - lang: 'en', - strings: { title: 'Puzzle Game', play: 'Play' }, -}); - -export function resetLevel() { - game.level = 1; - game.moves = 0; - game.isWon = false; -} -``` - -**App.svelte:** -```svelte - - -

{i18n.strings.title}

-

Level {game.level} • Moves: {game.moves}

- -``` - -### When to Use Stores (Deprecated Pattern) -Only for **async data streams** (polling, WebSocket). Most games don't need this. - -### Why Runes > Stores -- No boilerplate (no `writable()`, `.subscribe()`). -- Works in plain `.js` files (shared logic). -- Memoization via `$derived`. - ---- - -## 6. SvelteKit vs. Plain Svelte for Static Game - -### Recommendation: **Use Plain Svelte + Vite** - -| Criteria | Plain Svelte | SvelteKit | -|----------|--------------|-----------| -| **Setup time** | 2 min | 3 min | -| **Routing** | Manual | File-based (unnecessary) | -| **Build output** | Single HTML/JS | Server capable | -| **Static deploy** | ✅ Simpler | ✅ Needs adapter-static | -| **DevTools** | Vite only | Vite + SvelteKit CLI | -| **Scaling** | Hits limits ~2-3 scenes | Extensible | - -### For a Static SPA Game -```bash -npm create vite@latest my-game -- --template svelte-ts -``` - -This is **KISS**. No file-based routing overhead, no server code, direct control. - -### Only Use SvelteKit If -- Game needs **server-side scoring/leaderboards** (then use SvelteKit backend). -- Game will expand to **10+ pages** (use file-based routing). -- Team prefers **unified framework** across multiple projects. - ---- - -## Implementation Checklist - -### Phase 1: Setup -- [ ] `npm create vite@latest` (svelte-ts template) -- [ ] Replace Vite's default App.svelte with game component -- [ ] Verify `npm run dev` opens at localhost:5173 - -### Phase 2: Grid & State -- [ ] Create `gameState.svelte.js` with `$state(grid)` -- [ ] Build `GameBoard.svelte` with CSS Grid + `{#each grid}` -- [ ] Wire `$derived(isWon)` from grid state - -### Phase 3: Interactions -- [ ] Add cell click handlers → update grid state -- [ ] Bind to `$effect` for persistence (localStorage) -- [ ] Test fine-grained reactivity (click cell, only that cell re-renders) - -### Phase 4: Transitions -- [ ] Import `fade`, `fly`, `slide` from `svelte/transition` -- [ ] Wrap scenes in conditional blocks with `transition:` directives -- [ ] Test bidirectional transitions (scene switches) - -### Phase 5: Deploy -- [ ] `npm run build` → outputs `dist/` (static) -- [ ] Upload `dist/` to any static host (Vercel, GitHub Pages, Netlify) - ---- - -## Trade-offs & Adoption Risk - -| Risk | Mitigation | -|------|-----------| -| Svelte 5 is new (2024+) | Use LTS minor versions; runes are stable now | -| Team unfamiliar with runes | Runes are **simpler** than Vue/React hooks; quick ramp | -| Grid performance at scale | Fine-grained reactivity handles 100+ cells easily | -| Transitions can stutter | Use `duration: 200–400ms`; test on target devices | - ---- - -## Sources - -- [Svelte Runes Documentation](https://svelte.dev/docs/svelte/what-are-runes) -- [Svelte Transitions](https://svelte.dev/docs/svelte/svelte-transition) -- [Svelte Stores](https://svelte.dev/docs/svelte/stores) -- [SvelteKit Single-Page Apps](https://svelte.dev/docs/kit/single-page-apps) -- [Getting Started](https://svelte.dev/docs/svelte/getting-started) -- [Mainmatter: Runes and Global State](https://mainmatter.com/blog/2025/03/11/global-state-in-svelte-5/) -- [Frontend Masters: Game Board Grid](https://frontendmasters.com/courses/svelte-v2/game-board-grid/) - ---- - -## Unresolved Questions - -- **WebSocket multiplayer?** Not covered; requires SvelteKit backend or third-party service. -- **Mobile touch events?** Standard DOM events work; no Svelte-specific guidance needed. -- **Save file encryption?** Depends on backend; out of scope for static game.