Files
storybook/docs/book/reference/16a-locations.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

478 lines
20 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 - 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"><a class="header" href="#locations">Locations</a></h1>
<p>Locations define places in your world rooms, buildings, cities, landscapes, or abstract spaces. They provide spatial context for characters, events, and narratives.</p>
<hr />
<h2 id="syntax"><a class="header" href="#syntax">Syntax</a></h2>
<pre><code class="language-bnf">&lt;location-decl&gt; ::= "location" &lt;identifier&gt; "{" &lt;field&gt;* &lt;prose-block&gt;* "}"
</code></pre>
<p>A location declaration consists of:</p>
<ul>
<li>The <code>location</code> keyword</li>
<li>A unique name (identifier)</li>
<li>A body block containing fields and optional prose blocks</li>
</ul>
<p>Locations are one of the simpler declaration types they hold fields and prose blocks but do not support resource linking (<code>uses behaviors</code> / <code>uses schedule</code>) like characters or institutions.</p>
<hr />
<h2 id="basic-location"><a class="header" href="#basic-location">Basic Location</a></h2>
<p>The simplest location has a name and descriptive fields:</p>
<pre><code class="language-storybook">location BakersBakery {
type: bakery
capacity: 30
address: "14 Main Street"
open_hours: "06:00-18:00"
}
</code></pre>
<p>Fields can use any <a href="./18-value-types.html">value type</a>: integers, floats, strings, booleans, enums, lists, ranges, and durations.</p>
<hr />
<h2 id="fields"><a class="header" href="#fields">Fields</a></h2>
<p>Location fields describe properties of the place:</p>
<pre><code class="language-storybook">location BakerHome {
type: residence
bedrooms: 3
has_garden: true
garden_size_sqft: 450.0
residents: ["Martha", "David", "Jane"]
comfort_level: 0.85
}
</code></pre>
<h3 id="common-field-patterns"><a class="header" href="#common-field-patterns">Common Field Patterns</a></h3>
<div class="table-wrapper"><table><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody>
<tr><td><code>type</code></td><td>enum/identifier</td><td>Category of the place</td></tr>
<tr><td><code>capacity</code></td><td>integer</td><td>How many can be in this place</td></tr>
<tr><td><code>size</code></td><td>enum/float</td><td>Physical size</td></tr>
<tr><td><code>coordinates</code></td><td>integer/float</td><td>Position in a world map</td></tr>
<tr><td><code>accessible</code></td><td>boolean</td><td>Whether characters can enter</td></tr>
</tbody></table>
</div>
<p>These are conventions, not enforced schema. You define whatever fields are meaningful for your world.</p>
<hr />
<h2 id="prose-blocks"><a class="header" href="#prose-blocks">Prose Blocks</a></h2>
<p>Locations support prose blocks for rich narrative content. Prose blocks are delimited by <code>---tag</code> markers:</p>
<pre><code class="language-storybook">location BakersBakery {
type: bakery
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.
---
---atmosphere
Flour dust catches the light from tall windows. A display case
holds rows of golden pastries. Behind the counter, the kitchen
hums with activity from 4 AM onward.
---
}
</code></pre>
<p><strong>Prose block rules:</strong></p>
<ul>
<li>Start with <code>---tag_name</code> on its own line</li>
<li>Content is free-form text (Markdown supported)</li>
<li>End with <code>---</code> on its own line</li>
<li>The tag name becomes the key for retrieval</li>
<li>Multiple prose blocks per location are allowed</li>
<li>Each tag must be unique within the location</li>
</ul>
<hr />
<h2 id="ranges"><a class="header" href="#ranges">Ranges</a></h2>
<p>Locations can use range values for procedural variation:</p>
<pre><code class="language-storybook">location MarketSquare {
type: outdoor_market
stalls: 10..25
daily_visitors: 50..200
noise_level: 0.4..0.9
}
</code></pre>
<p>When instantiated, values are selected from within the specified range. This is useful for locations that should vary across simulation runs.</p>
<hr />
<h2 id="lists"><a class="header" href="#lists">Lists</a></h2>
<p>Locations can hold list values:</p>
<pre><code class="language-storybook">location BakersBakery {
type: bakery
products: ["sourdough", "croissants", "rye bread", "cinnamon rolls"]
equipment: ["stone oven", "mixing station", "proofing cabinet"]
}
</code></pre>
<hr />
<h2 id="referencing-other-entities"><a class="header" href="#referencing-other-entities">Referencing Other Entities</a></h2>
<p>Location fields can reference other declarations by name:</p>
<pre><code class="language-storybook">location BakersBakery {
type: bakery
owner: Martha
neighborhood: MainStreet
part_of: TownCenter
}
</code></pre>
<p>These are identifier references they are not validated as cross-references at parse time, but the resolver checks that referenced names exist in the name table.</p>
<hr />
<h2 id="nested-structure-with-fields"><a class="header" href="#nested-structure-with-fields">Nested Structure with Fields</a></h2>
<p>You can model spatial hierarchy using fields:</p>
<pre><code class="language-storybook">location BakersBakery {
type: bakery
parent: MainStreet
// Zones within the bakery
has_kitchen: true
has_storefront: true
has_storage_room: true
// Dimensions
total_sqft: 1200
kitchen_sqft: 600
storefront_sqft: 400
storage_sqft: 200
}
location MainStreet {
type: street
parent: TownCenter
length_meters: 500
shops: 12
}
location TownCenter {
type: district
population: 2000
}
</code></pre>
<p>There is no built-in parent-child relationship for locations you model hierarchy through conventional field names like <code>part_of</code>, <code>parent</code>, or <code>contains</code>.</p>
<hr />
<h2 id="location-with-enum-fields"><a class="header" href="#location-with-enum-fields">Location with Enum Fields</a></h2>
<p>Use <a href="./16-other-declarations.html#enums">enums</a> for type-safe categorization:</p>
<pre><code class="language-storybook">enum PlaceType {
residence, shop, workshop, office,
park, street, square, church
}
enum Accessibility {
public, private, restricted, members_only
}
location BakersBakery {
type: shop
accessibility: public
capacity: 30
}
location BakerHome {
type: residence
accessibility: private
capacity: 8
}
</code></pre>
<hr />
<h2 id="complete-example-baker-family-locations"><a class="header" href="#complete-example-baker-family-locations">Complete Example: Baker Family Locations</a></h2>
<pre><code class="language-storybook">use schema::enums::{PlaceType, Accessibility};
location BakersBakery {
type: shop
accessibility: public
owner: Martha
address: "14 Main Street"
capacity: 30
employees: 4
established: "2011"
specialty: "artisan sourdough"
daily_output_loaves: 80..120
---description
Martha Baker's artisan bakery, known throughout town for its
sourdough and pastries. The shop opens at 6 AM sharp, and by
mid-morning there's usually a line out the door.
---
---history
Originally a hardware store, Martha converted the space after
winning a local baking competition. The stone oven was imported
from France and is the heart of the operation.
---
}
location BakerHome {
type: residence
accessibility: private
address: "22 Elm Lane"
bedrooms: 4
has_garden: true
garden_size_sqft: 600
residents: ["Martha", "David", "Jane", "Tom"]
comfort_level: 0.9
---description
A comfortable family home on a quiet street. The kitchen is
oversized (Martha insisted) and there's always something
baking, even at home.
---
}
location BakersGuildHall {
type: office
accessibility: members_only
address: "7 Guild Row"
capacity: 100
meeting_room_capacity: 40
established: "1892"
---description
The historic headquarters of the Bakers Guild, where trade
matters are discussed and apprenticeships are arranged.
---
}
</code></pre>
<hr />
<h2 id="validation-rules"><a class="header" href="#validation-rules">Validation Rules</a></h2>
<ol>
<li><strong>Unique names</strong>: Location names must be unique within their scope</li>
<li><strong>Valid field values</strong>: All fields must have values that conform to <a href="./18-value-types.html">value types</a></li>
<li><strong>Unique field names</strong>: No duplicate field names within a location</li>
<li><strong>Unique prose tags</strong>: No duplicate prose block tags within a location</li>
<li><strong>Valid identifiers</strong>: Location names must follow identifier rules (<code>[a-zA-Z_][a-zA-Z0-9_]*</code>)</li>
</ol>
<hr />
<h2 id="locations-vs-other-declarations"><a class="header" href="#locations-vs-other-declarations">Locations vs. Other Declarations</a></h2>
<div class="table-wrapper"><table><thead><tr><th>Aspect</th><th>Locations</th><th>Institutions</th><th>Characters</th></tr></thead><tbody>
<tr><td>Purpose</td><td>Physical/abstract places</td><td>Organizations/groups</td><td>Individuals</td></tr>
<tr><td>Resource linking</td><td>No</td><td>Yes</td><td>Yes</td></tr>
<tr><td>Prose blocks</td><td>Yes</td><td>Yes</td><td>Yes</td></tr>
<tr><td>Species</td><td>No</td><td>No</td><td>Yes</td></tr>
<tr><td>Templates</td><td>No</td><td>No</td><td>Yes</td></tr>
</tbody></table>
</div>
<p>Locations are intentionally simple. They define <em>where</em> things happen. For <em>who</em> does things, use <a href="./10-characters.html">characters</a>. For <em>organizational structures</em>, use <a href="./16b-institutions.html">institutions</a>.</p>
<hr />
<h2 id="cross-references"><a class="header" href="#cross-references">Cross-References</a></h2>
<ul>
<li><a href="./10-characters.html">Characters</a> Characters can reference locations via fields</li>
<li><a href="./16b-institutions.html">Institutions</a> Institutions can be associated with locations</li>
<li><a href="./14-schedules.html">Schedules</a> Schedule activities can reference locations</li>
<li><a href="./18-value-types.html">Value Types</a> All field value types</li>
<li><a href="./16-other-declarations.html">Other Declarations</a> Overview of all utility declarations</li>
<li><a href="./19-validation.html">Validation Rules</a> Complete validation reference</li>
</ul>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../reference/15-relationships.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/16b-institutions.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/15-relationships.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/16b-institutions.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>