From 3e603c349da5e2ab67850ef95b6ebc78f3c849c0 Mon Sep 17 00:00:00 2001 From: MeatSlicer Developer Date: Thu, 30 Jul 2026 22:47:39 +1000 Subject: [PATCH] Document v0.4.0 expansion --- docs/README.md | 4 ++-- docs/enemies.md | 36 +++++++++++++++++++++++++++--------- docs/gameplay.md | 33 ++++++++++++++++++++++++++------- index.html | 42 +++++++++++++++++++++--------------------- js/sfxbank.js | 2 +- js/sprites.js | 2 +- package.json | 2 +- 7 files changed, 79 insertions(+), 42 deletions(-) diff --git a/docs/README.md b/docs/README.md index 1a7e5d1..13de947 100644 --- a/docs/README.md +++ b/docs/README.md @@ -12,7 +12,7 @@ MeatSlicer is an endless top-down arena roguelite. You are the Butcher, descendi | [Weapons](weapons.md) | All 16 weapons with exact stats, drop tiers, and damage math | | [Items](items.md) | All 46 passive items, stacking/tier rules, proc numbers, synergies | | [Perks](perks.md) | All 23 Mutation Draft perks, XP curve, draft mechanics | -| [Enemies](enemies.md) | All 6 enemy types, elites, drops, wave and scaling formulas | +| [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 | ### Quick numbers @@ -20,7 +20,7 @@ MeatSlicer is an endless top-down arena roguelite. You are the Butcher, descendi - **16 weapons** (1 infinite-ammo sidearm + 15 special weapons across 4 tiers) - **46 passive items** (stacking, tier cap IX) - **23 perks** (draft 1 of 3 per level-up) -- **6 enemy types** (+ elite variants) +- **11 enemy types** (+ elite variants) - **9 bosses** (fixed rotation, one per floor) - **∞ floors** (permadeath, endless descent, high-score chase) diff --git a/docs/enemies.md b/docs/enemies.md index e11fbd1..36c71a2 100644 --- a/docs/enemies.md +++ b/docs/enemies.md @@ -1,6 +1,6 @@ # Enemies -All **6 enemy types** in MeatSlicer, plus elites, drops, and the exact difficulty-scaling formulas. Pulled from `js/enemies.js` and `js/rooms.js`. +All **11 enemy types** in MeatSlicer, plus elites, drops, and the exact difficulty-scaling formulas. Pulled from `js/enemies.js` and `js/rooms.js`. --- @@ -88,22 +88,40 @@ Suicide bomber. Charges at you; within 56 px its **0.5 s fuse** lights (it flash ## Spawn weights -| Enemy | Floor 1 | Floor 2 | Floor 3+ | -|---|---|---|---| -| Shambler | 40 | 40 | 40 | -| Runner | 25 | 25 | 25 | -| Spitter | — | 18 | 18 | -| Splitter | — | 16 | 16 | -| Exploder | — | — | 14 | +| Enemy | First floor | Weight | +|---|---:|---:| +| Shambler | 1 | 30 | +| Runner | 1 | 22 | +| Spitter | 2 | 14 | +| Splitter | 2 | 12 | +| Exploder | 3 | 10 | +| Censer | 4 | 8 | +| Bulwark | 5 | 8 | +| Flenserling | 5 | 7 | +| Choirmaster | 6 | 6 | +| Brood Sac | 7 | 6 | Weights stay flat once unlocked; floors get harder through stat scaling and wave size, not new mixes. +## New enemy types + +**Censer** — 34 HP, speed 45, damage 1, radius 19, XP 3; floor 4, weight 8. A zone caster that every ~3.3s predicts player movement and telegraphs a hostile acid pool (radius 38, 0.75s warning, ~4.2s life). Excluded from wave picks when the arena's minimum dimension is below 300px. + +**Bulwark** — 60 HP, speed 38, damage 2, radius 23, XP 3; floor 5, weight 8. Slowly faces the player; hits within ±70° of its front deal 20%, flank/rear hits full. Resists on-hit stun and cannot be elite. + +**Flenserling** — 22 HP, speed 150, damage 2, radius 16, XP 3; floor 5, weight 7. Periodically phases untargetable, appears behind the aim vector, and lunges. + +**Choirmaster** — 40 HP, speed 50, damage 1, radius 19, XP 4; floor 6, weight 6. Its 180px aura grants +35% speed and heals nearby non-boss monsters ~1.8% max HP per second (minimum 0.5). At most one is selected by a normal wave. + +**Brood Sac** — 45 HP, speed 20, damage 1, radius 22, XP 4; floor 7, weight 6. Spawns 2 Minis every 4s and 4 on death. At most two are wave-selected; the brood population cap is 40. Cannot be elite and is excluded from thin halls. + ## Elites Any wave-spawned enemy can roll as an **elite** (gold ring, HP bar pip): - Chance: `min(0.03 + floor × 0.02, 0.20)` — 5% on floor 1, capping at 20% from floor 9. - **HP ×2.6**, speed ×0.9, contact damage **+1**, radius ×1.45, XP ×3. +- Bulwark and Brood Sac are excluded from elite rolls. - Drops: 3× XP plus 3–5 bonus gems, **18% + luck×10% chance of an item pedestal drop**, otherwise 60% + luck×20% chance of ammo. ## Drops @@ -135,7 +153,7 @@ base speed × (1 + 0.04 × (floor − 1) + 0.01 × power) × (elite ? 0.9 : 1) **Contact damage:** flat per type (elites +1) — it **never scales with floor**. What kills you late-game is volume, speed, and HP pools, not bigger hit numbers. -**Pressure** (see [gameplay.md](gameplay.md#pressure-dynamic-difficulty)) ranges 0.75–1.60 and multiplies both HP and speed. Playing flawlessly pushes it up; getting hit pulls it down. The title-screen **Pressure Dial** (−5…+5) tunes how fast it rises per clean room and how much relief each hit grants — and every point of score is multiplied by the live pressure value. +**Pressure** (see [gameplay.md](gameplay.md#pressure-dynamic-difficulty)) ranges 0.60–2.00 and multiplies both HP and speed. Playing flawlessly pushes it up; getting hit pulls it down. The title-screen **Pressure Dial** (−10…+10) tunes how fast it rises per clean room and how much relief each hit grants — and every point of score is multiplied by the live pressure value. ### Wave spawn telegraph diff --git a/docs/gameplay.md b/docs/gameplay.md index a74bba5..ab06449 100644 --- a/docs/gameplay.md +++ b/docs/gameplay.md @@ -76,7 +76,7 @@ Your best score is saved in `localStorage` as `meatslicer_best` and shown on the | **[ / ]** or **Left / Right arrows** | Previous / next music track | | **Click arrows** | Cycle music tracks | | **Drag sliders** | SFX / Music / HUD opacity bars are click-and-drag | -| **H / [?] button** | Open the Field Manual (8 pages: controls, loop, arsenal, mutations, implants I, implants II, bestiary, pressure) | +| **H / [?] button** | Open the 12-page Field Manual | ### Menus @@ -84,6 +84,10 @@ Your best score is saved in `localStorage` as `meatslicer_best` and shown on the |---|---| | **Enter / Space / Click** | Start run (title), restart after death | +The title screen has **[X] Exit to Desktop**. Pause has **[Q] Main Menu** and **[X] Exit**. +The first matching press arms a 3-second confirmation; press it again to confirm. `P` or +`Escape` resumes. Electron quits directly; the browser fallback asks you to close the tab. + Right-click is disabled (context menu suppressed). There is **no dash, no interact key** — pickups are collected by walking over them. --- @@ -140,6 +144,12 @@ Right-click is disabled (context menu suppressed). There is **no dash, no intera Doors in combat and boss rooms **lock** until the room is cleared. Re-entering an uncleared room restarts its remaining waves. +The canvas is fixed at **960 × 640**. Playable bounds are inset into five room shapes: full +hall, wide horizontal hall (224px playable height), tall vertical hall (224px playable width), +compact chamber, and inset pit. Start and boss rooms force full halls; item rooms force +chambers; combat rooms mix the shapes. There is no camera or larger-than-screen room. Visual +tiles recombine into four themes: **abattoir**, **plant**, **oxide**, and **flesh**. + ### Waves per combat room ``` @@ -211,7 +221,7 @@ Clearing a combat room drops: The **PRESSURE** meter on the HUD is a run-long adaptive difficulty multiplier applied to enemy and boss **HP and speed at spawn time**. It also **multiplies every point of score you earn** — the higher the pressure, the more each kill, room, and floor is worth. -- Range: **0.75 – 1.60** (starts at 1.00). +- Range: **0.60 – 2.00** (starts at 1.00). - Gains for rooms cleared **without taking damage** (flawless streak). - Taking damage **relieves** pressure, scaled by how big the hit was, how low your HP is, and how many recent hits you've taken. - Decays passively while below 35% HP, or if you stall in a room for 90+ seconds. @@ -219,15 +229,17 @@ The **PRESSURE** meter on the HUD is a run-long adaptive difficulty multiplier a ### The Pressure Dial -The title screen has a **PRESSURE DIAL** (−5 … +5) that tunes how pressure responds. It sets two curves — how fast pressure **rises** per clean room, and how much **relief** each hit grants (which also scales the passive decay). The midpoint (0) is the standard tuning; the dial persists between sessions. +The title screen has a **PRESSURE DIAL** (−10 … +10) that tunes how pressure responds. It sets two curves — how fast pressure **rises** per clean room, and how much **relief** each hit grants (which also scales the passive decay). The midpoint (0) is the standard tuning; the dial persists between sessions. | Dial | Rise / clean room | Relief on hit (base) | Behaviour | |---|---|---|---| -| **−5** | 0 | 0.050 (max) | Mercy — pressure can only ever fall; score capped at 1.00× | +| **−10** | −0.020 | 0.080 | Strong mercy; negative rise respects the 0.60 floor | +| **−5** | 0 | 0.050 | Mercy — pressure does not rise from clean rooms | | **0** | +0.010 | 0.030 | Standard — today's balance | | **+5** | +0.050 | 0 (none) | Ratchet — pressure can only ever rise; score floored at 1.00× | +| **+10** | +0.100 | 0 | Maximum ratchet; pressure respects the 2.00 ceiling | -Values between the ends interpolate linearly (rise: 0→1→5 units; relief: 5→3→0 units, 1 unit = 0.01). At −5 pressure never rises so your score multiplier stays ≤ 1.00×; at +5 it never falls, so pressure climbs to the 1.60× cap and your score climbs with it. **Cranking the dial up is how you chase the BEST CUT.** +Values between the anchors interpolate linearly. Negative rise respects the 0.60 floor and positive rise respects the 2.00 ceiling. **Cranking the dial up is how you chase the BEST CUT.** ### Streak @@ -263,7 +275,7 @@ Full formulas are in [enemies.md](enemies.md) and [bosses.md](bosses.md). | Screen | What it offers | |---|---| -| **Title** | INITIATE DESCENT, PRESSURE DIAL (−5…+5), control reference, best score | +| **Title** | INITIATE DESCENT, PRESSURE DIAL (−10…+10), control reference, best score, confirmed desktop exit | | **Pause** | Jukebox (track cycling), SFX/music volume + HUD opacity sliders, resume/swap/auto/mute hints, Field Manual (`H` / `?` button) | | **Mutation Draft** | 3 perk cards, random cut, reroll, auto-draft | | **Game Over ("BUTCHERED")** | Floor, kills, score, NEW BEST CUT tag, restart | @@ -278,7 +290,14 @@ Stored in `localStorage` between sessions: - `meatslicer_sfx_volume` (default 0.45) - `meatslicer_music_volume` (default 0.55) - `meatslicer_autoperk` — auto-draft toggle -- `meatslicer_pressure_dial` — Pressure Dial setting (−5…+5, default 0) +- `meatslicer_pressure_dial` — Pressure Dial setting (−10…+10, default 0) - `meatslicer_hud_alpha` — HUD opacity (default 1.0) Nothing else carries over: no unlocks, no meta-currency, no saved runs. Every descent starts fresh with a Bone Popper and 6 HP. + +### Death and restart + +When HP reaches zero, simulation freezes. Input is locked for 3 seconds, then **R**, **Enter**, +or a click restarts the run. During the lock the player plays a derived gore animation with +particles. Normal damaged monsters show mini HP pips for 2.5 seconds; elite and boss bars stay +persistent. diff --git a/index.html b/index.html index a2d8ff7..ef72062 100644 --- a/index.html +++ b/index.html @@ -6,31 +6,31 @@ MEATSLICER - +
- - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/js/sfxbank.js b/js/sfxbank.js index 53223eb..055de7b 100644 --- a/js/sfxbank.js +++ b/js/sfxbank.js @@ -111,7 +111,7 @@ const SfxBank = { if (this.buffers.has(name) || this.pending.has(name) || this.failed.has(name)) return; const file = this.FILES[name]; if (!file || !this.ctx || typeof fetch !== 'function') return; - const job = fetch('/assets/sfx/' + encodeURIComponent(file) + '?v=44') + const job = fetch('/assets/sfx/' + encodeURIComponent(file) + '?v=45') .then(r => { if (!r.ok) throw new Error('HTTP ' + r.status); return r.arrayBuffer(); }) .then(bytes => this.ctx.decodeAudioData(bytes)) .then(buffer => this.buffers.set(name, this.trim(buffer))) diff --git a/js/sprites.js b/js/sprites.js index fc36582..ac84138 100644 --- a/js/sprites.js +++ b/js/sprites.js @@ -86,7 +86,7 @@ const Sprites = { const img = new Image(); img.onload = () => { this.imgs[name] = img; if (--pending === 0) G.imagesLoaded = true; }; img.onerror = () => { if (--pending === 0) G.imagesLoaded = true; }; - img.src = 'assets/' + name + '.png?v=44'; + img.src = 'assets/' + name + '.png?v=45'; } }, diff --git a/package.json b/package.json index 926fe2d..990687f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "meatslicer", "productName": "MeatSlicer", - "version": "0.3.0", + "version": "0.4.0", "private": true, "description": "A Butcher's Descent - clear, harvest, descend.", "main": "desktop/main.js",