From 55c76cf20e363a697bc0dffe671da6572ccc21f1 Mon Sep 17 00:00:00 2001 From: Eric Wagoner Date: Tue, 18 Aug 2026 00:38:42 -0400 Subject: [PATCH] Game over unmasks the mystery machines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hands already go face-up when the maze is won; now the roster does too — a secret automaton's row reads 'archmage berserker (was a mystery)' once the game finishes, broadcast with the final command from either a human or the bot pump. Mid-game, the mood stays its own business. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_0138A8CjeQRpvzKxuMfz1Bqc --- packages/server/src/index.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 79adef4..871a12e 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -180,7 +180,14 @@ function roomInfo(room: Room) { started: room.state !== null, colors: Object.fromEntries(room.colorChoices), bots: Object.fromEntries( - [...room.bots].map(([name, b]) => [name, `${b.tier} ${b.secret ? "mystery" : b.style}`]), + // A mystery machine keeps its mood only while the game lives: once it + // ends, the hands go face-up and so does the temperament. + [...room.bots].map(([name, b]) => [ + name, + room.state?.phase === "finished" + ? `${b.tier} ${b.style}${b.secret ? " (was a mystery)" : ""}` + : `${b.tier} ${b.secret ? "mystery" : b.style}`, + ]), ), }; } @@ -254,6 +261,8 @@ function runBots(room: Room): void { } broadcast(room, (playerId) => ({ type: "events", events: redactFor(step.events, playerId) })); broadcast(room, (playerId) => ({ type: "state", view: viewForPlayer(room, playerId), seq: room.log.length })); + // The game's end unmasks the mystery machines in the roster. + if (room.state?.phase === "finished") broadcast(room, () => roomInfo(room)); botRemark(room, step.seat, step.events as { type: string }[]); setTimeout(tick, BOT_STEP_MS); }; @@ -368,6 +377,7 @@ wss.on("connection", (socket) => { if ("error" in result) return send(socket, { type: "error", message: result.error }); broadcast(room, (playerId) => ({ type: "events", events: redactFor(result.events, playerId) })); broadcast(room, (playerId) => ({ type: "state", view: viewForPlayer(room, playerId), seq: room.log.length })); + if (room.state?.phase === "finished") broadcast(room, () => roomInfo(room)); botRemark(room, session.playerId, result.events as { type: string }[]); runBots(room); break;