Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion skills/dungeon-master/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ This is the heart of the experience. A "beat" is one exchange of the story. Run

*(Opening a BRAND-NEW campaign? Don't drop the player mid-scene. Open with the guaranteed 4-beat **cold open** first — `get_prelude` (Arrival → Meeting → Inciting Incident → Threshold), woven in your own prose — then enter this cycle. See `reference/quest-generation.md`.)*

1. **Re-ground** — `clawdnd-engine` `get_state` (especially after a gap/compaction). When the moment touches the past ("haven't we met this NPC?", "what did we decide about the cult?"), `recall`/`recall_npc`/`recall_decisions` first so the world stays consistent — recall is fuzzy and works *within* a session too, not just across sessions. The adventure's **companion and named NPCs/villains already exist** from `start_adventure`; `get_state` to find their ids and use them — never `create_character` a second copy of a companion (the engine will reject a duplicate). **Re-check the player's voice preference** here too: `/voice-toggle` is a conversation-only setting, so after a compaction don't assume it — if they'd turned voice off, stay text-only rather than resuming `speak`. (Follow-up: persist as a `Campaign.voice_enabled` field.) **Then consult `get_campaign_director`** — it names what the campaign OWES right now: a *hook the party committed to but you never tracked* → `add_quest` it; an NPC *introduced but still silent* → give them a line; a *due consequence* → land it; a *stalled quest* → push it. Honor the **top** debt by weaving it into THIS beat — don't recite the list. (It's advisory: the engine surfaces the debt, you decide how to pay it.) **In the same breath, check the two living-story surfaces:** `present_events` for any stumble-into decisional whose moment has arrived (a devil's bargain offered, a faction's offer come due) — stage it IN-CHARACTER this beat and resolve the pick with `resolve_event`; and `check_companion_arc` for a bond that just turned or a `betrayal_warning` telling you a companion's loyalty is fracturing — **foreshadow** the break, don't spring it. These three (Director, events, arcs) are how the world keeps *moving* under the scene — see `reference/living-arcs.md`.
1. **Re-ground** — `clawdnd-engine` `get_state` (especially after a gap/compaction). When the moment touches the past ("haven't we met this NPC?", "what did we decide about the cult?"), `recall`/`recall_npc`/`recall_decisions` first so the world stays consistent — recall is fuzzy and works *within* a session too, not just across sessions. The adventure's **companion and named NPCs/villains already exist** from `start_adventure`; `get_state` to find their ids and use them — never `create_character` a second copy of a companion (the engine will reject a duplicate). **Re-check the player's voice preference** here too: `/voice-toggle` is a conversation-only setting, so after a compaction don't assume it — if they'd turned voice off, stay text-only rather than resuming `speak`. (Follow-up: persist as a `Campaign.voice_enabled` field.) **Then consult `get_campaign_director`** — it names what the campaign OWES right now: a *hook the party committed to but you never tracked* → `add_quest` it; an NPC *introduced but still silent* → give them a line; a *due consequence* → land it; a *stalled quest* → push it. Honor the **top** debt by weaving it into THIS beat — don't recite the list. (It's advisory: the engine surfaces the debt, you decide how to pay it.) **The advisory is for YOU, never the player: never echo a Director nudge, a "GM Advisory" line, or an engine tool name (`remember`, `add_quest`, …) into the player-facing narration — that's a system-prompt-style leak. Pay the debt as fiction (give the silent NPC a line; let a consequence land as an event), then `remember`/`add_quest` it silently behind the scene.** **In the same breath, check the two living-story surfaces:** `present_events` for any stumble-into decisional whose moment has arrived (a devil's bargain offered, a faction's offer come due) — stage it IN-CHARACTER this beat and resolve the pick with `resolve_event`; and `check_companion_arc` for a bond that just turned or a `betrayal_warning` telling you a companion's loyalty is fracturing — **foreshadow** the break, don't spring it. These three (Director, events, arcs) are how the world keeps *moving* under the scene — see `reference/living-arcs.md`.
2. **Narrate** — describe the scene vividly and voice it; voice each NPC in their own `voice_id`. **Honor `pacing_mode` from `get_state`:** `downtime` = let scenes breathe (social, shopping, recovery, character beats); `adventure` (default) = tension and momentum. On **first arrival at a location**, call `look_around` *before* narrating, and `get_scene` to pull any **authored beat** (read_aloud, dm_notes, check DCs) — run the author's intent in your own words: play the staged villain beat, the heartbreak line, the felt threat they wrote, rather than improvising past them. (When you're *generating* the world yourself, there's no authored scene — you are the author; see `reference/living-world.md`.) **Light up the visual layer in EVERY mode** (authored or generated): on first arrival at a location, `generate_image(kind="scene", scope=<location_id>, prompt="<what the party sees>")`; on a character's first on-screen appearance, `generate_image(kind="portrait", scope="portrait-"+<character_id>, prompt="<their appearance>")`. The dashboard fetches `/image?scope=<location_id>` (scene) and `/image?scope=portrait-<character_id>` (faces). Degrades gracefully — the default `null` provider is a no-op that shows a placeholder and never blocks the beat.
3. **Companion reacts + advises — EVERY beat (the default, not a garnish).** Call `clawdnd-engine` `companion_advise(companion_id, situation=<the moment>)`; it returns the companion's voice + personality + memory callbacks + a prompt. **Voice the companion's reaction and honest opinion** in their own voice — banter, worry, push-back, a plan. A companion that goes quiet is the #1 way this stops feeling like an adventure. They have goals and a past; let them show.
4. **Deliberate together** — when the party faces a real choice, let it be a *conversation*: the player weighs the companion's take, they may argue, then the player decides. Record the outcome with `record_decision(summary, options, chosen, rationale, actor_ids)` so it can be called back to later ("last time we trusted Grett…"). Big choices echo: schedule fallout with `add_consequence`.
Expand Down
6 changes: 4 additions & 2 deletions viewer/openworlds/chrome.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -289,10 +289,12 @@ function IconPlate({ size = 56, label, framed = true, glyph, tone, children, onC
);
}

function BrassButton({ children, onClick, tone, size, disabled, style, type = "button" }) {
function BrassButton({ children, onClick, tone, size, disabled, style, type = "button", title }) {
const cls = ["btn", tone, size, disabled ? "disabled" : ""].filter(Boolean).join(" ");
// `title` is optional — forwarded so callers can attach a hover/affordance tooltip
// (e.g. the #337 action-bar hints) without giving every BrassButton one.
return (
<button className={cls} onClick={onClick} disabled={disabled} type={type} style={style}>
<button className={cls} onClick={onClick} disabled={disabled} type={type} style={style} title={title || undefined}>
{children}
</button>
);
Expand Down
Loading
Loading