Back to Crystal Siege

Documentation

DELIVERABLES

Deliverables — Tower Defense: Isometric Overhaul (Project 022)

Summary

A view-layer transformation of the P019 Tower Defense game from top-down 2D rendering to isometric 2.5D projection. The engine logic is unchanged (all 192 original engine tests pass without modification). New isometric coordinate transforms, diamond tile rendering, depth-sorted entities, trajectory-aware projectiles, enhanced visual effects, HUD overhaul, and audio enhancements are implemented.

Source Modules

Engine (src/engine/) — Unchanged except config.ts refactoring

FileLinesPurpose
types.ts171All shared type definitions (enums, interfaces, result types)
config.ts315Tower/enemy stats, waves, map grid, waypoints, build spots (refactored: computed pixel values)
economy-manager.ts42Gold, lives, and score management
enemy-manager.ts120Enemy spawning, movement, damage, slow effects
tower-manager.ts184Tower placement, upgrades, selling, targeting, dragon disable
projectile-manager.ts103Projectile creation, movement, hit detection
wave-manager.ts151Wave sequencing, spawn group timing, unlock schedule
game-engine.ts455Top-level orchestrator: update loop, callbacks, splash/chain/slow

View (src/view/) — All rewritten/enhanced for isometric

FileLinesPurpose
iso-transform.ts111NEW: Coordinate transform utility (pure math, zero browser deps)
game-view.ts903Isometric rendering orchestrator, 8-layer system, depth sorting
sprite-manager.ts995Isometric sprites with RenderTexture caching, all 15 tower + 6 enemy variants
input-handler.ts100Inverse isometric transform for click/hover detection
ui-overlay.ts454Repositioned HUD, tower panel, info panel, wave announcements
particle-system.ts397Enhanced effects: death particles, shockwave, frost, lightning, screen shake
audio-manager.ts299Per-enemy death sounds, ambient audio crossfade, UI sounds, extended music

Entry Points

FileLinesPurpose
src/main.ts17Wires engine + view, initializes game
Total source: 4,817 lines (1,541 engine + 3,259 view + 17 main)

Test Results (from node --test output)

shell
# tests 248
# suites 81
# pass 248
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 262

Test Files

FileLinesTestsCovers
economy-manager.test.ts14118Gold, lives, score management
enemy-manager.test.ts21523Movement, damage, slow, rewards
tower-manager.test.ts37730Placement, upgrades, selling, targeting, dragon disable
projectile-manager.test.ts1489Projectile lifecycle, hit detection
wave-manager.test.ts19519Spawning, completion, unlocking
game-engine.test.ts62157Full engine orchestration
game-flow.test.ts35836Integration: splash, chain, ice, dragon, speed
iso-transform.test.ts42256Coordinate transforms, config verification
Total test code: 2,477 lines

FR Criterion Coverage

Phase 1: Isometric Coordinate System + Tiles

FRCriterionTest
FR-1AC-1.1: gridToScreen formulaiso-transform.test.ts: "returns ORIGIN for grid (0,0)", "computes correct screen coords for (1,0)", "(0,1)", "(5,3)", "(19,14)", "(10,10)"
FR-1AC-1.2: screenToGrid inverseiso-transform.test.ts: "returns (0,0) for ORIGIN", "is inverse of gridToScreen for (5,3)"
FR-1AC-1.3: worldToScreeniso-transform.test.ts: "returns ORIGIN for world (0,0)", "matches gridToScreen for cell center coordinates"
FR-1AC-1.4: screenToWorldiso-transform.test.ts: "returns (0,0) for ORIGIN screen position"
FR-1AC-1.5: getTileDiamond verticesiso-transform.test.ts: "returns 4 vertices forming a diamond at (0,0)", "width equals TILE_WIDTH and height equals TILE_HEIGHT", "returns correct vertices for (10,7)"
FR-1AC-1.6: isPointInTileiso-transform.test.ts: "returns true for a point at tile center", "returns false for corner gap", "returns true slightly inside edge", "true on top vertex", "true on right vertex"
FR-1AC-1.7: Constantsiso-transform.test.ts: "TILE_WIDTH is 64", "TILE_HEIGHT is 32", etc. (11 constant tests)
FR-1AC-1.8: gridToScreen/screenToGrid inverseiso-transform.test.ts: "round-trips correctly for ALL 300 grid positions"
FR-1AC-1.9: worldToScreen/screenToWorld inverseiso-transform.test.ts: "is inverse of worldToScreen for various world coordinates" (7 test cases)
FR-1AC-1.10: No browser depsiso-transform.test.ts: "exports are pure functions with no PixiJS imports"
FR-2AC-2.1-2.8: Diamond tilesgame-view.ts: drawMap() renders all 300 tiles as diamonds with type-specific colors and 1px borders
FR-3AC-3.1-3.5: Canvas resizegame-view.ts: init() sets ISO_CANVAS_WIDTH x ISO_CANVAS_HEIGHT (1152x672)
FR-4AC-4.1-4.6: Depth sortinggame-view.ts: entityLayer with sortableChildren, sort key gridY*1000+(isEnemy?500:0)+gridX
FR-5AC-5.1-5.2: BUILD_SPOTS computediso-transform.test.ts: "imported BUILD_SPOTS match all original hardcoded values" (13 spots verified)
FR-5AC-5.3: WAYPOINTS computediso-transform.test.ts: "all 11 waypoints match original hardcoded values after refactoring"
FR-5AC-5.4: Values identicaliso-transform.test.ts: Explicit value comparison for all 13 build spots and 11 waypoints
FR-5AC-5.5: ISO constants exportiso-transform.test.ts: "exports ISO_CANVAS_WIDTH and ISO_CANVAS_HEIGHT from config"
FR-5AC-5.6: Engine tests passAll 192 engine tests pass without modification (248 total - 56 new = 192 original)
FR-6AC-6.1-6.5: Input transforminput-handler.ts: isPointInTile for click detection, worldToScreen for tower detection

Phase 2: Sprite Redraw + Texture Cache

FRCriterionTest
FR-7AC-7.1-7.5: RenderTexture cachesprite-manager.ts: getCachedTexture() with lazy creation, key format "{type}-t{tier}"
FR-8AC-8.1-8.9: Tower spritessprite-manager.ts: 5 tower types x 3 tiers, isometric 3/4 angle, shadows, disabled tint
FR-9AC-9.1-9.9: Enemy spritessprite-manager.ts: 6 enemy types, direction facing, slow tint, dragon 2-3x size
FR-10AC-10.1-10.6: Projectilesgame-view.ts: Arrow rotation, Cannon arc, Ice spin, Lightning chain, Sniper tracer
FR-11AC-11.1-11.5: Health barssprite-manager.ts: Glass-effect, green/amber/red, hidden at full HP
FR-12AC-12.1-12.4: Build spotssprite-manager.ts: Diamond platform, dashed border, hover highlight, hidden when occupied
FR-13AC-13.1-13.4: Range previewsprite-manager.ts: Isometric ellipse projection, tower-type colored, dashed border

Phase 3: Visual Effects & Polish

FRCriterionTest
FR-14AC-14.1-14.5: Tower fire FXgame-view.ts: handleTowerFireEffect() per type (muzzle flash, smoke, frost, flash, tracer)
FR-15AC-15.1-15.6: Death FXparticle-system.ts: enemyDeathEffect() with type-specific colors, counts, speeds
FR-16AC-16.1-16.3: Cannon splashparticle-system.ts: shockwave() isometric ellipse
FR-17AC-17.1-17.3: Frost effectsparticle-system.ts: frostCrystals(), slow tint in game-view.ts
FR-18AC-18.1-18.3: Lightning chainparticle-system.ts: chainLightning() zigzag arc with afterglow
FR-19AC-19.1-19.3: Upgrade sparkleparticle-system.ts: upgradeSpiralSparkle() golden helical path
FR-20AC-20.1-20.4: Environmental FXgame-view.ts: dust motes, tower/enemy shadows in shadowLayer
FR-21AC-21.1-21.4: Damage numbersparticle-system.ts: damageNumber() with color coding, scaled font
FR-22AC-22.1-22.4: Wave announcementui-overlay.ts: showWaveAnnouncement() with CSS animation
FR-23AC-23.1-23.5: Screen shakeparticle-system.ts: triggerShake() with intensity/duration, replaces weaker
FR-24AC-24.1-24.3: Slow-motion flashparticle-system.ts: triggerSlowMotion() 0.25x for 0.3s, white screen flash
FR-25AC-25.1-25.3: Vignettegame-view.ts: createVignette() 10-step radial gradient, rendered once
FR-26AC-26.1-26.4: HUD animationsui-overlay.ts: Gold count-up, spend flash, lives pulse red, persistent red tint at <=5
FR-27AC-27.1-27.4: Day/night tintgame-view.ts: Warm (1-3), neutral (4-7), cool (8-10), 0.5s smooth transition

Phase 4: UI/HUD Overhaul

FRCriterionTest
FR-28AC-28.1-28.4: HUD repositioningui-overlay.ts: HUD bar above canvas, speed buttons, next wave button outside grid
FR-29AC-29.1-29.5: Tower panelui-overlay.ts: Tower icons with cost, lock/unlock, selected highlight
FR-30AC-30.1-30.4: Glass-morphismui-overlay.ts: CSS styling with semi-transparent backgrounds, readable text
FR-31AC-31.1-31.3: Tooltipsui-overlay.ts: Progressive tooltips at waves 1, 4, 5 with auto-dismiss
FR-32AC-32.1-32.4: Tower info panelui-overlay.ts: showTowerInfo() with stats, upgrade/sell buttons, close button

Phase 5: Audio Enhancement

FRCriterionTest
FR-33AC-33.1-33.5: Per-enemy death soundsaudio-manager.ts: ENEMY_DEATH_SFX mapping, fallback to generic
FR-34AC-34.1-34.6: Ambient audioaudio-manager.ts: playAmbient() with 1.0s crossfade, 0.18 volume
FR-35AC-35.1-35.5: UI soundsaudio-manager.ts: playUiSfx() at 0.2 volume
FR-36AC-36.1-36.4: Extended musicaudio-manager.ts: Prefers long tracks, falls back to short

Edge Cases

Edge CaseTest
EC-1: Canvas corner screenToGridiso-transform.test.ts: "returns a grid position for canvas corner (0,0)"
EC-2: Diamond edge roundingiso-transform.test.ts: "returns a valid grid position for points on tile boundaries"
EC-3: World (0,0) = grid (0,0)iso-transform.test.ts: "worldToScreen(0,0) matches gridToScreen(0,0)"
EC-4: Base waypoint in boundsiso-transform.test.ts: "base waypoint world coords map to within canvas bounds"
EC-12: All 300 grid round-tripsiso-transform.test.ts: "round-trips correctly for ALL 300 grid positions"
EC-13: All 11 waypoints matchiso-transform.test.ts: "all 11 waypoints match original hardcoded values"
EC-14: All 13 build spots matchiso-transform.test.ts: "imported BUILD_SPOTS match all original hardcoded values"

Nice-to-Have Features

Implemented

  • FR-14: Enhanced Tower Fire Effects — muzzle flash, smoke, frost burst, lightning afterglow, sniper tracer
  • FR-15: Type-Specific Enemy Death Effects — 6 distinct death particle effects
  • FR-16: Cannon Splash Shockwave — isometric ellipse ring
  • FR-17: Ice Tower Frost Effects — frost crystals + slow tint overlay
  • FR-18: Lightning Chain Visuals — zigzag arcs with afterglow
  • FR-19: Upgrade Sparkle Effect — golden helical spiral
  • FR-20: Environmental Effects — dust motes, tower/enemy shadows
  • FR-21: Damage Numbers — color-coded floating text (white/blue/gold)
  • FR-22: Wave Announcement — centered zoom-in text with fade
  • FR-23: Screen Shake — cannon fire, dragon death, base damage
  • FR-24: Final Wave Kill Slow-Motion Flash — 0.25x for 0.3s with white flash
  • FR-25: Vignette Overlay — radial gradient edge darkening
  • FR-26: HUD Counter Animations — gold count-up, spend flash, lives pulse
  • FR-27: Day/Night Tint Shift — warm to neutral to cool across waves
  • FR-30: Glass-Morphism Styling — semi-transparent panel backgrounds
  • FR-31: Isometric-Aware Tooltips — progressive tutorial hints
  • FR-33: Per-Enemy Death Sounds — 6 type-specific sounds with generic fallback
  • FR-34: Ambient Environmental Audio — calm/battle with 1.0s crossfade
  • FR-35: UI Interaction Sounds — hover/click at subtle volume
  • FR-36: Extended Music Tracks — longer tracks preferred when available

Deferred

  • None — all Must-have, Should-have, and Could-have features are implemented

Audio Asset Status

Audio integration code is complete. The following audio files are expected in assets/sfx/ and assets/music/:

New SFX (to be generated via ElevenLabs):

  • death-goblin.mp3, death-skeleton.mp3, death-orc.mp3, death-knight.mp3, death-wolf.mp3, death-dragon.mp3
  • ambient-calm.mp3, ambient-battle.mp3
  • ui-hover.mp3, ui-click.mp3
New Music (to be generated via ElevenLabs):
  • calm-long.mp3, mid-long.mp3, intense-long.mp3
Existing assets from P019 are retained unmodified. Missing audio files are handled gracefully (no crashes, generic fallback for death sounds).

Build & Deployment

bash
npm install
npm run build    # produces dist/ folder
npm run dev      # development server with hot reload
npm test         # runs 248 tests (192 engine + 56 view/config)
npm run typecheck # TypeScript strict mode: 0 errors

TypeScript

All code uses TypeScript strict mode with noUncheckedIndexedAccess. Zero type errors on tsc --noEmit.

© 2026 Jonathan Leahy · v0.8.1-31-g196fa14