Files
nagi-bench/outputs/claude-opus-4-8/claude-code-max/dust2-arena/src/engine/effects.ts
T
Jesse ZhangandClaude Opus 4.8 cbfce3b7cc feat: add cs-dust2 React case with two model takes + react kind support
New case cs-dust2 (kind:react): a from-scratch React + three.js 5v5 Dust2 FPS.
Two takes registered: Claude Fable 5 / Claude Code / High (with a hand fix for a
64Hz fixed-step firing bug on high-refresh displays) and Claude Opus 4.8 /
Claude Code / Max (dust2-arena, one-shot). React cases commit source only; the
single file is built from source at deploy time. validate-data.ts now accepts a
React project directory as the declared artifact. AGENTS.md rewritten
English-only with React-case, bare-harness (no skills/plugins) and
artifact-anonymity rules.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 00:08:28 +01:00

119 lines
3.9 KiB
TypeScript

// Pooled transient visuals: bullet tracers, muzzle flashes, and impact sparks /
// blood puffs. All are cheap emissive/additive meshes recycled from fixed pools.
import * as THREE from 'three';
interface Slot {
mesh: THREE.Mesh;
life: number;
max: number;
}
export class Effects {
private tracers: Slot[] = [];
private flashes: Slot[] = [];
private impacts: Slot[] = [];
private scene: THREE.Scene;
constructor(scene: THREE.Scene) {
this.scene = scene;
const tracerGeo = new THREE.BoxGeometry(0.035, 0.035, 1);
for (let i = 0; i < 24; i++) {
const mesh = new THREE.Mesh(
tracerGeo,
new THREE.MeshBasicMaterial({ color: 0xffe08a, transparent: true, opacity: 0, blending: THREE.AdditiveBlending, depthWrite: false }),
);
mesh.visible = false;
scene.add(mesh);
this.tracers.push({ mesh, life: 0, max: 0.06 });
}
const flashGeo = new THREE.SphereGeometry(0.16, 6, 6);
for (let i = 0; i < 16; i++) {
const mesh = new THREE.Mesh(
flashGeo,
new THREE.MeshBasicMaterial({ color: 0xfff2b0, transparent: true, opacity: 0, blending: THREE.AdditiveBlending, depthWrite: false }),
);
mesh.visible = false;
scene.add(mesh);
this.flashes.push({ mesh, life: 0, max: 0.05 });
}
const impactGeo = new THREE.SphereGeometry(0.09, 6, 6);
for (let i = 0; i < 32; i++) {
const mesh = new THREE.Mesh(
impactGeo,
new THREE.MeshBasicMaterial({ color: 0xffffff, transparent: true, opacity: 0, depthWrite: false }),
);
mesh.visible = false;
scene.add(mesh);
this.impacts.push({ mesh, life: 0, max: 0.18 });
}
}
private take(pool: Slot[]): Slot | null {
for (const s of pool) if (s.life <= 0) return s;
return pool[0] ?? null;
}
tracer(fx: number, fy: number, fz: number, tx: number, ty: number, tz: number): void {
const s = this.take(this.tracers);
if (!s) return;
const dx = tx - fx, dy = ty - fy, dz = tz - fz;
const len = Math.hypot(dx, dy, dz) || 0.01;
s.mesh.position.set((fx + tx) / 2, (fy + ty) / 2, (fz + tz) / 2);
s.mesh.scale.set(1, 1, len);
s.mesh.lookAt(tx, ty, tz);
s.mesh.visible = true;
(s.mesh.material as THREE.MeshBasicMaterial).opacity = 0.9;
s.life = s.max;
}
flash(x: number, y: number, z: number): void {
const s = this.take(this.flashes);
if (!s) return;
s.mesh.position.set(x, y, z);
const sc = 0.7 + Math.random() * 0.6;
s.mesh.scale.setScalar(sc);
s.mesh.visible = true;
(s.mesh.material as THREE.MeshBasicMaterial).opacity = 1;
s.life = s.max;
}
impact(x: number, y: number, z: number, blood: boolean): void {
const s = this.take(this.impacts);
if (!s) return;
s.mesh.position.set(x, y, z);
s.mesh.scale.setScalar(blood ? 1.4 : 1);
(s.mesh.material as THREE.MeshBasicMaterial).color.setHex(blood ? 0x9c1c1c : 0xd9cdb0);
s.mesh.visible = true;
(s.mesh.material as THREE.MeshBasicMaterial).opacity = 1;
s.life = s.max;
}
update(dt: number): void {
for (const s of this.tracers) {
if (s.life <= 0) continue;
s.life -= dt;
const mat = s.mesh.material as THREE.MeshBasicMaterial;
mat.opacity = Math.max(0, (s.life / s.max) * 0.9);
if (s.life <= 0) s.mesh.visible = false;
}
for (const s of this.flashes) {
if (s.life <= 0) continue;
s.life -= dt;
const mat = s.mesh.material as THREE.MeshBasicMaterial;
mat.opacity = Math.max(0, s.life / s.max);
if (s.life <= 0) s.mesh.visible = false;
}
for (const s of this.impacts) {
if (s.life <= 0) continue;
s.life -= dt;
const k = s.life / s.max;
const mat = s.mesh.material as THREE.MeshBasicMaterial;
mat.opacity = Math.max(0, k);
s.mesh.scale.multiplyScalar(1 + dt * 2);
s.mesh.position.y += dt * 0.4;
if (s.life <= 0) s.mesh.visible = false;
}
}
}