The table speaks plainer, and keeps its whimsy
Wording throughout the app, the server's messages and the engine's refusals trades its stacked dashes for sentences, while the maze still trusts you to honor its curses, the gallery still listens, and the clockwork still learns its best tricks from the humans at the table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
ad23c83b8a
commit
c64ff70ed4
@@ -38,7 +38,7 @@ export function humanize(e: GameEvent): string | null {
|
||||
const rolloff = e.players
|
||||
.map((p) => `${p} 🎲 ${(e.dieRolls[p] ?? []).join(", ")}`)
|
||||
.join(" · ");
|
||||
return `Game started. The roll-off: ${rolloff} — ${e.firstPlayer} goes first.`;
|
||||
return `Game started. The roll-off: ${rolloff}. ${e.firstPlayer} goes first.`;
|
||||
}
|
||||
case "turnStarted": return `— ${e.player}'s turn (round ${e.round}) —`;
|
||||
case "turnSkipped": return `${e.player} loses a turn.`;
|
||||
@@ -58,7 +58,7 @@ export function humanize(e: GameEvent): string | null {
|
||||
return `${e.player} counters ${what} with ${cardDef(e.cardId).name}!`;
|
||||
}
|
||||
case "counterNullified": return `${cardDef(e.card.cardId).name} is nullified by Anti-Anti!`;
|
||||
case "attackAbsorbedIntoHand": return `${e.player} absorbs ${cardDef(e.attackCard.cardId).name} into their hand — the card is theirs now!`;
|
||||
case "attackAbsorbedIntoHand": return `${e.player} absorbs ${cardDef(e.attackCard.cardId).name} into their hand, and the card is theirs now!`;
|
||||
case "attackResolved":
|
||||
if (e.redirected) return `The spell is reflected back at ${e.attacker}!`;
|
||||
if (e.fullyStopped) return `The attack is completely stopped.`;
|
||||
@@ -68,42 +68,42 @@ export function humanize(e: GameEvent): string | null {
|
||||
// blow that follows it has nothing to add.
|
||||
if (e.amount === 0 && e.source === "shadow upkeep") return null;
|
||||
const soak = e.soaks?.map((s) => `${s.what} soaks ${s.amount}`).join(", ");
|
||||
return `${e.player} takes ${e.amount} damage (${e.source}${soak ? ` — ${soak}` : ""}) — ${e.lifeAfter} life left.`;
|
||||
return `${e.player} takes ${e.amount} damage (${e.source}${soak ? `, ${soak}` : ""}), ${e.lifeAfter} life left.`;
|
||||
}
|
||||
case "stunned": return `${e.player} is stunned and loses a turn!`;
|
||||
case "knockedBack": return `${e.player} is knocked back ${e.squares} square(s)!`;
|
||||
case "stonesDestroyed": return `${e.player}'s magic stones are destroyed!`;
|
||||
case "wallCreated": return `A wall appears!`;
|
||||
case "wallDestroyed": return e.wasDoor ? `A door is blasted to rubble!` : `A wall crumbles!`;
|
||||
case "warpOpened": return `The outer wall breaches clean through — a new warp opens across the maze!`;
|
||||
case "extraTurnGranted": return `${e.player} speeds up — extra turn banked.`;
|
||||
case "wardWindow": return `The grab hangs in the air — ${e.owner} clutches something…`;
|
||||
case "pushWindow": return `${e.giant} bears down on ${e.pushee} at a fork — which way will they go?`;
|
||||
case "warpOpened": return `The outer wall breaches clean through, and a new warp opens across the maze!`;
|
||||
case "extraTurnGranted": return `${e.player} speeds up and banks an extra turn.`;
|
||||
case "wardWindow": return `The grab hangs in the air as ${e.owner} clutches something…`;
|
||||
case "pushWindow": return `${e.giant} bears down on ${e.pushee} at a fork. Which way will they go?`;
|
||||
case "pushChosen": return `${e.pushee} breaks ${SIDE_NAMES[e.direction].toLowerCase()}.`;
|
||||
case "treasureTorn": return e.toFloor
|
||||
? `${e.attacker} TEARS the treasure from ${e.defender}'s arms — it tumbles to the floor!`
|
||||
? `${e.attacker} TEARS the treasure from ${e.defender}'s arms, and it tumbles to the floor!`
|
||||
: `${e.attacker} TEARS the treasure from ${e.defender}'s arms!`;
|
||||
case "tearResisted": return `${e.defender} clutches the treasure with white knuckles — ${e.attacker} comes away empty!`;
|
||||
case "tearResisted": return `${e.defender} clutches the treasure with white knuckles, and ${e.attacker} comes away empty!`;
|
||||
case "treasureTearAttempted": return `${e.attacker} grabs for the treasure in ${e.defender}'s arms!`;
|
||||
case "powerstoneBoost": return `The powerstone hums over the number already played — ${e.player} gains a stride (${e.newAllowance} total).`;
|
||||
case "powerstoneBoost": return `The powerstone hums over the number already played, and ${e.player} gains a stride (${e.newAllowance} total).`;
|
||||
case "trapSprung": return e.cardId === "gift-from-below"
|
||||
? `${e.player} draws GIFT FROM BELOW — it bites for 3, then deals again!`
|
||||
? `${e.player} draws GIFT FROM BELOW. It bites for 3, then deals again!`
|
||||
: `${e.player} walked into an old TRAP! Lose a turn.`;
|
||||
case "attackMissed": return e.because === "invisible"
|
||||
? `The attack passes through empty air — ${e.defender} is invisible!`
|
||||
: `${e.defender} is too small to hit — the attack misses!`;
|
||||
? `The attack passes through empty air, for ${e.defender} is invisible!`
|
||||
: `${e.defender} is too small to hit, and the attack misses!`;
|
||||
case "damageImmune": return e.because === "bloodstone"
|
||||
? `${e.player}'s bloodstone drinks the whole blow — no damage.`
|
||||
: `${e.player} is stone — the damage has no effect.`;
|
||||
case "lifeGained": return `${e.player} gains ${e.amount} life (${e.source}) — now ${e.lifeAfter}.`;
|
||||
case "madDash": return `${e.player} MAD DASHES — every stride doubles: ${e.newAllowance} movement this turn!`;
|
||||
case "safeOpened": return `${e.player}'s ${cardDef(e.withCardId).name} clicks the safe open — until turn's end.`;
|
||||
case "safeDamaged": return `${e.attacker} batters the safe — ${e.amount} damage (${e.total}/15).`;
|
||||
? `${e.player}'s bloodstone drinks the whole blow, and no damage lands.`
|
||||
: `${e.player} is stone, and the damage has no effect.`;
|
||||
case "lifeGained": return `${e.player} gains ${e.amount} life (${e.source}), now ${e.lifeAfter}.`;
|
||||
case "madDash": return `${e.player} MAD DASHES, every stride doubled, for ${e.newAllowance} movement this turn!`;
|
||||
case "safeOpened": return `${e.player}'s ${cardDef(e.withCardId).name} clicks the safe open until the turn's end.`;
|
||||
case "safeDamaged": return `${e.attacker} batters the safe for ${e.amount} damage (${e.total}/15).`;
|
||||
case "safeSmashed": return `💥 The safe BURSTS open under ${e.attacker}'s assault!`;
|
||||
case "squareContentDamaged": return `${e.attacker} ${e.kind === "ooze" ? "burns" : "batters"} the ${e.kind} — ${e.amount} damage (${e.total}/${e.needed}).`;
|
||||
case "squareContentDamaged": return `${e.attacker} ${e.kind === "ooze" ? "burns" : "batters"} the ${e.kind} for ${e.amount} damage (${e.total}/${e.needed}).`;
|
||||
case "squareContentDestroyed": return e.kind === "ooze" ? `🔥 The ooze burns away under ${e.attacker}'s fire!` : `🌿 The ${e.kind} is torn apart by ${e.attacker}'s attack!`;
|
||||
case "slowDeathWindow": return `Slow Death bites ${e.player} for ${e.points} — a counter hovers over the wound…`;
|
||||
case "slowDeathCountered": return `${e.player}'s ${cardDef(e.cardId).name} blunts the rot — ${e.remaining} point${e.remaining === 1 ? "" : "s"} still coming.`;
|
||||
case "slowDeathWindow": return `Slow Death bites ${e.player} for ${e.points}, and a counter hovers over the wound…`;
|
||||
case "slowDeathCountered": return `${e.player}'s ${cardDef(e.cardId).name} blunts the rot, with ${e.remaining} point${e.remaining === 1 ? "" : "s"} still coming.`;
|
||||
case "spellSustained": return isPermanentDuration(e.turns)
|
||||
? `${spellName(e.cardId)} settles over ${e.target} for good.`
|
||||
: `${spellName(e.cardId)} settles over ${e.target} (${e.turns} turn${e.turns === 1 ? "" : "s"}).`;
|
||||
@@ -112,16 +112,16 @@ export function humanize(e: GameEvent): string | null {
|
||||
? `${e.player} teleports across the maze!`
|
||||
: `${e.player} is teleported away by ${e.by}!`;
|
||||
case "positionsSwapped": return `${e.a} and ${e.b} swap places!`;
|
||||
case "homeBasesSwapped": return `${e.a} and ${e.b} swap home bases — the maze's loyalties shift!`;
|
||||
case "homeBasesSwapped": return `${e.a} and ${e.b} swap home bases, and the maze's loyalties shift!`;
|
||||
case "cardErased": return e.found
|
||||
? `${e.player}'s ${e.cardId ? cardDef(e.cardId).name : "card"} is erased from their mind!`
|
||||
: `${e.player} wasn't holding that card — the erasure fizzles.`;
|
||||
: `${e.player} wasn't holding that card, so the erasure fizzles.`;
|
||||
case "cardsStolen": return `${e.to} steals ${e.count} card(s) from ${e.from}'s thoughts!`;
|
||||
case "handRevealed": return `${e.to} reads ${e.player}'s mind — their hand is revealed.`;
|
||||
case "handRevealed": return `${e.to} reads ${e.player}'s mind, and their hand lies revealed.`;
|
||||
case "cardsDrawnPrivate": return e.cards.length > 0
|
||||
? `You draw ${e.cards.map((c) => cardDef(c.cardId).name).join(", ")}.`
|
||||
: null;
|
||||
case "creationDispelled": return `The ${e.what.replace(/-/g, " ")} unravels — dispelled!`;
|
||||
case "creationDispelled": return `The ${e.what.replace(/-/g, " ")} unravels, dispelled!`;
|
||||
case "firewallCreated": return `A wall of fire roars up (${e.turns} turn${e.turns === 1 ? "" : "s"})!`;
|
||||
case "firewallExpired": return `The wall of fire gutters out.`;
|
||||
case "firewallBurned": return `${e.player} steps through the flames!`;
|
||||
@@ -134,7 +134,7 @@ export function humanize(e: GameEvent): string | null {
|
||||
case "doorReleased": return `The held door swings shut.`;
|
||||
case "doorsRelocked": return `The door swings shut and relocks.`;
|
||||
case "washedBack": return `${e.player} is washed back ${e.blockedSpaces > 0 ? "and crushed against the stone" : "by the wave"}!`;
|
||||
case "enteredThornbush": return `${e.player} pushes into the thorns — and bleeds for it.`;
|
||||
case "enteredThornbush": return `${e.player} pushes into the thorns and bleeds for it.`;
|
||||
case "objectDragged": return `The ${e.what.replace(/-/g, " ")} is dragged across the maze.`;
|
||||
case "cardsDealt": return `${e.player} is dealt ${e.count} card${e.count === 1 ? "" : "s"}.`;
|
||||
case "cardsStolenPrivate": return e.cards.length > 0
|
||||
@@ -142,25 +142,25 @@ export function humanize(e: GameEvent): string | null {
|
||||
: null;
|
||||
case "handRevealedPrivate": return `${e.player}'s hand lies open to you: ${e.cards.map((c) => cardDef(c.cardId).name).join(", ")}.`;
|
||||
case "creatureWarpStepped": return `The creature slips through the dimensional warp!`;
|
||||
case "mentalForceFizzled": return `${e.attacker}'s mental force strains at ${e.defender} — and fails to find a path.`;
|
||||
case "mentalForceFizzled": return `${e.attacker}'s mental force strains at ${e.defender} and fails to find a path.`;
|
||||
case "doorJammed": return `${e.player} jams a door's lock solid.`;
|
||||
case "lockRemoved": return `${e.player} removes a door's lock for good.`;
|
||||
case "cardDisplayed": return `${e.player} displays ${cardDef(e.card.cardId).name}.`;
|
||||
case "lifeTraded": return `${e.player} burns ${e.points} life for speed!`;
|
||||
case "castAroundCorner": return `The spell bends around the corner!`;
|
||||
case "moveBumped": return e.why === "pit"
|
||||
? `${e.player} leaps the pit — nothing to land on beyond it — and teeters back.`
|
||||
? `${e.player} leaps the pit, finds nothing to land on beyond it, and teeters back.`
|
||||
: `${e.player} blunders into a wall!`;
|
||||
case "attackMisdirected": return e.newTarget
|
||||
? `${e.attacker}'s blind attack veers off — and hits ${e.newTarget}!`
|
||||
? `${e.attacker}'s blind attack veers off and hits ${e.newTarget}!`
|
||||
: `${e.attacker}'s blind attack flies off into the darkness.`;
|
||||
case "retreatedInHorror": return `${e.player} flees the hideous sight!`;
|
||||
case "illusionWallCreated": return `A wall appears... or does it?`;
|
||||
case "illusionTested": return e.result === "seesThrough"
|
||||
? `${e.player} sees right through the illusion!`
|
||||
: `${e.player} is convinced the wall is real.`;
|
||||
case "sectorRotated": return `The maze GRINDS — a sector rotates ${e.clockwise ? "clockwise" : "counterclockwise"}!`;
|
||||
case "sectorRelocated": return `The maze SHUDDERS — an entire sector slides away!`;
|
||||
case "sectorRotated": return `The maze GRINDS as a sector rotates ${e.clockwise ? "clockwise" : "counterclockwise"}!`;
|
||||
case "sectorRelocated": return `The maze SHUDDERS as an entire sector slides away!`;
|
||||
case "creatureCreated": return `${e.controller} summons a ${e.kind.replace(/-/g, " ")}!`;
|
||||
case "creatureMoved": return null;
|
||||
case "creatureAttacked":
|
||||
@@ -172,7 +172,7 @@ export function humanize(e: GameEvent): string | null {
|
||||
case "trollRegenerated": return `The troll's stony hide knits itself back together.`;
|
||||
case "shadowUpkeep": return `The shadow drains its master (${e.lifeAfter} life left).`;
|
||||
case "impScorches": return `The fire imp scorches ${e.player}!`;
|
||||
case "monsterBoosted": return `The monster GROWS — its ${e.boost} doubles!`;
|
||||
case "monsterBoosted": return `The monster GROWS, and its ${e.boost} doubles!`;
|
||||
case "wandCharged": return `${e.player} charges a wand (${e.charges} charges).`;
|
||||
case "wandUsed": return e.chargesLeft > 0 ? `The wand crackles (${e.chargesLeft} left).` : null;
|
||||
case "wandExhausted": return `${e.player}'s wand crumbles to dust.`;
|
||||
@@ -190,59 +190,59 @@ export function humanize(e: GameEvent): string | null {
|
||||
case "stuckInSlime": return `${e.player} squelches into the slime and sticks fast.`;
|
||||
case "boobytrapPlaced": return `${e.caster} places four suspicious tokens...`;
|
||||
case "boobytrapSprung": return `SNAP! ${e.player} finds the real boobytrap!`;
|
||||
case "boobytrapBlank": return `${e.player} flips a face-down token — a blank. The rest still wait.`;
|
||||
case "boobytrapBlank": return `${e.player} flips a face-down token and finds a blank. The rest still wait.`;
|
||||
case "objectsGlued": return `Everything on that square is glued down (${e.turns} turns).`;
|
||||
case "safeCreated": return `A massive safe slams down around the loot.`;
|
||||
case "itemsTraded": return `Two items blink and trade places.`;
|
||||
case "stoneTurnedToWater": return `Stone runs like water — a wave crashes out!`;
|
||||
case "stoneTurnedToWater": return `Stone runs like water, and a wave crashes out!`;
|
||||
case "handsSwapped": return `${e.a} and ${e.b} trade entire hands of cards!`;
|
||||
case "handsScrambled": return `CHAOS! Every hand is thrown in a pile and redealt!`;
|
||||
case "rammed": return `BAAA! ${e.attacker} turns into a goat and rams ${e.target} (${e.distance} spaces)!`;
|
||||
case "treasureThrown": return `${e.attacker} HURLS their treasure (${e.distance} spaces)!`;
|
||||
case "illusionBelieved": return e.believed ? `${e.player} flinches — the illusion feels real!` : `${e.player} laughs off the illusion.`;
|
||||
case "illusionBelieved": return e.believed ? `${e.player} flinches, for the illusion feels real!` : `${e.player} laughs off the illusion.`;
|
||||
case "itemStolen": return `${e.to} picks ${e.from}'s pocket.`;
|
||||
case "itemsSwapped": return `${e.a} and ${e.b} swap items.`;
|
||||
case "swapFizzled": return `${e.player}'s trade comes to nothing — the named items were not there to swap.`;
|
||||
case "wardSprung": return `${e.owner}'s treasure was WARDED — it bites ${e.victim}!`;
|
||||
case "swapFizzled": return `${e.player}'s trade comes to nothing, for the named items were not there to swap.`;
|
||||
case "wardSprung": return `${e.owner}'s treasure was WARDED, and it bites ${e.victim}!`;
|
||||
case "curseRemoved": return `${e.caster} lifts a curse from ${e.target}.`;
|
||||
case "objectEnchanted": return `An object gleams with Swarthmore's enchantment.`;
|
||||
case "warpTokensPlaced": return `Two dimensional warp tokens hum to life.`;
|
||||
case "warpStepped": return `${e.player} steps through the dimensional warp!`;
|
||||
case "exitsRedirected": return `The maze's outer exits twist and reconnect!`;
|
||||
case "outOfTurnWindow": return `${e.player} interrupts the flow of time (${e.kind === "interrupt" ? "Interrupt" : "Opportunity Fire"})!`;
|
||||
case "thumbOfGod": return `THE THUMB OF GOD descends! The die crashes down${e.aimedAt.x === e.landedAt.x && e.aimedAt.y === e.landedAt.y ? " dead on target" : " — and drifts"}!`;
|
||||
case "thumbOfGod": return `THE THUMB OF GOD descends! The die crashes down${e.aimedAt.x === e.landedAt.x && e.aimedAt.y === e.landedAt.y ? " dead on target" : ", and drifts"}!`;
|
||||
case "tokenScattered": return `${e.what} goes flying!`;
|
||||
case "ambushSet": return `You commit ${e.spell} to an ambush (${e.via}).`;
|
||||
case "ambushCancelled": return `You quietly disarm your ambush.`;
|
||||
case "ambushSprung": return `AMBUSH! ${e.owner}'s hidden ${e.via.replace(/-/g, " ")} springs on ${e.victim}!`;
|
||||
case "trapRedrawnDuringDeal": return null;
|
||||
case "died": return `☠ ${e.player} is dead${e.killedBy ? ` — killed by ${e.killedBy}` : ""}.`;
|
||||
case "died": return `☠ ${e.player} is dead${e.killedBy ? `, killed by ${e.killedBy}` : ""}.`;
|
||||
case "handTaken": return `${e.to} takes ${e.count} cards from ${e.from}'s body.`;
|
||||
case "treasurePickedUp": return `${e.player} grabs ${e.owner}'s treasure!`;
|
||||
case "objectDropped": return `${e.player} sets down the ${cardDef(e.card.cardId).name}${e.forced ? " (forced)" : ""}.`;
|
||||
case "objectPickedUp": return `${e.player} picks up the ${cardDef(e.card.cardId).name} — actions over.`;
|
||||
case "objectPickedUp": return `${e.player} picks up the ${cardDef(e.card.cardId).name}, and their actions are over.`;
|
||||
case "chaosShielded": return `${e.player} raises a FULL SHIELD and sits out the chaos.`;
|
||||
case "tableTalk": return `\u{1F4AC} ${e.player}: ${e.text}`;
|
||||
case "dieRolled": return `\u{1F3B2} ${e.player ?? "The maze"} rolls a ${e.roll} — ${e.purpose}.`;
|
||||
case "dieRolled": return `\u{1F3B2} ${e.player ?? "The maze"} rolls a ${e.roll} (${e.purpose}).`;
|
||||
case "pushed": return e.player
|
||||
? `${e.by} shoves ${e.player} down the corridor!`
|
||||
: `${e.by} shoves the beast down the corridor!`;
|
||||
case "spellTrapped": return `${e.caster} casts ${spellName(e.cardId)} into the slime — it sticks, waiting.`;
|
||||
case "spellTrapped": return `${e.caster} casts ${spellName(e.cardId)} into the slime, where it sticks and waits.`;
|
||||
case "slimeTrapSprung": return `The slime releases its ${spellName(e.cardId)} at ${e.victim}!`;
|
||||
case "slimeWashed": return `The wave washes the slime away.`;
|
||||
case "wallDamaged": {
|
||||
const what = e.needed === 15 ? "door" : "wall";
|
||||
const weapon = e.source === "punch" ? "bare fists" : e.source === "troll" ? "the troll's fist" : cardDef(e.source).name;
|
||||
return `${e.player} batters the ${what} with ${weapon} — ${e.total}/${e.needed}.`;
|
||||
return `${e.player} batters the ${what} with ${weapon} (${e.total}/${e.needed}).`;
|
||||
}
|
||||
case "treasureDropped": return e.onHomeOf ? `${e.player} drops a treasure on ${e.onHomeOf}'s home base!` : `${e.player} drops a treasure.`;
|
||||
case "playerEliminated": return e.reason === "treasuresLost" ? `${e.player} is eliminated — both treasures lost!` : null;
|
||||
case "playerEliminated": return e.reason === "treasuresLost" ? `${e.player} is eliminated, both treasures lost!` : null;
|
||||
case "cardsDiscarded":
|
||||
return `${e.player} discards ${e.cards.map((c) => spellName(c.cardId)).join(", ")}.`;
|
||||
case "cardsDrawn": return e.count > 0 ? `${e.player} draws ${e.count} card(s).` : null;
|
||||
case "deckReshuffled": return `The discard pile is reshuffled (${e.size} cards).`;
|
||||
case "turnEnded": return null;
|
||||
case "gameWon": return `🏆 ${e.player} WINS ${e.reason === "treasures" ? "by treasure!" : "— last wizard standing!"}`;
|
||||
case "gameWon": return `🏆 ${e.player} WINS ${e.reason === "treasures" ? "by treasure!" : "as the last wizard standing!"}`;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
@@ -329,7 +329,7 @@ export interface GameSummary {
|
||||
|
||||
export function attentionLabel(a: GameSummary["attention"]): string {
|
||||
switch (a) {
|
||||
case "counteract": return "UNDER ATTACK — respond!";
|
||||
case "counteract": return "UNDER ATTACK! Respond.";
|
||||
case "discard": return "DISCARD to your hand limit";
|
||||
case "interrupt": return "your interruption is waiting";
|
||||
default: return "YOUR TURN";
|
||||
@@ -682,8 +682,8 @@ class Net {
|
||||
this.pendingImage = null;
|
||||
if (image && typeof msg.id === "string") {
|
||||
fetch(`${API_URL}/api/feedback-image/${msg.id}`, { method: "POST", headers: { "content-type": image.type }, body: image })
|
||||
.then((r) => { if (!r.ok) this.flash("The words went through; the picture did not — a PNG or JPEG under 2.5 MB, please"); })
|
||||
.catch(() => this.flash("The words went through; the picture did not"));
|
||||
.then((r) => { if (!r.ok) this.flash("The words went through, but the picture did not. A PNG or JPEG under 2.5 MB, please."); })
|
||||
.catch(() => this.flash("The words went through, but the picture did not."));
|
||||
}
|
||||
this.refreshFeedback();
|
||||
break;
|
||||
@@ -753,7 +753,7 @@ class Net {
|
||||
create(name: string): void {
|
||||
this.you = name;
|
||||
this.spectating = false;
|
||||
if (!this.send({ type: "create", name })) this.flash("Still reaching the table — try again in a moment");
|
||||
if (!this.send({ type: "create", name })) this.flash("Still reaching the table. Try again in a moment.");
|
||||
}
|
||||
|
||||
/** Take a seat in the Peanut Gallery: watch a game with no name and no voice. */
|
||||
@@ -771,7 +771,7 @@ class Net {
|
||||
(s) => s.roomId === this.roomIdPending && s.name === name,
|
||||
);
|
||||
if (!this.send({ type: "join", roomId, name, token: existing?.token ?? this.token })) {
|
||||
this.flash("Still reaching the table — try again in a moment");
|
||||
this.flash("Still reaching the table. Try again in a moment.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -813,7 +813,7 @@ class Net {
|
||||
/** A word back to the desk under one of your reports, proven by the seat it was filed from. */
|
||||
answerReport(report: { id: string; roomId: string; player?: string }, text: string): boolean {
|
||||
const seat = this.seats.find((s) => s.roomId === report.roomId && (!report.player || s.name === report.player));
|
||||
if (!seat) { this.flash("That seat is no longer in this browser, so the desk cannot be sure it is you"); return false; }
|
||||
if (!seat) { this.flash("That seat is no longer in this browser, so the keeper cannot be sure it is you."); return false; }
|
||||
return this.send({ type: "feedbackAnswer", reportId: report.id, text, seat: $state.snapshot(seat) });
|
||||
}
|
||||
|
||||
@@ -922,8 +922,8 @@ class Net {
|
||||
try {
|
||||
new Notification(
|
||||
g.attention === "counteract"
|
||||
? `Wiz-War — you are under attack in ${g.roomId}!`
|
||||
: `Wiz-War — your turn in ${g.roomId}`,
|
||||
? `Wiz-War · you are under attack in ${g.roomId}!`
|
||||
: `Wiz-War · your turn in ${g.roomId}`,
|
||||
{
|
||||
body: `${g.players.join(" vs ")} · round ${g.round ?? "?"}`,
|
||||
tag: `wizwar-${g.roomId}`,
|
||||
@@ -1055,7 +1055,7 @@ class Net {
|
||||
const label = describeCommand(command);
|
||||
if (!this.send({ type: "command", command })) {
|
||||
this.unsent = label;
|
||||
this.flash(`${label[0]!.toUpperCase()}${label.slice(1)} was not sent — the table is out of reach`);
|
||||
this.flash(`${label[0]!.toUpperCase()}${label.slice(1)} was not sent. The table is out of reach.`);
|
||||
return false;
|
||||
}
|
||||
this.unsent = null;
|
||||
|
||||
Reference in New Issue
Block a user