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
This commit is contained in:
MeatSlicer Developer committed 2026-07-30 17:18:11 +10:00
1 parent 0ec12cd72c
commit 4dad22d087
9 files changed
+36 -33

No files matched your search

+2 -2
View File
@@ -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.
+21 -21
View File
@@ -6,31 +6,31 @@
<meta name="theme-color" content="#0a0305">
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 64 64%22><text y=%2252%22 x=%2250%22 text-anchor=%22middle%22 dominant-baseline=%22middle%22 font-size=%2252%22>🔪</text></svg>">
<title>MEATSLICER</title>
<link rel="stylesheet" href="css/style.css?v=40">
<link rel="stylesheet" href="css/style.css?v=41">
</head>
<body>
<div id="wrap">
<canvas id="game" width="960" height="640"></canvas>
</div>
<script src="js/utils.js?v=40"></script>
<script src="js/state.js?v=40"></script>
<script src="js/input.js?v=40"></script>
<script src="js/sfx.js?v=40"></script>
<script src="js/sfxbank.js?v=40"></script>
<script src="js/audio.js?v=40"></script>
<script src="js/sprites.js?v=40"></script>
<script src="js/particles.js?v=40"></script>
<script src="js/weapons.js?v=40"></script>
<script src="js/bullets.js?v=40"></script>
<script src="js/pickups.js?v=40"></script>
<script src="js/items.js?v=40"></script>
<script src="js/perks.js?v=40"></script>
<script src="js/enemies.js?v=40"></script>
<script src="js/bosses.js?v=40"></script>
<script src="js/rooms.js?v=40"></script>
<script src="js/player.js?v=40"></script>
<script src="js/hud.js?v=40"></script>
<script src="js/help.js?v=40"></script>
<script src="js/main.js?v=40"></script>
<script src="js/utils.js?v=41"></script>
<script src="js/state.js?v=41"></script>
<script src="js/input.js?v=41"></script>
<script src="js/sfx.js?v=41"></script>
<script src="js/sfxbank.js?v=41"></script>
<script src="js/audio.js?v=41"></script>
<script src="js/sprites.js?v=41"></script>
<script src="js/particles.js?v=41"></script>
<script src="js/weapons.js?v=41"></script>
<script src="js/bullets.js?v=41"></script>
<script src="js/pickups.js?v=41"></script>
<script src="js/items.js?v=41"></script>
<script src="js/perks.js?v=41"></script>
<script src="js/enemies.js?v=41"></script>
<script src="js/bosses.js?v=41"></script>
<script src="js/rooms.js?v=41"></script>
<script src="js/player.js?v=41"></script>
<script src="js/hud.js?v=41"></script>
<script src="js/help.js?v=41"></script>
<script src="js/main.js?v=41"></script>
</body>
</html>
+2 -2
View File
@@ -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);
+3 -3
View File
@@ -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',
+2 -2
View File
@@ -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));
}
+1 -1
View File
@@ -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)))
+1 -1
View File
@@ -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';
}
},
+3
View File
@@ -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
+1 -1
View File
@@ -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;