[lane E] The boss banner: five fights stop being guesswork
boss.js has emitted boss:start / boss:phase / boss:window / boss:telegraph / boss:end since the framework landed, and NOTHING has ever listened. Every boss in the game was therefore illegible: the Pyloric Guardian's whole design is "shoot only while the iris relaxes", and the only tell a player had was a node's flash uniform going white-hot — a cue nobody is told to read. A top-centre banner, fed entirely by events the boss already broadcasts: - the boss's NAME (five of them; there was no way to know what you were fighting) - phase pips, filling as you break each phase - the state: SEALED / opening (blinking, off boss:telegraph) / VULNERABLE in green The Sovereign's Communion reports `phases: 0`, so a symbiotic player gets "stand down" and no pip rail rather than the promise of a battle they have just earned their way out of. Verified in the Guardian's lair: banner on, "PYLORIC GUARDIAN / VULNERABLE", 3 pips, state tracking boss.open exactly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
b9d9d816ad
commit
caa5f95339
@ -31,6 +31,18 @@ const CSS = `
|
|||||||
.hud .bl { bottom:18px; left:18px; }
|
.hud .bl { bottom:18px; left:18px; }
|
||||||
.hud .br { bottom:18px; right:18px; align-items:flex-end; }
|
.hud .br { bottom:18px; right:18px; align-items:flex-end; }
|
||||||
.hud .bc { bottom:18px; left:50%; transform:translateX(-50%); align-items:center; }
|
.hud .bc { bottom:18px; left:50%; transform:translateX(-50%); align-items:center; }
|
||||||
|
.hud .tc { top:14px; left:50%; transform:translateX(-50%); align-items:center; gap:6px;
|
||||||
|
opacity:0; transition:opacity .35s ease; pointer-events:none; }
|
||||||
|
.hud .tc.on { opacity:1; }
|
||||||
|
.hud .boss-name { font-size:13px; letter-spacing:.30em; color:${C.bad}; }
|
||||||
|
.hud .boss-state { font-size:9.5px; letter-spacing:.26em; opacity:.85; }
|
||||||
|
.hud .boss-state.open { color:${C.good}; }
|
||||||
|
.hud .boss-state.shut { color:${C.warn}; opacity:.55; }
|
||||||
|
.hud .boss-state.tele { color:${C.warn}; animation:bosstele .5s steps(2,end) infinite; }
|
||||||
|
@keyframes bosstele { 50% { opacity:.25 } }
|
||||||
|
.hud .phase { display:flex; gap:5px; }
|
||||||
|
.hud .phase i { width:16px; height:3px; background:currentColor; opacity:.22; }
|
||||||
|
.hud .phase i.on { opacity:1; }
|
||||||
.hud .dim { opacity:.5; }
|
.hud .dim { opacity:.5; }
|
||||||
.hud .row { display:flex; align-items:center; gap:8px; white-space:nowrap; }
|
.hud .row { display:flex; align-items:center; gap:8px; white-space:nowrap; }
|
||||||
.hud .lbl { font-size:9px; opacity:.65; letter-spacing:.2em; }
|
.hud .lbl { font-size:9px; opacity:.65; letter-spacing:.2em; }
|
||||||
@ -107,6 +119,17 @@ export function createHUD({ bus, flags = {}, level = null, mount = null } = {})
|
|||||||
const br = el('div', 'q br', root);
|
const br = el('div', 'q br', root);
|
||||||
const bc = el('div', 'q bc', root);
|
const bc = el('div', 'q bc', root);
|
||||||
|
|
||||||
|
// --- top-centre: the boss banner ------------------------------------------------------
|
||||||
|
// boss.js has emitted boss:start/phase/window/telegraph/end since the framework landed and
|
||||||
|
// NOTHING has ever listened. That made every fight illegible: the Pyloric Guardian's entire
|
||||||
|
// design is "shoot only while the iris relaxes", and the only tell was a node's flash uniform.
|
||||||
|
// This is the same information the boss already broadcasts, made visible.
|
||||||
|
const tc = el('div', 'q tc', root);
|
||||||
|
const bossName = makeText(tc, 'boss-name');
|
||||||
|
const phaseWrap = el('div', 'phase', tc);
|
||||||
|
const bossState = makeText(tc, 'boss-state');
|
||||||
|
let phasePips = [];
|
||||||
|
|
||||||
// --- top-left: where we are -----------------------------------------------------------
|
// --- top-left: where we are -----------------------------------------------------------
|
||||||
const where = makeText(tl, 'big');
|
const where = makeText(tl, 'big');
|
||||||
where.set(level?.name ?? '—');
|
where.set(level?.name ?? '—');
|
||||||
@ -210,6 +233,34 @@ export function createHUD({ bus, flags = {}, level = null, mount = null } = {})
|
|||||||
|
|
||||||
offs.push(bus.on('combo', (e) => chain.set(e.n > 1 ? `×${e.n} chain` : '')));
|
offs.push(bus.on('combo', (e) => chain.set(e.n > 1 ? `×${e.n} chain` : '')));
|
||||||
|
|
||||||
|
// --- boss banner ----------------------------------------------------------------------
|
||||||
|
const BOSS_NAMES = {
|
||||||
|
mast_cell: 'MAST CELL', pyloric_guardian: 'PYLORIC GUARDIAN', tapeworm: 'TAENIA',
|
||||||
|
the_blockage: 'THE BLOCKAGE', appendix_sovereign: 'THE SOVEREIGN',
|
||||||
|
};
|
||||||
|
const setState = (txt, cls) => {
|
||||||
|
bossState.set(txt);
|
||||||
|
bossState.node.className = `boss-state ${cls}`;
|
||||||
|
};
|
||||||
|
offs.push(bus.on('boss:start', (e) => {
|
||||||
|
bossName.set(BOSS_NAMES[e?.id] ?? 'CONTACT');
|
||||||
|
// The Sovereign's Communion is a boss that resolves to no fight at all — announcing phases
|
||||||
|
// for it would promise a battle the player has just earned their way out of.
|
||||||
|
const n = Math.max(0, e?.phases ?? 0);
|
||||||
|
phaseWrap.textContent = '';
|
||||||
|
phasePips = Array.from({ length: n }, () => el('i', null, phaseWrap));
|
||||||
|
if (phasePips[0]) phasePips[0].classList.add('on');
|
||||||
|
setState(n ? 'sealed' : 'stand down', n ? 'shut' : 'open');
|
||||||
|
tc.classList.add('on');
|
||||||
|
}));
|
||||||
|
offs.push(bus.on('boss:phase', (e) => {
|
||||||
|
const p = (e?.phase | 0);
|
||||||
|
phasePips.forEach((el_, i) => el_.classList.toggle('on', i < p));
|
||||||
|
}));
|
||||||
|
offs.push(bus.on('boss:window', (e) => setState(e?.open ? 'VULNERABLE' : 'sealed', e?.open ? 'open' : 'shut')));
|
||||||
|
offs.push(bus.on('boss:telegraph', () => setState('opening', 'tele')));
|
||||||
|
offs.push(bus.on('boss:end', () => tc.classList.remove('on')));
|
||||||
|
|
||||||
// biome standing — reveal on first event, then scale-only (60fps law). Brightens near full
|
// biome standing — reveal on first event, then scale-only (60fps law). Brightens near full
|
||||||
// to telegraph an incoming ally.
|
// to telegraph an incoming ally.
|
||||||
let biomeShown = false, lastStand = -1, lastStandC = '';
|
let biomeShown = false, lastStand = -1, lastStandC = '';
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user