fix: server motion defaults were sized for a large floor and overrode the client — bob 5-10cm -> 3mm (tunable), wander 60cm -> 2.5cm, path 8 -> 1.5cm/s

This commit is contained in:
2026-08-24 10:37:30 +10:00
parent 155ca62951
commit 4753162ea1
+16 -5
View File
@@ -41,7 +41,11 @@ class Playlist {
static defaults() {
return {
crossfadeSeconds: 3,
behaviors: { staticChance: 0.5, wanderRadius: 60, wanderSpeed: 0.15, wanderVertical: 10, pathSpeed: 8, pathChance: 0.4 },
/* Motion defaults are in centimetres and sized for the real display — a
* ~25-42cm table with ~4cm ghosts. They were originally set for a large
* open floor (60cm wander, 8cm/s walk, 5-10cm bob), which on a table reads
* as ghosts bouncing and sprinting off the edge. */
behaviors: { staticChance: 0.5, wanderRadius: 2.5, wanderSpeed: 0.15, wanderVertical: 0.4, pathSpeed: 1.5, pathChance: 0.4, bobAmp: 0.3, bobHz: 0.4 },
rarity: {
weights: { Common: 1.0, Rare: 0.5, Epic: 0.25, Legendary: 0.1 },
movementScale: { Common: 1.0, Rare: 1.0, Epic: 0.7, Legendary: 0.4 },
@@ -175,13 +179,20 @@ class Playlist {
const k = this.scale(g);
if (kind === 'path' && path) {
return { type: 'path', points: path.points.map(p => [...p]), mode: path.mode || 'loop',
speed: (b.pathSpeed ?? 8) * k, phase: Math.random() * 1000, seed: Math.floor(Math.random() * 1e6) };
speed: (b.pathSpeed ?? 1.5) * k, phase: Math.random() * 1000, seed: Math.floor(Math.random() * 1e6) };
}
if (kind === 'wander') {
return { type: 'wander', radius: (b.wanderRadius ?? 60) * k, speed: (b.wanderSpeed ?? 0.15) * k,
vertical: (b.wanderVertical ?? 10) * k, seed: Math.floor(Math.random() * 1e6) };
return { type: 'wander', radius: (b.wanderRadius ?? 2.5) * k, speed: (b.wanderSpeed ?? 0.15) * k,
vertical: (b.wanderVertical ?? 0.4) * k, seed: Math.floor(Math.random() * 1e6) };
}
return { type: 'static', bobAmp: (5 + Math.random() * 5) * k, bobHz: 0.3 + Math.random() * 0.3 };
/* Idle bob, in cm. The server is authoritative here, so these defaults —
* not the client's — decide how much a static ghost floats. 3mm suits the
* real display (~4cm ghosts on a 25-42cm sheet); the jitter just keeps a
* group of statics from bobbing in lockstep. Tunable: behaviors.bobAmp/bobHz. */
const amp = b.bobAmp ?? 0.3, hz = b.bobHz ?? 0.4;
return { type: 'static',
bobAmp: amp * (0.8 + Math.random() * 0.4) * k,
bobHz: hz * (0.85 + Math.random() * 0.3) };
}
makeRecord(g, { spawn, path }, behavior, opts = {}) {