// combat/index.js (Lane B) — the createCombat factory. Composes enemies + weapons and // feeds them Lane C's level events. All the wiring, none of the behaviour. // // Update order inside a frame is load-bearing: // boot: player.update(dt) -> combat.update(dt) // here: enemies.update(dt) -> weapons.update(dt) // Enemies move first so weapons hit-test against this frame's positions, not last frame's. import { createEnemies } from './enemies.js'; import { createWeapons } from './weapons.js'; export function createCombat({ scene, world, bus, rng, flags = {}, assets = null, player }) { const enemies = createEnemies({ scene, world, bus, rng, assets }); const weapons = createWeapons({ scene, world, bus, rng, player, targets: enemies }); enemies.setBlastFx((pos, size, dur) => weapons.spawnBlast(pos, size, dur)); enemies.setOnDeath((e) => weapons.spawnBlast(e.pos, e.radius * 1.6, 0.3)); const score = { value: 0, kills: 0 }; // Lane C's events arrive on the bus as the player crosses their `s`. We only care about // `spawn`; checkpoints/hazards/bosses belong to C and E this round. Unknown types are // ignored on purpose — C may add event kinds without breaking combat. const offEvent = bus.on('level:event', (ev) => { if (!ev || ev.type !== 'spawn') return; const n = ev.count ?? 1; for (let i = 0; i < n; i++) { // fan the group out over a short stretch of s so a count of 6 is an encounter, // not a wall of enemies at one arclength enemies.spawn(ev.enemy, { s: (ev.s ?? 0) + i * 3.5, theta: ev.theta ?? null, rho: ev.rho ?? null }); } }); const offDie = bus.on('enemy:die', (e) => { score.value += e.score ?? 0; score.kills++; }); function update(dt) { enemies.update(dt, player); weapons.update(dt); // combat's slice of the HUD state (E reads player:state for the flight half) bus.emit('combat:state', { heat: weapons.heat, heatMax: weapons.heatMax, overheated: weapons.overheated, ammo: weapons.ammo, ammoMax: weapons.ammoMax, score: score.value, kills: score.kills, enemies: enemies.count, }); } return { update, enemies, weapons, score, dispose() { offEvent(); offDie(); enemies.dispose(); weapons.dispose(); }, }; }