35babd24007ce3d698ea3649e29d281e958ebe64
## What was done - agents.js: spring physics (stiffness=7, damping=0.8, clamp ±0.44 rad) on timmy.group.rotation.x/z via slapOffset/slapVelocity integrated per-frame with proper dt (capped at 50ms for tab-background safety) - agents.js: applySlap(hitPoint) — computes XZ impulse direction from hit point relative to TIMMY_POS, adds angular velocity, triggers pip startle + crystal flash - agents.js: _playBoing() — lazy AudioContext, sine oscillator 260→90 Hz with exponential gain decay (0.38s) - agents.js: Pip startle — 3s decay timer, random scatter direction offset, 4x spin speed while startled, boosted pip light intensity - agents.js: Crystal ball hit flash — hitFlashTimer=0.5s, intensity spikes to 10 and fades; normal crystalLight/cbMat logic when not flashing - agents.js: getTimmyGroup() export for raycaster target - interaction.js: registerSlapTarget(group, applyFn) — stores targets - interaction.js: _onPointerDown capture-phase listener — raycasts against timmyGroup recursively, calls applySlap on hit, suppresses OrbitControls drag for 220ms via stopImmediatePropagation + controls.enabled toggle - main.js: imports getTimmyGroup, applySlap, registerSlapTarget; wires registerSlapTarget(getTimmyGroup(), applySlap) after initInteraction ## Verification - Vite build: clean, 14 modules, 0 errors - /tower HTTP 200 - Testkit: 27/27 PASS
Description
Timmy Tower World — sovereign AI agent economy
Languages
TypeScript
68.9%
JavaScript
15.5%
Shell
10.6%
HTML
4.5%
CSS
0.4%