Files
storybook/docs/book/reference/17-expressions.html
Sienna Meridian Satterwhite 16deb5d237 release: Storybook v0.2.0 - Major syntax and features update
BREAKING CHANGES:
- Relationship syntax now requires blocks for all participants
- Removed self/other perspective blocks from relationships
- Replaced 'guard' keyword with 'if' for behavior tree decorators

Language Features:
- Add tree-sitter grammar with improved if/condition disambiguation
- Add comprehensive tutorial and reference documentation
- Add SBIR v0.2.0 binary format specification
- Add resource linking system for behaviors and schedules
- Add year-long schedule patterns (day, season, recurrence)
- Add behavior tree enhancements (named nodes, decorators)

Documentation:
- Complete tutorial series (9 chapters) with baker family examples
- Complete reference documentation for all language features
- SBIR v0.2.0 specification with binary format details
- Added locations and institutions documentation

Examples:
- Convert all examples to baker family scenario
- Add comprehensive working examples

Tooling:
- Zed extension with LSP integration
- Tree-sitter grammar for syntax highlighting
- Build scripts and development tools

Version Updates:
- Main package: 0.1.0 → 0.2.0
- Tree-sitter grammar: 0.1.0 → 0.2.0
- Zed extension: 0.1.0 → 0.2.0
- Storybook editor: 0.1.0 → 0.2.0
2026-02-13 21:52:03 +00:00

746 lines
33 KiB
HTML

<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Expression Language - 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="expression-language"><a class="header" href="#expression-language">Expression Language</a></h1>
<p>The Storybook expression language enables conditions, queries, and logic throughout the system. Expressions appear in life arc transitions, behavior tree guards, decorator conditions, and relationship queries. This chapter provides a complete reference for expression syntax and semantics.</p>
<h2 id="what-are-expressions"><a class="header" href="#what-are-expressions">What are Expressions?</a></h2>
<p>Expressions are logical statements that evaluate to <code>true</code> or <code>false</code>. They combine:</p>
<ul>
<li><strong>Literals</strong>: Numbers, strings, booleans</li>
<li><strong>Identifiers</strong>: References to fields and entities</li>
<li><strong>Comparisons</strong>: <code>==</code>, <code>!=</code>, <code>&lt;</code>, <code>&lt;=</code>, <code>&gt;</code>, <code>&gt;=</code></li>
<li><strong>Logical operators</strong>: <code>and</code>, <code>or</code>, <code>not</code></li>
<li><strong>Field access</strong>: <code>self.field</code>, <code>other.field</code></li>
<li><strong>Quantifiers</strong>: <code>forall</code>, <code>exists</code> (for collections)</li>
</ul>
<h2 id="syntax"><a class="header" href="#syntax">Syntax</a></h2>
<pre><code class="language-bnf">&lt;expression&gt; ::= &lt;literal&gt;
| &lt;identifier&gt;
| &lt;field-access&gt;
| &lt;comparison&gt;
| &lt;logical&gt;
| &lt;unary&gt;
| &lt;quantifier&gt;
| "(" &lt;expression&gt; ")"
&lt;literal&gt; ::= &lt;int&gt; | &lt;float&gt; | &lt;string&gt; | &lt;bool&gt;
&lt;identifier&gt; ::= &lt;simple-name&gt;
| &lt;qualified-path&gt;
&lt;field-access&gt; ::= &lt;expression&gt; "." &lt;identifier&gt;
| "self" "." &lt;identifier&gt;
| "other" "." &lt;identifier&gt;
&lt;comparison&gt; ::= &lt;expression&gt; &lt;comp-op&gt; &lt;expression&gt;
&lt;comp-op&gt; ::= "==" | "!=" | "&lt;" | "&lt;=" | "&gt;" | "&gt;="
&lt;logical&gt; ::= &lt;expression&gt; "and" &lt;expression&gt;
| &lt;expression&gt; "or" &lt;expression&gt;
&lt;unary&gt; ::= "not" &lt;expression&gt;
| "-" &lt;expression&gt;
&lt;quantifier&gt; ::= ("forall" | "exists") &lt;identifier&gt; "in" &lt;expression&gt; ":" &lt;expression&gt;
</code></pre>
<h2 id="literals"><a class="header" href="#literals">Literals</a></h2>
<h3 id="integer-literals"><a class="header" href="#integer-literals">Integer Literals</a></h3>
<pre><code class="language-storybook">42
-7
0
1000
</code></pre>
<h3 id="float-literals"><a class="header" href="#float-literals">Float Literals</a></h3>
<pre><code class="language-storybook">3.14
-0.5
0.0
100.25
</code></pre>
<h3 id="string-literals"><a class="header" href="#string-literals">String Literals</a></h3>
<pre><code class="language-storybook">"Martha"
"Sourdough takes patience."
"active"
</code></pre>
<p>Strings are enclosed in double quotes. Escape sequences: <code>\n</code>, <code>\t</code>, <code>\\</code>, <code>\"</code>.</p>
<h3 id="boolean-literals"><a class="header" href="#boolean-literals">Boolean Literals</a></h3>
<pre><code class="language-storybook">true
false
</code></pre>
<h2 id="identifiers"><a class="header" href="#identifiers">Identifiers</a></h2>
<p>Identifiers reference fields or entities.</p>
<h3 id="simple-identifiers"><a class="header" href="#simple-identifiers">Simple Identifiers</a></h3>
<pre><code class="language-storybook">health
enemy_count
is_ready
</code></pre>
<h3 id="qualified-paths"><a class="header" href="#qualified-paths">Qualified Paths</a></h3>
<pre><code class="language-storybook">Martha.skill_level
Character.emotional_state
</code></pre>
<h2 id="comparison-operators"><a class="header" href="#comparison-operators">Comparison Operators</a></h2>
<h3 id="equality-"><a class="header" href="#equality-">Equality: <code>==</code></a></h3>
<p>Tests if two values are equal.</p>
<pre><code class="language-storybook">name == "Martha"
count == 5
status == active
</code></pre>
<p><strong>Type compatibility:</strong></p>
<ul>
<li>Both operands must be the same type</li>
<li>Works with: int, float, string, bool, enum values</li>
</ul>
<h3 id="inequality-"><a class="header" href="#inequality-">Inequality: <code>!=</code></a></h3>
<p>Tests if two values are not equal.</p>
<pre><code class="language-storybook">name != "Gregory"
health != 0
ready != true
</code></pre>
<h3 id="less-than"><a class="header" href="#less-than">Less Than: <code>&lt;</code></a></h3>
<p>Tests if left operand is less than right.</p>
<pre><code class="language-storybook">health &lt; 20
age &lt; 18
distance &lt; 10.0
</code></pre>
<p><strong>Valid types:</strong> int, float</p>
<h3 id="less-than-or-equal-"><a class="header" href="#less-than-or-equal-">Less Than or Equal: <code>&lt;=</code></a></h3>
<pre><code class="language-storybook">health &lt;= 50
count &lt;= max_count
</code></pre>
<h3 id="greater-than"><a class="header" href="#greater-than">Greater Than: <code>&gt;</code></a></h3>
<pre><code class="language-storybook">strength &gt; 10
bond &gt; 0.5
</code></pre>
<h3 id="greater-than-or-equal-"><a class="header" href="#greater-than-or-equal-">Greater Than or Equal: <code>&gt;=</code></a></h3>
<pre><code class="language-storybook">age &gt;= 21
score &gt;= 100
</code></pre>
<h2 id="logical-operators"><a class="header" href="#logical-operators">Logical Operators</a></h2>
<h3 id="and-and"><a class="header" href="#and-and">AND: <code>and</code></a></h3>
<p>Both operands must be true.</p>
<pre><code class="language-storybook">health &lt; 50 and has_potion
is_ready and not is_busy
age &gt;= 18 and age &lt; 65
</code></pre>
<p><strong>Evaluation:</strong> Short-circuit (if left is false, right is not evaluated)</p>
<h3 id="or-or"><a class="header" href="#or-or">OR: <code>or</code></a></h3>
<p>At least one operand must be true.</p>
<pre><code class="language-storybook">is_day or is_lit
health &lt; 20 or surrounded
enemy_count == 0 or all_enemies_dead
</code></pre>
<p><strong>Evaluation:</strong> Short-circuit (if left is true, right is not evaluated)</p>
<h3 id="operator-precedence"><a class="header" href="#operator-precedence">Operator Precedence</a></h3>
<p>From highest to lowest:</p>
<ol>
<li><strong>Parentheses</strong>: <code>(...)</code></li>
<li><strong>Unary</strong>: <code>not</code>, <code>-</code></li>
<li><strong>Comparisons</strong>: <code>==</code>, <code>!=</code>, <code>&lt;</code>, <code>&lt;=</code>, <code>&gt;</code>, <code>&gt;=</code></li>
<li><strong>AND</strong>: <code>and</code></li>
<li><strong>OR</strong>: <code>or</code></li>
</ol>
<p><strong>Examples:</strong></p>
<pre><code class="language-storybook">not is_ready and is_awake
// Equivalent to: (not is_ready) and is_awake
health &lt; 50 or is_poisoned and has_antidote
// Equivalent to: (health &lt; 50) or (is_poisoned and has_antidote)
// Use parentheses for clarity:
(health &lt; 50 or is_poisoned) and has_antidote
</code></pre>
<h2 id="unary-operators"><a class="header" href="#unary-operators">Unary Operators</a></h2>
<h3 id="not-not"><a class="header" href="#not-not">NOT: <code>not</code></a></h3>
<p>Inverts a boolean value.</p>
<pre><code class="language-storybook">not is_ready
not (health &lt; 20)
not enemy_nearby and safe
</code></pre>
<h3 id="negation--"><a class="header" href="#negation--">Negation: <code>-</code></a></h3>
<p>Negates a numeric value.</p>
<pre><code class="language-storybook">-health
-10
-(max_value - current_value)
</code></pre>
<h2 id="field-access"><a class="header" href="#field-access">Field Access</a></h2>
<h3 id="direct-field-access"><a class="header" href="#direct-field-access">Direct Field Access</a></h3>
<pre><code class="language-storybook">health
bond
emotional_state
</code></pre>
<p>References a field on the current entity.</p>
<h3 id="dot-access"><a class="header" href="#dot-access">Dot Access</a></h3>
<pre><code class="language-storybook">Martha.skill_level
Character.emotional_state
enemy.health
</code></pre>
<p>Access fields on other entities.</p>
<h3 id="self-access"><a class="header" href="#self-access">Self Access</a></h3>
<p>In relationships and certain contexts, <code>self</code> refers to the current participant:</p>
<pre><code class="language-storybook">self.bond
self.responsibility
self.trust
</code></pre>
<p><strong>Use case:</strong> Relationship transitions, symmetric queries</p>
<h3 id="other-access"><a class="header" href="#other-access">Other Access</a></h3>
<p>In relationships, <code>other</code> refers to other participants:</p>
<pre><code class="language-storybook">other.bond
other.aware_of_mentor
other.respect
</code></pre>
<p><strong>Use case:</strong> Relationship queries with perspective</p>
<h3 id="example-in-life-arcs"><a class="header" href="#example-in-life-arcs">Example in Life Arcs</a></h3>
<pre><code class="language-storybook">life_arc RelationshipState {
state new {
on self.bond &gt; 0.7 and other.bond &gt; 0.7 -&gt; stable
}
state stable {
on self.bond &lt; 0.3 -&gt; troubled
on other.bond &lt; 0.3 -&gt; troubled
}
state troubled {
on self.bond &lt; 0.1 or other.bond &lt; 0.1 -&gt; broken
}
state broken {}
}
</code></pre>
<h2 id="quantifiers"><a class="header" href="#quantifiers">Quantifiers</a></h2>
<p>Quantifiers test conditions over collections.</p>
<h3 id="forall-forall"><a class="header" href="#forall-forall">ForAll: <code>forall</code></a></h3>
<p>Tests if a condition holds for all elements in a collection.</p>
<pre><code class="language-storybook">forall e in enemies: e.defeated
forall item in inventory: item.weight &lt; 10
</code></pre>
<p><strong>Syntax:</strong></p>
<pre><code class="language-bnf">forall &lt;variable&gt; in &lt;collection&gt;: &lt;predicate&gt;
</code></pre>
<p><strong>Semantics:</strong></p>
<ul>
<li>Returns <code>true</code> if predicate is true for every element</li>
<li>Returns <code>true</code> for empty collections (vacuously true)</li>
</ul>
<p><strong>Examples:</strong></p>
<pre><code class="language-storybook">// All enemies defeated?
forall enemy in enemies: enemy.health &lt;= 0
// All party members ready?
forall member in party: member.is_ready
// All doors locked?
forall door in doors: door.is_locked
</code></pre>
<h3 id="exists-exists"><a class="header" href="#exists-exists">Exists: <code>exists</code></a></h3>
<p>Tests if a condition holds for at least one element.</p>
<pre><code class="language-storybook">exists e in enemies: e.is_hostile
exists item in inventory: item.is_healing_potion
</code></pre>
<p><strong>Syntax:</strong></p>
<pre><code class="language-bnf">exists &lt;variable&gt; in &lt;collection&gt;: &lt;predicate&gt;
</code></pre>
<p><strong>Semantics:</strong></p>
<ul>
<li>Returns <code>true</code> if predicate is true for any element</li>
<li>Returns <code>false</code> for empty collections</li>
</ul>
<p><strong>Examples:</strong></p>
<pre><code class="language-storybook">// Any enemy nearby?
exists enemy in enemies: enemy.distance &lt; 10
// Any door unlocked?
exists door in doors: not door.is_locked
// Any ally wounded?
exists ally in allies: ally.health &lt; ally.max_health * 0.5
</code></pre>
<h3 id="nested-quantifiers"><a class="header" href="#nested-quantifiers">Nested Quantifiers</a></h3>
<p>Quantifiers can nest:</p>
<pre><code class="language-storybook">forall team in teams: exists player in team: player.is_leader
// Every team has at least one leader
exists room in dungeon: forall enemy in room.enemies: enemy.defeated
// At least one room has all enemies defeated
</code></pre>
<h2 id="usage-in-context"><a class="header" href="#usage-in-context">Usage in Context</a></h2>
<h3 id="life-arc-transitions"><a class="header" href="#life-arc-transitions">Life Arc Transitions</a></h3>
<pre><code class="language-storybook">life_arc CombatState {
state idle {
on enemy_count &gt; 0 -&gt; combat
}
state combat {
on health &lt; 20 -&gt; fleeing
on enemy_count == 0 -&gt; victorious
}
state fleeing {
on distance_from_enemies &gt; 100 -&gt; safe
}
state victorious {
on celebration_complete -&gt; idle
}
state safe {
on health &gt;= 50 -&gt; idle
}
}
</code></pre>
<h3 id="behavior-tree-conditions"><a class="header" href="#behavior-tree-conditions">Behavior Tree Conditions</a></h3>
<pre><code class="language-storybook">behavior GuardedAction {
if(health &gt; 50 and has_weapon) {
AggressiveAttack
}
}
behavior ConditionalChoice {
choose tactics {
then melee {
if(distance &lt; 5 and weapon_type == "sword")
MeleeAttack
}
then ranged {
if(distance &gt;= 5 and has_arrows)
RangedAttack
}
}
}
</code></pre>
<h3 id="behavior-tree-conditions-1"><a class="header" href="#behavior-tree-conditions-1">Behavior Tree Conditions</a></h3>
<pre><code class="language-storybook">behavior SmartAI {
choose strategy {
then aggressive {
if(health &gt; 70 and enemy_count &lt; 3)
Attack
}
then defensive {
if(health &lt; 30 or enemy_count &gt;= 5)
Defend
}
then balanced {
if(health &gt;= 30 and health &lt;= 70)
TacticalManeuver
}
}
}
</code></pre>
<h2 id="type-system"><a class="header" href="#type-system">Type System</a></h2>
<h3 id="type-compatibility"><a class="header" href="#type-compatibility">Type Compatibility</a></h3>
<p>Comparisons require compatible types:</p>
<div class="table-wrapper"><table><thead><tr><th>Operator</th><th>Left Type</th><th>Right Type</th><th>Valid?</th></tr></thead><tbody>
<tr><td><code>==</code>, <code>!=</code></td><td>int</td><td>int</td><td></td></tr>
<tr><td><code>==</code>, <code>!=</code></td><td>float</td><td>float</td><td></td></tr>
<tr><td><code>==</code>, <code>!=</code></td><td>string</td><td>string</td><td></td></tr>
<tr><td><code>==</code>, <code>!=</code></td><td>bool</td><td>bool</td><td></td></tr>
<tr><td><code>==</code>, <code>!=</code></td><td>enum</td><td>same enum</td><td></td></tr>
<tr><td><code>==</code>, <code>!=</code></td><td>int</td><td>float</td><td></td></tr>
<tr><td><code>&lt;</code>, <code>&lt;=</code>, <code>&gt;</code>, <code>&gt;=</code></td><td>int</td><td>int</td><td></td></tr>
<tr><td><code>&lt;</code>, <code>&lt;=</code>, <code>&gt;</code>, <code>&gt;=</code></td><td>float</td><td>float</td><td></td></tr>
<tr><td><code>&lt;</code>, <code>&lt;=</code>, <code>&gt;</code>, <code>&gt;=</code></td><td>string</td><td>string</td><td></td></tr>
</tbody></table>
</div>
<h3 id="implicit-coercion"><a class="header" href="#implicit-coercion">Implicit Coercion</a></h3>
<p><strong>None.</strong> Storybook has no implicit type coercion. All comparisons must be between compatible types.</p>
<p><strong>Error:</strong></p>
<pre><code class="language-storybook">count == "5" // Error: int vs string
health &lt; true // Error: int vs bool
</code></pre>
<p><strong>Correct:</strong></p>
<pre><code class="language-storybook">count == 5
health &lt; 50
</code></pre>
<h2 id="special-keyword-is"><a class="header" href="#special-keyword-is">Special Keyword: <code>is</code></a></h2>
<p>The <code>is</code> keyword provides syntactic sugar for equality with enum values:</p>
<pre><code class="language-storybook">// Instead of:
status == active
// You can write:
status is active
</code></pre>
<p><strong>More examples:</strong></p>
<pre><code class="language-storybook">name is "Martha"
skill_level is master
emotional_state is focused
</code></pre>
<p>This is purely syntactic—<code>is</code> and <code>==</code> are equivalent.</p>
<h2 id="complete-examples"><a class="header" href="#complete-examples">Complete Examples</a></h2>
<h3 id="simple-conditions"><a class="header" href="#simple-conditions">Simple Conditions</a></h3>
<pre><code class="language-storybook">health &lt; 20
enemy_nearby
not is_ready
count &gt; 5
</code></pre>
<h3 id="complex-conditions"><a class="header" href="#complex-conditions">Complex Conditions</a></h3>
<pre><code class="language-storybook">(health &lt; 20 and not has_potion) or surrounded
forall e in enemies: e.defeated
exists item in inventory: item.is_healing_potion and item.quantity &gt; 0
</code></pre>
<h3 id="life-arc-with-complex-conditions"><a class="header" href="#life-arc-with-complex-conditions">Life Arc with Complex Conditions</a></h3>
<pre><code class="language-storybook">life_arc CharacterMood {
state content {
on health &lt; 30 or hunger &gt; 80 -&gt; distressed
on social_interaction &gt; 0.8 -&gt; happy
}
state distressed {
on health &gt;= 50 and hunger &lt; 30 -&gt; content
on (health &lt; 10 or hunger &gt; 95) and help_available -&gt; desperate
}
state happy {
on social_interaction &lt; 0.3 -&gt; content
on received_bad_news -&gt; distressed
}
state desperate {
on help_received -&gt; distressed
}
}
</code></pre>
<h3 id="behavior-with-quantifiers"><a class="header" href="#behavior-with-quantifiers">Behavior with Quantifiers</a></h3>
<pre><code class="language-storybook">behavior SquadLeader {
choose leadership {
then regroup {
if(squad_has_wounded)
OrderRetreat
}
then advance {
if(squad_all_ready)
OrderAdvance
}
then hold_position {
if(not squad_all_ready)
OrderHold
}
}
}
</code></pre>
<h3 id="relationship-query"><a class="header" href="#relationship-query">Relationship Query</a></h3>
<pre><code class="language-storybook">life_arc FriendshipQuality {
state new_friends {
on self.bond &gt; 0.7 and other.bond &gt; 0.7 -&gt; strong_bond
on self.trust &lt; 0.3 or other.trust &lt; 0.3 -&gt; shaky
}
state strong_bond {
on self.bond &lt; 0.5 -&gt; weakening
}
state weakening {
on self.bond &lt; 0.2 or other.bond &lt; 0.2 -&gt; ended
on self.bond &gt; 0.7 and other.bond &gt; 0.7 -&gt; strong_bond
}
state shaky {
on self.trust &gt; 0.6 and other.trust &gt; 0.6 -&gt; new_friends
on self.trust &lt; 0.1 or other.trust &lt; 0.1 -&gt; ended
}
state ended {}
}
</code></pre>
<h2 id="validation-rules"><a class="header" href="#validation-rules">Validation Rules</a></h2>
<ol>
<li><strong>Type consistency</strong>: Both sides of comparison must be compatible types</li>
<li><strong>Boolean context</strong>: Logical operators (<code>and</code>, <code>or</code>, <code>not</code>) require boolean operands</li>
<li><strong>Field existence</strong>: Referenced fields must exist on the entity</li>
<li><strong>Collection validity</strong>: Quantifiers require collection-typed expressions</li>
<li><strong>Variable scope</strong>: Quantifier variables only valid within their predicate</li>
<li><strong>No division by zero</strong>: Arithmetic operations must not divide by zero</li>
<li><strong>Enum validity</strong>: Enum comparisons must reference defined enum values</li>
</ol>
<h2 id="best-practices"><a class="header" href="#best-practices">Best Practices</a></h2>
<h3 id="1-use-parentheses-for-clarity"><a class="header" href="#1-use-parentheses-for-clarity">1. Use Parentheses for Clarity</a></h3>
<p><strong>Avoid:</strong></p>
<pre><code class="language-storybook">health &lt; 50 or is_poisoned and has_antidote
</code></pre>
<p><strong>Prefer:</strong></p>
<pre><code class="language-storybook">(health &lt; 50 or is_poisoned) and has_antidote
</code></pre>
<h3 id="2-break-complex-conditions"><a class="header" href="#2-break-complex-conditions">2. Break Complex Conditions</a></h3>
<p><strong>Avoid:</strong></p>
<pre><code class="language-storybook">on (health &lt; 20 and not has_potion) or (surrounded and not has_escape) or (enemy_count &gt; 10 and weapon_broken) -&gt; desperate
</code></pre>
<p><strong>Prefer:</strong></p>
<pre><code class="language-storybook">state combat {
on health &lt; 20 and not has_potion -&gt; desperate
on surrounded and not has_escape -&gt; desperate
on enemy_count &gt; 10 and weapon_broken -&gt; desperate
}
</code></pre>
<h3 id="3-name-complex-conditions"><a class="header" href="#3-name-complex-conditions">3. Name Complex Conditions</a></h3>
<p>For repeated complex conditions, consider using intermediate fields:</p>
<p><strong>Instead of:</strong></p>
<pre><code class="language-storybook">on health &lt; (max_health * 0.2) and enemy_count &gt; 5 -&gt; flee
</code></pre>
<p><strong>Consider:</strong></p>
<pre><code class="language-storybook">// In character definition:
critically_wounded: health &lt; (max_health * 0.2)
outnumbered: enemy_count &gt; 5
// In life arc:
on critically_wounded and outnumbered -&gt; flee
</code></pre>
<h3 id="4-use-is-for-enums"><a class="header" href="#4-use-is-for-enums">4. Use <code>is</code> for Enums</a></h3>
<p><strong>Prefer:</strong></p>
<pre><code class="language-storybook">status is active
emotional_state is focused
</code></pre>
<p><strong>Over:</strong></p>
<pre><code class="language-storybook">status == active
emotional_state == focused
</code></pre>
<h3 id="5-quantifiers-for-collections"><a class="header" href="#5-quantifiers-for-collections">5. Quantifiers for Collections</a></h3>
<p><strong>Avoid:</strong></p>
<pre><code class="language-storybook">// Manual checks for each element
if enemy1.defeated and enemy2.defeated and enemy3.defeated
</code></pre>
<p><strong>Prefer:</strong></p>
<pre><code class="language-storybook">if forall enemy in enemies: enemy.defeated
</code></pre>
<h2 id="cross-references"><a class="header" href="#cross-references">Cross-References</a></h2>
<ul>
<li><a href="./13-life-arcs.html">Life Arcs</a> - Transition conditions</li>
<li><a href="./11-behavior-trees.html">Behavior Trees</a> - Guard and condition nodes</li>
<li><a href="./12-decorators.html">Decorators</a> - Guard decorator</li>
<li><a href="./15-relationships.html">Relationships</a> - Self/other field access</li>
<li><a href="./18-value-types.html">Value Types</a> - Literal value types</li>
</ul>
<h2 id="related-concepts"><a class="header" href="#related-concepts">Related Concepts</a></h2>
<ul>
<li><strong>Type safety</strong>: Strong typing prevents type errors at compile time</li>
<li><strong>Short-circuit evaluation</strong>: AND/OR operators optimize evaluation</li>
<li><strong>Quantifiers</strong>: Enable expressive collection queries</li>
<li><strong>Field access</strong>: Context-sensitive (<code>self</code>, <code>other</code>) for relationships</li>
<li><strong>Boolean algebra</strong>: Standard logical operators with expected semantics</li>
</ul>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../reference/16-other-declarations.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/18-value-types.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/16-other-declarations.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/18-value-types.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>