Phase 3: 12 hook passives with conditional damage, auras, and kill/room effects

- dmgLiveMul live damage channel wired into fireWeapon, sawTick, fireBeam, orbitals
- 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
- Iron Lung re-armed per room in enterRoom; Slaughter Rhythm stamps kills
- Targeted smoke tests for the five stat-conditional effects; icons + docs
This commit is contained in:
MeatSlicer Developer committed 2026-07-30 15:44:31 +10:00
1 parent 4f46d54e99
commit a93802ebd9
23 files changed
+195 -13

No files matched your search

+51 -4
View File
@@ -318,9 +318,9 @@ step(30, 16);
console.log('== expanded perk and item rosters ==');
{
check('41 new items added (14 phase-2 passives)', Object.keys(ctx.ITEMS).length === 60);
check('41 new items added (14 phase-2 + 12 phase-3 passives)', Object.keys(ctx.ITEMS).length === 72);
const newPerks = ['critbone', 'critmeat', 'flensing', 'ember', 'frostbile', 'heavyhand', 'thickhide', 'secondwind', 'scrapfeed', 'boneknit', 'spiteflesh', 'carrion', 'sinew'];
const newItems = ['chainsinew', 'mortarbone', 'bloatrounds', 'marrowglut', 'hollowneedle', 'bloodshoteye', 'flayerkiss', 'emberjar', 'acidgland', 'hookrounds', 'sledgerounds', 'graftedtrigger', 'deadmanswitch', 'orbitcrown', 'tannedhide', 'deadmansclock', 'hollowbones', 'boneplate', 'wormgut', 'spinecage', 'secondstomach', 'spitewell', 'twinhearts', 'brassmagazine', 'crowbait', 'gorgingleech', 'rerollrib', 'chillgland', 'hookedsinew', 'gyroscopicribs', 'marrowpiston', 'splitcortex', 'gristlecord', 'renderedfat', 'whipcordtendon', 'rusteddiadem', 'gorgedtick', 'bonemealpowder', 'rimedfang', 'butcherstwine', 'cindersump'];
const newItems = ['chainsinew', 'mortarbone', 'bloatrounds', 'marrowglut', 'hollowneedle', 'bloodshoteye', 'flayerkiss', 'emberjar', 'acidgland', 'hookrounds', 'sledgerounds', 'graftedtrigger', 'deadmanswitch', 'orbitcrown', 'tannedhide', 'deadmansclock', 'hollowbones', 'boneplate', 'wormgut', 'spinecage', 'secondstomach', 'spitewell', 'twinhearts', 'brassmagazine', 'crowbait', 'gorgingleech', 'rerollrib', 'chillgland', 'hookedsinew', 'gyroscopicribs', 'marrowpiston', 'splitcortex', 'gristlecord', 'renderedfat', 'whipcordtendon', 'rusteddiadem', 'gorgedtick', 'bonemealpowder', 'rimedfang', 'butcherstwine', 'cindersump', 'deadweight', 'cauterizedveins', 'hollowchoir', 'sawbonecoil', 'gluttonsgut', 'slaughterrhythm', 'painengine', 'thresherplate', 'bloodmoat', 'ironlung', 'meathook', 'blooddebt'];
check('all new perks have manifest icons or fallbacks', newPerks.every(id => ctx.SPRITE_MANIFEST.includes('perk_' + id)));
check('all new items have manifest icons or fallbacks', newItems.every(id => ctx.SPRITE_MANIFEST.includes('i_' + id)));
check('new roster entries all have names, descriptions and effects',
@@ -498,6 +498,53 @@ console.log('== tier-scaled items + duplicate favoring ==');
check('item rolls favor owned items (' + ownedHits + '/200)', ownedHits >= 40);
}
console.log('== phase-3 hook passives ==');
{
const p = ctx.G.player;
const s = p.stats;
// Dead Weight: execute bonus on wounded enemies (isolate stats so no leftover items leak)
const savedStats = s;
p.stats = Object.assign({}, s, { executeBonus: 0.40, burnDamageBonus: 0, crit: 0, critMul: 2 });
const wounded = ctx.makeEnemy('shambler', 500, 300, 1, false); wounded.hp = wounded.maxHp = 100; wounded.hp = 20; // 20% of maxHp
const healthy = ctx.makeEnemy('shambler', 500, 300, 1, false); healthy.hp = healthy.maxHp = 100;
const wBefore = wounded.hp, hBefore = healthy.hp;
ctx.damageEnemy(wounded, 10, 0, false, { noCrit: true });
ctx.damageEnemy(healthy, 10, 0, false, { noCrit: true });
check('Dead Weight executes wounded enemies', Math.abs((wBefore - wounded.hp) - 14) < 0.01 && Math.abs((hBefore - healthy.hp) - 10) < 0.01);
p.stats = savedStats;
// Cauterized Veins: bonus damage to burning enemies
const savedBurn = s.burnDamageBonus;
s.burnDamageBonus = 0.25;
const burner = ctx.makeEnemy('shambler', 500, 300, 1, false); burner.burnT = 1;
const bBefore = burner.hp;
ctx.damageEnemy(burner, 10, 0, false, { noCrit: true });
check('Cauterized Veins boosts damage on burning enemies', Math.abs((bBefore - burner.hp) - 12.5) < 0.01);
s.burnDamageBonus = savedBurn;
ctx.G.enemies.length = 0;
// Pain Engine: dmgLiveMul rises after being hit
const savedPain = s.painEngine, savedArmor = s.armor, savedMax = s.maxHp;
s.painEngine = 0.30; s.armor = 0; s.maxHp = 999; p.hp = 999; p.invT = 0; p.painEngineT = 0; ctx.G.mode = 'play';
ctx.hurtPlayer(1, 0);
check('Pain Engine arms after being hit', p.painEngineT > 0);
s.painEngine = savedPain; s.armor = savedArmor; s.maxHp = savedMax; p.invT = 1; p.painEngineT = 0;
// Iron Lung: first hit in a room is blocked
const savedLung = s.ironLung;
s.ironLung = 1; p.ironLungReady = true; p.invT = 0; const hpBeforeLung = p.hp;
ctx.hurtPlayer(1, 0);
check('Iron Lung blocks the first room hit', p.hp === hpBeforeLung && p.ironLungReady === false);
s.ironLung = savedLung; p.invT = 1;
// Slaughter Rhythm: recent kills grant fire rate
const savedRhythm = s.slaughterRhythm;
s.slaughterRhythm = 0.04; p.killStamps = [ctx.G.time, ctx.G.time, ctx.G.time];
p.fireT = 0; ctx.updatePlayer(0.016);
check('Slaughter Rhythm grants live fire rate', (s.rhythmRateBonus || 0) > 0);
s.slaughterRhythm = savedRhythm; p.killStamps = [];
}
console.log('== all weapons fire without crashing ==');
for (const wid of Object.keys(ctx.WEAPONS)) {
ctx.G.player.weapon = { id: wid, ammo: 50 };
@@ -937,8 +984,8 @@ console.log('== item stickiness ramps with owned count ==');
console.log('== help manual grows with the implant roster ==');
{
check('9 help pages (3 implant pages for 60 items)', ctx.HELP_PAGES.length === 9);
check('9 help renderers', ctx.HELP_RENDERERS.length === 9);
check('10 help pages (4 implant pages for 72 items)', ctx.HELP_PAGES.length === 10);
check('10 help renderers', ctx.HELP_RENDERERS.length === 10);
ctx.G.player = ctx.G.player || {};
const fctx = fakeCtx();
let renderOK = true;