/** * Lane A demo — the playground. * * Boots the shared world, builds the greybox, spawns a blob, and wires the * controller (physics), feel (wobble, via the frozen events) and follow camera. * * CONTROLS * WASD / Arrows move (camera-relative) * Space jump * * DEBUG KEYS (toggle — press again to reset to base). Proves modifier plumbing: * 1 speedMul 1.6 2 jumpMul 1.5 3 size 1.5 * 4 massMul 3.0 5 grip 1.0 6 glow 1.0 (super-state pulse) * 0 reset all modifiers R respawn at start * * A HUD (top-left) and a once-per-second console "state" line report live values * so integration can verify fast without a screenshot. */ import { createWorld } from '../world' import { defaultModifiers } from '../contracts' import { buildGreybox } from '../course/greybox' import { createBlob } from '../blob/createBlob' import { createBlobController } from '../blob/controller' import { createBlobFeel } from '../blob/feel' import { createFollowCamera } from '../blob/camera' const world = await createWorld(document.getElementById('app')!) const course = buildGreybox(world) const blob = createBlob(world, { position: course.spawn }) world.blob = blob // physics system (fixed 60Hz) world.addSystem(createBlobController(world, blob)) // feel layer — driven by the FROZEN wire events, updated per render frame const feel = createBlobFeel(blob) world.events.on('blob:jumped', () => feel.onJump()) world.events.on('blob:landed', (p: { impact: number }) => feel.onLand(p.impact)) world.onFrame((dt) => feel.update(dt)) // follow camera const camera = createFollowCamera(world, blob) world.onFrame((dt) => camera.update(dt)) // ---- event counters + last-impact (for HUD/console) ---- let jumps = 0 let lands = 0 let lastImpact = 0 world.events.on('blob:jumped', () => { jumps++ }) world.events.on('blob:landed', (p: { impact: number }) => { lands++; lastImpact = p.impact }) // ---- respawn if you fall off the world ---- world.onFrame(() => { const t = blob.body.translation() if (t.y < -25) respawn() }) function respawn(): void { blob.body.setTranslation({ x: course.spawn.x, y: course.spawn.y, z: course.spawn.z }, true) blob.body.setLinvel({ x: 0, y: 0, z: 0 }, true) } // ---- debug keys ---- const m = blob.modifiers const toggle = (cur: number, on: number, base: number) => (cur === base ? on : base) addEventListener('keydown', (e) => { switch (e.code) { case 'Digit1': m.speedMul = toggle(m.speedMul, 1.6, 1); break case 'Digit2': m.jumpMul = toggle(m.jumpMul, 1.5, 1); break case 'Digit3': m.size = toggle(m.size, 1.5, 1); break case 'Digit4': m.massMul = toggle(m.massMul, 3.0, 1); break case 'Digit5': m.grip = toggle(m.grip, 1.0, 0); break case 'Digit6': m.glow = toggle(m.glow, 1.0, 0); break case 'Digit0': Object.assign(m, defaultModifiers()); break case 'KeyR': respawn(); break default: return } console.log('[lane-a] modifiers', JSON.stringify(m)) }) // ---- HUD overlay ---- const hud = document.createElement('div') hud.style.cssText = 'position:fixed;top:10px;left:10px;font:12px/1.5 ui-monospace,Menlo,monospace;' + 'color:#0a2540;background:rgba(255,255,255,.72);padding:10px 12px;border-radius:8px;' + 'white-space:pre;pointer-events:none;z-index:10' document.body.appendChild(hud) // ---- per-frame HUD + fps + once/sec console state line ---- let frames = 0 let fps = 0 let acc = 0 let logAcc = 0 world.onFrame((dt) => { frames++ acc += dt if (acc >= 0.5) { fps = Math.round(frames / acc); frames = 0; acc = 0 } const t = blob.body.translation() const v = blob.body.linvel() const speed = Math.hypot(v.x, v.z) hud.textContent = `BLOBBO · lane-a ${fps} fps\n` + `pos ${t.x.toFixed(1)} ${t.y.toFixed(1)} ${t.z.toFixed(1)}\n` + `spd ${speed.toFixed(2)} jumps ${jumps} lands ${lands} impact ${lastImpact.toFixed(1)}\n` + `mods speed ${m.speedMul} jump ${m.jumpMul} size ${m.size} mass ${m.massMul} grip ${m.grip} glow ${m.glow}\n` + `WASD move · Space jump · 1 speed 2 jump 3 size 4 mass 5 grip 6 glow · 0 reset · R respawn` logAcc += dt if (logAcc >= 1) { logAcc = 0 console.log( `[lane-a] state fps=${fps} pos=(${t.x.toFixed(1)},${t.y.toFixed(1)},${t.z.toFixed(1)}) ` + `spd=${speed.toFixed(2)} jumps=${jumps} lands=${lands} impact=${lastImpact.toFixed(1)} ` + `mods=${JSON.stringify(m)}`, ) } }) world.start() console.log('[lane-a] booted. WASD to move, Space to jump, keys 1-6 modifiers, 0 reset, R respawn.')