SOURCE / PINNED RELEASE
Made of little things.
Browser Side Quest
- Release
- b9d42f5768e4…
- Author-recorded commit
- c267690145fa…
- License
- LICENSE
- Author’s source reference
- nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.shakespeare.diy%2F/browser-side-quest
Archive hash verified: 6d4b90b8a5f328f7…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
/**
* Compact world state the shard host broadcasts so a peer can take over when
* the host leaves. Mob ids are deterministic (area/kind/index), so a fresh
* World can be brought in line with the departed host's: mobs that were dead
* are killed again (and respawn on the normal 30 s timer), survivors keep their
* hit points and position. Items on the ground and chest drops are transient
* and are not carried over.
*/
import { Mob } from './entities.js';
import type { World } from './world.js';
/** [id, hitPoints, x, y] for every living mob. */
export type WorldSnapshot = { v: 1; mobs: [number, number, number, number][] };
export function takeSnapshot(world: World): WorldSnapshot {
const mobs: WorldSnapshot['mobs'] = [];
for (const mob of world.mobs.values()) {
if (!mob.isDead) mobs.push([mob.id, mob.hitPoints, mob.x, mob.y]);
}
return { v: 1, mobs };
}
export function isSnapshot(value: unknown): value is WorldSnapshot {
const s = value as WorldSnapshot;
return (
!!s &&
s.v === 1 &&
Array.isArray(s.mobs) &&
s.mobs.every((m) => Array.isArray(m) && m.length === 4 && m.every((n) => typeof n === 'number' && Number.isFinite(n)))
);
}
/** Apply a snapshot to a freshly started world (before any player joins). */
export function restoreSnapshot(world: World, snapshot: WorldSnapshot): void {
const alive = new Map(snapshot.mobs.map(([id, hp, x, y]) => [id, { hp, x, y }]));
for (const mob of [...world.mobs.values()]) {
const state = alive.get(mob.id);
if (!state) {
killSilently(world, mob);
continue;
}
mob.hitPoints = Math.max(1, Math.min(mob.maxHitPoints, Math.round(state.hp)));
if (world.isValidPosition(state.x, state.y)) world.moveEntity(mob, state.x, state.y);
}
}
function killSilently(world: World, mob: Mob): void {
mob.hitPoints = 0;
world.removeEntity(mob);
}
