God of Sparrow 太歲 — Developer Wiki · single source of truth for contributors (human or LLM)

HK Mahjong roguelite · single-file HTML5 game · targeting Steam 0.1 (PC) → 0.2 (macOS + Mobile)

Design & Test Pages

🀄 The Gamesparrow.html — the entire game (deployed as index.html) 📖 Player Wikiroguelite-style reference: rules, 144 charms, items, 39 bosses. Auto-generated from game data. ⚙ How the Game WorksMechanics reference written from the live code: tiles, blind flow, scoring/fan, run structure, dice, economy, walls & stakes, controls 🎨 Art, Sound & Music ReviewEvery shipped image + music bed at review size, captioned from game data, orphan detection. Regenerate: tools/build-art-review.py 🎚️ Audio Test BenchEvery SFX + all music rooms with scrub timelines & shared volume settings 📈 Economy SimulatorBalance/design simulator for gold, targets and scaling ⚖️ Design ComparisonSystem-by-system design comparison (items · relics · bosses · consumables) + a sibling-project side-by-side. Local/dev only — not deployed to the public site. 📋 ChangelogEvery change, newest first — REQUIRED reading before touching code

Architecture

FileWhat it is
sparrow.htmlThe whole game — CSS + HTML + JS in one file (~14k lines). Deployed to web as index.html. Key data blocks: JOKER_POOL (144 charms), CONSUMABLE_POOL (66), VOUCHER_POOL (31), BOSS_POOL (39) + BOSS_LORE, WALL_TYPES/STAKE_TYPES (8+8), SIGIL_TYPES, EDITION_TYPES, and the game object G.
audio.jsWeb Audio engine (window.GameAudio): procedural SFX (incl. mahjong clack()), scoring ensemble, music-room manager that swaps in audio/music/m_<room>.ogg (7 rooms) over a procedural fallback pad. Per-bus volumes persisted in localStorage. Audio hooks are monkey-patched onto G at the bottom of sparrow.html.
serve-mahjong.jsDev static server on 0.0.0.0:5502 (Tailscale-reachable). No-cache headers, audio MIME types, HTTP Range support. MUST serve .ogg as audio/ogg.
images/39 boss portraits (boss_<id>.webp, 560×840), landing art, transparent title PNGs. Top level only is deployed.
audio/music/8 MusicGen beds (m_title/blind/boss/shop/pack/victory/defeat/credits.ogg) — seamless loops.
tools/Dev tooling — see Tools below.
electron/Steam wrapper scaffold (Electron, mirrors the Melder project approach).
STEAM_PLAN.mdThe release plan — roadmap, role-by-role analyses, art manifest, LLM handoff instructions.

Golden Rules (do these EVERY session)

  1. Backup before editing: copy sparrow.htmlrevisions/MMddyyyyHHmm.html.
  2. Every save: update changelog.txt (top, newest first) and the in-game tutorial if gameplay changed.
  3. Deploy with sync-once.ps1 (or tools/deploy.bat) — pushes game→index.html, audio, wiki, images/ + audio/music/ diffs to W:\sparrow and SFTP.
  4. NEVER sync imagegen/ to the web, copy API keys out of ~/.claude/openai-config.json, or echo keys anywhere.
  5. Use the Tailscale IP 100.122.243.71:5502 (not localhost) in anything shown to the user.
  6. After regenerating data (charms/bosses/etc.), run tools/build-wiki.bat so the player wiki stays in sync.

Tools (tools/)

ToolPurpose
start-server.batRun the dev server (auto-restarts if it crashes)
deploy.batOne-click deploy (wraps sync-once.ps1: backup + W: + SFTP)
backup.batTimestamped revision backup of sparrow.html
smoke-test.bat / smoke-test.js46 CI-style checks: JS syntax, asset presence (39 portraits, 8 music beds, titles), data integrity, server MIME config, security leak scan, regression guards. Run before every deploy (deploy.bat gates on it).
qa-data.js20 deep data-spec checks (rarity distribution, unique ids, fx/apply presence, voucher require-chains). See QA_MATRIX.md.
check-syntax.jsParse-checks every inline <script> in sparrow.html without executing — catches broken edits instantly.
make_steam_art.pyBuilds all 8 Steam capsule sizes + electron/icon.png/.ico from a master image.
build-wiki.bat / build-wiki.jsRegenerate wiki-data.json from sparrow.html (validates portraits + counts)
gen-music.bat / gen_music.pyRegenerate music beds (MusicGen-large, seamless loops) + transcode to OGG. Args = track names for single-track re-rolls.
electron/build-app.batPackage the Steam build (Electron → .exe; .app on macOS)

In-game QA hooks

Asset pipelines (local, zero-cost)

IMAGES  imagegen server (port 5511, gpt-image; NEVER deploy this folder) → PNG → images/
MUSIC   tools/gen_music.py → MusicGen-large (audiocraft venv, RTX 5090) → seamless-loop WAV → ffmpeg → audio/music/*.ogg
SFX     procedural in audio.js (no files needed) — audition via audio-test.html

Rebuild From Scratch — full tech-stack spec

If this codebase were lost, or you are a new developer/LLM with no access to prior sessions, this section + wiki-data.json (all game data) + images/ + audio/ is enough to rebuild the game.

1. Stack (deliberately boring)

Runtime      Vanilla HTML + CSS + ES2020 JS. ONE file: sparrow.html (~14k lines). No framework, no build step, no npm deps.
Audio        Web Audio API (audio.js): procedural SFX synth + <audio>-element music rooms w/ WebAudio gain crossfade.
Persistence  localStorage only: save blob (gos_save), unlocks, volumes (gos_vol/gos_musicvol/gos_sfxvol/gos_muted).
Rendering    Absolutely-positioned DOM divs on a 1280×720 virtual canvas, scaled by scaleGameRoot(); NO <canvas>.
Desktop      Electron wrapper (electron/) — loads the same files unmodified. Target: Steam Deck-class HW.
Server       Any static file server. MUST serve .ogg as audio/ogg + support Range requests (see serve-mahjong.js).

2. Source-of-truth data (all exported to wiki-data.json)

JOKER_POOL[144]      {id, icon, name(EN 中文), rarity C|U|R|L, cost, col, desc, fx(ctx)…, bane?}   charms
CONSUMABLE_POOL[66]  {id, icon, name, type gem|scroll|rune|spirit, cost, col, desc, apply(G)}
VOUCHER_POOL[31]     {id, icon, name, cost, tier, requires?, desc, effect flags}
WALL_TYPES[8]        decks: {id, icon, name, col, desc, reward, mod flags}     STAKE_TYPES[8] additive difficulty
EDITION_TYPES / SIGIL_TYPES                                                    BOSS_POOL[39] {id, icon, name, minAnte, descFn, effect flags} + portraits images/boss_<id>.webp
MJ_SUITS/MJ_HONOURS/MJ_FLOWERS  tile definitions   ANTE_BASE[8] score targets  BLIND_NAMES

3. Core algorithms (the 20% that is the game)

RNG        Mulberry32 seeded (RNG object) — EVERYTHING random flows through it so runs are seed-reproducible.
Deal       Wall = 4×(suits+honours)+flowers, minus wall-type removals; Fisher-Yates shuffle w/ RNG.
Validate   validate(meldTiles): Pung=3 identical · Kong=4 · Chow=3+ consecutive same suit · wilds adopt meld identity.
           GUARD: [].every() returns TRUE — every all-X check must test length>0 first (bit us: 1.2B exploit).
Scoring    eat = Σ(meld baseChips + tile enhancements) × (mult from fan 番 table + charms).
           Fan table: HK classics (碰碰糊+3, 混一色+3, 清一色+7, 字一色/清老頭 limit=20…); min-fan gate rises w/ ante.
Blinds     8 antes × (Small ×1.0 · Big ×1.5 · Boss ×2.0 target from ANTE_BASE, stake-scaled). Boss applies its effect flags.
Economy    reward = base + $1/unused draw + interest floor(gold/5) cap $5 (voucher-raisable); shop = 2 charms+3 items+1 voucher, reroll cost escalates.
Charms     fx(ctx) hooks fire at named phases (onScore, onDraw, perMeld, blindStart/End…). Neighbor charms read the
           charm ROW ORDER — order is a mechanic (drag-to-reorder).
Save       JSON blob of full run state incl. RNG cursor; save each blind end; NEEDS saveVersion guard (top 0.1 risk).

4. Rebuild order (tested path, ~7 milestones)

1 tiles+deal+hand UI → 2 meld validate+eat scoring w/ fan table → 3 blind loop+antes+gameover
4 shop+economy+consumables → 5 charm fx engine (phase hooks) → 6 bosses+stakes+editions/sigils
7 audio+saves+landing/setup UI. At each step: add a smoke-test assertion; keep bilingual EN/繁中 strings.

5. Asset regeneration (no hand art needed)

Item icons   tools/gen-icons.py (PIL) → images/ic_<id>.webp — 265 tile-style cards from wiki-data.json. Deterministic.
Boss art     imagegen server (gpt-image) — prompts per boss in BOSSES.md style: HK Teddyboy manhua portrait 560×840.
Titles       white brush lettering on black → alpha-key + tint + outline (scratchpad scripts in changelog Jul 4).
Music        tools/gen-music.bat [room] → MusicGen-large prompts per room (genre table in gen_music.py). 8 rooms.
Steam art    tools/make_steam_art.py from a master poster → 8 capsule sizes + electron icon.
For LLM contributors: read OPUS_HANDOFF.md then STEAM_PLAN.md first — roadmap, done-vs-missing, exact conventions, and every known trap. Do not restructure sparrow.html into modules before 0.1; single-file is a deliberate choice until the Electron wrapper stabilises.