import { CFG } from './config.js'; import { FX } from './fx.js'; // ============================================================ // THE JUICE BUDGET. // Written FIRST, before bolts/detonations/cascades can stack on it. // Without a cap, a chain reaction reads as "the game crashed" // rather than "the game is going off". // ============================================================ const win = []; // [{t, ms}] hitstop spend inside the window let clock = 0; export const Juice = { tick(dt) { clock += dt; while (win.length && clock - win[0].t > CFG.JUICE_WINDOW) win.shift(); }, spent() { let s = 0; for (const e of win) s += e.ms; return s; }, // request hitstop; returns what was actually granted hitstop(ms) { const room = Math.max(0, CFG.JUICE_HITSTOP_CAP - this.spent()); const grant = Math.min(ms, room); if (grant > 0) { win.push({ t: clock, ms: grant }); FX.freeze(grant / 1000); } return grant; }, // trauma is self-limiting (clamped 0..1 + decay) but funnel it here // so every source is auditable in one place trauma(camera, v) { camera.addTrauma(v); }, reset() { win.length = 0; clock = 0; }, };