Training Bundle — Widget Vocabulary Extensions¶
Bundle id: training
Vocab spec base: STORYTANGL_WIDGET_VOCAB.md v1.5
Status: draft v0.2 · aligned to v1.5 core vocabulary
Genre: scheduled skill progression / succession-game (Long Live the Queen-inspired)
Grounded in: worlds/coronate_the_regent (StoryTangl demo world)
Audience: authors writing training-style bundles; port implementers covering the training profile suite
This document is a Tier P3 genre extension (per main spec §8). It introduces no new top-level vocabulary; it codifies conventions on top of v1.5 for scheduled-training gameplay.
The training-specific enrichments are advisory labels and previews over core choices, projected rows, pieces, and rolls. A client that ignores the skill-category styling or stat-check preview still conforms when it renders the same choices and backend-authored outcomes.
0 · Genre summary¶
Training bundles model scheduled accumulation with gated payoffs: the player makes repeated choices about how to spend a constrained resource (study weeks, training periods, lessons), accumulates skills / inventory / flags, and meets pre-scheduled events whose outcomes depend on accumulated state.
The reference instance is worlds/coronate_the_regent: four weeks,
two skills (combat, charm), two scheduled events (prince audience,
dragon fight), one inventory unlock (dragonslayer sword), plus a
mood-as-tag-modifier mechanic. This document describes what the demo
currently exercises and the conventions it establishes. Forward
direction (full LLtQ-style skill trees, calendar widgets, per-skill
XP curves) is acknowledged as Tier P3 aspirational — not in scope
for the current contract.
Three orthogonal patterns the training genre exercises:
Pattern |
Main spec mechanism |
Genre layer adds |
|---|---|---|
Repeated constrained choices (study weeks) |
|
conventional |
Scheduled event with skill check |
|
|
Conditional outcome routing |
Backend evaluates |
|
1 · Domain vocabulary mapped to v1.5¶
Training concept |
v1.5 surface |
|---|---|
Player (regent / heir / pupil) |
The cursor; not a piece. Player stats are |
Stat (intrinsic: body, mind, spirit) |
|
Skill (governed: combat, magic, charm) |
|
Mood |
|
Wallet (coin, stamina) |
|
Inventory (dragonslayer sword, flags) |
Either inventory |
Week / schedule |
|
Study choice |
|
Scheduled event |
A non-choice envelope sequence; the player’s choice happened on the prior turn (e.g., “Receive the visiting prince” vs. “Train at arms”), and this envelope resolves the consequence |
Skill check |
|
Inventory unlock |
|
Outcome branch |
The backend evaluates conditions; the next envelope reflects whichever branch fired |
Ending |
Sequential |
2 · Mood as growth modulator¶
worlds/coronate_the_regent introduces a clever mechanic:
mood doesn’t gate skills; it modulates training gains per tag.
# excerpt from coronate_the_regent.domain
MOOD_EFFECTS = {
"martial": [
SituationalEffect(applies_to_tags={"#martial"}, growth_modifier=1.0),
SituationalEffect(applies_to_tags={"#courtly"}, growth_modifier=-0.5),
],
"studious": [
SituationalEffect(applies_to_tags={"#courtly"}, growth_modifier=1.0),
],
}
The rendering convention: mood projects as both a state indicator AND as cost-preview deltas on each affected study choice.
// projected mood scalar
{
section_id: "mood",
title: "Mood",
kind: "mood",
value: { value_type: "scalar", value: "martial" },
hints: { icon: "sword", style_tags: ["mood-indicator"] }
}
// study choice surfaces the mood-adjusted preview
{
uid: "f-choice-combat",
fragment_type: "choice",
edge_id: "e-train-combat",
text: "Train at arms",
accepts: { kind: "pick" },
ui_hints: {
hotkey: "1",
cost_previews: [
{ ledger_key: "skills.combat", delta: 2, unit: "xp" }
],
stat_check: null // no check, just a gain preview
}
}
{
uid: "f-choice-charm",
fragment_type: "choice",
edge_id: "e-train-charm",
text: "Study courtly graces",
accepts: { kind: "pick" },
ui_hints: {
hotkey: "2",
cost_previews: [
{ ledger_key: "skills.charm", delta: 1, unit: "xp" } // halved by martial mood
]
}
}
cost_previews.delta carries positive values for gains. Per the
v1.5 spec, CostPreview.delta is signed; the name “cost” is
historical. A bundle showing a gain preview MAY render the user-
facing text as “+2 combat” while the contract field stays
cost_previews. Do not invent gain_previews.
The mood-modulated value is computed backend-side. The client
renders what the backend tells it. If the player has martial mood,
the preview for charm-training shows the modulated gain (+1,
half of the base +2), not the unmodulated number. This is §0.3
backend authority: the math is the backend’s, the rendering is the
client’s, and the contract surface is the rendered preview.
3 · Scheduled events and skill checks¶
A scheduled event is just the next envelope in the sequence, whose
content reflects whichever branch the prior choice took. The check
is a RollFragment(kind="dice") with against referencing a stat.
// envelope after player chose "Receive the visiting prince"
fragments: [
{ uid: "f-prose-prince", fragment_type: "content",
content: "The prince watches you across the receiving hall. The
guards lower their eyes. You step forward and bow." },
// The skill check fires immediately
{ uid: "f-roll-audience", fragment_type: "roll",
label: "Prince's audience",
kind: "dice",
inputs: {
dice: "1d20",
rolled: [14],
modifier: 0,
total: 14,
target: 10, // computed backend-side from prince difficulty + charm
probability_text: "12/20"
},
outcome: "success",
narrative: "The prince leaves visibly charmed by your bearing.",
against: { piece_id: "player", property: "charm" },
ritual_hints: {
skip_label: "Skip the roll",
auto_skip_after_seen: false,
duration_ms: 1400
} },
// Result: an inventory flag is set
{ uid: "f-inv-update", fragment_type: "control",
ref_type: "section", ref_id: "inventory",
payload: {
value: {
value_type: "badges",
items: ["impressed_prince"] // added to existing badges
}
} },
// Next-turn choice list
{ uid: "f-choice-next", fragment_type: "choice",
edge_id: "e-week3",
text: "Begin Week 3.", accepts: { kind: "pick" },
ui_hints: { hotkey: "1", emphasis: "primary" } }
]
The fork lives on the backend. If the audience had failed, the
next envelope would have skipped the impressed_prince flag and
proceeded directly to week 3. Per spec §7.3, the player has nothing
to decide about the roll itself; the ritual is skippable per §5.2
Time Parity.
ui_hints.stat_check preview (carwars convention reused)¶
When a prior choice triggers the check, surfacing the difficulty helps the player understand the wager. Reused directly from carwars EXTENSIONS:
{
uid: "f-choice-audience",
fragment_type: "choice",
edge_id: "e-receive-prince",
text: "Receive the visiting prince",
accepts: { kind: "pick" },
ui_hints: {
hotkey: "1",
stat_check: {
label: "Audience",
dice: "1d20",
target: 10,
against: { piece_id: "player", property: "charm" },
modifier: 0,
success_text: "12/20 chance"
}
}
}
CLI rendering:
1) Receive the visiting prince [Audience: 1d20 vs charm, 12/20]
4 · Inventory unlocks¶
The dragonslayer sword pattern: a purchase, an inventory mutation, a downstream effect.
// At the merchant: the offer
{
uid: "f-offer-sword",
fragment_type: "piece",
piece_id: "dragonslayer-sword",
kind: "weapon",
realized: false,
properties: { name: "Dragonslayer sword", description: "Said to slay dragons." },
cost: [{ ledger_key: "coin", delta: -3 }],
available: true,
hints: { label_text: "Dragonslayer sword" }
}
// Buy choice
{
uid: "f-choice-buy-sword",
fragment_type: "choice",
edge_id: "e-buy-sword",
text: "Buy the dragonslayer sword (3 coin)",
accepts: {
kind: "pieces", min: 1, max: 1,
constraints: { target_zone_ref: "z-merchant-catalog" }
},
blockers: [
{
code: "insufficient_coin",
message: "You only have 2 coin.",
refs: ["coin"]
}
],
available: false // when player has < 3 coin
}
After commit, the next envelope:
[
{ uid: "pc-sword-realized", fragment_type: "piece",
piece_id: "dragonslayer-sword", kind: "weapon",
realized: true,
zone_ref: "z-inventory",
properties: { name: "Dragonslayer sword" },
hints: { label_text: "Dragonslayer sword" } },
{ uid: "f-content-buy", fragment_type: "content",
content: "The merchant pockets your coin and hands you the sword.
It is heavier than you expected." },
// wallet projection updates by way of next envelope's ProjectedState
// (no control fragment needed — ProjectedState is re-projected per turn)
]
Effect on downstream rolls. Per coronate_the_regent, holding
the sword causes the dragon-fight check to fire with forced_outcome: MAJOR_SUCCESS. The client never sees this rule directly — the dragon
check’s RollFragment.outcome simply reads "success", with no
explanation of why. Per §0.3 / §0.6 backend authority and narrative
authoring stance, this is exactly right: the mechanism is invisible
to the client, the outcome is canonical.
5 · Weekly study commit (richer compose form)¶
coronate_the_regent uses accepts.kind="pick" for its two-option
weeks. A richer training bundle (full LLtQ-style “morning study +
afternoon study”) uses compose:
{
uid: "f-choice-week",
fragment_type: "choice",
edge_id: "e-week5-schedule",
text: "Plan Week 5",
accepts: {
kind: "compose",
parts: [
{
role: "morning",
accepts: {
kind: "pieces",
min: 1, max: 1,
constraints: { target_zone_ref: "z-available-courses" }
}
},
{
role: "afternoon",
accepts: {
kind: "pieces",
min: 1, max: 1,
constraints: { target_zone_ref: "z-available-courses" }
}
}
]
}
}
The commit payload:
{
"edge_id": "e-week5-schedule",
"payload": {
"parts": {
"morning": { "piece_ids": ["course-rhetoric"] },
"afternoon": { "piece_ids": ["course-economics"] }
}
}
}
This is forward direction — coronate_the_regent doesn’t yet use it, but the contract is ready when a bundle does.
6 · Worked example — Week 2 of coronate_the_regent¶
Envelope at start of week 2¶
fragments: [
{ uid: "f-prose-w2", fragment_type: "content",
content: "Week 2. A royal visitor is expected." },
// Three choices
{ uid: "f-c-prince", fragment_type: "choice",
edge_id: "e-prince", text: "Receive the visiting prince",
accepts: { kind: "pick" },
ui_hints: {
hotkey: "1", emphasis: "primary",
stat_check: { label: "Audience", dice: "1d20", target: 10,
against: { piece_id: "player", property: "charm" },
success_text: "varies with charm" }
} },
{ uid: "f-c-combat", fragment_type: "choice",
edge_id: "e-combat", text: "Train at arms instead",
accepts: { kind: "pick" },
ui_hints: { hotkey: "2",
cost_previews: [{ ledger_key: "skills.combat", delta: 2, unit: "xp" }] } },
{ uid: "f-c-charm", fragment_type: "choice",
edge_id: "e-charm", text: "Study courtly graces instead",
accepts: { kind: "pick" },
ui_hints: { hotkey: "3",
cost_previews: [{ ledger_key: "skills.charm", delta: 2, unit: "xp" }] } }
],
projected_state: {
sections: [
{ section_id: "schedule", title: "Schedule", kind: "calendar",
value: { value_type: "scalar", value: "Week 2 of 4" },
hints: { icon: "calendar" } },
{ section_id: "mood", title: "Mood", kind: "mood",
value: { value_type: "scalar", value: "—" } },
{ section_id: "stats", title: "Stats", kind: "stats",
value: {
value_type: "kv_list",
items: [
{ key: "body", value: 10, max: 20, hint: "bar" },
{ key: "mind", value: 10, max: 20, hint: "bar" },
{ key: "spirit", value: 10, max: 20, hint: "bar" },
{ key: "combat", value: 12, max: 20, hint: "bar", emphasis: "ok" },
{ key: "magic", value: 10, max: 20, hint: "bar" },
{ key: "charm", value: 10, max: 20, hint: "bar" }
]
} },
{ section_id: "wallet", title: "Wallet", kind: "wallet",
value: {
value_type: "kv_list",
items: [
{ key: "coin", value: 3, unit: "c" },
{ key: "stamina", value: 5, max: 5, hint: "bar" }
]
} },
{ section_id: "inventory", title: "Inventory", kind: "inventory",
value: { value_type: "badges", items: [] } }
]
}
CLI rendering¶
Week 2. A royal visitor is expected.
[Schedule] Week 2 of 4
[Mood] —
[Stats]
body 10/20 (bar)
mind 10/20 (bar)
spirit 10/20 (bar)
combat 12/20 (bar) (ok)
magic 10/20 (bar)
charm 10/20 (bar)
[Wallet]
coin 3 c
stamina 5/5
[Inventory] (none)
1) Receive the visiting prince [Audience: 1d20 vs charm, varies with charm]
2) Train at arms instead (+2 combat xp)
3) Study courtly graces instead (+2 charm xp)
> 1
(commits e-prince)
The prince watches you across the receiving hall. The guards lower
their eyes. You step forward and bow.
[Audience: 1d20 vs charm]
rolled: 14 +0 = 14 vs target 10
outcome: success
The prince leaves visibly charmed by your bearing.
1) Begin Week 3.
7 · Forward direction (Tier P3 aspirational)¶
The current coronate_the_regent demo is intentionally minimal.
Conventions a richer training bundle would land:
Forward feature |
v1.5 surface |
|---|---|
Skill tree visualization |
|
Skill-prereq gating |
|
Per-skill XP-to-next |
|
Calendar visualization |
|
Inventory zone (richer than badges) |
|
Romance / relationship tracks |
|
None of these require new vocabulary. They’re convention-level extensions that ship when a bundle author needs them.
8 · Port parity addendum¶
Widget |
Web (Vue) |
CLI |
tkinter |
Hypothetical Ren’Py |
|---|---|---|---|---|
Mood indicator |
icon + label chip |
|
|
side image |
Stat row ( |
progress bar with value/max label |
|
|
stat screen widget |
Stat row ( |
|
|
|
screen action |
Schedule scalar |
tile or banner |
|
large |
top banner |
Study choice (with |
button + cost chip |
|
|
|
Study choice (with |
button + difficulty badge |
|
|
|
Skill check roll |
one-shot animation |
|
inline |
screen with portrait |
Inventory unlock |
toast / item card |
|
|
|
Ending |
scrolled narrative |
wrapped prose |
full-screen |
|
Appendix — Prior art¶
Long Live the Queen (Hanako Games, 2012) is the canonical reference for scheduled-training succession games. Its design DNA traces back to Princess Maker (1991) and forward through Magical Diary, Cute Knight, and countless visual-novel hybrids. The genre’s defining tension is commitment under uncertainty: the player must commit study time before knowing exactly which check it will pay off, and the joy is watching accumulated decisions intersect with pre-scheduled events.
worlds/coronate_the_regent is a deliberate micro-instance of
this genre — four weeks instead of forty, two skills instead of
forty, two events instead of dozens. The compactness is the point:
the demo proves the contract handles the genre’s full shape
(scheduling, gating, branching, payoff) without needing the genre’s
full content.
The training genre overlaps with farming-life simulators (Stardew Valley’s weekly festivals), management games (Two Point Hospital’s scheduled events), and tabletop XP-based RPGs (D&D session-by-session progression). The vocabulary lifts cleanly.
The training engine architecture is documented in
tangl.mechanics.progression (stat systems, situational effects,
stat challenges). The engine-side StatChallenge, Outcome,
SituationalEffect, LinearGrowthHandler are authoring concerns;
this document is rendering contract.
End of training EXTENSIONS v0.2.