The mystery machine keeps its mood to itself
"Random" read as random ACTIONS, and a revealed random pick is barely a mystery anyway. The fourth workshop button is now "mystery": the server rolls the temperament and keeps it — the join line records it for replay, the drive loop plays it, but the room tells the table only "mystery". Roster and scoresheet show ⚙ mystery; the machine's behavior is the only tell. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
af75a024ff
commit
1aafb9e837
@@ -47,8 +47,8 @@ export interface Room {
|
||||
events: GameEvent[]; // full history (unredacted — redact per recipient)
|
||||
/** Table talk, persisted with the room (public to all seats). */
|
||||
chat: { player: PlayerId; text: string; at: string }[];
|
||||
/** Seats the server itself plays, and each one's temperament. */
|
||||
bots: Map<PlayerId, AutomatonStyle>;
|
||||
/** Seats the server itself plays: temperament, and whether it is told. */
|
||||
bots: Map<PlayerId, { style: AutomatonStyle; secret: boolean }>;
|
||||
}
|
||||
|
||||
const rooms = new Map<string, Room>();
|
||||
@@ -247,12 +247,14 @@ export function addAutomaton(room: Room, styleWanted?: string): { name: PlayerId
|
||||
if (room.players.length >= 6) return { error: "room is full" };
|
||||
const name = AUTOMATON_NAMES.find((n) => !room.players.includes(n));
|
||||
if (!name) return { error: "the workshop is empty" };
|
||||
const style: AutomatonStyle = AUTOMATON_STYLES.includes(styleWanted as AutomatonStyle)
|
||||
const known = AUTOMATON_STYLES.includes(styleWanted as AutomatonStyle);
|
||||
const style: AutomatonStyle = known
|
||||
? (styleWanted as AutomatonStyle)
|
||||
: AUTOMATON_STYLES[randomInt(AUTOMATON_STYLES.length)]!;
|
||||
const secret = !known; // the mystery machine keeps its mood to itself
|
||||
room.players.push(name);
|
||||
room.bots.set(name, style);
|
||||
appendLine(room.id, { kind: "join", name, bot: true, style });
|
||||
room.bots.set(name, { style, secret });
|
||||
appendLine(room.id, { kind: "join", name, bot: true, style, ...(secret ? { secret: true } : {}) });
|
||||
return { name };
|
||||
}
|
||||
|
||||
@@ -277,7 +279,7 @@ export function driveOneAutomaton(room: Room): { seat: PlayerId; events: GameEve
|
||||
const seat = actingSeat(room);
|
||||
if (!seat || !room.bots.has(seat)) return null;
|
||||
const view = viewFor(room.state!, seat);
|
||||
const cmd = automatonCommand(view, room.bots.get(seat)) ?? automatonFallback(view);
|
||||
const cmd = automatonCommand(view, room.bots.get(seat)?.style) ?? automatonFallback(view);
|
||||
let r = runCommand(room, seat, cmd);
|
||||
if ("error" in r) {
|
||||
r = runCommand(room, seat, automatonFallback(view));
|
||||
@@ -494,7 +496,10 @@ export function loadPersistedRooms(): void {
|
||||
if (line.kind === "join") {
|
||||
if (line.bot) {
|
||||
room.players.push(line.name);
|
||||
room.bots.set(line.name, (line.style as AutomatonStyle) ?? "hunter");
|
||||
room.bots.set(line.name, {
|
||||
style: (line.style as AutomatonStyle) ?? "hunter",
|
||||
secret: line.secret === true,
|
||||
});
|
||||
} else {
|
||||
const joinHash = line.tokenHash ?? (line.token ? hashToken(line.token) : null);
|
||||
if (!joinHash) throw new Error("join line has no token");
|
||||
|
||||
Reference in New Issue
Block a user