Files
storybook/docs/book/tutorial/09-locations-institutions.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

636 lines
25 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Locations and Institutions - 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="locations-and-institutions"><a class="header" href="#locations-and-institutions">Locations and Institutions</a></h1>
<p>So far you have created characters, given them behaviors, connected them with relationships, scheduled their days, and guided them through life arcs. But characters need places to <em>be</em> and organizations to <em>belong to</em>. That is what locations and institutions provide.</p>
<hr />
<h2 id="what-are-locations"><a class="header" href="#what-are-locations">What Are Locations?</a></h2>
<p>A <strong>location</strong> is a place in your world. It can be a building, a room, a park, a city any space where things happen. Locations hold fields that describe the place and optional prose blocks for narrative detail.</p>
<h3 id="your-first-location"><a class="header" href="#your-first-location">Your First Location</a></h3>
<p>Lets create the bakery where Martha works:</p>
<pre><code class="language-storybook">location BakersBakery {
type: bakery
address: "14 Main Street"
capacity: 30
}
</code></pre>
<p>That is all it takes. The <code>location</code> keyword, a name, and a block of fields. Every field is a key-value pair, and you choose whatever fields make sense for your world.</p>
<h3 id="adding-detail"><a class="header" href="#adding-detail">Adding Detail</a></h3>
<p>A real location needs more than three fields. Lets flesh out the bakery:</p>
<pre><code class="language-storybook">location BakersBakery {
type: bakery
address: "14 Main Street"
capacity: 30
employees: 4
specialty: "artisan sourdough"
daily_output_loaves: 80..120
open: true
established: "2011"
}
</code></pre>
<p>Notice <code>daily_output_loaves: 80..120</code> that is a range. Each simulation run can pick a different number of loaves, adding natural variation.</p>
<h3 id="prose-blocks"><a class="header" href="#prose-blocks">Prose Blocks</a></h3>
<p>Bare fields are good for data, but locations also need narrative flavor. Use prose blocks:</p>
<pre><code class="language-storybook">location BakersBakery {
type: bakery
address: "14 Main Street"
capacity: 30
---description
A warm, inviting bakery on Main Street. The smell of fresh bread
wafts out the door every morning at dawn. Martha has run the shop
for fifteen years, and the locals consider it the heart of the
neighborhood.
---
}
</code></pre>
<p>Prose blocks start with <code>---tag_name</code> and end with <code>---</code>. The tag name (<code>description</code> here) becomes the key. You can have as many prose blocks as you want:</p>
<pre><code class="language-storybook">location BakersBakery {
type: bakery
---description
The bakery on Main Street...
---
---history
Originally a hardware store, Martha converted the space in 2011...
---
---atmosphere
Flour dust catches the light from tall windows...
---
}
</code></pre>
<hr />
<h2 id="building-a-world-with-locations"><a class="header" href="#building-a-world-with-locations">Building a World with Locations</a></h2>
<p>Locations work best when they form a coherent world. Here is the Baker familys neighborhood:</p>
<pre><code class="language-storybook">location BakersBakery {
type: bakery
address: "14 Main Street"
capacity: 30
owner: Martha
---description
Martha's artisan bakery. The stone oven was imported from France.
---
}
location BakerHome {
type: residence
address: "22 Elm Lane"
bedrooms: 4
has_garden: true
---description
The Baker family home. Martha insisted on an oversized kitchen.
---
}
location BakersGuildHall {
type: guild_hall
address: "7 Guild Row"
capacity: 100
established: "1892"
---description
The historic headquarters of the Bakers Guild.
---
}
location TownSquare {
type: public_square
capacity: 500
has_fountain: true
has_market_stalls: true
---description
The central gathering place. On weekends, the farmers market
fills the square with produce stalls.
---
}
</code></pre>
<h3 id="modeling-hierarchy"><a class="header" href="#modeling-hierarchy">Modeling Hierarchy</a></h3>
<p>Storybook does not enforce a built-in parent-child relationship for locations. Instead, you use fields to express hierarchy:</p>
<pre><code class="language-storybook">location MainStreet {
type: street
district: TownCenter
shops: 12
}
location BakersBakery {
type: bakery
street: MainStreet
district: TownCenter
}
</code></pre>
<p>This convention-based approach keeps the language simple while letting you model whatever spatial relationships your world needs.</p>
<hr />
<h2 id="what-are-institutions"><a class="header" href="#what-are-institutions">What Are Institutions?</a></h2>
<p>An <strong>institution</strong> is an organization, group, or system. Think of it as a character that represents a collective: a guild, a government, a school, a business. Institutions have a key capability that locations lack they can <strong>use behaviors and schedules</strong>, just like characters.</p>
<h3 id="your-first-institution"><a class="header" href="#your-first-institution">Your First Institution</a></h3>
<pre><code class="language-storybook">institution BakersGuild {
type: trade_guild
members: 50
founded: "1892"
reputation: 0.85
}
</code></pre>
<p>This looks just like a location so far. The difference comes when you add behaviors.</p>
<h3 id="institutions-with-behaviors"><a class="header" href="#institutions-with-behaviors">Institutions with Behaviors</a></h3>
<p>Institutions can act. The <code>uses behaviors</code> clause links behavior trees to the institution:</p>
<pre><code class="language-storybook">institution BakersGuild {
type: trade_guild
members: 50
reputation: 0.85
uses behaviors: [
{ tree: ManageApprentices },
{ tree: NegotiateSuppliers },
{ tree: HostEvents }
]
}
</code></pre>
<p>Each entry in the list is a behavior link object with a <code>tree</code> field. This tells the simulation engine that the Bakers Guild can manage apprentices, negotiate with suppliers, and host events.</p>
<h3 id="behavior-priorities"><a class="header" href="#behavior-priorities">Behavior Priorities</a></h3>
<p>Not all behaviors are equally important. Use the <code>priority</code> field:</p>
<pre><code class="language-storybook">institution BakersGuild {
type: trade_guild
uses behaviors: [
{ tree: ManageApprentices, priority: normal },
{ tree: NegotiateSuppliers, priority: high },
{ tree: HostEvents, priority: low }
]
}
</code></pre>
<p>Priority levels are <code>low</code>, <code>normal</code>, <code>high</code>, and <code>critical</code>. Higher-priority behaviors take precedence when the institution must choose between actions.</p>
<h3 id="conditional-behaviors"><a class="header" href="#conditional-behaviors">Conditional Behaviors</a></h3>
<p>Some behaviors only activate under certain conditions:</p>
<pre><code class="language-storybook">institution BakersGuild {
type: trade_guild
reputation: 0.85
uses behaviors: [
{ tree: ManageApprentices },
{ tree: NegotiateSuppliers, priority: high },
{ tree: EmergencyMeeting, when: reputation &lt; 0.3, priority: critical }
]
}
</code></pre>
<p>The <code>when</code> clause uses an <a href="../reference/17-expressions.html">expression</a>. Here, the emergency meeting behavior only activates when reputation drops below 0.3.</p>
<h3 id="institutions-with-schedules"><a class="header" href="#institutions-with-schedules">Institutions with Schedules</a></h3>
<p>Institutions can also follow schedules:</p>
<pre><code class="language-storybook">institution BakersGuild {
type: trade_guild
uses schedule: GuildOperatingHours
}
</code></pre>
<p>For multiple schedules:</p>
<pre><code class="language-storybook">institution BakersGuild {
type: trade_guild
uses schedules: [WeekdaySchedule, WeekendSchedule]
}
</code></pre>
<h3 id="prose-blocks-1"><a class="header" href="#prose-blocks-1">Prose Blocks</a></h3>
<p>Just like locations, institutions support prose blocks:</p>
<pre><code class="language-storybook">institution BakersGuild {
type: trade_guild
members: 50
---description
The Bakers Guild has been the backbone of the town's bread trade
since 1892. Members share recipes, arrange apprenticeships, and
collectively negotiate flour prices.
---
---charter
Article I: All members shall maintain the highest standards.
Article II: Apprentices must complete a three-year program.
---
}
</code></pre>
<hr />
<h2 id="connecting-characters-to-institutions"><a class="header" href="#connecting-characters-to-institutions">Connecting Characters to Institutions</a></h2>
<p>Institutions do not have a built-in membership list. You model membership through character fields or relationships.</p>
<h3 id="through-character-fields"><a class="header" href="#through-character-fields">Through Character Fields</a></h3>
<p>The simplest approach add fields to your characters:</p>
<pre><code class="language-storybook">character Martha {
age: 45
occupation: baker
guild: BakersGuild
guild_role: guild_master
guild_member_since: "2005"
}
character Jane {
age: 19
occupation: apprentice_baker
guild: BakersGuild
guild_role: apprentice
guild_member_since: "2024"
}
</code></pre>
<h3 id="through-relationships"><a class="header" href="#through-relationships">Through Relationships</a></h3>
<p>For richer modeling, use relationships:</p>
<pre><code class="language-storybook">relationship GuildMembership {
Martha as guild_master { years_active: 20 }
BakersGuild as organization { }
bond: 0.95
}
relationship Apprenticeship {
Jane as apprentice { skills_learned: 12 }
Martha as mentor { patience_remaining: 0.7 }
BakersGuild as guild { }
years_completed: 1
}
</code></pre>
<p>This approach captures richer information: roles, duration, and multi-party connections.</p>
<hr />
<h2 id="locations-vs-institutions"><a class="header" href="#locations-vs-institutions">Locations vs. Institutions</a></h2>
<p>When should you use each?</p>
<div class="table-wrapper"><table><thead><tr><th>Question</th><th>Use…</th></tr></thead><tbody>
<tr><td>Where does something happen?</td><td>Location</td></tr>
<tr><td>Who or what organizes things?</td><td>Institution</td></tr>
<tr><td>Does it need behaviors?</td><td>Institution</td></tr>
<tr><td>Does it need a schedule?</td><td>Institution</td></tr>
<tr><td>Is it purely a place?</td><td>Location</td></tr>
<tr><td>Is it a group or organization?</td><td>Institution</td></tr>
</tbody></table>
</div>
<p>Sometimes the same concept needs both:</p>
<pre><code class="language-storybook">// The physical building
location BakersGuildHall {
type: guild_hall
address: "7 Guild Row"
capacity: 100
}
// The organization that meets there
institution BakersGuild {
type: trade_guild
members: 50
location: BakersGuildHall
uses behaviors: [
{ tree: ManageApprentices },
{ tree: NegotiateSuppliers }
]
uses schedule: GuildOperatingHours
}
</code></pre>
<p>The guild hall is a <em>place</em>. The guild is an <em>organization</em>. Keeping them separate lets you say “the guild meets at the guild hall” without conflating the building with the institution.</p>
<hr />
<h2 id="putting-it-all-together"><a class="header" href="#putting-it-all-together">Putting It All Together</a></h2>
<p>Here is a complete example showing how locations, institutions, and characters work together in the Baker family world:</p>
<pre><code class="language-storybook">// Enums for type safety
enum PlaceType {
bakery, residence, guild_hall, public_square
}
enum GuildRole {
guild_master, journeyman, apprentice
}
// Locations: where things happen
location BakersBakery {
type: bakery
address: "14 Main Street"
capacity: 30
owner: Martha
---description
Martha's artisan bakery on Main Street.
---
}
location BakerHome {
type: residence
address: "22 Elm Lane"
bedrooms: 4
residents: ["Martha", "David", "Jane", "Tom"]
}
location BakersGuildHall {
type: guild_hall
address: "7 Guild Row"
capacity: 100
---description
The historic Bakers Guild headquarters, established 1892.
---
}
// Institution: the organization
institution BakersGuild {
type: trade_guild
members: 50
founded: "1892"
reputation: 0.85
location: BakersGuildHall
leader: Martha
uses behaviors: [
{ tree: ManageApprentices, priority: normal },
{ tree: NegotiateSuppliers, priority: high },
{ tree: HostAnnualBakeOff, when: month is october }
]
uses schedule: GuildOperatingHours
---description
The Bakers Guild oversees apprenticeships, quality standards,
and the annual Great Bake-Off competition.
---
}
// Institution: the business
institution BakersBakeryBusiness {
type: business
owner: Martha
employees: 4
location: BakersBakery
uses behaviors: [
{ tree: DailyBakingOps, priority: high },
{ tree: InventoryManagement }
]
uses schedule: BakeryOperatingHours
}
// Characters connected to all of the above
character Martha {
age: 45
occupation: baker
workplace: BakersBakery
home: BakerHome
guild: BakersGuild
guild_role: guild_master
}
character Jane {
age: 19
occupation: apprentice_baker
workplace: BakersBakery
home: BakerHome
guild: BakersGuild
guild_role: apprentice
}
// Relationships tying it all together
relationship GuildLeadership {
Martha as guild_master { }
BakersGuild as guild { }
years_in_role: 8
}
relationship BakeryApprenticeship {
Jane as apprentice { }
Martha as mentor { }
BakersGuild as certifying_body { }
year: 1
total_years: 3
}
</code></pre>
<hr />
<h2 id="key-takeaways"><a class="header" href="#key-takeaways">Key Takeaways</a></h2>
<ol>
<li><strong>Locations</strong> are simple: name, fields, prose blocks. They model <em>places</em>.</li>
<li><strong>Institutions</strong> are richer: they add <code>uses behaviors</code> and <code>uses schedule</code> on top of fields and prose. They model <em>organizations</em>.</li>
<li><strong>Membership</strong> is modeled through character fields or relationships, not built into institution syntax.</li>
<li><strong>Separate place from organization</strong>: A guild hall (location) and the guild (institution) are different things.</li>
<li><strong>Use enums</strong> for type-safe categorization of locations and institutions.</li>
</ol>
<hr />
<h2 id="next-steps"><a class="header" href="#next-steps">Next Steps</a></h2>
<ul>
<li>Learn about <a href="../reference/17-expressions.html">expressions</a> used in conditional behavior links</li>
<li>Explore <a href="./03-first-behavior-tree.html">behavior trees</a> to create the behaviors your institutions use</li>
<li>See <a href="./07-schedules.html">schedules</a> to define operating hours for institutions</li>
<li>Read the full <a href="../reference/16a-locations.html">Locations Reference</a> and <a href="../reference/16b-institutions.html">Institutions Reference</a></li>
</ul>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../tutorial/08-life-arcs.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/09-overview.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="../tutorial/08-life-arcs.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/09-overview.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>