{"id":51332685,"url":"https://github.com/itsgitz/metal-slug-pwa","last_synced_at":"2026-07-02T00:04:09.517Z","repository":{"id":362994561,"uuid":"1261225666","full_name":"itsgitz/metal-slug-pwa","owner":"itsgitz","description":"Metal Slug game but it's PWA for everyone.","archived":false,"fork":false,"pushed_at":"2026-06-06T21:50:06.000Z","size":169,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-06-06T23:18:33.972Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://metal-slug-pwa.vercel.app","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/itsgitz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-06T12:04:36.000Z","updated_at":"2026-06-06T21:50:10.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/itsgitz/metal-slug-pwa","commit_stats":null,"previous_names":["itsgitz/metal-slug-pwa"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/itsgitz/metal-slug-pwa","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itsgitz%2Fmetal-slug-pwa","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itsgitz%2Fmetal-slug-pwa/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itsgitz%2Fmetal-slug-pwa/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itsgitz%2Fmetal-slug-pwa/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/itsgitz","download_url":"https://codeload.github.com/itsgitz/metal-slug-pwa/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itsgitz%2Fmetal-slug-pwa/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35027345,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-01T02:00:05.325Z","response_time":130,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-02T00:04:08.791Z","updated_at":"2026-07-02T00:04:09.509Z","avatar_url":"https://github.com/itsgitz.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Metal Slug PWA\n\nBrowser-based 2.5D side-scrolling run-and-gun game in the spirit of Metal Slug. Runs offline as a Progressive Web App. No server, no login — open the URL and play instantly.\n\n**Live:** https://metal-slug-pwa.vercel.app/\n\n---\n\n## Tech Stack\n\n| Layer | Choice |\n|-------|--------|\n| UI | Svelte 5 (runes) + SvelteKit |\n| Game core | Plain TypeScript (no framework deps) |\n| Renderer | Three.js, orthographic camera, procedural geometry |\n| Build | Vite via SvelteKit |\n| Package manager / test runner | Bun |\n| PWA | `@vite-pwa/sveltekit` (Workbox) |\n| Deploy | Vercel + `@sveltejs/adapter-static` |\n\n---\n\n## Getting Started\n\n```bash\nbun install       # install deps (first time only)\nbun run dev       # dev server → http://localhost:5173\nbun test          # run game-core unit tests\nbun run build     # production build → build/\nbun run preview   # serve production build → http://localhost:4173\n```\n\n### Controls\n\n| Key | Action |\n|-----|--------|\n| A / ← | Move left |\n| D / → | Move right |\n| W / ↑ / Space | Jump |\n| Z | Shoot |\n| X | Throw grenade |\n\nTouch controls appear on mobile.\n\n---\n\n## Project Structure\n\n```\nsrc/\n  lib/game/\n    types.ts              — Entity, World, ActionMap interfaces\n    events.ts             — Pure event bus (GameEvent union, subscribe/emit)\n    state.svelte.ts       — Runes store bridge (HUD ↔ game core)\n    loop.ts               — Fixed-timestep game loop (60 Hz)\n    entities/\n      player.ts           — Player physics, shoot, grenade, muzzle flash timer\n      enemies.ts          — Soldier (patrol), turret (aim), drone (hover)\n      boss.ts             — 3-phase tank boss, fire patterns\n      bullet.ts           — Trajectory + off-screen cull\n      grenade.ts          — Arc, bounce, fuse, splash (GRENADE_DAMAGE vs boss)\n      terrain.ts          — Static ground/platform entity factories\n    systems/\n      collision.ts        — AABB overlaps, terrain landing, processCollisions\n      input.ts            — ActionMap, KeyboardAdapter, edge detection\n      scoring.ts          — Points, lives, hi-score localStorage\n      boss-ai.ts          — 3-phase state machine (HP thresholds)\n      spawning.ts         — Camera-gated spawn trigger\n    stages/\n      stage-1.ts          — Jungle Outpost (200u, boss HP 300)\n      stage-2.ts          — Industrial Fortress (250u, pit gap, boss HP 500)\n      types.ts            — StageConfig, GroundSegment, Platform, SpawnEntry\n      validate.ts         — Stage invariant checks\n    render/\n      scene.ts            — WebGLRenderer, OrthographicCamera, lighting\n      renderer.ts         — Entity→mesh lifecycle, interpolation, animation hook\n      factories/\n        types.ts          — AnimatedParts, AnimState, AnimateFn, AnimLocal\n        player-mesh.ts    — Articulated player (hip/shoulder pivots, walk cycle)\n        enemy-meshes.ts   — Soldier (marching), turret (recoil), drone (rotors)\n        boss-mesh.ts      — Tank (tread scroll, cannon pivot, phase emissive)\n        fx-mesh.ts        — Explosion shard pool, bullet pool, flash quad\n        terrain-mesh.ts   — Ground and platform box meshes\n        background-mesh.ts — Parallax mountains + buildings, sky gradient\n    audio/\n      sfx.ts              — Procedural WebAudio SFX engine (DI AudioContext)\n  routes/\n    +page.svelte          — Canvas, HUD, overlays, touch controls, event bus wiring\n    +layout.svelte        — PWA update banner\n```\n\n---\n\n## Architecture Highlights\n\n### Game Core is Framework-Free\n\nEverything in `src/lib/game/` (except `state.svelte.ts`) is plain TypeScript with no browser, Svelte, or Three.js imports. This keeps the game-core `bun test`-able in isolation. The render layer and audio engine subscribe to the same event bus that the core emits on.\n\n### Event Bus\n\n`events.ts` exposes a `GameEvent` union and `createEventBus()`. The game core emits events (`shoot`, `explosion`, `enemy-death`, `boss-phase`, etc.) that the render layer (FX triggers) and audio engine both subscribe to. Presentation never reaches into core; core never knows about audio or Three.js.\n\n### Entity Lifecycle\n\nEntities are plain objects with an `update(dt, world)` method. `World.spawn()` and `World.kill()` mutate the entity list. After each fixed-step update, dead entities are swept out. The renderer's `syncEntities()` mirrors this — it adds meshes for new entities and removes + explodes meshes for dead ones.\n\n### Animation\n\nMesh factories return `THREE.Group` with named pivot sub-groups and attach `parts` + `animate` to `group.userData`. The renderer reads these when creating a `MeshEntry` and calls `animate(root, parts, animState, dt, local)` each render frame. Walk cycles are time-driven (not position-driven) to avoid interpolation jitter.\n\n### Terrain \u0026 Collision\n\nGround segments and platforms from stage configs are spawned as static `TerrainEntity` objects. One-way platforms: snap only when `vy ≤ 0` and player's bottom is within the top slab tolerance. Ground is always solid. Falling below `y \u003c -5` triggers pit death.\n\n---\n\n## Game Systems\n\n| System | Description |\n|--------|-------------|\n| Boss HP | Bullet deals `BULLET_DAMAGE = 10`, grenade deals `GRENADE_DAMAGE = 50`; boss spawns at camera trigger position |\n| Scoring | Enemy kills: soldier 100, turret 200, drone 150, boss 5000; stage clear bonus |\n| Lives | 3 lives; enemy bullet / contact / pit fall deducts 1; game over at 0 |\n| Parallax | Two background layers (mountains + buildings) at different `x = cameraX * factor` |\n| Audio | WebAudio synthesized SFX; lazy `AudioContext` init; mute toggle persisted to `localStorage` |\n\n---\n\n## Test Coverage\n\n108 tests across 11 files (all game-core logic). Three.js render layer and Svelte HUD verified visually per project convention.\n\n```bash\nbun test\n# 108 pass / 0 fail\n```\n\n---\n\n## Docs\n\nFull architecture, game loop, systems, rendering, stage data, PWA deploy, and TDD strategy: [`docs/`](docs/) directory. Start with [`docs/00-overview.md`](docs/00-overview.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fitsgitz%2Fmetal-slug-pwa","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fitsgitz%2Fmetal-slug-pwa","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fitsgitz%2Fmetal-slug-pwa/lists"}