Some checks failed
CI / Typecheck & Lint (pull_request) Failing after 0s
## Epic #222 — The Workshop: Timmy as Presence ### Backend - lib/db/src/schema/world-events.ts: world_events table (id, type, agentId, summary, jobId, createdAt) - lib/db/src/schema/index.ts: export worldEvents - artifacts/api-server/src/lib/world-state.ts: in-memory world state (timmyState, agentStates, derived mood/activity) - artifacts/api-server/src/routes/world.ts: GET /api/world/state - artifacts/api-server/src/routes/index.ts: register worldRouter - artifacts/api-server/src/routes/events.ts: WS world_state bootstrap on connect; visitor_enter/leave/message protocol; job events update world-state + log to DB ### Frontend (the-matrix/) - world.js: Workshop room — dark stone floor, wooden desk, shelves, fireplace warm light, atmospheric fog - agents.js: Timmy wizard — cone robe, sphere head, hat, crystal ball (glows on events), Pip familiar (wanders), speech bubble sprite; state-driven animations - effects.js: floating ambient dust motes (amber + green) - websocket.js: world_state bootstrap, visitor_enter, job events → crystal ball, chat → speech bubble - ui.js: minimal HUD + event log + touch-first input bar - main.js: updated imports, clean loop - index.html: Workshop HTML — dark theme, input bar, payment panel 20/20 testkit PASS
80 lines
2.2 KiB
JavaScript
80 lines
2.2 KiB
JavaScript
import * as THREE from 'three';
|
|
|
|
let dustParticles = null;
|
|
let dustPositions = null;
|
|
let dustVelocities = null;
|
|
const DUST_COUNT = 600;
|
|
|
|
export function initEffects(scene) {
|
|
initDustMotes(scene);
|
|
}
|
|
|
|
function initDustMotes(scene) {
|
|
const geo = new THREE.BufferGeometry();
|
|
const positions = new Float32Array(DUST_COUNT * 3);
|
|
const colors = new Float32Array(DUST_COUNT * 3);
|
|
const velocities = new Float32Array(DUST_COUNT);
|
|
|
|
for (let i = 0; i < DUST_COUNT; i++) {
|
|
positions[i * 3] = (Math.random() - 0.5) * 22;
|
|
positions[i * 3 + 1] = Math.random() * 10;
|
|
positions[i * 3 + 2] = (Math.random() - 0.5) * 16 - 2;
|
|
velocities[i] = 0.008 + Math.random() * 0.012;
|
|
|
|
const roll = Math.random();
|
|
if (roll < 0.6) {
|
|
colors[i * 3] = 0.9 + Math.random() * 0.1;
|
|
colors[i * 3 + 1] = 0.7 + Math.random() * 0.2;
|
|
colors[i * 3 + 2] = 0.3 + Math.random() * 0.3;
|
|
} else {
|
|
const b = 0.3 + Math.random() * 0.5;
|
|
colors[i * 3] = 0;
|
|
colors[i * 3 + 1] = b;
|
|
colors[i * 3 + 2] = 0;
|
|
}
|
|
}
|
|
|
|
geo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
|
|
geo.setAttribute('color', new THREE.BufferAttribute(colors, 3));
|
|
dustPositions = positions;
|
|
dustVelocities = velocities;
|
|
|
|
const mat = new THREE.PointsMaterial({
|
|
size: 0.06,
|
|
vertexColors: true,
|
|
transparent: true,
|
|
opacity: 0.55,
|
|
sizeAttenuation: true,
|
|
});
|
|
|
|
dustParticles = new THREE.Points(geo, mat);
|
|
scene.add(dustParticles);
|
|
}
|
|
|
|
export function updateEffects(time) {
|
|
if (!dustParticles) return;
|
|
const t = time * 0.001;
|
|
|
|
for (let i = 0; i < DUST_COUNT; i++) {
|
|
dustPositions[i * 3 + 1] += dustVelocities[i];
|
|
dustPositions[i * 3] += Math.sin(t * 0.5 + i * 0.1) * 0.002;
|
|
|
|
if (dustPositions[i * 3 + 1] > 10) {
|
|
dustPositions[i * 3 + 1] = 0;
|
|
dustPositions[i * 3] = (Math.random() - 0.5) * 22;
|
|
dustPositions[i * 3 + 2] = (Math.random() - 0.5) * 16 - 2;
|
|
}
|
|
}
|
|
dustParticles.geometry.attributes.position.needsUpdate = true;
|
|
}
|
|
|
|
export function disposeEffects() {
|
|
if (dustParticles) {
|
|
dustParticles.geometry.dispose();
|
|
dustParticles.material.dispose();
|
|
dustParticles = null;
|
|
}
|
|
dustPositions = null;
|
|
dustVelocities = null;
|
|
}
|