This repository has been archived on 2026-03-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
token-gated-economy/the-matrix/js/effects.js
Replit Agent 45ffab77ab
Some checks failed
CI / Typecheck & Lint (pull_request) Failing after 0s
feat(epic222): Workshop — Timmy wizard presence, world state, WS bootstrap
## 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
2026-03-19 02:14:51 +00:00

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;
}