From 4dad22d087a5fbe88a60ca92ee732ab197760168 Mon Sep 17 00:00:00 2001 From: MeatSlicer Developer Date: Thu, 30 Jul 2026 17:18:11 +1000 Subject: [PATCH] Ammo economy: 25% less ammo per box, 25% fewer ammo drops - AMMO_REFILL_SCALE 0.75 on ammoRefillFraction (smaller refills) - AMMO_DROP_SCALE 0.75 on all three ammo spawn sites (room clear, elite, normal kill); help/gameplay drop-rate text updated; cache v=41 --- docs/gameplay.md | 4 ++-- index.html | 42 +++++++++++++++++++++--------------------- js/enemies.js | 4 ++-- js/help.js | 6 +++--- js/pickups.js | 4 ++-- js/sfxbank.js | 2 +- js/sprites.js | 2 +- js/state.js | 3 +++ test/smoke.js | 2 +- 9 files changed, 36 insertions(+), 33 deletions(-) diff --git a/docs/gameplay.md b/docs/gameplay.md index ccaf3e8..69cd0ea 100644 --- a/docs/gameplay.md +++ b/docs/gameplay.md @@ -190,8 +190,8 @@ Clearing a combat room drops: | Source | Drops | |---|---| -| Normal enemy | XP gems (enemy's XP value) · 7% + luck×4% ammo · 2.2% + luck×3% heart | -| Elite enemy | 3× XP + 3–5 bonus gems · 18% + luck×10% **item** · else 60% + luck×20% ammo | +| Normal enemy | XP gems (enemy's XP value) · 5.25% + luck×3% ammo · 2.2% + luck×3% heart | +| Elite enemy | 3× XP + 3–5 bonus gems · 18% + luck×10% **item** · else 45% + luck×15% ammo | | Boss | 1 item pedestal + 1 weapon (rolled at floor + 2) + stairs | **Luck** is a player stat (Lucky Coin, Crow Bait, Scavenger perk) that scales most drop chances as shown above. diff --git a/index.html b/index.html index 2abbd9f..cee3268 100644 --- a/index.html +++ b/index.html @@ -6,31 +6,31 @@ MEATSLICER - +
- - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/js/enemies.js b/js/enemies.js index 48e57f3..408f012 100644 --- a/js/enemies.js +++ b/js/enemies.js @@ -300,12 +300,12 @@ function killEnemy(e, ang) { spawnGems(e.x, e.y, gemVal); if (chance(0.18 + luck * 0.1)) { spawnPickup('item', e.x, e.y, { iid: rollItemId('elite', G.floor) }); - } else if (chance(0.6 + luck * 0.2)) { + } else if (chance((0.6 + luck * 0.2) * AMMO_DROP_SCALE)) { spawnPickup('ammo', e.x, e.y); } } else { spawnGems(e.x, e.y, gemVal); - if (chance(0.07 + luck * 0.04)) { + if (chance((0.07 + luck * 0.04) * AMMO_DROP_SCALE)) { spawnPickup('ammo', e.x, e.y); } else if (chance(0.022 + luck * 0.03)) { spawnPickup('heart', e.x, e.y); diff --git a/js/help.js b/js/help.js index bffddf3..d2ff432 100644 --- a/js/help.js +++ b/js/help.js @@ -122,9 +122,9 @@ function drawHelpLoop(ctx, px, py) { 'WAVES SPAWN 0.9S APART · INCOMING SPAWNS TELEGRAPH FOR 0.5S · CAP 72', ] }, { head: 'DROPS (LUCK SCALES MOST CHANCES)', lines: [ - 'ROOM CLEAR: 3–5+FLOOR XP · 24%+LUCK×30% AMMO · 10%+LUCK×30% HEART', - 'NORMAL KILL: 7%+LUCK×4% AMMO · 2.2%+LUCK×3% HEART', - 'ELITE KILL: 3× XP + 3–5 GEMS · 18%+LUCK×10% ITEM · ELSE 60%+LUCK×20% AMMO', + 'ROOM CLEAR: 3–5+FLOOR XP · 18%+LUCK×22.5% AMMO · 10%+LUCK×30% HEART', + 'NORMAL KILL: 5.25%+LUCK×3% AMMO · 2.2%+LUCK×3% HEART', + 'ELITE KILL: 3× XP + 3–5 GEMS · 18%+LUCK×10% ITEM · ELSE 45%+LUCK×15% AMMO', ] }, { head: 'SCORE (× LIVE PRESSURE)', lines: [ 'KILL +10 · ELITE +40 · BOSS 500 + FLOOR × 100 · ROOM +50 · FLOOR +250', diff --git a/js/pickups.js b/js/pickups.js index 58a5923..9843365 100644 --- a/js/pickups.js +++ b/js/pickups.js @@ -25,14 +25,14 @@ function ammoRefillFraction(w) { const rateWeight = Math.log2(interval / 0.3); // Heavy, slow attacks push the fraction down; light, rapid attacks push it up. const heaviness = clamp((damageWeight + rateWeight) / 4, -1, 1); - return clamp(0.62 - heaviness * 0.28, 0.30, 0.88) * (w.ammoWeight || 1); + return clamp(0.62 - heaviness * 0.28, 0.30, 0.88) * (w.ammoWeight || 1) * AMMO_REFILL_SCALE; } // room-clear reward burst (weapons come from bosses / item rooms, not room clears) function spawnRoomReward(x, y, floor) { spawnGems(x, y, irand(3, 5 + floor)); const luck = G.player.stats.luck; - if (chance(0.24 + luck * 0.3)) spawnPickup('ammo', x + rand(-30, 30), y + rand(-30, 30)); + if (chance((0.24 + luck * 0.3) * AMMO_DROP_SCALE)) spawnPickup('ammo', x + rand(-30, 30), y + rand(-30, 30)); if (chance(0.10 + luck * 0.3)) spawnPickup('heart', x + rand(-30, 30), y + rand(-30, 30)); } diff --git a/js/sfxbank.js b/js/sfxbank.js index 0bc2435..3d48503 100644 --- a/js/sfxbank.js +++ b/js/sfxbank.js @@ -115,7 +115,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=40') + const job = fetch('/assets/sfx/' + encodeURIComponent(file) + '?v=41') .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 27416c9..3983939 100644 --- a/js/sprites.js +++ b/js/sprites.js @@ -84,7 +84,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=40'; + img.src = 'assets/' + name + '.png?v=41'; } }, diff --git a/js/state.js b/js/state.js index f8160f7..4d7bcb7 100644 --- a/js/state.js +++ b/js/state.js @@ -45,6 +45,9 @@ const G = { }; const PERK_POWER_WEIGHT = 0.5; +// ammo economy: global dials for refill size and drop frequency +const AMMO_REFILL_SCALE = 0.75; +const AMMO_DROP_SCALE = 0.75; const PRESSURE_MIN = 0.75, PRESSURE_MAX = 1.60; const PRESSURE_UNIT = 0.01; // one "point" on the dial scale const PRESSURE_GAIN = PRESSURE_UNIT; // retained: dial 0 == 1 point diff --git a/test/smoke.js b/test/smoke.js index 3d5cc8e..5b1b8f4 100644 --- a/test/smoke.js +++ b/test/smoke.js @@ -791,7 +791,7 @@ console.log('== drop economy: weapons are rare, elites feed you =='); } step(3, 16); check('elites always drop bonus gems (avg value ' + (eliteGemValue / 30).toFixed(1) + ')', eliteGemValue / 30 >= 5.5); - check('elites mostly drop ammo (' + eliteAmmo + '/30)', eliteAmmo >= 8); + check('elites mostly drop ammo (' + eliteAmmo + '/30)', eliteAmmo >= 6); check('elites sometimes drop items (' + eliteItems + '/30)', eliteItems >= 1); check('elites never drop weapons', eliteWeapons === 0); ctx.G.pickups.length = 0;