[lane F] Chain the campaign: CONTINUE now loads the next segment

cards.js has been raising `ui:continue {from,to}` since Lane E round 1, with the comment
"INTEGRATOR: nothing consumes ui:continue yet." Boot consumes it now, so crossing a gate and
pressing CONTINUE loads C's `next` level instead of ending the run. Three levels in a folder
become a campaign.

Deliberately a full page load: the alternative is rebuilding world + player + combat + UI in
place and re-wiring every bus listener to save ~1s on a transition that happens 4 times a run,
and the reload guarantees no leaked pools or stale event pumps. Flags ride along (?dbg=1,
?seed= survive the hop). A `to` that isn't in CAMPAIGN — or is null — ends the campaign on the
medal card rather than dumping the player into the stub tube.

Verified: L2 -> CONTINUE -> ?lvl=L3_stomach with flags intact; unknown "L4_small_intestine"
and null both correctly no-op.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-07-25 22:19:14 +10:00
parent 3c059f20db
commit fb08d7895b

View File

@ -133,6 +133,22 @@ bus.on('level:event', (ev) => {
});
}
});
// CONTINUE on the medal card -> load the next segment. cards.js raises ui:continue with C's
// `next` id; consuming it here is what makes the campaign a campaign rather than three levels
// in a folder. A full page load is deliberate (ponytail: the alternative is rebuilding world +
// player + combat + UI in place and re-wiring every bus listener, to save ~1s on a transition
// that happens 4 times a run) — it also guarantees no leaked pools or stale event pumps.
// Flags ride along, so ?dbg=1 / ?seed= survive the hop. Unknown/absent `to` = campaign over:
// stay on the card rather than dumping the player into the stub tube.
bus.on('ui:continue', async (e) => {
if (!e?.to) return;
const mod = await import('./levels/index.js').catch(() => null);
if (!mod?.CAMPAIGN?.includes(e.to)) { console.info(`[boot] no level "${e.to}" yet — campaign ends here`); return; }
const q = new URLSearchParams(location.search);
q.set('lvl', e.to);
location.search = q.toString();
});
bus.on('player:death', () => { run.deaths++; run.respawnT = 2.0; }); // 2s of feed-drop, then back
function updateRun(dt) {
if (!run.done) run.t += dt;