The chronicle marks each line with its wizard's color; the turn's owner stands out
Two of Randy's flourishes. Every chronicle line now carries a small square in the color of the wizard acting — whoever cast, struck, walked, or countered, else the turn's owner — so one player's doings can be picked out of the scroll at a glance. And the life-points row of the wizard whose turn it is wears an accent bar and a tint, since bold alone was lost in the handwriting. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
19aec73b30
commit
3fc9df85ac
@@ -2958,7 +2958,7 @@
|
||||
{:else}
|
||||
{#each net.log as line, i (i)}
|
||||
<div class:table-talk={line.text.startsWith("\u{1F4AC}")}>
|
||||
{line.text}
|
||||
<span class="log-mark" class:blank={!line.actor} style:background={line.actor ? playerColor(line.actor) : "transparent"}></span>{line.text}
|
||||
{#if line.notable && line.turn !== null && prefs.instantReplay && !net.spectating}
|
||||
<button class="moment-eye" title="relive this turn"
|
||||
onclick={() => net.requestMoment(line.turn!)}>👁</button>
|
||||
@@ -3848,6 +3848,14 @@
|
||||
min-height: 1.55rem;
|
||||
}
|
||||
.slip.creature-note { background: #e7edda; border-color: #7d8a5a; }
|
||||
.score-row.active {
|
||||
font-weight: 700;
|
||||
background: rgba(58, 47, 31, 0.08);
|
||||
border-left: 3px solid #3a2f1f;
|
||||
margin-left: -0.45rem;
|
||||
padding-left: 0.3rem;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.score-row.active .score-name { font-weight: 700; }
|
||||
.score-temper {
|
||||
font-family: "Caveat", cursive;
|
||||
@@ -3916,6 +3924,17 @@
|
||||
box-shadow: 0 2px 7px rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
.chronicle div + div { margin-top: 0.05rem; }
|
||||
/* the acting wizard's color, so one player's lines can be picked out at a glance */
|
||||
.log-mark {
|
||||
display: inline-block;
|
||||
width: 0.55em;
|
||||
height: 0.55em;
|
||||
margin-right: 0.45em;
|
||||
border-radius: 2px;
|
||||
border: 1px solid rgba(58, 47, 31, 0.35);
|
||||
vertical-align: 0.05em;
|
||||
}
|
||||
.log-mark.blank { border-color: transparent; }
|
||||
.moment-eye {
|
||||
background: none;
|
||||
border: none;
|
||||
|
||||
@@ -234,6 +234,22 @@ export interface LogLine {
|
||||
text: string;
|
||||
turn: number | null;
|
||||
notable: boolean;
|
||||
/** The wizard acting, for the color mark beside the line: whoever
|
||||
* cast, struck, walked, or countered — otherwise the turn's owner. */
|
||||
actor?: string;
|
||||
}
|
||||
|
||||
/** Events whose `player` is the one acting rather than the one acted on. */
|
||||
const ACTING = new Set([
|
||||
"moved", "warpStepped", "jumpedPit", "counteractionPlayed", "treasurePickedUp", "treasureDropped",
|
||||
"numberPlayedForMovement", "cardsDiscarded", "turnStarted", "turnEnded", "doorUnlocked", "madDash",
|
||||
"climbedFromPit", "spellTrapped",
|
||||
]);
|
||||
function actorOf(e: GameEvent, turnOwner: string | null): string | undefined {
|
||||
const any = e as unknown as Record<string, unknown>;
|
||||
for (const k of ["caster", "attacker", "by", "owner"]) if (typeof any[k] === "string") return any[k] as string;
|
||||
if (ACTING.has(e.type) && typeof any.player === "string") return any.player as string;
|
||||
return turnOwner ?? undefined;
|
||||
}
|
||||
|
||||
/** The boundaries the turn count walks — mirrored by the server's
|
||||
@@ -305,6 +321,8 @@ class Net {
|
||||
* socket that never fires onclose; the watchdog closes it by hand so
|
||||
* the reconnect loop (and the masthead banner) actually engage. */
|
||||
private lastHeard = Date.now();
|
||||
/** Whose turn the chronicle is in, for the mark on lines with no actor of their own. */
|
||||
private turnOwner: string | null = null;
|
||||
private watchdog: ReturnType<typeof setInterval> | null = null;
|
||||
roomId = $state<string | null>(null);
|
||||
players = $state<string[]>([]);
|
||||
@@ -510,6 +528,7 @@ class Net {
|
||||
this.openingRolls = { rolls: e.dieRolls, first: e.firstPlayer, players: e.players };
|
||||
}
|
||||
if (TURN_BOUNDARY.has(e.type)) this.turnCounter++;
|
||||
if (e.type === "turnStarted") this.turnOwner = e.player;
|
||||
const line = humanize(e);
|
||||
if (line) {
|
||||
// Every turn wears its eye, on the header line that opens
|
||||
@@ -517,7 +536,7 @@ class Net {
|
||||
// winning line carries its own besides.
|
||||
const notable = this.turnCounter >= 0 &&
|
||||
(e.type === "gameWon" || TURN_BOUNDARY.has(e.type));
|
||||
this.log = [...this.log, { text: line, turn: this.turnCounter >= 0 ? this.turnCounter : null, notable }];
|
||||
this.log = [...this.log, { text: line, turn: this.turnCounter >= 0 ? this.turnCounter : null, notable, actor: actorOf(e, this.turnOwner) }];
|
||||
}
|
||||
}
|
||||
if (talk > 0) {
|
||||
@@ -545,7 +564,7 @@ class Net {
|
||||
break;
|
||||
}
|
||||
case "chat": {
|
||||
this.log = [...this.log, { text: `\u{1F4AC} ${msg.player}: ${msg.text}`, turn: null, notable: false }];
|
||||
this.log = [...this.log, { text: `\u{1F4AC} ${msg.player}: ${msg.text}`, turn: null, notable: false, actor: msg.player }];
|
||||
this.chatCount += 1;
|
||||
if (this.roomId) this.markChatSeen();
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user