diff --git a/desktop/main.js b/desktop/main.js index 2cdad76..8d1fddd 100644 --- a/desktop/main.js +++ b/desktop/main.js @@ -39,6 +39,8 @@ function installProtocol() { }); } +const DEV = process.argv.includes('--dev'); + function createWindow() { const win = new BrowserWindow({ fullscreen: true, // borderless windowed fullscreen on Windows @@ -50,6 +52,9 @@ function createWindow() { preload: path.join(__dirname, 'preload.js'), contextIsolation: true, nodeIntegration: false, + // forward --dev into the preload's process.argv (custom app flags aren't + // guaranteed to appear there otherwise); enables the hidden debug console + additionalArguments: DEV ? ['--ms-dev'] : [], }, }); Menu.setApplicationMenu(null); diff --git a/desktop/preload.js b/desktop/preload.js index 68e9817..c5d49f0 100644 --- a/desktop/preload.js +++ b/desktop/preload.js @@ -2,4 +2,7 @@ const { contextBridge, ipcRenderer } = require('electron'); contextBridge.exposeInMainWorld('MSDesktop', { quit: () => ipcRenderer.send('ms-quit'), + // true only when launched with --dev (forwarded as --ms-dev via additionalArguments); + // enables the hidden debug console + dev: process.argv.includes('--ms-dev') || process.argv.includes('--dev'), }); diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md new file mode 100644 index 0000000..4b9ee7b --- /dev/null +++ b/docs/CHANGELOG.md @@ -0,0 +1,158 @@ +# Changelog + +All notable changes to **MeatSlicer** are documented here. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +--- + +## [0.5.0] — 2026-07-30 + +### Added +- **Hidden in-game debug console** — a dev-only cheat/inspection toolset opened with the backtick `` ` `` key from play or pause, gated behind a launch flag so it stays undiscovered in normal play (`MeatSlicer.exe --dev`, `npm start -- --dev`, `play-dev.bat`, or `localStorage.meatslicer_dev='1'`). Eight tabs reusing the field-manual canvas UI: + - **Player** — hp/maxHp/shield/level, full-heal, kill, grant levelups, force the perk draft, teleport to cursor, and god-mode / infinite-ammo / one-shot-kill toggles + - **Weapons** — equip any of the 16 weapons (old weapon goes to the holster), refill/set ammo, drop at cursor, spawn item pedestal + - **Items** — give, remove, or set exact tier for any item via a stat-rebuild; presets for one-of-everything, max-everything, clear-build + - **Perks+Active** — grant any of the 22 perks by id, remove drafted perks, equip/fire any active item + - **World** — warp to any floor, teleport to the boss/item room, clear/kill room, spawn any of the 11 enemy types (count + elite) or any of the 9 bosses by id, spawn pickups + - **Pressure** — set the pressure value or dial numerically, freeze pressure, live gain/relief readout + - **Stats** *(read-only)* — the full `p.stats` table in two scrollable columns, diff-highlighted against defaults, plus powerScore, entity counts, and a wall-clock FPS meter + - **Misc** — timescale (0.25×/1×/4×), freeze + single-frame step, render visualizers (collision circles, arena bounds, enemy HP numbers, hazard shapes, magnet radius), set score/kills/streak, trigger game over, SFX/FX test buttons, and a pin-live mode that keeps the world running behind the panel +- **Run tainting** — any mutating debug action marks the run so best-score persistence is skipped; the HUD shows a `[DEBUG]` tag and the game-over screen shows "score not recorded". Read-only tools do not taint +- **Item/perk removal via stat rebuild** — `defaultPlayerStats()` factory extracted from `initPlayer()`, plus `p.perks` tracking in `grantPerk()`, enabling `debugRebuildStats()` to reset and re-apply the whole build +- **`applyPressureDelta()`** — single mutation point for `G.pressure` (hurt relief, room decay, room-clear gain) so the debug pressure-lock can freeze difficulty in one place +- **Dev documentation** — `docs/dev-debug.md` (kept out of the player-facing manual) + +### Changed +- **`spawnBoss()` accepts an optional forced boss index** so the console can spawn any boss by id +- **`useActive()` accepts an optional force flag** to fire from the debug console outside play mode + +### Fixed +- **Positional audio NaN guard** — `Sfx.output()` no longer produces a silent/broken node when a caller passes a positionless object (this is why the debug BOSS ROAR test was inaudible); `pan`/`distanceGain` are only computed for finite coordinates + +--- + +## [0.4.0] — 2026-07-30 + +### Added +- **Confirmed menu and desktop exits** — explicit confirm dialog before quitting +- **Locked gore death and enemy health pips** — visual health indicators on enemies, gore-styled death animation locked behind kill state +- **Variable room shapes and themes** — rooms are no longer uniform rectangles; different shapes and visual themes appear across the descent +- **Pressure dial expanded to 21 notches** — finer granularity on the pressure mechanic (previously 11 notches) +- **Five pressure-monster archetypes** — new enemy types that spawn based on pressure level +- **Boss stairs sealed after victory** — stairs to the next floor are blocked until the boss is defeated; the seal breaks on victory + +### Changed +- **Pause actions and duplicate weapons polished** — pause menu interactions refined; duplicate weapon handling improved + +### Fixed +- **Desktop exit disabled in browsers** — the desktop-specific quit action no longer appears when running in a browser + +--- + +## [0.3.0] — 2026-07-30 + +### Added +- **Per-weapon ammo refill system** — every special weapon gets an explicit `refill` value (units per ammo pickup, tuned so one pickup funds ~10 kills), replacing the `ammoRefillFraction` log2 heuristic and `ammoWeight` overrides +- **Stream weapons** — Bile Blunderbuss and The Cauterizer join Red Right Hand as time-denominated stream weapons (`drain` per second) instead of per-shot cost +- **Boss room ammo** — boss rooms now stock one ammo pickup on entry and drop one guaranteed ammo on death +- **Calibration tool** — `tools/ammo_sim.js` calibrates every weapon into the 8–13 kills/refill band (all 15 pass); smoke test guards the ammo structure + +### Changed +- **Sawblades ricochet through enemies** — bounce fix makes them chain effectively +- **Traps catch 3 victims** — increased from lower cap +- **Lobbed projectiles arc overhead** — Flesh Masher, Trap Queen, and Swarm Jar payloads now reliably deploy at the target point +- **Marrow Draught** now covers stream drain +- **Balance trimming**: Cauterizer burn (2.2s×2 → 1.5s×0.8), bile acid pools (3.5s×1.5 → 2.5s×0.8) + +### Removed +- **Unused `wpn-hemophage-2.mp3`** — stale audio asset deleted + +--- + +## [0.2.0] — 2026-07-30 + +### Changed +- **Toast durations are now per-toast** — `addToast` accepts a third argument for display duration; passive item and active item pickups display for **5.5s** instead of the default 2.5s + +--- + +## [0.1.0] — Pre-release (2026-07-29 – 2026-07-30) + +The initial series of commits that built the game from scratch. Grouped by feature area. + +### Added — Core Game +- **Adaptive progression and item expansion** *(`8ab0857`)* — initial commit with 298 files establishing the entire game codebase: player, enemies, bosses, weapons, items, perks, HUD, rooms, bullets, particles, audio, music, sprites, and the smoke-test harness +- **Doom-style weapon audio batch** *(`64ac534`)* — all 13 weapon fire one-shots and 4 weapon loops regenerated in a chunky, bass-heavy 1993 Doom style via ElevenLabs +- **New SfxBank clips**: `ui_active`, `ui_active_empty` (active items), `ui_curse` (legendary pickups), `plr_revive` (Second Skin revive) + +### Added — Items & Progression +- **Item system overhaul** *(`4f46d54`)*: + - Item rarity bands (`common`/`uncommon`/`rare`/`legendary`) with per-item tier caps + - Weighted source pools (elites → common, item rooms → uncommon, bosses → rare/legendary) + - Rarity-weighted powerScore to cap difficulty inflation from common stacks + - 14 new passives including Chill Gland (activates `slowOnHit`) and Hooked Sinew (activates `magnetPull`) + - Rarity-tinted pedestal glow + nameplates, rarity-sorted HUD implant tray + - Generated 14 item icons; asset cache v=38 +- **Phase 3: 12 hook passives** *(`a93802e`)* — Dead Weight (execute), Cauterized Veins (burn bonus), Pain Engine, Iron Lung, Slaughter Rhythm, Hollow Choir, Sawbone Coil, Glutton's Gut, Thresher Plate, Blood Moat, Meat Hook, Blood Debt +- **Phase 4: 10 boss-exclusive legendaries** *(`f955232`)* — Butcher's Oath, Second Skin, Twin Sidearm, Crimson Metronome, Abattoir Engine, Gore Crown, Thousand Teeth, Hollow Father, The Last Cut, Meat Grinder (cap 1, boss-only, reset per floor) +- **Phase 5: Active items** *(`0ec12cd`)*: + - 10 actives: Bone Nova, Offal Bomb, Blood Transfusion, Cleaver Storm, Butcher's Bell, Marrow Draught, Slaughter Time, Panic Room, Skinner's Coin, Gut Reroll + - Charges accrue +1/combat room, +2/boss + - Dedicated active pickup with weapon-style swap-drop; 30% boss / 15% item-room bonus pedestal spawns + - HUD active slot with charge pips + READY state; ACTIVES help page +- **Differentiate duplicates** *(`4f46d54`)* — Grafted Trigger, Crow Bait, Hollow Bones, Twin Hearts + +### Added — Pressure & Difficulty +- **Pressure dial** *(`a02cff5`)* — title-screen dial (-5..+5) tunes pressure rise/relief; score multiplied by live pressure +- **500ms spawn telegraph** *(`a02cff5`)* — wave spawns telegraph before appearing + +### Added — Weapons +- **Rebalanced weapon tiers** *(`f71d202`)* via quantitative DPS/range/accuracy/pool scoring: + - T0: repeater, marrow + - T1: cleaver, saw, hemophage, eye + - T2: bile, guthook, fleshmasher, trapqueen, redhand + - T3: cauterizer, tenderizer, spinaltap, swarmjar +- **Ammo economy tightened** *(`4dad22d`)* — 25% less ammo per box, 25% fewer ammo drops + +### Added — HUD & UI +- **In-game field manual** *(`3f856e4`)* — 7-page technical manual (controls, run loop, arsenal, mutations, implants, bestiary, pressure/defense math); renders live from game tables +- **Vitals HUD** *(`a02cff5`)* — two-row hearts, half-container outlines, half-heart shield pips +- **HUD opacity slider** *(`a02cff5`)* — in pause menu; boss bar + toasts stay opaque +- **Readable 8-page field manual** *(`a02cff5`)* — bigger panel + fonts, implants split across pages +- **Help manual layout fix** *(`d5b1828`)* — tabs shrink to fit 920px panel; implant rows spaced at 26px + +### Added — Visual & Environment +- **Bigger gore-red Minis** *(`a02cff5`)* — radius 10→14, sprite 32→48 with regenerated art +- **Wider walkable doorways** *(`a02cff5`)* — clear passage for open doors, teeth only when locked +- **Item stickiness ramps** *(`a02cff5`)* — 10%/owned item (cap 50%) instead of flat 50% +- **Generated perk and item icons** *(`9ba0e22`)* — via `gen_assets.py` +- **Player documentation** *(`c76d126`)* — docs for items, perks, bosses, and gameplay +- **Desktop build** *(`956f116`)* — Electron app with custom `app://` protocol, borderless fullscreen, single-instance lock, and `MS_VERIFY` diagnostic harness + +### Changed +- **Bone Knit** *(`8cf9932`)* — now a chance-based room heal: 3% roll per stack to mend half a heart on combat room clear (was guaranteed) +- **Scrap Feed** *(`a02cff5`)* — diminishing returns like Thick Hide +- **Ghoul Heart** *(`f955232`)* — now +4 HP (2 full hearts) +- **Brass Magazine** *(`f955232`)* — ammo efficiency uses diminishing additive formula (same as Scrap Feed) +- **Audio rebatch** *(`64ac534`)* — weapon fire sounds regenerated in Doom-style +- **Pinned Electron** `@40.10.2` + `electron-builder@25.1.8` for Node 20 compatibility + +### Removed +- **Footstep system** *(`64ac534`)* — removed entirely (8 `plr_step*` samples, `Sfx.footstep`, player.js call site, smoke test block) + +--- + +## Legend + +| Symbol | Meaning | +|--------|---------| +| `Added` | New features | +| `Changed` | Changes in existing functionality | +| `Fixed` | Bug fixes | +| `Removed` | Removed features | + +--- + +*Generated from the git log. Last commit: v0.5.0* diff --git a/docs/README.md b/docs/README.md index 13de947..8b6b211 100644 --- a/docs/README.md +++ b/docs/README.md @@ -15,6 +15,13 @@ MeatSlicer is an endless top-down arena roguelite. You are the Butcher, descendi | [Enemies](enemies.md) | All 11 enemy types, elites, drops, wave and scaling formulas | | [Bosses](bosses.md) | All 9 bosses: rotation, scaling, full attack patterns, counters | +For developers: + +| Doc | Contents | +|---|---| +| [Debug Console](dev-debug.md) | Hidden dev-only cheat/inspection toolset (backtick `` ` `` key, `--dev` flag): all 8 tabs, run tainting, stat-rebuild internals | +| [Changelog](CHANGELOG.md) | Release history by version | + ### Quick numbers - **16 weapons** (1 infinite-ammo sidearm + 15 special weapons across 4 tiers) diff --git a/docs/dev-debug.md b/docs/dev-debug.md new file mode 100644 index 0000000..f7da314 --- /dev/null +++ b/docs/dev-debug.md @@ -0,0 +1,118 @@ +# Debug Console (dev only) + +A hidden in-game cheat / inspection toolset. Not linked from the player-facing +help manual — it is meant to stay undiscovered in normal play. + +## Enabling it + +The console is gated behind a dev flag and is completely inert otherwise (the +open key does nothing and `G.devMode` is `false`). + +| Launch | Flag | +|---|---| +| Packaged Electron app | `MeatSlicer.exe --dev` | +| `npm start` | `npm start -- --dev` | +| Browser (local server) | `http://localhost:8123/?dev=1` → use **`play-dev.bat`** | +| Any browser (persistent) | `localStorage.setItem('meatslicer_dev', '1')` in devtools | + +Detection lives in `debugEnabled()` (`js/debug.js`). For the packaged app, the +`--dev` flag is forwarded into the preload's `process.argv` as `--ms-dev` via +`webPreferences.additionalArguments` (`desktop/main.js`), and +`desktop/preload.js` exposes it as `window.MSDesktop.dev` (custom app flags +aren't guaranteed to reach a renderer's `process.argv` on their own, hence the +explicit forward). + +## Opening it + +Press the **backtick `` ` ``** (or `~`) key from **play** or **pause**. The +game pauses (its own `G.mode = 'debug'`). `` ` `` / `Esc` / the `[X]` button +closes it back to where you came from. + +Navigate tabs with `←`/`→`, number keys `1-8`, or by clicking the tab tags. + +## Run tainting + +Any **mutating** action sets `G.debugUsed = true`. When tainted: + +- `gameOver()` skips the `meatslicer_best` best-score write. +- The HUD shows a red `[DEBUG]` tag and the game-over screen shows + `[ DEBUG RUN — SCORE NOT RECORDED ]`. +- The console footer shows `TAINTED`. + +Read-only tools (the STATS tab, the render visualizers, the FPS counter, +pinning) do **not** taint. Taint resets on the next run (`resetRun()`). + +## Tabs + +1. **PLAYER** — hp/maxHp/shield/level number rows, full-heal, kill, +1 pending + levelup, force-open the perk draft, teleport to cursor, +500 score, and + three toggles: **God mode** (`hurtPlayer` no-ops), **Infinite ammo** (ammo + topped up each frame), **One-shot kill** (`damageEnemy` forces `dmg=1e9`, + which still respects Plate Father's plate mitigation). +2. **WEAPONS** — picker over all 16 `WEAPONS`. Equip (old weapon goes to the + holster unless it was the Bone Popper), refill ammo, holster current, drop + the weapon as a pickup at the cursor, spawn an item pedestal, set ammo. +3. **ITEMS** — picker over all `ITEMS` grouped by rarity, showing current tier. + Give +1 (routes through `giveItem`), remove 1 tier, remove all, set to max + tier, or set an exact tier. Presets: *one of everything*, *max everything*, + *clear build*. +4. **PERKS+ACTIVE** — grant any of the 22 `PERKS` by id, remove a drafted perk, + clear all perks. Set / fire an active item from `ACTIVES`. +5. **WORLD** — warp to floor N (`genFloor` + `enterRoom`), next floor, teleport + to the boss/item room, clear room, kill all, kill boss, regen floor. Spawn + any of the 11 `ENEMY_TYPES` (count + elite flag) or any of the 9 `BOSS_DEFS` + by id (uses `spawnBoss(floor, forcedIndex)`), and spawn pickups at cursor. +6. **PRESSURE** — set `G.pressure` (clamped to `PRESSURE_MIN..MAX`), set the + dial, and **freeze pressure** (the `applyPressureDelta` helper in + `js/state.js` no-ops). Shows live `pressureGainUnits`/`pressureDropUnits`, + streak, recent hits, and the Abattoir Engine multiplier. +7. **STATS** *(read-only)* — the full `p.stats` table in two scrollable columns, + diff-highlighted against `defaultPlayerStats()`. Plus `powerScore()`, entity + counts, FPS, floor/time. +8. **MISC** — timescale (0.25×/1×/4×), freeze + single-frame step, render + visualizers (collision circles, arena bounds, enemy HP numbers, + hazard/telegraph shapes, magnet radius), set score/kills/streak, trigger + game over, and SFX/FX test buttons. The **PIN** toggle keeps the panel open + while the world runs live behind it (and shows a compact always-on-top + readout when the panel is closed but a visualizer is active). + +## How item/perk removal works (stat rebuild) + +`ITEMS[iid].apply(stats, p)` and `perk.apply(stats, p)` are one-way mutations, +so removal isn't a subtraction — it's a rebuild. `debugRebuildStats()` resets +`p.stats` to `defaultPlayerStats()`, zeroes `shieldHp`, then re-applies every +owned item tier (`p.items[iid]` times, mirroring `giveItem`) and every drafted +perk (`p.perks`, tracked by `grantPerk`). + +Two caveats: + +- **maxHp-lowering items** (Blood Debt, Butcher's Oath) replay in ownership + insertion order, so a rebuild can produce a valid but slightly different + maxHp than the original build order. Current hp is snapshotted before and + clamped to the new maxHp afterward. +- One-time player side effects baked into some `apply` functions (e.g. the + immediate heal from Iron Stomach, the immediate shield from Bone Plate / + Shield Heart) are re-granted on rebuild. This is intentional and keeps the + model simple. + +## Architecture + +Everything lives in `js/debug.js`, loaded after `js/help.js` and before +`js/main.js` in `index.html`. It reuses the help manual's canvas UI primitives +(`drawPixelPanel`, `drawPixelTag`, `hfont`, `inRect`, `wrapText`) and mirrors +its `HELP_PAGES` / `updatePauseHelp` structure with a `DEBUG_PAGES` array. + +The hooks into core code are intentionally minimal: + +- `js/state.js` — `debugUsed`/`debugFlags`/debug panel state on `G`, plus the + `applyPressureDelta()` helper. +- `js/player.js` — `defaultPlayerStats()` factory (extracted from `initPlayer`), + `perks: []` field, and the god-mode early-return in `hurtPlayer()`. +- `js/perks.js` — one line in `grantPerk()` to record the perk id. +- `js/enemies.js` — one line in `damageEnemy()` for OHKO. +- `js/rooms.js` — two `applyPressureDelta()` call sites. +- `js/bosses.js` — optional `forcedIndex` param on `spawnBoss()`. +- `js/main.js` — `G.devMode` in `init()`, the open gesture in `play`/`pause`, + the `debug` case in `update()`, timescale in `loop()`, and the + `drawDebug`/`drawDebugOverlays`/`drawDebugPin` calls in `draw()`. +- `js/hud.js` + `drawGameOver` — the taint markers. diff --git a/index.html b/index.html index ef72062..3cef805 100644 --- a/index.html +++ b/index.html @@ -6,31 +6,32 @@