Commit Graph
92 Commits
Author SHA1 Message Date
tiennm99 9fea2ecc9d chore(i18n,art,audio): finalize EN/VI strings, pixel art, audio cues, docs
Finalize EN+VI translations (42 new keys). Add 6 procedural Web Audio cues
(stone-throw, impact, key-pickup, door-unlock, suspicion-alert, suspicion-fire).
Pixel art for sniper, suspicion (3 tiers), stones, doors+keys (3 colors),
one-way arrows, warm cells. Foreshadowing rewrite for L10-L12 reflecting
level-restart model. Docs updated for v2.
2026-04-26 08:27:33 +07:00
tiennm99 4e7b58405a feat(ui): add throw-targeting, affordance gates, suspicion overlays, key inventory
Render new tile types (door/key/one-way/warm) and new guard sprites (sniper
triangle+beam, suspicion ring). Five new HUD components. Throw-targeting state
machine (E enter, arrows/click move, Enter/click confirm, Esc cancel). Per-level
affordance gates wired to Z/Y/V key handlers. GameHistory snapshots extended to
round-trip keysHeld + key/door cell state for correct undo.
2026-04-26 08:27:30 +07:00
tiennm99 bb8d3a61d0 feat(levels): redesign 11 solvable levels around new mechanics
Full L1-L11 overhaul around 6-mechanic palette. One mechanic intro per level
(one-way L3, suspicion L4, doors+keys L5, decay L6, mirror L7, sniper L8,
stones L9). L10-L11 compounding. L12 byte-identical (unsolvable narrative
preserved). All BFS-verified under 2M nodes.
2026-04-26 08:27:28 +07:00
tiennm99 f498284c9c feat(player): enforce doors, one-ways, key auto-collection on movement
Player.moveTo enforces wall/door/one-way validation with direction-of-approach
matching. keysHeld bitmask auto-collects on stepping on key cell. Capture/apply
includes inventory state.
2026-04-26 08:27:26 +07:00
tiennm99 8f22138774 refactor(death): replace lives counter with level-restart on detection
Drop run-wide lives state. Detection routes to level-restart instead of
game-over. Repurpose GameOver scene for run-complete (L11) and bittersweet
(L12) flows. One-shot save migration wipes legacy lives field with welcome
modal.
2026-04-26 08:27:25 +07:00
tiennm99 e550600218 feat(solver): canonicalize state hash, extend BFS for new mechanics, 2M node cap
Replace JSON.stringify with stable canonical state hash. Extend captureState
with stones, keys, key/door cell snapshots, warm timers. Enumerate throw-target
actions with eligibility pruning. MAX_BFS_NODES = 2_000_000 with par-based path
pruning. All 11 solvable levels under cap, L12 unsolvable preserved.
2026-04-26 08:27:23 +07:00
tiennm99 09f804c2ea feat(engine): add sniper, suspicion, throwable, doors/keys/one-way cells
New cell flags (door, key, one-way, warm). SniperGuard with line-of-sight beam
+ mirror reflection. SuspicionGuard with 3-tier meter. ThrowableSystem with
stone-throw distraction resolved before guard turn. Capture/apply round-trip
preserves all new state.
2026-04-26 08:27:21 +07:00
tiennm99 89943829c0 chore(levels): tighten L1/L2 par values to optimal+2/+4
L1 par 26\u219222 (slack 2). L2 par 30\u219224 (slack 4, leaves room for
wait-strategy on wilting tomatoes). Other levels already at spec.
2026-04-21 23:29:21 +07:00
tiennm99 ba687ea130 feat(game): wilting tomato mechanic replaces static light
Static guards now emit a Manhattan aura that shrinks by 1 per turn until
harmless (clamped at -1 to keep solver state finite). Level data schema
change: 'static' guards now take 'initialRadius' instead of 'litCells'.
All 27 static-guard entries migrated in one pass.

L1 and L2 redesigned with real forced-zigzag walls — solver path is now
20 moves (vs Manhattan 14), so players must move AWAY from the goal to
reach it. L2 adds three wilting tomatoes parked on the zigzag path as
a gentle intro to the mechanic.

Tests: +3 new tests on StaticGuard wilting behavior; all 70 tests pass.
2026-04-21 22:40:53 +07:00
tiennm99 05cc053cb6 docs: sync README and docs with redesigned levels
Reflect new grid sizes (8x8-13x13), chaser guard type, L12 easter egg,
and solvability CI guard. Remove lingering mobile-support claims.
Delete completed level-redesign plan directory (preserved in git history).
2026-04-21 21:41:33 +07:00
tiennm99 90e6958d41 docs(nntv): document solver, viewport, and L12 easter egg
Enable wall-light overlap lint (previously skipped); clean up remaining
overlaps in L4, L5, L8, L11. Add solver and easter-egg sections to
system-architecture.md. Extend code-standards.md review checklist with
solvability requirements.
2026-04-21 21:35:26 +07:00
tiennm99 02f85ef115 feat(levels): redesign L12 Princess Chamber and add console easter egg
L12 widened to 13x13 with 10 guards including 2 chasers and a mirror.
Remains unsolvable by normal play (BFS-verified). window.__nntvDev.teleport
exposed as an intentional escape hatch — console-savvy users who invoke it
win the game. Hidden from README and in-game UI.
2026-04-21 21:31:14 +07:00
tiennm99 2f122d1896 feat(levels): redesign L11 Throne Room on 12x12 grid
Climactic Act 6 level — 9 guards covering ALL six guard types
(static, rotating, blinking, patrolling, mirror, chaser). BFS-proven
solvable in 22 moves. All playable levels (L1-L11) now verified
solvable; only L12 remains unsolvable by design.
2026-04-21 21:29:20 +07:00
tiennm99 f3394bd15f feat(levels): redesign L9 and L10 on 12x12 grid
Act 5 redesign — chaser guard introduced. L9 has 8 guards with the
chaser ambushing the short route, forcing a perimeter detour. L10 is
the Hall of Mirrors with 9 guards (2 rotating + 3 mirrors + chaser).
Both solvable under 50 ms via BFS.
2026-04-21 21:28:03 +07:00
tiennm99 014a55199d feat(levels): redesign L7 and L8 on 11x11 grid
Act 4 redesign — mirror guard introduced. L7 has 7 guards including
one mirror that deflects a rotating beam. L8 has 8 guards with two
mirrors creating a crossfire pattern.
2026-04-21 21:26:32 +07:00
tiennm99 aa3ea39b71 feat(levels): redesign L5 and L6 on 10x10 grid
Act 3 redesign — patrolling guard introduced in L5 (6 guards total).
L6 widens with two patrollers, two blinkers, and a rotating guard
(7 guards). Both solvable via BFS solver.
2026-04-21 21:23:57 +07:00
tiennm99 0aad28e81d feat(levels): redesign L3 and L4 on 9x9 grid
Act 2 redesign — rotating guard (L3) and blinking guard (L4) introduced.
Both levels widened to 9x9. L4 solution requires a wait action to
synchronize with the blinking cycle, teaching the timing mechanic.
2026-04-21 21:22:30 +07:00
tiennm99 33f6c98a14 fix(levels): redesign L1 and L2 on 8x8 grid
L2 right-side region was disconnected — player could not reach the goal
via any sequence of moves (verified via BFS). New L2 places three static
guards with two viable paths. L1 widens to 8x8 as a gentle Act 1 intro.
Preserves id/name/storyKey/isFinalLevel for progress persistence.
2026-04-21 21:20:25 +07:00
tiennm99 bfed64d871 test(levels): add solvability suite with metadata invariants
Asserts L1/5-8/10 solvable, L12 unsolvable (easter-egg invariant), and
locks the 12-level count. Known-broken levels (L2/3/4/9/11) tracked
explicitly and auto-unskip when their redesign phases flip them.
2026-04-21 21:20:19 +07:00
tiennm99 19e3e9cb67 feat(game): add BFS level solver for solvability validation
Pure BFS over (player, guard snapshots, princess state). Reuses guards.js
and princess-mechanic.js so solver verdicts match runtime behavior. State
budget cap prevents runaway search on impossible levels.
2026-04-21 21:20:12 +07:00
tiennm99 0bf72c50d8 feat(game): scrollable viewport with camera-follow for large grids
Fix cellSize at 50 and wrap board-container with overflow:auto + smooth
scrollTo effect centered on player. Unlocks grid sizes beyond 9x9 without
shrinking cells below readable size.
2026-04-21 21:20:04 +07:00
tiennm99 6e2114162f docs(plans): add level redesign brainstorm and 11-phase plan
Fixes L2 unsolvable bug and widens grids across all 12 levels.
L12 preserved as unsolvable-by-normal-play (easter egg).
2026-04-21 21:19:56 +07:00
dependabot[bot] 692d90cafb build(deps): bump rollup from 4.40.0 to 4.60.1 (#7)
Bumps [rollup](https://github.com/rollup/rollup) from 4.40.0 to 4.60.1.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.40.0...v4.60.1)

---
updated-dependencies:
- dependency-name: rollup
  dependency-version: 4.60.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-20 22:55:58 +07:00
dependabot[bot] 11860bcda3 build(deps-dev): bump vite from 6.3.6 to 6.4.2 (#6)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.3.6 to 6.4.2.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.4.2/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.4.2/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 6.4.2
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-20 22:54:45 +07:00
tiennm99 fb88e86d9a chore: ignore local .claude/ scratch dir 2026-04-20 22:46:27 +07:00
tiennm99 656c9ea504 fix: use relative asset base for portable build output
Built index.html now references ./assets/... instead of /assets/...
so the dist folder works when opened directly (file://), served from a
subpath (github pages project site, netlify deploy preview, etc.), or
any non-root path. Previously the absolute-root paths produced a blank
page with module load failures in those environments.

Also ignore .claude/ local scratch dir.
2026-04-20 22:46:14 +07:00
tiennm99 932516ca3e refactor: address code review — capture/apply contract, tests, docs
- Guards: add capture()/apply() dynamic-state snapshot contract; removes fragile per-type field lists in previewNextTurn and game-history
- TurnManager.previewNextTurn: use guard.capture/apply so new dynamic fields are picked up automatically
- PrincessMechanic: add capture()/apply() and include messageShown in undo snapshots
- Game.svelte: navigate to MainMenu when loadLevel returns null; track detection setTimeout for unmount cleanup
- LevelSelect: use getTotalLevels() instead of hardcoded 12
- audio.js: swallow resume() promise rejections
- art-scenes: norm() pads short rows with '.' transparent instead of last-char bleed
- Rename controls-overlay.svelte → ControlsOverlay.svelte (match PascalCase convention)
- Add vitest + tests for grid, player, guards, princess, turn-manager, game-history (46 tests)
- Update docs (codebase-summary, system-architecture, code-standards, README) for pixel pipeline and capture/apply contract
2026-04-20 22:22:03 +07:00
tiennm99 fb2f72f85b feat: apply pixel-art design assets across sprites, tiles, hud, and scenes 2026-04-20 22:03:16 +07:00
tiennm99 6a1b3ec572 feat: add design canvas assets from claude design 2026-04-20 21:46:21 +07:00
tiennm99 286edc3c1d remove completed plan and report files
Plans for Svelte 5 rewrite and gameplay improvements have been
fully implemented — removing the now-obsolete plan phases and
research report.
2026-04-14 11:43:03 +07:00
tiennm99 7233310662 feat: add undo/redo, audio, mobile controls, BFS pathfinding, and accessibility
- Fix ChaserGuard with BFS pathfinding and proper chase/return states
- Add undo/redo system (Z/Y keys) with full state snapshots
- Add procedural audio via Web Audio API (move, wait, detection, complete)
- Add mobile swipe controls with touch gesture detection
- Add detection feedback (cell flash, player shake animation)
- Add CSS transitions on grid cells for smooth lighting changes
- Add ARIA accessibility labels on game board and cells
- Add controls overlay ("?" button) showing all keyboard/touch shortcuts
- Add mute toggle in HUD
- Update Guide scene with chaser/mirror guard descriptions and tips
- Replace guard switch statement with factory registry pattern
- Extract princess mechanic and touch controls into separate modules
- Localize all UI strings (EN/VI) including new controls and tips
- Update README for Svelte 5 architecture with all current features
- Update project docs (architecture, code standards, codebase summary)
2026-04-13 18:24:46 +07:00
tiennm99 7aa81730a6 feat: add gameplay improvements — wait action, star rating, chaser guard, vision preview
- Wait action (Space/click player): skip turn while guards advance, enables timing puzzles
- Star rating system: 1-3 stars per level based on par moves, persisted in localStorage
- Level complete popup with animated star display and move count vs par
- Stars and best moves shown on level select screen
- ChaserGuard: new guard type that chases player's last known position when nearby
- All 12 levels redesigned with tighter corridors, more guards, and chasers in Act 5
- Guard vision preview (V key/button): ghost overlay shows next-turn danger zones
- Level 12 remains intentionally unbeatable with added chaser
2026-04-13 14:47:42 +07:00
tiennm99 710d7dec27 docs: add game design document with full level and mechanic specs
Complete game design requirements: story, 5 guard types, exact 12-level
spec (levels 1-11 solvable, level 12 unsolvable by design), escalating
princess detection, visual design, controls, UI screens, localization.
2026-04-12 23:35:10 +07:00
tiennm99 40a5b6218f docs: update all project docs for Svelte 5 rewrite and remove old plan
Rewrote project-overview-pdr, system-architecture, code-standards, and
codebase-summary to reflect current Svelte 5 architecture, mirror guard
mechanic, renderVersion reactivity pattern, and redesigned level
progression. Removed obsolete plan/NNTV.md (Phaser 3 plan).
2026-04-12 23:16:48 +07:00
tiennm99 0a00209881 fix: resolve Svelte 5 reactivity for class instances and redesign levels
Svelte 5 does not proxy class instances, so self-assignment (grid = grid)
was a no-op. Introduced renderVersion counter to force re-derivation of
all rendering data after game state mutations.

Also redesigned level progression with better mechanic teaching arc,
added Mirror Guard type (reflects rotating beams), and replaced instant
final-level detection with escalating light wave radiating from goal.
2026-04-12 22:58:05 +07:00
tiennm99 a1b9f38dea fix: downgrade vite-plugin-svelte to v6 for vite 6.x compatibility
@sveltejs/vite-plugin-svelte@7 requires vite@^8.0.0 but we use
vite@6.3.6. Downgrade to v6 which supports vite@^6.3.0.
Fixes npm ci failure in GitHub Actions deploy workflow.
2026-04-12 20:01:28 +07:00
tiennm99 f52dfde719 feat: rewrite from Phaser 3 to Svelte 5
Replace Phaser 3 game engine (~1MB) with Svelte 5 + Vite.
Bundle size: 77KB JS (28KB gzip) vs ~1MB with Phaser.

Architecture:
- src/lib/game/ — Pure JS game logic (guards, grid, player, turns)
- src/components/ — Svelte UI components (GameBoard, sprites, HUD)
- src/scenes/ — Scene components with fade transitions
- src/App.svelte — Scene router
- CSS Grid for game board, CSS transitions for movement
- svelte/transition for scene switching

All game logic preserved: 12 levels, 4 guard types, turn system,
detection, lives, progress persistence, bilingual localization,
level 12 special mechanic.

Removed: Phaser, terser, canvas rendering, physics engine,
custom vite configs, log.js telemetry.
2026-04-12 18:58:19 +07:00
tiennm99 4461e45502 fix: reset game state flags in init() so input works on level transitions
Phaser constructor only runs once; init() runs on every scene.start().
handleLevelComplete() was setting isPaused=true/inputEnabled=false which
persisted into subsequent levels, freezing player input.
2026-04-12 11:14:31 +07:00
tiennm99 35d3f45d6f feat: redesign architecture, UI/UX, and story-driven levels
- Fix StaticGuard ignoring litCells and RotatingGuard not lighting
  facing direction
- Fix guard cleanup leak (direction indicators not destroyed)
- Add centralized theme system with dark stealth aesthetic
- Add scene fade transitions and level completion animation
- Add level progress persistence via localStorage
- Add turn counter, locked/completed level states in LevelSelect
- Redesign 12 levels across 6-act narrative arc with per-level story
- Add LevelIntro scene showing story text before each level
- Add bilingual (EN/VI) level story descriptions
- Remove dead code (counter.js, duplicate game/main.js)
- Initialize project docs (overview, architecture, code standards)
2026-04-12 10:53:04 +07:00
dependabot[bot] 7eb454443c build(deps-dev): bump vite from 6.3.4 to 6.3.6
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.3.4 to 6.3.6.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.3.6/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.3.6/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 6.3.6
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-10 08:45:30 +07:00
tiennm99 103170db25 feat: update guard logics 2025-05-03 14:39:48 +07:00
tiennm99 8d0ff8ff03 feat: remove camera follow 2025-05-03 14:31:04 +07:00
tiennm99 df5c0c051c feat: intro 2025-05-03 14:26:08 +07:00
tiennm99 e3939d4132 feat: update story 2025-05-03 14:14:06 +07:00
tiennm99 f38b0d6865 fix: center game 2025-05-03 13:32:07 +07:00
tiennm99 44db5a4470 feat: update readme, merge style 2025-05-03 13:26:23 +07:00
tiennm99 09bf0863a4 feat: remove mobile buttons 2025-05-03 13:20:25 +07:00
tiennm99 b209662a67 Merge branch 'main' of https://github.com/tiennm99/nntv 2025-05-03 13:20:12 +07:00
dependabot[bot] 45b480753d build(deps-dev): bump vite from 6.3.2 to 6.3.4
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.3.2 to 6.3.4.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.3.4/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 6.3.4
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-03 13:12:55 +07:00
tiennm99 d81e800609 feat: merge plans 2025-05-03 13:10:39 +07:00