The banter moves to its own book, and learns new verses
BOT_LINES leaves the server internals for banter.ts, keyed by semantic trigger instead of raw event type, so editing the clockwork's voice takes no knowledge of the event stream. The repertoire grows from fourteen lines to fifty across twelve occasions — grabbing and delivering gold, dealing and taking pain, killing and dying, summoning, walling, escaping, springing traps, dodging, and winning. Bots also now speak when things happen TO them: the actor remarks on its deeds, bystanders on their suffering — a berserker taking a hit answers "I FELT THAT. DO IT AGAIN." even on your turn. Still at most one voice per step, still half the time, menace over chatter. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
d4565006bc
commit
b01e27c644
@@ -49,6 +49,7 @@ import {
|
||||
type Room,
|
||||
} from "./rooms";
|
||||
import { engagementStats, recordHotseat } from "./stats";
|
||||
import { BOT_LINES, type BanterTrigger } from "./banter";
|
||||
|
||||
// --- Abuse limits: this is a public server on a small box. -----------------
|
||||
const MAX_SOCKETS = 300; // concurrent connections
|
||||
@@ -192,49 +193,44 @@ function broadcast(room: Room, makeMessage: (playerId: PlayerId) => unknown): vo
|
||||
*/
|
||||
const BOT_STEP_MS = 1500;
|
||||
|
||||
/** The clockwork occasionally speaks. Sparingly — menace over chatter. */
|
||||
const BOT_LINES: Record<string, Record<string, string[]>> = {
|
||||
hunter: {
|
||||
treasurePickedUp: ["ACQUISITION COMPLETE.", "YOUR GOLD HAS BEEN REALLOCATED."],
|
||||
treasureDropped: ["DELIVERY CONFIRMED. PLEASURE DOING BUSINESS."],
|
||||
damaged: ["AN INEFFICIENT USE OF YOUR TURN."],
|
||||
died: ["INVENTORY TRANSFERRED. CONDOLENCES."],
|
||||
},
|
||||
berserker: {
|
||||
treasurePickedUp: ["GOLD IS MERELY BAIT."],
|
||||
damaged: ["YES. AGAIN.", "PAIN RECEIPT PRINTED."],
|
||||
died: ["SCHEDULED DEMISE: DELIVERED.", "NEXT."],
|
||||
creatureCreated: ["I HAVE MADE YOU A FRIEND. IT IS NOT FRIENDLY."],
|
||||
},
|
||||
worrier: {
|
||||
treasurePickedUp: ["taking this. sorry. sorry."],
|
||||
damaged: ["ow. logged.", "unnecessary!"],
|
||||
wallCreated: ["good wall. safe wall."],
|
||||
died: ["oh no. oh no. it worked?"],
|
||||
},
|
||||
};
|
||||
/** What a step's event means to this bot — its own deed when it acted,
|
||||
* its own suffering when somebody else did. Null: nothing worth a word. */
|
||||
function banterTrigger(
|
||||
e: { type: string; [k: string]: unknown }, seat: string, actor: string,
|
||||
): BanterTrigger | null {
|
||||
if (actor === seat) {
|
||||
if (e.type === "treasurePickedUp" && e.player === seat) return "grabGold";
|
||||
if (e.type === "treasureDropped" && e.player === seat) return "deliverGold";
|
||||
if (e.type === "damaged" && e.player !== seat) return "dealPain";
|
||||
if (e.type === "died" && e.killedBy === seat && e.player !== seat) return "kill";
|
||||
if (e.type === "creatureCreated" && e.controller === seat) return "summon";
|
||||
if (e.type === "wallCreated" && e.caster === seat) return "buildWall";
|
||||
if (e.type === "teleported" && e.player === seat && e.by === seat) return "escape";
|
||||
if (e.type === "trapSprung" && e.player === seat) return "springTrap";
|
||||
if (e.type === "gameWon" && e.player === seat) return "win";
|
||||
}
|
||||
if (e.type === "damaged" && e.player === seat) return "takePain";
|
||||
if (e.type === "died" && e.player === seat) return "die";
|
||||
if (e.type === "attackMissed" && e.defender === seat) return "dodge";
|
||||
return null;
|
||||
}
|
||||
|
||||
function botRemark(room: Room, seat: string, events: { type: string; [k: string]: unknown }[]): void {
|
||||
const bot = room.bots.get(seat);
|
||||
if (!bot) return;
|
||||
const lines = BOT_LINES[bot.style] ?? {};
|
||||
for (const e of events) {
|
||||
// Speak only about its own deeds, and rarely.
|
||||
const mine =
|
||||
(e.type === "treasurePickedUp" && e.player === seat) ||
|
||||
(e.type === "treasureDropped" && e.player === seat) ||
|
||||
(e.type === "damaged" && e.player !== seat) ||
|
||||
(e.type === "died" && e.killedBy === seat) ||
|
||||
(e.type === "creatureCreated" && e.controller === seat) ||
|
||||
(e.type === "wallCreated" && e.caster === seat);
|
||||
const pool = lines[e.type];
|
||||
if (!mine || !pool || Math.random() > 0.5) continue;
|
||||
const text = pool[Math.floor(Math.random() * pool.length)]!;
|
||||
const said = addChat(room, seat, text);
|
||||
if (!("error" in said)) {
|
||||
broadcast(room, () => ({ type: "chat", player: seat, text: said.text, at: said.at }));
|
||||
/** Every seated bot gets a chance to remark on the step — the actor on its
|
||||
* deeds, bystanders on their suffering. Rarely, and one voice at most. */
|
||||
function botRemark(room: Room, actor: string, events: { type: string; [k: string]: unknown }[]): void {
|
||||
for (const [seat, bot] of room.bots) {
|
||||
const lines = BOT_LINES[bot.style] ?? {};
|
||||
for (const e of events) {
|
||||
const trigger = banterTrigger(e, seat, actor);
|
||||
const pool = trigger ? lines[trigger] : undefined;
|
||||
if (!pool || Math.random() > 0.5) continue;
|
||||
const text = pool[Math.floor(Math.random() * pool.length)]!;
|
||||
const said = addChat(room, seat, text);
|
||||
if (!("error" in said)) {
|
||||
broadcast(room, () => ({ type: "chat", player: seat, text: said.text, at: said.at }));
|
||||
}
|
||||
return; // one remark per step, whole table
|
||||
}
|
||||
return; // one remark per step at most
|
||||
}
|
||||
}
|
||||
const pumping = new Set<string>();
|
||||
@@ -363,6 +359,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 }));
|
||||
botRemark(room, session.playerId, result.events as { type: string }[]);
|
||||
runBots(room);
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user