Phase 5: active items — room-clear charged, dedicated pedestals, Space to use

- ACTIVES table with 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; flat +2 powerScore while held
- HUD active slot with charge pips + READY state; ACTIVES help page (12 pages);
  teal medallion frame variant in draw_sprites.py; 10 generated icons
- Procedural Sfx.active()/activeEmpty() (real clips deferred to audio batch)
- Fix: toast panel width now fits the wider of title/subtitle and clamps to screen
- Bump asset cache to v=40
This commit is contained in:
MeatSlicer Developer committed 2026-07-30 17:14:25 +10:00
1 parent f71d202850
commit 0ec12cd72c
27 files changed
+337 -37

No files matched your search

+27
View File
@@ -121,6 +121,21 @@ function collectPickup(k) {
for (const q of G.pickups) if (q.type === 'itemspot') q.dead = true; // remove pedestal base
break;
}
case 'active': {
// picking up a new active drops the old one with its remaining charges
if (p.active) {
const a = rand(0, TAU);
spawnPickup('active', p.x + Math.cos(a) * 34, p.y + Math.sin(a) * 34,
{ aid: p.active.iid, charges: p.active.charges, delay: 0.6 });
}
const def = ACTIVES[k.aid];
p.active = { iid: k.aid, charges: k.charges !== undefined ? k.charges : def.cost };
addToast(def.name, def.desc + ' — [SPACE] when charged');
spawnText(p.x, p.y - 14, def.name.toUpperCase(), '#55f5dc');
Sfx.item();
for (const q of G.pickups) if (q.type === 'itemspot') q.dead = true; // remove pedestal base
break;
}
case 'stairs':
nextFloor();
break;
@@ -177,6 +192,17 @@ function drawPickups(ctx) {
ctx.beginPath(); ctx.arc(k.x, k.y - 28, 25 + Math.sin(k.t * 4) * 3, 0, TAU); ctx.stroke();
ctx.restore();
Sprites.draw(ctx, 'i_' + k.iid, k.x, k.y - 28 + bob, 0, 64);
} else if (k.type === 'active') {
ctx.save();
ctx.globalAlpha = 0.22 + Math.sin(k.t * 3) * 0.08;
ctx.fillStyle = '#55f5dc';
ctx.beginPath(); ctx.arc(k.x, k.y - 28, 30, 0, TAU); ctx.fill();
ctx.globalAlpha = 0.4;
ctx.strokeStyle = '#a5f5e8'; ctx.lineWidth = 2;
const ringR = 25 + Math.sin(k.t * 4) * 3;
ctx.beginPath(); ctx.arc(k.x, k.y - 28, ringR, -Math.PI / 2, -Math.PI / 2 + TAU * ((k.charges || 0) / Math.max(1, ACTIVES[k.aid].cost))); ctx.stroke();
ctx.restore();
Sprites.draw(ctx, 'a_' + k.aid, k.x, k.y - 28 + bob, 0, 64);
} else if (k.type === 'stairs') {
Sprites.draw(ctx, 'stairs', k.x, k.y, 0, 64);
ctx.save();
@@ -191,6 +217,7 @@ function drawPickups(ctx) {
if (dist2(k.x, k.y, p.x, p.y) < 90 * 90) {
if (k.type === 'weapon') drawPickupLabel(ctx, WEAPONS[k.wid].name, k.x, k.y - 44, '#ffd060');
else if (k.type === 'item') drawPickupLabel(ctx, ITEMS[k.iid].name, k.x, k.y - 66, ITEM_RARITY[ITEMS[k.iid].rarity].color);
else if (k.type === 'active') drawPickupLabel(ctx, ACTIVES[k.aid].name, k.x, k.y - 66, '#55f5dc');
else if (k.type === 'stairs') drawPickupLabel(ctx, 'STAIRS DOWN', k.x, k.y - 52, '#e8e0d0');
else if (k.type === 'ammo') drawPickupLabel(ctx, 'AMMO', k.x, k.y - 26, '#d0b060');
}