Files
storybook/docs/book/reference/10-characters.html

612 lines
26 KiB
HTML
Raw Normal View History

<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Characters - Storybook Language Guide</title>
<!-- Custom HTML head -->
<meta name="description" content="Comprehensive documentation for the Storybook narrative simulation language">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="../favicon.svg">
<link rel="shortcut icon" href="../favicon.png">
<link rel="stylesheet" href="../css/variables.css">
<link rel="stylesheet" href="../css/general.css">
<link rel="stylesheet" href="../css/chrome.css">
<link rel="stylesheet" href="../css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="../fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="../highlight.css">
<link rel="stylesheet" href="../tomorrow-night.css">
<link rel="stylesheet" href="../ayu-highlight.css">
<!-- Custom theme stylesheets -->
<!-- Provide site root to javascript -->
<script>
var path_to_root = "../";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
</script>
<!-- Start loading toc.js asap -->
<script src="../toc.js"></script>
</head>
<body>
<div id="body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
const html = document.documentElement;
html.classList.remove('light')
html.classList.add(theme);
html.classList.add("js");
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
var sidebar = null;
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<!-- populated by js -->
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
<noscript>
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
</noscript>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Storybook Language Guide</h1>
<div class="right-buttons">
<a href="../print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
<a href="https://github.com/r3t-studios/storybook" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="characters"><a class="header" href="#characters">Characters</a></h1>
<p>Characters are the primary entities in Storybook—the people, creatures, and beings that inhabit your world. Each character has a set of attributes that define who they are, what they can do, and how they relate to the world around them.</p>
<h2 id="syntax"><a class="header" href="#syntax">Syntax</a></h2>
<pre><code class="language-bnf">&lt;character-decl&gt; ::= "character" &lt;identifier&gt; &lt;species-clause&gt;? &lt;template-clause&gt;? &lt;body&gt;
&lt;species-clause&gt; ::= ":" &lt;qualified-path&gt;
&lt;template-clause&gt; ::= "from" &lt;qualified-path&gt; ("," &lt;qualified-path&gt;)*
&lt;body&gt; ::= "{" &lt;body-item&gt;* "}"
&lt;body-item&gt; ::= &lt;field&gt;
| &lt;uses-behaviors&gt;
| &lt;uses-schedule&gt;
&lt;uses-behaviors&gt; ::= "uses" "behaviors" ":" &lt;behavior-link-list&gt;
&lt;uses-schedule&gt; ::= "uses" ("schedule" | "schedules") ":" (&lt;qualified-path&gt; | &lt;schedule-list&gt;)
&lt;behavior-link-list&gt; ::= "[" &lt;behavior-link&gt; ("," &lt;behavior-link&gt;)* "]"
&lt;behavior-link&gt; ::= "{" &lt;behavior-link-field&gt;* "}"
&lt;behavior-link-field&gt; ::= "tree" ":" &lt;qualified-path&gt;
| "when" ":" &lt;expression&gt;
| "priority" ":" ("low" | "normal" | "high" | "critical")
&lt;schedule-list&gt; ::= "[" &lt;qualified-path&gt; ("," &lt;qualified-path&gt;)* "]"
&lt;field&gt; ::= &lt;identifier&gt; ":" &lt;value&gt;
&lt;value&gt; ::= &lt;literal&gt;
| &lt;qualified-path&gt;
| &lt;list&gt;
| &lt;object&gt;
| &lt;prose-block&gt;
| &lt;override&gt;
&lt;prose-block&gt; ::= "---" &lt;identifier&gt; &lt;content&gt; "---"
</code></pre>
<h2 id="components"><a class="header" href="#components">Components</a></h2>
<h3 id="name"><a class="header" href="#name">Name</a></h3>
<p>The characters identifier. Must be unique within its scope and follow standard identifier rules (alphanumeric + underscore, cannot start with digit).</p>
<h3 id="species-optional"><a class="header" href="#species-optional">Species (Optional)</a></h3>
<p>The species clause (<code>: SpeciesName</code>) defines what the character fundamentally <em>is</em>. This is distinct from templates, which define what attributes they <em>have</em>.</p>
<ul>
<li><strong>Purpose</strong>: Ontological typing—what kind of being this is</li>
<li><strong>Validation</strong>: Must reference a defined <code>species</code> declaration</li>
<li><strong>Single inheritance</strong>: A character can only have one species</li>
<li><strong>Default behavior</strong>: Species fields are inherited automatically</li>
</ul>
<p>Example:</p>
<pre><code class="language-storybook">character Martha: Human {
age: 34
}
</code></pre>
<h3 id="template-inheritance-optional"><a class="header" href="#template-inheritance-optional">Template Inheritance (Optional)</a></h3>
<p>The template clause (<code>from Template1, Template2</code>) specifies templates from which the character inherits fields. Templates provide reusable attribute sets.</p>
<ul>
<li><strong>Purpose</strong>: Compositional inheritance—mix-and-match capabilities and traits</li>
<li><strong>Multiple inheritance</strong>: Characters can inherit from multiple templates</li>
<li><strong>Merge semantics</strong>: Fields from later templates override earlier ones</li>
<li><strong>Override allowed</strong>: Character fields override all inherited fields</li>
</ul>
<p>Example:</p>
<pre><code class="language-storybook">character Martha: Human from Baker, BusinessOwner {
specialty: "sourdough"
}
</code></pre>
<h3 id="fields"><a class="header" href="#fields">Fields</a></h3>
<p>Fields define the characters attributes using the standard field syntax. All <a href="./18-value-types.html">value types</a> are supported.</p>
<p><strong>Common field categories:</strong></p>
<ul>
<li><strong>Physical traits</strong>: <code>height</code>, <code>weight</code>, <code>age</code>, <code>eye_color</code></li>
<li><strong>Personality</strong>: <code>confidence</code>, <code>patience</code>, <code>dedication</code></li>
<li><strong>Professional</strong>: <code>skill_level</code>, <code>specialty</code>, <code>recipes_mastered</code></li>
<li><strong>State tracking</strong>: <code>energy</code>, <code>mood</code>, <code>orders_today</code></li>
<li><strong>Capabilities</strong>: <code>can_teach</code>, <code>can_work_independently</code></li>
</ul>
<h3 id="prose-blocks"><a class="header" href="#prose-blocks">Prose Blocks</a></h3>
<p>Characters can contain multiple prose blocks for narrative content. Common tags:</p>
<ul>
<li><code>---backstory</code>: Character history and origin</li>
<li><code>---appearance</code>: Physical description</li>
<li><code>---personality</code>: Behavioral traits and quirks</li>
<li><code>---motivation</code>: Goals and desires</li>
<li><code>---secrets</code>: Hidden information</li>
</ul>
<p>Prose blocks are narrative-only and do not affect simulation logic.</p>
<h3 id="behavior-integration"><a class="header" href="#behavior-integration">Behavior Integration</a></h3>
<p>Characters can link to <a href="./14-behavior-trees.html">behavior trees</a> using the <code>uses behaviors</code> clause.</p>
<pre><code class="language-storybook">character Guard {
uses behaviors: [
{
tree: combat::patrol_route
priority: normal
},
{
tree: combat::engage_intruder
when: threat_detected
priority: high
}
]
}
</code></pre>
<p>Each behavior link includes:</p>
<ul>
<li><strong><code>tree</code></strong>: Qualified path to the behavior tree (required)</li>
<li><strong><code>when</code></strong>: Condition expression for activation (optional)</li>
<li><strong><code>priority</code></strong>: Execution priority (optional, default: <code>normal</code>)</li>
</ul>
<p>See <a href="./11-behavior-trees.html">Behavior Trees</a> for details on behavior tree syntax and semantics.</p>
<h3 id="schedule-integration"><a class="header" href="#schedule-integration">Schedule Integration</a></h3>
<p>Characters can follow <a href="./16-schedules.html">schedules</a> using the <code>uses schedule</code> or <code>uses schedules</code> clause.</p>
<pre><code class="language-storybook">character Baker {
uses schedule: BakerySchedule
}
character Innkeeper {
uses schedules: [WeekdaySchedule, WeekendSchedule]
}
</code></pre>
<ul>
<li>Single schedule: <code>uses schedule: ScheduleName</code></li>
<li>Multiple schedules: <code>uses schedules: [Schedule1, Schedule2]</code></li>
</ul>
<p>The runtime selects the appropriate schedule based on temporal constraints. See <a href="./14-schedules.html">Schedules</a> for composition and selection semantics.</p>
<h2 id="species-vs-templates"><a class="header" href="#species-vs-templates">Species vs. Templates</a></h2>
<p>The distinction between species (<code>:</code>) and templates (<code>from</code>) reflects ontological vs. compositional typing:</p>
<div class="table-wrapper"><table><thead><tr><th>Feature</th><th>Species (<code>:</code>)</th><th>Templates (<code>from</code>)</th></tr></thead><tbody>
<tr><td><strong>Semantics</strong></td><td>What the character <em>is</em></td><td>What the character <em>has</em></td></tr>
<tr><td><strong>Cardinality</strong></td><td>Exactly one</td><td>Zero or more</td></tr>
<tr><td><strong>Example</strong></td><td><code>: Human</code>, <code>: Dragon</code></td><td><code>from Warrior, Mage</code></td></tr>
<tr><td><strong>Purpose</strong></td><td>Fundamental nature</td><td>Reusable trait sets</td></tr>
<tr><td><strong>Override</strong></td><td>Can override species fields</td><td>Can override template fields</td></tr>
</tbody></table>
</div>
<p>Example showing both:</p>
<pre><code class="language-storybook">species Dragon {
max_lifespan: 1000
can_fly: true
}
template Hoarder {
treasure_value: 0..1000000
greed_level: 0.0..1.0
}
template Ancient {
age: 500..1000
wisdom: 0.8..1.0
}
character Smaug: Dragon from Hoarder, Ancient {
age: 850
treasure_value: 500000
greed_level: 0.95
}
</code></pre>
<h2 id="field-resolution-order"><a class="header" href="#field-resolution-order">Field Resolution Order</a></h2>
<p>When a character inherits from species and templates, fields are resolved in this order (later overrides earlier):</p>
<ol>
<li><strong>Species fields</strong> (base ontology)</li>
<li><strong>Template fields</strong> (left to right in <code>from</code> clause)</li>
<li><strong>Character fields</strong> (highest priority)</li>
</ol>
<p>Example:</p>
<pre><code class="language-storybook">species Human {
lifespan: 80
speed: 1.0
}
template Warrior {
speed: 1.5
strength: 10
}
template Berserker {
speed: 2.0
strength: 15
}
character Conan: Human from Warrior, Berserker {
strength: 20
}
// Resolved fields:
// lifespan: 80 (from Human)
// speed: 2.0 (Berserker overrides Warrior overrides Human)
// strength: 20 (character overrides Berserker)
</code></pre>
<h2 id="validation-rules"><a class="header" href="#validation-rules">Validation Rules</a></h2>
<p>The Storybook compiler enforces these validation rules:</p>
<ol>
<li><strong>Unique names</strong>: Character names must be unique within their module</li>
<li><strong>Species exists</strong>: If specified, the species must reference a defined <code>species</code> declaration</li>
<li><strong>Templates exist</strong>: All templates in the <code>from</code> clause must reference defined <code>template</code> declarations</li>
<li><strong>No circular inheritance</strong>: Templates cannot form circular dependency chains</li>
<li><strong>Field type consistency</strong>: Field values must match expected types from species/templates</li>
<li><strong>Reserved fields</strong>: Cannot use reserved keywords as field names</li>
<li><strong>Behavior trees exist</strong>: All behavior tree references must resolve to defined <code>behavior</code> declarations</li>
<li><strong>Schedules exist</strong>: All schedule references must resolve to defined <code>schedule</code> declarations</li>
<li><strong>Prose tag uniqueness</strong>: Each prose tag can appear at most once per character</li>
</ol>
<h2 id="examples"><a class="header" href="#examples">Examples</a></h2>
<h3 id="basic-character"><a class="header" href="#basic-character">Basic Character</a></h3>
<pre><code class="language-storybook">character SimpleMerchant {
name: "Gregor"
occupation: "Fish Merchant"
wealth: 50
---personality
A straightforward fish seller at the market. Honest, hardworking,
and always smells faintly of mackerel.
---
}
</code></pre>
<h3 id="character-with-species"><a class="header" href="#character-with-species">Character with Species</a></h3>
<pre><code class="language-storybook">character Martha: Human {
age: 34
skill_level: 0.95
specialty: "sourdough"
---backstory
Martha learned to bake from her grandmother, starting at age
twelve. She now runs the most popular bakery in town.
---
}
</code></pre>
<h3 id="character-with-template-inheritance"><a class="header" href="#character-with-template-inheritance">Character with Template Inheritance</a></h3>
<pre><code class="language-storybook">character Jane: Human from Baker, PastrySpecialist {
age: 36
specialty: "pastries"
recipes_mastered: 120
years_experience: 18
can_teach: true
---appearance
A focused woman with flour-dusted apron and steady hands.
Known for her intricate pastry decorations and precise
temperature control.
---
}
</code></pre>
<h3 id="character-with-all-features"><a class="header" href="#character-with-all-features">Character with All Features</a></h3>
<pre><code class="language-storybook">character CityGuard: Human from CombatTraining, LawEnforcement {
age: 30
rank: "Sergeant"
// Physical traits
height: 175
strength: 12
// Equipment
has_weapon: true
armor_level: 2
// Behavior integration
uses behaviors: [
{
tree: guards::patrol_route
priority: normal
},
{
tree: guards::engage_hostile
when: threat_detected
priority: high
},
{
tree: guards::sound_alarm
when: emergency
priority: critical
}
]
// Schedule integration
uses schedules: [guards::day_shift, guards::night_shift]
---backstory
A veteran of the city watch, now responsible for training new recruits
while maintaining order in the merchant district.
---
---personality
Gruff exterior with a hidden soft spot for street children. Follows
the rules but knows when to look the other way.
---
}
</code></pre>
<h3 id="character-with-overrides"><a class="header" href="#character-with-overrides">Character with Overrides</a></h3>
<pre><code class="language-storybook">template WeaponUser {
damage: 5..15
accuracy: 0.7
}
character MasterSwordsman: Human from WeaponUser {
// Override template range with specific value
damage: 15
accuracy: 0.95
// Add character-specific fields
signature_move: "Whirlwind Strike"
}
</code></pre>
<h2 id="use-cases"><a class="header" href="#use-cases">Use Cases</a></h2>
<h3 id="protagonist-definition"><a class="header" href="#protagonist-definition">Protagonist Definition</a></h3>
<p>Define rich, dynamic protagonists with complex attributes:</p>
<pre><code class="language-storybook">character Elena: Human from Scholar, Diplomat {
age: 28
intelligence: 18
charisma: 16
languages_known: ["Common", "Elvish", "Draconic"]
books_read: 347
current_quest: "Broker peace between warring kingdoms"
---backstory
Raised in the Grand Library, Elena discovered ancient texts that
hinted at a forgotten alliance between humans and dragons. She now
seeks to revive that alliance to end the current war.
---
}
</code></pre>
<h3 id="npc-templates"><a class="header" href="#npc-templates">NPC Templates</a></h3>
<p>Create diverse NPCs from templates:</p>
<pre><code class="language-storybook">template Villager {
occupation: "Farmer"
wealth: 10..50
disposition: 0.0..1.0 // 0=hostile, 1=friendly
}
character Oswald: Human from Villager {
occupation: "Blacksmith"
wealth: 45
disposition: 0.8
}
character Mildred: Human from Villager {
occupation: "Baker"
wealth: 35
disposition: 0.95
}
</code></pre>
<h3 id="ensemble-casts"><a class="header" href="#ensemble-casts">Ensemble Casts</a></h3>
<p>Define multiple related characters:</p>
<pre><code class="language-storybook">template BakeryStaff {
punctuality: 0.5..1.0
teamwork: 0.5..1.0
}
template Apprentice {
skill_level: 0.0..0.5
dedication: 0.5..1.0
}
character Elena: Human from BakeryStaff, Apprentice {
age: 16
natural_talent: 0.8
dedication: 0.9
---backstory
Elena comes from a family of farmers who could never afford to
buy bread from the bakery. When Martha offered her an apprenticeship,
she jumped at the chance to learn a trade.
---
}
</code></pre>
<h2 id="cross-references"><a class="header" href="#cross-references">Cross-References</a></h2>
<ul>
<li><a href="./16-other-declarations.html#species">Species</a> - Species declarations</li>
<li><a href="./16-other-declarations.html#templates">Templates</a> - Template definitions and strict mode</li>
<li><a href="./18-value-types.html">Value Types</a> - All supported value types</li>
<li><a href="./11-behavior-trees.html">Behavior Trees</a> - Character behavior integration</li>
<li><a href="./14-schedules.html">Schedules</a> - Character schedule integration</li>
<li><a href="./15-relationships.html">Relationships</a> - Relationships between characters</li>
<li><a href="./13-life-arcs.html">Life Arcs</a> - Character state machines over time</li>
</ul>
<h2 id="related-concepts"><a class="header" href="#related-concepts">Related Concepts</a></h2>
<ul>
<li><strong>Instantiation</strong>: Characters are concrete instances; they cannot be instantiated further</li>
<li><strong>Composition</strong>: Prefer template composition over deep species hierarchies</li>
<li><strong>Modularity</strong>: Characters can reference behaviors and schedules from other modules</li>
<li><strong>Narrative-driven</strong>: Use prose blocks to embed storytelling directly with data</li>
</ul>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../reference/09-overview.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../reference/11-behavior-trees.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../reference/09-overview.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../reference/11-behavior-trees.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<!-- Livereload script (if served using the cli tool) -->
<script>
const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsAddress = wsProtocol + "//" + location.host + "/" + "__livereload";
const socket = new WebSocket(wsAddress);
socket.onmessage = function (event) {
if (event.data === "reload") {
socket.close();
location.reload();
}
};
window.onbeforeunload = function() {
socket.close();
}
</script>
<script>
window.playground_copyable = true;
</script>
<script src="../elasticlunr.min.js"></script>
<script src="../mark.min.js"></script>
<script src="../searcher.js"></script>
<script src="../clipboard.min.js"></script>
<script src="../highlight.js"></script>
<script src="../book.js"></script>
<!-- Custom JS scripts -->
</div>
</body>
</html>