Elf Revel Wiki
Welcome to the comprehensive reference for Elf Revel, a colony sim where elves compose music, throw revels, and argue about art.
This wiki documents every game system in detail — exact formulas, tick-level behavior, hidden mechanics, and strategy advice. Whether you're a new player figuring out the controls or an optimizer calculating revel satisfaction scores, you'll find what you need here.
The Three-Tier Model
Elf Revel has three levels of intentionality:
- You (the Patron) — provide taste, artistic direction, and broad guidance via the chat panel and keyboard commands
- The AI Advisor (Curator) — translates your preferences into concrete cultural policies: role assignments, revel scheduling, build orders
- The Elves — execute autonomously at the tick level: gather resources, compose music, attend revels, form relationships
The human has taste, the AI has hands, the elves have lives.
How to Use This Wiki
New players — start with Controls and Your First Settlement.
Understanding mechanics — each system page in Core Systems and Art & Culture covers mechanics from overview down to exact formulas.
Optimizing play — the Strategy Guides connect game systems into practical advice.
Every page follows the same structure:
- Overview — what the system does
- How It Works — mechanics description
- Values & Formulas — tables with exact numbers from the source code
- Interactions — how it connects to other systems
- Tips — practical advice
Game Timing
All game mechanics run on ticks. One day = 100 ticks. One season = 25 days. One year = 100 days (4 seasons). The game starts in Spring.
Controls
Complete keybinding reference for Elf Revel. The game is controlled entirely via keyboard.
Core
| Key | Action |
|---|---|
Space | Pause / Resume simulation |
+ / = | Speed up (1x → 2x → 4x → 8x) |
- | Slow down |
Arrow keys | Pan map (when map focused) |
Tab | Cycle focus: Map → Chat → Events |
BackTab (Shift+Tab) | Cycle focus backward |
Esc | Close overlay / deselect / back |
Ctrl+C | Quit |
? | Help overlay (press any key to close) |
Elf Selection
| Key | Action |
|---|---|
1-9 | Select elf by number |
, | Cycle to previous elf |
. | Cycle to next elf |
Enter | Open elf detail popup (when elf selected) |
Screens (Full Overlays)
Each screen opens a full overlay. Press the same key again or Esc to close. You can switch directly between screens by pressing another screen key.
| Key | Screen | What It Shows |
|---|---|---|
s | Settlement Overview | Population, resources (Wood/Stone/Food/FineWood), buildings, daily production rates |
a | Art Gallery | All compositions with genre, quality, properties, titles, descriptions |
x | Relationships | Elf-to-elf bonds (friends, rivals, mentors) with relationship strength |
v | Revel History | Past revels: when they happened, who attended, what was performed, satisfaction |
b | Build Management | Build queue: pending and completed buildings with progress |
w | Work Assignments | Elf-by-role grid for reassigning roles |
All screens support Up/Down to scroll.
Work Assignments Screen
The Work Assignments screen (w) uses a cursor-based grid:
| Key | Action |
|---|---|
Up/Down | Select elf row |
Left/Right | Select role column |
Enter | Assign the selected role to the selected elf |
Current assignment shows as [X] in green. Cursor cell is highlighted in cyan.
Panels & Modes
| Key | Action |
|---|---|
r | Toggle roster sidebar (list of all elves with mood indicators) |
e | Toggle events panel (scrolling settlement event log) |
l | Enter look mode (inspect tiles on the map) |
Look Mode
In look mode, a cursor appears on the map for tile inspection:
| Key | Action |
|---|---|
Arrow keys | Move look cursor |
w/a/s/d | Pan camera (WASD) |
Enter | Select elf at cursor position |
n | Name the location at cursor position |
, / . | Cycle elves (from cursor) |
Esc or l | Exit look mode |
Chat (AI Advisor)
| Key | Action |
|---|---|
/ | Open chat input (from map focus) |
Tab → type | Focus chat panel, then type freely |
Enter | Send message to the curator |
Esc | Cancel / close chat input |
Up/Down | Scroll message history (when chat focused) |
See How the Curator Works for what the advisor can do.
Actions
| Key | Action |
|---|---|
d | Cycle artistic direction: Balanced → Favor Mastery → Favor Originality → Favor Emotion |
i | Watch/unwatch selected elf (marks for tracking in detail view) |
f | Toggle favorite on selected composition (in elf detail) |
j/k | Navigate between compositions (in elf detail) |
Tips
- Pause often (
Space) — the simulation runs even while you browse screens - Events panel (
e) is the most informative panel — it shows compositions, relationships, mood shifts, spirit warnings, and season changes - Name locations via look mode (
l→ navigate →n) — named spots persist and appear on the map
UI Guide
The main screen is a terminal interface with several panels and overlay screens.
Main View
Map (Center)
The map shows your settlement on a procedurally generated terrain grid. Elves are colored letters moving across the landscape.
| Glyph | Meaning |
|---|---|
| Colored letters | Elves (each has a unique color) |
. | Meadow |
# | Stone |
~ | Water |
T | Ancient Forest |
t | Young Forest |
= | Wall (player-built) |
| Building glyphs | Dwellings, Workshops, Gardens, Feast Halls |
See Terrain for full details on each tile type.
Status Bar (Top)
Displays the current game state at a glance:
- Season and day: e.g., "Spring Day 12 (Day)"
- Weather icon: current weather condition
- Curator mode: which advisor is active (Dummy or LLM)
- Speed: current simulation speed (1x-8x)
- Pause indicator: shows when paused
Footer Bar (Bottom)
Shows resource counts (Wood, Stone, Food, FineWood) and a condensed status line with season and temperature.
Elf Detail Panel (Right, when selected)
Select an elf (1-9 or ,/.) then press Enter to see:
- Mood — current morale state and active modifiers
- Needs — bar display for Rest, Sustenance, Beauty, Stimulation, Company
- Current task — what the elf is doing right now
- Skills — Music, Building, Gathering levels with XP progress
- Compositions — portfolio of works (navigate with
j/k, favorite withf) - Relationships — bonds with other elves
- Aesthetic position — four-axis position (structure, tradition, emotion, social)
Events Panel (Right, toggle with e)
A scrolling log of settlement events:
- Composition completions
- Relationship changes (new bonds, breakups, mentoring)
- Mood shifts and need warnings
- Revel announcements and results
- Forest spirit warnings
- Season changes and notable weather
- Departure warnings and departures
Roster Sidebar (Right, toggle with r)
List of all elves with mood indicator icons. Quick way to scan settlement morale at a glance.
Overlay Screens
Press any screen key to open a full overlay. Overlays replace the main view until closed with Esc or by pressing another screen key.
Settlement Overview (s)
Population count, resource stockpiles with daily production/consumption rates, and a list of completed and in-progress buildings.
Art Gallery (a)
Every composition in the settlement, showing:
- Title (three-part procedural name)
- Composer
- Genre family and base type
- Quality scores (mastery, originality, emotional)
- Properties (up to 15 attributes)
Relationships (x)
Grid of all elf-to-elf bonds with type (Friend, Rival, Mentor) and strength value. See Relationships.
Revel History (v)
Record of past revels: date, location, attendees, performances, and audience satisfaction. See Revels.
Build Management (b)
Build queue showing pending construction with progress bars, plus completed buildings.
Work Assignments (w)
Interactive elf-by-role grid. Cursor navigation to reassign elves between Gatherer, Builder, Composer, and Unassigned. See Roles.
Chat Panel (Bottom)
The chat panel shows your conversation with the AI cultural advisor. Press / or Tab to focus, type a message, and press Enter to send.
The curator responds based on settlement state and your artistic direction. See How the Curator Works.
Your First Settlement
A walkthrough of your first game session, covering what happens in the first 50 ticks and the key decisions that shape your colony.
Starting State
When you begin a new game, you have:
- 5-8 elves with randomized skills, aesthetic positions, and starting needs
- Resources: a small starting stockpile of Wood, Stone, and Food
- No buildings — your elves are in the open
- Season: Spring (the most forgiving season — resource regeneration is doubled)
- Forest Spirit: at 0 (Harmony)
The Dummy Curator is active by default and will auto-assign initial roles based on each elf's highest skill.
First 25 Ticks: Establish Basics
Priority 1: Check Your Elves
Press 1-9 to select each elf, then Enter to see their detail panel. Note:
- Who has the highest Music skill? They should be composing.
- Who has the highest Gathering skill? They should be gathering.
- The curator usually handles this, but check the Work Assignments screen (
w) to verify.
Priority 2: Watch the Events Panel
Press e to open the events panel. Early events tell you what's happening:
- Role assignments from the curator
- Elves starting to forage and gather
- Social interactions as elves meet each other
Priority 3: Start Building
The curator will typically queue a Dwelling first (10 Wood). Dwellings provide shelter and faster rest recovery (+5/tick vs. base rate). Check the Build Management screen (b) to see the queue.
If you have a Builder-role elf, they'll start quarrying stone and constructing automatically.
Suggested early build order:
- Dwelling — shelter from weather, faster rest
- Workshop — composing station, stimulation boost
- Garden — beauty +3/tick, calms forest spirit -2/dawn
Ticks 25-50: First Compositions and Social Bonds
Compositions Begin
Composer-role elves will start creating music once they have enough inspiration. Watch for "composed" events in the event panel. Early compositions are typically low quality (skill level 1-3), but they improve as elves gain XP.
XP note: each level costs level × 50 XP. A level 1 elf needs just 50 XP to reach level 2. See Skills.
Relationships Form
Elves form bonds through proximity and shared experiences. After ~25 ticks, you'll see Friend and occasionally Rival relationships appearing. Elves with similar aesthetic positions bond more easily. See Relationships.
Watch the Needs
Needs decay continuously:
| Need | Decays Every | Watch For |
|---|---|---|
| Rest | Every tick | Elves auto-rest when low |
| Sustenance | Every 4 ticks | Need food stockpile > 0 |
| Beauty | Every 2 ticks | Gardens and forest terrain help |
| Stimulation | Every 2 ticks | Social events and composing |
| Company | Varies | Proximity to friends |
If any elf's morale drops below 50 (Stressed tier), they'll appear with a warning indicator in the roster (r).
First Revel
The Dummy Curator schedules a revel when:
- A Feast Hall exists (15 Wood, 5 Stone), or the curator decides to hold one anyway
- Food stockpile is sufficient (>= 5, or >= 15 in Winter)
- Cooldown from last revel has expired
Revels are the core social event — they satisfy Beauty and Stimulation needs, generate mood modifiers, and reveal how your elves react to each other's compositions.
See Revels for the full lifecycle.
Key Milestones
| Milestone | When | Why It Matters |
|---|---|---|
| First Dwelling built | Ticks 10-30 | Shelter from rain/storms, faster rest |
| First composition | Ticks 15-40 | Art system is active |
| First relationship | Ticks 20-50 | Social bonds affect mood and aesthetics |
| First revel | Ticks 40-80 | Settlement-wide celebration |
| Food stockpile > 15 | Before Winter | Curator won't schedule winter revels without it |
What to Watch For
- Forest Spirit: if you clear too much forest, the spirit meter rises. Stay below 20 for Harmony. See Forest Spirit.
- Discontented elves: if an elf shows the discontented indicator, their satisfaction has been low for 300 ticks. You have 500 more ticks before they depart. Prioritize their needs.
- Seasonal shift: Winter starts at day 75. Foraging drops to ×0.5 (halved again with snow). Summer (×1.5) and Autumn (×1.25) are your stockpiling windows. See Seasons & Weather.
Tips for New Players
- Pause early and often (
Space) — take time to explore screens and understand the UI - Don't clear Ancient Forest unless necessary — it gives the most wood but angers the spirit the most (+5 anger, +7 in Spring)
- Gardens are powerful — +3 beauty/tick for nearby elves AND -2 spirit anger/dawn, even in Winter
- Let the curator work — the Dummy Curator makes reasonable decisions. Override via Work Assignments (
w) only when you disagree - Use artistic direction (
d) to guide the colony's cultural output without micromanaging
Needs & Mood
Overview
Every elf has five basic needs and a mood stack that together determine their morale -- the single number (0-100) that governs behavior, work speed, and departure risk. Needs decay at different rates; mood modifiers stack additively; and morale tiers have hysteresis bands to prevent flickering when an elf hovers near a boundary.
How It Works
Needs
Needs live on a 0-100 scale. Each need decays automatically every tick (or every Nth tick). When a need drops below a threshold, the elf's behavior tree redirects them toward satisfying it before doing productive work.
| Need | Initial Value | Decay Rate | Decay Frequency |
|---|---|---|---|
| Rest | 100 | -1 | Every tick |
| Sustenance | 100 | -1 | Every 4th tick |
| Beauty | 60 | -1 | Every 2nd tick |
| Stimulation | 60 | -1 | Every 2nd tick |
| Company | 50 | varies | Every 2nd tick (personality) |
(Source: crates/er-sim/src/sim/components.rs, Needs::full();
crates/er-sim/src/sim/systems/needs.rs, rest_decay_system(), sustenance_decay_system(),
beauty_decay_system(), stimulation_decay_system(), company_system())
Need Status Labels
The game displays a named tier for each need value:
| Value Range | Label | Compact Char |
|---|---|---|
| 80-100 | Fulfilled | + |
| 50-79 | Fine | ~ |
| 20-49 | Wanting | - |
| 0-19 | Critical | ! |
(Source: src/sim/components.rs, need_label(), need_char())
Company (the 5th Need)
Company is unique: its behavior depends on the elf's Social aesthetic axis (0.0 = Personal, 1.0 = Social).
| Social Axis | Behavior | Recovery |
|---|---|---|
| > 0.7 (Social elf) | Decays -1 per cycle toward 0 | +2 when >= 2 elves within 3 tiles |
| < 0.3 (Personal elf) | Rises toward 100 (wants solitude) | -2 when alone; +1 when >= 3 within 5 tiles |
| 0.3-0.7 (Middle) | Gentle pull toward 50 | +1 if below 50; -1 if above 50 |
The proximity check uses Manhattan distance. Company ticks every 2nd tick, same as Beauty and Stimulation.
(Source: crates/er-sim/src/sim/systems/needs.rs, company_system())
Mood Stack
Mood is a collection of active MoodModifiers. Each modifier has:
description-- human-readable label (e.g. "Ate a meal")value-- signed integer bonus/penalty (i8)ticks_remaining-- countdown to expiry
Every tick, the mood system decrements ticks_remaining by 1 and removes
expired modifiers. Modifiers stack additively.
Morale is computed as:
morale = clamp(base + sum(modifier.value), 0, 100)
where base = 50 (constant).
(Source: src/sim/components.rs, Mood::compute_morale(), Mood::net_mood())
Common Mood Modifiers
| Source | Value | Duration (ticks) |
|---|---|---|
| Ate a meal | +5 | 50 |
| Slept in dwelling | +3 | 100 |
| Slept on ground | -3 | 50 |
| Caught in rain (outdoors) | -1 | 50 |
| Snow-covered landscape | +2 | 50 |
| Awed by ancient grove | +2 | 200 |
| Grieving (mourning) | -5 | mourning duration |
| Spring optimism (seasonal) | +2 | 1 day (100 ticks) |
| Autumn melancholy (seasonal) | -1 | 1 day (100 ticks) |
| Winter stillness (seasonal) | -1 | 1 day (100 ticks) |
| Favorite spot | +1 | 15 ticks |
| Personal time | +2 | 50 ticks |
(Source: crates/er-sim/src/sim/systems/gathering.rs, eating_system(), resting_system();
crates/er-sim/src/sim/systems/mood.rs, weather_mood_system(), seasonal_mood_system();
crates/er-sim/src/sim/systems/satisfaction.rs, favorite_places_system(), personal_time_system();
crates/er-sim/src/sim/components.rs, Mourning)
Replace vs Push Semantics
mood.push()-- always adds a new modifier (stacks with existing same-name entries).mood.replace()-- if a modifier with the same description exists, refreshes its value and duration; otherwise inserts. Weather modifiers use replace to avoid stacking.
(Source: src/sim/components.rs, Mood::push(), Mood::replace())
Values & Formulas
Morale Tiers
| Tier | Morale Range | Effect |
|---|---|---|
| Inspired | 80-100 | Work speed +2; gravitates toward creative work |
| Normal | 50-79 | Standard behavior |
| Stressed | 20-49 | Work speed -1 |
| Breaking | 0-19 | Refuses all non-critical work; idles |
Work Speed Modifier = match morale_state: Inspired => +2, Stressed => -1, else => 0
(Source: crates/er-sim/src/sim/systems/helpers.rs, work_speed_modifier();
crates/er-sim/src/sim/components.rs, morale_state())
Hysteresis Bands
To prevent rapid tier-flickering, transitions require crossing a threshold 3 points beyond the nominal boundary:
| Current Tier | Transition Up | Transition Down |
|---|---|---|
| Breaking | > 23 -> Stressed | -- |
| Stressed | > 53 -> Normal | < 17 -> Breaking |
| Normal | > 83 -> Inspired | < 47 -> Stressed |
| Inspired | -- | < 77 -> Normal |
If no previous tier exists (e.g. first evaluation), the nominal thresholds (80, 50, 20) are used directly.
(Source: src/sim/components.rs, morale_state_with_hysteresis())
Interactions
- Roles -- morale determines which tasks an elf will accept; Breaking-tier elves refuse all non-critical work.
- Buildings -- Dwellings speed rest recovery (+5/tick vs +2 outdoors); buildings within Manhattan distance 2 count as "shelter" for weather mood modifiers.
- Skills -- work speed modifier from morale applies to gathering and building progress rates.
- Terrain -- terrain beauty values passively restore the Beauty need.
- Resources -- eating consumes 1 Food and restores +30 Sustenance (capped at 100).
Tips
- Rest decays fastest (every tick). Prioritize building Dwellings early to keep rest recovery efficient (+5/tick indoors vs +2 outdoors).
- Sustenance decays slowest (every 4th tick). In a pinch, elves can survive a long time on foraging alone.
- Beauty and Stimulation both decay every 2nd tick starting from 60 (not 100). Build a Garden and Workshop early to prevent mid-game mood dips.
- Company is personality-driven. Social elves (Social > 0.7) need companions nearby; personal elves (Social < 0.3) want solitude. Keep an eye on the aesthetic axes of your colony members.
- Hysteresis means momentum matters. An elf who climbs to Inspired will stay there until morale drops below 77. Plan mood boosts in clusters rather than spreading them thin.
- Weather modifiers use replace semantics, so you will never see "Caught in rain" stacked five times. But one-time events (first ancient grove visit) do stack with ongoing weather effects.
Resources
Overview
The settlement stockpile holds four resource types used for construction, feeding, and crafting. Resources are gathered from map sources, deposited at buildings, and consumed by various systems. Seasonal multipliers and passive foraging prevent hard starvation while still making resource management meaningful.
How It Works
Resource Types
| Resource | Starting Stock | Description |
|---|---|---|
| Food | 50 | Consumed when eating; prevents starvation |
| Wood | 30 | Primary building material |
| Stone | 10 | Used for Workshops and Feast Halls |
| FineWood | 3 | Rare material (reserved for future crafting) |
(Source: src/sim/world.rs, Resources::starting())
Gathering Pipeline
The full resource loop is:
- Task Decision -- an idle elf decides to gather a resource type
based on role, policy priority, or the
most_needed_resource()fallback. - Pathfinding -- the elf walks to the nearest
ResourceSourceof that type. - Gathering -- once at the source and with no remaining path, the elf accumulates progress each tick.
- Pickup -- at progress >= 100, the elf picks up 5 units of the resource and the source loses 1 amount.
- Return -- the elf pathfinds back to the nearest building.
- Deposit -- at a building tile, carried resources are added to the stockpile.
Gathering Progress Rate = (5 + gathering_skill) + work_speed_modifier
(minimum 1)
At completion, the elf gains 10 XP toward the Gathering skill.
(Source: crates/er-sim/src/sim/systems/gathering.rs, gathering_system(), deposit_system(),
return_to_stockpile_system())
Most-Needed Resource Fallback
When no role or policy dictates a specific resource, the system picks the resource with the lowest current stock:
if food <= wood && food <= stone -> Food
else if wood <= stone -> Wood
else -> Stone
FineWood is never selected by this fallback.
(Source: crates/er-sim/src/sim/systems/helpers.rs, most_needed_resource())
Resource Priority Override
Cultural policies can set a resource_priority list. If the top-priority
resource has stock < 10, it overrides the elf's role-based gathering choice.
(Source: crates/er-sim/src/sim/systems/behavior.rs, decide_default())
Values & Formulas
Passive Foraging
Elves with Sustenance < 40 passively forage every 10th tick when standing on a walkable, non-stone, non-wall tile. This is an automatic safety net -- no task decision required.
| Terrain | Forage Amount | Cap |
|---|---|---|
| Forest (Ancient or Young) | +8 | 60 |
| Other walkable | +5 | 60 |
Foraging restores Sustenance directly (not stockpile Food). It caps at 60, preventing full satisfaction from foraging alone.
(Source: crates/er-sim/src/sim/systems/gathering.rs, foraging_system())
Eating
When an elf performs the Eat task at a building with Food in the stockpile:
- Consumes 1 Food from stockpile
- Restores +30 Sustenance (capped at 100)
- Applies mood modifier: "Ate a meal" +5 for 50 ticks
(Source: crates/er-sim/src/sim/systems/gathering.rs, eating_system())
Season Foraging Multiplier
The climate system provides a seasonal multiplier that affects foraging yields:
| Season | Multiplier |
|---|---|
| Spring | 1.0x |
| Summer | 1.5x |
| Autumn | 1.25x |
| Winter | 0.5x |
(Source: src/sim/climate.rs, Climate::season_foraging_multiplier())
Note: Seasons last 25 days each (100 days per year).
(Source: src/sim/climate.rs, SEASON_LENGTH, YEAR_LENGTH)
Resource Source Regeneration
At dawn each day, resource sources regenerate +1 amount, up to a cap:
| Source Type | Regeneration Cap |
|---|---|
| FineWood | 5 |
| All others | 10 |
(Source: crates/er-sim/src/sim/systems/building.rs, regeneration_system())
Gathering Yield
When gathering completes (progress reaches 100):
- Elf picks up 5 units of the resource
- Source loses 1 amount
- Elf gains 10 Gathering XP
(Source: crates/er-sim/src/sim/systems/gathering.rs, gathering_system())
Daily Rate Tracking
The stockpile tracks net change per resource per day. At each dawn,
Resources::snapshot_day() computes the delta from the previous day's
snapshot. This powers the daily rate display in the UI.
(Source: src/sim/world.rs, Resources::snapshot_day(), Resources::daily_rate())
Interactions
- Needs & Mood -- Sustenance below 20 triggers critical need behavior; eating gives a +5 mood boost.
- Roles -- Gatherer role defaults to Wood; Builder role defaults to Stone. Resource priority can override both.
- Buildings -- construction consumes resources from the stockpile; deposits require proximity to a building.
- Skills -- Gathering skill increases collection speed via the progress rate formula.
- Terrain -- foraging yields differ by terrain type; forest tiles give +8, other walkable tiles give +5.
Tips
- Build a Feast Hall early -- elves performing the Eat task need to be at a building tile. Without buildings, the eating pipeline stalls even with Food in the stockpile.
- Watch the daily rates. Negative Food rate means you are consuming faster than you gather. Assign a Gatherer role or raise Food in the priority list.
- FineWood is scarce. Sources cap at 5 and it starts at only 3 in the stockpile. Treat it as a strategic reserve.
- Foraging caps at 60 Sustenance. Elves can survive on foraging alone, but they will never be "Fulfilled" (80+) without proper meals from the stockpile.
- Winter halves foraging. Stockpile Food in Autumn (1.25x) to buffer the Winter deficit (0.5x).
- Resource priority < 10 triggers override. If your top-priority resource drops below 10, all Unassigned elves switch to gathering it regardless of their normal behavior.
Roles
Overview
Each elf can be assigned one of four roles that influence their task selection,
default gathering target, and behavior under the Cultural Policies system.
Roles are assigned by the patron (or AI curator) and stored in the
CulturalPolicies::workshop_assignments map. Unassigned elves follow a
general-purpose behavior tree.
How It Works
Role Types
| Role | Description |
|---|---|
| Unassigned | Default. Follows the general behavior tree; gathers the most-needed resource. |
| Composer | Gravitates toward composing at a Workshop when no urgent needs exist. |
| Builder | Prioritized for build queue assignments; default gather target is Stone. |
| Gatherer | Default gather target is Wood; handles general resource collection. |
(Source: src/sim/components.rs, enum ElfRole)
Role Assignment
Roles are set via CulturalPolicies::workshop_assignments, a map from elf
name to ElfRole. If an elf's name is not in the map, they default to
ElfRole::Unassigned.
role = workshop_assignments.get(name).unwrap_or(Unassigned)
(Source: src/sim/world.rs, CulturalPolicies::role_for())
Role-Resource Linkage
Each role has a default resource it will gather when sent to decide_default():
| Role | Default Resource |
|---|---|
| Gatherer | Wood |
| Builder | Stone |
| Composer | None (uses policy priority or most-needed) |
| Unassigned | None (uses policy priority or most-needed) |
(Source: src/sim/world.rs, CulturalPolicies::role_resource())
Values & Formulas
Task Decision Priority
The behavior tree (task_decision_system) evaluates priorities in strict
order. Roles only matter at Steps 3c and 4 -- critical needs always come
first.
| Priority | Condition | Action |
|---|---|---|
| Step 0: Preempt | Sustenance or Rest critical (< 20, or < 5 for construction) | Cancel current task, go idle |
| Step 1: Critical Needs | Sustenance < 20 | Eat (if Food available) or Gather Food |
| Rest < 20 | Rest (seek Dwelling) | |
| Step 1b: Discontented | Elf has Discontented marker | Only gather Food or idle; refuses creative/ambitious work |
| Step 1c: Mourning | Elf has Mourning marker | Compose at Workshop (tribute); Wander if no Workshop. Skips gathering/building. |
| Step 2: Creative Block | Elf has CreativeBlock | Seek Garden (if available) |
| Step 3: Moderate Needs | Beauty < 30 or Stimulation < 30 | Compose (Stimulation) or SeekGarden (Beauty), whichever is lower |
| Step 3b: Aspirations | Aspiration wants compose/socialize | Compose at Workshop or Wander to social buildings |
| Step 3c: Skill-Driven | Music skill >= 7 | Compose at Workshop |
| Building skill >= 7 + build queue not empty | Build | |
| Step 3d: Personal Time | 5% random chance | Seek favorite place or Garden for beauty/inspiration |
| Step 4: Cultural Policy | Role == Composer | Compose at Workshop |
| Any other role | decide_default() | |
| Breaking Morale | Morale < 20 (inserted between Steps 3 and 3b) | Idle (refuse all non-critical work) |
(Source: crates/er-sim/src/sim/systems/behavior.rs, task_decision_system())
Default Behavior (decide_default)
When an elf reaches the default branch:
- Check
resource_priority[0]-- if that resource stock < 10, gather it (override). - Otherwise, if the elf's role has a
role_resource, gather that. - Otherwise, use
resource_priority[0]if set. - Otherwise, gather
most_needed_resource()(lowest stock of Food/Wood/Stone). - If no source exists for the chosen resource, fall back to most-needed.
- If still no source, Wander.
(Source: crates/er-sim/src/sim/systems/behavior.rs, decide_default())
Build Queue Assignment
When the build queue is non-empty and resources are available:
- Prefer idle elf with Builder role.
- Then any idle elf.
- Then any Builder-role elf on a non-critical task (Sustenance >= 40 and Rest >= 40).
- Only one elf builds at a time (no double-assignment).
(Source: crates/er-sim/src/sim/systems/building.rs, build_queue_system())
Preemption Rules
Active tasks can be interrupted when needs become critical:
| Current Task | Preempt Threshold |
|---|---|
| Gather, Compose, SeekGarden, Wander | Sustenance < 20 OR Rest < 20 |
| Build, ClearForest | Sustenance < 5 OR Rest < 5 |
When preempted, the elf's task is set to Idle, their path is canceled, and any carried resources are dropped (lost). Construction tasks get a lower preemption threshold to avoid interrupting nearly-finished builds.
(Source: crates/er-sim/src/sim/systems/behavior.rs, task_decision_system(), Phase 0)
Revel Attendance Override
Elves attending an active revel skip the entire behavior tree. The
RevelAttending marker component is checked first, and those elves are
excluded from all task decisions until the revel ends.
(Source: crates/er-sim/src/sim/systems/behavior.rs, task_decision_system())
Role Behavior Summary Table
| Role | Idle Behavior | When Needs OK | When Needs Critical |
|---|---|---|---|
| Unassigned | Gather most-needed resource | Follow policy priority | Eat/Rest |
| Composer | Compose at Workshop | Compose at Workshop | Eat/Rest |
| Builder | Gather Stone | Assigned to build queue first | Eat/Rest |
| Gatherer | Gather Wood | Gather Wood | Eat/Rest |
Interactions
- Needs & Mood -- critical needs always override role behavior; morale tier affects willingness to work.
- Resources -- role determines which resource an elf gathers by default; resource priority can override role.
- Skills -- high Music skill (>= 7) self-selects to Compose regardless of role; high Building skill (>= 7) self-selects to Build.
- Buildings -- Builders are prioritized for the build queue; Composers require a Workshop.
Tips
- Composer is the only role that directly affects task selection at Step 4. Builder and Gatherer mostly influence which resource is gathered in the default branch.
- Skill-driven preferences (Step 3c) bypass role. An elf with Music 7+ will self-select to compose even if unassigned. Assign roles primarily for lower-skill elves.
- Aspirations (Step 3b) also bypass role. Elves pursuing a "compose works" aspiration will compose without needing the Composer role.
- Builders get pulled from non-critical tasks to fill the build queue. Make sure your Builder has adequate Sustenance and Rest (>= 40 each) or they will be skipped.
- Unassigned is not idle. Unassigned elves still gather the most-needed resource or follow the resource priority list. In a small settlement, leaving everyone Unassigned is a valid strategy.
- Resource priority override is powerful. Setting a priority resource effectively overrides all non-Builder roles when that resource drops below 10 units.
Skills
Overview
Every elf has four skill proficiencies -- Music, Building, Gathering, and Stewardship -- each on a 1-10 scale. Skills level up through XP accumulation, improve task efficiency, and influence composition quality. At higher levels, skills can drive autonomous behavior: a Music-7 elf will self-select to compose even without a role assignment.
How It Works
Skill Types
| Skill | Governs | Key Threshold |
|---|---|---|
| Music | Composition speed, quality (mastery score) | >= 7: self-selects to compose |
| Building | Construction speed, building XP gains | >= 7: self-selects to build (if queue non-empty) |
| Gathering | Resource collection speed | -- |
| Stewardship | Animal bonding speed, bond eligibility | >= 3: can bond with Tolerant species |
All skills start at level 1 with 0 XP.
(Source: src/sim/components.rs, struct Skills, impl Default for Skills)
XP and Leveling
XP accumulates per-skill. When accumulated XP reaches the threshold for the current level, the elf levels up and excess XP carries over.
XP Threshold = level x 50
(Source: src/sim/components.rs, Skills::xp_threshold())
The maximum skill level is 10. XP gains are ignored at level 10.
(Source: src/sim/components.rs, Skills::add_xp())
Full XP Table
| Level | XP to Next Level | Cumulative XP |
|---|---|---|
| 1 | 50 | 0 |
| 2 | 100 | 50 |
| 3 | 150 | 150 |
| 4 | 200 | 300 |
| 5 | 250 | 500 |
| 6 | 300 | 750 |
| 7 | 350 | 1,050 |
| 8 | 400 | 1,400 |
| 9 | 450 | 1,800 |
| 10 | -- (max) | 2,250 |
Formula: Cumulative XP to reach level L = sum(i=1..L-1) of i*50 = 25 * L * (L-1).
XP Sources
| Activity | Skill | XP per Event |
|---|---|---|
| Complete a gathering task | Gathering | +10 |
| Complete a building task | Building | +15 |
| While composing (per tick) | Music | +1 |
| Near a bondable animal | Stewardship | +2 |
| Reaching companion bond (60+) | Stewardship | +20 (bonus) |
(Source: crates/er-sim/src/sim/systems/gathering.rs, gathering_system();
crates/er-sim/src/sim/systems/building.rs, building_progress_system();
crates/er-sim/src/sim/systems/compose.rs, compose_system();
crates/er-sim/src/sim/fauna_systems.rs, animal_bond_system())
Values & Formulas
Gathering Progress
progress_rate = (5 + gathering_skill) + work_speed_modifier
Progress starts at 0 and completes at 100. At completion, the elf picks up 5 units and gains 10 Gathering XP.
work_speed_modifier: Inspired = +2, Normal = 0, Stressed = -1. Minimum rate is 1.
| Gathering Skill | Ticks to Complete (Normal morale) |
|---|---|
| 1 | 17 |
| 3 | 13 |
| 5 | 10 |
| 7 | 9 |
| 10 | 7 |
(Source: crates/er-sim/src/sim/systems/gathering.rs, gathering_system(); crates/er-sim/src/sim/systems/helpers.rs, work_speed_modifier())
Building Progress
progress_rate = (5 + building_skill) + work_speed_modifier
Same formula as gathering. Completes at 100. Grants 15 Building XP.
| Building Skill | Ticks to Complete (Normal morale) |
|---|---|
| 1 | 17 |
| 3 | 13 |
| 5 | 10 |
| 7 | 9 |
| 10 | 7 |
(Source: crates/er-sim/src/sim/systems/building.rs, building_progress_system())
Composition Speed
Composition duration scales inversely with Music skill:
duration = max(50 - music_skill * 2, 30)
Progress rate per tick = 100 / duration (minimum 1).
| Music Skill | Duration (ticks) | Progress/tick |
|---|---|---|
| 1 | 48 | 2 |
| 3 | 44 | 2 |
| 5 | 40 | 2 |
| 7 | 36 | 2 |
| 10 | 30 | 3 |
(Source: src/sim/art.rs, compose_duration())
Composition Quality
Music skill directly drives the mastery quality axis:
mastery = clamp(music_skill * 10 + random(-5..+5), 0, 100)
A level-10 musician produces mastery scores around 95-105 (clamped to 100). A level-1 musician produces mastery around 5-15.
Originality depends on inspiration total; emotional depends on net mood.
(Source: src/sim/art.rs, compute_quality())
Stewardship and Animal Bonding
Stewardship governs how quickly an elf can bond with settlement animals and which species they can bond with at all.
Bond growth rate = 1 + (stewardship_level / 3), clamped to 1--5 per bonding tick (every 50 ticks).
| Stewardship | Bond Growth/Tick | Can Bond Tolerant Species? |
|---|---|---|
| 1 | 1 | No |
| 2 | 1 | No |
| 3 | 2 | Yes |
| 5 | 2 | Yes |
| 7 | 3 | Yes |
| 10 | 4 | Yes |
Curious species can be bonded by any elf. Tolerant species require Stewardship >= 3. Shy species cannot be bonded.
When an animal bond reaches strength 60, the animal becomes a companion -- it receives a name and the elf gets a +20 Stewardship XP bonus.
See Fauna for the full animal bonding system.
(Source: crates/er-sim/src/sim/fauna_systems.rs, animal_bond_system())
Skill-Driven Behavior Thresholds
| Skill | Level | Behavior |
|---|---|---|
| Music | >= 7 | Self-selects to Compose (Step 3c in behavior tree) |
| Building | >= 7 | Self-selects to Build when build queue is non-empty |
These thresholds bypass role assignment -- the elf follows their expertise automatically.
(Source: crates/er-sim/src/sim/systems/behavior.rs, task_decision_system(), Step 3c skill-driven branch)
Interactions
- Roles -- roles provide a suggestion for default behavior; high skill levels provide an override at Step 3c.
- Needs & Mood -- morale affects work speed modifier: Inspired (+2), Stressed (-1).
- Resources -- gathering skill controls how fast resources are collected.
- Buildings -- building skill controls construction speed; completion awards 15 XP.
- Fauna -- stewardship skill gates bonding with settlement animals. Higher levels bond faster and can bond Tolerant species.
Tips
- XP scales quadratically. Getting from level 1 to 5 takes 550 cumulative XP. Getting from 5 to 10 takes another 1,950. Late levels are a long grind.
- Music XP trickles in at 1/tick while composing. Even at the slow rate, a composer working full-time levels up steadily. Building XP comes in bigger chunks (15 per completion) but less frequently.
- Level 7 is the magic number. At Music 7 or Building 7, elves start self-directing. This is the threshold where specialization pays off without needing explicit role assignment.
- Work speed modifier matters most at low skill. The Inspired +2 bonus on a skill-1 elf increases their rate from 6 to 8 (33% faster). On a skill-10 elf, it goes from 15 to 17 (13% faster). Keep your rookies happy.
- Composition quality tracks mastery closely with music skill. A skill-10 composer produces consistently excellent mastery. Invest in your top musician for the best compositions.
Terrain
Overview
The map is a 2D grid of terrain tiles that determine movement, beauty, foraging yields, and resource placement. Six terrain types form three functional zones: the settlement clearing (Meadow), the surrounding forest ring (Ancient and Young Forest), and the outer wilderness (mixed, with obstacles).
How It Works
Terrain Types
| Terrain | Glyph | Walkable | Is Forest | Beauty Value |
|---|---|---|---|---|
| Meadow | . | Yes | No | 0 |
| Stone | # | Yes | No | 0 |
| Water | ~ | No | No | 0 |
| AncientForest | T | Yes | Yes | 2 |
| YoungForest | t | Yes | Yes | 1 |
| Wall | X | No | No | 0 |
(Source: src/sim/components.rs, enum Terrain, walkable(), is_forest(),
beauty_value())
Walkability
Elves can move on any tile except Water and Wall. The pathfinding system uses walkability to compute valid routes.
walkable = !matches!(terrain, Water | Wall)
(Source: src/sim/components.rs, Terrain::walkable())
Beauty Restoration
Each tick, the terrain_effect_system checks the elf's current tile and all
four adjacent tiles (N, S, E, W). The highest beauty value among these
five tiles is added to the elf's Beauty need.
beauty_gain = max(current_tile.beauty_value, adjacent_tiles.beauty_value)
Seasonal overrides:
| Terrain | Season | Effective Beauty |
|---|---|---|
| AncientForest | Autumn | 3 (base 2 + autumn color bonus) |
| YoungForest | Winter | 0 (bare branches, normally 1) |
| All others | Any | Base value |
(Source: crates/er-sim/src/sim/systems/terrain.rs, terrain_effect_system())
Stream Adjacency
If any adjacent tile is Water, the elf gains a solitude inspiration bonus (+1 every 10 ticks).
(Source: crates/er-sim/src/sim/systems/terrain.rs, terrain_effect_system())
Inspiration from Terrain
Every 10 ticks:
| Terrain | Inspiration Gain |
|---|---|
| AncientForest | +1 Nature inspiration |
| YoungForest | +1 Nature inspiration (every 20 ticks only) |
| Adjacent Water | +1 Solitude inspiration |
(Source: crates/er-sim/src/sim/systems/terrain.rs, terrain_effect_system())
First-Visit Mood
The first time an elf enters an AncientForest tile, they receive:
- Mood modifier: "Awed by ancient grove" +2 for 200 ticks
This is tracked per-elf via VisitedTerrains and only fires once.
(Source: crates/er-sim/src/sim/systems/terrain.rs, terrain_effect_system();
src/sim/components.rs, VisitedTerrains)
Values & Formulas
Foraging Yields by Terrain
| Terrain | Forage Amount | Condition |
|---|---|---|
| Forest (Ancient or Young) | +8 Sustenance | Sustenance < 40, every 10th tick |
| Other walkable (Meadow, Stone) | +5 Sustenance | Sustenance < 40, every 10th tick |
| Water, Wall | 0 (not walkable) | -- |
Foraging caps at 60 Sustenance (elves cannot reach "Fulfilled" from foraging). Stone is explicitly excluded from foraging despite being walkable.
Wait -- re-reading the source: Stone is excluded in foraging_system() by the
check !matches!(terrain, Terrain::Stone | Terrain::Wall).
| Terrain | Forageable |
|---|---|
| Meadow | Yes (+5) |
| AncientForest | Yes (+8) |
| YoungForest | Yes (+8) |
| Stone | No |
| Water | No |
| Wall | No |
(Source: crates/er-sim/src/sim/systems/gathering.rs, foraging_system())
Resource Sources
Resource sources are separate entities placed on map tiles. Their type determines what can be gathered there. Sources regenerate +1 per dawn, up to a cap of 10 (5 for FineWood).
(Source: crates/er-sim/src/sim/systems/building.rs, regeneration_system())
Map Generation
Maps are generated with three concentric zones:
Zone 1: Settlement Clearing (center)
- Radius:
max(min(width, height) * 0.15, 5.0)tiles from center - Mostly Meadow
- A narrow stream (Water) runs through the center
Zone 2: Forest Ring
- Extends 3 tiles beyond the clearing radius
- 65% forest (inner half = AncientForest, outer half = YoungForest)
- 10% Stone
- 25% Meadow
Zone 3: Outer Wilderness
- Everything beyond the forest ring
- Distribution:
| Terrain | Probability |
|---|---|
| Wall | 2% |
| Water | 5% |
| Stone | 8% |
| YoungForest | 15% |
| Meadow | 70% |
(Source: src/sim/map.rs, generate_map())
Default map size: 40 x 30.
Interactions
- Needs & Mood -- terrain beauty restores the Beauty need; first-visit moods add to the mood stack.
- Resources -- foraging yields vary by terrain; resource sources are placed on specific tiles.
- Buildings -- buildings are placed on walkable tiles; the settlement clearing provides the main building area.
- Skills -- terrain does not directly affect skill gains, but proximity to forest tiles enables passive beauty restoration that keeps elves happy and productive.
Tips
- AncientForest is the most valuable terrain. Beauty 2, +1 Nature inspiration every 10 ticks, first-visit mood bonus, and best foraging yield. Build paths (or settle) near AncientForest tiles.
- Autumn is peak beauty season. AncientForest beauty jumps to 3 in Autumn. Plan revels and creative work for this season.
- Winter kills YoungForest beauty. Bare branches mean 0 beauty value. Make sure you have a Garden or AncientForest access to compensate.
- The center stream is a double-edged tile. Water blocks movement but gives adjacent elves solitude inspiration. Build near it, not on it.
- Stone terrain is walkable but not forageable. Elves can cross Stone tiles freely but will not passively forage there.
- Wall tiles (2% of outer wilderness) are impassable obstacles. They can block pathfinding to outer resource sources. Scout the map for bottlenecks.
Buildings
Overview
Buildings are permanent structures placed on the map via the build queue. They provide shelter from weather, enable critical tasks (eating, resting, composing), and buff nearby elves. Four building types cover the settlement's needs: housing, crafting, aesthetics, and communal gathering.
How It Works
Building Types
| Building | Description |
|---|---|
| Dwelling | Living quarters. Speeds rest recovery. Counts as shelter. |
| Workshop | Crafting space. Required for composing. Counts as shelter. |
| Garden | Beauty and creative block recovery. Does NOT count as shelter. |
| Feast Hall | Communal eating. Social gathering space. Counts as shelter. |
(Source: src/sim/components.rs, enum BuildingType)
Build Costs
| Building | Wood | Stone | FineWood |
|---|---|---|---|
| Dwelling | 10 | -- | -- |
| Workshop | 10 | 10 | -- |
| Garden | 5 | -- | -- |
| Feast Hall | 15 | 5 | -- |
(Source: crates/er-sim/src/sim/systems/building.rs, build_cost())
Build Queue
Buildings are constructed through the build queue system:
- The patron or AI curator adds a
QueuedBuild(type + site position) toCulturalPolicies::build_queue. build_queue_system()checks if the settlement can afford the top item.- If affordable, it assigns an idle elf (preferring Builders) and deducts resources immediately.
- The assigned elf pathfinds to the build site and begins construction.
- Only one build is active at a time (no double-assignment).
(Source: crates/er-sim/src/sim/systems/building.rs, build_queue_system())
Builder Assignment Priority
When a build is ready to start:
| Priority | Candidate |
|---|---|
| 1st | Idle elf with Builder role |
| 2nd | Any idle elf |
| 3rd | Builder-role elf on a non-critical task (Sustenance >= 40, Rest >= 40) |
Builder-role elves on critical tasks (Eat, Rest, Build, ClearForest) or with low needs are not reassigned.
(Source: crates/er-sim/src/sim/systems/building.rs, build_queue_system())
Values & Formulas
Construction Progress
progress_rate = (5 + building_skill) + work_speed_modifier
Construction completes when progress reaches 100. Minimum rate is 1.
work_speed_modifier: Inspired = +2, Normal = 0, Stressed = -1.
| Building Skill | Ticks to Complete (Normal) | Ticks (Inspired) | Ticks (Stressed) |
|---|---|---|---|
| 1 | 17 | 13 | 20 |
| 3 | 13 | 10 | 15 |
| 5 | 10 | 8 | 12 |
| 7 | 9 | 7 | 10 |
| 10 | 7 | 6 | 8 |
On completion, the builder gains 15 Building XP.
(Source: crates/er-sim/src/sim/systems/building.rs, building_progress_system();
crates/er-sim/src/sim/components.rs, Skills::add_xp())
Shelter Radius
Buildings classified as shelter (Dwelling, Workshop, Feast Hall) protect elves within Manhattan distance <= 2 from weather penalties.
is_indoors = any shelter building within Manhattan distance 2 of elf
Garden does not count as shelter.
(Source: crates/er-sim/src/sim/systems/mood.rs, weather_mood_system())
Weather Mood Effects (Shelter-Dependent)
| Weather | Outdoors Effect | Indoors Effect |
|---|---|---|
| Rain | "Caught in rain" -1 (50 ticks) | None |
| Snow | "Snow-covered landscape" +2 (50 ticks) | None |
| Storm | "Sheltering from storm" +0 (50 ticks) | "Sheltering from storm" +0 (50 ticks) |
(Source: crates/er-sim/src/sim/systems/mood.rs, weather_mood_system())
Rest Recovery
Resting elves recover at different rates depending on proximity to a Dwelling:
| Location | Rest Recovery Rate | Mood on Completion |
|---|---|---|
| At Dwelling | +5 per tick | "Slept in dwelling" +3 (100 ticks) |
| Outdoors | +2 per tick | "Slept on ground" -3 (50 ticks) |
Resting completes when Rest reaches 80 ("Fulfilled" threshold).
(Source: crates/er-sim/src/sim/systems/gathering.rs, resting_system())
Eating
Elves perform the Eat task at any building tile (not just Feast Halls). Eating consumes 1 Food from the stockpile and restores +30 Sustenance.
(Source: crates/er-sim/src/sim/systems/gathering.rs, eating_system())
Composing
Composing requires presence at a Workshop tile with no remaining path. Composition duration scales with Music skill (see Skills for the formula).
(Source: crates/er-sim/src/sim/systems/compose.rs, compose_system())
Garden and Creative Block
Elves with Creative Block seek a Garden. The Garden does not directly
restore beauty via a building buff -- instead, Gardens are typically placed
near forest tiles where the terrain_effect_system provides passive beauty
restoration. Creative Block drives the elf to the Garden as a waypoint.
(Source: crates/er-sim/src/sim/systems/behavior.rs, task_decision_system(), Step 2)
Interactions
- Resources -- build costs are deducted from the stockpile when construction starts; deposits require proximity to any building.
- Needs & Mood -- Dwellings provide faster rest recovery (+5 vs +2) and a positive mood modifier; sheltered buildings prevent rain mood penalties.
- Roles -- Builder role elves are prioritized for construction assignments.
- Skills -- Building skill determines construction speed; completion grants 15 XP.
- Terrain -- buildings must be placed on walkable tiles; Gardens are most effective near forest terrain for beauty synergy.
Tips
- Build order matters. A Dwelling first ensures Rest recovery is efficient. A Workshop second enables composing and Stimulation restoration. Garden third handles Beauty needs.
- Feast Hall is expensive but versatile. At 15 Wood + 5 Stone, it is the costliest building. But it serves as shelter, eating location, and social gathering point for elves pursuing social aspirations.
- Garden is cheap and essential. At only 5 Wood, it is the most cost-effective building. Place it adjacent to AncientForest tiles to maximize beauty synergy.
- Shelter radius is Manhattan 2. Buildings placed 1 tile apart create overlapping shelter zones. Cluster buildings to cover the most elves.
- One build at a time. The system prevents multiple simultaneous constructions. Queue your builds in priority order; the next one starts automatically when the current one finishes.
- Resources are deducted immediately when a build starts, not when it finishes. If a builder gets interrupted, the resources are still consumed. Make sure your builder has adequate needs before starting expensive constructions.
- Outdoor resting gives a mood penalty. Without a Dwelling, elves who rest get "Slept on ground" (-3 for 50 ticks). This stacks with weather effects and can spiral morale downward in early game.
Compositions
Overview
Compositions are the primary creative output of an elven settlement. When an elf with the Composer role (or any elf choosing to compose) finishes a composing task, the simulation generates a unique musical work with a procedurally generated name, title, description, quality scores, and special properties. Compositions are permanent artifacts stored in the settlement's portfolio and performed at Revels.
Every composition encodes the emotional and aesthetic state of its creator at the moment of creation. Two compositions by the same elf in different moods will sound completely different.
How It Works
Composing Duration
The time to compose is determined by music skill. Higher skill means faster work, but there is a floor of 30 ticks:
Duration = max(50 - skill x 2, 30) ticks
| Music Skill | Compose Time (ticks) |
|---|---|
| 1 | 48 |
| 5 | 40 |
| 8 | 34 |
| 10 | 30 |
Source: src/sim/art.rs, compose_duration
Genre Families
Every composition belongs to one of three genre families, selected based on the composer's Aesthetic Position and Inspiration.
| Genre | Description | Selection Bias |
|---|---|---|
| Traditional | Classical forms, familiar structures | High tradition axis |
| Radical | Avant-garde, experimental | Low tradition axis |
| Pastoral | Nature-inspired, ambient | High Nature inspiration (>40) |
Genre Selection Algorithm
Genre selection proceeds in two stages: a cross-genre surprise check, then a weighted probability roll with seasonal bias.
Stage 1 -- Cross-genre surprise (8%)
Before any probability calculation, the system rolls a uniform random check. If the roll is below 0.08 (8% chance), a genre is picked uniformly at random from all three families, ignoring the composer's aesthetic position, inspiration, and season entirely. This means roughly once per 20-day session, even a deeply traditional elf might compose something radical.
Source: src/sim/art.rs, select_genre_seasonal -- if surprise < 0.08 at line 97.
Stage 2 -- Weighted probability
If the surprise check does not fire (92% of the time), base probabilities are computed from the composer's state:
| Probability | Formula | Notes |
|---|---|---|
p_radical | max((1.0 - tradition - 0.5) x 1.5, 0.0) | Rises as tradition axis falls; ~0% at tradition 0.83+, ~15% at tradition 0.5, ~75% at tradition 0.0 |
p_nature | 0.6 if Nature is dominant source AND nature > 40; else 0.1 | Requires both conditions; dominant source alone is not enough |
p_traditional | max(1.0 - p_radical - p_nature, 0.0) | Remainder after radical and pastoral claims |
All three probabilities are then adjusted by seasonal bias:
| Season | Adjustment |
|---|---|
| Winter | p_nature += 0.2 (Pastoral +20%) |
| Summer | p_radical += 0.2 (Radical +20%) |
| Spring | p_traditional += 0.1 (Traditional +10%) |
| Autumn | No adjustment (most varied season) |
After bias is applied, the three values are summed to a total and a weighted
random roll selects the genre. The probabilities are not renormalized to 1.0
before rolling -- the roll range is [0, total), so seasonal bias effectively
increases the absolute weight of one family rather than redistributing from
others.
Example at tradition=0.5, no nature dominance, Summer:
| Family | Base | Seasonal | Effective share |
|---|---|---|---|
| Traditional | 0.15 | +0 | 0.15 / 1.45 = ~10% |
| Radical | 0.75 | +0.2 | 0.95 / 1.45 = ~66% |
| Pastoral | 0.10 | +0 | 0.10 / 1.45 = ~7% |
| Cross-genre surprise (pre-roll) | 8% (uniform) |
Source: src/sim/art.rs, select_genre (base logic, lines 55--85) and select_genre_seasonal (with season, lines 89--128)
Base Types (Skill Tiers)
Within each genre, the composer's music skill determines which tier of base type is available. There are 4 tiers per genre (12 total):
| Skill | Tier | Traditional | Radical | Pastoral |
|---|---|---|---|---|
| 0-3 | Apprentice | Lullaby, Hymn, Folk Song, Ditty, Canticle, Air | Chant Riff, Drum Circle, Voice Loop, Spoken Word | Birdsong, Creek Melody, Windchime, Leaf Rustle |
| 4-6 | Journeyman | Ballad, Rondo, Serenade, Nocturne, Madrigal, Minuet | Blues Lament, Groove, Free Verse, Syncopation | Storm Song, Forest Waltz, River Suite, Dawn Chorus |
| 7-9 | Master | Sonata, Concerto, Rhapsody, Aubade, Fantasia, Elegy | Jazz Standard, Rock Anthem, Beat Poem, Fusion | Thunder Concerto, Tide Rhapsody, Season Cycle |
| 10+ | Grandmaster | Requiem, Symphony, Magnum Opus, Oratorio | Noise Symphony, Rap Epic, Punk Requiem, Ambient Opus | World Song, Elements Symphony, Celestial Harmony, Earthsong, Aurora Opus |
Source: src/sim/art.rs, select_base_type
Values & Formulas
Quality Scores
Each composition has three quality dimensions scored 0-100:
| Dimension | Driver | Formula |
|---|---|---|
| Mastery | Music skill | skill x 10 + random(-5..+5), clamped 0-100 |
| Originality | Inspiration total | inspiration_total x 0.6 + random(0..20), clamped 0-100 |
| Emotional | Net mood | mood x 0.5 + 30 + random(0..10), clamped 0-100 |
Average quality = (mastery + originality + emotional) / 3
| Average Quality | Tier Label |
|---|---|
| 90-100 | Transcendent |
| 70-89 | Masterful |
| 50-69 | Skilled |
| 25-49 | Modest |
| 0-24 | Crude |
Elegy bonus: Compositions created while an elf is mourning a departed friend receive +15 to the emotional score.
Source: src/sim/art.rs, compute_quality; src/sim/components.rs, quality_avg, quality_tier
Composition Properties
Properties are special tags earned from creation conditions. Each composition carries up to 2 behavioral properties plus an always-present seasonal property and an optional Stormborn premiere marker.
Condition properties (checked in order):
| Property | Condition |
|---|---|
| Cathartic | Composer has an active creative block |
| Transcendent | Total inspiration > 90 |
| Debut | First composition ever (empty portfolio) |
| Magnum Opus | Skill 10 AND inspiration > 90 AND mood > 50 (once per lifetime) |
| Masterwork | Skill >= 10 (if Magnum Opus not triggered) |
| Elegy | Composed while mourning a departed friend |
Compound inspiration properties (require two channels both > 30):
| Property | Channels Required |
|---|---|
| Manifesto | Rivalry > 30 AND Social > 30 |
| Obsessive | Rivalry > 30 AND Solitude > 30 |
| Communal | Nature > 30 AND Social > 30 |
| Timeless | Nature > 30 AND Solitude > 30 |
| Virtuosic | Beauty > 30 AND Rivalry > 30 |
| Sublime | Beauty > 30 AND Solitude > 30 |
| Bittersweet | Social > 30 AND Solitude > 30 |
| Enchanted | Nature > 30 AND Beauty > 30 |
| Anthem | Social > 30 AND Beauty > 30 |
Source: crates/er-sim/src/sim/systems/compose.rs; crates/er-sim/src/sim/components.rs, CompositionProperty
Seasonal Properties
Every composition automatically receives one seasonal property based on the season it was completed in. This does not count toward the 2-property cap -- it's always first in the property list.
| Property | Season | Meaning |
|---|---|---|
| Vernal | Spring | Beginnings, renewal, hope |
| Solstitial | Summer | Ambition, peak energy, glory |
| Autumnal | Autumn | Reflection, bittersweet, impermanence |
| Hibernal | Winter | Depth, memory, stillness |
Seasonal properties interact with the resonance scoring system at performance time. A Vernal composition performed in Spring gets +5 audience goodwill; the same composition in Winter gets -1.
Stormborn is a special premiere-time property. When a composition is performed for the first time during Storm weather, it gains the Stormborn property. This is permanent, rare, and stacks with the seasonal property. A "Hibernal Stormborn" composition is a winter piece that debuted in a storm -- the kind of memorable event that defines a colony's cultural history.
Source: crates/er-sim/src/sim/components.rs, CompositionProperty::for_season; crates/er-sim/src/sim/systems/revel.rs, Stormborn attachment.
Lineage Property: InStyleOf
Compositions written by an active apprentice carry an additional InStyleOf({master_name}) property -- displayed as Style: {master_name}. Like seasonal and Stormborn, it is cap-exempt: it is appended after the 2-property truncation, so it never displaces a behavioral property.
The marker is informational only -- it does not affect quality scores, audience reactions, or revel resonance. It exists to record cultural lineage: which master shaped which works. The property is attached only while the apprenticeship bond is active. Compositions written after graduation do not carry it; the lineage instead lives in the student's permanent TrainedBy component.
Source: crates/er-sim/src/sim/systems/compose.rs, lines 189-192; crates/er-sim/src/sim/components.rs, CompositionProperty::InStyleOf.
Creative Block
Three or more consecutive compositions with average quality below 50 give a 30% chance of triggering a creative block. Duration is 100 to 200 ticks (random). During a block, the elf refuses to compose and seeks a Garden instead.
Source: crates/er-sim/src/sim/systems/compose.rs, ConsecutiveMediocre, creative block trigger logic
Name Generation
Composition names follow the structure "{Prefix} {Base Type} {Suffix}".
Prefix is selected from mood tier pools, with two override conditions checked first:
| Condition | Pool | Examples |
|---|---|---|
| Night AND mood < -10 | Haunted | Haunted, Spectral, Moonlit, Shadow-woven |
| Inspiration > 70 AND mood < 0 | Fierce | Fierce, Defiant, Tempestuous, Raw |
| Mood <= -41 | Devastated | Shattered, Grieving, Tormented |
| Mood -40 to -21 | Sad | Melancholic, Mournful, Desolate |
| Mood -20 to +10 | Reflective | Contemplative, Wistful, Pensive |
| Mood +11 to +40 | Content | Serene, Gentle, Tender |
| Mood +41 to +70 | Happy | Joyful, Radiant, Exuberant |
| Mood > +70 | Euphoric | Ecstatic, Triumphant, Blazing |
Suffix comes from the dominant inspiration source:
| Source | Example Suffixes |
|---|---|
| Nature | "of the Silver Wood", "of Falling Rain" |
| Social | "of Fellowship", "of the Long Table" |
| Rivalry | "of the Challenge", "of Defiance" |
| Beauty | "of the Master's Touch", "of Perfect Form" |
| Solitude | "of Midnight", "of the Lonely Peak" |
Title Generation
In addition to the composition name, each work gets a short and long title pair. The short title is drawn from a pool matching the dominant inspiration source (e.g., "Still Water", "The Argument"). The long title appends a mood modifier (e.g., "Still Water, in the Rain").
Source: src/sim/art.rs, generate_name, generate_title
Interactions
- Inspiration -- Originality score and compound properties depend on inspiration channels
- Aesthetic Position -- Determines genre family selection and composition aesthetic snapshot
- Revels -- Compositions are performed at revels where audience reactions are computed
- Needs & Mood -- Mood drives the emotional score and name prefix selection
- Skills -- Music skill determines mastery score, base type tier, and compose duration
- Artistic Direction -- Patron direction influences which elves the curator assigns as Composers
- Seasons & Weather -- Season at completion determines the seasonal property; Storm weather during premiere can trigger Stormborn
- Apprenticeship -- An apprentice's compositions carry the cap-exempt InStyleOf({master}) lineage marker
Tips
- A skill-10 elf with high inspiration and good mood can produce a Magnum Opus -- the rarest property in the game. Protect your best musicians from mood crashes.
- Creative blocks are not purely bad: composing during a block earns the Cathartic property, which is otherwise unobtainable.
- Seasonal genre bias means Winter is the best time for Pastoral works and Summer for Radical. Plan revel timing accordingly.
- The 8% cross-genre surprise means even a deeply traditional elf occasionally writes something radical. This is intentional and can spark interesting audience reactions.
- Compositions with the Elegy property are only possible when an elf is mourning -- these tend to have very high emotional scores due to the +15 bonus.
Inspiration
Overview
Inspiration is the creative fuel that drives composition quality. Every elf has five independent inspiration channels that accumulate from different experiences. These channels feed into the originality score of Compositions, determine which composition properties are earned, and influence genre selection.
Inspiration is not a single number -- it is a 5-dimensional creative profile that reflects how an elf has been inspired, not just how much.
How It Works
The Five Channels
Each channel is a u8 value (0-255 per channel, but the total is capped):
| Channel | Source | What Builds It |
|---|---|---|
| Nature | Exposure to the natural world | Being near forests, gardens, water; wandering in wilderness |
| Social | Community and companionship | Proximity to other elves, shared meals, conversations |
| Rivalry | Artistic competition | Proximity to rivals, competing at revels, aesthetic disagreements |
| Beauty | Witnessing great art | Hearing loved compositions at revels, being near gardens |
| Solitude | Time alone | Wandering alone, working in isolation, time away from others |
Total Inspiration
The total is the sum of all five channels, capped at 100 for threshold checks:
Total = min(nature + social + rivalry + beauty + solitude, 100)
This cap means that a broadly inspired elf (moderate across all channels) reaches the ceiling just as easily as a deeply specialized one.
Source: src/sim/components.rs, Inspiration::total
Dominant Source
The channel with the highest value is the "dominant source." This determines:
- The suffix of composition names (e.g., "of the Silver Wood" for Nature)
- The title pool for short/long titles
- Which channel receives a +5 boost from Love reactions at revels
- Genre selection bias (Nature dominance with value > 40 favors Pastoral)
Ties are broken by priority order: Nature > Social > Rivalry > Beauty > Solitude.
Source: src/sim/components.rs, Inspiration::dominant_source
Values & Formulas
Inspiration Equilibrium
Each elf has a natural equilibrium for their inspiration channels, derived from their Aesthetic Position. The equilibrium represents where the elf's inspiration naturally gravitates:
| Channel | Equilibrium Formula |
|---|---|
| Nature | (1.0 - structure) x 20 |
| Social | social x 25 |
| Rivalry | (1.0 - tradition) x 15 |
| Beauty | (structure + tradition) x 10 |
| Solitude | (1.0 - social) x 25 |
Example: An elf with aesthetic position (structure=0.3, tradition=0.2, emotion=0.7, social=0.8) would have equilibrium:
- Nature:
(1.0 - 0.3) x 20 = 14 - Social:
0.8 x 25 = 20 - Rivalry:
(1.0 - 0.2) x 15 = 12 - Beauty:
(0.3 + 0.2) x 10 = 5 - Solitude:
(1.0 - 0.8) x 25 = 5
Source: src/sim/components.rs, InspirationEquilibrium::from_aesthetic
Seeding from Equilibrium
When an elf's inspiration is initialized from equilibrium, the system guarantees a minimum total of 25 by boosting the highest-equilibrium channel in increments of 5 until the floor is reached.
Source: src/sim/components.rs, Inspiration::from_equilibrium
Impact on Composition Quality
Inspiration total feeds directly into the Originality score:
Originality = inspiration_total x 0.6 + random(0..20), clamped 0-100
An elf with 0 total inspiration will produce originality scores of 0-20 (random noise only). An elf with 100 total inspiration will score 60-80 on average.
Revel Inspiration Boost
When an elf has a Love reaction to a composition at a revel, their dominant inspiration channel receives +5 (capped at 100 per channel).
Source: crates/er-sim/src/sim/systems/revel.rs, revel_tick_system Love reaction handler
Composition Property Thresholds
Inspiration channels interact to create special composition properties when two channels both exceed 30:
| Property | Required Channels |
|---|---|
| Transcendent | Total > 90 (any combination) |
| Manifesto | Rivalry > 30 AND Social > 30 |
| Obsessive | Rivalry > 30 AND Solitude > 30 |
| Communal | Nature > 30 AND Social > 30 |
| Timeless | Nature > 30 AND Solitude > 30 |
| Virtuosic | Beauty > 30 AND Rivalry > 30 |
| Sublime | Beauty > 30 AND Solitude > 30 |
| Bittersweet | Social > 30 AND Solitude > 30 |
| Enchanted | Nature > 30 AND Beauty > 30 |
| Anthem | Social > 30 AND Beauty > 30 |
See Compositions for the full property system.
Creative Block
Trigger Mechanism
When an elf produces three or more consecutive compositions with average quality below 50, each subsequent composition has a 30% chance of triggering a creative block.
A composition with quality >= 50 resets the consecutive mediocre counter to zero.
Block Duration
Creative block lasts 100 to 200 ticks (randomly determined).
Block Effects
- The elf refuses to compose and instead seeks a Garden
- The elf is excluded from revel attendance
- If the elf manages to compose during a block (e.g., forced by circumstances), the composition earns the Cathartic property
- Critical needs (rest, sustenance below 20) override block behavior -- the elf will seek a Dwelling to rest even during a block
Source: crates/er-sim/src/sim/components.rs, CreativeBlock, ConsecutiveMediocre;
crates/er-sim/src/sim/systems/compose.rs and crates/er-sim/src/sim/systems/behavior.rs, creative block trigger and behavior tree
Interactions
- Compositions -- Inspiration drives originality score and enables compound properties
- Aesthetic Position -- Determines inspiration equilibrium values
- Revels -- Love reactions boost dominant inspiration channel by +5
- Needs & Mood -- Mood and inspiration together determine emotional and originality scores
- Terrain -- Forest and garden proximity builds Nature and Beauty inspiration
Tips
- An elf's equilibrium is fixed by their aesthetic position. To shift where inspiration naturally gravitates, you need to shift the elf's aesthetics (via friend drift, school drift, or revel audience drift).
- The Solitude and Social channels are in natural tension: an elf with high
socialaxis will have high Social equilibrium but low Solitude equilibrium, and vice versa. This makes the Bittersweet property (Social > 30 AND Solitude > 30) one of the harder compound properties to earn. - Keep an eye on Nature inspiration for potential Pastoral genre selection. An elf needs Nature as their dominant source AND nature > 40 for the 60% Pastoral bias to kick in.
- The total cap of 100 means you do not need all channels maxed out. A specialist with 80 in one channel and 20 spread across others already hits the cap.
- Creative blocks are recoverable. Place a Garden nearby and the elf will seek it out. The block self-resolves after 100-200 ticks.
Aesthetic Position
Overview
Every elf has a 4-axis aesthetic position that defines their artistic taste and creative identity. This is the most influential hidden stat in the game: it determines which genre an elf composes in, how they react to others' art at Revels, their natural Inspiration equilibrium, and whether they feel alienated from the settlement's cultural center.
Aesthetic positions drift over time through four independent mechanisms -- friendship proximity, revel audience exposure, school influence, and apprenticeship bonds -- creating emergent cultural movements without any scripting. See Drift Mechanics below for how they combine.
How It Works
The Four Axes
Each axis is a continuous float from 0.0 to 1.0. The "opposite" of any value
is simply 1.0 - value -- there are no separate fields for opposing poles.
| Axis | Low End (0.0) | High End (1.0) |
|---|---|---|
| Structure | Freedom (improvisation) | Structure (formal composition) |
| Tradition | Innovation (hunger for the new) | Tradition (love of the familiar) |
| Emotion | Intellect (provoke thought) | Emotion (move the audience) |
| Social | Personal (art for oneself) | Social (art for the community) |
New elves are assigned a uniformly random position on all four axes.
Source: src/sim/components.rs, AestheticPosition
Distance Metric
Aesthetic distance between two elves (or between an elf and a composition) uses Euclidean distance in 4D space:
distance = sqrt((s1-s2)^2 + (t1-t2)^2 + (e1-e2)^2 + (sc1-sc2)^2)
Since each axis ranges 0.0-1.0, the maximum possible distance is:
max distance = sqrt(1^2 + 1^2 + 1^2 + 1^2) = 2.0
Two elves at diametrically opposite corners of the aesthetic space are exactly 2.0 apart. Elves within about 0.5 distance are aesthetically compatible; beyond 1.0 they are quite different.
Source: src/sim/components.rs, AestheticPosition::distance_to
Values & Formulas
Audience Evaluation Weights
When an elf evaluates a composition at a revel, their aesthetic position determines how much they weight each quality dimension. The raw weights before normalization:
| Weight | Formula |
|---|---|
| w_mastery | structure x 0.4 + tradition x 0.2 + (1-emotion) x 0.2 + (1-social) x 0.2 |
| w_originality | (1-tradition) x 0.3 + (1-structure) x 0.2 + social x 0.2 + (1-emotion) x 0.3 |
| w_emotional | emotion x 0.4 + social x 0.3 + (1-structure) x 0.15 + (1-tradition) x 0.15 |
These three weights are then normalized to sum to 1.0. The raw sum ranges from about 1.15 to 1.85 depending on position.
The final audience score is:
score = mastery x w_mastery + originality x w_originality + emotional x w_emotional
A social modifier then adjusts the score:
- Social axis > 0.7: +5 points (crowd energy)
- Social axis < 0.3: -3 points (crowd discomfort for introverts)
Source: src/sim/art.rs, evaluate_audience
Genre Affinity
The tradition axis is the primary driver of genre selection:
| Tradition Value | Genre Tendency |
|---|---|
| High (near 1.0) | Strongly Traditional |
| Mid (around 0.5) | Mixed, with ~15% Radical |
| Low (near 0.0) | High Radical probability |
The exact formula:
p_radical = max((1.0 - tradition - 0.5) x 1.5, 0.0)
At tradition=1.0, p_radical=0. At tradition=0.0, p_radical=0.75.
Source: src/sim/art.rs, select_genre
Inspiration Equilibrium
Aesthetic position determines the natural resting state of each inspiration channel:
| Channel | Equilibrium |
|---|---|
| Nature | (1.0 - structure) x 20 |
| Social | social x 25 |
| Rivalry | (1.0 - tradition) x 15 |
| Beauty | (structure + tradition) x 10 |
| Solitude | (1.0 - social) x 25 |
See Inspiration for full details.
Source: src/sim/components.rs, InspirationEquilibrium::from_aesthetic
Drift Mechanics
Aesthetic positions are not static. Four independent drift mechanisms slowly reshape the cultural landscape of the settlement: friend proximity, revel audience exposure, school influence from successful composers, and apprenticeship. Together they are the engine of emergent cultural movements -- no scripting, no designer-placed factions, just small daily nudges that accumulate into settlement-wide artistic identity.
How Drift Direction Works
All four drift systems use signum-based drift: the direction is determined by the sign of the difference, but the magnitude is a fixed step. This means a friend whose tradition is 0.51 (when yours is 0.49) pulls you the same 0.01 as a friend whose tradition is 0.99.
This is a ratchet, not a spring. Distance doesn't amplify drift -- only existence of a difference does. Two consequences players should note:
- Alignment converges slowly, then stops. Once your axis matches a friend's sign-side of theirs, drift continues but by only one step; when you cross to the same side, oscillation can happen at the boundary.
- Extreme taste doesn't pull harder. A radical elf with tradition=0.0 does not exert more force on their friends than a moderately-innovative elf with tradition=0.3 -- the pull is identical, just slower to saturate.
Audience drift (per performance) and apprenticeship drift (toward master) follow the same rule. School drift is the one exception: it pulls toward the sign of the school composer's deviation from 0.5, not toward the composer's exact position.
Friend Drift
When two friends (relationship strength >= 50) are within 3 Manhattan tiles of each other, their aesthetics converge. This runs once per game day.
Rate: 0.01 per axis per day
The drift direction is the sign of the difference: if a friend's structure is higher, the elf's structure increases by 0.01 (and vice versa). All four axes drift independently.
Only friends within 3 tiles are affected. Distant friends do not drift.
Source: crates/er-sim/src/sim/systems/aesthetics.rs, FRIEND_DRIFT_RATE = 0.01,
aesthetic_friend_drift_system
Audience Drift (Revel)
When an elf hears a composition performed at a Revel, their aesthetic position drifts toward the composition's aesthetic snapshot (captured at creation time, see Composition Aesthetic Shift below).
Rate: 0.005 per axis per composition heard
This is applied once per composition performed, for every audience member. A revel with 5 performances means up to 5 drift applications per attendee. Because audience drift fires per performance (not per day), a single well-attended revel can out-weigh a full day of friend drift: 5 performances × 0.005 = 0.025, versus the daily friend cap of 0.01.
Source: crates/er-sim/src/sim/systems/aesthetics.rs, AUDIENCE_DRIFT_RATE = 0.005,
apply_audience_aesthetic_drift
School Drift
Composers who have received 3 or more Love reactions at revels become aesthetic "schools" -- cultural attractors. Non-friend elves within 5 Manhattan tiles of a school composer drift toward that school's aesthetic.
Rate: 0.008 per axis per day
Unlike friend drift which pulls toward the friend's exact position, school drift pulls toward the direction of the school's aesthetic relative to the center (0.5). If a school composer has structure=0.9, nearby non-friends drift in the positive structure direction by 0.008 per day. If the composer has structure=0.2, nearby non-friends drift in the negative structure direction.
Friends are excluded from school drift because friend drift already handles them.
⚠ Terminology note: The code refers to these composers as "legendary" composers, and
SchoolFormedevents use that language. This is not the same as the Legendary prestige tier (score 81+). A "school composer" needs only 3 Love reactions ever -- a very low bar that most active composers cross in their first few revels. The Legendary prestige tier is a far higher achievement. Both systems exist; they are not linked.
Source: crates/er-sim/src/sim/systems/aesthetics.rs, SCHOOL_DRIFT_RATE = 0.008, SCHOOL_THRESHOLD = 3,
aesthetic_school_drift_system
Apprenticeship Drift
When an elf is in an active apprenticeship bond, they drift toward their master's aesthetic position daily -- provided they remain within 5 Manhattan tiles of the master.
Rate: 0.02 per axis per day (2x the friend rate)
This is the strongest drift in the system. A full season of apprenticeship can
shift a student's aesthetic by up to 0.02 × days, enough to move a radical
apprentice meaningfully toward their traditional master's values or vice versa.
The drift persists after the bond resolves -- imprint, not transient influence.
Apprenticeship drift stacks with friend drift: if the apprentice is also a friend of their master (strength >= 50) and within 3 tiles, both systems apply, producing daily drift of up to 0.03 per axis.
See Apprenticeship for the full apprenticeship system. The drift logic lives alongside other drift systems for co-location.
Source: crates/er-sim/src/sim/systems/aesthetics.rs, APPRENTICE_DRIFT_RATE = 0.02,
apprenticeship_aesthetic_drift_system.
Drift Rate Summary
| Drift Type | Rate/axis | Range | Trigger | Frequency |
|---|---|---|---|---|
| Friend | 0.01 | 3 tiles | Friendship (strength >= 50) | Once per day |
| Audience | 0.005 | Revel attendance | Each composition heard | Per performance |
| School | 0.008 | 5 tiles | Composer with 3+ Love reactions (non-friends only) | Once per day |
| Apprenticeship | 0.02 | 5 tiles | Active apprentice-master bond | Once per day |
Drift Interactions
Multiple drifts can apply to the same elf on the same day. The rules:
- Friend and school drift are mutually exclusive toward a given composer. School drift only affects non-friends; friends of a school composer get friend drift toward that composer's exact position, not school drift toward their axis direction.
- Apprenticeship and friend drift stack if the apprentice is both a friend and within range of their master.
- Audience drift always stacks with any daily drift -- it runs during revels regardless of what else the elf has going on.
- Multiple friends or schools stack. An elf with three nearby friends gets three friend-drift applications per axis per day (one per friend, using each friend's direction). An elf within range of two schools gets two school-drift applications.
This means a well-integrated elf -- friends nearby, revels frequent, perhaps an apprentice -- can accumulate substantially more drift per day than a reclusive one. Social integration literally shapes taste faster.
Worked Example
Consider Tamsin, an apprentice with two close friends, near one school composer (but not a friend of that composer), who attends a revel with 4 performances one evening. Her daily drift budget for the tradition axis, if every system moves her the same direction:
| Source | Applications | Contribution |
|---|---|---|
| Master (apprenticeship) | 1 | 0.02 |
| Friend A (tradition higher) | 1 | 0.01 |
| Friend B (tradition higher) | 1 | 0.01 |
| School composer (not a friend) | 1 | 0.008 (direction only) |
| Revel with 4 performances | 4 | 4 × 0.005 = 0.02 |
| Daily total (one axis) | ~0.068 |
Tamsin's tradition axis can shift by nearly 7% in one busy day. Over a 20-day season without contradiction, that's a full point on the [0.0, 1.0] scale -- from fully innovative to fully traditional.
A reclusive elf with no friends, no master, not near a school, who doesn't attend the revel: zero drift that day. Aesthetic identity is a social phenomenon in this system; hermits keep whatever aesthetic they started with.
Composition Aesthetic Shift
When a composition is created, its aesthetic snapshot is not identical to the composer's position. Small shifts are applied based on inspiration balance:
| Axis | Shift |
|---|---|
| Structure | +(solitude - social) x 0.001 |
| Tradition | -(originality / 100) x 0.3 |
| Emotion | +(emotional / 100) x 0.2 |
| Social | +(social - solitude) x 0.001 |
This means highly original works drift the composition's tradition axis downward, and highly emotional works drift the emotion axis upward. These shifted values are what audiences drift toward during revels.
Source: crates/er-sim/src/sim/systems/compose.rs, composition aesthetic drift logic in compose_system
Interactions
- Compositions -- Genre family, quality weights, and composition aesthetic all derive from position
- Inspiration -- Equilibrium values are computed from aesthetic position
- Revels -- Audience reactions depend on aesthetic distance; attendance causes drift
- Relationships -- Friends within 3 tiles cause mutual aesthetic convergence
- Apprenticeship -- Apprentices drift toward their master at 2x friend rate
- Prestige -- The "Legendary" prestige tier is distinct from the "school composer" threshold of 3 Love reactions
- Satisfaction & Departure -- Elves far from the settlement's aesthetic center feel alienated
Tips
- Apprenticeship drift is the strongest single source at 0.02/axis/day -- if you want to shift a specific elf's aesthetic deliberately, pair them with a master whose taste you want them to inherit.
- Friend drift (0.01/day) only activates at close range. Housing friends near each other accelerates cultural convergence. Distant friendships have zero aesthetic pull.
- School drift creates emergent "movements." A composer with 3+ Love reactions (the school threshold -- note this is much lower than the Legendary prestige tier) surrounded by non-friend neighbours will gradually pull those neighbours toward their taste direction, creating a coherent artistic identity in that area.
- Audience drift at revels is the main mechanism for settlement-wide cultural coherence. Regular revels with compositions from varied composers spread aesthetic influence broadly. A single 5-performance revel can move an attendee more than a full day of friend drift.
- Drift is a ratchet, not a spring. Taste difference magnitude doesn't amplify the pull -- only presence of difference does. Cultivating radical elves requires many exposures, not fewer-but-more-extreme ones.
- An elf's genre tendency is almost entirely determined by their tradition axis. If you want more Radical compositions, cultivate low-tradition elves by exposing them to innovative works at revels.
- The composition aesthetic shift means that a purely traditional elf who writes a highly original piece will produce a composition that actually nudges audiences away from tradition. Art has a life of its own.
- Hermits keep their starting aesthetic. An elf with no nearby friends, no master, not near a school, who avoids revels, will never drift. Most settlements don't produce hermits accidentally -- isolation has to be engineered or suffered through lifecycle events.
- The maximum aesthetic distance of 2.0 is useful context: two elves at 1.5+ distance are aesthetically alien to each other and will react very differently to the same composition.
Revels
Overview
Revels are the cultural heartbeat of an elven settlement -- communal gatherings where compositions are performed, audience reactions are evaluated, relationships form, and the settlement's aesthetic identity evolves. They are the primary way that art connects to the social fabric: a single revel can boost morale, trigger creative blocks, form new fandoms, and shift the aesthetic landscape of the entire community.
Revels are scheduled by the Curator (or the Dummy Curator's rule-based logic) and require a Feast Hall, food, compositions, and enough available elves.
How It Works
Scheduling Requirements
The Dummy Curator schedules a revel when all of the following are true:
| Requirement | Condition |
|---|---|
| Feast Hall | At least one built |
| Compositions | At least one exists in the settlement portfolio |
| Available elves | >= 5 elves not in creative block |
| Food | >= 5 (or >= 15 in Winter) |
| Cooldown | >= 400 ticks since last revel ended |
The LLM Curator uses the same information but makes its own judgment call. The player can also influence revel timing through messages to the curator.
Source: src/sim/curator/dummy.rs, revel scheduling logic
Lifecycle Phases
A revel progresses through three phases, each with a fixed tick duration:
None --> Gathering (5 ticks) --> Performing (5 ticks per piece) --> Aftermath (5 ticks) --> None
Phase 1: Gathering (5 ticks)
During gathering, elves walk toward the Feast Hall. Elves are directed to the hall each tick until they arrive or the phase ends:
- Elves with a creative block are excluded from attendance
- Attendance is capped at the settlement's capacity
- Elves who arrive at the hall receive the
RevelAttendingmarker - Attending elves skip the normal behavior tree for the revel's duration
At the end of the gathering phase, food is consumed:
Food consumed = min(attendee_count, available_food)
If there is not enough food for everyone, all attendees receive a "Sparse feast" mood modifier: -3 for 100 ticks.
Source: crates/er-sim/src/sim/systems/revel.rs, revel_tick_system, Gathering phase
Phase 2: Performing (5 ticks per composition)
Compositions are performed one at a time, newest first. Each performance takes 5 ticks to complete. At the end of each 5-tick window, audience evaluation runs.
For each audience member and each composition:
- The audience member's aesthetic position determines evaluation weights
- A weighted score is computed from the composition's mastery, originality, and emotional dimensions
- A social modifier is applied (+5 for social axis > 0.7, -3 for social axis < 0.3)
- Discontented elves receive a -5 penalty to their score
- The score maps to a reaction tier
| Score Range | Reaction |
|---|---|
| 0-24 | Dislike |
| 25-50 | Indifferent |
| 51-75 | Enjoy |
| 76-100 | Love |
See Aesthetic Position for the full weight formulas.
Source: src/sim/art.rs, evaluate_audience
Phase 3: Aftermath (5 ticks)
The aftermath phase is a brief wind-down:
RevelAttendingmarkers are removed from all elves- The revel zone is detected from attendee positions
- Average audience score is computed across all performances and reactions
- Great/boring revel satisfaction spikes are applied (see below)
- The revel is archived to history with full reaction data
last_revel_tickis recorded (starts the cooldown timer)
Source: crates/er-sim/src/sim/systems/revel.rs, revel_tick_system, Aftermath phase
Values & Formulas
Mood Effects
Audience reactions produce mood modifiers:
| Reaction | Mood Effect | Duration |
|---|---|---|
| Dislike | -3 ("Disliked {name}") | 50 ticks |
| Indifferent | No effect | -- |
| Enjoy | +3 ("Enjoyed {name}") | 50 ticks |
| Love | +6 ("Loved {name}") | 100 ticks |
Satisfaction Spikes
Revels directly affect elf satisfaction, which drives the departure system:
| Event | Spike |
|---|---|
| Love reaction (per composition) | +15 to that audience member |
| Great revel (avg score > 60) | +10 to all attendees |
| Boring revel (avg score < 30) | -10 to all attendees |
These are buffered as "spikes" consumed by the satisfaction system on its next tick, so they integrate properly with the satisfaction recomputation.
Source: crates/er-sim/src/sim/systems/revel.rs, revel satisfaction spike logic in revel_tick_system
Inspiration Boost
A Love reaction boosts the audience member's dominant inspiration channel by +5 (capped at 100 per channel).
Source: crates/er-sim/src/sim/systems/revel.rs, revel_tick_system Love reaction handler
Aesthetic Drift
Every composition heard at a revel nudges all audience members' aesthetic positions toward the composition's aesthetic snapshot:
Drift rate: 0.005 per axis per composition heard
A revel with 4 performances applies up to 4 drift increments per attendee. See Aesthetic Position for details.
Source: crates/er-sim/src/sim/systems/aesthetics.rs, AUDIENCE_DRIFT_RATE = 0.005
Fandom Formation
When an elf has a Love reaction to a composition (and is not the composer), the reaction is recorded in their Fandom memory. Each elf tracks up to 5 fan relationships, pruning the weakest if capacity is exceeded.
When a new fandom forms (first Love reaction for that composer), a
FandomFormed event is emitted and archived as a revel incident.
See Fandom for how fan proximity then boosts composer
inspiration, strengthens relationships, and emits FanRequest events over time.
Source: crates/er-sim/src/sim/components.rs, Fandom; crates/er-sim/src/sim/systems/fandom.rs, fandom_system; crates/er-sim/src/sim/systems/revel.rs, fandom emission in revel_tick_system
Prestige Update
When a composer's work receives a Love reaction at a revel, their
last_love_tick is set to the current tick. This is the heartbeat of the
prestige system -- it resets the decay clock.
The prestige score itself is computed every 50 ticks by the prestige_system
from three inputs:
Prestige = (recent_loves x 5.0) + (fans x 10.0) + (compositions x 2.0) - decay
| Input | Weight | Source |
|---|---|---|
| Recent Love reactions | x 5.0 | Count of Love reactions in the last 5 revels |
| Fan count | x 10.0 | Number of elves with a Fandom entry for this composer |
| Portfolio size | x 2.0 | Total compositions in the elf's portfolio |
| Decay | -1.0 per 50-tick cycle | Applied when no Love reaction received in the last 200 ticks |
The score is clamped to 0--100 and mapped to a tier:
| Tier | Score Range |
|---|---|
| Unknown | 0--15 |
| Emerging | 16--35 |
| Established | 36--55 |
| Renowned | 56--80 |
| Legendary | 81--100 |
Prestige feeds into satisfaction at a weight of x 0.15 (max +15 at score 100). High-prestige elves also shape the settlement's aesthetic center with up to 3x weight (at prestige 100), making them cultural anchors whose taste disproportionately defines the community norm.
Tier changes emit PrestigeChanged events and are visible in the revel recap.
Source: crates/er-sim/src/sim/systems/prestige.rs, prestige_system;
crates/er-sim/src/sim/components.rs, PrestigeTier::from_score
Performance Incidents
Two types of incidents can occur during a revel performance and are archived with the revel record:
PublicCritique
A PublicCritique fires when a rival of the composer gives a Dislike
reaction to the performance, stacking an additional -4 mood on the composer
(80 ticks) and granting +2 rivalry inspiration to the critic. The total
mood hit on the composer is -7 (-3 Dislike + -4 PublicCritique). The
incident is also recorded in the revel archive as
PerformanceIncident::PublicCritique.
For the full mechanic — when rivalries form, how the conflict system runs, escalation/reconciliation, and how PublicCritique interacts with ego crises — see Rivalries → Public Critique.
Source: crates/er-sim/src/sim/systems/revel.rs, revel_tick_system -- PublicCritique logic.
FandomFormed
When a Love reaction creates a new fan relationship (first Love for that
composer), a FandomFormed incident is recorded. See
Fandom Formation above for details.
Ego Crisis
When a composer's own work receives a majority negative reception (more than half of audience reactions are Dislike or Indifferent), the composer suffers an ego crisis:
| Effect | Value |
|---|---|
| Satisfaction spike | -30 |
This is separate from (and stacks with) individual Dislike mood penalties and PublicCritique penalties. A composer who is publicly criticized by a rival and gets majority negative reception takes:
- -3 mood (Dislike reaction)
- -4 mood (PublicCritique)
- -30 satisfaction spike (ego crisis)
Ego crises are a significant blow. Combined with the -10 "boring revel" spike (if avg score < 30), a disastrous revel can push a composer toward departure.
Source: crates/er-sim/src/sim/systems/revel.rs, revel_tick_system -- ego crisis at majority
negative check.
Dislike Accumulation
When an audience member Dislikes 2 or more performances in a single revel, they receive an additional satisfaction spike:
-20 satisfaction (on top of the per-performance Dislike mood penalties)
This punishes consistently bad revels more than a single weak performance.
Source: crates/er-sim/src/sim/systems/revel.rs, revel_tick_system -- dislike_counts tracking.
School Formation
When a composer accumulates 3 or more Love reactions across revels (tracked
in their Aspirations), a SchoolFormed event fires.
This marks the composer as a cultural attractor -- their aesthetic position
becomes a gravitational center that pulls nearby non-friend elves toward it.
| Parameter | Value |
|---|---|
| Love reaction threshold | 3 total (lifetime, not per-revel) |
| School drift rate | 0.008 per axis per day |
| School influence | Non-friends only (friends already drift via friend drift) |
The school drift system runs once per day (every DAY_LENGTH ticks). For each elf who is not a friend of the school composer and has fewer than 3 Love reactions themselves (i.e., is not a school-founder), their aesthetic position is nudged toward the school composer's aesthetic at a rate of 0.008 per axis per day.
Schools are how legendary composers reshape a settlement's entire aesthetic landscape. A composer with 5+ Love reactions acts as a constant aesthetic pull on every non-friend elf, slowly aligning the community toward their taste.
Source: crates/er-sim/src/sim/systems/aesthetics.rs, aesthetic_school_drift_system,
SCHOOL_THRESHOLD = 3, SCHOOL_DRIFT_RATE = 0.008
Revel Absence
During the gathering phase, elves with extreme personality values may choose not to attend:
| Personality | Condition | Absence Reason |
|---|---|---|
| Cautious | boldness < 0.3 | "chose the forest over the fire" |
| Proud | pride > 0.8 | "refused to attend" |
These absences are logged as RevelAbsence events. Absent elves miss all
performance effects (mood, satisfaction spikes, aesthetic drift, fandom formation)
but also avoid negative outcomes from bad revels.
Source: crates/er-sim/src/sim/systems/revel.rs, revel_tick_system -- personality-driven absence
events in Gathering phase.
Cooldown
After a revel ends, 400 ticks must pass before the next one can be scheduled. The first revel after settlement creation has its cooldown waived.
| Parameter | Value |
|---|---|
| Cooldown duration | 400 ticks |
| First revel | Cooldown waived |
| Food requirement (normal) | >= 5 |
| Food requirement (Winter) | >= 15 |
| Minimum elves | 5 |
Source: src/sim/curator/dummy.rs, cooldown check; src/sim/world.rs,
last_revel_tick initialization
Revel Archive
Every completed revel is archived with:
- Day number
- Zone name (detected from attendee positions)
- Attendee names
- Full performance records with per-elf reactions and scores
- Incidents (public critiques, fandom formations)
The archive computes an average score across all reactions and identifies the highlight (best-received performance by total reaction score sum).
Source: src/sim/components.rs, RevelArchive
Revel Types
Each revel has a type that determines its seasonal character. The type affects audience scoring through a seasonal appropriateness modifier -- holding the right revel in the right season boosts quality, while mismatched revels are penalized.
| Type | Label | Season | Appropriateness |
|---|---|---|---|
| Standard | Standard Revel | Any | 0 (neutral) |
| GardenWalk | Garden Walk | Spring | +5 in Spring |
| GrandPerformance | Grand Performance | Summer | +5 in Summer, -8 in Winter |
| Retrospective | Retrospective | Autumn | +5 in Autumn, -3 in Spring |
| ChamberPerformance | Chamber Performance | Winter | +5 in Winter, -3 in Summer |
| Spontaneous | Spontaneous Revel | Any (weather-triggered) | 0 (neutral) |
The curator selects from each season's natural options when scheduling. The seasonal appropriateness modifier is added to each audience member's score during the evaluation phase.
Source: crates/er-sim/src/sim/components.rs, RevelType::seasonal_options, RevelType::seasonal_appropriateness.
Seasonal Resonance
When a composition is performed, its seasonal property is checked against the current season. If they match, the audience responds more warmly. If they clash, the audience is slightly cooler.
| Condition | Score Modifier |
|---|---|
| Composition's season matches current season | +5 |
| Composition's season does not match | -1 |
These modifiers stack with the revel type's seasonal appropriateness. A Vernal composition performed at a Garden Walk in Spring receives both the +5 resonance bonus and the +5 GardenWalk-in-Spring appropriateness, for a total of +10 to every audience member's score. This is the core feedback loop that makes seasonal timing matter.
Conversely, a Hibernal composition at a summer Grand Performance gets -1 (mismatch) but the Grand Performance still provides +5 (it's in-season for its type). The combination is +4 -- not bad, just not as strong as a season-aligned performance.
Source: crates/er-sim/src/sim/art.rs, evaluate_audience -- seasonal resonance section.
Interactions
- Compositions -- Performed newest-first; quality dimensions drive audience scores
- Aesthetic Position -- Evaluation weights and audience drift
- Inspiration -- Love reactions boost dominant channel; high inspiration prevents creative block
- Satisfaction & Departure -- Great revels boost satisfaction, boring revels damage it
- Needs & Mood -- Reactions generate mood modifiers; sparse feasts cause mood penalties
- Relationships -- Fandom formation creates persistent social bonds
- Buildings -- Feast Hall required; compositions require Workshop
- Artistic Direction -- Direction influences which compositions are created before the revel
- Romance -- Shared transcendence (both Loved the same piece) is the strongest romance catalyst (+8 warmth)
- Arrivals -- Great revels (avg score >= 75) trigger the cultural event arrival channel (1-2 new elves)
- Personality -- Extreme personality values can cause revel absences
- Seasons & Weather -- Seasonal anchor events create revel windows; missing them applies mood penalties
Tips
- Schedule revels when your best composers have recently finished new works. Compositions are performed newest-first, so recent high-quality pieces get heard.
- Ensure adequate food before a revel. A "Sparse feast" mood penalty hits every attendee and can tank audience scores for the whole event.
- Revels are the primary mechanism for settlement-wide aesthetic convergence. Without regular revels, elves drift apart culturally and satisfaction drops.
- A revel with low average scores (< 30) actively harms the settlement through the -10 satisfaction spike. It is better to skip a revel than to hold one with only crude compositions.
- Watch for discontented elves at revels -- they evaluate with a -5 penalty, making bad reactions even more likely. Addressing satisfaction before a revel improves outcomes.
- The 400-tick cooldown means roughly one revel every 4 game days (with DAY_LENGTH=100). Winter's higher food requirement (15 vs 5) means you need well-stocked granaries to keep the revel cadence up.
- Fandom is a powerful social mechanic. An elf who Loves a composer's work becomes a fan, which affects future proximity-seeking and inspiration. Legendary composers can reshape the settlement's culture through school drift.
Cultural Movements
When a settlement's artistic life crosses certain thresholds, the simulation emits cultural milestone events: visible signals in the event feed that something larger than any single composition or elf has happened. These events mark when a composer's audience grows large enough to constitute a "school," when a genre is invented for the first time, or when a movement of like-minded elves crystallizes around shared loves.
This page covers three colony-level events:
- SchoolFormed — a composer crosses 3 cumulative Love reactions. Implemented.
- GenreInvented — a settlement's first anachronistic composition. Defined but not yet emitted.
- MovementCrystallized — coalescence of co-loving elves into a named movement. Defined but not yet emitted.
All three are EventPriority::Notable -- they appear in the event feed and
revel recap but do not pause the game. Their reach extends well beyond
individual elves: each marks a moment where the colony's culture has, in some
small or large way, taken on a new shape.
Source: crates/er-sim/src/sim/events.rs, variants SchoolFormed,
GenreInvented, MovementCrystallized; priority classification in
CulturalEvent::priority.
SchoolFormed
A composer becomes a "school" the moment their cumulative count of Love reactions received as a composer crosses 3. From that moment forward, they are an aesthetic attractor: nearby non-friend elves slowly drift toward their taste direction (see School Drift).
Formation Conditions
| Condition | Value |
|---|---|
| Trigger | Composer's Aspirations.love_reactions_received crosses SCHOOL_THRESHOLD |
| Threshold | 3 total Love reactions across the composer's career |
| Frequency | One-shot — fires only on the transition from below to at-or-above 3 |
| When | During revel processing, immediately after a composition's audience reactions are tallied |
The check is "was below threshold and is now at-or-above" -- so if a composer gets two Love reactions in one performance and they were already at 1, the event fires once for the threshold crossing, not twice.
Event Payload
| Field | Type | Description |
|---|---|---|
composer | String | Name of the elf who became a school |
love_count | u8 | The composer's total Love reactions at the moment of formation (>= 3) |
Display
"{composer} has become a legendary school ({love_count} loves)"
⚠ Terminology warning: The display string says "legendary school," but the threshold is just 3 Love reactions -- a very low bar. This is not related to the Legendary prestige tier (which requires score 81+). A school composer can be a brand-new arrival with an Unknown prestige tier and a single hit performance. See the aesthetic position page for further discussion of this naming collision.
Player Significance
A SchoolFormed event tells the player: this composer's work is now influencing
other elves' aesthetic positions. Concretely:
- Non-friend elves within 5 Manhattan tiles of the school composer drift toward the direction of the composer's aesthetic (relative to 0.5) by 0.008 per axis per day.
- The composer's prestige score gets the same fan and
Love-reaction inputs it always did --
SchoolFormedis a separate, parallel signal, not a prestige driver. - Schools tend to cluster: a school composer who keeps performing accumulates fans, the fans drift toward them aesthetically, and over time a coherent "movement" emerges around the school's chosen axis directions.
The event is the cue to start watching that part of the settlement for cultural identity formation.
Source: crates/er-sim/src/sim/systems/revel.rs -- emission at the
was_below && asp.love_reactions_received >= SCHOOL_THRESHOLD branch around
line 371; crates/er-sim/src/sim/systems/aesthetics.rs -- school drift
behavior keyed to the same SCHOOL_THRESHOLD = 3 constant.
GenreInvented
A GenreInvented event would mark the moment a settlement composes its first
anachronistic piece in a given genre family -- a cultural shock event
celebrating that someone broke from the traditional and natural genres into
something new (Jazz, Rock, Blues, Free Verse, and similar genre families
described in the compositions reference).
⚠ Status: defined but not currently emitted. The event variant exists in
CulturalEventand would render in the event feed if produced, but no system in the codebase constructs it today. The 2026-03-16 art-data-files PRD explicitly defers it ("GenreInvented event deferred (noted as TODO)").
Intended Formation Conditions
Per the original game-mechanics specification:
"When an anachronistic piece is the settlement's first, emit
GenreInvented."
The intended trigger is a per-settlement "first anachronistic genre" recognition: the system would need to track which genre families have been performed at least once in the settlement's history and fire the event the first time a composition lands in a previously-unseen anachronistic family.
Event Payload
| Field | Type | Description |
|---|---|---|
genre | String | Name of the genre family invented (e.g., "Jazz") |
inventor | String | Name of the elf whose composition triggered the event |
Display (when implemented)
"{inventor} invented a new genre: {genre}!"
Why It's Worth Implementing
Anachronistic genres carry strong aesthetic signal -- they are the colony's break from tradition, the moment innovation overtakes inheritance. A first emission would be a real cultural milestone worth marking in feeds, and could plausibly factor into:
- Arrival mechanics (new wanderers attracted by cultural novelty -- see the reputation-based arrival design noted in earlier handoffs).
- Aspiration progress for innovation-leaning elves.
- A potential
Foundertag for the inventing composer (analogous to colony founders).
See the follow-up beads for the open implementation issue.
Source: crates/er-sim/src/sim/events.rs -- variant declaration only.
Original design: docs/plans/2026-03-14-game-mechanics-spec.md line ~540 and
docs/brainstorms/2026-03-16-art-data-files.md line ~63.
MovementCrystallized
A MovementCrystallized event would mark the formation of a named cultural
movement: a coherent group of elves bound together by shared aesthetic loves
strong enough to constitute a recognizable artistic faction within the colony.
⚠ Status: defined but not currently emitted. The event variant exists in
CulturalEventand would render in the event feed if produced, but no system in the codebase constructs it today. The full crystallization logic (co-love subset detection, movement naming, member tracking, mood bonuses, revel score modifiers) is unimplemented.
Intended Formation Conditions
Per the original game-mechanics specification:
"After 3+ revels, scan for co-loving subsets: ≥3 elves who LOVED ≥3 same compositions. Named from founding compositions: 'The Defiant Shadow School.'"
The intended pipeline:
- After every revel (once the colony has hosted at least 3 revels), scan the audience reaction history for co-loving subsets: groups of 3 or more elves who all gave Love reactions to the same 3 or more compositions.
- When such a subset is detected and not already constituting an existing movement, name the movement after one or more of its founding compositions (e.g., "The Defiant Shadow School" from compositions like "Defiant Nocturne" and "Shadow Aubade").
- Tag aligned elves with movement membership.
- Emit
MovementCrystallized { name, member_count }.
Event Payload
| Field | Type | Description |
|---|---|---|
name | String | Name of the movement (e.g., "The Defiant School") |
member_count | u32 | Number of founding members at crystallization |
Display (when implemented)
"A cultural movement has formed: {name} ({member_count} members)"
Intended Downstream Effects
The same specification documents the gameplay effects movements would have if implemented:
- Mood bonus: aligned elves get "Part of a movement" +2 morale per 200 ticks.
- Revel scoring modifier: at revels, movement-aligned compositions get +3 from aligned elves and -2 from opposed elves.
- Patron interaction: the human director would be able to favorite or disfavor movements, shaping the colony's cultural arc.
- Curator awareness: the AI advisor would surface movement formation as a notable event for player attention.
None of the above is wired today. A MovementCrystallized payload appears in
test fixtures ("The Defiant School", 3 members) but only to exercise the
display formatter -- no production code path produces one.
See the follow-up beads for the open implementation issue.
Source: crates/er-sim/src/sim/events.rs -- variant declaration only.
Original design: docs/plans/2026-03-14-game-mechanics-spec.md lines ~600-606
and docs/prd/elf-revel-prd.md lines ~118-122.
How These Events Interact
Even though only SchoolFormed fires today, all three events were designed as
stages of cultural emergence at increasing scale:
| Stage | Event | Scope | Trigger |
|---|---|---|---|
| 1. Recognition of a single composer | SchoolFormed | One composer | 3+ Love reactions |
| 2. Recognition of a genre breakthrough | GenreInvented | One genre family | First anachronistic of its family |
| 3. Recognition of a coherent audience faction | MovementCrystallized | Group of 3+ elves | Shared multi-composition loves |
A fully populated cultural-emergence pipeline would let a player watch their colony progress through these stages: a single compelling composer becomes a school, an experimental composition opens a genre, and eventually like-minded audience members coalesce into a recognizable movement around the new aesthetic. Today only the first step is operative.
Related Systems
- Revels -- where Love reactions are tallied and where
SchoolFormedis emitted. The intended trigger sites forGenreInventedandMovementCrystallizedwould also live here or in adjacent revel-processing logic. - Aesthetic Position — School Drift -- the simulation effect of becoming a school: nearby non-friends drift toward the school's aesthetic direction.
- Compositions -- genre families, including the anachronistic
family relevant to
GenreInvented. - Prestige -- a parallel reputation system for
composers; the Legendary prestige tier is unrelated to the "school"
threshold despite the shared word in the
SchoolFormeddisplay string. - Fandom -- per-elf fan tracking; fandom contributes to prestige independently of school formation.
Follow-up Work
Two of the three events on this page are unimplemented design intent. Open beads track the gap so future cultural depth work can pick them up:
- Revel-ris — implement
GenreInventedemission: track first-seen genre families per settlement, fire on first anachronistic composition. - Revel-awc — implement
MovementCrystallizeddetection and effects (epic): co-love subset scanning, movement naming, membership tracking, mood bonuses, revel-scoring modifiers, patron/curator hooks.
These are noted as TODO in the original PRD and game-mechanics spec but were
never scheduled. If you're considering implementing either, see
docs/plans/2026-03-14-game-mechanics-spec.md (~lines 540-606) for the
original spec.
Artistic Direction
Overview
Artistic Direction is the patron's primary tool for shaping the settlement's creative culture. It is a single setting with four modes that influences how the Curator makes decisions about role assignment, composition encouragement, and settlement priorities. The direction does not force any specific outcome -- it nudges the curator's judgment toward a particular aesthetic value.
You cycle through directions by pressing 'd' during normal gameplay (not in Look mode, where 'd' controls the camera).
How It Works
The Four Directions
| Direction | Label | Description |
|---|---|---|
| Balanced | Bal | No preference -- the curator decides freely based on settlement needs |
| Favor Mastery | Mas | Prioritizes high-skill elves as composers; values technical excellence |
| Favor Originality | Orig | Encourages experimental, inspiration-driven work |
| Favor Emotion | Emo | Values emotional resonance over technical skill |
The default starting direction is Balanced.
Pressing 'd' cycles through them in order:
Balanced --> Favor Mastery --> Favor Originality --> Favor Emotion --> Balanced
Source: src/sim/components.rs, ArtisticDirection;
src/model.rs, cycle_artistic_direction
How the Curator Interprets Direction
The direction is communicated to the curator in two ways:
-
System prompt context: The LLM Curator receives the direction as part of its system prompt under "Patron's Direction," with explicit guidance:
- Favor Mastery: "nurture your most skilled composers"
- Favor Originality: "encourage avant-garde elves"
- Favor Emotion: "protect your deeply-feeling artists from creative block"
- Balanced: no specific instruction
-
Dummy Curator rule: When the direction is non-Balanced, the Dummy Curator ensures the best musician (highest music skill) is assigned the Composer role if no Composer has been assigned yet. This applies equally to all three non-Balanced directions.
Source: src/sim/curator/prompt.rs, build_system_prompt;
src/sim/curator/dummy.rs, artistic direction handling
State Snapshot
The current direction is included in the settlement state snapshot sent to the curator each consultation cycle:
patron_direction: "FavorMastery"(or Balanced, FavorOriginality, FavorEmotion)
This means the LLM Curator sees the direction every time it evaluates the settlement and can adjust its strategy accordingly.
Source: src/sim/curator/state.rs, patron_direction field
Values & Formulas
Direction Does Not Affect Composition Scores Directly
Artistic direction does not modify the quality formulas. Mastery is always driven by music skill, originality by inspiration total, and emotional by mood. The direction works indirectly:
| Direction | Indirect Effect |
|---|---|
| Favor Mastery | Curator assigns high-skill elves as Composers, who produce higher mastery scores |
| Favor Originality | Curator encourages conditions for high inspiration (gardens, varied experiences) |
| Favor Emotion | Curator protects emotionally-driven elves from creative block, maintains positive mood |
| Balanced | Curator optimizes for settlement health without bias toward any quality dimension |
Interaction with Audience Evaluation
While direction does not change evaluation weights (those come from individual elf aesthetic positions), the compositions produced under each direction tend to score differently:
| Direction | Tends to Produce | Audience Effect |
|---|---|---|
| Favor Mastery | High mastery scores | Appreciated by structured, traditional elves |
| Favor Originality | High originality scores | Appreciated by innovative, low-tradition elves |
| Favor Emotion | High emotional scores | Appreciated by emotional, social elves |
| Balanced | Even distribution | Moderate reception across the board |
Patron Taste System
Artistic direction is one part of the broader Patron Taste system, which also includes:
| Feature | Description | Control |
|---|---|---|
| Artistic Direction | Bias curator decisions | 'd' key |
| Favorite Compositions | Mark compositions you value | Composition interaction |
| Interesting Elves | Mark elves you want the curator to focus on | Elf interaction |
| Named Locations | Place meaningful names on the map | Map interaction |
The curator sees all of these when making decisions. Favorite compositions that are performed at revels trigger a special patron notification event.
Source: src/sim/components.rs, PatronTaste
TUI Display
The current direction is shown in the status bar with a short label:
| Direction | Status Bar |
|---|---|
| Balanced | Bal |
| Favor Mastery | Mas |
| Favor Originality | Orig |
| Favor Emotion | Emo |
Source: src/render_terminal.rs, direction label rendering
Interactions
- Compositions -- Direction biases which compositions get created through role assignment
- Revels -- Composition quality affects revel outcomes; direction shapes what is composed
- Roles -- Non-Balanced directions influence the curator to assign Composer roles
- The Curator -- Primary consumer of direction; both Dummy and LLM curators respond to it
- Aesthetic Position -- Audience evaluation weights are per-elf, not direction-dependent
Tips
- Balanced is the safe default. The curator already optimizes for settlement health. Only switch to a specific direction when you have a deliberate artistic vision.
- Favor Mastery is strongest in established settlements with high-skill composers. In a young settlement where everyone is skill 1-3, the mastery ceiling is too low for direction to matter much.
- Favor Originality works best when you can supply high inspiration. Build gardens, encourage wandering, and ensure varied experiences. Without inspiration sources, the direction is aspirational but ineffective.
- Favor Emotion is the defensive choice for settlements under stress. Emotional compositions with high mood tend to get broad audience approval, which boosts satisfaction and prevents departures.
- Direction changes take effect on the next curator consultation cycle. There is no delay or transition cost -- switch freely as conditions change.
- The LLM Curator interprets direction more creatively than the Dummy Curator. Where the Dummy Curator simply assigns the best musician as Composer, the LLM Curator might rearrange multiple roles, suggest garden placement, or write messages explaining its artistic reasoning.
- Watch the composition quality breakdowns to verify your direction is working. If you favor Mastery but compositions still have low mastery scores, the problem is likely insufficient music skill, not the direction setting.
Relationships
Elves form bonds with each other through proximity, shared activities, and aesthetic affinity. These relationships shape mood, satisfaction, and the social fabric of your settlement.
Overview
Every elf maintains a list of up to 20 relationships, each with a numeric strength value ranging from -100 to +100. As strength changes, relationships cross thresholds that reclassify them into one of three types: Acquaintance, Friend, or Rival.
Relationships form gradually. Two elves standing near each other and working gain small increments of strength. Over time, those small gains accumulate until a bond crystallizes. The social system runs every 50 ticks, evaluating proximity and adjusting strength for every pair of elves.
How It Works
Bond Types
Relationships are classified by their strength value:
| Type | Strength Range | Description |
|---|---|---|
| Acquaintance | -29 to +49 | Neutral. Default state for any new relationship. |
| Friend | +50 to +100 | Positive bond. Provides mood bonuses and satisfaction gains. |
| Rival | -100 to -30 | Negative bond. Causes mood penalties when nearby. |
New relationships always start as Acquaintance with strength 0.
Source: src/sim/components.rs, Relationships::adjust -- strength clamped to -100..100, Friend at 50+, Rival at -30 or below.
Formation Triggers
Relationships shift through proximity-based delta calculations that fire every 50 ticks in the social system:
Base delta (requires Manhattan distance <= 3 between the two elves):
| Condition | Base Delta |
|---|---|
| Both actively working (not Idle) | +1 |
| Both resting or eating, distance <= 2 | +2 |
| One or both idle | 0 (no change) |
Aesthetic affinity modifier (applied on top of base delta):
| 4D Aesthetic Distance | Modifier |
|---|---|
| < 0.3 (very similar) | +1 |
| 0.3 -- 0.7 (moderate) | 0 |
| 0.7 -- 0.9 (different) | -1 |
| > 0.9 (strongly opposed) | -2 |
Aesthetic distance is computed as Euclidean distance in 4D aesthetic space (structure, tradition, emotion, social axes, each 0.0--1.0). The maximum possible distance is 2.0.
Source: crates/er-sim/src/sim/systems/social.rs, social_system -- proximity check at Manhattan <= 3, aesthetic distance thresholds at 0.3/0.7/0.9.
Social Axis Scaling
After computing the combined delta, it is scaled by each elf's Social axis (from their Aesthetic Position):
| Social Axis Value | Multiplier | Personality |
|---|---|---|
| > 0.7 | x2 | Social elf -- bonds form and break faster |
| 0.3 -- 0.7 | x1 | Balanced |
| < 0.3 | x0.5 (halved) | Personal elf -- bonds form slowly |
This means a Social elf gains +2 per working-proximity tick instead of +1, while a Personal elf only gains +0 (rounded down from 0.5). The scaling applies to both positive and negative deltas.
Source: crates/er-sim/src/sim/systems/social.rs, apply_social_axis_scale -- threshold checks at 0.7 and 0.3.
Bond Origins
Partner relationships remember how they formed. Three origins are currently tracked — Apprenticeship, SharedCrisis, and RivalryToLove — and they shift the partnership dissolution threshold up or down by bounded amounts. Four additional variants (Workshop, Revel, Critique, Rescue) are reserved for future triggers and carry no effect yet.
Origin history is a per-partnership property: only Partner relationships carry entries, and the history is cleared when a partnership dissolves. Acquaintances, Friends, and Rivals do not carry origin markers.
See Partnership Origins for the full system — triggers, the strength_min invariant that enables RivalryToLove detection, dissolution-threshold math, and the OriginSavedPartnership event that surfaces the mechanic in the feed.
Source: src/sim/components.rs, BondOrigin enum and Relationship::origin_history.
Relationship Affixes
Each relationship carries two boolean flags:
-
Tested: Set to
truewhen a friendship (strength >= 50) dips below 50 and then recovers back above 50. Also set during departure recovery -- if an elf nearly leaves but stays, friends who helped keep them get the Tested flag. A "Tested" bond is one that survived adversity. -
Fragile: Starts as
truefor all new relationships. Indicates the bond has not yet been reinforced. Set totrueon relationships where an elf recovered from near-departure without any friend support.
Source: src/sim/components.rs, Relationship struct -- tested and fragile fields.
Values & Formulas
Strength Progression Example
Starting from 0, with both elves actively working within 3 tiles and having similar aesthetics (distance < 0.3):
- Base delta: +1 (both active)
- Aesthetic bonus: +1 (affinity)
- Total per 50-tick cycle: +2 per elf (before social scaling)
- Social elf (> 0.7): +4 per cycle
- Balanced elf: +2 per cycle
- Personal elf (< 0.3): +1 per cycle
At +2/cycle, a balanced elf pair reaches Friend status (+50) in approximately 25 cycles = 1,250 ticks (~12.5 days).
Decay
Every 500 ticks, any relationship where the two elves are not within Manhattan distance 3 of each other decays by -1 strength. This means neglected friendships slowly fade, though it takes 500+ ticks of separation for a friend (50+) to drop back to acquaintance.
Source: crates/er-sim/src/sim/systems/social.rs, social_system -- decay branch at tick.is_multiple_of(500).
Capacity and Pruning
Each elf can hold at most 20 relationships. When the list exceeds 20, it is sorted by absolute strength (strongest bonds first) and truncated. This means the weakest acquaintanceships are pruned to make room for stronger bonds.
Source: src/sim/components.rs, Relationships::prune -- MAX_RELATIONSHIPS = 20.
Rivalries
When a relationship's strength falls to -30 or below, it reclassifies to Rival, and a separate set of mechanics activates: arguments at close range, public critiques during revels, escalation past -60, and reconciliation back above -30. Those mechanics — including the RivalryArgument, PublicCritique, CompetitionEscalated, and RivalryReconciled events — live on the dedicated Rivalries page.
This page covers the underlying strength model that creates the Rival classification in the first place; Rivalries covers what happens once an elf is in one.
Interactions
Mood Effects
The social mood system runs every 10 ticks and applies mood modifiers based on nearby relationships:
| Situation | Mood Modifier | Duration |
|---|---|---|
| Near a friend (distance <= 3) | +3 | 50 ticks |
| Near a rival (distance <= 3) | -2 | 50 ticks |
| Near beloved (partner within 3 tiles) | +5 | 50 ticks |
| Social elf (> 0.7) with any company nearby | +1 "Enjoying company" | 50 ticks |
| Personal elf (< 0.3) alone | +2 "Peaceful solitude" | 50 ticks |
| Personal elf (< 0.3) with 3+ nearby | -1 "Too many people" | 50 ticks |
Source: crates/er-sim/src/sim/systems/social.rs, social_mood_system -- distance checks at 3 (friends/rivals) and 5 (general nearby).
Company Need
The company need (0--100 scale) is influenced by the Social axis and proximity, running every 2 ticks:
| Elf Type | Condition | Effect |
|---|---|---|
| Social (> 0.7) | 2+ elves within distance 3 | +2 company/tick |
| Social (> 0.7) | Alone | -1 company/tick (lonely) |
| Personal (< 0.3) | Nobody within distance 5 | -2 company/tick (relieved) |
| Personal (< 0.3) | 3+ within distance 5 | +1 company/tick (overwhelmed) |
| Balanced | Any | Drifts toward 50 |
For Personal elves, "company" is inverted -- low values are good (solitude achieved).
Source: crates/er-sim/src/sim/systems/needs.rs, company_system.
Satisfaction
Friend count feeds directly into the satisfaction formula:
Satisfaction = inspiration x 0.3 + morale x 0.2 + friends x 5.0 + aesthetic_fit x 20.0 + revel_score x 0.1 + prestige x 0.15 + spike
Each friend contributes +5.0 to satisfaction. An elf with 4 friends gets +20 satisfaction from relationships alone, which can be the difference between staying and departing.
Mentor Bonds
Apprenticeship is a separate component, not a normal relationship. While the bond is active it does not appear in the relationship list. At graduation, the master and student exchange a permanent +15 to +25 strength delta (scaled by bond strength), almost always pushing the pair into Friend territory.
The graduation also writes a permanent TrainedBy lineage marker on the student, visible in their detail panel.
If the master's tradition axis is > 0.6 and the student's final skill exceeds the master's, the master takes a -10 relationship penalty to the student instead of the +10 a more open master would receive -- a traditional master who feels surpassed registers it as a slight rather than a triumph.
Source: crates/er-sim/src/sim/systems/apprenticeship.rs, apprenticeship_graduation_system -- bonus tiers at lines 322-324, surpass branch at line 348.
Departure Cascade
When an elf departs, remaining elves receive mood effects based on their relationship:
| Relationship to Departed | Mood Effect | Duration |
|---|---|---|
| Friend | -10 | 300 ticks |
| Rival | +3 | 100 ticks |
| Acquaintance | -2 | 100 ticks |
Close friends (strength >= 60) also enter a Mourning state for 200 ticks, gaining an additional -5 "Grieving" mood modifier. Mourning elves are drawn toward composing tributes.
Source: crates/er-sim/src/sim/systems/satisfaction.rs, satisfaction_system -- departure cascade section.
Tips
-
Cluster housing near workshops to maximize proximity time. Elves who rest and work near each other accumulate relationship strength fastest (+2 base for co-resting).
-
Watch the aesthetic positions of your elves. Pairs with similar aesthetics (distance < 0.3) gain bonds 50% faster. The Aesthetic Position panel shows each elf's 4D position.
-
Social elves are relationship engines -- their x2 multiplier means they bond (and feud) at double speed. Assign them roles that keep them near others.
-
Personal elves need space. They bond slowly (x0.5) and get stressed near crowds. Give them solitary tasks like distant gathering.
-
Friendships prevent departure. Each friend adds +5 satisfaction. An elf with 0 friends and low inspiration is at serious risk. Check the Satisfaction page for threshold details.
-
Don't ignore rival buildup. Two aesthetically opposed elves working together can drift to rivalry (-30) in about 15 cycles (750 ticks). Separate them before the -2 mood penalty kicks in.
-
Pruning is automatic at 20 relationships. If an elf has many weak acquaintances, stronger bonds are preserved. You don't need to manage this.
-
Friendships can become romance. Two friends who share emotional experiences accumulate romantic warmth. Friendship is a prerequisite for the SlowBurn and AestheticResonance warmth sources. Strong friends are more likely to fall in love -- and more likely to be devastated if it ends.
-
Partnerships are protected. The
adjust()method guards Partner relationships from being reclassified by the normal social system. Only romance progression creates Partners; only romance dissolution removes them. -
Master-student bonds compound friendships. A successful apprenticeship ends with both elves taking +15..+25 relationship -- often the largest single relationship event in the game. Pair masters and students whose aesthetic distance is small to also benefit from the post-graduation drift toward each other.
Rivalries
Not every bond is a friendship. Two elves who keep getting in each other's way — clashing aesthetics, repeated negative encounters, a public slight at the wrong revel — can drift into rivalry, and rivalry has its own social weather: arguments at close range, hostile critiques during performances, escalation into entrenched feud, and (sometimes) reconciliation back to acquaintance.
This page is the canonical home for the rivalry mechanic and the four cultural events it generates: RivalryArgument, PublicCritique, CompetitionEscalated, and RivalryReconciled. For the underlying relationship strength model — how any bond, including rivalry, is created and adjusted — see Relationships.
Overview
A relationship becomes a rivalry when its strength falls to -30 or below. Below that threshold, the bond is classified as RelationType::Rival, which unlocks four related behaviors:
- Mood penalties when the rivals are within 3 tiles of each other (handled by the social mood system — see Relationships → Mood Effects).
- Arguments triggered by the conflict system on rival proximity.
- Public critiques triggered when a rival in a revel audience Dislikes the composer's work.
- Escalation past -60 and reconciliation back above -30, each surfacing its own event.
Rivalries are not just costs — they fuel the rivalry inspiration channel, which can push elves toward composition properties like Manifesto, Obsessive, and Virtuosic. A well-tuned rivalry produces art the elves wouldn't otherwise make.
Source: crates/er-sim/src/sim/components.rs, Relationships::adjust — Rival classification at strength -128..=-30.
How Rivalries Form
Rivalries form through the same proximity-and-aesthetic-affinity machinery as friendships, just running in the negative direction. Two elves with strongly opposed aesthetics (4D distance > 0.9) working near each other receive a -2 per cycle penalty to their bond instead of a positive delta. After enough cycles of negative drift, the strength crosses -30 and the relationship reclassifies to Rival.
Other paths into rivalry:
- Argument deltas: each rivalry argument applies an additional -2 to both sides of the bond, deepening it.
- Composition disliked by an enemy: if a relationship was already negative, a Dislike reaction at a revel doesn't change the strength directly, but the social cost of the public critique makes future positive drift harder (the composer's mood drop reduces their willingness to be near the critic).
See Relationships → Formation Triggers for the full delta tables. The same machinery, just with negative aesthetic affinity producing rivalry instead of friendship.
Rival threshold: relationship strength <= -30 (and not a Partner — partnerships are protected from reclassification).
Source: crates/er-sim/src/sim/components.rs, Relationships::adjust — strength clamped to -100..100, kind reclassified unless Partner.
The Conflict System
Once a relationship is classified as Rival, it becomes visible to the conflict system, which runs every 10 ticks and scans all elf pairs for:
- Manhattan distance <= 3 (rivals must be near each other for anything to happen).
- Both sides classified as Rival (the rivalry must be mutual — strength check on at least one side).
- Per-pair cooldown elapsed (no two arguments between the same pair within
DAY_LENGTH × 3ticks — three game days).
When all three conditions are met, the system rolls a personality-modulated argument chance:
Argument chance = 10% + 15% × max(impulsivity_a, impulsivity_b)
Impulsivity is a personality axis on each elf, mapped through a sigmoid into a 0–1 multiplier. Two patient elves argue at base ~10% per check; a pair where one elf is highly impulsive argues at ~25% per check. Since the system runs every 10 ticks, effective per-tick rates are ~1–2.5%.
Source: crates/er-sim/src/sim/systems/social.rs, conflict_system — proximity at Manhattan <= 3, cooldown at day_length * 3, chance formula at 0.10 + 0.15 * impulsivity.max().
Argument Events (RivalryArgument)
When a check succeeds, the system emits a RivalryArgument event with both elf names and a topic derived from the pair's greatest aesthetic-axis disagreement:
| Greatest disagreement axis | Topic |
|---|---|
| structure | "form versus freedom" |
| tradition | "tradition versus innovation" |
| emotion | "emotion versus intellect" |
| social | "art's purpose in the community" |
If aesthetic data is missing for either elf, the topic falls back to "artistic direction".
Argument Effects
Both elves take immediate consequences:
| Effect | Value | Duration |
|---|---|---|
| Mood penalty (both) | -2 to -5 ("Argued with rival"), scaled by pride | 50–100 ticks (longer for proud elves) |
| Rivalry inspiration boost (both) | +3 | Permanent (capped at 100) |
| Relationship strength delta (both) | -2 | Permanent |
Pride modulation matters. A high-pride elf takes a deeper mood hit (down to -5) for a longer duration (up to 100 ticks). A humble elf brushes off the same insult with -2 mood for 50 ticks. The formula is mood = -2 - (pride × 3) and duration = 50 + (pride × 50).
Personality Crisis Triggers
Arguments can also kick a fragile elf into a personality crisis, surfaced by the PersonalityCrisis event:
- EgoWound: pride > 0.85 AND morale < 40 AND no existing wound → +150 ticks of "Wounded pride" (-8 mood) and an
EgoWoundcomponent. - LongPatienceBroken: patience > 0.85, 10% chance per qualifying argument → +50 ticks of "Patience shattered" (-5 mood) and a
LongPatienceBrokencomponent.
Argument priority in the event feed: Notable (worth reading, not pause-worthy).
Source: crates/er-sim/src/sim/systems/social.rs, conflict_system and argument_topic — mood/inspiration/strength deltas; pride scaling at lines 525–540; crisis triggers at lines 596–620.
Source: crates/er-sim/src/sim/events.rs, EventPriority — RivalryArgument is Notable.
Public Critique (PublicCritique)
Rivalry follows elves into the revel hall. When a composer performs at a revel, every audience member rolls an aesthetic reaction (Love / Enjoy / Indifferent / Dislike). If a rival of the composer rolls Dislike, the system emits a PublicCritique event:
| Effect | Value | Duration |
|---|---|---|
| Composer mood penalty | -4 ("Publicly criticized") | 80 ticks |
| Rivalry inspiration boost for the critic | +2 | Permanent (capped at 100) |
This stacks on the base Dislike penalty (-3 mood for 50 ticks). A composer publicly critiqued by a rival receives a total of -7 mood from one event.
PublicCritiques are also recorded as PerformanceIncident::PublicCritique in the revel archive, preserving the critic and target names for later inspection.
If the critique pushes the revel into majority-negative reception, the composer additionally takes the -30 satisfaction "ego crisis" spike. See Revels → Ego Crisis for that interaction.
Priority: Notable.
Source: crates/er-sim/src/sim/systems/revel.rs, revel_tick_system lines 294–334 — rival check on Dislike reactions, mood and inspiration deltas.
Escalation (CompetitionEscalated)
When an argument's -2 strength delta pushes the bond past -60, the system emits a CompetitionEscalated event. This marks a deeply entrenched rivalry — the pair has argued (or drifted) enough times that hostility is now a defining social feature of their relationship.
Escalation threshold: relationship strength crosses from above -60 to <= -60.
The event itself fires only on the crossing — re-escalating after recovery requires the bond to first recover above -60 and then fall again. There is no separate "deeply escalated" state machine; the threshold is purely an event-emission gate. Escalated rivals continue to use the same conflict-system mechanics, just from a deeper hole that takes longer to climb out of.
Priority: Notable.
Source: crates/er-sim/src/sim/systems/social.rs, conflict_system lines 646–652 — escalation check on strength crossing from > -60 to <= -60.
Reconciliation (RivalryReconciled)
A rivalry can heal. When a Rival relationship's strength rises back above -30, the bond reclassifies to Acquaintance and the system emits RivalryReconciled.
Reconciliation threshold: relationship strength crosses from <= -30 to > -30.
How does a rivalry recover? Through the normal social system's positive proximity deltas: two former rivals working closely together with aligned aesthetics slowly heal the rift. Each cycle they get along can restore +1 to +2 strength; each argument re-applies -2.
This is why the 3-day per-pair argument cooldown matters for reconciliation: it gives the social system time to apply enough positive deltas between arguments. A pair that argues every 3 days but gets along between sessions can still trend toward reconciliation if the positive deltas outweigh the -2 per argument.
Reconciliation does not restore lost mood, lost inspiration, or the strength_min watermark on the relationship — that watermark is permanent (a relationship can be marked "once a rivalry" forever, which feeds the RivalryToLove partnership origin if the bond eventually flips all the way to Partner).
Priority: Notable.
Source: crates/er-sim/src/sim/systems/social.rs, conflict_system lines 654–660 — reconciliation check; crates/er-sim/src/sim/components.rs Relationships::adjust for strength_min semantics.
When Rivals Depart
When a rival departs, remaining elves who had a Rival bond with them receive a +3 mood "Rival departed" modifier for 100 ticks. See Satisfaction → Departure Cascade for the full mood effect table including friend and acquaintance reactions.
Tips
-
Don't ignore rival buildup. Two aesthetically opposed elves working together can drift to rivalry (-30) in roughly 15 social cycles (~750 ticks). Once they cross -30, the conflict system starts firing arguments, and each argument deepens the rivalry by another -2 while costing both elves mood. Separate them early if you don't want a feud, or commit to it if you do.
-
Pride is a multiplier on every argument. A high-pride elf takes -5 mood for 100 ticks per argument; a humble elf takes -2 for 50. If two proud elves become rivals, each argument is genuinely destabilizing. Watch for the
EgoWoundevent — it means a pride > 0.85 elf has been pushed past their morale threshold. -
Rivalry is a creative engine. Each argument grants both elves +3 rivalry inspiration, and public critiques add +2 for the critic. The Manifesto, Obsessive, and Virtuosic composition properties all require rivalry > 30. A composer with one well-tuned rival often produces more (and more pointed) art than one with none.
-
Public critiques compound disastrously. Composer mood, fan loss, ego crisis, and a permanent record in the revel archive all stack. A composer who is publicly criticized at a revel that also tilts majority-negative takes -7 mood plus -30 satisfaction in one tick. Schedule revels with the audience composition in mind — a rival in the audience is a coin flip, not a guarantee.
-
Reconciliation is slow but real. Two rivals working together with aligned aesthetics can heal back to Acquaintance over time. The 3-day per-pair argument cooldown is the window where reconciliation is possible. If you want to repair a rivalry, give the pair shared work in close proximity and avoid revel co-attendance until the bond crosses -30.
-
The
strength_minwatermark is permanent. A reconciled rivalry leaves a trace: the relationship remembers it was once at -30 or worse. This enables the RivalryToLove partnership origin — a former rival who became a partner has a more resilient bond than one who was always a friend. Reconciled rivals are not "back to neutral"; they have history. -
Personality crises are rare but durable. EgoWound (150 ticks) and LongPatienceBroken (50 ticks) both apply mood pushes that last long enough to risk a cascade into the satisfaction departure window. If you see
PersonalityCrisisin the feed, the affected elf is briefly fragile.
Related Pages
- Relationships — the underlying strength model, formation triggers, and bond classifications.
- Revels — performance reactions, including how a rival in the audience produces a PublicCritique.
- Inspiration — the rivalry channel and which composition properties it unlocks.
- Compositions — Manifesto / Obsessive / Virtuosic properties and their rivalry inspiration requirements.
- Partnership Origins — how a reconciled rivalry that becomes a partnership unlocks the RivalryToLove origin.
- Satisfaction & Departure — rivalry's contribution to the mood/satisfaction loop and the departure cascade.
Apprenticeship
A skilled elf and a novice working side by side at the same craft can form a master-student bond. Over time the apprentice's hands grow surer, their aesthetic drifts toward the master's, and their compositions carry the master's name as a stylistic signature. When the apprentice reaches mastery, the bond resolves -- sometimes in pride, sometimes in resentment.
Overview
Apprenticeship is a one-to-one bond between two elves working the same skill. There is no manual assignment: the simulation matches eligible pairs every 50 ticks based on skill, proximity, and current task. Bonds form silently, deliver their effects continuously while the pair stays close, and resolve at graduation when the student reaches skill 10.
A master can hold at most one active apprentice. A student can have at most one master. Bonds dissolve cleanly if either elf departs the colony, and lapse if 500 ticks pass without proximity.
Three tick systems run the lifecycle, all every 50 ticks:
- Formation -- match eligible pairs, dissolve stale and dead-master bonds
- Effects -- award proximity XP, refresh master mood, count bond strength
- Graduation -- detect skill-10 students, sever the bond, apply social effects
A separate aesthetic drift system runs once per day to slowly pull the apprentice's aesthetic position toward the master's.
Source: crates/er-sim/src/sim/systems/apprenticeship.rs; crates/er-sim/src/sim/systems/aesthetics.rs, apprenticeship_aesthetic_drift_system.
How It Works
Formation Eligibility
Every 50 ticks the formation system snapshots all elves and looks for unbonded pairs that match all of:
| Condition | Threshold |
|---|---|
| Master skill in current-task skill | >= 8 |
| Student skill in same skill | <= 3 |
| Same exercised skill (Music / Building / Gathering) | both must currently be performing the matching task |
| Manhattan distance between master and student | <= 5 tiles |
| Master not already mentoring | no HasApprentice component |
| Student not already apprenticed | no Apprenticeship component |
Only Compose, Build, and Gather tasks count as skill exercises. A master idling does not exercise their skill, and so cannot form a bond that tick. A pair must be actively working the same craft, near each other for the match to fire.
The matcher takes the first eligible student per master (sequential pass; no scoring). One student per master per pass.
Source: crates/er-sim/src/sim/systems/apprenticeship.rs, apprenticeship_formation_system -- task_to_skill, eligibility filters at lines 137-148.
Bond Strength
bond_strength (0--255) increments by +1 every 50 ticks the pair stays within 5 tiles. It is the count of proximity ticks the bond has accumulated. It never decreases except by dissolution. Three thresholds matter at graduation:
| Bond Strength | Graduation Bonus |
|---|---|
| 0 -- 99 | +15 relationship |
| 100 -- 199 | +20 relationship |
| 200+ | +25 relationship |
Reaching 200 takes 200 proximity ticks = 10,000 ticks of continuous near-master work. A bond can persist longer than that without graduating if the student's skill is still climbing.
Source: crates/er-sim/src/sim/systems/apprenticeship.rs, apprenticeship_effects_system; graduation tiers at lines 322-324.
Effects (Per 50-tick Tick)
While the pair is within 5 tiles:
| Effect | Target | Value |
|---|---|---|
| Bonus XP in shared skill | Student | +1 XP (on top of normal compose/build/gather drip) |
| "Teaching" mood modifier | Master | +2 for 50 ticks (refreshed each cycle) |
bond_strength increment | Bond | +1 |
last_bond_tick update | Bond | set to current tick |
The XP bonus is small but constant. Over a full apprenticeship a student typically gains hundreds of bonus XP from proximity alone, accelerating their climb to skill 10.
Source: crates/er-sim/src/sim/systems/apprenticeship.rs, apprenticeship_effects_system -- proximity check at line 217, mutations at lines 224-243.
Aesthetic Drift
Once per day, every apprentice within 5 tiles of their master drifts 0.02 along each aesthetic axis (structure, tradition, emotion, social) toward the master's value. This is 2x the friend drift rate. The pull is signed -- it always moves toward the master, clamped to [0.0, 1.0].
Apprentices learn what their master values, not just what their master does. An apprentice studying under a deeply traditional master will gradually become more traditional themselves, even if they entered the bond as a radical.
This drift persists in the student's aesthetic position after graduation -- the bond ends, but the imprint remains.
Source: crates/er-sim/src/sim/systems/aesthetics.rs, apprenticeship_aesthetic_drift_system -- APPRENTICE_DRIFT_RATE = 0.02, proximity check at 5 tiles.
Graduation
When a student's skill in the bonded craft reaches 10, the next graduation pass resolves the bond:
- Remove
Apprenticeshipfrom student andHasApprenticefrom master. - Attach a permanent
TrainedBy { master_name, skill }component to the student. - Apply mutual relationship bonus (+15, +20, or +25 depending on bond strength).
- Push "Graduated" mood +8 on the student for 300 ticks.
- Emit a
Graduationevent (notable priority -- shows in event feed).
The TrainedBy component is permanent and visible in the elf detail panel as Trained by {master} ({skill}). It marks lineage even after the master has departed.
Source: crates/er-sim/src/sim/systems/apprenticeship.rs, apprenticeship_graduation_system -- skill 10 gate at line 279, bonus tiers at lines 322-324.
The Surpass Branch
If the graduating student's skill is strictly greater than the master's at graduation time, a StudentSurpassedMaster event fires. The master's reaction depends on their tradition axis:
| Master Tradition | Reaction | Effects |
|---|---|---|
| > 0.6 (traditional) | Resentment | Master: -10 relationship to student; +5 Rivalry inspiration |
| <= 0.6 (open) | Pride | Master: +10 relationship to student; +4 "Proud Teacher" mood for 200 ticks |
A traditional master expects deference and grades the apprentice's success as a slight against the lineage. An open master treats it as the system working as intended -- the student became greater than the teacher, which is the point.
This branch fires once per (student, master) pair for the lifetime of the colony, tracked by the surpass_acknowledged set. A student who surpasses a master, leaves the colony, and returns will not re-trigger the event.
Source: crates/er-sim/src/sim/systems/apprenticeship.rs, apprenticeship_graduation_system -- surpass check at line 348, tradition threshold 0.6 at line 352.
Maintenance and Dissolution
The formation system also handles cleanup every 50 ticks:
| Condition | Action |
|---|---|
| Master entity no longer exists | Remove student's Apprenticeship component silently |
tick - last_bond_tick > 500 (no proximity for 500+ ticks) | Dissolve bond: remove Apprenticeship and HasApprentice |
| Either elf departs the colony | Cleanup runs; if student loses master, they get -8 "Mentor departed" mood for 200 ticks |
Stale bonds dissolve quietly (no event). A master and student who drift apart -- because tasks reassign or one moves to a distant zone -- will lose the bond after about 10 game days of separation.
Source: crates/er-sim/src/sim/systems/apprenticeship.rs, apprenticeship_formation_system -- staleness at line 104; cleanup_apprenticeship_on_departure.
Interactions
Composition Style
Every composition by an active apprentice gets the InStyleOf({master_name}) composition property. It is cap-exempt (does not count toward the 2-property limit), inserted alongside the seasonal property.
A composition titled Wistful Sonata of Fellowship written by an apprentice of a master named Calenor will display the property as Style: Calenor. It marks the work as belonging to a lineage. The property is attached only while the bond is active -- post-graduation compositions do not carry it (the lineage lives in the TrainedBy component instead).
Source: crates/er-sim/src/sim/systems/compose.rs, lines 189-192; crates/er-sim/src/sim/components.rs, CompositionProperty::InStyleOf.
Mentor Relationships
The master-student bond does not show up in the relationship list while the apprenticeship is active -- it is a separate component. Only at graduation do the two elves exchange a permanent +15..+25 relationship strength bonus, which generally pushes them firmly into Friend territory and often beyond.
The surpass branch can flip this: a traditional master who feels surpassed loses 10 relationship to the student (net result: typically still positive, but smaller). An open master gains an extra +10 (deep, lasting friendship).
Lifecycle and Departure
When a master departs, their student loses the bond and takes a -8 mood penalty. The aesthetic drift the student has accumulated remains. The TrainedBy component, if already set by an earlier graduation under a different master, is preserved -- a student can only have one TrainedBy at a time, but lineage records are not retroactively erased.
Elder masters who depart through fulfilled departure leave behind students who must finish learning on their own. The master's name lives on as master_name in the (now-orphaned) lineage record.
Aesthetic Position Convergence
Pairs that maintain the bond for a full season can drift up to 0.02 x days in each axis -- enough to noticeably shift an apprentice's aesthetic identity. Two elves who started 0.4 apart in tradition can converge to within 0.2 over a long apprenticeship.
This is one of the few systems in the game that changes an elf's aesthetic position after personality maturation. Most aesthetic shifts come from environmental drift; apprenticeship is targeted, directional drift.
Tips
-
Pair masters and apprentices in the same workshop. The matching system requires both elves to be actively performing the same task within 5 tiles. Co-locating composer huts, builder yards, and gathering paths gives the matcher more candidate pairs to work with.
-
Aesthetic alignment is not required for formation -- only skill and proximity. A radical apprentice can bond with a traditional master, and the drift system will pull them toward the master's aesthetic over time. This can be used deliberately to shift a colony's aesthetic balance: appoint elders with the values you want to preserve, and apprentices will inherit them.
-
Watch for the surpass event. If your master's tradition is high (> 0.6), a strong apprentice will eventually surpass them and trigger resentment. The master's +5 Rivalry inspiration can fuel new compositions (Manifesto, Obsessive, Virtuosic require Rivalry > 30) -- but the -10 relationship hit can also push them toward departure if they were already strained.
-
Long apprenticeships are not punished. A bond at strength 200+ gives a +25 relationship bonus at graduation, vs +15 for a quick match. There is no maximum duration; bonds can run thousands of ticks. The only cost is opportunity -- a low-skill student locked into one master cannot learn from anyone else.
-
InStyleOf is a lineage marker, not a quality bonus. The property does not affect mastery, originality, or emotional scores -- it is purely informational. A composition's quality still depends on the composer's own skill, inspiration, and mood. The marker matters for cultural history (which master shaped which compositions), not for revel performance.
-
Don't over-cluster apprenticeships in one craft. A master can only hold one apprentice. If you have three skill-9 builders and only one skill-2 builder, two of your masters will sit unbonded. Spread skill levels across crafts to maximize the number of active mentorships.
-
Bonds dissolve quietly. If a master is reassigned to a task that doesn't exercise the bonded skill, the pair will stop accumulating proximity ticks and the bond will lapse after 500 ticks of inactivity. There is no warning -- the apprentice simply loses the bond and will be re-matched (potentially to a different master) on the next formation pass.
Romance
Elves don't choose who to love. Attraction emerges from shared emotional experiences -- grief, transcendence, comfort in dark moments. Romance in your settlement is asymmetric, gradual, and deeply shaped by personality.
Overview
Each elf can hold up to 3 simultaneous attractions. Attractions accumulate warmth over time through shared experiences. When warmth crosses key thresholds, the relationship progresses through stages: attraction, courtship, and partnership. Partnerships can dissolve under aesthetic drift or status asymmetry, leaving permanent personality scars.
Romance runs on three tick systems:
- Warmth accumulation (every 50 ticks)
- Progression (every 50 ticks)
- Dissolution (every 100 ticks)
Source: crates/er-sim/src/sim/systems/romance.rs -- romance_warmth_system, romance_progression_system, romance_dissolution_system.
How It Works
Warmth Sources
Warmth grows through shared emotional moments, not compatibility scores. Two elves must experience something together for attraction to build.
| Source | Warmth | Catalyst? | Condition |
|---|---|---|---|
| Shared transcendence | +8.0 | Yes | Both loved the same composition at the last revel |
| Shared grief | +6.0 | Yes | Both mourning the same departed friend |
| Comfort in darkness | +5.0 | Yes | One elf's morale < 30, other within 3 tiles |
| Aesthetic resonance | +2.0 | No | Aesthetic distance < 0.25 AND friends |
| Slow burn | +1.0 | No | Friends AND within 5 tiles |
Catalyst events are special -- they don't just add warmth, they unlock the courtship transition. A spark without a catalyst stays as unacted attraction.
Source: crates/er-sim/src/sim/systems/romance.rs, romance_warmth_system -- catalyst flag set on SharedTranscendence, SharedGrief, ComfortInDarkness.
Personality Multipliers
All warmth gains are scaled by the attracted elf's personality:
| Personality | Multiplier | Effect |
|---|---|---|
| Bold (boldness > 0.7) | x1.3 | Falls faster |
| Cautious (boldness < 0.3) | x0.6 | Falls slowly, carefully |
| Proud (pride > 0.7) | x0.8 | Slightly guarded |
A cautious, proud elf accumulates warmth at 0.6 x 0.8 = 48% of the base rate. A bold elf with low pride accumulates at 130%. Love is not equally distributed.
Source: crates/er-sim/src/sim/systems/romance.rs, romance_warmth_system -- Personality::sigmoid_effect applied to boldness/pride thresholds.
Progression Stages
| Stage | Threshold | Requirements |
|---|---|---|
| Attraction | warmth > 0 | Automatic when any warmth source fires |
| Courtship | warmth >= 60.0 | Mutual warmth >= 60 AND catalyst within last 100 ticks |
| Partnership | warmth >= 85.0 | Both courting each other AND both warmth >= 85 |
Courtship requires both elves to have warmth >= 60 for each other (mutual) and a recent catalyst event (within 100 ticks of the spark). One-sided warmth above 60 doesn't trigger courtship -- it starts the unrequited clock.
Partnership is the final stage. Only romance_progression_system creates partnerships; only romance_dissolution_system removes them. The Relationships::adjust method guards Partners from being reclassified by the normal social system.
Source: crates/er-sim/src/sim/systems/romance.rs, romance_progression_system -- COURTSHIP_THRESHOLD = 60.0, PARTNERSHIP_THRESHOLD = 85.0.
Unrequited Love
When one elf's warmth crosses 60 (spark) but the target's warmth stays below 30 for 200 ticks, the attraction resolves as unrequited:
- An
Unrequitedpersonality scar is applied - Mood penalty: -5 for 150 ticks
- The attraction is removed
This is one of the few ways scars form outside of partnership dissolution. A cautious elf who builds warmth slowly may carry unrequited scars from multiple failed attractions over a long game.
Source: crates/er-sim/src/sim/systems/romance.rs, romance_progression_system -- unrequited branch at 200 ticks after spark_tick.
Dissolution
Partnerships accumulate dissolution pressure over time. When pressure reaches 100.0, the partnership ends.
Pressure sources (per 100-tick cycle):
| Source | Pressure | Condition |
|---|---|---|
| Aesthetic alienation | (distance - 0.8) x 5.0 | Aesthetic distance > 0.8 |
| Status asymmetry | (gap - 0.3) x 3.0 | Prestige gap > 0.3 |
| Partner is rival | +10.0 | Relationship type = Rival |
Patience modifier: all pressure is scaled by (1.5 - patience). A patient elf (patience = 1.0) scales pressure by 0.5x. An impulsive elf (patience = 0.0) scales by 1.5x.
Proximity repair: when partners are within 3 tiles, pressure decreases by up to -2.0 per cycle (minimum delta: -5.0). Keeping partners near each other physically slows dissolution.
Dissolution events:
| Trigger | Scar Type | Who Gets Scarred |
|---|---|---|
| Betrayal (partner courting someone else) | Betrayal | Only the betrayed elf |
| Aesthetic drift | Heartbreak | Both elves |
| Prestige asymmetry | Heartbreak | Both elves |
| Growing apart | Heartbreak | Both elves |
Betrayal is instant -- it doesn't wait for pressure to reach 100. If one partner begins courting a third elf, the partnership dissolves immediately.
Source: crates/er-sim/src/sim/systems/romance.rs, romance_dissolution_system -- pressure cap at 100.0, patience modifier at 1.5 - patience.
Partnerships carry origin markers — records of how the bond grew (apprenticeship, shared grief, rivalry-turned-love) that shift the dissolution threshold above or below baseline 100. A partnership with Apprenticeship and SharedCrisis origins dissolves at pressure 135, not 100; a RivalryToLove partnership dissolves at 90. When origin history saves a partnership from a pressure cycle that would have ended it at baseline, an OriginSavedPartnership event fires in the feed.
Values & Formulas
Warmth Timeline Example
Two bold friends (boldness > 0.7) who share a revel transcendence moment:
- SharedTranscendence: +8.0 x 1.3 = +10.4 (catalyst set)
- SlowBurn each 50-tick cycle: +1.0 x 1.3 = +1.3
- At cycle 39 (~1,950 ticks): warmth crosses 60.0 -- spark
- If mutual and catalyst within 100 ticks: courtship begins
- Continued SlowBurn + AestheticResonance: ~2.0-3.3 per cycle
- At ~cycle 58 (~2,900 ticks): warmth crosses 85.0 -- partnership
A cautious pair (x0.6) would take roughly twice as long to reach the same thresholds.
Dissolution Timeline Example
Partners with aesthetics drifting apart (distance = 0.9):
- Pressure per cycle: (0.9 - 0.8) x 5.0 = 0.5
- Patient elf (patience = 0.8): 0.5 x 0.7 = 0.35 per 100 ticks
- Impulsive elf (patience = 0.2): 0.5 x 1.3 = 0.65 per 100 ticks
- If not near each other: ~150-290 cycles (15,000-29,000 ticks) to dissolve
- If near each other: repair of -2.0 per cycle far exceeds pressure -- stable
Aesthetic drift alone dissolves partnerships slowly. Add a prestige gap and the pressure compounds rapidly.
Interactions
Mood Effects
| Situation | Mood | Duration |
|---|---|---|
| Near beloved (partner within 3 tiles) | +5 | 50 ticks |
| Courting someone | +5 | While courting |
| Unrequited resolution | -5 | 150 ticks |
| Publicly criticized by partner | -4 | 80 ticks |
Source: crates/er-sim/src/sim/systems/social.rs, social_mood_system -- "Near beloved" modifier.
Satisfaction
Romance contributes directly to satisfaction:
| Situation | Satisfaction |
|---|---|
| Has partner | +10.0 |
| Courting someone | +5.0 |
| Per scar | -1.5 |
| Unrequited attraction (active) | -3.0 |
Source: crates/er-sim/src/sim/systems/satisfaction.rs, satisfaction_system -- romance_bonus section.
Composition Properties
Romance states unlock composition properties when an elf composes:
| Condition | Property |
|---|---|
| Has partner, partner satisfied (mood > 0) | Contentment |
| Heartbreak scar AND currently unpartnered | Heartbreak (+15 emotional) |
| Currently courting | LoveSong (+10 emotional) |
| Partner just departed | Elegy (compound property) |
| PartnerLoss scar for currently-mourned elf | +10 emotional (no slot) |
See Scars — Composition Properties for the full picture.
Source: crates/er-sim/src/sim/systems/compose.rs, compose_system -- partner contentment and scar-driven properties.
Tips
-
Shared transcendence is the strongest catalyst. Schedule revels with compositions that polarize the audience -- elves who both Love the same piece get +8 warmth and a catalyst flag. This is the fastest path to courtship.
-
Grief bonds are powerful. When an elf departs, watch who mourns together. Shared grief (+6, catalyst) can spark romances between elves who barely knew each other before the loss.
-
Keep partners near each other. Proximity repair (-2.0/cycle) counteracts mild aesthetic drift. Partners who work in the same zone are more stable than those assigned to opposite ends of the settlement.
-
Watch for unrequited spirals. A cautious elf (x0.6 warmth) paired with a bold elf (x1.3 warmth) can create asymmetric attraction -- the bold elf's warmth climbs past 60 while the cautious elf's is still at 35. The 200-tick unrequited clock starts for the bold elf.
-
Betrayal is instant and one-sided. If a partnered elf begins courting someone new, the existing partnership dissolves immediately. Only the betrayed elf gets the Betrayal scar. The betrayer walks away clean.
-
Scars accumulate. An elf who has been through two heartbreaks carries -3.0 satisfaction permanently. Three unrequited loves add another -4.5. Multiple scars can push an elf toward departure even if everything else is going well.
Partnership Origins
Partnerships remember how they began. When two elves become Partners, the stages they passed through on the way — a teaching bond, a shared grief, a rivalry that softened — are stamped onto the partnership as origin markers. Markers are identity, not numerical bonuses. They don't inflate strength. They make partnerships that grew through specific hardships harder to break, and they tell a visible story about what the bond is made of.
Overview
Every Partner relationship carries an origin_history: an append-only list of (origin, day) entries recorded in the order they happened. Three triggers currently write origins:
- Apprenticeship — the pair graduated a master-student bond before becoming Partners
- SharedCrisis — the pair comforted each other through grief or darkness
- RivalryToLove — the pair were once rivals (strength reached -30 or below) before becoming Partners
Non-Partner relationships do not carry origin history. When a partnership dissolves, origin_history is cleared — if the elves later re-partner, their history starts fresh.
Origins have one gameplay effect: they shift the dissolution pressure threshold. Baseline partnerships dissolve at pressure 100; partnerships with origins dissolve at 100 + delta, where the delta is computed from history with per-variant caps. When an origin-strengthened partnership survives a pressure cycle it would have dissolved under at baseline, a CulturalEvent::OriginSavedPartnership fires — the moment the mechanic becomes visible to you.
Source: crates/er-sim/src/sim/components.rs, Relationship::origin_history; crates/er-sim/src/sim/systems/origin_balance.rs.
How It Works
Origin Triggers
Each origin is written at the moment of a specific semantic event. The write is deduplicated per event, not per system firing — a single underlying event produces at most one entry even if the tick system it rides on runs many times.
| Origin | Trigger Condition | Timing |
|---|---|---|
| Apprenticeship | Master and student are already Partners at graduation, OR the pair graduated with bond_strength >= 60 (of 0--255) and later becomes Partners within 30 in-game days | Graduation tick (if already Partner) or partnership-formation tick (if pair partners later) |
| SharedCrisis | ComfortInDarkness or SharedGrief warmth catalyst fires for the pair during an active Mourning episode | Tick of the first catalyst firing within that mourning episode |
| RivalryToLove | Pair transitions to Partner AND strength_min <= -30 at the moment of transition | Partnership-formation tick |
Source: crates/er-sim/src/sim/systems/apprenticeship.rs, apprenticeship_graduation_system; crates/er-sim/src/sim/systems/romance.rs, SharedCrisis write in romance_warmth_system and RivalryToLove detector in romance_progression_system.
The strength_min Invariant
To detect RivalryToLove after the fact, every relationship tracks strength_min — the lowest strength it has ever held. Every strength change updates it via strength_min = strength_min.min(new_strength). The field is monotonic (only decreases) until dissolution, when it resets to the current strength.
Rivalry sits at strength -30 or below. If a pair's strength_min ever dipped to -30 or lower, then later climbed back up through reconciliation and courtship into partnership, the evidence of the old rivalry is still there when the partnership forms. Without strength_min there would be no way to know — the current strength at formation time is always positive.
Hard-set strength assignments (partnership formation at +80, betrayal at -50) also update strength_min inline, so dramatic swings are not missed.
Source: crates/er-sim/src/sim/components.rs, Relationship::strength_min and Relationship::adjust_strength.
SharedCrisis Deduplication
A bereaved elf enters a Mourning state for 150--300 ticks. During that time, romance_warmth_system runs every 50 ticks and may fire a ComfortInDarkness or SharedGrief catalyst between the mourner and a nearby friend — potentially 3--6 times over one bereavement.
Without deduplication, a single shared death would stamp 3--6 SharedCrisis entries onto the partnership's history, undermining the per-variant cap that prevents runaway resilience. Instead, the trigger keys on the (pair, departed_friend) tuple during the active Mourning episode: exactly one entry is written per mourning episode, regardless of how many catalyst firings occur within it. A second, later bereavement — a different departed friend — produces a second entry; repeated firings during the same bereavement do not.
This keeps the origin_history count of SharedCrisis entries equal to the number of distinct grief events the pair lived through together, not the number of times the sim ticked over during them.
Source: crates/er-sim/src/sim/systems/romance.rs, SharedCrisis dedup table keyed by mourning-instance ID.
Pending Origins
Some origins are determined before a partnership exists. When a strong apprenticeship (bond_strength >= 60) graduates without the pair being Partners yet, the trigger stores a pending entry in a resource-side table keyed by the pair's stable ID hash. If the pair later becomes Partners within 30 in-game days, the pending entry drains into the new partnership's origin_history with the formation tick. If either elf departs or dies before then, the pending entry is cleared.
Pending entries are not saved — they represent in-flight causal state, not persistent history. A save/load during the 30-day window will lose the pending entry. This is accepted: the window is short, and the mechanic does not rely on save durability.
Source: crates/er-sim/src/sim/pending_origins.rs, PendingOriginWrites resource.
Dissolution Resistance
Partnership dissolution pressure accumulates per the rules in Romance. The dissolution threshold is 100 + origin_threshold_delta, computed from origin_history:
| Origin | Per-Entry Delta |
|---|---|
| Apprenticeship | +20 |
| SharedCrisis | +15 (capped at 3 occurrences = +45 max) |
| RivalryToLove | -10 |
| Workshop, Revel, Critique, Rescue | 0 (reserved for future triggers) |
Stacking: Positive deltas sum, then clamp to a total of +50. Negative deltas (RivalryToLove) are added after the positive cap — so a rivalry bond always registers as fragility, even in a partnership stacked with Apprenticeship and SharedCrisis. Empty history gives delta 0 and the baseline threshold 100.
Worked examples:
[Apprenticeship]→ +20 → threshold 120[Apprenticeship, SharedCrisis]→ +35 → threshold 135[SharedCrisis × 5]→ +45 (cap of 3 holds) → threshold 145[Apprenticeship, SharedCrisis × 3]→ +65 → cap at +50 → threshold 150[Apprenticeship, SharedCrisis × 3, RivalryToLove]→ +50 capped, then -10 → threshold 140[RivalryToLove]alone → -10 → threshold 90 (dissolves easier than baseline)
Source: crates/er-sim/src/sim/systems/origin_balance.rs, origin_threshold_delta.
The Save Event
CulturalEvent::OriginSavedPartnership fires when romance_dissolution_system finds a partnership where:
- Accumulated pressure would cross baseline 100 (dissolution would have fired without origins), AND
- Pressure is still below the origin-adjusted threshold (dissolution does not fire with origins), AND
- The prior pressure was under 100 (this is the transition tick, not every tick afterward)
It fires once per such crossing and reads in the event feed as a brief line naming both elves and the origins that saved them: "<A> and <B>'s partnership endured — an apprenticeship once shared, a crisis weathered together". The phrasing varies per origin: SharedCrisis reads as "a crisis weathered together", RivalryToLove as "a rivalry turned to love", Apprenticeship as "an apprenticeship once shared".
This is the mechanic made visible. A silent dissolution-resistance modifier would be invisible to the player; the event surfaces the moment the bond's history actually kept it alive.
Source: crates/er-sim/src/sim/systems/romance.rs, romance_dissolution_system save-detection logic; crates/er-sim/src/sim/events.rs, CulturalEvent::OriginSavedPartnership::description.
Reading Origin History on an Elf
The Relationships screen renders each Partner's origin history as a single extra line below the partner row:
Partner Kellen [#####.....] 80
grew from apprenticeship (day 14), then shared crisis (day 47)
Format rules:
- The first entry uses grew from; subsequent entries use then, comma-separated
- Variants render as lowercase words:
Apprenticeship→ "apprenticeship",SharedCrisis→ "shared crisis",RivalryToLove→ "rivalry to love" - Day number =
tick / 100using the canonicalDAY_LENGTHconstant - Empty history → no line rendered at all (no placeholder text)
- Non-Partner relationships → no line
There is no truncation: a partnership with a dense history renders as one long line that may extend beyond the panel width. Dense histories are rare in practice; this is accepted until data shows otherwise.
Source: crates/er-sim/src/render_terminal.rs, format_origin_history_line.
Values & Formulas
Threshold Timeline Example
Two elves who graduated a strong apprenticeship together (Apprenticeship origin written at graduation, +20) and later lost a close friend together during a Mourning episode (one SharedCrisis entry written at the first catalyst firing, +15):
- Empty partnership threshold: 100
- After graduation (Apprenticeship origin): 120
- After shared bereavement (+ SharedCrisis origin): 135
If aesthetic drift later pushes dissolution pressure to 115, a baseline partnership dissolves. This one survives — pressure < 135 — and a CulturalEvent::OriginSavedPartnership fires on the tick pressure crosses 100. If pressure continues climbing to 140, the partnership eventually dissolves anyway, but it lasted meaningfully longer than an origin-less partnership under identical conditions.
Dissolution Clearing
All three partnership exit paths clear origin_history and reset strength_min on the surviving-side relationship record:
- Graceful dissolution from accumulated pressure (
romance_dissolution_system) - Satisfaction departure — partner leaves the settlement due to low satisfaction
- Farewell departure — partner dies of old age
After clearing, a surviving elf who later re-partners starts with empty history and a fresh strength_min. The new partnership's resilience is shaped only by the bond that forms next.
Tick Ordering
Same-tick writes are ordered by the tick pipeline: romance_progression_system at slot 9.6, apprenticeship_graduation_system at slot 23d. A graduation and a partnership-formation that land on the same tick write in a consistent order; tests assume this ordering. Future system reorderings must re-confirm the invariant.
Source: crates/er-sim/src/sim/world.rs, tick pipeline; slot documentation in system files.
Interactions
With Romance Dissolution
Origin history is currently the sole lever on the dissolution threshold. The tested and fragile flags on Relationship are computed but not yet read by romance_dissolution_system. If a future system wires those flags into threshold math, the interaction with origin history will need documentation.
With Apprenticeship
A graduation with bond_strength >= 60 records a pending-origin entry even if the pair is not yet Partners. If they become Partners within 30 in-game days, the entry drains. If not, it expires silently. A graduation with bond_strength < 60 does not qualify — the teaching did not sustain long enough for the bond to count as causal. See Apprenticeship for how bond_strength accumulates.
With Mourning
SharedCrisis writes are keyed to Mourning episodes. Two distinct bereavements produce two entries; repeated catalyst firings during one bereavement produce one. See Relationships § Departure Cascade for how Mourning is triggered.
With Save Files
origin_history and strength_min both persist across saves. Partnerships formed before this system existed load with empty history and strength_min equal to current strength (no lost-history assumption — the pre-feature data simply cannot be inferred).
PendingOriginWrites is a runtime-only resource and is not saved. An apprenticeship that graduates shortly before a save, followed by a partnership formation shortly after a load, will not record the Apprenticeship origin — the pending entry was lost. This window is narrow (graduation → partnership within 30 days, across exactly the save boundary) and accepted as a known limitation.
Tips
-
Watch for the endurance event.
... 's partnership enduredin the event feed means dissolution pressure crossed 100 but the partnership survived on origin resistance. It is the most reliable signal that origin markers are doing work in your settlement. These events first appear roughly day 30--80 in a typical playthrough — the earlier ones are memorable precisely because they're the first time the mechanic manifests. -
Apprenticeship + partnership is the strongest pairing. A master-student pair who reach
bond_strength60+ and later fall in love enter partnership with +20 resilience already banked. Pair elves with similar aesthetics during apprenticeship formation (smaller aesthetic distance means faster post-graduation friendship drift, which is a prerequisite for the romance warmth sources). See Apprenticeship. -
Grief forges durable bonds. A pair who comfort each other through the departure of a mutual friend earn a SharedCrisis origin — and the +6 warmth catalyst from SharedGrief often sparks the romance itself. Bereavement is generative, not purely corrosive. See Romance § Warmth Sources.
-
Rivalries that turn to love are fragile. RivalryToLove carries a -10 dissolution delta — partnerships that rose out of hostility dissolve easier than baseline, not harder. The identity is preserved as cost, not as protection. Expect these to break more readily under aesthetic drift; pair them with proximity (partners within 3 tiles get -2 pressure per cycle) if you want them to last.
-
Caps prevent runaway resilience. SharedCrisis stops contributing after 3 occurrences (+45 max) and the total positive delta clamps at +50 (threshold 150). A partnership that weathered five bereavements is not five-bereavements strong — it is three-bereavements strong in game terms, though the full count still renders in the UI. This is deliberate: long-running dynasties cannot accumulate infinite resistance and lock into un-dissolvable pairs.
-
Re-partnering is a clean slate. When a partnership dissolves,
origin_historyandstrength_minreset on the surviving partner. A widow who loses a 20-year-strong mentor-partnership and later pairs with someone new starts that new bond with the baseline threshold of 100. Grief carries forward as personality scars, not as partnership resilience. -
Dense histories are legible but long. The Relationships screen shows full origin history on one line. A partnership with five entries will produce a wide line. No truncation is applied; overflow is accepted until the shape of real-game data shows it matters.
Personality
Every elf has a personality defined by three axes -- boldness, patience, and pride. These aren't static labels. Root values shift permanently through scars, while expressed behavior drifts gradually toward the new root through maturation. An elf who suffers heartbreak doesn't change overnight; they change over hundreds of ticks, slowly becoming more cautious.
Overview
Personality has two layers:
- Root values (boldness, patience, pride): the elf's deep self. Only modified by scars. Set at birth/arrival and rarely change.
- Expressed values: what the elf actually acts on. Nudged toward root values by maturation every 50 ticks. These are what other systems read.
Each axis ranges from 0.0 to 1.0, with 0.5 as neutral:
| Axis | Low (< 0.3) | Mid | High (> 0.7) |
|---|---|---|---|
| Boldness | Cautious | Balanced | Bold |
| Patience | Impulsive | Balanced | Patient |
| Pride | Humble | Balanced | Proud |
The combination of all three axes defines an elf's gestalt -- a named archetype based on which octant of personality space they occupy.
Source: src/sim/components.rs, Personality struct -- root and expressed fields, PersonalityAxis enum.
How It Works
Gestalts
Every elf is assigned a gestalt based on whether each root axis is above or below 0.5. There are 8 gestalts, one for each corner of the personality cube:
| Boldness | Patience | Pride | Gestalt |
|---|---|---|---|
| > 0.5 | > 0.5 | > 0.5 | IronCrowned |
| > 0.5 | > 0.5 | <= 0.5 | DeepRoot |
| > 0.5 | <= 0.5 | > 0.5 | FlameTouched |
| > 0.5 | <= 0.5 | <= 0.5 | WindWalker |
| <= 0.5 | > 0.5 | > 0.5 | StillWater |
| <= 0.5 | > 0.5 | <= 0.5 | RootCalm |
| <= 0.5 | <= 0.5 | > 0.5 | ThornKeeper |
| <= 0.5 | <= 0.5 | <= 0.5 | DewLight |
Gestalt names appear in the elf detail view. They don't directly affect mechanics -- the three axis values do -- but they give a quick read on an elf's temperament.
Source: src/sim/components.rs, PersonalityGestalt enum and Personality::gestalt() method.
Generation
Founders (the starting elves) get personality values generated near the 8 corners:
- Base: 0.85 (high) or 0.15 (low) per axis
- Jitter: +/-0.1
- This creates distinct personality clusters in your starting population
Arrivals use bimodal generation:
- Each axis: 0.2 or 0.8 base (50% coin flip) + +/-0.1 jitter
- This produces elves with strong personality leanings, not wishy-washy midpoints
Expressed values start equal to root values. Divergence only happens after scars shift the root.
Source: src/sim/components.rs, Personality::random_founder and arrival generation in src/sim/arrival.rs.
Maturation
Every 50 ticks, expressed values drift toward root values:
expressed += (root - expressed) x rate
The rate is age-dependent -- young elves reshape quickly, elders resist change. See Lifecycle & Aging for the full stage-by-stage breakdown:
| Stage | Convergence Rate | Behavior |
|---|---|---|
| Young | 0.04 (4%) | Visibly shifts within a few days of a scar |
| Prime | 0.02 (2%) | Baseline; noticeable shift over ~10 days |
| Mature | 0.01 (1%) | Slow adjustment; identity is largely set |
| Elder | 0.005 (0.5%) | Nearly immovable; a late-life scar barely affects behavior |
At the Prime rate of 2% per cycle, after a scar shifts a root value:
- 500 ticks (~5 days): 82% of the way to the new root
- 1,000 ticks (~10 days): 96% converged
- 1,500 ticks (~15 days): effectively identical to root
Young elves reach the same convergence roughly twice as fast; Elder elves take roughly four times as long. Identity calcifies with age.
Source: crates/er-sim/src/sim/systems/mood.rs, personality_maturation_system; crates/er-sim/src/sim/components.rs, Stage::maturation_rate.
Sigmoid Effect
When personality values are read by other systems (romance warmth, social bonding, crisis triggers), they pass through a sigmoid function that compresses the middle and stretches the extremes:
if raw < 0.5: 2.0 x raw x raw
if raw >= 0.5: 1.0 - 2.0 x (1.0 - raw) x (1.0 - raw)
| Input | Output | Effect |
|---|---|---|
| 0.0 | 0.0 | Extreme low preserved |
| 0.25 | 0.125 | Middle compressed |
| 0.5 | 0.5 | Neutral unchanged |
| 0.75 | 0.875 | Middle compressed |
| 1.0 | 1.0 | Extreme high preserved |
This means personality only has strong mechanical effects at the extremes. An elf with boldness 0.4 vs 0.6 behaves almost identically. An elf with boldness 0.1 vs 0.9 behaves very differently.
Source: src/sim/components.rs, Personality::sigmoid_effect.
Personality Crises
Extreme personality values can trigger transient crisis states under certain conditions. The crisis system runs every 10 ticks. See Scars — Transient Crisis Markers for the full table and mechanics.
| Crisis | Trigger | Duration | Leaves Scar? |
|---|---|---|---|
| EgoWound | argument AND pride > 0.85 AND morale < 40 | 150 ticks | Yes — EgoWound scar |
| Withdrawal | boldness < 0.15 AND company > 80 | 200 ticks | No |
| RecklessAbandon | patience < 0.15 AND stalled aspiration | 100 ticks | No |
| LongPatienceBroken | argument AND patience > 0.85 (10% chance) | 50 ticks | No |
Only EgoWound leaves a permanent scar when it expires. The others resolve without lasting personality change — the elf rode out the weather.
Source: crates/er-sim/src/sim/systems/social.rs, conflict_system (EgoWound / LongPatienceBroken triggers) and personality_crisis_system (Withdrawal / RecklessAbandon triggers, all four expiries).
Scars
Scars are permanent modifications to root personality values — the one mechanism that actually moves root axes after character generation. Each scar applies a small axis shift (clamped to 0.05-0.95), carries a narrative label, and costs -1.5 satisfaction forever.
Five scar kinds exist: Heartbreak, EgoWound, Betrayal, Unrequited, PartnerLoss. Four of five shift boldness downward — emotional loss makes elves cautious.
For full details — formation conditions, root-shift values per scar, events, composition interactions, and the boldness-erosion spiral that pushes elves toward departure — see the dedicated Scars page.
Source: src/sim/components.rs, PersonalityScar struct, ScarKind enum, Personality::apply_scar.
Values & Formulas
Satisfaction Bonuses by Personality
The satisfaction system applies personality-specific bonuses every 10 ticks:
| Personality | Bonus | Condition |
|---|---|---|
| Bold (> 0.7) | +0.05 per friend above 3 | Rewards social boldness |
| Cautious (< 0.3) | +0.05 per solitude inspiration | Rewards introspection |
| Patient (> 0.7) | +0.1 per completed aspiration | Rewards long-term goals |
| Impulsive (< 0.3) | +0.05 per composition | Rewards prolific creation |
| Proud (> 0.7) | prestige_tier x 0.03 | Rewards status |
| Humble (< 0.3) | +0.05 per friend (cap 4) | Rewards community |
Source: crates/er-sim/src/sim/systems/satisfaction.rs, satisfaction_system -- personality bonus section.
Personality Effects Across Systems
| System | Boldness Effect | Patience Effect | Pride Effect |
|---|---|---|---|
| Romance warmth | x1.3 (bold) / x0.6 (cautious) | -- | x0.8 (proud) |
| Romance dissolution | -- | Pressure x (1.5 - patience) | -- |
| Revel absence | < 0.3: may skip revel | -- | > 0.8: may skip out of pride |
| Social bonding | Via Social axis | -- | -- |
| Crisis: Withdrawal | < 0.15 triggers | -- | -- |
| Crisis: Reckless Abandon | -- | < 0.15 triggers | -- |
Interactions
Departure Risk
Scars push elves toward departure through two channels:
- Direct satisfaction penalty: -1.5 per scar
- Boldness erosion: Heartbreak, Betrayal, and PartnerLoss all reduce boldness. Lower boldness means slower romance warmth accumulation (x0.6), making it harder to form new partnerships -- which would provide +10 satisfaction.
An elf who suffers multiple romantic losses enters a downward spiral: scars reduce satisfaction, boldness erosion makes recovery harder, and departure becomes increasingly likely.
Composition Properties
Personality influences composition through crisis states and scars:
- Heartbreak scar + currently unpartnered: unlocks the
Heartbreakproperty (raw emotional art, +15 emotional) - PartnerLoss scar + actively mourning same person: +10 emotional boost (no property slot — it's a resonance)
- Currently courting (not a scar): unlocks the
LoveSongproperty
Revel Behavior
Personality drives revel attendance decisions. During the gathering phase, elves with extreme personality values may choose not to attend:
- Cautious (boldness < 0.3): "chose the forest over the fire"
- Proud (pride > 0.8): "refused to attend -- if they don't want their art, they don't want their festival"
These absences generate RevelAbsence events in the log.
Tips
-
Read the gestalt, but watch the axes. IronCrowned tells you an elf is bold+patient+proud, but the specific values matter. An IronCrowned at (0.55, 0.55, 0.55) behaves very differently from one at (0.95, 0.95, 0.95) due to the sigmoid effect.
-
Scars are permanent but slow-acting. After a heartbreak, the elf's expressed boldness doesn't drop for days. Use the maturation window to stabilize them -- place them near friends, give them meaningful work.
-
Cautious elves are fragile. They accumulate romance warmth at 60% rate, making courtship slower. If their partner leaves, the PartnerLoss scar pushes boldness even lower. A formerly cautious elf with two PartnerLoss scars may never form another partnership.
-
Proud elves resist romance. The x0.8 warmth multiplier for pride > 0.7 means proud elves take longer to fall in love, but their partnerships are no more stable than anyone else's. A proud elf who does partner up is just as vulnerable to dissolution from aesthetic drift.
-
EgoWound is the only scarring crisis. A proud elf (pride > 0.85) whose morale drops below 40 after an argument enters an EgoWound crisis — 150 ticks. If it expires, they gain a permanent EgoWound scar (pride +0.03, patience -0.05). Withdrawal, RecklessAbandon, and LongPatienceBroken all expire silently. See Scars — Transient Crisis Markers.
Scars
Elves remember. When something significant happens to them — a partnership ends, a love goes unreturned, a pride is wounded past bearing — the event leaves a permanent mark called a scar. Scars shift root personality values by small amounts, and those shifts are forever. They accumulate. A long-lived elf who has weathered two heartbreaks and a betrayal is, mechanically, a different person than the one who arrived — slightly more cautious, slightly more bitter, slower to warm, quicker to leave.
This is the deep-time social layer of the sim. Most mood shifts decay. Scars don't.
Overview
A scar has two effects:
- A permanent shift to root personality values. Root values feed expressed values through slow maturation (see personality), so the change is visible but gradual.
- A narrative label — a human-readable string attached to the elf, visible in the detail view, naming what happened and who caused it.
Scars are additive. The same elf can accumulate multiple heartbreaks, a betrayal, and a partner loss, and every one of them contributes to the final root values. Root values are clamped to the range 0.05 -- 0.95 after each scar applies, so an elf can approach but never reach absolute extremes.
Each scar also costs -1.5 satisfaction forever, via the satisfaction system's scar_penalty term. Four scars is -6.0 permanent satisfaction — a background gravity pulling the elf toward departure.
Source: crates/er-sim/src/sim/components.rs, Scars, PersonalityScar, ScarKind, Personality::apply_scar. crates/er-sim/src/sim/systems/satisfaction.rs, scar_penalty term.
Scar Types
Five scar kinds exist today. Each has its own trigger, axis shift, and narrative template.
| Scar | Root Shift | Narrative Template | Source System |
|---|---|---|---|
| Heartbreak | boldness -0.05 | "Scarred by the silence of [partner]" | romance dissolution |
| EgoWound | pride +0.03, patience -0.05 | "[elf] bears the mark of wounded pride" | personality crisis (EgoWound expiry) |
| Betrayal | patience -0.05, boldness -0.03 | "Trust broken by [partner]" | romance dissolution (betrayal branch) |
| Unrequited | boldness -0.03, pride +0.02 | "Love unmet by [target]" | romance courtship (one-sided resolution) |
| PartnerLoss | boldness -0.05 | "Lost [partner] to the road" | satisfaction (partner departure) |
Note the pattern: four of five scars shift boldness down. Emotional loss makes elves cautious. The sim systematically erodes boldness across an elf's romantic life, and only deliberate intervention — giving them friends, meaningful work, aesthetic alignment — keeps them bold enough to form the next partnership.
Source: crates/er-sim/src/sim/components.rs, ScarKind enum and Personality::apply_scar match.
Formation Conditions
Each scar has precise trigger conditions in code. Knowing them helps you predict where scars will appear and intervene before they do.
Heartbreak — both partners
Fires in the romance dissolution system (every 100 ticks) when a partnership dissolves via the normal-drift path (aesthetic distance, prestige asymmetry, accumulated pressure ≥ 100.0). Both ex-partners receive the scar and a "Partnership ended" -8 mood for 300 ticks.
- Condition:
dissolution_pressure >= 100.0AND origin history does not save the partnership. - Applied to: both elves.
- Label: each sees
"Scarred by the silence of [the other]".
Betrayal — the betrayed only
Fires in the same dissolution system, but through a different branch. If an elf's partner is currently courting someone else, the partnership dissolves instantly (pressure snaps to 100.0, reason = "betrayal").
- Condition: partner has
RomanticState.courting = Some(third_name)wherethird_name != self. - Applied to: only the betrayed elf — the cheater walks away with no permanent mark.
- Label:
"Trust broken by [ex-partner]". - Mood: -10 "Betrayed" for 300 ticks.
- Origin history does not protect against betrayal. By design: a chosen act is categorically different from gradual drift.
Unrequited — the pining elf
Fires in the romance courtship system (every 50 ticks) when an attraction has been one-sided for too long.
- Condition:
tick - spark_tick >= 200AND the target's warmth toward this elf is< 30.0. - Applied to: the elf who pined.
- Label:
"Love unmet by [target]". - Side effects: their own warmth toward the target is set to 20.0 (bittersweet memory, not erased); -5 "Unrequited feelings" mood for 150 ticks.
PartnerLoss — the one left behind
Fires in the satisfaction system (every 10 ticks, departure phase) when a partner elf departs the settlement (satisfaction-driven departure, legacy, or otherwise).
- Condition: the departing elf has an active partnership; their partner remains.
- Applied to: the remaining partner only.
- Label:
"Lost [departed] to the road". - Side effects: partnership is cleared (
partner = None,courting = None,dissolution_pressure = 0);Mourningattached for 300 ticks (longer than friend mourning's 200); grief stacks with any existing mourning.
EgoWound — from the crisis, not the argument
Unlike the others, EgoWound does not fire directly from an event. It is the expiry payload of the EgoWound crisis marker (see Transient Crisis Markers below). The chain:
- Conflict system (every 10 ticks): when two elves argue, if one has
pride > 0.85 AND morale < 40 ANDno existing EgoWound, they gain anEgoWoundcrisis marker with 150 ticks remaining and a -8 "Wounded pride" mood. - Personality crisis system (every 10 ticks): decrements the marker each tick-batch. When it hits 0, the marker is removed — and the elf receives a permanent EgoWound scar.
So EgoWound scars are a delayed consequence of unresolved wounded pride. A proud elf whose morale recovers fast enough might weather the crisis without lasting mark — if the crisis expired would still leave the scar, but the timeline gives you ~15 in-game days to fix what hurt them.
Source: crates/er-sim/src/sim/systems/social.rs, conflict_system (crisis trigger at line ~598), personality_crisis_system (scar on EgoWound expiry at line ~702).
Transient Crisis Markers
Four non-scar marker components exist as short-lived ECS attachments. They modulate behavior temporarily and sometimes leave a permanent scar when they expire. They are not scars — they are crisis states the elf is currently in.
| Marker | Trigger | Duration | Mood While Active | Scar on Expiry |
|---|---|---|---|---|
| EgoWound | Argument AND pride > 0.85 AND morale < 40 | 150 ticks | -8 "Wounded pride" | Yes — EgoWound scar |
| Withdrawal | boldness < 0.15 AND company > 80 | 200 ticks | -3 "In withdrawal" | No |
| RecklessAbandon | patience < 0.15 AND stalled aspiration | 100 ticks | +2 "Reckless abandon" | No |
| LongPatienceBroken | Argument AND patience > 0.85, 10% chance | 50 ticks | -5 "Patience shattered" | No |
EgoWound is the only crisis that always leaves a permanent scar. The others are temporary — a lesson the elf learned, or a weather pattern they rode out. They fire PersonalityCrisis cultural events when they begin, but no ScarFormed event when they expire (except EgoWound).
Why the asymmetry? Bold elves overwhelmed by crowding (Withdrawal) can recover when the crowd thins; impulsive elves giving up on stuck goals (RecklessAbandon) feel relief, not damage; patient elves who finally snap (LongPatienceBroken) reset. But pride, once wounded below morale-40, calcifies — the memory persists as a personality shift.
Source: crates/er-sim/src/sim/components.rs, EgoWound/Withdrawal/RecklessAbandon/LongPatienceBroken structs. crates/er-sim/src/sim/systems/social.rs, personality_crisis_system — all four markers decay, only EgoWound writes a scar.
Events
Two CulturalEvent variants surface scars and crises to the player log.
ScarFormed
Fires every time a scar is applied. Payload:
#![allow(unused)] fn main() { ScarFormed { elf: String, // the scarred elf's display name scar_label: String, // e.g. "Scarred by the silence of Thalindra" } }
- Priority:
Notable(shows in standard log, not just detailed view). - Display string: the
scar_labelverbatim. The scar speaks for itself — no templating wraps it. - Fires from:
romance_courtship_system(Unrequited),romance_dissolution_system(Heartbreak, Betrayal),satisfaction_system(PartnerLoss),personality_crisis_system(EgoWound scar on crisis expiry).
PersonalityCrisis
Fires when a transient crisis marker is attached — not when it expires.
#![allow(unused)] fn main() { PersonalityCrisis { elf: String, crisis: String, // "ego wound" | "patience shattered" | "withdrawal" | "reckless abandon" } }
- Priority:
Notable. - Display string (evocative per crisis):
"ego wound"→"[elf] retreats, pride wounded beyond bearing""patience shattered"→"[elf]'s legendary patience finally snapped""withdrawal"→"[elf] withdraws from the settlement, overwhelmed""reckless abandon"→"[elf] throws caution aside in a manic burst"
Source: crates/er-sim/src/sim/events.rs, ScarFormed and PersonalityCrisis variants; EventPriority match; display_string implementation.
Interactions
The Boldness Spiral
Scars push elves toward departure through two reinforcing channels:
- Direct satisfaction penalty. Each scar is a permanent -1.5 on the daily satisfaction calculation. Four scars is -6.0 — approaching the threshold where an elf starts strongly considering departure.
- Boldness erosion. Heartbreak, Betrayal, PartnerLoss, and Unrequited all push boldness down. Lower boldness slows romance warmth accumulation (x0.6 for boldness < 0.3 vs x1.3 for > 0.7), making recovery through new partnership harder. It also raises the chance of a revel absence — "chose the forest over the fire" — compounding isolation.
An elf who loses one partner to departure and then goes unrequited when reaching out to someone new has two scars costing -3.0 satisfaction, boldness reduced by -0.08, and is now measurably worse at starting the next partnership. Without intervention, the spiral is how elves leave.
Composition Properties
Scars shape the art elves make.
- Heartbreak scar + currently unpartnered: unlocks the
Heartbreakcomposition property. Emotional score +15, property slot filled. - PartnerLoss scar for a currently-mourned elf: +10 emotional boost (stacked if the mourning is for the same name as the scar). No property slot consumed — it's a compound resonance, grief made legible through art.
Note: the LoveSong composition property is driven by currently courting, not by scars. Wiki entries elsewhere that attribute LoveSong to Heartbreak/PartnerLoss are stale — see crates/er-sim/src/sim/systems/compose.rs for the current rules.
Source: crates/er-sim/src/sim/systems/compose.rs, Romance composition properties section.
Rebuilding After Scars
Scars cannot be removed, but their effect on an elf's life depends on what comes next.
- Maturation moves expressed values toward root, and root values are what scars shift. So the personality change is slow — see lifecycle. A heartbroken young elf shifts within a week of in-game time; an elder barely moves.
- New partnerships provide +10 satisfaction, enough to offset several scars' direct penalty.
- Friends, meaningful work, and aesthetic alignment keep satisfaction above departure threshold even as scars accumulate.
The sim models a specific emotional truth: damage is real and permanent, but it's not deterministic. An elf with five scars can still have a long, satisfying life — they'll just be different than the elf who arrived.
Tips
-
Watch for proud-and-miserable elves. Pride > 0.85 and morale < 40 after any argument is the EgoWound crisis window. 150 ticks to recover their mood before the scar locks in. A single kept friendship or a satisfying composition can save them.
-
Protect couples during prestige gaps. Prestige asymmetry feeds dissolution pressure; pressure ≥ 100 fires Heartbreak on both sides. If one partner is breaking out as Renowned and the other stays Emerging, consider leveling the trailing partner's revel exposure before the gap becomes decisive.
-
Betrayal is one-sided by design. When a partner starts courting someone else, the betrayed elf takes the full hit and the cheater walks. This is intentional — players should read infidelity as a moral event, not a symmetric breakup. But mechanically: the cheater is the one who survives unscarred, and the betrayed may never form another stable partnership.
-
Unrequited has a 200-tick deadline. A one-sided attraction that hasn't reached mutual warmth (≥30) within 200 ticks resolves as Unrequited. If you see a potential pairing that's stalled, the window to shift aesthetic alignment or geography is finite.
-
PartnerLoss compounds with Mourning. The -5 "Grieving" mood stacks with the -8 "Partnership ended" style grief; the scar is permanent and the boldness drop slows future recovery. Losing a partner to departure is the single hardest scar to recover from — a partner, a mood penalty, a boldness drop, and 300 ticks of active mourning, all at once.
-
Scars tell stories. Read the labels. An elf carrying "Scarred by the silence of Thalindra", "Love unmet by Celebwen", and "Lost Aerendir to the road" isn't just a set of lowered stats — they are a person with a specific emotional history. The sim remembers for you; let it show up in how you play them.
Grief & Tribute
When an elf loses someone they were close to — a friend, a partner, a bonded tree — they don't simply log a mood penalty and move on. They enter mourning: a multi-tick state that pulls them toward the workshop, marks any composition they create as an elegy, and weaves the lost name into the colony's emotional memory.
Grief is one of the few states that overrides an elf's normal task priorities. A mourning elf will leave food gathering, building, and aspiration work to compose. What they make while grieving carries unusual emotional weight — and what they refuse to make can fracture friendships across the settlement.
Overview
The grief arc has four stages:
- Loss — a close friend departs, a partner leaves, or a bonded tree dies.
- Mourning — the bereaved elf gains a
Mourningcomponent with aticks_remainingcountdown and a "Grieving" mood modifier. They prefer composing over other tasks. - Tribute — if a workshop is available, the mourner composes. Any composition created while
Mourningis present gains the Elegy property (+15 emotional). ATributeComposedevent fires. - Resolution — when
ticks_remaininghits zero,grief_systemremoves theMourningcomponent and clears per-episode dedup keys inPendingOriginWrites. The grief is over; the elegy remains.
The arc is gated by emotional closeness. Acquaintances and rivals don't trigger mourning. Friends below strength 60 don't trigger mourning. Only close friends (strength ≥ 60), partners, and bonded trees do.
Source: crates/er-sim/src/sim/components.rs, Mourning; crates/er-sim/src/sim/systems/mood.rs, grief_system; crates/er-sim/src/sim/systems/satisfaction.rs and lifecycle.rs for the spawn sites.
How It Works
What Triggers Mourning
Three pathways apply a Mourning component. Each sets a different duration based on the depth of the loss:
| Loss Type | Source System | Mourning Duration | Conditions |
|---|---|---|---|
| Friend departs in dissatisfaction | satisfaction_system | 200 ticks | Friend relationship strength ≥ 60. Skipped if already mourning. |
| Friend departs in fulfillment | farewell_departure_system | 150 ticks | Friend strength ≥ 60. Bittersweet farewell — shorter than dissatisfied departure. Also Partner. |
| Partner departs | satisfaction_system | 300 ticks | Partnership cleared, PartnerLoss scar applied. Extends existing mourning by +100 if already mourning. |
| Bonded tree dies | tree_death_grief (flora_systems.rs) | 300 ticks | Only if tree is Notable (named) and elf has TreeBond. Tradition-gated — see Refusing the Elegy. |
A MourningBegan event fires for elf-on-elf mourning. Tree mourning fires BondedTreeDied instead (and ElegyRefused for the refusal branch). Both are emitted at EventPriority::Notable.
Source: crates/er-sim/src/sim/systems/satisfaction.rs:518-532 (friend), :535-575 (partner); lifecycle.rs:158-167 (fulfilled departure); flora_systems.rs:585-680 (tree death).
Effects on the Mourner
While Mourning is present, the elf is changed in three player-visible ways:
Mood. A "Grieving" modifier of -5 is pushed for 200 ticks. This stacks with the "Friend departed" or "Partner departed in fulfillment" mood modifier that fires alongside the death — a partnered elf losing their bonded mate carries both penalties simultaneously.
Behavior. task_decision_system checks is_mourning before checking creative block, aspirations, and idle decisions. A mourning elf is pulled toward TaskKind::Compose if a Workshop building exists. If no workshop is available, they default to TaskKind::Wander — wandering in grief is preferred over gathering or building. Critical needs (sustenance < 20, rest < 20) still take priority over mourning.
Composition. Any composition completed while Mourning is present automatically gains the CompositionProperty::Elegy property, which adds +15 to the emotional score. The Elegy property occupies a property slot (max 2), so it competes with seasonal properties, Heartbreak, LoveSong, Debut, Stormborn, etc.
Source: crates/er-sim/src/sim/systems/behavior.rs:181-191 (task decision); crates/er-sim/src/sim/systems/compose.rs (Elegy property assignment, TributeComposed emission).
The Tribute
When a mourning elf finishes a composition, two events fire:
ArtCompleted— the normal completion event listing the piece's tier and properties (including Elegy).TributeComposed { composer, departed, composition_name }— the elegy-specific event naming the departed friend. Notable priority.
There's no quality threshold for a tribute. Any composition a mourning elf finishes is a tribute, regardless of mastery, originality, or audience reception. The dedication is what matters — the mourner pointed their grief at a workshop and made something.
A single mourning episode can produce multiple tributes if the mourner composes more than once before ticks_remaining expires. Each fires its own TributeComposed event.
Source: crates/er-sim/src/sim/systems/compose.rs:305-312, Elegy property assignment.
Refusing the Elegy
The tree-death branch of grief has a hard fork that elf-on-elf mourning does not. When a Notable tree dies and its bonded elves are about to enter mourning, each bonded elf is checked against their AestheticPosition.tradition value:
| Tradition | Outcome |
|---|---|
| ≥ 0.3 | Full mourning: Mourning component (300 ticks, with departed_tree set), -8 "Bonded tree lost" mood for 300 ticks. |
| < 0.3 | Refusal: -8 mood only. No Mourning component. An ElegyRefused event fires. |
A refuser doesn't grieve the bonded-tree-style elegy, but they still take the full mood penalty. They reject the ritual, not the feeling.
Refusal has a social cost. Any elf with high tradition (aesthetic.tradition > 0.7) and low tolerance (aesthetic.social × personality.patience < 0.2) is offended. Each traditionalist:
- Gains a
-3"Ancient rites disrespected" mood modifier for 100 ticks - Has their
Relationshipstoward each refuser adjusted by -10 - Emits a
RitesDisrespected { offended_elf, refuser }event per refusal
This is the only place in the sim where a refusal to grieve in the traditional way generates measurable interpersonal damage. A colony with many low-tradition elves and many high-tradition traditionalists can fracture along ritual lines after a single notable tree dies.
Source: crates/er-sim/src/sim/flora_systems.rs:619-677, tradition gate and traditionalist reaction.
Resolution
grief_system runs every tick at pipeline slot 6.5 (after mood_system). Each tick it does two passes:
- Expire: collect Mournings where
ticks_remaining == 0, capture(entity, elf_name, departed_friend), then remove the component and clear matchingSharedCrisisdedup entries inPendingOriginWritesso the partnership-origin bookkeeping doesn't grow unbounded. - Decay: for every remaining
Mourning, decrementticks_remaining.
Order matters: capture-before-remove ensures the departed name is preserved for pending_origins.clear_crisis_keys_by_departed(...). The clear is scoped per (elf_name, departed_friend) pair — unrelated SharedCrisis entries for the same elf pair (a different departed) are preserved.
There is no event for mourning ending. The Mourning component simply disappears. Player-facing closure comes from the tribute (TributeComposed) or the silence of having composed nothing.
Source: crates/er-sim/src/sim/systems/mood.rs:57-96, grief_system; crates/er-sim/src/sim/pending_origins.rs:80-90, dedup-clear contract.
Cultural Events
The grief arc fires four cultural events, all at EventPriority::Notable:
| Event | Fires When | Payload | Display Example |
|---|---|---|---|
MourningBegan | Friend (str ≥ 60) departs in dissatisfaction | { elf, departed } | "Lyra withdraws into grief for Elara" (one of three rotated phrasings) |
TributeComposed | Mourning elf finishes any composition | { composer, departed, composition_name } | "Lyra completes Quiet Hour — a tribute to Elara" |
BondedTreeDied | Notable tree with bond dies | { elf, tree_name } | "The Old Oak falls. Mira's bond breaks." |
ElegyRefused | Low-tradition (< 0.3) elf bonded to a dying notable tree | { elf, tree_name } | "Mira refuses the elegy for The Old Oak" |
RitesDisrespected | High-tradition + low-tolerance elf reacts to an elegy refusal | { offended_elf, refuser } | "Old Tomar takes offense at Mira's refusal" |
MourningBegan is not emitted by the fulfilled-departure path or the tree-death path — only by dissatisfied departures. A fulfilled elder's friends enter mourning silently (no event), and tree mourners fire BondedTreeDied instead.
Source: crates/er-sim/src/sim/events.rs:229-256, 339-353, 741-771 (priority tags); events.rs:579-668 (display strings).
Interactions
With Partnerships
A Partner relationship triggers the partner-loss branch on departure, distinct from the friend branch:
- Partnership state cleared (
partner = None,courting = None,dissolution_pressure = 0.0) PersonalityScar { kind: ScarKind::PartnerLoss }added toScarsPersonality::apply_scar(PartnerLoss)permanently shifts root personality (see Scars)- Mourning duration: 300 ticks (vs. 200 for a close friend) — or current
ticks_remaining + 100if already mourning ScarFormedevent fires alongside anyMourningBegan
A partner death is one of the few events that can stack mourning on top of mourning. An elf already grieving a friend whose partner then departs sees their mourning extended by 100 ticks rather than reset.
Source: crates/er-sim/src/sim/systems/satisfaction.rs:535-575, partner-loss branch.
With Romance
Mourning is one of the strongest catalysts for new attractions. Per romance:
- Shared grief (both elves mourning the same departed friend): +6.0 warmth per cycle, catalyst flag set — unlocks the courtship transition.
Two elves who lose the same close friend can spark a courtship from comfort alone, even if they barely knew each other before the loss. This is the "grief bond" — emergent, asymmetric, and unscripted.
The PendingOriginWrites system tracks this with per-episode dedup so that a single 200-300 tick mourning episode doesn't multiply SharedCrisis partnership-origin entries across the 50-tick warmth cadence (romance_warmth_system runs every 50 ticks; mourning lasts 150-300 ticks, so naive accumulation would produce 3-6 entries per pair per death).
Source: crates/er-sim/src/sim/pending_origins.rs, dedup contract; crates/er-sim/src/sim/systems/romance.rs, SharedGrief warmth source.
With Composition
The Elegy property is one of five "named-circumstance" composition properties (alongside Heartbreak, LoveSong, Stormborn, Debut). It is automatic — no aspiration, skill check, or audience condition gates it. If Mourning is present at the moment of composition completion, the property is added (provided fewer than 2 properties are already attached).
Elegy properties stack with seasonal properties (Vernal/Solstitial/Autumnal/Hibernal) — a winter elegy can carry both Hibernal and Elegy. They do not stack with Heartbreak and LoveSong if the slot budget is full; the property-pushing order in compose_system determines which wins. Elegy is pushed before Heartbreak and LoveSong, so an elf mourning a friend while also unpartnered+heartbreak-scarred ends up with Elegy (not Heartbreak).
A separate composition path triggers on PartnerLoss scar specifically: when an elf with an unresolved PartnerLoss scar composes about the mourned partner, an extra +10 emotional bonus is added without occupying a property slot. See Scars — Composition Properties.
Source: crates/er-sim/src/sim/systems/compose.rs, property-assignment order.
With Behavior Priority
The task-decision priority cascade in task_decision_system places mourning between Discontented (refuses ambitious work) and the normal block/aspiration check:
- Critical needs (sustenance < 20, rest < 20)
- Discontented elves — only basic survival
- Mourning elves — compose if workshop exists, else wander
- Creative block — skip composing
- Aspiration goals
- Normal idle decisions
A mourning and discontented elf falls into the discontented branch and never composes — discontent overrides grief. This is the one case where grief is silenced rather than expressed.
Source: crates/er-sim/src/sim/systems/behavior.rs:142-191, priority cascade.
Tips
-
Tributes are quiet revolutions. A mourning elf doesn't need high music skill to produce a tribute. Even a tier-1 Mediocre composition with the Elegy property carries +15 emotional — enough to shift a revel's audience reactions toward Love. If you have a low-skill elf and an unexpected friend-departure, let them compose; the tribute may outperform their normal work.
-
Workshops save grief. A mourning elf with no workshop available will wander instead of compose. That means no tribute, no
TributeComposedevent, no Elegy property — just 200-300 ticks of -5 mood with nothing produced. Build workshops near where your tightest friendships cluster. -
Notable trees are emotional time bombs. Once a tree becomes
Notable, every elf with aTreeBondto it is on a fuse. Tree death is not always avoidable (seasonal die-off, age), and a single notable death can mourn 3-5 elves simultaneously. Watch which elves bond to which trees and consider whether the tree is in a stable spot. -
Tradition fault lines. A colony with a wide spread of
traditionvalues (some elves below 0.3, some above 0.7) is one notable-tree death away from aRitesDisrespectedcascade — each refuser triggers a -10 relationship hit from every offended traditionalist, and -10 per refusal compounds quickly. Track aesthetic distance, not just population count, when picking arrivals. -
Don't fear the grief bond. When a beloved elf departs, the friends they leave behind often form unexpected partnerships through
SharedGrief. Two elves who mourned the same person can become each other's partners. The loss isn't replaced — it's woven into the next bond's origin. -
Partner-loss is permanent in a way friend-loss isn't. The
PartnerLossscar shifts root personality (Personality::apply_scar) and persists for the rest of the elf's life unless resolved. A widowed elf grieves harder (300 ticks vs 200), carries the scar forever, and unlocks the+10 emotional bonuscomposition path when they compose about the partner. Friend-grief is a wave; partner-grief is a tide.
Lifecycle & Aging
Elves age. Skill comes easier when they are young; compositions reach their peak in their prime; their voice carries further once they are elders. When an elder has lived a satisfied life long enough, they do not depart in bitterness -- they attend one last revel, say goodbye, and leave a legacy behind that the colony remembers.
Overview
Every elf carries an Age component set at spawn. Age measures ticks lived since birth. Four stages bracket that lifespan, each shifting how the elf interacts with nearly every downstream system:
| Stage | Ticks Lived | Game Days |
|---|---|---|
| Young | 0 -- 499 | 0 -- 4 |
| Prime | 500 -- 1,999 | 5 -- 19 |
| Mature | 2,000 -- 3,999 | 20 -- 39 |
| Elder | 4,000+ | 40+ |
(DAY_LENGTH = 100 ticks.)
Stage is derived from tick - birth_tick on demand -- it is not stored. A cached last_known_stage field lets transition detection fire exactly once per boundary crossing.
Three systems drive the lifecycle:
age_stage_system(once per day) -- emitsStageTransitionevents when the cached stage differs from the current one.elder_fulfillment_system(every 50 ticks) -- tracks how long each elder has been satisfied; marks themFulfilledafter 500 ticks above satisfaction 70.farewell_departure_system(every tick) -- processes elders markedAwaitingFarewell, spawning aLegacyFigurerecord and despawning the elder.
Source: crates/er-sim/src/sim/systems/lifecycle.rs; crates/er-sim/src/sim/components.rs, Stage, Age, Fulfilled, FulfillmentProgress, AwaitingFarewell, LegacyFigure.
How It Works
Stage Multipliers
Each stage scales four numeric bands of the elf's behavior. The multipliers are read at call-sites throughout the sim; no single system applies them all.
| Multiplier | Young | Prime | Mature | Elder | Reads Applied In |
|---|---|---|---|---|---|
| Skill XP | 1.3x | 1.0x | 0.7x | 0.5x | compose, build, gather XP awards |
| Composition quality (mastery + originality) | 0.8x | 1.2x | 1.0x | 1.1x | compose system |
| Physical (work progress) | 1.0x | 1.0x | 0.8x | 0.5x | build, gather task progress |
| Influence (prestige weight in satisfaction) | 0.7x | 1.0x | 1.3x | 1.5x | satisfaction prestige term |
The shape is deliberate:
- Young elves learn fast (1.3x XP) but make lower-quality work (0.8x quality).
- Prime elves produce their best compositions (1.2x quality) with normal XP and physical ability.
- Mature elves lose some XP gain (0.7x) and physical rate (0.8x) but their prestige weighs more heavily in satisfaction (1.3x).
- Elder elves move slowly (0.5x physical, 0.5x XP) but their work carries unusual emotional weight (+10 to the emotional score on every composition, on top of 1.1x base quality). Their prestige also weighs more in satisfaction than any other stage (1.5x).
Elders are slower hands, but deeper voices.
Source: crates/er-sim/src/sim/components.rs, Stage::skill_xp_multiplier, comp_quality_multiplier, physical_multiplier, influence_multiplier, elder_emotional_bonus.
Personality Maturation Rate
The personality maturation system -- which nudges expressed personality values toward their roots -- also scales by age stage. Young elves change most quickly; elders are almost immovable:
| Stage | Maturation Rate (per 50-tick cycle) |
|---|---|
| Young | 0.04 (4%) |
| Prime | 0.02 (2%) |
| Mature | 0.01 (1%) |
| Elder | 0.005 (0.5%) |
This means a scar applied in youth reshapes the elf fast; a scar applied to an elder barely shifts their behavior before they depart. Identity calcifies with age.
Source: crates/er-sim/src/sim/components.rs, Stage::maturation_rate; applied in crates/er-sim/src/sim/systems/mood.rs, personality_maturation_system.
Stage Transitions
Once per day (tick multiple of DAY_LENGTH = 100, skipping tick 0), every elf's current stage is compared to their cached last_known_stage. If the stage advanced, the cache updates and a StageTransition { elf, from, to } cultural event fires.
There is no hard cap at Elder -- once an elf reaches stage Elder at tick 4,000, they remain Elder for the rest of their life. Departure happens through satisfaction mechanics, not a hard age limit.
Source: crates/er-sim/src/sim/systems/lifecycle.rs, age_stage_system.
Elder Fulfillment
When an elf reaches Elder stage and has satisfaction > 70, an invisible counter starts. Every 50 ticks:
- If their satisfaction is still above 70, add +50 to
FulfillmentProgress.ticks_above. - If their satisfaction drops to 70 or below, reset
FulfillmentProgressto zero. Partial progress does not persist.
When the counter reaches 500 ticks (10 game days of continuous happiness as an elder), the elf is marked Fulfilled. The FulfillmentProgress component is removed and an ElderFulfilled event fires.
An elf who spends their elder years bouncing between content and discontent never completes the fulfillment path. The fulfillment counter is strict -- sustained contentment, not average contentment, is what marks an elder for an honored departure.
Source: crates/er-sim/src/sim/systems/lifecycle.rs, elder_fulfillment_system -- threshold 70.0, accumulator step 50, completion at 500.
The Farewell Revel
Being marked Fulfilled does not trigger an immediate departure. Instead, the elf waits for the next revel they attend. When that revel concludes, the finalization pass scans attendees for the Fulfilled marker and tags each one with AwaitingFarewell.
This is deliberate: the revel is the farewell. The community gathers, the fulfilled elder performs or witnesses one last performance, and only then do they leave. There is no silent fulfilled departure.
On the next tick after being tagged, farewell_departure_system runs the full departure sequence:
- Build a
LegacyFigurerecord from the elder's portfolio, prestige, love reactions, and revels performed. - Emit a
FulfilledDeparture { elf, legacy_score }event (notable priority). - Apply the fulfilled-departure mood cascade to every elf in the elder's relationship list.
- Apply any apprenticeship cleanup (see apprenticeship).
- Despawn the elder.
Source: crates/er-sim/src/sim/systems/revel.rs (Fulfilled detection + AwaitingFarewell tag at line 523); crates/er-sim/src/sim/systems/lifecycle.rs, farewell_departure_system.
Fulfilled Departure Mood Cascade
A fulfilled departure is bittersweet, not devastating. Mood effects on surviving elves are softer than for a dissatisfied departure, and the mourning period is shorter:
| Relationship to Departed Elder | Mood Modifier | Duration |
|---|---|---|
| Partner | -8 "Partner departed in fulfillment" | 300 ticks |
| Friend | -5 "Friend departed in fulfillment" | 200 ticks |
| Rival | +2 "Rival departed" | 50 ticks |
| Acquaintance | -1 "Elder departed" | 50 ticks |
Friends with strength >= 60 and all Partners also enter a Mourning state -- but the Mourning component is created with ticks_remaining: 150, shorter than the 200-tick mourning that follows dissatisfied departures. The shortening reflects the different emotional shape: the community mourns, but the mourning is wrapped in pride.
Source: crates/er-sim/src/sim/systems/lifecycle.rs, farewell_departure_system -- mood matches at lines 137-141, mourning at line 159.
Legacy Figures
When a fulfilled elder departs, a LegacyFigure record is pushed onto the colony's permanent legacy list. Legacy figures outlive the elves themselves -- they are cultural memory the simulation never forgets.
A LegacyFigure captures:
| Field | Source |
|---|---|
name | Elder's display name |
gestalt | Personality gestalt at departure (e.g., DeepRoot, WildFlame) |
composition_count | Portfolio size at departure |
legacy_score | composition_count * 2 + love_reactions * 3 + revels_performed + prestige_tier * 5 |
departure_day | Game day of departure (tick / 100) |
departure_reason | DepartureReason::Fulfilled |
The legacy_score is a rough numeric summary of cultural impact. A prolific Legendary-tier composer who performed at many revels will leave a higher-scored legacy than a quiet Mature-stage soloist. The score is also announced in the FulfilledDeparture event.
Legacy figures persist in the colony state; they are the kind of historical record a future UI panel could render as a "Hall of Elders." Dissatisfied departures do not currently push LegacyFigure records -- only fulfilled ones earn the remembrance.
Source: crates/er-sim/src/sim/components.rs, LegacyFigure; crates/er-sim/src/sim/systems/lifecycle.rs, score at line 106.
Interactions
Satisfaction & Departure
Fulfilled departure is a separate departure path from the satisfaction-triggered one. The discontented timeline (300 ticks warning, 500 more ticks to depart) does not apply to elders who are flourishing -- they leave through a different gate, at a different cadence, with a different mood footprint on those they leave behind.
The satisfaction formula itself also weights elder elves differently: the prestige bonus term is scaled by the elder's 1.5x influence multiplier. A renowned elder's prestige contributes more heavily to their own satisfaction than a young elf's would, which makes it easier for them to accumulate the 500 ticks of sustained happiness that fulfillment requires.
Personality
The personality maturation system reads the elf's age stage to set the convergence rate. Elves in crisis (scarred) heal quickly when young and slowly when old. An Elder who loses a partner carries that scar for the remainder of their life with very little behavioral adjustment.
Compositions & Revels
Every composition reads the composer's stage through Age to scale mastery and originality by comp_quality_multiplier. Elder compositions receive a flat +10 to their emotional score on top of that multiplier, making late-career works often the most emotionally resonant pieces in a colony's portfolio.
At revel time, the finalization pass checks for attending elders marked Fulfilled. An elder who is fulfilled and attends any revel will depart after that revel -- your scheduling of revels implicitly schedules their goodbyes.
Apprenticeship
When an elder with AwaitingFarewell departs, the farewell system calls cleanup_apprenticeship_on_departure. If the elder was mentoring an apprentice, the student loses their Apprenticeship component and receives a -8 "Mentor departed" mood modifier for 200 ticks.
Students graduated under the elder retain their permanent TrainedBy lineage marker -- the master's name lives on in the student's detail panel even though the elder is gone.
Skills & Work
Build and gather task progress is scaled by physical_multiplier -- Mature elves work at 80% speed and Elder elves at 50%. Skill XP gains are scaled by skill_xp_multiplier, so aging elves climb skill levels slowly even when they work often. A Young elf learning their craft gains XP 2.6x faster than an Elder performing the same task (1.3 / 0.5).
Tips
-
Schedule revels for your elders. An elder who reaches
Fulfilledstatus is waiting for the next revel to say goodbye. If you want to delay a beloved elder's departure, holding off on revels buys time -- but keeping them in an environment that produces sustained satisfaction is already in tension with extending their life indefinitely. The simulation is designed for graceful exits. -
Prime is the composition sweet spot. Stage Prime gives 1.2x quality with full physical and XP multipliers, and no elder emotional bonus -- pure technical peak. If you are trying to push a specific elf toward a Magnum Opus (skill 10, inspiration > 90, mood > 50), their Prime window (ticks 500--1,999, days 5--19) is when the composition quality math is most forgiving.
-
Elder prestige compounds. The 1.5x influence multiplier on prestige means an elder with a high prestige tier gets more satisfaction from their reputation than any other stage. Keeping a prestige-Renowned elder's satisfaction above 70 long enough to trigger Fulfilled is easier than it looks; the system rewards long-lived success.
-
Young elves are emotionally volatile. 4% maturation rate means a scar applied in youth shifts their behavior visibly within a few days. Young elves who suffer heartbreak or unrequited love can change noticeably before you have time to stabilize them. Pair them with friends early.
-
Mourning for a fulfilled elder is shorter. 150 ticks vs 200 for dissatisfied departures. Your surviving elves will compose fewer Elegies after a fulfilled departure than they will after a bitter one. If you're chasing Elegy-tagged compositions, dissatisfaction cascades produce them more reliably -- though at the cost of a colony that feels worse.
-
LegacyFigures are cumulative, not displayed (yet). The simulation records them permanently, but there is no in-game panel that lists them. This is future UI territory -- a Hall of Elders that shows every fulfilled departure's name, gestalt, compositions, and score. The data is already being collected for that day.
-
Cascade softness matters for colony resilience. A dissatisfied departure gives friends -10 mood for 300 ticks; a fulfilled departure gives -5 for 200. Across a long-running colony, a string of fulfilled departures leaves the survivors in noticeably better emotional shape than a string of dissatisfied ones. The lifecycle system is balanced so that peaceful exits preserve the colony's capacity for the next generation.
Satisfaction & Departure
Satisfaction measures how content each elf is with life in the settlement. When satisfaction stays too low for too long, the elf will warn you, then leave permanently. Departures are the primary population-loss mechanic, balanced against the arrival system which brings new elves as colony reputation grows.
Overview
Every elf has a satisfaction value (0--100) that is recomputed every 10 ticks from a weighted formula combining inspiration, mood, friendships, aesthetic alignment, revel quality, and prestige. When satisfaction drops below an elf's personal departure threshold, a countdown begins. If the elf stays below threshold for 300 ticks, they become Discontented (visible warning). If they remain below threshold for 500 more ticks (800 total), they depart -- permanently removed from the settlement.
How It Works
The Satisfaction Formula
Satisfaction is recomputed from scratch every 10 ticks (not accumulated):
Satisfaction = (inspiration x 0.3) + (morale x 0.2) + (friends x 5.0) + (aesthetic_fit x 20.0) + (revel_score x 0.1) + prestige_bonus + personality_bonus + romance_bonus - scar_penalty - overcrowding_penalty + spike
The result is clamped to 0--100.
| Component | Weight | Range | Max Contribution |
|---|---|---|---|
| Inspiration total | x 0.3 | 0--100 | 30.0 |
| Morale (base 50 + mood modifiers) | x 0.2 | 0--100 | 20.0 |
| Friend count | x 5.0 | 0--20 | 100.0 (capped by max relationships) |
| Aesthetic fit (1 - distance/2) | x 20.0 | 0.0--1.0 | 20.0 |
| Last revel avg score | x 0.1 | 0--100 | 10.0 |
| Prestige bonus | x 0.15 of prestige score | 0--100 | 15.0 |
| Personality bonus | archetype-dependent | varies | ~10 |
| Romance bonus | partner/courting dependent | varies | ~10 |
| Scar penalty | -1.5 per active scar | 0--∞ | unbounded |
| Overcrowding penalty | (pop / max(dwellings*3, 8) - 1) * 15 | >= 0 | unbounded |
| Satisfaction spike (revel bonuses) | +direct | varies | consumed once |
Where:
- Inspiration total = sum of all 5 inspiration channels (nature, social, rivalry, beauty, solitude), capped at 100. See Inspiration.
- Morale = base 50 + sum of all active mood modifiers, clamped 0--100. See Needs & Mood.
- Friend count = number of relationships with strength >= 50.
- Aesthetic fit = how well this elf's aesthetic position matches the settlement mean. Calculated as
1.0 - (euclidean_distance / 2.0). An elf at the exact center of the community gets 1.0; one at maximum distance (2.0 in 4D space) gets 0.0. - Revel score = average audience score from the most recent revel.
- Prestige bonus = the elf's social prestige score (0--100) x 0.15, derived from revel performances and fandom.
- Personality bonus = archetype-specific rewards. See Personality for the full table. Examples: Bold elves gain +0.05 per friend above 3; Patient elves gain +0.1 per completed aspiration; Proud elves gain prestige_tier x 0.03.
- Romance bonus = +10.0 for having a partner, +5.0 for actively courting, -3.0 per active unrequited attraction. See Romance for details.
- Scar penalty = -1.5 per personality scar. Permanent and cumulative. An elf with 3 scars carries -4.5 satisfaction permanently. See Scars for the full list of scar types, formation conditions, and the boldness-erosion spiral.
- Overcrowding penalty = pressure from population outpacing dwellings. If population exceeds
max(dwellings * 3, 8), each elf's satisfaction is reduced proportionally. There is no hard population cap — overcrowding is the natural governor for growth from the arrival system. - Spike = buffered satisfaction delta from revel events. Consumed and zeroed each time the formula runs.
Source: crates/er-sim/src/sim/systems/satisfaction.rs, satisfaction_system -- formula at the new_value computation.
Departure Thresholds
Each elf has a personal departure threshold that determines when they start considering leaving. This threshold is based on the elf's highest skill level at creation:
| Highest Skill Level | Base Departure Threshold |
|---|---|
| 1--3 | 15.0 |
| 4--6 | 20.0 |
| 7--9 | 25.0 |
| 10 | 30.0 |
More skilled elves are harder to satisfy -- they have higher standards.
Source: src/sim/components.rs, Satisfaction::new -- skill-based threshold.
The Departure Timeline
When satisfaction drops below the elf's personal threshold, a tick counter starts incrementing (by 10 each cycle, since the system runs every 10 ticks):
| Phase | Tick Range Below Threshold | What Happens |
|---|---|---|
| Silent | 0 -- 299 | Counter accumulates. No visible indicator. |
| Discontented | 300 -- 799 | Warning event fires. Discontented marker added. UI shows warning. |
| Departure | 800+ | Elf is permanently despawned from the world. |
- At 300 ticks: a
LeaveWarningevent fires. TheDiscontentedcomponent is added to the elf. The event reports how many ticks remain before departure. - At 800 ticks: the elf departs. A
ElfDepartedevent fires with a reason string -- either "profound dissatisfaction" (satisfaction < 10) or "aesthetic alienation" (satisfaction >= 10 but still below threshold).
The counter resets to 0 whenever satisfaction rises back above the threshold.
Source: src/sim/components.rs, Satisfaction::warning_threshold() returns 300, Satisfaction::departure_ticks() returns 800.
Recovery Mechanics
If a Discontented elf's satisfaction rises above their departure threshold, they recover:
- The
Discontentedmarker is removed. - The ticks-below-threshold counter resets to 0.
- The elf's departure threshold permanently increases by 5.0. This means each near-departure makes the elf harder to keep next time.
- The elf receives a mood modifier: "Reconsidered leaving" +8 for 300 ticks.
- Relationship affixes are updated:
- If the elf has any Friend relationships, those friends get the Tested affix (the bond survived crisis).
- If the elf has no friends, all their relationships get the Fragile affix.
The threshold increase is permanent and cumulative. An elf that has recovered twice from departure attempts has their base threshold raised by 10.0 total.
Source: crates/er-sim/src/sim/components.rs, Satisfaction::recover -- threshold += 5.0. crates/er-sim/src/sim/systems/satisfaction.rs, SatAction::Recover branch.
Revel Satisfaction Spikes
Revels can buffer satisfaction deltas through the satisfaction_spike field. Instead of writing directly to the satisfaction value (which would be overwritten on the next 10-tick recomputation), revel events write to this buffer. The satisfaction system consumes the spike by folding it into the formula, then zeroes the buffer.
This means a great revel performance can temporarily boost an elf's satisfaction above their threshold, potentially triggering recovery.
Source: src/sim/components.rs, Satisfaction::apply_spike and the formula in satisfaction_system.
Fulfilled Departure (Elders Only)
Elves who reach the Elder stage have a second departure path -- a graceful one. An Elder whose satisfaction stays above 70 for 500 continuous ticks (10 game days) is marked Fulfilled and will depart after the next revel they attend.
This is a separate system from the discontented timeline above. The Discontented/Departure ticks-below-threshold counter still exists for elders, but fulfilled elders leave through a different mechanism with a different mood footprint.
Fulfillment path:
- Elder with satisfaction > 70 accumulates
FulfillmentProgress.ticks_aboveat +50 per 50-tick cycle. - Any dip to satisfaction <= 70 resets the counter to zero. Sustained, not average, happiness is required.
- At 500 accumulated ticks, the elder is tagged
FulfilledandElderFulfilledfires. - The next revel attended by a Fulfilled elder tags them
AwaitingFarewell. - The following tick,
farewell_departure_systemdespawns them and records aLegacyFigure.
Fulfilled departure mood cascade (softer than dissatisfied):
| Relationship to Departed Elder | Mood Modifier | Duration |
|---|---|---|
| Partner | -8 | 300 ticks |
| Friend | -5 | 200 ticks |
| Rival | +2 | 50 ticks |
| Acquaintance | -1 | 50 ticks |
Friends (strength >= 60) and Partners still enter Mourning, but for 150 ticks rather than the 200 that follows a dissatisfied departure. The community mourns a fulfilled elder, but the mourning is wrapped in pride.
See Lifecycle & Aging for the full stage progression, multipliers, and LegacyFigure schema.
Source: crates/er-sim/src/sim/systems/lifecycle.rs, elder_fulfillment_system (threshold 70, completion at 500); farewell_departure_system.
Values & Formulas
Satisfaction Scenarios
| Scenario | Inspiration | Morale | Friends | Aes. Fit | Revel | Prestige | Total |
|---|---|---|---|---|---|---|---|
| Happy elf | 60 (x0.3=18) | 70 (x0.2=14) | 3 (x5=15) | 0.8 (x20=16) | 50 (x0.1=5) | 30 (x0.15=4.5) | 72.5 |
| Struggling elf | 20 (x0.3=6) | 30 (x0.2=6) | 0 (x5=0) | 0.3 (x20=6) | 0 (x0.1=0) | 0 (x0.15=0) | 18.0 |
| Social butterfly | 40 (x0.3=12) | 60 (x0.2=12) | 6 (x5=30) | 0.5 (x20=10) | 40 (x0.1=4) | 20 (x0.15=3) | 71.0 |
| Lonely master | 80 (x0.3=24) | 50 (x0.2=10) | 0 (x5=0) | 0.9 (x20=18) | 60 (x0.1=6) | 50 (x0.15=7.5) | 65.5 |
Recovery Threshold Escalation
| Recovery Count | Threshold (Skill 1-3) | Threshold (Skill 4-6) | Threshold (Skill 10) |
|---|---|---|---|
| 0 (never recovered) | 15.0 | 20.0 | 30.0 |
| 1 | 20.0 | 25.0 | 35.0 |
| 2 | 25.0 | 30.0 | 40.0 |
| 3 | 30.0 | 35.0 | 45.0 |
Timing Summary
| Event | Ticks |
|---|---|
| Satisfaction recomputed | Every 10 ticks |
| Silent countdown phase | 0 -- 299 ticks below threshold |
| Warning fires (Discontented) | 300 ticks below threshold |
| Negotiation window | 300 -- 799 ticks (500-tick window) |
| Departure | 800 ticks below threshold |
At real speed, the full departure timeline from first dropping below threshold is 800 ticks = 8 in-game days.
Interactions
Departure Cascade
When an elf departs, their relationships trigger mood effects on remaining elves:
| Relationship | Mood Effect | Duration |
|---|---|---|
| Friend | "Friend departed" -10 | 300 ticks |
| Rival | "Rival departed" +3 | 100 ticks |
| Acquaintance | "Acquaintance departed" -2 | 100 ticks |
Mourning: Close friends (strength >= 60) enter a Mourning state for 200 ticks. This adds an additional "Grieving" -5 mood modifier that stacks with "Friend departed." Total mood impact for a close friend's departure: -15 for 200 ticks, then -10 alone for the remaining 100 ticks. Mourning elves are drawn toward composing (creating tributes to the departed).
One departure can cascade: the mood hit from a friend leaving can push another elf below their satisfaction threshold, starting a new departure countdown. In a tightly bonded colony, losing one key elf can trigger a chain of departures.
Source: crates/er-sim/src/sim/systems/satisfaction.rs, satisfaction_system -- Phase 3 departure processing.
Aesthetic Fit
Aesthetic fit rewards elves who align with the settlement consensus. The settlement aesthetic center is the weighted mean of all elves' aesthetic positions. Outlier elves -- those with very different creative values -- have lower aesthetic fit and thus lower satisfaction.
This creates a natural tension: aesthetic diversity makes for richer compositions at revels, but outliers are harder to retain.
Prestige and Recognition
Prestige contributes up to 15 points of satisfaction (at score 100). Elves who perform well at revels, gain fans, and build a strong portfolio accumulate prestige. Recognizing an elf's work helps keep them satisfied.
Forest Spirit Impact
The Forest Spirit does not directly affect satisfaction, but its effects on mood (through weather disruption and comfort) can indirectly lower morale, which feeds the satisfaction formula at a 0.2 weight.
Tips
-
Friends are the strongest lever. Each friend adds +5 to satisfaction. Getting even one friendship for an at-risk elf can pull them above threshold. See Relationships for how to accelerate bonding.
-
Watch the events panel for
LeaveWarningevents. You have 500 ticks (5 in-game days) between the warning and departure. Act immediately. -
Revel spikes can save elves. A well-timed revel with high audience scores injects a satisfaction spike that can push a Discontented elf above their threshold, triggering recovery.
-
Recovery has a cost. Each recovery raises the threshold by +5 permanently. An elf that has recovered twice is now 10 points harder to keep happy. At some point, it may be better to invest in new arrivals than to keep saving the same discontented elf.
-
Outlier aesthetics are a risk. If one elf has a radically different aesthetic position from the settlement center, their aesthetic_fit will be low (potentially 0.0, costing them 20 satisfaction points). Consider using Artistic Direction to shift the settlement's creative center.
-
Morale management matters. At 0.2 weight, the difference between morale 30 (Stressed) and morale 80 (Inspired) is 10 satisfaction points. Keep needs above the Wanting threshold.
-
Monitor the "Struggling elf" scenario -- an elf with 0 friends, low inspiration, and poor aesthetic fit can have satisfaction as low as 18. That is below even the lowest departure threshold (15.0) and will trigger departure in 800 ticks with no intervention.
-
Plan for cascade risk. If your top composer has 4+ close friends and departs, every friend takes -10 mood for 300 ticks, and close friends (strength >= 60) additionally enter Mourning at -5 for 200 ticks. This can domino. Prioritize retention of well-connected elves.
Prestige
Prestige measures an elf's social standing based on their artistic output, audience reception, and fan following. High-prestige elves enjoy greater satisfaction and exert more influence over the settlement's cultural direction.
Overview
Every elf has a prestige score (0--100) and a prestige tier derived from that score. The prestige system runs every 50 ticks, recalculating scores from three inputs: recent Love reactions at revels, number of fans across the colony, and the size of the elf's composition portfolio. When an elf's score crosses a tier boundary, a PrestigeChanged event fires.
Prestige is not accumulated over time -- it is recomputed from scratch each cycle. An elf who stops performing and loses fans will see their prestige decline, especially once decay kicks in.
How It Works
Score Calculation
Every 50 ticks, the system scans three data sources for each elf:
- Recent Love reactions -- the number of Love reactions the elf received as a composer in the last 5 revels (not the last 5 ticks -- the last 5 completed revel events, however far apart they were).
- Fan count -- the number of elves across the entire colony who have this elf in their fandom list (i.e., who have Loved this elf's work and become a fan -- one Love is enough).
- Portfolio size -- the number of finished compositions in the elf's portfolio.
These three inputs are combined into a raw score:
Score = (recent_loves x 5.0) + (fans x 10.0) + (compositions x 2.0)
After the raw score is calculated, decay may subtract from it (see below). The final result is clamped to 0--100.
Source: crates/er-sim/src/sim/systems/prestige.rs, prestige_system -- formula at the new_score computation.
Prestige Tiers
The numeric score maps to a named tier:
| Tier | Score Range | Stars | Description |
|---|---|---|---|
| Unknown | 0 -- 15 | (none) | No reputation. Default for new elves and those who have never performed. |
| Emerging | 16 -- 35 | 1 | Starting to get noticed. A few Love reactions or a small fan base. |
| Established | 36 -- 55 | 2 | Recognized artist. Consistent revel performer with a growing portfolio. |
| Renowned | 56 -- 80 | 3 | Major cultural figure. Large fan following and strong revel history. |
| Legendary | 81 -- 100 | 4 | Settlement-defining talent. Requires sustained excellence across all three inputs. |
Tier transitions fire a PrestigeChanged cultural event. Rising above score 50 produces an emphatic message ("has risen to X status!"), while lower transitions use a neutral phrasing ("is now X").
Source: src/sim/components.rs, PrestigeTier::from_score -- threshold boundaries. src/sim/events.rs, PrestigeChanged -- event formatting.
Decay
Prestige decays when an elf stops receiving audience love. Specifically:
- If the current tick minus the elf's last Love tick exceeds 200 ticks, the system subtracts 1.0 from the raw score each 50-tick cycle.
- The last Love tick is updated any time the elf receives a Love reaction as a composer during a revel performance, not just during the prestige recalculation.
This means an elf who stops performing will lose 1 point per cycle after a 200-tick grace period. Since the score is also being recalculated from current data (recent Love counts, current fan count, current portfolio size), decay acts as an additional penalty on top of naturally declining inputs.
Source: crates/er-sim/src/sim/systems/prestige.rs, prestige_system -- decay branch at tick.saturating_sub(last_love) > 200. crates/er-sim/src/sim/systems/revel.rs, revel_tick_system -- prestige.last_love_tick = tick on Love.
The PrestigeChanged Event
When an elf's tier changes (up or down), the system emits a PrestigeChanged event containing:
| Field | Type | Description |
|---|---|---|
| elf | String | Name of the elf whose tier changed |
| from_tier | String | Previous tier label |
| to_tier | String | New tier label |
| score | f32 | Current prestige score |
This event has Notable priority -- it appears in the event feed but does not trigger a popup.
Source: src/sim/events.rs, PrestigeChanged variant and EventPriority::Notable.
Values & Formulas
Input Weights
| Input | Weight | Typical Range | Max Contribution |
|---|---|---|---|
| Recent Love reactions (last 5 revels) | x 5.0 | 0 -- 20 | ~100 (but clamped) |
| Fan count (colony-wide) | x 10.0 | 0 -- 10 | ~100 (but clamped) |
| Portfolio compositions | x 2.0 | 0 -- 50 | ~100 (but clamped) |
| Decay (no Love in 200 ticks) | -1.0 per cycle | 0 or -1 | -1.0 |
The raw sum is clamped to 0--100, so in practice an elf with strong numbers in any two categories can reach Legendary.
Example Scenarios
| Scenario | Loves | Fans | Comps | Raw Score | Decay? | Final |
|---|---|---|---|---|---|---|
| First-time performer | 2 | 0 | 1 | 12.0 | No | 12.0 (Unknown) |
| Solid contributor | 4 | 2 | 5 | 50.0 | No | 50.0 (Established) |
| Fan favorite | 6 | 4 | 3 | 76.0 | No | 76.0 (Renowned) |
| Retired legend | 0 | 3 | 15 | 60.0 | Yes (-1) | 59.0 (Renowned) |
| Fading star (no recent work) | 0 | 1 | 3 | 16.0 | Yes (-1) | 15.0 (Unknown) |
Timing Summary
| Event | Interval |
|---|---|
| Prestige recalculated | Every 50 ticks |
| "Recent" Love window | Last 5 completed revels |
| Decay grace period | 200 ticks since last Love reaction |
| Decay rate | -1.0 per 50-tick cycle (after grace period) |
Interactions
Satisfaction
Prestige feeds directly into the satisfaction formula with a weight of 0.15:
prestige_bonus = prestige_score x 0.15
At maximum prestige (100), this contributes +15 satisfaction points. At zero prestige, it contributes nothing. This makes prestige a meaningful but not dominant factor in retention -- worth about as much as 3 friends or a perfect aesthetic fit.
Source: crates/er-sim/src/sim/systems/satisfaction.rs, satisfaction_system -- prestige_bonus = p.score * 0.15.
Settlement Aesthetic Center
High-prestige elves have more influence over the settlement's aesthetic center. When computing the mean aesthetic position, each elf's position is weighted by:
weight = 1.0 + (prestige_score x 0.02)
At prestige 0, the weight is 1.0 (equal voice). At prestige 100, the weight is 3.0 (triple influence). This means Legendary elves pull the settlement's aesthetic center toward their own taste, which in turn affects aesthetic fit for everyone else.
This creates a feedback loop: a Legendary elf's aesthetic preferences shape the settlement center, which improves aesthetic fit for elves who share those preferences and worsens it for those who differ. Over time, the colony's cultural identity converges around its most prestigious members.
Source: crates/er-sim/src/sim/systems/satisfaction.rs, settlement_aesthetic_center -- weight formula 1.0 + p.score * 0.02.
Fandom
Fandom is the primary driver of prestige. Each fan contributes 10 points to the prestige score. Fans are created the first time an audience member Loves one of an elf's compositions during a revel -- a single Love is enough. Each elf tracks up to 5 fan relationships, but prestige counts fans colony-wide, so a popular composer can accumulate a substantial fan base.
Love reactions during revels also reset the decay timer (last_love_tick), keeping prestige from eroding while the elf remains active.
Compositions
Each composition in an elf's portfolio contributes 2 points. This rewards prolific creators but at a lower rate than audience reception. A composer with 10 compositions but no fans and no recent performances would have a score of 20 (Emerging) -- respectable but not enough to reach higher tiers without audience engagement.
Revels
Revels are where prestige is earned. The prestige system counts Love reactions from the last 5 revel events. Performing consistently across multiple revels builds prestige faster than a single standout performance, since the window looks at 5 revels regardless of how much time passes between them.
Tips
-
Fans are the strongest prestige lever. At 10 points each, even 2 fans bring an elf to Emerging. Focus on scheduling revels so your best composers get repeated exposure to the same audience members, which builds fandom.
-
Portfolio compounds over time. Each composition is worth only 2 points, but they never go away. A veteran elf with 20 compositions has a 40-point floor before fans and Love reactions are counted -- enough for Established tier on portfolio alone.
-
Decay is gentle but persistent. At -1.0 per 50-tick cycle, an inactive elf loses prestige slowly. But if they also lose fans (because fans expire or shift allegiance), the score drops from both the formula and the decay penalty simultaneously. Keep your top performers active.
-
Legendary status influences everyone. Because Legendary elves weight the aesthetic center 3x, their tastes ripple through the entire colony's satisfaction scores. If your Legendary elf has unusual aesthetic preferences, other elves may suffer lower aesthetic fit. Consider this when deciding which composers to promote.
-
Prestige smooths satisfaction. The +15 max from prestige can be the difference between an elf staying above their departure threshold and starting the countdown. If an elf is Discontented, boosting their prestige through a well-received revel can buy time. See Satisfaction & Departure for the full formula.
-
Watch for tier-change events. A tier drop (e.g., Renowned to Established) signals that an elf is losing cultural relevance -- their fans may be drifting or their Love reactions declining. Schedule a revel featuring their work to reverse the slide.
-
New elves start at Unknown. Every arriving elf has prestige 0. They need at least 4 Love reactions across recent revels (4 x 5 = 20) or 2 fans (2 x 10 = 20) to reach Emerging. Early revels are critical for integrating newcomers into the cultural fabric.
Fandom
Fandom turns audience reception into enduring social bonds. When an elf Loves a composition at a revel, they become a fan of the composer -- and fans shape the composer's creative life long after the revel ends. They boost the composer's social inspiration when nearby, slowly deepen their relationship over time, and occasionally wish aloud that the composer would make more work. Fandom is also the single largest contributor to prestige.
Overview
Every elf carries a Fandom memory tracking up to 5 composers whose work has moved them. The roster is built exclusively from Love reactions at revels -- not friendship, not proximity, not aesthetic alignment. To be someone's fan is to have loved their work, concretely, at a specific performance.
Once a fandom exists, the fandom_system applies its effects every 50 ticks.
A fan within 4 tiles of a composer they're a fan of:
- boosts the composer's social inspiration (+2),
- strengthens their own relationship with the composer (+1),
- and, on rare rhythmic intervals, emits a FanRequest cultural event.
These effects are small per tick but compounding. A composer who spends time near their fans accumulates social inspiration for future work and slowly turns fans into friends. A composer who never leaves their workshop gains none of the benefit, even if they have many fans on paper.
How It Works
Becoming a Fan
During a revel, when the audience reacts to a composition, each attendee's
reaction is computed from their aesthetic position relative to the piece. If an
attendee's reaction is Love and they are not the composer themselves, the
system calls record_love on that elf's Fandom component, passing the composer's
name and the current tick.
The threshold to form a fandom is one Love reaction. The first Love from a
non-self audience member immediately creates a fandom entry and fires a
FandomFormed cultural event. Subsequent Love reactions from the same fan
increment the entry's love_count but do not emit another event -- the fan is
already a fan.
| Trigger | Effect |
|---|---|
| First Love from fan X for composer Y (X != Y) | Creates FanOf { composer_name: Y, love_count: 1 }, emits FandomFormed { fan: X, composer: Y } |
| Subsequent Love from X for Y | Increments love_count (saturating at u8 max), no event |
| Love from the composer for their own work | Ignored -- no self-fandom |
Source: crates/er-sim/src/sim/systems/revel.rs, Love-reaction block around fandom.record_love(&comp.composer, tick); crates/er-sim/src/sim/components.rs, Fandom::record_love.
The 5-Fandom Cap
Each elf tracks at most 5 fandoms (MAX_FANDOMS = 5). When a sixth Love
would create a new entry, the system sorts the full list by love_count
(highest first) and truncates to 5. The weakest fandom -- the composer the elf
Loved the fewest times -- is pruned.
This means an elf's five fandoms are effectively their five most-Loved composers. An elf who Loves six different composers once each will lose one of them; the prune is deterministic (sort order) but the displaced composer is whichever tied entry landed at index 5 after sorting.
Fandoms do not decay on their own. The only way to lose a fandom is to be crowded out by a stronger one. This makes fandom memory asymmetric: easy to acquire (one Love), sticky until bumped.
Source: crates/er-sim/src/sim/components.rs, Fandom::record_love -- prune branch at self.fans_of.len() > Self::MAX_FANDOMS.
The Fandom Tick System
The fandom_system runs every 50 ticks (skipping tick 0). On each cycle, it
walks every elf with at least one fandom and checks distance to each composer
they're a fan of.
A composer within Manhattan distance 4 of the fan triggers three effects on that tick:
| Effect | Target | Magnitude | Saturation |
|---|---|---|---|
| Social inspiration boost | Composer | +2 | Saturating add (caps at u8 max) |
| Relationship strengthen | Fan's record of composer | +1 strength | Clamped in Relationships::adjust |
| Fan request | Cultural event | FanRequest { fan, composer } | Only when tick % 500 == 0 |
Multiple fandoms in range stack: a composer with three nearby fans gets +6 social inspiration that cycle. A fan with two nearby composer-idols boosts both. The effect is symmetric in that fans create value for composers, but asymmetric in flow -- only the composer's inspiration is touched, not the fan's.
The 50-tick cadence means fandom effects are slow and ambient rather than immediate. A fan moving in next door to their favorite composer will begin nudging the composer's inspiration within a handful of 50-tick cycles, not instantly.
Source: crates/er-sim/src/sim/systems/fandom.rs, fandom_system -- guard tick.is_multiple_of(50) && tick != 0, proximity check dist <= 4, effect application block.
Fan Requests
At ticks divisible by 500 -- roughly once per in-game day -- each in-range
fan/composer pair emits a FanRequest cultural event of the form "X wishes Y
would compose more." These are flavor events at EventPriority::Minor priority,
archived but not surfaced as popups.
Importantly, the request does not cause the composer to actually compose. It's narrative colour, not a task directive. The composer's own needs, aspirations, and policies still drive what they make and when. Fan requests are the atmospheric signal that fandom exists in the world -- gossip that this elf has admirers -- rather than a causal pathway.
Source: crates/er-sim/src/sim/systems/fandom.rs, FanRequest emission block at tick.is_multiple_of(500); crates/er-sim/src/sim/events.rs, FanRequest variant and EventPriority::Minor classification.
Values & Formulas
Key Constants
| Constant | Value | Role |
|---|---|---|
MAX_FANDOMS | 5 | Per-elf fandom roster cap |
| Fandom tick cadence | every 50 ticks (skipping 0) | Proximity scan and effect application |
| Proximity threshold | Manhattan distance ≤ 4 | Required for effects to apply |
| Social inspiration boost | +2 per in-range fan per cycle | Applied to composer Inspiration.social (saturating) |
| Relationship boost | +1 per in-range fandom per cycle | Applied to fan's Relationships entry for composer |
| Fan request cadence | every 500 ticks | FanRequest event emitted for each in-range pair |
Proximity Arithmetic
Manhattan distance is |fan.x - composer.x| + |fan.y - composer.y|. At
threshold 4, in-range tiles form a diamond around the composer:
. . X . .
. X X X .
X X C X X
. X X X .
. . X . .
Where C is the composer and X is an in-range tile. A fan anywhere in this
13-tile diamond contributes to the composer's inspiration that cycle.
In a typical settlement layout, this means fandom effects trigger when fans and composers share a clearing, a dining area, or an adjacent workshop -- everyday social spaces. It does not reach across large settlements or require deliberate pilgrimage.
Interaction with Prestige
Fandom is the largest single input to prestige. The formula is:
Prestige = (recent_loves x 5.0) + (fans x 10.0) + (compositions x 2.0) - decay
Each fan contributes +10 to the composer's colony-wide prestige score. Since a single Love reaction creates a fandom, and a single fandom is worth 2 Love reactions' worth of prestige, fandom formation is the moment a composer's audience reception crosses from ephemeral to durable reputation.
Because the 5-fandom cap is per-fan (not per-composer), a composer can accumulate arbitrarily many fans across the colony. A widely-loved composer easily reaches Renowned or Legendary tiers on fan count alone.
Interaction with Revel Scoring
Fandom does not directly shift revel scoring; reactions are computed from the attendee's aesthetic position relative to the composition, not from whether they're a fan. But fandom feeds back into revels indirectly:
- Composer inspiration from nearby fans (+2 social per cycle) accumulates into stronger compositions over time, which in turn produce more Love reactions at future revels, which in turn build more fandom.
- Relationship strengthening (+1 per cycle per nearby fandom) turns fans into friends, which feeds friendship-bonus dynamics elsewhere in the social fabric.
This is the core positive feedback loop for cultural celebrity: Loved work creates fans, fans create inspiration, inspiration creates more Loved work.
Strategy & Play
Reading the Fan Economy
- One Love is the only threshold that matters. You don't need a composer to blow the audience away -- a single "Love" reaction from a single attendee starts a fandom. Revels with diverse audiences are therefore efficient at spreading fandom: each attendee whose taste aligns can become a new fan.
- The 5-fandom cap is per-listener, not per-composer. An elf who has Loved 5 different composers is at capacity; their next Love for a new composer displaces their least-loved existing fandom. Mature cultures with many active composers will see fandom turnover, not just accretion.
- Fandoms stick until crowded out. There's no time-based decay on the Fandom component itself -- a fan who never sees their idol again still counts. This stabilises prestige for composers who've had breakthrough revels.
Layout Implications
Because the proximity threshold is Manhattan ≤ 4, settlement layout matters:
- Composer housing near social hubs (dining, gathering spots, revel grounds) maximises fan-adjacency, accelerating inspiration gain.
- Isolated workshops prevent fan-proximity effects even if the composer is widely Loved. A reclusive composer banks prestige (fans count colony-wide) but misses the inspiration and relationship compounding.
- Fan-heavy clearings become natural creative zones: the ambient +2 social inspiration per cycle from two or three nearby fans keeps a composer's inspiration full, enabling sustained output.
Reading FandomFormed and FanRequest Events
- FandomFormed appears in the revel recap and event log -- a one-shot "fan X became a fan of composer Y" signal. Track these to see which compositions are building durable audiences.
- FanRequest appears as ambient minor-priority events: "X wishes Y would compose more." These are indicators of ongoing cultural presence rather than action items. They do not change composer behaviour -- the composer still acts on their own aspirations.
Related Systems
- Revels -- where fandoms are formed. Love
reactions during performances create Fandom entries and
FandomFormedevents. - Prestige -- fandom is the highest-weighted input to prestige scoring (+10 per fan).
- Relationships -- fandom-driven proximity effects strengthen the fan's relationship with the composer, potentially escalating into friendship.
- Inspiration -- composers gain +2 social inspiration per cycle per in-range fan.
- Compositions -- compositions are the artefacts that, when Loved, create fans in the first place.
Aspirations
Aspirations are proactive, personality-driven goals that emerge from an elf's aesthetic position. Rather than merely reacting to needs and policies, an elf with active aspirations will seek out composing, socializing, or performing on their own initiative. Aspirations give each elf a sense of personal direction beyond survival.
Overview
Every elf maintains 1--2 active aspirations at a time, drawn from five possible goal types. The system selects goals by weighting each type against the elf's aesthetic axes -- an emotional elf gravitates toward mastery and earning love, while a social elf pursues friendships and revel performances. Aspirations are assigned automatically; the player does not pick them.
Aspirations progress naturally as elves go about their lives: composing a piece advances the ComposeWorks goal, leveling up a skill advances MasterSkill, and so on. When an aspiration completes, the elf receives a mood celebration. When one stalls too long, mood suffers instead.
How It Works
Goal Types
There are five aspiration goals an elf can pursue:
| Goal | Tracked Stat | Target Formula | Max Target |
|---|---|---|---|
| MasterSkill | Skill level (currently Music only) | current_level + 2 | 10 |
| MakeFriends | Count of Friends (strength >= 50) | current_friends + 2 | 6 |
| ComposeWorks | Number of completed compositions | current_compositions + 3 | 20 |
| PerformAtRevels | Revel performances attended | current_performances + 3 | 20 |
| EarnLove | "Love" audience reactions received at revels | current_love_reactions + 2 | 10 |
Each goal is set just beyond the elf's current achievement: a composer with 4 works gets a target of 7, an elf with 1 friend gets a target of 3.
Source: crates/er-sim/src/sim/systems/aspirations.rs, aspiration_assignment_system -- target formulas and .min() caps in the pool-construction block.
Assignment Algorithm
The assignment system runs every 50 ticks. It scans all elves and identifies those with fewer than 2 active aspirations. For each eligible elf, it builds a weighted pool of candidate goals and picks the highest-weight options to fill empty slots.
Step 1: Build the candidate pool. Each goal type gets a base weight plus a personality bonus from the elf's aesthetic axes:
| Goal | Base Weight | Aesthetic Bonus | Weight Formula |
|---|---|---|---|
| MasterSkill (Music) | 1.0 | Emotion axis | 1.0 + emotion |
| MakeFriends | 1.0 | Social axis (x2) | 1.0 + social * 2.0 |
| ComposeWorks | 1.0 | Structure axis | 1.0 + structure |
| PerformAtRevels | 0.8 | Social axis | 0.8 + social |
| EarnLove | 0.8 | Emotion axis (x1.5) | 0.8 + emotion * 1.5 |
Aesthetic axes range from 0.0 to 1.0, so the weight range for each goal is:
- MasterSkill: 1.0--2.0
- MakeFriends: 1.0--3.0 (strongest possible bias)
- ComposeWorks: 1.0--2.0
- PerformAtRevels: 0.8--1.8
- EarnLove: 0.8--2.3
Step 2: Filter. Goals are removed from the pool if:
- The elf already has an active aspiration of the same type (no duplicate variants).
- The elf has already reached the cap (e.g., Music skill is already 10, or they already have 6 friends).
Step 3: Select. The pool is sorted by weight (highest first). The system picks the top 1--2 goals to fill empty slots. Selection is deterministic -- the highest-weight goal always wins.
Source: crates/er-sim/src/sim/systems/aspirations.rs, aspiration_assignment_system -- pool construction, dedup filter, and deterministic sort.
Progress Tracking
The progress system runs every 10 ticks. It reads each elf's current state (skill levels, friend count, composition count, revel performances, love reactions) and compares against aspiration targets.
Progress updates are absolute, not incremental: if an elf's music skill is 5 and the aspiration target is 7, the progress value is set to 5. When the elf levels up to 6, progress becomes 6. When it reaches 7, the aspiration completes.
Each time progress increases, the last_progress_tick timestamp resets. This is important for stall detection.
Source: crates/er-sim/src/sim/systems/aspirations.rs, aspiration_progress_system -- progress read and update per 10-tick cycle.
Completion
An aspiration completes when progress >= target. On completion:
- The aspiration status changes from Active to Completed.
- A
CulturalEvent::AspirationCompletedevent fires (priority: Notable). - The elf receives a mood modifier: "Fulfilled an aspiration!", value +8, duration 200 ticks.
- At the next 50-tick assignment cycle, a new aspiration is assigned to fill the vacated slot.
Source: crates/er-sim/src/sim/systems/aspirations.rs, aspiration_progress_system -- status-change / event emission and mood-modifier branches on completion.
Abandonment and Stalling
Aspirations are never explicitly abandoned by the elf. Instead, a stall is detected when an active aspiration has made no progress for 500 ticks.
When a stall is detected:
- A
CulturalEvent::AspirationStalledevent fires (priority: Notable). - The elf receives a mood modifier: "Aspiration stalled", value -2, duration 100 ticks.
- The stall timer resets (
last_progress_tickupdated to current tick), so the stall event fires at most once per 500-tick window rather than every 10 ticks.
The aspiration stays Active even after stalling -- it does not automatically become Abandoned. It can still complete if the elf eventually makes progress. The stall is a warning, not a death sentence.
Source: crates/er-sim/src/sim/components.rs, Aspiration::is_stalled; crates/er-sim/src/sim/systems/aspirations.rs, aspiration_progress_system -- stall detection and mood-modifier branches.
Values & Formulas
Timing Constants
| Constant | Value | Description |
|---|---|---|
| Assignment interval | 50 ticks | How often new aspirations are assigned |
| Progress check interval | 10 ticks | How often progress is re-evaluated |
| Stall threshold | 500 ticks | No-progress duration before stall fires |
| Completion mood duration | 200 ticks | How long the +8 celebration mood lasts |
| Stall mood duration | 100 ticks | How long the -2 stall penalty lasts |
Mood Effects
| Event | Mood Value | Duration | Method |
|---|---|---|---|
| Aspiration fulfilled | +8 | 200 ticks | push (stacks with other modifiers) |
| Aspiration stalled | -2 | 100 ticks | replace (refreshes if already present) |
Weight Summary
| Goal | Axis | Min Weight | Max Weight | Personality That Favors It |
|---|---|---|---|---|
| MasterSkill | Emotion | 1.0 | 2.0 | Emotional elves (emotion near 1.0) |
| MakeFriends | Social | 1.0 | 3.0 | Social elves (social near 1.0) |
| ComposeWorks | Structure | 1.0 | 2.0 | Structured elves (structure near 1.0) |
| PerformAtRevels | Social | 0.8 | 1.8 | Social elves (social near 1.0) |
| EarnLove | Emotion | 0.8 | 2.3 | Emotional elves (emotion near 1.0) |
Interactions
Aspiration-Driven Task Selection
Aspirations directly influence what idle elves choose to do. In the task decision system (Step 3b of Roles), aspiration goals are checked before cultural policy roles take effect:
- ComposeWorks or MasterSkill (Music): the elf's
wants_composeflag activates. If a Workshop is available, the elf walks there and composes instead of waiting for a role assignment. - MakeFriends: the elf's
wants_socializeflag activates. The elf heads to the nearest Feast Hall (or Garden as fallback) to seek proximity with other elves.
These aspiration-driven behaviors sit between moderate need fulfillment (Step 3) and skill-driven preference (Step 3c) in the priority order. They are overridden by critical needs, creative block, mourning, and discontented states.
Source: crates/er-sim/src/sim/systems/aspirations.rs, aspiration_wants_compose and aspiration_wants_socialize; crates/er-sim/src/sim/systems/behavior.rs, task_decision_system -- aspiration-driven task selection.
Discontented Override
Discontented elves refuse all aspiration-driven work. A discontented elf with an active ComposeWorks aspiration will not compose -- they fall through to basic survival behaviors (gathering, wandering). The aspiration remains active but cannot make progress until the elf's satisfaction recovers.
Source: crates/er-sim/src/sim/systems/behavior.rs, task_decision_system -- Discontented short-circuit.
Related Systems
- Aesthetic Position -- the four axes (structure, tradition, emotion, social) determine which aspirations an elf is drawn toward.
- Relationships -- MakeFriends tracks the friend count (relationships with strength >= 50).
- Compositions -- ComposeWorks tracks completed compositions in the elf's portfolio.
- Revels -- PerformAtRevels and EarnLove track revel participation and audience reactions.
- Skills -- MasterSkill tracks skill level progression.
- Prestige & Reputation -- completing aspirations contributes to an elf's visible accomplishments, though prestige is tracked separately.
- Needs & Mood -- completion and stall events push mood modifiers that ripple into morale and satisfaction.
Tips
- Watch the aesthetic axes. An elf with high social (near 1.0) will almost always pick MakeFriends first (weight up to 3.0), crowding out other goals. If you want more composing, use the Curator to assign the Composer role -- that bypasses aspiration priority.
- Stalls are diagnostic. A stall event means an elf has been stuck for 500 ticks. Common causes: no Workshop for compose-aspiring elves, no nearby elves for social aspirations, or a skill ceiling that requires more practice time. Build the infrastructure the elf needs.
- Completion chains are self-reinforcing. The +8 mood boost from fulfilling an aspiration is strong (comparable to a revel performance bonus). Happy elves work faster, which accelerates the next aspiration. A single completion can start a virtuous cycle.
- Discontented elves lose all momentum. Since discontented elves refuse aspiration work, their progress stalls. This can trigger the -2 stall penalty on top of existing satisfaction problems, creating a downward spiral. Address discontent early.
- PerformAtRevels and EarnLove need revels. These aspirations cannot progress without active revel scheduling. If no revels are happening, elves with these goals will stall. Make sure the Curator (or you through policies) keeps revels on the calendar.
- Two slots, five types. Since each elf holds at most 2 aspirations and duplicates are blocked, the system naturally diversifies. An elf will never double up on MakeFriends -- the second slot will always be a different goal type.
Arrivals
New elves arrive through three channels: reputation, cultural events, and wandering. Each channel has different triggers, personalities, and first-impression filters. Your settlement's artistic output and social health directly control who shows up and whether they stay.
Overview
After the founding elves, every new elf arrives through one of three channels:
| Channel | Trigger | Frequency | Personality |
|---|---|---|---|
| Reputation | Colony reputation score | Periodic (interval scales with reputation) | Aesthetically aligned |
| Cultural Event | Great revel (avg score >= 75) | Burst of 1-2 elves | Drawn by the art |
| Wanderer | Random interval | ~500 ticks | Unpredictable aesthetics |
All arrivals use bimodal personality generation: each axis is set to 0.2 or 0.8 (coin flip) with +/-0.1 jitter. This produces elves with strong personality leanings, unlike founders who cluster near the 8 personality corners.
The arrival system runs two tick systems:
- Scheduling (every tick): checks whether a new arrival should be queued
- Spawning (every tick): processes queued arrivals after their spawn delay
Source: src/sim/arrival.rs -- arrival_scheduling_system, arrival_spawn_system.
How It Works
Reputation Channel
Your colony's artistic reputation attracts elves who have heard of the settlement and want to join.
Reputation formula:
reputation = prestige_sum + (portfolio_count x 0.5) + (recent_revel_avg x 0.8)
prestige_sum: sum of all elves' prestige scoresportfolio_count: total number of compositions in the settlementrecent_revel_avg: average audience score from the most recent revel
Scheduling interval:
interval = baseline_interval / clamp(reputation / reputation_pivot, 0.2, 5.0)
baseline_interval: 300 ticksreputation_pivot: 50.0- At reputation 50: interval = 300 ticks (baseline)
- At reputation 250: interval = 60 ticks (5x faster, max rate)
- At reputation 10: interval = 1,500 ticks (5x slower, min rate)
Jitter of +/-(interval/4) prevents arrivals from being predictable.
Spawn delay: 0-30 ticks after scheduling.
Reputation arrivals start with 60.0 satisfaction and always pass the first-impression filter (threshold 2.0 is unreachable by aesthetic distance, which caps at 2.0).
Source: src/sim/arrival.rs -- reputation_score(), schedule_reputation_arrival().
Cultural Event Channel
When a revel ends with an average audience score >= 75.0, word spreads and 1-2 elves arrive as a burst. These arrivals are drawn specifically by the artistic quality they heard about.
- Spawn delay: 20-80 ticks (they travel from afar)
- Initial satisfaction: 60.0
- First-impression filter: threshold 2.0 (always pass)
- Burst size: 1-2 elves per qualifying revel
This is the primary reward loop for running great revels. The better your compositions match your audience's tastes, the more new elves arrive.
Source: src/sim/arrival.rs -- great revel detection in scheduling system.
Wanderer Channel
Wanderers arrive on a regular interval regardless of your settlement's reputation. They represent elves who stumble upon the settlement while traveling.
- Interval: 500 ticks, with jitter of +/-(interval/3)
- Spawn delay: 0-50 ticks
- Initial satisfaction: 45.0 (lower than reputation/cultural -- they're skeptical)
- First-impression filter: strict threshold of 1.2 (or 1.5 if the settlement recently held a welcoming revel)
Wanderers are the most likely to leave during the evaluation period. Their lower starting satisfaction and stricter aesthetic filter mean they're testing whether this settlement is right for them.
Source: src/sim/arrival.rs -- schedule_wanderer_arrival().
First Impressions
Every arriving elf gets a 50-tick evaluation window to assess the settlement. During this window, their aesthetic position is compared against the colony average:
- If aesthetic distance > threshold: the elf rejects the settlement and leaves, generating a
WandererRejectedevent - If aesthetic distance <= threshold: the elf settles, generating a
WandererSettledorElfArrivedevent
| Channel | Threshold | Practical Effect |
|---|---|---|
| Reputation | 2.0 | Always passes (max possible distance is 2.0) |
| Cultural Event | 2.0 | Always passes |
| Wanderer | 1.2 (or 1.5 if recent revel) | ~60-70% of wanderers pass |
The recent-revel bonus (threshold 1.5 instead of 1.2) rewards active settlements. If you're holding regular revels, wanderers are more likely to stay.
Source: src/sim/arrival.rs -- first_impression_system, evaluation window of 50 ticks.
Aesthetic Bias
Arriving elves don't have random aesthetics. Their aesthetic position is generated using a Gaussian distribution centered on the colony average with sigma = 0.3.
This means most arrivals have tastes somewhat similar to the existing population, with occasional outliers. Over many arrivals, the settlement's aesthetic consensus gradually reinforces itself -- new elves who arrive tend to agree with the existing majority, which pushes the colony average further in that direction.
The sigma of 0.3 provides enough variance that surprising arrivals happen. An elf with radically different tastes can still arrive and stay (especially via the reputation channel, which always passes the first-impression filter). These outliers become the seeds of aesthetic diversity and potential rivalry.
Source: src/sim/arrival.rs -- Box-Muller transform with sigma 0.3, centered on colony aesthetic mean.
Name Family Weighting
Arrival names are drawn from name families with weights that reflect the elf's aesthetic position:
| Family | Weight Formula | Aesthetic Lean |
|---|---|---|
| Silvani | 1.0 + (1.0 - tradition) x 0.5 | Low tradition (experimental) |
| Valdari | 1.0 + tradition x structure x 1.5 | High tradition + structure |
| Nelvari | 1.0 + emotion x (1.0 - social) x 1.5 | Emotional + personal |
| Aetheri | 1.0 + (1.0 - tradition) x social x 1.5 | Social + experimental |
A highly traditional, structured elf is more likely to receive a Valdari name. An emotional, personal elf is more likely to be Nelvari. The weighting is probabilistic, not deterministic -- any elf can get any family name, just at different rates.
Source: src/sim/arrival.rs -- name family weight calculation.
Founder Marker
The starting elves carry a Founder marker component. This is a permanent tag that distinguishes the original settlers from later arrivals. Founders use corner-cluster personality generation (base 0.85/0.15 with +/-0.1 jitter) rather than the bimodal generation (0.2/0.8 coin flip) used for arrivals.
The Founder marker has no direct mechanical effect beyond personality generation, but it serves as narrative context -- your founding elves are the original vision for the settlement.
Source: src/sim/components.rs, Founder struct.
Values & Formulas
Arrival Rate Examples
| Reputation Score | Interval | ~Arrivals per 1,000 ticks |
|---|---|---|
| 10 | 1,500 ticks | 0.67 |
| 25 | 600 ticks | 1.67 |
| 50 | 300 ticks | 3.33 |
| 100 | 150 ticks | 6.67 |
| 250+ | 60 ticks | 16.67 |
Plus wanderers every ~500 ticks and burst arrivals from great revels.
Satisfaction Starting Points
| Channel | Starting Satisfaction | Departure Risk |
|---|---|---|
| Reputation | 60.0 | Low |
| Cultural Event | 60.0 | Low |
| Wanderer | 45.0 | Moderate |
For context, the departure warning threshold is crossed at 300 ticks of low satisfaction. Wanderers, starting 15 points lower, reach danger faster.
Interactions
Reputation Feedback Loop
Arrivals create a positive feedback loop:
- More elves produce more compositions
- More compositions raise
portfolio_countin the reputation formula - Higher reputation shortens the arrival interval
- More elves arrive
This loop is bounded by satisfaction -- if new elves aren't happy, they depart, reducing the population back. Overcrowding and aesthetic clashes naturally limit growth.
Aesthetic Drift
Because arrivals are biased toward the colony aesthetic mean (sigma 0.3), a large settlement with strong aesthetic consensus will attract more like-minded elves. This can create aesthetic monocultures where everyone agrees -- which reduces rivalry and revel drama but also makes compositions more predictable.
Wanderers (with their stricter first-impression filter but random timing) are the main source of aesthetic diversity. A wanderer who passes the 1.2 threshold but sits at the edge of the aesthetic space can become the seed of a new artistic movement.
Revel Quality
Great revels (avg score >= 75) directly trigger the cultural event channel. This makes revel optimization a population growth strategy -- running revels that match your audience's tastes not only boosts mood and satisfaction but brings in new elves.
Tips
-
Prestige drives reputation. The prestige_sum component of the reputation formula is usually the largest contributor. Elves with high prestige (from Love reactions at revels) are your best recruiters.
-
Portfolio padding works. Even mediocre compositions contribute +0.5 to reputation each. A settlement with 20 compositions (even Crude ones) gets +10 reputation from portfolio alone.
-
Wanderer rejection is information. When a
WandererRejectedevent fires, it tells you the settlement's aesthetic consensus is narrow enough to filter out travelers. This isn't necessarily bad -- it means your aesthetic identity is strong. -
Early revels attract early arrivals. The cultural event channel (great revel >= 75 avg) is the fastest way to grow in the early game. Schedule revels as soon as you have a FeastHall, 5+ elves, food, and compositions.
-
Watch for satisfaction collapse. Rapid growth from high reputation can overwhelm your food supply, crowd housing, and dilute social bonds. New arrivals start with 60 satisfaction but need friend formation and aesthetic alignment to stay above the departure threshold.
Favorite Places
Overview
Elves develop attachment to locations they spend time in. Over repeated visits, a position becomes a favorite place -- a spot the elf feels drawn to and gains comfort from. Each elf can have up to 3 favorite places.
Favorite places interact with the personal time behavior: idle elves occasionally take short breaks to visit a favorite spot or a garden, gaining beauty inspiration and a mood boost.
How It Works
Attachment Formation
The favorite places system runs every 10 ticks. For each stationary elf (no active pathfinding), the system checks whether their current position is "meaningful":
| Position Type | Qualifies? |
|---|---|
| Within 1 tile of a building (Workshop, Garden, Dwelling, etc.) | Yes |
| On scenic terrain (Ancient Forest, Meadow) | Yes |
| Open terrain with no buildings nearby | No |
If the position qualifies, visit ticks accumulate. Once a position reaches the attachment threshold of 20 visit ticks, it becomes a favorite.
Source: crates/er-sim/src/sim/systems/satisfaction.rs, favorite_places_system; crates/er-sim/src/sim/components.rs, FavoritePlaces.
Slot Management
Each elf has 3 favorite place slots. Slots are sorted by visit count (most visited first). New positions are added to empty slots. When all 3 slots are full, new positions cannot displace existing favorites -- the elf must continue visiting one of their current 3.
Mood Boost
When an elf is standing at a favorite place, they receive a mood modifier:
| Modifier | Value | Duration | Semantics |
|---|---|---|---|
| "Favorite spot" | +1 | 15 ticks | Replace (no stacking) |
This is a small but steady comfort bonus for elves who spend time in places they've grown attached to.
FavoritePlaceFormed Event
When a new favorite forms (position crosses the 20-tick threshold), a FavoritePlaceFormed event fires with the elf's name and a description of the location (zone name if in a zone, or terrain type with coordinates).
Personal Time
Idle elves have a 5% chance per idle decision to take personal time instead of accepting work. During personal time, the elf paths toward their top favorite place (or a Garden if no favorites exist).
On arrival, the elf receives:
| Benefit | Amount |
|---|---|
| Beauty need | +5 |
| Beauty inspiration | +3 |
| Solitude inspiration | +2 |
| Mood: "Personal time" | +2 for 50 ticks |
After receiving these benefits, the elf returns to Idle and resumes normal behavior.
Personal time is a low-priority behavior -- it is preempted by hunger, exhaustion, and all other urgent needs.
Source: crates/er-sim/src/sim/systems/satisfaction.rs, personal_time_system; crates/er-sim/src/sim/systems/behavior.rs, task_decision_system Step 3d.
Interactions
- Needs & Mood -- Favorite spot mood boost contributes to morale; personal time restores beauty need.
- Inspiration -- Personal time adds beauty and solitude inspiration, feeding into composition quality.
- Satisfaction -- Mood improvements from favorite places and personal time contribute to morale, which feeds the satisfaction formula at 0.2 weight.
- Buildings -- Buildings create "meaningful" positions that can become favorites. Gardens are common personal time destinations.
Tips
-
Workshops and Gardens naturally generate favorites. Elves who compose regularly at the same workshop will develop attachment to it.
-
Favorite places are a retention tool. The +1 mood boost is small but persistent. An elf standing at a favorite spot while composing gets a steady morale lift.
-
Personal time is self-regulating. At 5% per idle check, it happens roughly once every 20 idle decisions. You don't need to manage it -- elves take breaks on their own.
-
Scenic terrain matters. Elves near Ancient Forest or Meadow tiles can develop favorites without buildings nearby. Consider settlement placement near scenic areas.
Tree Bonds
Elves form deep bonds with notable trees -- relationships that root them to the landscape. These bonds grow through proximity, provide quiet comfort, and when a bonded tree dies, the grief that follows tests the settlement's social fabric. How an elf mourns depends on their tradition values, and disagreements over mourning rites can fracture relationships.
Overview
Tree bonds follow a lifecycle:
- Formation -- an elf near a notable tree begins building a bond automatically.
- Strengthening -- continued proximity increases bond strength over time.
- Loss -- when a bonded tree dies, the elf enters grief. Their response depends on their personality's tradition axis.
- Social friction -- elves who refuse the traditional elegy can draw the ire of traditionalist community members, damaging relationships.
How Bonds Form
Any elf within 3 tiles (Manhattan distance) of a notable tree can form a bond. The system runs every 50 ticks:
- An unbonded elf near a notable tree starts a bond at strength 20.
- A bonded elf near their tree gains +1 strength per cycle (capped at 100).
- Each elf can hold only one tree bond at a time -- the ECS enforces this as a single component.
TreeBond Component
| Field | Type | Range | Description |
|---|---|---|---|
| tree_entity | Entity | — | The bonded notable tree |
| tree_name | String | — | The tree's notable name (e.g., "The Elder Oak of the Northern Glade") |
| strength | u8 | 0--100 | Bond strength, starting at 20 |
| compositions_nearby | u8 | 0--255 | Number of compositions created near this tree |
The compositions_nearby field tracks creative work done in the tree's presence, connecting the bond to the settlement's artistic output.
Source: crates/er-sim/src/sim/components.rs, TreeBond; crates/er-sim/src/sim/flora_systems.rs.
Grief and Mourning
When a bonded notable tree dies (reaches the Dying stage and is despawned), all elves bonded to that tree enter grief. The elf's tradition axis value determines their response:
| Tradition | Response | Mood Penalty | Mourning Component | Duration |
|---|---|---|---|---|
| >= 0.3 | Formal mourning with elegy | -8 "Bonded tree lost" | Yes: -5 "Grieving", pulled toward composing elegies (+15 emotional) | 300 ticks |
| < 0.3 | Elegy refusal (personal grief) | -8 "Bonded tree lost" | No | 300 ticks |
Both paths carry the same base mood hit. The key difference is the Mourning component: traditional elves enter a behavioral state that adds an extra -5 mood ("Grieving") but also drives them toward composing elegy works. These elegies receive a +15 emotional property bonus, making grief a potent source of emotionally charged art.
A BondedTreeDied event appears in the log when grief initiates.
Source: crates/er-sim/src/sim/flora_systems.rs, tree_death_grief.
Elegy Refusal
Elves with tradition below 0.3 refuse the formal elegy. They grieve privately -- same mood penalty, but no Mourning component and no elegy composition. An ElegyRefused event appears in the log, described as the elf "grieving silently, refusing the old forms."
This is not a lesser grief -- the mood hit is identical. But the refusal is visible to other elves and can trigger social consequences.
Social Friction
Elegy refusal offends illiberal traditionalists -- elves who hold strongly traditional values and have low tolerance for deviation. The threshold is:
Tradition > 0.7 AND social x patience < 0.2
This combination identifies elves who are both deeply traditional and interpersonally intolerant. When they witness an elegy refusal, they react with:
| Effect | Value | Duration |
|---|---|---|
| Mood penalty | -3 "Rites disrespected" | 100 ticks |
| Relationship damage toward refuser | -10 | Permanent |
The -10 relationship damage is significant -- it can push an Acquaintance (starting at 0) a quarter of the way toward Rival status (-30 threshold; see Relationships). If multiple elves refuse, a single traditionalist accumulates damage toward each refuser separately.
A RitesDisrespected event appears in the log for each offended traditionalist.
This creates genuine social tension: settlements with mixed tradition values will experience friction when notable trees die. Highly traditional communities mourn together; diverse communities fracture along tradition lines.
Source: crates/er-sim/src/sim/flora_systems.rs, tree_death_grief; crates/er-sim/src/sim/events.rs, RitesDisrespected.
Interactions
- Flora -- trees must be notable before bonds can form. Notable promotion criteria (Ancient stage, drought survival, beloved gathering place) determine which trees become bondable.
- Relationships -- social friction from elegy refusal feeds into the relationship system. The -10 damage per refusal can push neutral relationships toward rivalry.
- Personality -- the tradition axis gates whether an elf performs the formal elegy or refuses it. This is one of the most visible consequences of tradition values in the settlement.
- Aesthetic Position -- the tradition axis (0.0--1.0) is part of the 4D aesthetic position. Elves with tradition < 0.3 refuse elegies; elves with tradition > 0.7 may be offended by refusal.
- Compositions -- mourning elves are pulled toward composing, and their works receive +15 emotional property. Tree grief is one of the strongest drivers of emotionally powerful art.
Tips
-
Diverse settlements will fracture when notable trees die. If your elves span the tradition spectrum (some < 0.3, some > 0.7), expect
ElegyRefusedandRitesDisrespectedevents to cascade into relationship damage. This is by design -- it creates social drama that drives art. -
Traditional settlements mourn in unison. If most elves have tradition >= 0.3, tree death triggers collective mourning. The -5 "Grieving" mood is painful, but the elegy compositions (+15 emotional) can produce some of the settlement's most powerful art.
-
Watch for illiberal traditionalists. The social x patience < 0.2 threshold means even a moderately traditional elf (tradition 0.75) can become illiberal if they're also impatient and antisocial. Check Personality values to identify potential friction sources.
-
Tree bonds form passively. You don't need to manage bond formation -- elves near notable trees will bond automatically. But you can influence which trees become notable by encouraging elves to designate Favorite Places near specific trees (3+ favorites triggers promotion).
-
Grief produces great art. The Mourning component pulls elves toward composing and grants +15 emotional. If an elf was already near a workshop, tree death can trigger an immediate elegy composition. The most emotionally resonant works in your settlement may come from loss.
Seasons & Weather
The climate system drives a 100-day year divided into four 25-day seasons. Weather and temperature shift each day, affecting mood, foraging efficiency, and the forest spirit.
Overview
Time in Elf Revel is measured in ticks. One in-game day is 100 ticks. A full year is 100 days (10,000 ticks), split evenly into four seasons of 25 days each. At each day boundary, the climate system advances the season (if needed), rolls for new weather, and computes a daily base temperature with jitter.
Temperature updates every tick based on time of day. Weather persists with 70% momentum -- there is only a 30% chance it changes on any given day within the same season, while season boundaries always force a reroll.
How It Works
The Year Cycle
| Season | Days | Day Range (in year) | Description |
|---|---|---|---|
| Spring | 0--24 | 0--24 | Awakening. Balanced foraging. Spirit is sensitive. |
| Summer | 25--49 | 25--49 | Peak abundance. Highest foraging multiplier. |
| Autumn | 50--74 | 50--74 | Declining bounty. Snow begins to appear. |
| Winter | 75--99 | 75--99 | Harsh. Reduced foraging. Snow is common. Spirit dormant. |
Years wrap: day 100 is Spring of year 1, day 200 is Spring of year 2, and so on.
Source: src/sim/climate.rs, SEASON_LENGTH = 25, YEAR_LENGTH = 100, Season::from_day.
Time of Day
Each 100-tick day is divided into four periods:
| Period | Tick Range (within day) | Temperature Modifier |
|---|---|---|
| Dawn | 0--9 | -5 |
| Day | 10--69 | +0 (base) |
| Dusk | 70--84 | -3 |
| Night | 85--99 | -8 |
The temperature modifier is applied to the daily base temperature each tick. Dawn and Night are the coldest periods.
Source: src/sim/world.rs, TimeOfDay::from_tick. src/sim/climate.rs, Climate::update_temperature.
Temperature Curve
Each season defines a quadratic temperature curve with three control points: start, mid-extreme, and end. The temperature is interpolated across the 25 days of the season using quadratic interpolation, then jittered by a random value in the range +/-5.
| Season | Start Temp | Mid-Extreme | End Temp |
|---|---|---|---|
| Spring | 35 | 45 | 55 |
| Summer | 55 | 75 | 65 |
| Autumn | 65 | 45 | 30 |
| Winter | 30 | 15 | 25 |
The interpolation formula uses quadratic Bezier-style weighting:
base = start x (1-t)(1-2t) + mid x 4t(1-t) + end x t(2t-1)
Where t = day_in_season / 24 (0.0 to 1.0).
After interpolation, jitter of +/-5 is applied, and the result is clamped to 0--100. Temperature can never go below 0 or above 100.
Source: src/sim/climate.rs, Season::temperature_curve and Climate::advance_day.
Temperature Thresholds
| Condition | Threshold | Effect |
|---|---|---|
| Cold | Temperature < 25 | Outdoor mood penalty: "Chilled" -2 |
| Hot | Temperature > 75 | Universal mood penalty: "Sweltering" -1 |
Cold only affects elves outdoors (not within Manhattan distance 2 of a roofed building). Hot affects everyone.
Source: crates/er-sim/src/sim/climate.rs, is_cold() and is_hot(). crates/er-sim/src/sim/systems/mood.rs, weather_mood_system.
Weather System
Weather is determined daily. On a season boundary, weather is always rerolled. Within a season, there is a 30% chance of reroll and a 70% chance the previous weather persists (momentum).
Source: src/sim/climate.rs, Climate::advance_day -- rng.gen::<f32>() < 0.3 triggers reroll.
Weather Types
| Weather | Description | Possible Seasons |
|---|---|---|
| Clear | Sunny skies. No special effects. | All |
| Cloudy | Overcast. No direct effects. | All |
| Rain | Outdoor mood penalty. | All |
| Storm | Flavor event (neutral mood). | All |
| Snow | Outdoor beauty bonus. | Autumn, Winter only |
| Fog | Inspiration boost for skilled musicians. | All |
Snow has zero weight in Spring and Summer -- it physically cannot occur in those seasons.
Source: src/sim/climate.rs, Weather::season_weights.
Values & Formulas
Season Weather Weights
Each weather type has a probability weight per season. To get the percentage chance, divide by the row total (100 in all cases).
| Weather | Spring | Summer | Autumn | Winter |
|---|---|---|---|---|
| Clear | 40 | 60 | 40 | 30 |
| Cloudy | 25 | 15 | 25 | 25 |
| Rain | 25 | 10 | 20 | 10 |
| Storm | 5 | 10 | 10 | 5 |
| Snow | 0 | 0 | 5 | 25 |
| Fog | 5 | 5 | 0 | 5 |
Notable patterns:
- Summer is 60% Clear -- the best season for outdoor work.
- Winter has a 25% Snow chance -- the highest of any weather type besides Clear.
- Fog only appears in Spring, Summer, and Winter (0% in Autumn).
- Storms are rare in Spring and Winter (5%), more common in Summer and Autumn (10%).
Source: src/sim/climate.rs, Weather::season_weights.
Seasonal Mood Modifiers
In addition to weather, each season applies a persistent mood modifier to all elves. These are refreshed at every day boundary using replace semantics.
| Season | Modifier | Value | Duration |
|---|---|---|---|
| Spring | "Spring optimism" | +2 | 1 day (100 ticks) |
| Summer | (none) | -- | -- |
| Autumn | "Autumn melancholy" | -1 | 1 day (100 ticks) |
| Winter | "Winter stillness" | -1 | 1 day (100 ticks) |
Spring is the most mood-positive season (+2 baseline), while Autumn and Winter carry a persistent -1 drag on morale. Summer is neutral -- no seasonal modifier, but typically benefits from 60% Clear weather.
Source: crates/er-sim/src/sim/systems/mood.rs, seasonal_mood_system.
Weather Mood Effects
These are applied by the weather mood system. "Outdoors" means the elf is more than Manhattan distance 2 from any Dwelling, Workshop, or Feast Hall.
| Weather/Condition | Target | Mood Value | Duration | Notes |
|---|---|---|---|---|
| Rain (outdoors) | Outdoor elves | -1 "Caught in rain" | 50 ticks | Shelter negates |
| Snow (outdoors) | Outdoor elves | +2 "Snow-covered landscape" | 50 ticks | Beauty bonus |
| Storm | All elves | 0 "Sheltering from storm" | 50 ticks | Flavor only |
| Fog | Musicians (skill > 5) | +1 "Mysterious fog" | 50 ticks | Also +1 Nature inspiration |
| Cold (temp < 25, outdoors) | Outdoor elves | -2 "Chilled" | 50 ticks | Shelter negates |
| Hot (temp > 75) | All elves | -1 "Sweltering" | 50 ticks | Cannot be avoided |
Mood effects use replace semantics -- they refresh the duration instead of stacking. An elf standing in rain for 100 ticks gets one -1 modifier refreshed repeatedly, not multiple stacking penalties.
Source: crates/er-sim/src/sim/systems/mood.rs, weather_mood_system -- uses mood.replace().
Foraging Multipliers
The seasonal foraging multiplier affects passive sustenance gain when elves forage:
| Season | Multiplier | Effective Foraging |
|---|---|---|
| Spring | 1.0x | Baseline |
| Summer | 1.5x | Peak abundance |
| Autumn | 1.25x | Slight bonus |
| Winter | 0.5x | Halved |
Foraging happens every 10 ticks for elves with sustenance below 40 on walkable, non-stone terrain. The base gain is 5, multiplied by the seasonal factor and rounded (minimum 1). This means Winter foraging yields only 3 sustenance per forage event, while Summer yields 8.
| Season | Base | Multiplied | Rounded Yield |
|---|---|---|---|
| Spring | 5 | 5.0 | 5 |
| Summer | 5 | 7.5 | 8 |
| Autumn | 5 | 6.25 | 6 |
| Winter | 5 | 2.5 | 3 |
Sustenance is capped at 60 from foraging alone.
Source: crates/er-sim/src/sim/climate.rs, Climate::season_foraging_multiplier. crates/er-sim/src/sim/systems/gathering.rs, foraging_system.
Hydrology
Weather directly drives the water system. See Hydrology for full details.
| Weather | Water Effect |
|---|---|
| Rain | Adds water to all outdoor tiles (orographic: more at high elevation) |
| Storm | Heavy water addition (storm_amount = 8 vs rain_amount = 3) |
| Snow | Adds water at half rain rate; accumulates on frozen surfaces |
| Clear/Cloudy/Fog | No precipitation |
Temperature drives freeze/thaw:
| Temperature | Water Effect |
|---|---|
| Below 20 | Shallow water freezes (15 ice/day); deep water freezes slowly (3 ice/day) |
| 20--30 | Hysteresis band -- no change |
| Above 30 | Ice melts at 10/day; frozen tiles thaw |
Summer adds +2 to evaporation rate, drying up shallow pools faster.
Seasonal Anchor Events
Each season has a named cultural anchor event -- a window of opportunity for the colony to celebrate. If the colony holds a revel during the window, the season's cultural identity is honored. If not, the colony feels the absence.
| Season | Anchor Event | Window |
|---|---|---|
| Spring | Awakening Revel | Day 10--14 |
| Summer | Radiance Festival | Day 10--14 |
| Autumn | Harvest Gathering | Day 10--14 |
| Winter | Stillness Vigil | Day 10--14 |
The anchor window lasts 5 days, centered on the season's midpoint. During this window, the curator's SettlementState includes the anchor name as a scheduling hint. Any revel held during the window (including a Standard revel) satisfies the anchor.
Missed anchor penalty: If the window closes (day 15) without a revel, every elf receives "Missed [Anchor Name]" (-2 mood, 150 ticks). This is a colony-wide narrative beat -- the community didn't gather when the season called for it. The penalty is mild but visible, and it stacks with other seasonal mood modifiers.
The anchor watchdog resets at the start of each season (day 0).
Source: crates/er-sim/src/sim/climate.rs, SeasonalAnchor; crates/er-sim/src/sim/systems/mood.rs, seasonal_anchor_watchdog_system.
Seasonal Mood Modifiers
Beyond weather-driven mood effects, each season carries its own ambient emotional texture. These modifiers are applied at day boundary using replace semantics (refreshed daily, never stacking with themselves).
Ambient Modifiers
| Season | Modifier | Value | Duration |
|---|---|---|---|
| Spring | Spring Awakening | +2 | 100 ticks (1 day) |
| Summer | Summer Vigor | +1 | 100 ticks (1 day) |
| Autumn | Autumn Melancholy | -1 | 100 ticks (1 day) |
| Winter | Winter Reflection | 0 | 100 ticks (1 day) |
Winter Reflection is zero mood -- present in the stack for display but not a penalty. The contemplative season doesn't punish; it provides context.
Transition Modifiers
| Modifier | Value | Duration | Trigger |
|---|---|---|---|
| First [Season] | +3 | 50 ticks | First day of each new season (day 0) |
| Season's End | -1 | 30 ticks | Last 3 days of each season (day 22--24) |
"First Spring" fires on the year's first day -- the freshest start energy. "Season's End" is the waning melancholy as one season gives way to the next.
Source: crates/er-sim/src/sim/systems/mood.rs, seasonal_mood_system.
Interactions
Forest Spirit
The Forest Spirit responds differently to clearing based on season. Clearing forest in Spring adds +7 anger (vs. +5 in other seasons) because the forest is awakening. The spirit's natural dawn decay and garden calming also vary by season -- see the Forest Spirit page for details.
Composition and Inspiration
Fog weather adds +1 to the Nature inspiration channel for all elves, which can help trigger compositions. Snow gives a beauty mood bonus that feeds into morale, indirectly improving composition emotional depth.
Needs Decay
Weather does not directly change needs decay rates, but the mood modifiers it produces affect morale, which determines the morale state (Inspired/Normal/Stressed/Breaking). See Needs & Mood. Morale state in turn affects work speed: Inspired elves get +2 work speed, Stressed elves get -1.
Building Shelter
Roofed buildings (Dwelling, Workshop, Feast Hall) provide shelter within Manhattan distance 2. Gardens do not provide shelter -- they are open-air structures. In rainy or cold weather, having roofed buildings near work areas protects your elves from mood penalties.
Tips
-
Stockpile food before Winter. Foraging drops to 0.5x, and Snow weather (25% chance) adds further pressure. Aim for a food stockpile of 15+ by day 50 (mid-Autumn).
-
Summer is build season. With 60% Clear weather and 1.5x foraging, your elves can work outdoors without mood penalties and food accumulates naturally. Queue major construction projects for Summer.
-
Build roofed structures early. A single Dwelling shelters all elves within 2 tiles from rain (-1 mood) and cold (-2 mood). Place it centrally.
-
Fog is a gift for musicians. If you have a high-skill composer (Music > 5), fog gives them +1 mood and +1 Nature inspiration. Don't pull them indoors during fog.
-
Snow is surprisingly pleasant. The +2 beauty mood bonus outweighs any cold penalty for elves near shelter. Let your elves enjoy it, but make sure they have a warm building nearby.
-
Watch season transitions. Weather always rerolls at a season boundary. The jump from Autumn to Winter can bring sudden Snow (25% weight) and cold temperatures. Prepare your settlement layout accordingly.
-
Spring clearing is dangerous. Clearing forest in Spring costs +7 spirit anger instead of +5. If you need to expand, wait until Summer when the spirit is less reactive.
-
Night is the coldest period. At -8 from base temperature, Night in Winter can push temperatures down to single digits. Ensure resting elves are near Dwellings.
-
Spring is the best season for morale. The +2 "Spring Awakening" modifier offsets rain penalties and helps recovering Discontented elves. The "First Spring" transition bonus (+3 for 50 ticks) adds an extra boost on the first day of the year. Time revel performances for Spring to maximize audience mood.
-
Autumn and Winter stack mood drags. "Autumn Melancholy" (-1) on top of cold weather (-2) and rain (-1) can push morale down quickly. Plan building shelter completion before Autumn arrives. But remember: Autumn Melancholy feeds the deepest art.
-
Don't miss anchor events. If no revel is held during the anchor window (days 10--14 of each season), every elf gets "Missed [Anchor Name]" (-2, 150 ticks). Even a Standard revel satisfies the watchdog. See Seasonal Anchor Events below.
Hydrology
Water flows through your settlement as a living system -- springs feed streams, rain fills valleys, and ice locks the surface in winter. The hydrology layer connects climate, geology, flora, and fauna into a single ecological web.
Overview
Every tile on the map has a water state tracking depth (0--255), flow direction, ice thickness, and water source. Five systems update water at every day boundary: precipitation, spring production, flow, evaporation, and freeze/thaw. Water depth affects walkability, beauty, soil moisture, and fauna behavior.
How It Works
Water Depth Thresholds
| Threshold | Depth | Effect |
|---|---|---|
| Dry | 0 | No water. Normal terrain. |
| Subsurface | 1--30 | Wet but invisible. Contributes to soil moisture. |
| Visible | 31--80 | Rendered as shallow water. +1 beauty. Wading speed (2x movement cost). |
| Deep | 81--200 | Impassable unless frozen. +2 beauty. |
| Very Deep | 201--255 | Deep pool. +3 beauty. |
Source: crates/er-sim/src/sim/hydrology.rs, DEPTH_VISIBLE, DEPTH_DEEP, DEPTH_VERY_DEEP.
Precipitation
Rain, Storm, and Snow weather add water to tiles. The amount is modulated by elevation (orographic precipitation):
- High elevation (>= 150): 2x base rainfall -- peaks catch moisture from rising air.
- Low elevation (<= 100): 0.4x base rainfall -- rain shadow behind peaks.
- Mid elevation (100--150): linear interpolation between 0.4x and 2.0x.
| Weather | Base Amount | High Elev | Mid Elev | Low Elev |
|---|---|---|---|---|
| Rain | 3 | 6 | ~4 | 1 |
| Storm | 8 | 16 | ~10 | 3 |
| Snow | 1 (half rain) | 2 | ~1 | 0 |
Snow on already-frozen tiles accumulates at half rate.
Source: crates/er-sim/src/sim/systems/hydrology_systems.rs, precipitation_system. Config: data/hydrology.ron.
Springs and River Heads
Water sources produce water continuously at day boundary:
| Source | Production/Day |
|---|---|
| Spring | Per-spring flow_rate (default 5) |
| River Head | 20 (configurable) |
Sources resist evaporation -- they don't dry out.
Source: crates/er-sim/src/sim/systems/hydrology_systems.rs, spring_production_system.
Water Flow
Water moves downhill each day. The system finds the lowest neighbor (by elevation + water depth) and transfers water at the configured flow rate (default 4 depth/day). Frozen water does not flow.
Flow direction is stored per tile and rendered as stream direction indicators.
Source: crates/er-sim/src/sim/systems/hydrology_systems.rs, water_flow_system.
Evaporation
Water slowly disappears from non-source, non-frozen tiles:
| Season | Evaporation Rate |
|---|---|
| Summer | 3/day (base 1 + summer bonus 2) |
| Other seasons | 1/day |
When a tile dries completely (depth reaches 0), its flow direction is cleared.
Source: crates/er-sim/src/sim/systems/hydrology_systems.rs, evaporation_system.
Freeze/Thaw
Water freezing is depth-aware -- shallow water freezes faster than deep water:
| Water Depth | Ice Growth/Day (below 20 temp) |
|---|---|
| Shallow (< 81) | 15 ice/day |
| Deep (>= 81) | 3 ice/day |
Thawing occurs above 30 temperature at 10 ice/day. Between 20--30 temperature, nothing changes (hysteresis prevents oscillation).
Frozen water is passable with no movement cost -- rivers become ice bridges in winter. Frozen visible water has +2 beauty (scenic frozen landscape).
Source: crates/er-sim/src/sim/systems/hydrology_systems.rs, freeze_thaw_system. Config: data/hydrology.ron.
Soil Moisture
Water feeds soil moisture, which gates flora growth:
- Surface absorption: water on a tile is absorbed into soil moisture at per-soil-kind rates (Sand 4, Loam 2, Clay/Peat/Rocky 0).
- Adjacent boost: tiles next to visible water gain +3 moisture/day.
- Evaporation: soil moisture drains at 1/day (+1 in summer). Clay and Peat resist (halved rate).
- Retention cap: soil moisture cannot exceed the soil kind's retention value (Peat 90, Clay 80, Loam 50, Sand 20, Rocky 10).
Flora growth stops when soil moisture reaches 0 (drought dormancy). Seed germination requires moisture >= 10.
Source: crates/er-sim/src/sim/systems/hydrology_systems.rs, soil_moisture_system.
Interactions
- Seasons & Weather -- weather type determines daily precipitation; temperature drives freeze/thaw and evaporation rates.
- Flora -- soil moisture gates flora growth and spread. Drought stalls expansion but doesn't kill existing plants.
- Fauna -- water sources attract animals. Drought concentrates fauna at remaining water.
- Forest Spirit -- deforestation increases erosion on bare soil, which reduces fertility, which reduces flora, which reduces soil moisture retention.
Tips
-
Valleys flood, peaks dry out. Low-elevation tiles accumulate water from orographic rainfall. Place settlements on mid-elevation terrain for balanced water access without flooding.
-
Build near springs. Spring tiles continuously produce water, keeping adjacent soil moist and supporting flora growth year-round.
-
Watch winter rivers. When deep water freezes, it becomes a free walkway. Elves will path across frozen rivers, but when spring thaw comes, that path becomes impassable again.
-
Summer dries shallow pools. Evaporation triples in summer (3/day vs 1/day). Shallow water features may disappear by mid-summer. Deep pools and spring-fed streams persist.
-
Sand drains fast. Sandy soil absorbs water quickly (rate 4) but has the lowest retention cap (20). Flora on sand needs constant water proximity. Clay holds moisture (cap 80) but absorbs slowly.
Fauna
Wildlife roams the world as a living ecosystem. Deer herds cross meadows, foxes stalk the forest edge, songbirds migrate with the seasons, and individual animals near your settlement can become companions. The fauna system operates on three layers: regional population pools, visible roaming herds, and settlement individuals that bond with elves.
Overview
At world generation, 7 procedural species are created -- each with a unique name (e.g., "Ember Stag", "Copper Fox", "Sun Thrush") and ecological parameters. Every geological region maintains population counts for each species. When a region's population grows large enough, herds manifest as visible entities on the map. Animals that wander near buildings become individuals that elves can observe and bond with.
Species
The Food Web
| Archetype | Trophic Level | Size | Social | Seasonal | Temperament |
|---|---|---|---|---|---|
| Deer | Herbivore | Medium | Herd (5--10) | Resident | Tolerant |
| Rabbit | Herbivore | Small | Herd (5--14) | Resident | Shy |
| Fox | Predator | Medium | Pair (2--4) | Resident | Curious |
| Wolf | Predator | Large | Pack (5--7) | Resident | Shy |
| Songbird | Pollinator | Tiny | Flock (5--20) | Migrant | Tolerant |
| Bee | Pollinator | Tiny | Swarm (5--20) | Hibernator | Tolerant |
| Crow | Scavenger | Small | Pair (2--4) | Resident | Curious |
Source: crates/er-sim/src/sim/fauna_gen.rs, ARCHETYPES.
Trophic Cascade
The food web creates a chain of dependencies:
Flora density -> Herbivore capacity -> Predator capacity
- Herbivore carrying capacity scales with flora count in the region (up to 2x base).
- Predator carrying capacity scales with herbivore population (0.3x--2.0x base).
- Predators consume up to 15% of herbivores per day.
- Deforestation reduces flora, which reduces herbivore food, which reduces predator numbers -- a visible cascade.
Seasonal Behavior
| Pattern | Spring | Summer | Autumn | Winter |
|---|---|---|---|---|
| Resident | Breeding (1.5x births) | Normal | Reduced breeding (0.6x) | Minimal breeding (0.2x) |
| Migrant | Return | Normal | Normal | 90% depart |
| Hibernator | Normal | Normal | Normal | Dormant (no births/deaths) |
Migrant species (Songbird) mostly leave in winter -- their population drops to ~10% of normal. When they return in spring, their numbers recover through high birth rates.
Source: crates/er-sim/src/sim/fauna_systems.rs, fauna_pool_system.
The Three Layers
Layer 1: Regional Population Pools
Each geological region tracks population counts per species. Lotka-Volterra dynamics run at day boundary:
- Logistic growth: births scale with distance from carrying capacity.
- Predation: predators reduce herbivore populations proportionally.
- Seasonal breeding: spring is boom time (1.5x), winter is lean (0.2x).
- Fractional accumulation: sub-unit daily growth (e.g., 0.3 births/day) is tracked across days to prevent small populations from being stuck.
Pools seed at 40% of base capacity at world generation and recover naturally if depleted.
Layer 2: Roaming Herds
When a species' regional population exceeds 130% of base carrying capacity, herds manifest as ECS entities on the map:
- Herds spawn near the region center with slight position jitter.
- Maximum 2 herds per species per region.
- Herds alternate between Grazing (stationary) and Migrating (walking to another region).
- Migration uses A* pathfinding -- herds follow valid terrain paths, avoiding impassable tiles.
- Movement updates every 10 ticks for smooth visible motion.
- When a herd reaches its destination, it starts grazing again.
- Herds dissolve back into the regional pool when their count drops below the species minimum (Solitary: 1, Pair: 2, Herd: 5).
Source: crates/er-sim/src/sim/fauna_systems.rs, herd_spawn_system, herd_move_system, herd_dissolve_system.
Layer 3: Settlement Individuals
Animals within 8 tiles of any building are "individuated" from their regional pool -- they become named ECS entities that persist near the settlement:
- 15% daily chance of a new individual appearing near a building (drawn from the regional pool).
- Maximum 4 individuals per species to keep entity counts bounded.
- Shy species (Rabbit, Wolf) rarely individuate (10% of normal rate).
- Individuals that wander more than 16 tiles from any building are dissolved back into the pool.
- Bonded animals (with an elf companion) are never dissolved regardless of distance.
Source: crates/er-sim/src/sim/fauna_systems.rs, individuation_system.
Animal Bonding
How Bonds Form
Elves within 2 tiles of a bondable individual animal build a bond over time:
- The elf must be near the animal during a bonding tick (every 50 ticks).
- Bond growth depends on the elf's Stewardship skill: base 1 + (stewardship / 3), clamped to 1--5 per tick.
- Curious species (Fox, Crow) can be bonded by any elf.
- Tolerant species (Deer, Songbird, Bee) require Stewardship >= 3.
- Shy species (Rabbit, Wolf) cannot be bonded.
- Only one elf can bond with a given animal -- the first to reach threshold wins.
Bond Strength
| Range | Status | Effect |
|---|---|---|
| 0--19 | Wary | Animal tolerates proximity |
| 20--59 | Familiar | Animal follows loosely |
| 60--100 | Companion | Animal is named, follows elf, full bond |
At strength 60, the animal becomes a companion:
- It receives an automatic name (e.g., "Copper Fox Friend").
- The bonding elf receives +20 Stewardship XP.
Bond Decay
Bonds decay when elf and animal are apart:
- After 5 days without interaction, bond strength decreases by 1/day.
- If bond drops to 0, the animal is no longer bonded and may be dissolved.
Source: crates/er-sim/src/sim/fauna_systems.rs, animal_bond_system, animal_bond_decay_system.
Observation and Inspiration
Elves near animals gain Nature inspiration:
- Checked every 50 ticks.
- Picks the most inspiring nearby species (not cumulative across animals).
- Grants +1 Nature inspiration, soft-capped at 40 -- animals flavor inspiration rather than dominate it.
- Observation radius: 3 tiles (Manhattan distance).
Species inspiration values:
| Species | Inspiration |
|---|---|
| Wolf | 4 |
| Songbird | 4 |
| Deer | 3 |
| Fox | 3 |
| Bee | 2 |
| Crow | 2 |
| Rabbit | 1 |
Source: crates/er-sim/src/sim/fauna_systems.rs, animal_observation_system.
Interactions
- Seasons & Weather -- seasonal breeding rates drive population dynamics. Migrants depart in winter. Hibernators go dormant.
- Hydrology -- drought concentrates fauna at water sources. Frozen rivers change herd movement paths.
- Forest Spirit -- deforestation reduces flora, which reduces herbivore carrying capacity, which reduces predator populations.
- Skills -- Stewardship skill gates bonding speed and species eligibility.
- Inspiration -- animal observation contributes to Nature inspiration (soft-capped at 40).
Tips
-
Build near forests for deer. Forest-habitat species (Deer, Fox, Wolf) prefer forested regions. Settlement buildings on the forest edge attract individuated Deer for observation and bonding.
-
Songbirds vanish in winter. Migrant Songbirds drop to 10% population in winter, taking their +4 inspiration bonus with them. Enjoy them in spring and summer.
-
Level Stewardship to 3 early. Curious species (Fox, Crow) bond easily but most forest animals are Tolerant, requiring Stewardship 3+. Assign an elf to spend time near animals.
-
Predators keep the ecosystem balanced. Wolf and Fox populations control herbivore numbers, preventing overgrazing that would damage flora and soil. Resist the urge to "protect" prey -- healthy predator populations mean a healthy forest.
-
Watch for herds crossing the map. When you see a herd entity moving between regions, that's the ecology in action -- populations responding to carrying capacity. A region with many herds is thriving.
-
Companions follow their elf. A bonded companion at strength 60+ follows its elf, providing a persistent +1 Nature inspiration within observation range. This is worth the Stewardship investment.
-
Don't neglect bonds. After 5 idle days, bond strength decays. Keep bonded elves near their companions or the bond will weaken.
Flora
Trees and plants are the living landscape of your settlement. They grow through stages, develop beauty that inspires elves, and the oldest among them can become notable landmarks -- named trees with histories and the power to form deep bonds with individual elves. The flora system generates 19 procedural species across 8 families at world creation, each with unique aesthetic properties that interact with your elves' cultural values.
Overview
Flora operates on three layers that connect the natural world to elf culture:
- Notable flora -- trees that earn names and histories through age, hardship, or community significance. Notable trees become landmarks that elves bond with (see Tree Bonds).
- Species preference -- elves develop affinities for specific tree species through exposure, which amplifies their nature inspiration when near preferred species.
- Flora beauty -- each tree contributes beauty to its surroundings based on species, season, time of day, and how well the tree's aesthetic aligns with a nearby elf's cultural values.
Notable Flora
How Promotion Works
A tree becomes notable when it meets any one of these criteria:
| Criterion | Details |
|---|---|
| Ancient growth stage | The tree has reached the Ancient stage through natural growth |
| Drought survival | The tree's moisture reached 0 while at Mature stage or older |
| Beloved gathering place | 3 or more elves have a Favorite Place on the tree's tile |
When a tree is promoted, it receives a unique name and begins tracking its history. A TreeBecameNotable event appears in the event log with the tree's name and the reason for promotion.
Source: crates/er-sim/src/sim/flora_systems.rs, flora_notable_system.
Names and History
Notable trees are named in the format "The [Adjective] [Species] of [Zone]" -- for example, "The Whispering Silverbell of the Eastern Glade." The adjective is drawn from a pool of 10:
Great, Elder, Whispering, Ancient, Silver, Twilight, Starlit, Hollow, Sentinel, Mossy
Each notable tree maintains a TreeHistory that records significant events in its life:
| Event | Trigger |
|---|---|
| BecameNotable | The moment the tree was promoted |
| BondFormed | An elf formed a tree bond with this tree |
| SurvivedDrought | The tree survived a period of zero moisture |
| ElfEventNearby | A significant elf event occurred near the tree |
Source: crates/er-sim/src/sim/flora.rs, TreeHistory, TreeEvent.
Species Preference
Exposure and Affinity
Elves develop affinities for tree species through proximity. The species preference system runs every 50 ticks and checks a 3-tile Manhattan radius around each elf:
- Base exposure: 50 ticks accumulated per cycle for each nearby species.
- Aesthetic alignment bonus: If a species' aesthetic emphasis matches the elf's dominant aesthetic axis, exposure is doubled (100 ticks per cycle).
- Preference threshold: A species becomes preferred after 200 ticks of accumulated exposure.
- Maximum preferences: Each elf can prefer at most 2 species simultaneously.
Nature Inspiration
When an elf is near a preferred species, they receive +1 nature inspiration (soft-capped at 100). This stacks with other inspiration sources and creates a feedback loop: elves who spend time near trees develop preferences, which then make those trees more inspiring, which draws the elf back.
Source: crates/er-sim/src/sim/flora_systems.rs, species_preference_system.
Flora Beauty
Calculation
Each tree's beauty contribution is computed from four components:
| Component | Range | Details |
|---|---|---|
| Base beauty | 0--4 | Inherent to the species, varies by composition role |
| Seasonal modifier | -1 to +2 | Depends on the tree's current seasonal phase |
| Time-of-day bonus | 0 or +1 | +1 if current time matches the species' peak beauty phase |
| Aesthetic alignment | -2 to +5 | How well the elf's aesthetic values align with the species |
Total range: -2 to +12.
Base beauty by composition role:
| Composition Role | Beauty Range | Families |
|---|---|---|
| Dominant | 2--4 | Heartwood, Ironbark, Windcatcher |
| Companion | 1--3 | Silverleaf, Needlespire, Weepwater, Thornweave |
| Ground | 0--2 | Mosswhisper |
Seasonal modifiers:
| Phase | Modifier | Notes |
|---|---|---|
| Full | 0 | Standard foliage |
| Budding | -1 | Spring emergence |
| Fruiting | +1 | Fruit/seed display |
| Shedding | +2 | Autumn color bonus |
| Bare | -1 | Leafless |
| Dormant | -1 | Winter state |
Aesthetic alignment scales with the species' base beauty. When an elf's dominant aesthetic axis aligns with the species' emphasis, the bonus is beauty + 1 (ranging from +1 to +5). A neutral elf receives no modifier. When the elf's aesthetics actively oppose the species' emphasis, the penalty is -(beauty / 2) clamped to at least -1 (ranging from -1 to -2).
This means a Heartwood tree (structure + tradition emphasis) looks significantly more beautiful to a structured, traditional elf than to an elf who values emotion and social freedom -- and vice versa for Windcatcher trees.
Source: crates/er-sim/src/sim/flora_systems.rs, flora_beauty_contribution; crates/er-sim/src/sim/flora_gen.rs, generate_aesthetic_lookup.
Flora Species
At world generation, 19 species are created across 8 families. Each family has a distinct growth form, leaf type, and aesthetic emphasis that determines how its species interact with elf culture.
| Family | Count | Growth | Leaf Type | Aesthetic Emphasis | Notes |
|---|---|---|---|---|---|
| Heartwood | 3 | Tree | Broadleaf Deciduous | Structure + Tradition | Canopy shade, forageable |
| Ironbark | 3 | Tree | Broadleaf Evergreen | Structure (highest) | Canopy shade |
| Windcatcher | 3 | Tree | Broadleaf Deciduous | Emotion + Social | Forageable |
| Silverleaf | 2 | Tree | Broadleaf Deciduous | Emotion (personal) | — |
| Needlespire | 2 | Tree | Needle | Structure + Tradition | Canopy shade |
| Weepwater | 2 | Tree | Broadleaf Deciduous | Emotion (highest) | Forageable |
| Thornweave | 2 | Shrub | Broadleaf Deciduous | Social (communal) | Forageable |
| Mosswhisper | 2 | Shrub | Broadleaf Evergreen | Tradition + Emotion | — |
Within each family, individual species vary in soil preference, growth rate, beauty, and seasonal behavior. Species names are procedurally generated from family-specific adjective and core word pools (e.g., a Heartwood species might be "Silver Oak" or "Amber Maple").
Source: crates/er-sim/src/sim/flora_gen.rs, FAMILIES, generate_species_database.
Interactions
- Tree Bonds -- notable trees form bonds with nearby elves, creating deep social connections to the landscape.
- Seasons & Weather -- seasonal phase affects flora beauty modifiers. Shedding (autumn) is the most beautiful season; bare and dormant trees lose appeal.
- Inspiration -- flora beauty and species preference both contribute to nature inspiration, which feeds into compositions.
- Favorite Places -- when 3+ elves designate the same tile as a favorite place, any tree there becomes a notable flora promotion candidate.
- Hydrology -- drought conditions (zero moisture) can trigger notable promotion for mature trees that survive.
Tips
-
Autumn is art season. The Shedding phase gives +2 beauty to all deciduous trees. Combined with aesthetic alignment, a traditional elf near a Heartwood tree in autumn can receive +11 beauty contribution. Schedule revels accordingly.
-
Match elves to trees. An elf's dominant aesthetic axis determines which species look beautiful to them. Structured elves thrive near Heartwood and Ironbark; emotional elves near Weepwater and Silverleaf. Check the Aesthetic Position panel to find good matches.
-
Species preference rewards loyalty. An elf near a preferred species gets +1 nature inspiration per check. Two preferred species cover more ground. Since aesthetic alignment doubles exposure gain, elves naturally prefer species that match their values -- but diverse exposure can build unexpected affinities.
-
Notable trees are landmarks. Once a tree becomes notable, it can form bonds with elves. Trees promoted through community significance (3+ favorite places) tend to be near the settlement center, making them natural gathering points. Trees promoted through drought survival may be in harsh locations but carry stories of resilience.
Forest Spirit
The forest spirit is the ancient consciousness of the woodland that surrounds your settlement. As you clear trees and expand, its anger rises. If left unchecked, the spirit's displeasure manifests as alerts, mood effects, and a growing threat to your colony's harmony.
Overview
The forest spirit is tracked by a single meter value from 0 to 100. The meter determines the spirit's state, which escalates through four tiers as anger accumulates. Clearing forest tiles raises the meter; natural decay at dawn and Garden buildings lower it. The challenge is to balance expansion (which requires clearing) with the spirit's tolerance.
How It Works
Spirit States
The spirit's behavior is determined by its meter value:
| State | Meter Range | Description |
|---|---|---|
| Harmony | 0--20 | Peaceful coexistence. No negative effects. |
| Tension | 21--50 | The forest stirs. Alert appears in the UI. |
| Displeasure | 51--75 | Active resistance. The settlement feels the pressure. |
| Anger | 76--100 | The forest is hostile. Significant penalties. |
The meter is clamped to 0--100 using saturating arithmetic -- it cannot go below 0 or above 100.
Source: src/sim/world.rs, ForestSpirit::state -- match on meter ranges. ForestSpirit::add and sub use saturating ops with min(100).
Anger Triggers
The only action that raises the spirit meter is clearing forest tiles. When an elf completes a ClearForest task, changing a YoungForest tile to Meadow:
| Season | Anger Increase Per Tile |
|---|---|
| Spring | +7 |
| All other seasons | +5 |
Spring incurs a heavier penalty because the forest is awakening -- clearing during this time is seen as desecration. There is no penalty for simply walking through or gathering from forest tiles.
Each cleared tile also produces 3 Wood and grants 10 Building XP to the elf who cleared it.
Source: crates/er-sim/src/sim/systems/forest.rs, clear_forest_system -- spirit.add(if season == Season::Spring { 7 } else { 5 }).
Calming Mechanics
The spirit meter decays through two mechanisms, both applied at dawn (once per day, when tick is a multiple of the day length):
Natural Decay
The spirit naturally calms by -1 per dawn.
The doc-comments in the source describe seasonal variation (Spring: -2, Summer/Autumn: -1, Winter: 0 dormant), but the current implementation applies a flat -1 regardless of season.
Source: crates/er-sim/src/sim/systems/forest.rs, forest_spirit_system -- spirit.sub(1).
Garden Calming
Each Garden building reduces the spirit meter by -2 per dawn. This stacks with natural decay and with multiple gardens.
| Gardens | Total Dawn Decay |
|---|---|
| 0 | -1 (natural only) |
| 1 | -3 (-1 natural, -2 garden) |
| 2 | -5 (-1 natural, -4 gardens) |
| 3 | -7 (-1 natural, -6 gardens) |
Gardens work regardless of season -- even when natural decay is minimal, gardens still calm the spirit. This makes them the primary tool for spirit management.
Source: crates/er-sim/src/sim/systems/forest.rs, forest_spirit_system -- spirit.sub((garden_count as u8) * 2). Confirmed by test: "Gardens should reduce spirit faster" asserts 20 - 5 = 15 with 2 gardens.
Rate of Change Examples
Clearing without gardens (non-Spring): Each cleared tile adds +5. Dawn decay removes -1. Net per day if you clear one tile: +4.
Clearing with 2 gardens (non-Spring): Each cleared tile adds +5. Dawn decay removes -5 (-1 natural, -4 gardens). Net per day if you clear one tile: +0 -- perfectly balanced.
No clearing with 2 gardens: Dawn decay removes -5 per day. A meter at 50 reaches 0 in 10 days.
Spring clearing without gardens: Each cleared tile adds +7. Dawn decay removes -1. Net per day if you clear one tile: +6. The meter rises quickly.
Values & Formulas
Spirit Meter Math
| Action | Effect | When |
|---|---|---|
| Clear YoungForest tile (Spring) | +7 | On task completion |
| Clear YoungForest tile (other seasons) | +5 | On task completion |
| Natural dawn decay | -1 | Every dawn |
| Per Garden at dawn | -2 | Every dawn |
| Meter minimum | 0 | Saturating subtraction |
| Meter maximum | 100 | Capped at 100 |
Days to Reach Key Thresholds (No Gardens)
Starting from Harmony (meter 0), clearing one tile per day:
| Target State | Meter Required | Days (non-Spring) | Days (Spring) |
|---|---|---|---|
| Tension | 21 | 6 days (6 clears x net +4 = 24) | 4 days (4 x net +6 = 24) |
| Displeasure | 51 | 13 days | 9 days |
| Anger | 76 | 19 days | 13 days |
Days to Calm from Key States (No Clearing)
With various garden counts, days to return from Anger (meter 76) to Harmony (meter 20):
| Gardens | Dawn Decay | Days to Drop 56 Points |
|---|---|---|
| 0 | -1/day | 56 days |
| 1 | -3/day | 19 days |
| 2 | -5/day | 12 days |
| 3 | -7/day | 8 days |
Clearing Progress Rate
The ClearForest task progresses at a rate of 5 + building_skill per tick, completing at 100:
| Building Skill | Progress/Tick | Ticks to Complete |
|---|---|---|
| 1 | 6 | 17 ticks |
| 3 | 8 | 13 ticks |
| 5 | 10 | 10 ticks |
| 10 | 15 | 7 ticks |
Higher-skill builders clear faster, which means they can also anger the spirit faster.
Source: crates/er-sim/src/sim/systems/forest.rs, clear_forest_system -- rate = (5 + skill).max(1), completes at progress >= 100.
Interactions
Seasons
The spirit meter interacts with Seasons & Weather in two ways:
- Spring penalty: Clearing in Spring costs +7 instead of +5. Plan heavy expansion for Summer or Autumn.
- Dawn timing: Spirit decay happens once per dawn. Since a day is 100 ticks and dawn is ticks 0--9, the decay fires at the start of each new day.
Buildings
Gardens are the primary spirit management tool at -2/dawn each. Other buildings have no direct spirit interaction, but expanding your settlement by clearing forest to make room for buildings is what drives the meter up in the first place.
Mood and Satisfaction
The spirit meter currently produces UI alerts when above Harmony but does not directly apply mood modifiers. However, the consequences of reaching high anger states constrain your expansion, which can indirectly affect elf satisfaction by limiting available resources and building space.
Cultural Advisor
The Curator monitors the spirit state and includes it in settlement alerts when above Harmony. The Dummy Curator factors spirit state into its clearing decisions, avoiding forest clearing when the spirit is already elevated.
Terrain
Only YoungForest tiles can be cleared (converted to Meadow). AncientForest tiles cannot be cleared. This means the spirit meter has a natural ceiling based on the number of YoungForest tiles on the map.
Source: crates/er-sim/src/sim/systems/forest.rs, clear_forest_system -- checks terrain == Terrain::YoungForest.
Tips
-
Build 2 Gardens early. Two Gardens provide -4/dawn on top of the -1 natural decay, for a total of -5/dawn. This exactly offsets clearing one YoungForest tile per day in non-Spring seasons, keeping the meter stable.
-
Avoid clearing in Spring. The +7 per tile (vs. +5) and the fact that Spring is when the forest is most sensitive makes it the worst season for expansion. Use Spring for composing, socializing, and stockpiling.
-
The 20 → 21 boundary matters. Crossing from Harmony to Tension triggers a persistent alert in the UI. If the spirit reaches Tension, stop clearing and let gardens work it back down before resuming.
-
Plan clearing bursts with recovery periods. If you need to clear 5 tiles quickly, do it in a batch during Summer (+25 meter), then pause clearing for several days. With 2 Gardens, the meter drops at -5/day.
-
Track the cleared tile count. The game tracks total tiles cleared. More clearing means less forest for foraging and beauty effects. Clear strategically -- only where you need building space.
-
Gardens are dual-purpose. They calm the spirit (-2/dawn) AND provide +3 beauty/tick to nearby elves AND boost the beauty need. Build them at the edges of your settlement, facing the forest.
-
You cannot anger the spirit by gathering. Foraging, gathering wood from resource nodes, and walking through forest tiles are all safe. Only the ClearForest task (which converts tiles) provokes the spirit.
-
AncientForest is safe. You physically cannot clear AncientForest tiles. They provide higher beauty values (+2 vs. +1 for YoungForest) and a 30% chance of FineWood resource nodes. Expand into YoungForest areas instead.
Zones
As you place buildings in your settlement, the game automatically detects zones -- named clusters of buildings with a classification based on their composition. Zones give your settlement structure and identity, helping you and the elves understand the layout at a glance.
Overview
The zone detection system scans all placed buildings and groups them into proximity-based clusters. Each cluster with 2 or more buildings becomes a named zone with a classification (Art Quarter, Residential, Commons, or Mixed). Zones are recalculated periodically and displayed in the UI.
Zone detection is passive -- you do not place zones manually. Instead, zones emerge organically from your building placement decisions. Place a Workshop and a Garden near each other and an Art Quarter appears. Cluster Dwellings together and a Residential district forms.
How It Works
Detection Algorithm
The zone detection system uses proximity clustering with a Manhattan-distance radius of 4 tiles:
- Clear existing zones -- zones are recalculated from scratch each time.
- Collect all buildings -- every entity with a Position and Building component.
- Cluster by proximity -- for each building, check if it falls within Manhattan distance 4 of the center of an existing cluster. If so, add it to that cluster. If not, start a new cluster.
- Classify clusters -- only clusters with 2 or more buildings become zones. Single buildings are ignored.
- Name zones -- auto-generated from terrain and classification, or overridden by a nearby patron-named location.
The cluster center is computed as the arithmetic mean of all building positions in the cluster. Buildings are processed in order, and each building joins the first cluster whose center is within range (greedy assignment).
Source: crates/er-sim/src/sim/systems/zones.rs, zone_detection_system -- cluster_radius = 4i32, center computed as sum/count.
Zone Types
Zones are classified based on which building types are present in the cluster:
| Zone Type | Classification Rule | Typical Use |
|---|---|---|
| Commons | Contains a Feast Hall | Social gathering area |
| Art Quarter | Contains both a Workshop AND a Garden | Creative district |
| Residential | Contains 2+ Dwellings (no Feast Hall, not Workshop+Garden) | Living quarters |
| Mixed | Everything else with 2+ buildings | General-purpose area |
The rules are evaluated in priority order:
- If the cluster contains a Feast Hall --> Commons (regardless of other buildings).
- Else if the cluster contains both a Workshop and a Garden --> Art Quarter.
- Else if the cluster contains 2 or more Dwellings --> Residential.
- Otherwise --> Mixed.
This means a Feast Hall always dominates the classification. A cluster with a Feast Hall, 3 Dwellings, and a Workshop is still classified as Commons.
Source: crates/er-sim/src/sim/systems/zones.rs, zone_detection_system -- classification logic with has_feast_hall, has_workshop && has_garden, dwelling_count >= 2.
Building Types Reference
The four building types that participate in zone detection:
| Building | Zone Influence | Other Effects |
|---|---|---|
| Dwelling | Residential (if 2+) | Shelter from weather. Faster rest recovery. |
| Workshop | Art Quarter (with Garden) | Composing station. Stimulation boost. |
| Garden | Art Quarter (with Workshop) | Beauty +3/tick. Spirit calming -2/dawn. |
| Feast Hall | Commons (always) | Revel venue. Social gathering point. |
Zone Naming
Each zone receives an auto-generated name based on two components:
Terrain prefix (based on the terrain at the zone center):
| Terrain at Center | Prefix |
|---|---|
| AncientForest | "The Groveward" |
| YoungForest | "The Verdant" |
| Meadow | "The Sunlit" |
| Stone | "The Hearthstone" |
| Other | "The Central" |
Classification suffix:
| Zone Type | Suffix |
|---|---|
| Art Quarter | "Atelier" |
| Residential | "Rest" |
| Commons | "Commons" |
| Mixed | "Quarter" |
Combined examples:
- A Workshop + Garden on a Meadow tile: "The Sunlit Atelier"
- Two Dwellings near an AncientForest tile: "The Groveward Rest"
- A Feast Hall on a Stone tile: "The Hearthstone Commons"
Patron name override: If the player has placed a Named Location (via the patron naming system) within Manhattan distance 4 of the zone center, that name overrides the auto-generated name entirely. This lets you personalize your settlement districts.
Source: crates/er-sim/src/sim/systems/zones.rs, zone_detection_system -- terrain prefix match, kind suffix match, named_locations override.
Zone Properties
Each detected zone stores the following data:
| Field | Type | Description |
|---|---|---|
| center | Position | Arithmetic mean of all building positions in the cluster |
| radius | u8 | Always 4 (the cluster detection radius) |
| kind | ZoneKind | Art Quarter, Residential, Commons, or Mixed |
| name | String | Auto-generated or patron-overridden name |
| building_count | u32 | Number of buildings in the zone |
Source: src/sim/components.rs, Zone struct.
Values & Formulas
Clustering Math
Two buildings are in the same cluster if:
|building.x - center.x| + |building.y - center.y| <= 4
Where center is the running mean of all buildings already in the cluster. Buildings are assigned greedily -- the first cluster within range absorbs the building. Order of processing can affect which cluster a building joins if it is equidistant from two clusters.
Minimum Cluster Size
A cluster needs >= 2 buildings to become a zone. A single isolated building produces no zone. This means you need at least 2 buildings within Manhattan distance 4 of each other to see any zone appear.
Zone Classification Decision Table
| Feast Hall? | Workshop + Garden? | 2+ Dwellings? | Result |
|---|---|---|---|
| Yes | (any) | (any) | Commons |
| No | Yes | (any) | Art Quarter |
| No | No | Yes | Residential |
| No | No | No | Mixed |
Example Layouts
Minimal Art Quarter: Place a Workshop and a Garden within 4 tiles of each other. No other buildings needed. Result: Art Quarter with building_count = 2.
Minimal Residential: Place 2 Dwellings within 4 tiles. Result: Residential with building_count = 2.
Upgraded Commons: Place a Feast Hall, 2 Dwellings, and a Garden all within a 4-tile cluster. Result: Commons (Feast Hall dominates), building_count = 4.
Split zones: Place a Workshop at (0,0) and a Garden at (10,0). Distance = 10 > 4, so they form separate clusters. Neither cluster has 2 buildings, so no zones are detected. Move them closer (within 4 tiles) to form an Art Quarter.
Interactions
Revels
The Revel system uses zones to determine performance context. Revels happen at the Feast Hall, which is typically the center of a Commons zone. The zone context can influence the flavor of revel events.
Composition
Elves composing in an Art Quarter zone may receive contextual bonuses. The zone's presence near a Workshop affects which elves are drawn to compose there. See Compositions.
Terrain Effects
Zone classification is independent of terrain, but the auto-generated name depends on the terrain at the zone center. Building on different terrain types gives your zones different names and character. See Terrain.
Patron Naming
The patron taste system allows you to name locations on the map. If a named location falls within 4 tiles of a zone center, the zone adopts that name. This is how you personalize your settlement -- name a spot "Luthien's Garden" and any zone that forms near it takes that name.
Cultural Advisor
The Curator considers zones when making building placement decisions. It may prefer to expand existing zones (adding buildings near existing clusters) rather than starting new isolated structures.
Tips
-
Plan your layout with zones in mind. Place your first Workshop and Garden within 4 tiles of each other to create an Art Quarter early. This gives your composers a named creative district.
-
The Feast Hall defines your social center. Any cluster containing a Feast Hall becomes Commons, regardless of other buildings. Place the Feast Hall where you want your settlement's social hub.
-
Separate zones for different purposes. If you want both an Art Quarter and a Residential district, keep them more than 4 tiles apart. Otherwise they will merge into a single cluster and the classification will follow the priority rules.
-
Use patron naming for character. Auto-generated names like "The Sunlit Atelier" are pleasant, but naming a location yourself makes the settlement feel personal. Name a spot before or after buildings appear nearby.
-
Building density matters. A zone with 5 buildings is more robust than one with 2 -- if you remove one building, a 5-building zone survives, but a 2-building zone could drop below the minimum and disappear.
-
Watch for unintended merges. If you build a Dwelling at the edge of your Art Quarter cluster, it gets absorbed and the zone may reclassify. Keep residential buildings at least 5 tiles from your creative district if you want separate zones.
-
Mixed zones are a fallback. If a 2+ building cluster doesn't match any specific pattern, it becomes Mixed. This is fine early on, but you can upgrade a Mixed zone by adding the right building types (e.g., add a Garden to a Workshop cluster to get Art Quarter).
-
Zones are recalculated periodically. They are not permanent. Moving buildings (by demolishing and rebuilding) or adding new ones will change the zone layout on the next detection pass.
Engramica
Engramica is not a bestiary. It is a memory-map of the deeps: what was seen, what was inferred, what was changed by being observed.
The forest does not announce itself to you in summaries. It is observed, slowly, by the elves who live in it — one stag-sighting, one bloom, one fox-track at a time. The accumulated memory of those observations is Engramica: the colony's archive of what has been seen, and the slow inference of what those sightings mean.
What this is
When an elf moves through the world, they notice things. A fox crossing the meadow at dusk. The first willow buds breaking three days earlier than last spring. The herd that has, over four winters, abandoned the stream-bend and moved its gathering to the spring oak. Each of these noticings is an engram — a single record of what one elf saw, where, when, and through which sense.
Engrams accumulate quietly in each elf's private memory. Some elves keep them to themselves; others share them with friends, with apprentices, or with the whole colony. What gets shared is what enters the Engramica archive — the colony's collective recall of the forest.
A subset of elves — naturalists — make this their creative pursuit. They curate their engrams into readings, performances they bring to revels: not transcripts of what they saw, but inferences about what the forest is becoming. The audience reacts to these readings the way they react to compositions or dances. A reading can be the highlight of a revel; a reading can be ignored; a reading can be the beginning of a movement.
Telecology — the discipline
The practice of attending to the forest in this way is called telecology: the study of an ecology that appears to develop purposes. A naturalist practices telecology. The elf who first notices that the herd has changed its gathering-place, and who shares that engram with the colony, has practiced telecology even if they would not name it.
The shape of an engram
An engram records:
- Who observed it (the elf).
- What they observed (a species, an individual animal, a notable tree, an event).
- Where and when they observed it.
- How they observed it — by sight, by sound, by smell, or by trace (tracks, scat, broken branches).
- How clearly — confidence drops with distance, dim light, and the elf's own attentional state.
- Context — what else was true at that moment (the species was flowering, the herd was with young, the animal was fleeing, it was dawn).
Engrams are personal. An engram in your private memory is yours; the colony does not know it until you share it.
Sharing — a social act
Sharing an engram is a deliberate social act. A reclusive or distrustful elf may accumulate many engrams over a long life and never share one. A communal, generous elf may share daily. Apprentices may inherit engrams from their teachers as part of their training. Friends may share with friends in ways that never reach the broader colony.
This means the Engramica archive is not a database. It is a trace of the colony's social fabric. A colony with thin trust will have a thin archive even if many of its elves are observers. A colony with deep mutual openness will accumulate a rich one.
What you will see in the game
This page is a stub. The full feature lands across beads Revel-6f8 (observation), Revel-usp (sharing + archive), and Revel-hdf (naturalist readings). Detailed mechanics, formulas, and example readings will be documented as each bead lands.
For now: when the system is in place, expect to see ambient engram-minting events in inspection UI, deliberate sharing events in the cultural feed, and naturalist Reading contributions appearing alongside compositions and dances at revels.
See also
- Fauna — the species and individuals an engram may record.
- Flora — the bloom-timings and growth patterns that engrams accumulate around.
- Personality — what makes an elf likely or unlikely to share what they see.
- Revels — where naturalist readings are performed.
The Curator System
Overview
The curator is the AI layer that manages your settlement's cultural direction. It sits between you (the patron) and your elves, translating high-level artistic vision into concrete settlement decisions: who does what, what gets built, when to hold a revel.
Elf Revel uses a three-tier intentionality model:
- Patron (you) -- provides taste, values, and broad artistic direction via
ArtisticDirectionsettings and direct messages. - Curator (AI advisor) -- reads the settlement state and issues
CulturalCommands. Two implementations: a rule-basedDummyCurator(always available) and an LLM-backedLlmCurator(native only, usesclaudeCLI). - Elves (autonomous agents) -- execute tick-level behaviors: gathering, building, composing, attending revels. They have their own needs, aesthetics, and relationships.
The human has taste, the AI has hands, the elves have lives.
How It Works
The CulturalAdvisor Trait
Every curator implements the CulturalAdvisor trait, which defines three core methods:
| Method | Purpose |
|---|---|
consult(world) | Produce a list of CulturalCommands based on current game state |
consult_with_message(world, message) | Same, but with a player-typed message for context |
should_consult(world, events) | Decide whether the curator wants to run this tick |
Two additional methods handle forest-clearing tracking (on_forest_cleared) and UI display (last_reasoning).
When the Curator Is Consulted
The default cadence is once per game day (every 100 ticks). The should_consult method fires at day boundaries -- when tick / DAY_LENGTH changes.
The LlmCurator extends this with event-driven triggers. It will also consult when:
| Event | Condition |
|---|---|
RevelEnded | Always |
ArtCompleted | Always |
InspirationCrisis | Always |
ResourceLow | When amount < 5 |
SpiritStateChanged | When spirit enters "Anger" |
It also returns true immediately if a pending LLM result has arrived from the background thread.
The CulturalCommand Enum
When consulted, the curator returns a Vec<CulturalCommand>. Each variant maps to a concrete game action:
| Command | Fields | Effect |
|---|---|---|
AssignRole | name: String, role: ElfRole | Sets an elf's workshop assignment. Roles: Gatherer, Builder, Composer, Unassigned |
SetResourcePriority | Vec<ResourceType> | Reorders the settlement's gathering priority. Types: Food, Wood, Stone, FineWood |
QueueBuild | kind: BuildingType | Adds a building to the construction queue. Types: Dwelling, Garden, Workshop, FeastHall |
ClearForest | position: Position | Assigns an idle builder (or any idle elf) to clear a forest tile at the given coordinates |
ScheduleRevel | (none) | Transitions RevelState from None to Gathering with 5 ticks remaining |
Message | text: String | Adds curator commentary to the UI event log -- no gameplay effect |
Command Dispatch
Commands are applied via apply_commands(world, commands, curator). Each command:
- Mutates the
GameWorlddirectly (e.g., inserting intopolicies.workshop_assignments, pushing topolicies.build_queue). - Returns a
CulturalEventfor the UI log (e.g.,RoleAssigned,PriorityShifted,BuildQueued,ReadyForRevel,CuratorMessage).
For QueueBuild, the system first calls find_build_site to locate a suitable position:
- First choice: nearest meadow tile to the map center that is not occupied or already queued.
- Fallback: nearest
YoungForesttile, which gets cleared first (assigns an idle builder toClearForesttask). TheDummyCuratorlimits this to one clearing per consultation via itscleared_todayflag.
The search radius is (min(map_width, map_height) * 0.15).max(5.0) + 6 tiles from center.
The Curator Enum (Runtime Dispatch)
At runtime, the game holds a Curator enum that wraps either variant:
pub enum Curator {
Dummy(DummyCurator),
Llm(LlmCurator),
}
On WASM targets (browser build), LlmCurator is a thin stub that delegates to an inner DummyCurator, since the claude CLI is not available in the browser. The UI label shows "rules" for the dummy curator and the model name (e.g., "haiku") for the LLM curator.
State Snapshot Format
The curator never reads GameWorld directly for LLM consumption. Instead, SettlementState::from_world(world) produces a plain-data snapshot that the prompt layer serializes as readable text.
The snapshot includes:
| Section | Data |
|---|---|
| Header | Day, tick, season, day-in-season, year, weather, temperature |
| Resources | Name, current amount, daily rate -- for Food, Wood, Stone, FineWood |
| Elves | Name, role, task, skills (music/building/gathering), morale, satisfaction, aesthetic label, aesthetic distance from center, friend/rival names, portfolio count, aspirations, fandom, discontented/blocked flags |
| Aesthetic center | Settlement-wide average on four axes: structure, tradition, emotion, social |
| Compositions | Name, composer, genre, quality tier, mastery/originality/emotional scores, aesthetic position, properties, patron-favorite flag. Capped at 15 most recent |
| Revel state | Current phase (None / Gathering / Performing / Aftermath) plus history of last 5 revels with highlight composition and average score |
| Buildings | Count by type (Dwelling, Garden, Workshop, FeastHall) plus build queue size |
| Spirit | State label and meter value (0-100) |
| Current policies | Role assignments and resource priority order |
The prompt layer (prompt.rs) adds climate-aware alerts -- winter food warnings below 20, storm shelter alerts, discontented elf warnings, and spirit anger alerts -- before the main state sections.
Interactions
- Dummy Curator -- the rule-based fallback that handles bootstrap and steady-state decisions.
- LLM Curator -- the Claude-backed advisor with background threading and structured output.
- Needs & Mood -- morale values that appear in the state snapshot and inform curator decisions.
Tips
- The curator only runs at day boundaries by default. If your settlement is in crisis, the LLM curator reacts faster because it also triggers on critical events.
Messagecommands are free -- they have no gameplay cost. The LLM curator uses them to explain its reasoning in the event log.- The
QueueBuildcommand does not specify a location. The system automatically finds the best build site near the map center, clearing young forest if no meadow is available. - When the LLM curator's background thread dies or
claudeis not found, it permanently falls back to theDummyCuratorfor the rest of the session. - On WASM (browser), only the dummy curator is available. The LLM curator requires the native TUI build.
Dummy Curator (Rule-Based Fallback)
Overview
The DummyCurator is the settlement's rule-based cultural advisor. It runs on every platform (including WASM/browser), requires no external tools, and handles the essential decisions for a functioning settlement: role assignment, resource priorities, building construction, revel scheduling, and patron-directed composer promotion.
It also serves as the fallback for the LLM Curator -- whenever the LLM is unavailable, over budget, or between consultations, the dummy curator's logic runs instead.
How It Works
Consultation Cadence
The dummy curator runs once per game day, at day boundaries. A game day is 100 ticks (DAY_LENGTH = 100). The check is:
current_day = tick / 100
prev_day = (tick - 1) / 100
consult if current_day != prev_day
At the start of each consultation, the cleared_today flag resets to false, allowing one forest clearing per day.
Internal State
| Field | Type | Purpose |
|---|---|---|
bootstrapped | bool | Whether initial role assignment has run (once per game) |
clearings_since_garden | u32 | Forest tiles cleared since the last garden was built; triggers garden construction at 2 |
cleared_today | bool | Limits forest clearing to one per consultation (reset each day) |
Player Messages
When consult_with_message is called, the dummy curator runs its normal decision tree and appends a Message command acknowledging the input:
Understood, I'll consider your input: "..."
It does not actually modify its behavior based on the message -- that is an LLM Curator capability.
Decision Tree
Each consultation runs three phases in order: bootstrap roles, resource priority, and building queue (which also handles revel scheduling and composer promotion).
Phase 1: Bootstrap Roles
Runs once on the first consultation (bootstrapped == false). Assigns every elf a starting role based on gathering skill.
Algorithm:
- Query all elves with their
gatheringskill value. - Sort by gathering skill, descending (best gatherer first).
- Assign roles by index:
| Index | Role | Rationale |
|---|---|---|
| 0 (best gatherer) | Gatherer | Most efficient food/resource collector |
| 1-2 | Builder | Construction workforce |
| 3+ | Gatherer | Remaining elves gather resources |
With the default 8 starting elves, you get: 1 top Gatherer, 2 Builders, 5 additional Gatherers.
Phase 2: Resource Priority
Runs every consultation. Sets the settlement's gathering priority order based on food levels.
| Condition | Priority Order |
|---|---|
| Food < 20 | Food > Wood > Stone |
| Food >= 20 | Wood > Stone > Food |
The priority is always emitted, but only generates a PriorityShifted event if the new order differs from the current one.
Phase 3: Building Queue + Revel + Composer
The most complex phase. Skips entirely if the build queue already has a pending building. Otherwise, evaluates in order:
Spirit Management (Garden After Clearings)
Condition: clearings_since_garden >= 2 AND wood >= 5
Queues a Garden and resets clearings_since_garden to 0. This takes absolute priority over all other building decisions -- clearing forest angers the forest spirit, and gardens appease it.
Building Priority Order
If spirit management did not trigger, buildings are evaluated in this fixed order:
| Priority | Building | Condition | Purpose |
|---|---|---|---|
| 1 | Dwelling | dwelling_count < 3 AND wood >= 10 | Elf rest and shelter |
| 2 | Garden | garden_count == 0 AND wood >= 5 | Spirit appeasement |
| 3 | Workshop | workshop_count == 0 AND wood >= 10 AND stone >= 10 | Enables composing |
| 4 | FeastHall | feast_hall_count == 0 AND wood >= 15 AND stone >= 5 | Enables revels |
Only one building is queued per consultation. The first matching condition wins.
Revel Scheduling
Evaluated after building decisions. All five conditions must be true:
| Condition | Check |
|---|---|
| No revel active | revel_state == RevelState::None |
| Feast hall exists | At least one FeastHall building |
| Compositions exist | world.compositions is non-empty |
| Enough elves available | At least 5 elves without CreativeBlock |
| Sufficient food | Food >= 5 normally; food >= 15 in Winter |
| Cooldown elapsed | Either first revel ever (last_revel_tick == 0) or at least 400 ticks since last revel |
The winter food threshold (15 vs 5) is the dummy curator's only climate-aware rule. The 400-tick cooldown is equivalent to 4 game days.
Patron-Directed Composer Promotion
Evaluated last, only after bootstrap. When the patron's ArtisticDirection is anything other than Balanced (FavorMastery, FavorOriginality, or FavorEmotion):
- Find the elf with the highest
musicskill. - If no elf currently holds the
Composerrole, assign the best musician asComposer. - If someone is already
Composer, skip (does not re-evaluate).
This ensures non-Balanced artistic directions produce compositions aligned with the patron's taste.
Values & Formulas
Resource Thresholds
| Resource | Threshold | Effect |
|---|---|---|
| Food | < 20 | Priority shifts to Food-first |
| Wood | >= 5 | Can build Garden |
| Wood | >= 10 | Can build Dwelling or Workshop |
| Wood | >= 15 | Can build FeastHall |
| Stone | >= 5 | Can build FeastHall |
| Stone | >= 10 | Can build Workshop |
Building Material Costs (Required to Queue)
| Building | Wood | Stone |
|---|---|---|
| Dwelling | 10 | -- |
| Garden | 5 | -- |
| Workshop | 10 | 10 |
| FeastHall | 15 | 5 |
Revel Preconditions
| Parameter | Value |
|---|---|
Minimum elves (no CreativeBlock) | 5 |
| Food (non-winter) | >= 5 |
| Food (winter) | >= 15 |
| Cooldown between revels | 400 ticks (4 days) |
| Required building | FeastHall |
| Required content | At least 1 composition |
Spirit Management
| Parameter | Value |
|---|---|
| Clearings before forced garden | 2 |
| Max clearings per consultation | 1 (cleared_today flag) |
Interactions
- How the Curator Works -- the overall curator architecture and command dispatch system.
- LLM Curator -- the Claude-backed advisor that uses the dummy curator as its fallback.
- Needs & Mood -- morale and satisfaction values that the dummy curator does not directly read (it uses resource thresholds instead).
Tips
- The dummy curator builds at most 3 Dwellings, 1 Garden (via priority order), 1 Workshop, and 1 FeastHall. Additional gardens come from the spirit management rule (every 2 forest clearings).
- It never reassigns roles after bootstrap. If you want role changes with the dummy curator, you need to switch to the LLM curator or restart.
- Winter is the most dangerous season for revels. The food threshold triples from 5 to 15. Make sure your gatherers have been stocking up through autumn.
- The dummy curator ignores player messages beyond acknowledging them. For actual message-responsive behavior, use the LLM Curator.
- The
clearings_since_gardencounter persists across days and is tracked via theon_forest_clearedcallback. Both directClearForestcommands and build-site clearings increment it. - With 8 starting elves and the bootstrap allocation (6 Gatherers, 2 Builders), the settlement usually accumulates enough wood for its first Dwelling within the first 2-3 days.
LLM Curator (Claude-Backed Advisor)
Overview
The LlmCurator connects your settlement to Claude via the claude CLI, giving the curator genuine reasoning about aesthetics, social dynamics, and cultural strategy. Unlike the Dummy Curator which follows a fixed decision tree, the LLM curator reads the full settlement state -- elf personalities, aesthetic distances, compositions, revel history, climate -- and produces context-aware commands with explanatory reasoning.
It is available in the native TUI build only. On WASM (browser), LlmCurator is a thin stub that delegates to DummyCurator internally.
Setup
Prerequisites
- Install the
claudeCLI and ensure it is on yourPATH. The curator spawns it as a subprocess. - The default model is
haiku(fast, cheap). The model name is stored inself.modeland displayed in the UI as the curator label.
Verifying
Launch the native TUI build with cargo run. If claude is found, the curator label in the UI will show "haiku" instead of "rules". If spawning fails, the curator logs a warning and permanently falls back to the dummy curator for the rest of the session.
Platform Behavior
| Target | Behavior |
|---|---|
Native (cargo run) | Full LLM curator with background thread |
WASM (wasm32-unknown-unknown) | Stub that wraps DummyCurator; label shows "rules" |
How It Works
Background Thread Architecture
The LLM curator is non-blocking. It uses a dedicated background thread to call claude so the game loop never stalls waiting for a response.
Game tick
|
v
should_consult() -- check for pending result or day boundary / event trigger
|
v
consult() -- if pending result ready, return it
-- otherwise, build request, send to background thread
-- return DummyCurator fallback commands for this tick
|
v
[Background thread]
|-- Receives ConsultRequest via mpsc channel
|-- Spawns `claude -p` subprocess
|-- Pipes state message via stdin
|-- Parses JSON envelope from stdout
|-- Stores ConsultResult in Arc<Mutex<Option<...>>>
|
v
Next consult() call picks up the result
Key properties:
- The background thread lives for the entire game session (spawned once in the constructor).
- Only one request can be in flight at a time (the channel is unbounded, but the thread processes sequentially).
- While waiting for the LLM, the dummy curator's logic runs as fallback, so the settlement is never unmanaged.
- If the background thread panics or the channel closes,
permanently_fallbackis set totrue.
Budget and Rate Limiting
The LLM curator enforces two limits to control API costs:
| Parameter | Value | Description |
|---|---|---|
max_per_day | 5 | Maximum LLM consultations per game day |
min_consult_gap | 50 ticks | Minimum ticks between sending new requests (half a day) |
The daily budget resets when tick / DAY_LENGTH changes. When the budget is exhausted or the gap has not elapsed, the curator returns dummy fallback commands.
When It Consults
The LLM curator has a richer trigger set than the dummy curator:
| Trigger | Condition |
|---|---|
| Day boundary | tick / 100 changes (same as dummy) |
| Pending result | Background thread has a result ready -- always process immediately |
| Revel ended | RevelEnded event |
| Art completed | ArtCompleted event |
| Inspiration crisis | InspirationCrisis event |
| Resource critical | ResourceLow event with amount < 5 |
| Spirit anger | SpiritStateChanged event where new state is "Anger" |
Player Messages
When called with consult_with_message, the LLM curator:
- Checks for a pending background result first (returns it if available).
- Appends the player's message to the system prompt as a "Patron's Direct Message" section.
- Sends the request to the background thread.
- Returns
Message("Considering your request...")as an immediate acknowledgment.
The LLM sees the player's exact words and is instructed to "respond to this directive in your reasoning and adjust your decisions accordingly."
Structured Output with --json-schema
The curator uses Claude's structured output mode to guarantee parseable responses.
CLI Invocation
claude -p \
--model haiku \
--output-format json \
--json-schema <schema> \
--system-prompt <system_prompt> \
--tools ""
< state_message
The --tools "" flag disables all tools, forcing pure structured output. The state message is piped via stdin.
Response Envelope
Claude returns a JSON envelope:
{
"type": "result",
"subtype": "success",
"result": "",
"structured_output": { ... },
"total_cost_usd": 0.01
}
The curator reads structured_output first (primary path). If absent, it falls back to parsing result as a JSON string (older format compatibility).
Tool Schema Reference
The schema defines a CuratorResponse object with commands (array) and reasoning (string). Each command is tagged by its action field.
CuratorResponse
{
"commands": [ ... ],
"reasoning": "string"
}
Command Variants
| Action | Fields | Valid Values |
|---|---|---|
assign_role | name, role | role: "Gatherer", "Builder", "Composer", "Unassigned" |
set_resource_priority | priority (array) | "Food", "Wood", "Stone", "FineWood" |
queue_build | kind | "Dwelling", "Garden", "Workshop", "FeastHall" |
clear_forest | x, y (integers) | Map coordinates |
schedule_revel | (none) | |
message | text | Free-form string |
Unknown values (e.g., a role of "Knight") are silently skipped with a log warning. The parse_commands function filters invalid entries rather than rejecting the entire response.
Example LLM Response
{
"commands": [
{"action": "assign_role", "name": "Elowen", "role": "Composer"},
{"action": "set_resource_priority", "priority": ["Food", "Wood"]},
{"action": "message", "text": "Assigning Elowen as Composer -- she has the highest music skill and her emotional style aligns with our patron's taste."}
],
"reasoning": "Best musician gets Composer role."
}
The reasoning field is appended as an additional Message command when non-empty, so it appears in the event log.
The System Prompt
The system prompt establishes the curator's personality as "part colony manager, part artistic director." Key sections:
| Section | Content |
|---|---|
| Aesthetic Axes | Explains the four-axis model (structure, tradition, emotion, social) and how aesthetic distance drives departure |
| Patron's Direction | Inserts the current ArtisticDirection value (Balanced / FavorMastery / FavorOriginality / FavorEmotion) |
| Tools | Documents each command with usage guidance |
| Critical Awareness | Lists situations to watch: discontented elves, aesthetic outliers, creative blocks, spirit anger, food crises, missing compositions, stale revels |
| Style | "Be brief but specific. Name the elves you're acting on and explain why." |
Climate-Aware Context
The state message sent to the LLM includes full climate data:
- Season name, day within season, year
- Current weather and temperature
- Climate-specific alerts (e.g., "Winter food stores running low" when food < 20 in winter, "Settlement sheltering from storm" during storms)
This allows the LLM to make season-appropriate decisions that the dummy curator's fixed rules cannot.
State Snapshot Contents
The LLM receives a text-serialized SettlementState with:
- Resources: current amounts and daily production rates
- Elf roster: skills, morale, satisfaction, aesthetic label and distance, social graph (top 5 friends/rivals by name), portfolio size, aspirations, fandom, status flags (DISCONTENTED, BLOCKED)
- Compositions: last 15 with full quality breakdown, aesthetic position, properties, patron-favorite flag
- Revel history: last 5 with highlight composition, attendee/performance counts, average scores
- Buildings: counts by type, queue size
- Spirit: state label and meter (0-100)
- Current policies: all role assignments and resource priority order
- Patron context: favorite compositions, interesting elves
Compositions are capped at 15 and revel history at 5 to control token usage.
Interactions
- How the Curator Works -- the overall curator architecture, command dispatch, and
CulturalAdvisortrait. - Dummy Curator -- the rule-based fallback that runs when the LLM is unavailable or over budget.
- Needs & Mood -- morale and satisfaction values visible in the state snapshot.
Tips
Effective Prompting (Player Messages)
The patron message is injected verbatim into the system prompt. To get good results:
- Be specific about elves by name: "Focus on Elowen's development as a composer" works better than "make better music."
- Reference the aesthetic axes: "I want a more avant-garde settlement" tells the LLM to favor low-tradition elves.
- Ask for explanations: The LLM always emits a
reasoningfield, but a pointed question like "Why is Theron discontented?" produces richer analysis. - Give artistic direction: "Schedule a revel featuring emotional compositions" guides the LLM's revel timing and implicitly its composer promotion decisions.
Cost Management
- The default model is
haiku-- fast and inexpensive. - At 5 consultations per game day, a typical session costs fractions of a cent.
- The
min_consult_gapof 50 ticks prevents rapid-fire requests even when many events trigger in quick succession. - Token costs are tracked in
total_input_tokensandtotal_output_tokenson the curator struct (not yet surfaced in the UI).
Fallback Behavior
- When the LLM request is in flight, the dummy curator runs. This means the first day's decisions are always rule-based (bootstrap roles, initial priority).
- If
claudeis not on your PATH, the curator permanently switches to dummy mode after the first failed spawn. Check your terminal for the warning: "LLM curator: failed to spawn claude." - LLM results that contain only unknown commands (all filtered out) are treated as empty and trigger a fallback cycle.
Seasonal Planning
The 100-day year creates a rhythm of abundance and scarcity. Planning around seasons is the foundation of colony management.
The Yearly Cycle
| Season | Days | Foraging | Regen | Character |
|---|---|---|---|---|
| Spring (Awakening) | 0-24 | ×1.0 | +2/dawn | Renewal. Spirit heals fastest. |
| Summer (Radiance) | 25-49 | ×1.5 | +1/dawn | Abundance. Best foraging season. |
| Autumn (Fading) | 50-74 | ×1.25 | +1/dawn | Good foraging still. Peak beauty terrain. |
| Winter (Stillness) | 75-99 | ×0.5 | Skip odd days | Scarcity. Spirit dormant. Survival mode. |
(Source: src/sim/climate.rs)
Winter Preparation Checklist
Winter is the most dangerous season. Prepare during Summer and Autumn:
- Food stockpile ≥ 15 — the Dummy Curator won't schedule revels in Winter unless food is above 15 (vs. 5 normally)
- At least 1 Dwelling — shelter from storms and cold. Elves without shelter get "Chilled" (-2 mood) when temperature drops below 25
- Garden(s) built — the forest spirit doesn't heal naturally in Winter. Gardens are the only calming mechanism (-2 per garden per dawn)
- 2+ Gatherers assigned — winter foraging yields only 60% of normal (30% with snow). You need more hands gathering
Spring Opportunities
Spring is the most generous season:
- Resource regen doubled (+2 per source per dawn vs. +1)
- Spirit heals twice as fast (-2 natural dawn decay vs. -1)
- Foraging ×1.0 — baseline rate (Summer at ×1.5 is actually the best foraging season)
- But: clearing forest in Spring angers the spirit more (+7 vs. +5 normally)
Strategy: clear forest in Summer/Autumn when the spirit penalty is lower (+5 vs. +7 in Spring). Summer's ×1.5 foraging is the best stockpiling window. Use Spring's natural healing to recover from any anger accumulated over Winter.
Seasonal Composition Effects
Seasons subtly influence genre selection:
| Season | Genre Tendency |
|---|---|
| Spring | Leans Traditional |
| Summer | Favors Radical |
| Autumn | Most varied |
| Winter | Favors Pastoral |
If you're pursuing a specific cultural direction via Artistic Direction, plan your composition pushes around favorable seasons.
Weather Awareness
Weather has 70% momentum — it tends to persist for a few days. Plan around multi-day patterns:
- Rain: outdoor elves get "Caught in rain" (-1 mood). Shelter helps.
- Storm: elves shelter indoors. Good for composers in Workshops, bad for Gatherers.
- Snow: foraging halved again (stacks with Winter penalty). But "Snow-covered landscape" gives +2 beauty mood.
- Fog: creative elves (Music > 5) get "Mysterious fog" (+1 mood, +1 nature inspiration). A hidden bonus for composers.
Interactions
- Seasons & Weather — full weather mechanics
- Resources — foraging yield formulas
- Forest Spirit — seasonal anger and calming
- Revels — revel scheduling constraints
Spirit Management
The forest spirit tracks ecological balance. Keeping it in Harmony (0-20) is essential — higher states bring terrain effects and reduced yields.
The Anger Budget
Every forest clearing adds anger. Every dawn removes some. Your goal is to keep the net change negative.
Anger Sources
| Action | Anger Added | Notes |
|---|---|---|
| Clear Young Forest | +5 | Standard penalty |
| Clear Ancient Forest | +5 | Same penalty, but you lose more beauty |
| Clear any forest in Spring | +7 | Spring penalty is higher (forest is awakening) |
Calming Sources
| Source | Anger Removed | When |
|---|---|---|
| Natural dawn decay | -1 | Every dawn (except Winter) |
| Natural dawn decay (Spring) | -2 | Spring doubles natural healing |
| Garden | -2 per garden | Every dawn, all seasons including Winter |
Safe Harvesting Rates
With 0 Gardens:
- Non-Spring: clear 1 forest per day → net change = +5 - 1 = +4/day. Spirit enters Tension in 5 days.
- Not sustainable. Build a garden first.
With 1 Garden:
- Non-Spring: +5 - 1 - 2 = +2/day. Slow accumulation — budget ~10 clearings before hitting Tension.
- Spring: +7 - 2 - 2 = +3/day. Faster accumulation due to higher penalty.
With 2 Gardens:
- Non-Spring: +5 - 1 - 4 = 0/day. Break-even. You can clear 1 forest per day indefinitely.
- Spring: +7 - 2 - 4 = +1/day. Slight net positive — still need to pace.
With 3 Gardens:
- Non-Spring: +5 - 1 - 6 = -2/day. Net healing. Clear freely.
- Spring: +7 - 2 - 6 = -1/day. Still net healing.
The Winter Problem
The spirit doesn't heal naturally in Winter. No dawn decay occurs. But Gardens still work.
- With 0 Gardens in Winter: any anger accumulated persists until Spring.
- With 2 Gardens in Winter: -4/dawn. Spirit heals through gardens alone.
Key insight: build gardens before Winter. They're your only defense against persistent anger.
Recovery Timelines
If the spirit reaches Anger (76+) and you stop clearing:
| Gardens | Days to Harmony (from 76) | Season Assumed |
|---|---|---|
| 0 | 56 days | Non-Winter |
| 1 | 26 days | Non-Winter |
| 2 | 15 days | Non-Winter |
| 3 | 10 days | Non-Winter |
| 0 | Never (in Winter) | Winter |
| 2 | 19 days | Winter |
State Consequences
| State | Meter | Effect |
|---|---|---|
| Harmony | 0-20 | No effects. All is well. |
| Tension | 21-50 | Warning events in the event log. |
| Displeasure | 51-75 | Terrain effects, reduced resource yields. |
| Anger | 76+ | Active interference with the settlement. |
Interactions
- Forest Spirit — full mechanics reference
- Buildings — Garden cost (5 Wood) and placement
- Seasonal Planning — Spring vs. Winter clearing strategy
- Resources — wood sources and alternatives
Revel Optimization
Revels are the primary mechanism for boosting Beauty and Stimulation needs, generating mood modifiers, and creating shared cultural experiences. Optimizing them means better morale and longer elf retention.
When to Schedule Revels
The Dummy Curator schedules revels automatically based on:
- Food availability: needs ≥ 5 (or ≥ 15 in Winter)
- Cooldown: a minimum time between revels
- Feast Hall: having a Feast Hall improves revel quality
Player tip: if you're using the LLM Curator, you can request revels via chat. The curator considers settlement state before scheduling.
Timing Considerations
- Avoid scheduling during storms — elves shelter indoors and may not gather at the revel location
- Autumn revels benefit from peak beauty terrain — Ancient Forest gains +1 beauty in Autumn
- Winter revels are expensive — food threshold is 3× normal (15 vs. 5), but they're the best morale tool during the hardest season
- Spring revels pair well with high inspiration — the renewal season often coincides with creative output spikes
Performer Selection
The compositions performed at a revel determine audience satisfaction. Key factors:
- Quality matters: higher mastery, originality, and emotional scores create better experiences
- Aesthetic alignment: audience members enjoy compositions that match their aesthetic position. A Traditional composition played to innovation-leaning elves won't land well.
- Variety: a diverse composition portfolio creates broader appeal
Strategy: encourage a mix of genre families (Traditional, Radical, Pastoral) so revels have something for everyone. Use Artistic Direction set to "Balanced" for maximum variety, or target specific genres to match your colony's dominant aesthetic.
Maximizing Satisfaction
Revel satisfaction feeds into the Satisfaction system. High-satisfaction revels:
- Generate positive mood modifiers for attendees
- Satisfy Beauty and Stimulation needs
- Can rescue discontented elves through satisfaction spikes
The satisfaction spike mechanic: when an elf attends a great revel, the satisfaction boost is buffered to survive the 10-tick recompute cycle. This means a well-timed revel can save an elf approaching the departure threshold.
Saving Discontented Elves
If you see the discontented indicator on an elf:
- Check their needs — address the most critical deficit
- Schedule a revel — the satisfaction spike can buy time
- Assign them near friends — the Company need matters
- You have 500 ticks after the warning before departure
Interactions
- Revels — full lifecycle mechanics
- Compositions — quality formula and genre system
- Satisfaction & Departure — how revel satisfaction affects retention
- Seasonal Planning — seasonal timing for revels
Role Allocation
Choosing the right role for each elf is a balancing act between resource production, artistic output, and construction progress.
The Four Roles
| Role | Primary Output | Skill Used |
|---|---|---|
| Gatherer | Wood, Stone, Food, FineWood | Gathering |
| Builder | Building construction progress | Building |
| Composer | Musical compositions | Music |
| Unassigned | Idle — forages, socializes, rests | None specific |
See Roles for detailed per-role behavior and task selection logic.
Allocation Strategies
Early Game (First 50 Days)
With 5-8 elves, a good starting split:
- 2-3 Gatherers — food security and building materials
- 1 Builder — start the build queue (Dwelling → Workshop → Garden)
- 1-2 Composers — begin cultural output, gain Music XP
- 1-2 Unassigned — flexible labor, passive foraging
Why not all Gatherers? Resources accumulate faster, but you miss early composition XP. Music skill levels matter for quality — starting one tick sooner compounds.
Mid Game (Days 50-75, Autumn)
Shift toward stockpiling for Winter:
- 3+ Gatherers — build food reserves above 15 (Winter revel threshold)
- 1 Builder — finish remaining structures
- 2+ Composers — established skill levels produce better compositions for revels
Winter (Days 75-99)
Foraging yields drop to 50% (25% with snow). Adjust:
- Keep at least 2 Gatherers — maintaining food supply is critical
- More Composers — indoor work, unaffected by weather (if Workshop exists)
- Builder if queue remains — indoor construction if buildings are pending
Late Game
Once buildings are complete and resources are stable:
- 1-2 Gatherers — maintenance level
- 0 Builders — reassign once queue is empty
- Majority Composers — the art system is the endgame
Skill-Based Assignment
The Dummy Curator's bootstrap_roles() assigns roles based on each elf's highest skill:
- Highest Music skill → Composer
- Highest Building skill → Builder
- Highest Gathering skill → Gatherer
- Ties → falls through to Gatherer (most useful default)
Should you follow it? Usually yes. An elf with high Music skill produces better compositions faster. But consider:
- An elf with Music 1 and Gathering 8 generates way more resources as a Gatherer
- XP formula is linear (level × 50 per level), so low-skill elves catch up eventually
- Aesthetic position affects composition genre — an elf with extreme aesthetic values produces more distinctive work
Specialization vs. Generalization
Specialization (few role changes):
- Pros: faster skill growth, higher quality output
- Cons: fragile if an elf departs, resource bottlenecks
Generalization (frequent role changes):
- Pros: flexible, resilient to departures
- Cons: slower XP growth, lower quality output
Recommendation: specialize your best elves (highest skills) and keep 1-2 generalists as flexible labor. The XP curve is linear — every tick of role time matters.
Interactions
- Roles — detailed role behavior
- Skills — XP formula and level effects
- Satisfaction & Departure — departing high-skill elves hurt most
- Seasonal Planning — seasonal role adjustments