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:

  1. You (the Patron) — provide taste, artistic direction, and broad guidance via the chat panel and keyboard commands
  2. The AI Advisor (Curator) — translates your preferences into concrete cultural policies: role assignments, revel scheduling, build orders
  3. 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:

  1. Overview — what the system does
  2. How It Works — mechanics description
  3. Values & Formulas — tables with exact numbers from the source code
  4. Interactions — how it connects to other systems
  5. 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

KeyAction
SpacePause / Resume simulation
+ / =Speed up (1x → 2x → 4x → 8x)
-Slow down
Arrow keysPan map (when map focused)
TabCycle focus: Map → Chat → Events
BackTab (Shift+Tab)Cycle focus backward
EscClose overlay / deselect / back
Ctrl+CQuit
?Help overlay (press any key to close)

Elf Selection

KeyAction
1-9Select elf by number
,Cycle to previous elf
.Cycle to next elf
EnterOpen 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.

KeyScreenWhat It Shows
sSettlement OverviewPopulation, resources (Wood/Stone/Food/FineWood), buildings, daily production rates
aArt GalleryAll compositions with genre, quality, properties, titles, descriptions
xRelationshipsElf-to-elf bonds (friends, rivals, mentors) with relationship strength
vRevel HistoryPast revels: when they happened, who attended, what was performed, satisfaction
bBuild ManagementBuild queue: pending and completed buildings with progress
wWork AssignmentsElf-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:

KeyAction
Up/DownSelect elf row
Left/RightSelect role column
EnterAssign the selected role to the selected elf

Current assignment shows as [X] in green. Cursor cell is highlighted in cyan.

Panels & Modes

KeyAction
rToggle roster sidebar (list of all elves with mood indicators)
eToggle events panel (scrolling settlement event log)
lEnter look mode (inspect tiles on the map)

Look Mode

In look mode, a cursor appears on the map for tile inspection:

KeyAction
Arrow keysMove look cursor
w/a/s/dPan camera (WASD)
EnterSelect elf at cursor position
nName the location at cursor position
, / .Cycle elves (from cursor)
Esc or lExit look mode

Chat (AI Advisor)

KeyAction
/Open chat input (from map focus)
Tab → typeFocus chat panel, then type freely
EnterSend message to the curator
EscCancel / close chat input
Up/DownScroll message history (when chat focused)

See How the Curator Works for what the advisor can do.

Actions

KeyAction
dCycle artistic direction: Balanced → Favor Mastery → Favor Originality → Favor Emotion
iWatch/unwatch selected elf (marks for tracking in detail view)
fToggle favorite on selected composition (in elf detail)
j/kNavigate 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.

GlyphMeaning
Colored lettersElves (each has a unique color)
.Meadow
#Stone
~Water
TAncient Forest
tYoung Forest
=Wall (player-built)
Building glyphsDwellings, 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

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 with f)
  • 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.

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:

  1. Dwelling — shelter from weather, faster rest
  2. Workshop — composing station, stimulation boost
  3. 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:

NeedDecays EveryWatch For
RestEvery tickElves auto-rest when low
SustenanceEvery 4 ticksNeed food stockpile > 0
BeautyEvery 2 ticksGardens and forest terrain help
StimulationEvery 2 ticksSocial events and composing
CompanyVariesProximity 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

MilestoneWhenWhy It Matters
First Dwelling builtTicks 10-30Shelter from rain/storms, faster rest
First compositionTicks 15-40Art system is active
First relationshipTicks 20-50Social bonds affect mood and aesthetics
First revelTicks 40-80Settlement-wide celebration
Food stockpile > 15Before WinterCurator 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.

NeedInitial ValueDecay RateDecay Frequency
Rest100-1Every tick
Sustenance100-1Every 4th tick
Beauty60-1Every 2nd tick
Stimulation60-1Every 2nd tick
Company50variesEvery 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 RangeLabelCompact Char
80-100Fulfilled+
50-79Fine~
20-49Wanting-
0-19Critical!

(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 AxisBehaviorRecovery
> 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

SourceValueDuration (ticks)
Ate a meal+550
Slept in dwelling+3100
Slept on ground-350
Caught in rain (outdoors)-150
Snow-covered landscape+250
Awed by ancient grove+2200
Grieving (mourning)-5mourning duration
Spring optimism (seasonal)+21 day (100 ticks)
Autumn melancholy (seasonal)-11 day (100 ticks)
Winter stillness (seasonal)-11 day (100 ticks)
Favorite spot+115 ticks
Personal time+250 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

TierMorale RangeEffect
Inspired80-100Work speed +2; gravitates toward creative work
Normal50-79Standard behavior
Stressed20-49Work speed -1
Breaking0-19Refuses 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 TierTransition UpTransition 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

ResourceStarting StockDescription
Food50Consumed when eating; prevents starvation
Wood30Primary building material
Stone10Used for Workshops and Feast Halls
FineWood3Rare material (reserved for future crafting)

(Source: src/sim/world.rs, Resources::starting())

Gathering Pipeline

The full resource loop is:

  1. Task Decision -- an idle elf decides to gather a resource type based on role, policy priority, or the most_needed_resource() fallback.
  2. Pathfinding -- the elf walks to the nearest ResourceSource of that type.
  3. Gathering -- once at the source and with no remaining path, the elf accumulates progress each tick.
  4. Pickup -- at progress >= 100, the elf picks up 5 units of the resource and the source loses 1 amount.
  5. Return -- the elf pathfinds back to the nearest building.
  6. 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.

TerrainForage AmountCap
Forest (Ancient or Young)+860
Other walkable+560

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:

SeasonMultiplier
Spring1.0x
Summer1.5x
Autumn1.25x
Winter0.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 TypeRegeneration Cap
FineWood5
All others10

(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

RoleDescription
UnassignedDefault. Follows the general behavior tree; gathers the most-needed resource.
ComposerGravitates toward composing at a Workshop when no urgent needs exist.
BuilderPrioritized for build queue assignments; default gather target is Stone.
GathererDefault 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():

RoleDefault Resource
GathererWood
BuilderStone
ComposerNone (uses policy priority or most-needed)
UnassignedNone (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.

PriorityConditionAction
Step 0: PreemptSustenance or Rest critical (< 20, or < 5 for construction)Cancel current task, go idle
Step 1: Critical NeedsSustenance < 20Eat (if Food available) or Gather Food
Rest < 20Rest (seek Dwelling)
Step 1b: DiscontentedElf has Discontented markerOnly gather Food or idle; refuses creative/ambitious work
Step 1c: MourningElf has Mourning markerCompose at Workshop (tribute); Wander if no Workshop. Skips gathering/building.
Step 2: Creative BlockElf has CreativeBlockSeek Garden (if available)
Step 3: Moderate NeedsBeauty < 30 or Stimulation < 30Compose (Stimulation) or SeekGarden (Beauty), whichever is lower
Step 3b: AspirationsAspiration wants compose/socializeCompose at Workshop or Wander to social buildings
Step 3c: Skill-DrivenMusic skill >= 7Compose at Workshop
Building skill >= 7 + build queue not emptyBuild
Step 3d: Personal Time5% random chanceSeek favorite place or Garden for beauty/inspiration
Step 4: Cultural PolicyRole == ComposerCompose at Workshop
Any other roledecide_default()
Breaking MoraleMorale < 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:

  1. Check resource_priority[0] -- if that resource stock < 10, gather it (override).
  2. Otherwise, if the elf's role has a role_resource, gather that.
  3. Otherwise, use resource_priority[0] if set.
  4. Otherwise, gather most_needed_resource() (lowest stock of Food/Wood/Stone).
  5. If no source exists for the chosen resource, fall back to most-needed.
  6. 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:

  1. Prefer idle elf with Builder role.
  2. Then any idle elf.
  3. Then any Builder-role elf on a non-critical task (Sustenance >= 40 and Rest >= 40).
  4. 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 TaskPreempt Threshold
Gather, Compose, SeekGarden, WanderSustenance < 20 OR Rest < 20
Build, ClearForestSustenance < 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

RoleIdle BehaviorWhen Needs OKWhen Needs Critical
UnassignedGather most-needed resourceFollow policy priorityEat/Rest
ComposerCompose at WorkshopCompose at WorkshopEat/Rest
BuilderGather StoneAssigned to build queue firstEat/Rest
GathererGather WoodGather WoodEat/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

SkillGovernsKey Threshold
MusicComposition speed, quality (mastery score)>= 7: self-selects to compose
BuildingConstruction speed, building XP gains>= 7: self-selects to build (if queue non-empty)
GatheringResource collection speed--
StewardshipAnimal 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

LevelXP to Next LevelCumulative XP
1500
210050
3150150
4200300
5250500
6300750
73501,050
84001,400
94501,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

ActivitySkillXP per Event
Complete a gathering taskGathering+10
Complete a building taskBuilding+15
While composing (per tick)Music+1
Near a bondable animalStewardship+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 SkillTicks to Complete (Normal morale)
117
313
510
79
107

(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 SkillTicks to Complete (Normal morale)
117
313
510
79
107

(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 SkillDuration (ticks)Progress/tick
1482
3442
5402
7362
10303

(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).

StewardshipBond Growth/TickCan Bond Tolerant Species?
11No
21No
32Yes
52Yes
73Yes
104Yes

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

SkillLevelBehavior
Music>= 7Self-selects to Compose (Step 3c in behavior tree)
Building>= 7Self-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

TerrainGlyphWalkableIs ForestBeauty Value
Meadow.YesNo0
Stone#YesNo0
Water~NoNo0
AncientForestTYesYes2
YoungForesttYesYes1
WallXNoNo0

(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:

TerrainSeasonEffective Beauty
AncientForestAutumn3 (base 2 + autumn color bonus)
YoungForestWinter0 (bare branches, normally 1)
All othersAnyBase 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:

TerrainInspiration 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

TerrainForage AmountCondition
Forest (Ancient or Young)+8 SustenanceSustenance < 40, every 10th tick
Other walkable (Meadow, Stone)+5 SustenanceSustenance < 40, every 10th tick
Water, Wall0 (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).

TerrainForageable
MeadowYes (+5)
AncientForestYes (+8)
YoungForestYes (+8)
StoneNo
WaterNo
WallNo

(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:
TerrainProbability
Wall2%
Water5%
Stone8%
YoungForest15%
Meadow70%

(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

BuildingDescription
DwellingLiving quarters. Speeds rest recovery. Counts as shelter.
WorkshopCrafting space. Required for composing. Counts as shelter.
GardenBeauty and creative block recovery. Does NOT count as shelter.
Feast HallCommunal eating. Social gathering space. Counts as shelter.

(Source: src/sim/components.rs, enum BuildingType)

Build Costs

BuildingWoodStoneFineWood
Dwelling10----
Workshop1010--
Garden5----
Feast Hall155--

(Source: crates/er-sim/src/sim/systems/building.rs, build_cost())

Build Queue

Buildings are constructed through the build queue system:

  1. The patron or AI curator adds a QueuedBuild (type + site position) to CulturalPolicies::build_queue.
  2. build_queue_system() checks if the settlement can afford the top item.
  3. If affordable, it assigns an idle elf (preferring Builders) and deducts resources immediately.
  4. The assigned elf pathfinds to the build site and begins construction.
  5. 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:

PriorityCandidate
1stIdle elf with Builder role
2ndAny idle elf
3rdBuilder-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 SkillTicks to Complete (Normal)Ticks (Inspired)Ticks (Stressed)
1171320
3131015
510812
79710
10768

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)

WeatherOutdoors EffectIndoors 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:

LocationRest Recovery RateMood 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 SkillCompose Time (ticks)
148
540
834
1030

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.

GenreDescriptionSelection Bias
TraditionalClassical forms, familiar structuresHigh tradition axis
RadicalAvant-garde, experimentalLow tradition axis
PastoralNature-inspired, ambientHigh 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:

ProbabilityFormulaNotes
p_radicalmax((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_nature0.6 if Nature is dominant source AND nature > 40; else 0.1Requires both conditions; dominant source alone is not enough
p_traditionalmax(1.0 - p_radical - p_nature, 0.0)Remainder after radical and pastoral claims

All three probabilities are then adjusted by seasonal bias:

SeasonAdjustment
Winterp_nature += 0.2 (Pastoral +20%)
Summerp_radical += 0.2 (Radical +20%)
Springp_traditional += 0.1 (Traditional +10%)
AutumnNo 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:

FamilyBaseSeasonalEffective share
Traditional0.15+00.15 / 1.45 = ~10%
Radical0.75+0.20.95 / 1.45 = ~66%
Pastoral0.10+00.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):

SkillTierTraditionalRadicalPastoral
0-3ApprenticeLullaby, Hymn, Folk Song, Ditty, Canticle, AirChant Riff, Drum Circle, Voice Loop, Spoken WordBirdsong, Creek Melody, Windchime, Leaf Rustle
4-6JourneymanBallad, Rondo, Serenade, Nocturne, Madrigal, MinuetBlues Lament, Groove, Free Verse, SyncopationStorm Song, Forest Waltz, River Suite, Dawn Chorus
7-9MasterSonata, Concerto, Rhapsody, Aubade, Fantasia, ElegyJazz Standard, Rock Anthem, Beat Poem, FusionThunder Concerto, Tide Rhapsody, Season Cycle
10+GrandmasterRequiem, Symphony, Magnum Opus, OratorioNoise Symphony, Rap Epic, Punk Requiem, Ambient OpusWorld 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:

DimensionDriverFormula
MasteryMusic skillskill x 10 + random(-5..+5), clamped 0-100
OriginalityInspiration totalinspiration_total x 0.6 + random(0..20), clamped 0-100
EmotionalNet moodmood x 0.5 + 30 + random(0..10), clamped 0-100

Average quality = (mastery + originality + emotional) / 3

Average QualityTier Label
90-100Transcendent
70-89Masterful
50-69Skilled
25-49Modest
0-24Crude

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):

PropertyCondition
CatharticComposer has an active creative block
TranscendentTotal inspiration > 90
DebutFirst composition ever (empty portfolio)
Magnum OpusSkill 10 AND inspiration > 90 AND mood > 50 (once per lifetime)
MasterworkSkill >= 10 (if Magnum Opus not triggered)
ElegyComposed while mourning a departed friend

Compound inspiration properties (require two channels both > 30):

PropertyChannels Required
ManifestoRivalry > 30 AND Social > 30
ObsessiveRivalry > 30 AND Solitude > 30
CommunalNature > 30 AND Social > 30
TimelessNature > 30 AND Solitude > 30
VirtuosicBeauty > 30 AND Rivalry > 30
SublimeBeauty > 30 AND Solitude > 30
BittersweetSocial > 30 AND Solitude > 30
EnchantedNature > 30 AND Beauty > 30
AnthemSocial > 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.

PropertySeasonMeaning
VernalSpringBeginnings, renewal, hope
SolstitialSummerAmbition, peak energy, glory
AutumnalAutumnReflection, bittersweet, impermanence
HibernalWinterDepth, 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:

ConditionPoolExamples
Night AND mood < -10HauntedHaunted, Spectral, Moonlit, Shadow-woven
Inspiration > 70 AND mood < 0FierceFierce, Defiant, Tempestuous, Raw
Mood <= -41DevastatedShattered, Grieving, Tormented
Mood -40 to -21SadMelancholic, Mournful, Desolate
Mood -20 to +10ReflectiveContemplative, Wistful, Pensive
Mood +11 to +40ContentSerene, Gentle, Tender
Mood +41 to +70HappyJoyful, Radiant, Exuberant
Mood > +70EuphoricEcstatic, Triumphant, Blazing

Suffix comes from the dominant inspiration source:

SourceExample 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):

ChannelSourceWhat Builds It
NatureExposure to the natural worldBeing near forests, gardens, water; wandering in wilderness
SocialCommunity and companionshipProximity to other elves, shared meals, conversations
RivalryArtistic competitionProximity to rivals, competing at revels, aesthetic disagreements
BeautyWitnessing great artHearing loved compositions at revels, being near gardens
SolitudeTime aloneWandering 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:

ChannelEquilibrium Formula
Nature(1.0 - structure) x 20
Socialsocial 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:

PropertyRequired Channels
TranscendentTotal > 90 (any combination)
ManifestoRivalry > 30 AND Social > 30
ObsessiveRivalry > 30 AND Solitude > 30
CommunalNature > 30 AND Social > 30
TimelessNature > 30 AND Solitude > 30
VirtuosicBeauty > 30 AND Rivalry > 30
SublimeBeauty > 30 AND Solitude > 30
BittersweetSocial > 30 AND Solitude > 30
EnchantedNature > 30 AND Beauty > 30
AnthemSocial > 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 social axis 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.

AxisLow End (0.0)High End (1.0)
StructureFreedom (improvisation)Structure (formal composition)
TraditionInnovation (hunger for the new)Tradition (love of the familiar)
EmotionIntellect (provoke thought)Emotion (move the audience)
SocialPersonal (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:

WeightFormula
w_masterystructure 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_emotionalemotion 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 ValueGenre 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:

ChannelEquilibrium
Nature(1.0 - structure) x 20
Socialsocial 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 SchoolFormed events 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 TypeRate/axisRangeTriggerFrequency
Friend0.013 tilesFriendship (strength >= 50)Once per day
Audience0.005Revel attendanceEach composition heardPer performance
School0.0085 tilesComposer with 3+ Love reactions (non-friends only)Once per day
Apprenticeship0.025 tilesActive apprentice-master bondOnce 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:

SourceApplicationsContribution
Master (apprenticeship)10.02
Friend A (tradition higher)10.01
Friend B (tradition higher)10.01
School composer (not a friend)10.008 (direction only)
Revel with 4 performances44 × 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:

AxisShift
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:

RequirementCondition
Feast HallAt least one built
CompositionsAt 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 RevelAttending marker
  • 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:

  1. The audience member's aesthetic position determines evaluation weights
  2. A weighted score is computed from the composition's mastery, originality, and emotional dimensions
  3. A social modifier is applied (+5 for social axis > 0.7, -3 for social axis < 0.3)
  4. Discontented elves receive a -5 penalty to their score
  5. The score maps to a reaction tier
Score RangeReaction
0-24Dislike
25-50Indifferent
51-75Enjoy
76-100Love

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:

  • RevelAttending markers 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_tick is 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:

ReactionMood EffectDuration
Dislike-3 ("Disliked {name}")50 ticks
IndifferentNo 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:

EventSpike
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

InputWeightSource
Recent Love reactionsx 5.0Count of Love reactions in the last 5 revels
Fan countx 10.0Number of elves with a Fandom entry for this composer
Portfolio sizex 2.0Total compositions in the elf's portfolio
Decay-1.0 per 50-tick cycleApplied when no Love reaction received in the last 200 ticks

The score is clamped to 0--100 and mapped to a tier:

TierScore Range
Unknown0--15
Emerging16--35
Established36--55
Renowned56--80
Legendary81--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:

EffectValue
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.

ParameterValue
Love reaction threshold3 total (lifetime, not per-revel)
School drift rate0.008 per axis per day
School influenceNon-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:

PersonalityConditionAbsence Reason
Cautiousboldness < 0.3"chose the forest over the fire"
Proudpride > 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.

ParameterValue
Cooldown duration400 ticks
First revelCooldown waived
Food requirement (normal)>= 5
Food requirement (Winter)>= 15
Minimum elves5

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.

TypeLabelSeasonAppropriateness
StandardStandard RevelAny0 (neutral)
GardenWalkGarden WalkSpring+5 in Spring
GrandPerformanceGrand PerformanceSummer+5 in Summer, -8 in Winter
RetrospectiveRetrospectiveAutumn+5 in Autumn, -3 in Spring
ChamberPerformanceChamber PerformanceWinter+5 in Winter, -3 in Summer
SpontaneousSpontaneous RevelAny (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.

ConditionScore 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

ConditionValue
TriggerComposer's Aspirations.love_reactions_received crosses SCHOOL_THRESHOLD
Threshold3 total Love reactions across the composer's career
FrequencyOne-shot — fires only on the transition from below to at-or-above 3
WhenDuring 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

FieldTypeDescription
composerStringName of the elf who became a school
love_countu8The 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 -- SchoolFormed is 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 CulturalEvent and 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

FieldTypeDescription
genreStringName of the genre family invented (e.g., "Jazz")
inventorStringName 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 Founder tag 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 CulturalEvent and 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:

  1. 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.
  2. 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").
  3. Tag aligned elves with movement membership.
  4. Emit MovementCrystallized { name, member_count }.

Event Payload

FieldTypeDescription
nameStringName of the movement (e.g., "The Defiant School")
member_countu32Number 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:

StageEventScopeTrigger
1. Recognition of a single composerSchoolFormedOne composer3+ Love reactions
2. Recognition of a genre breakthroughGenreInventedOne genre familyFirst anachronistic of its family
3. Recognition of a coherent audience factionMovementCrystallizedGroup of 3+ elvesShared 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.

  • Revels -- where Love reactions are tallied and where SchoolFormed is emitted. The intended trigger sites for GenreInvented and MovementCrystallized would 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 SchoolFormed display 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 GenreInvented emission: track first-seen genre families per settlement, fire on first anachronistic composition.
  • Revel-awc — implement MovementCrystallized detection 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

DirectionLabelDescription
BalancedBalNo preference -- the curator decides freely based on settlement needs
Favor MasteryMasPrioritizes high-skill elves as composers; values technical excellence
Favor OriginalityOrigEncourages experimental, inspiration-driven work
Favor EmotionEmoValues 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:

  1. 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
  2. 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:

DirectionIndirect Effect
Favor MasteryCurator assigns high-skill elves as Composers, who produce higher mastery scores
Favor OriginalityCurator encourages conditions for high inspiration (gardens, varied experiences)
Favor EmotionCurator protects emotionally-driven elves from creative block, maintains positive mood
BalancedCurator 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:

DirectionTends to ProduceAudience Effect
Favor MasteryHigh mastery scoresAppreciated by structured, traditional elves
Favor OriginalityHigh originality scoresAppreciated by innovative, low-tradition elves
Favor EmotionHigh emotional scoresAppreciated by emotional, social elves
BalancedEven distributionModerate reception across the board

Patron Taste System

Artistic direction is one part of the broader Patron Taste system, which also includes:

FeatureDescriptionControl
Artistic DirectionBias curator decisions'd' key
Favorite CompositionsMark compositions you valueComposition interaction
Interesting ElvesMark elves you want the curator to focus onElf interaction
Named LocationsPlace meaningful names on the mapMap 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:

DirectionStatus Bar
BalancedBal
Favor MasteryMas
Favor OriginalityOrig
Favor EmotionEmo

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:

TypeStrength RangeDescription
Acquaintance-29 to +49Neutral. Default state for any new relationship.
Friend+50 to +100Positive bond. Provides mood bonuses and satisfaction gains.
Rival-100 to -30Negative 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):

ConditionBase Delta
Both actively working (not Idle)+1
Both resting or eating, distance <= 2+2
One or both idle0 (no change)

Aesthetic affinity modifier (applied on top of base delta):

4D Aesthetic DistanceModifier
< 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 ValueMultiplierPersonality
> 0.7x2Social elf -- bonds form and break faster
0.3 -- 0.7x1Balanced
< 0.3x0.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 true when 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 true for all new relationships. Indicates the bond has not yet been reinforced. Set to true on 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:

SituationMood ModifierDuration
Near a friend (distance <= 3)+350 ticks
Near a rival (distance <= 3)-250 ticks
Near beloved (partner within 3 tiles)+550 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 TypeConditionEffect
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)
BalancedAnyDrifts 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 DepartedMood EffectDuration
Friend-10300 ticks
Rival+3100 ticks
Acquaintance-2100 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:

  1. Mood penalties when the rivals are within 3 tiles of each other (handled by the social mood system — see Relationships → Mood Effects).
  2. Arguments triggered by the conflict system on rival proximity.
  3. Public critiques triggered when a rival in a revel audience Dislikes the composer's work.
  4. 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:

  1. Manhattan distance <= 3 (rivals must be near each other for anything to happen).
  2. Both sides classified as Rival (the rivalry must be mutual — strength check on at least one side).
  3. Per-pair cooldown elapsed (no two arguments between the same pair within DAY_LENGTH × 3 ticks — 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 axisTopic
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:

EffectValueDuration
Mood penalty (both)-2 to -5 ("Argued with rival"), scaled by pride50–100 ticks (longer for proud elves)
Rivalry inspiration boost (both)+3Permanent (capped at 100)
Relationship strength delta (both)-2Permanent

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 EgoWound component.
  • LongPatienceBroken: patience > 0.85, 10% chance per qualifying argument → +50 ticks of "Patience shattered" (-5 mood) and a LongPatienceBroken component.

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:

EffectValueDuration
Composer mood penalty-4 ("Publicly criticized")80 ticks
Rivalry inspiration boost for the critic+2Permanent (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 EgoWound event — 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_min watermark 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 PersonalityCrisis in the feed, the affected elf is briefly fragile.

  • 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:

ConditionThreshold
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 mentoringno HasApprentice component
Student not already apprenticedno 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 StrengthGraduation 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:

EffectTargetValue
Bonus XP in shared skillStudent+1 XP (on top of normal compose/build/gather drip)
"Teaching" mood modifierMaster+2 for 50 ticks (refreshed each cycle)
bond_strength incrementBond+1
last_bond_tick updateBondset 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:

  1. Remove Apprenticeship from student and HasApprentice from master.
  2. Attach a permanent TrainedBy { master_name, skill } component to the student.
  3. Apply mutual relationship bonus (+15, +20, or +25 depending on bond strength).
  4. Push "Graduated" mood +8 on the student for 300 ticks.
  5. Emit a Graduation event (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 TraditionReactionEffects
> 0.6 (traditional)ResentmentMaster: -10 relationship to student; +5 Rivalry inspiration
<= 0.6 (open)PrideMaster: +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:

ConditionAction
Master entity no longer existsRemove 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 colonyCleanup 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.

SourceWarmthCatalyst?Condition
Shared transcendence+8.0YesBoth loved the same composition at the last revel
Shared grief+6.0YesBoth mourning the same departed friend
Comfort in darkness+5.0YesOne elf's morale < 30, other within 3 tiles
Aesthetic resonance+2.0NoAesthetic distance < 0.25 AND friends
Slow burn+1.0NoFriends 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:

PersonalityMultiplierEffect
Bold (boldness > 0.7)x1.3Falls faster
Cautious (boldness < 0.3)x0.6Falls slowly, carefully
Proud (pride > 0.7)x0.8Slightly 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

StageThresholdRequirements
Attractionwarmth > 0Automatic when any warmth source fires
Courtshipwarmth >= 60.0Mutual warmth >= 60 AND catalyst within last 100 ticks
Partnershipwarmth >= 85.0Both 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 Unrequited personality 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):

SourcePressureCondition
Aesthetic alienation(distance - 0.8) x 5.0Aesthetic distance > 0.8
Status asymmetry(gap - 0.3) x 3.0Prestige gap > 0.3
Partner is rival+10.0Relationship 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:

TriggerScar TypeWho Gets Scarred
Betrayal (partner courting someone else)BetrayalOnly the betrayed elf
Aesthetic driftHeartbreakBoth elves
Prestige asymmetryHeartbreakBoth elves
Growing apartHeartbreakBoth 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:

  1. SharedTranscendence: +8.0 x 1.3 = +10.4 (catalyst set)
  2. SlowBurn each 50-tick cycle: +1.0 x 1.3 = +1.3
  3. At cycle 39 (~1,950 ticks): warmth crosses 60.0 -- spark
  4. If mutual and catalyst within 100 ticks: courtship begins
  5. Continued SlowBurn + AestheticResonance: ~2.0-3.3 per cycle
  6. 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

SituationMoodDuration
Near beloved (partner within 3 tiles)+550 ticks
Courting someone+5While courting
Unrequited resolution-5150 ticks
Publicly criticized by partner-480 ticks

Source: crates/er-sim/src/sim/systems/social.rs, social_mood_system -- "Near beloved" modifier.

Satisfaction

Romance contributes directly to satisfaction:

SituationSatisfaction
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:

ConditionProperty
Has partner, partner satisfied (mood > 0)Contentment
Heartbreak scar AND currently unpartneredHeartbreak (+15 emotional)
Currently courtingLoveSong (+10 emotional)
Partner just departedElegy (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.

OriginTrigger ConditionTiming
ApprenticeshipMaster 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 daysGraduation tick (if already Partner) or partnership-formation tick (if pair partners later)
SharedCrisisComfortInDarkness or SharedGrief warmth catalyst fires for the pair during an active Mourning episodeTick of the first catalyst firing within that mourning episode
RivalryToLovePair transitions to Partner AND strength_min <= -30 at the moment of transitionPartnership-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:

OriginPer-Entry Delta
Apprenticeship+20
SharedCrisis+15 (capped at 3 occurrences = +45 max)
RivalryToLove-10
Workshop, Revel, Critique, Rescue0 (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:

  1. Accumulated pressure would cross baseline 100 (dissolution would have fired without origins), AND
  2. Pressure is still below the origin-adjusted threshold (dissolution does not fire with origins), AND
  3. 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 / 100 using the canonical DAY_LENGTH constant
  • 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:

  1. Graceful dissolution from accumulated pressure (romance_dissolution_system)
  2. Satisfaction departure — partner leaves the settlement due to low satisfaction
  3. 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 endured in 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_strength 60+ 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_history and strength_min reset 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:

AxisLow (< 0.3)MidHigh (> 0.7)
BoldnessCautiousBalancedBold
PatienceImpulsiveBalancedPatient
PrideHumbleBalancedProud

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:

BoldnessPatiencePrideGestalt
> 0.5> 0.5> 0.5IronCrowned
> 0.5> 0.5<= 0.5DeepRoot
> 0.5<= 0.5> 0.5FlameTouched
> 0.5<= 0.5<= 0.5WindWalker
<= 0.5> 0.5> 0.5StillWater
<= 0.5> 0.5<= 0.5RootCalm
<= 0.5<= 0.5> 0.5ThornKeeper
<= 0.5<= 0.5<= 0.5DewLight

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:

StageConvergence RateBehavior
Young0.04 (4%)Visibly shifts within a few days of a scar
Prime0.02 (2%)Baseline; noticeable shift over ~10 days
Mature0.01 (1%)Slow adjustment; identity is largely set
Elder0.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)
InputOutputEffect
0.00.0Extreme low preserved
0.250.125Middle compressed
0.50.5Neutral unchanged
0.750.875Middle compressed
1.01.0Extreme 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.

CrisisTriggerDurationLeaves Scar?
EgoWoundargument AND pride > 0.85 AND morale < 40150 ticksYes — EgoWound scar
Withdrawalboldness < 0.15 AND company > 80200 ticksNo
RecklessAbandonpatience < 0.15 AND stalled aspiration100 ticksNo
LongPatienceBrokenargument AND patience > 0.85 (10% chance)50 ticksNo

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:

PersonalityBonusCondition
Bold (> 0.7)+0.05 per friend above 3Rewards social boldness
Cautious (< 0.3)+0.05 per solitude inspirationRewards introspection
Patient (> 0.7)+0.1 per completed aspirationRewards long-term goals
Impulsive (< 0.3)+0.05 per compositionRewards prolific creation
Proud (> 0.7)prestige_tier x 0.03Rewards 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

SystemBoldness EffectPatience EffectPride Effect
Romance warmthx1.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 bondingVia Social axis----
Crisis: Withdrawal< 0.15 triggers----
Crisis: Reckless Abandon--< 0.15 triggers--

Interactions

Departure Risk

Scars push elves toward departure through two channels:

  1. Direct satisfaction penalty: -1.5 per scar
  2. 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 Heartbreak property (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 LoveSong property

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:

  1. A permanent shift to root personality values. Root values feed expressed values through slow maturation (see personality), so the change is visible but gradual.
  2. 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.

ScarRoot ShiftNarrative TemplateSource System
Heartbreakboldness -0.05"Scarred by the silence of [partner]"romance dissolution
EgoWoundpride +0.03, patience -0.05"[elf] bears the mark of wounded pride"personality crisis (EgoWound expiry)
Betrayalpatience -0.05, boldness -0.03"Trust broken by [partner]"romance dissolution (betrayal branch)
Unrequitedboldness -0.03, pride +0.02"Love unmet by [target]"romance courtship (one-sided resolution)
PartnerLossboldness -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.0 AND 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) where third_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 >= 200 AND 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); Mourning attached 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:

  1. Conflict system (every 10 ticks): when two elves argue, if one has pride > 0.85 AND morale < 40 AND no existing EgoWound, they gain an EgoWound crisis marker with 150 ticks remaining and a -8 "Wounded pride" mood.
  2. 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.

MarkerTriggerDurationMood While ActiveScar on Expiry
EgoWoundArgument AND pride > 0.85 AND morale < 40150 ticks-8 "Wounded pride"Yes — EgoWound scar
Withdrawalboldness < 0.15 AND company > 80200 ticks-3 "In withdrawal"No
RecklessAbandonpatience < 0.15 AND stalled aspiration100 ticks+2 "Reckless abandon"No
LongPatienceBrokenArgument AND patience > 0.85, 10% chance50 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_label verbatim. 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:

  1. 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.
  2. 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 Heartbreak composition 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:

  1. Loss — a close friend departs, a partner leaves, or a bonded tree dies.
  2. Mourning — the bereaved elf gains a Mourning component with a ticks_remaining countdown and a "Grieving" mood modifier. They prefer composing over other tasks.
  3. Tribute — if a workshop is available, the mourner composes. Any composition created while Mourning is present gains the Elegy property (+15 emotional). A TributeComposed event fires.
  4. Resolution — when ticks_remaining hits zero, grief_system removes the Mourning component and clears per-episode dedup keys in PendingOriginWrites. 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 TypeSource SystemMourning DurationConditions
Friend departs in dissatisfactionsatisfaction_system200 ticksFriend relationship strength ≥ 60. Skipped if already mourning.
Friend departs in fulfillmentfarewell_departure_system150 ticksFriend strength ≥ 60. Bittersweet farewell — shorter than dissatisfied departure. Also Partner.
Partner departssatisfaction_system300 ticksPartnership cleared, PartnerLoss scar applied. Extends existing mourning by +100 if already mourning.
Bonded tree diestree_death_grief (flora_systems.rs)300 ticksOnly 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:

TraditionOutcome
≥ 0.3Full mourning: Mourning component (300 ticks, with departed_tree set), -8 "Bonded tree lost" mood for 300 ticks.
< 0.3Refusal: -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 Relationships toward 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:

  1. Expire: collect Mournings where ticks_remaining == 0, capture (entity, elf_name, departed_friend), then remove the component and clear matching SharedCrisis dedup entries in PendingOriginWrites so the partnership-origin bookkeeping doesn't grow unbounded.
  2. Decay: for every remaining Mourning, decrement ticks_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:

EventFires WhenPayloadDisplay Example
MourningBeganFriend (str ≥ 60) departs in dissatisfaction{ elf, departed }"Lyra withdraws into grief for Elara" (one of three rotated phrasings)
TributeComposedMourning elf finishes any composition{ composer, departed, composition_name }"Lyra completes Quiet Hour — a tribute to Elara"
BondedTreeDiedNotable tree with bond dies{ elf, tree_name }"The Old Oak falls. Mira's bond breaks."
ElegyRefusedLow-tradition (< 0.3) elf bonded to a dying notable tree{ elf, tree_name }"Mira refuses the elegy for The Old Oak"
RitesDisrespectedHigh-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 to Scars
  • Personality::apply_scar(PartnerLoss) permanently shifts root personality (see Scars)
  • Mourning duration: 300 ticks (vs. 200 for a close friend) — or current ticks_remaining + 100 if already mourning
  • ScarFormed event fires alongside any MourningBegan

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:

  1. Critical needs (sustenance < 20, rest < 20)
  2. Discontented elves — only basic survival
  3. Mourning elves — compose if workshop exists, else wander
  4. Creative block — skip composing
  5. Aspiration goals
  6. 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 TributeComposed event, 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 a TreeBond to 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 tradition values (some elves below 0.3, some above 0.7) is one notable-tree death away from a RitesDisrespected cascade — 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 PartnerLoss scar 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 bonus composition 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:

StageTicks LivedGame Days
Young0 -- 4990 -- 4
Prime500 -- 1,9995 -- 19
Mature2,000 -- 3,99920 -- 39
Elder4,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) -- emits StageTransition events when the cached stage differs from the current one.
  • elder_fulfillment_system (every 50 ticks) -- tracks how long each elder has been satisfied; marks them Fulfilled after 500 ticks above satisfaction 70.
  • farewell_departure_system (every tick) -- processes elders marked AwaitingFarewell, spawning a LegacyFigure record 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.

MultiplierYoungPrimeMatureElderReads Applied In
Skill XP1.3x1.0x0.7x0.5xcompose, build, gather XP awards
Composition quality (mastery + originality)0.8x1.2x1.0x1.1xcompose system
Physical (work progress)1.0x1.0x0.8x0.5xbuild, gather task progress
Influence (prestige weight in satisfaction)0.7x1.0x1.3x1.5xsatisfaction 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:

StageMaturation Rate (per 50-tick cycle)
Young0.04 (4%)
Prime0.02 (2%)
Mature0.01 (1%)
Elder0.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 FulfillmentProgress to 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:

  1. Build a LegacyFigure record from the elder's portfolio, prestige, love reactions, and revels performed.
  2. Emit a FulfilledDeparture { elf, legacy_score } event (notable priority).
  3. Apply the fulfilled-departure mood cascade to every elf in the elder's relationship list.
  4. Apply any apprenticeship cleanup (see apprenticeship).
  5. 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 ElderMood ModifierDuration
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:

FieldSource
nameElder's display name
gestaltPersonality gestalt at departure (e.g., DeepRoot, WildFlame)
composition_countPortfolio size at departure
legacy_scorecomposition_count * 2 + love_reactions * 3 + revels_performed + prestige_tier * 5
departure_dayGame day of departure (tick / 100)
departure_reasonDepartureReason::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 Fulfilled status 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.

ComponentWeightRangeMax Contribution
Inspiration totalx 0.30--10030.0
Morale (base 50 + mood modifiers)x 0.20--10020.0
Friend countx 5.00--20100.0 (capped by max relationships)
Aesthetic fit (1 - distance/2)x 20.00.0--1.020.0
Last revel avg scorex 0.10--10010.0
Prestige bonusx 0.15 of prestige score0--10015.0
Personality bonusarchetype-dependentvaries~10
Romance bonuspartner/courting dependentvaries~10
Scar penalty-1.5 per active scar0--∞unbounded
Overcrowding penalty(pop / max(dwellings*3, 8) - 1) * 15>= 0unbounded
Satisfaction spike (revel bonuses)+directvariesconsumed 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 LevelBase Departure Threshold
1--315.0
4--620.0
7--925.0
1030.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):

PhaseTick Range Below ThresholdWhat Happens
Silent0 -- 299Counter accumulates. No visible indicator.
Discontented300 -- 799Warning event fires. Discontented marker added. UI shows warning.
Departure800+Elf is permanently despawned from the world.
  • At 300 ticks: a LeaveWarning event fires. The Discontented component is added to the elf. The event reports how many ticks remain before departure.
  • At 800 ticks: the elf departs. A ElfDeparted event 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:

  1. The Discontented marker is removed.
  2. The ticks-below-threshold counter resets to 0.
  3. The elf's departure threshold permanently increases by 5.0. This means each near-departure makes the elf harder to keep next time.
  4. The elf receives a mood modifier: "Reconsidered leaving" +8 for 300 ticks.
  5. 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:

  1. Elder with satisfaction > 70 accumulates FulfillmentProgress.ticks_above at +50 per 50-tick cycle.
  2. Any dip to satisfaction <= 70 resets the counter to zero. Sustained, not average, happiness is required.
  3. At 500 accumulated ticks, the elder is tagged Fulfilled and ElderFulfilled fires.
  4. The next revel attended by a Fulfilled elder tags them AwaitingFarewell.
  5. The following tick, farewell_departure_system despawns them and records a LegacyFigure.

Fulfilled departure mood cascade (softer than dissatisfied):

Relationship to Departed ElderMood ModifierDuration
Partner-8300 ticks
Friend-5200 ticks
Rival+250 ticks
Acquaintance-150 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

ScenarioInspirationMoraleFriendsAes. FitRevelPrestigeTotal
Happy elf60 (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 elf20 (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 butterfly40 (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 master80 (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 CountThreshold (Skill 1-3)Threshold (Skill 4-6)Threshold (Skill 10)
0 (never recovered)15.020.030.0
120.025.035.0
225.030.040.0
330.035.045.0

Timing Summary

EventTicks
Satisfaction recomputedEvery 10 ticks
Silent countdown phase0 -- 299 ticks below threshold
Warning fires (Discontented)300 ticks below threshold
Negotiation window300 -- 799 ticks (500-tick window)
Departure800 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:

RelationshipMood EffectDuration
Friend"Friend departed" -10300 ticks
Rival"Rival departed" +3100 ticks
Acquaintance"Acquaintance departed" -2100 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 LeaveWarning events. 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:

  1. 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).
  2. 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).
  3. 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:

TierScore RangeStarsDescription
Unknown0 -- 15(none)No reputation. Default for new elves and those who have never performed.
Emerging16 -- 351Starting to get noticed. A few Love reactions or a small fan base.
Established36 -- 552Recognized artist. Consistent revel performer with a growing portfolio.
Renowned56 -- 803Major cultural figure. Large fan following and strong revel history.
Legendary81 -- 1004Settlement-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:

FieldTypeDescription
elfStringName of the elf whose tier changed
from_tierStringPrevious tier label
to_tierStringNew tier label
scoref32Current 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

InputWeightTypical RangeMax Contribution
Recent Love reactions (last 5 revels)x 5.00 -- 20~100 (but clamped)
Fan count (colony-wide)x 10.00 -- 10~100 (but clamped)
Portfolio compositionsx 2.00 -- 50~100 (but clamped)
Decay (no Love in 200 ticks)-1.0 per cycle0 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

ScenarioLovesFansCompsRaw ScoreDecay?Final
First-time performer20112.0No12.0 (Unknown)
Solid contributor42550.0No50.0 (Established)
Fan favorite64376.0No76.0 (Renowned)
Retired legend031560.0Yes (-1)59.0 (Renowned)
Fading star (no recent work)01316.0Yes (-1)15.0 (Unknown)

Timing Summary

EventInterval
Prestige recalculatedEvery 50 ticks
"Recent" Love windowLast 5 completed revels
Decay grace period200 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.

TriggerEffect
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 YIncrements love_count (saturating at u8 max), no event
Love from the composer for their own workIgnored -- 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:

EffectTargetMagnitudeSaturation
Social inspiration boostComposer+2Saturating add (caps at u8 max)
Relationship strengthenFan's record of composer+1 strengthClamped in Relationships::adjust
Fan requestCultural eventFanRequest { 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

ConstantValueRole
MAX_FANDOMS5Per-elf fandom roster cap
Fandom tick cadenceevery 50 ticks (skipping 0)Proximity scan and effect application
Proximity thresholdManhattan distance ≤ 4Required for effects to apply
Social inspiration boost+2 per in-range fan per cycleApplied to composer Inspiration.social (saturating)
Relationship boost+1 per in-range fandom per cycleApplied to fan's Relationships entry for composer
Fan request cadenceevery 500 ticksFanRequest 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.
  • Revels -- where fandoms are formed. Love reactions during performances create Fandom entries and FandomFormed events.
  • 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:

GoalTracked StatTarget FormulaMax Target
MasterSkillSkill level (currently Music only)current_level + 210
MakeFriendsCount of Friends (strength >= 50)current_friends + 26
ComposeWorksNumber of completed compositionscurrent_compositions + 320
PerformAtRevelsRevel performances attendedcurrent_performances + 320
EarnLove"Love" audience reactions received at revelscurrent_love_reactions + 210

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:

GoalBase WeightAesthetic BonusWeight Formula
MasterSkill (Music)1.0Emotion axis1.0 + emotion
MakeFriends1.0Social axis (x2)1.0 + social * 2.0
ComposeWorks1.0Structure axis1.0 + structure
PerformAtRevels0.8Social axis0.8 + social
EarnLove0.8Emotion 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:

  1. The aspiration status changes from Active to Completed.
  2. A CulturalEvent::AspirationCompleted event fires (priority: Notable).
  3. The elf receives a mood modifier: "Fulfilled an aspiration!", value +8, duration 200 ticks.
  4. 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:

  1. A CulturalEvent::AspirationStalled event fires (priority: Notable).
  2. The elf receives a mood modifier: "Aspiration stalled", value -2, duration 100 ticks.
  3. The stall timer resets (last_progress_tick updated 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

ConstantValueDescription
Assignment interval50 ticksHow often new aspirations are assigned
Progress check interval10 ticksHow often progress is re-evaluated
Stall threshold500 ticksNo-progress duration before stall fires
Completion mood duration200 ticksHow long the +8 celebration mood lasts
Stall mood duration100 ticksHow long the -2 stall penalty lasts

Mood Effects

EventMood ValueDurationMethod
Aspiration fulfilled+8200 tickspush (stacks with other modifiers)
Aspiration stalled-2100 ticksreplace (refreshes if already present)

Weight Summary

GoalAxisMin WeightMax WeightPersonality That Favors It
MasterSkillEmotion1.02.0Emotional elves (emotion near 1.0)
MakeFriendsSocial1.03.0Social elves (social near 1.0)
ComposeWorksStructure1.02.0Structured elves (structure near 1.0)
PerformAtRevelsSocial0.81.8Social elves (social near 1.0)
EarnLoveEmotion0.82.3Emotional 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_compose flag activates. If a Workshop is available, the elf walks there and composes instead of waiting for a role assignment.
  • MakeFriends: the elf's wants_socialize flag 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.

  • 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:

ChannelTriggerFrequencyPersonality
ReputationColony reputation scorePeriodic (interval scales with reputation)Aesthetically aligned
Cultural EventGreat revel (avg score >= 75)Burst of 1-2 elvesDrawn by the art
WandererRandom interval~500 ticksUnpredictable 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 scores
  • portfolio_count: total number of compositions in the settlement
  • recent_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 ticks
  • reputation_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 WandererRejected event
  • If aesthetic distance <= threshold: the elf settles, generating a WandererSettled or ElfArrived event
ChannelThresholdPractical Effect
Reputation2.0Always passes (max possible distance is 2.0)
Cultural Event2.0Always passes
Wanderer1.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:

FamilyWeight FormulaAesthetic Lean
Silvani1.0 + (1.0 - tradition) x 0.5Low tradition (experimental)
Valdari1.0 + tradition x structure x 1.5High tradition + structure
Nelvari1.0 + emotion x (1.0 - social) x 1.5Emotional + personal
Aetheri1.0 + (1.0 - tradition) x social x 1.5Social + 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 ScoreInterval~Arrivals per 1,000 ticks
101,500 ticks0.67
25600 ticks1.67
50300 ticks3.33
100150 ticks6.67
250+60 ticks16.67

Plus wanderers every ~500 ticks and burst arrivals from great revels.

Satisfaction Starting Points

ChannelStarting SatisfactionDeparture Risk
Reputation60.0Low
Cultural Event60.0Low
Wanderer45.0Moderate

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:

  1. More elves produce more compositions
  2. More compositions raise portfolio_count in the reputation formula
  3. Higher reputation shortens the arrival interval
  4. 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 WandererRejected event 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 TypeQualifies?
Within 1 tile of a building (Workshop, Garden, Dwelling, etc.)Yes
On scenic terrain (Ancient Forest, Meadow)Yes
Open terrain with no buildings nearbyNo

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:

ModifierValueDurationSemantics
"Favorite spot"+115 ticksReplace (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:

BenefitAmount
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:

  1. Formation -- an elf near a notable tree begins building a bond automatically.
  2. Strengthening -- continued proximity increases bond strength over time.
  3. Loss -- when a bonded tree dies, the elf enters grief. Their response depends on their personality's tradition axis.
  4. 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

FieldTypeRangeDescription
tree_entityEntityThe bonded notable tree
tree_nameStringThe tree's notable name (e.g., "The Elder Oak of the Northern Glade")
strengthu80--100Bond strength, starting at 20
compositions_nearbyu80--255Number 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:

TraditionResponseMood PenaltyMourning ComponentDuration
>= 0.3Formal mourning with elegy-8 "Bonded tree lost"Yes: -5 "Grieving", pulled toward composing elegies (+15 emotional)300 ticks
< 0.3Elegy refusal (personal grief)-8 "Bonded tree lost"No300 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:

EffectValueDuration
Mood penalty-3 "Rites disrespected"100 ticks
Relationship damage toward refuser-10Permanent

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 ElegyRefused and RitesDisrespected events 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

SeasonDaysDay Range (in year)Description
Spring0--240--24Awakening. Balanced foraging. Spirit is sensitive.
Summer25--4925--49Peak abundance. Highest foraging multiplier.
Autumn50--7450--74Declining bounty. Snow begins to appear.
Winter75--9975--99Harsh. 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:

PeriodTick Range (within day)Temperature Modifier
Dawn0--9-5
Day10--69+0 (base)
Dusk70--84-3
Night85--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.

SeasonStart TempMid-ExtremeEnd Temp
Spring354555
Summer557565
Autumn654530
Winter301525

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

ConditionThresholdEffect
ColdTemperature < 25Outdoor mood penalty: "Chilled" -2
HotTemperature > 75Universal 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

WeatherDescriptionPossible Seasons
ClearSunny skies. No special effects.All
CloudyOvercast. No direct effects.All
RainOutdoor mood penalty.All
StormFlavor event (neutral mood).All
SnowOutdoor beauty bonus.Autumn, Winter only
FogInspiration 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).

WeatherSpringSummerAutumnWinter
Clear40604030
Cloudy25152525
Rain25102010
Storm510105
Snow00525
Fog5505

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.

SeasonModifierValueDuration
Spring"Spring optimism"+21 day (100 ticks)
Summer(none)----
Autumn"Autumn melancholy"-11 day (100 ticks)
Winter"Winter stillness"-11 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/ConditionTargetMood ValueDurationNotes
Rain (outdoors)Outdoor elves-1 "Caught in rain"50 ticksShelter negates
Snow (outdoors)Outdoor elves+2 "Snow-covered landscape"50 ticksBeauty bonus
StormAll elves0 "Sheltering from storm"50 ticksFlavor only
FogMusicians (skill > 5)+1 "Mysterious fog"50 ticksAlso +1 Nature inspiration
Cold (temp < 25, outdoors)Outdoor elves-2 "Chilled"50 ticksShelter negates
Hot (temp > 75)All elves-1 "Sweltering"50 ticksCannot 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:

SeasonMultiplierEffective Foraging
Spring1.0xBaseline
Summer1.5xPeak abundance
Autumn1.25xSlight bonus
Winter0.5xHalved

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.

SeasonBaseMultipliedRounded Yield
Spring55.05
Summer57.58
Autumn56.256
Winter52.53

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.

WeatherWater Effect
RainAdds water to all outdoor tiles (orographic: more at high elevation)
StormHeavy water addition (storm_amount = 8 vs rain_amount = 3)
SnowAdds water at half rain rate; accumulates on frozen surfaces
Clear/Cloudy/FogNo precipitation

Temperature drives freeze/thaw:

TemperatureWater Effect
Below 20Shallow water freezes (15 ice/day); deep water freezes slowly (3 ice/day)
20--30Hysteresis band -- no change
Above 30Ice 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.

SeasonAnchor EventWindow
SpringAwakening RevelDay 10--14
SummerRadiance FestivalDay 10--14
AutumnHarvest GatheringDay 10--14
WinterStillness VigilDay 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

SeasonModifierValueDuration
SpringSpring Awakening+2100 ticks (1 day)
SummerSummer Vigor+1100 ticks (1 day)
AutumnAutumn Melancholy-1100 ticks (1 day)
WinterWinter Reflection0100 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

ModifierValueDurationTrigger
First [Season]+350 ticksFirst day of each new season (day 0)
Season's End-130 ticksLast 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

ThresholdDepthEffect
Dry0No water. Normal terrain.
Subsurface1--30Wet but invisible. Contributes to soil moisture.
Visible31--80Rendered as shallow water. +1 beauty. Wading speed (2x movement cost).
Deep81--200Impassable unless frozen. +2 beauty.
Very Deep201--255Deep 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.
WeatherBase AmountHigh ElevMid ElevLow Elev
Rain36~41
Storm816~103
Snow1 (half rain)2~10

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:

SourceProduction/Day
SpringPer-spring flow_rate (default 5)
River Head20 (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:

SeasonEvaporation Rate
Summer3/day (base 1 + summer bonus 2)
Other seasons1/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 DepthIce 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

ArchetypeTrophic LevelSizeSocialSeasonalTemperament
DeerHerbivoreMediumHerd (5--10)ResidentTolerant
RabbitHerbivoreSmallHerd (5--14)ResidentShy
FoxPredatorMediumPair (2--4)ResidentCurious
WolfPredatorLargePack (5--7)ResidentShy
SongbirdPollinatorTinyFlock (5--20)MigrantTolerant
BeePollinatorTinySwarm (5--20)HibernatorTolerant
CrowScavengerSmallPair (2--4)ResidentCurious

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

PatternSpringSummerAutumnWinter
ResidentBreeding (1.5x births)NormalReduced breeding (0.6x)Minimal breeding (0.2x)
MigrantReturnNormalNormal90% depart
HibernatorNormalNormalNormalDormant (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:

  1. The elf must be near the animal during a bonding tick (every 50 ticks).
  2. Bond growth depends on the elf's Stewardship skill: base 1 + (stewardship / 3), clamped to 1--5 per tick.
  3. Curious species (Fox, Crow) can be bonded by any elf.
  4. Tolerant species (Deer, Songbird, Bee) require Stewardship >= 3.
  5. Shy species (Rabbit, Wolf) cannot be bonded.
  6. Only one elf can bond with a given animal -- the first to reach threshold wins.

Bond Strength

RangeStatusEffect
0--19WaryAnimal tolerates proximity
20--59FamiliarAnimal follows loosely
60--100CompanionAnimal 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:

SpeciesInspiration
Wolf4
Songbird4
Deer3
Fox3
Bee2
Crow2
Rabbit1

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:

CriterionDetails
Ancient growth stageThe tree has reached the Ancient stage through natural growth
Drought survivalThe tree's moisture reached 0 while at Mature stage or older
Beloved gathering place3 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:

EventTrigger
BecameNotableThe moment the tree was promoted
BondFormedAn elf formed a tree bond with this tree
SurvivedDroughtThe tree survived a period of zero moisture
ElfEventNearbyA 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:

ComponentRangeDetails
Base beauty0--4Inherent to the species, varies by composition role
Seasonal modifier-1 to +2Depends on the tree's current seasonal phase
Time-of-day bonus0 or +1+1 if current time matches the species' peak beauty phase
Aesthetic alignment-2 to +5How well the elf's aesthetic values align with the species

Total range: -2 to +12.

Base beauty by composition role:

Composition RoleBeauty RangeFamilies
Dominant2--4Heartwood, Ironbark, Windcatcher
Companion1--3Silverleaf, Needlespire, Weepwater, Thornweave
Ground0--2Mosswhisper

Seasonal modifiers:

PhaseModifierNotes
Full0Standard foliage
Budding-1Spring emergence
Fruiting+1Fruit/seed display
Shedding+2Autumn color bonus
Bare-1Leafless
Dormant-1Winter 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.

FamilyCountGrowthLeaf TypeAesthetic EmphasisNotes
Heartwood3TreeBroadleaf DeciduousStructure + TraditionCanopy shade, forageable
Ironbark3TreeBroadleaf EvergreenStructure (highest)Canopy shade
Windcatcher3TreeBroadleaf DeciduousEmotion + SocialForageable
Silverleaf2TreeBroadleaf DeciduousEmotion (personal)
Needlespire2TreeNeedleStructure + TraditionCanopy shade
Weepwater2TreeBroadleaf DeciduousEmotion (highest)Forageable
Thornweave2ShrubBroadleaf DeciduousSocial (communal)Forageable
Mosswhisper2ShrubBroadleaf EvergreenTradition + 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:

StateMeter RangeDescription
Harmony0--20Peaceful coexistence. No negative effects.
Tension21--50The forest stirs. Alert appears in the UI.
Displeasure51--75Active resistance. The settlement feels the pressure.
Anger76--100The 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:

SeasonAnger 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.

GardensTotal 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

ActionEffectWhen
Clear YoungForest tile (Spring)+7On task completion
Clear YoungForest tile (other seasons)+5On task completion
Natural dawn decay-1Every dawn
Per Garden at dawn-2Every dawn
Meter minimum0Saturating subtraction
Meter maximum100Capped at 100

Days to Reach Key Thresholds (No Gardens)

Starting from Harmony (meter 0), clearing one tile per day:

Target StateMeter RequiredDays (non-Spring)Days (Spring)
Tension216 days (6 clears x net +4 = 24)4 days (4 x net +6 = 24)
Displeasure5113 days9 days
Anger7619 days13 days

Days to Calm from Key States (No Clearing)

With various garden counts, days to return from Anger (meter 76) to Harmony (meter 20):

GardensDawn DecayDays to Drop 56 Points
0-1/day56 days
1-3/day19 days
2-5/day12 days
3-7/day8 days

Clearing Progress Rate

The ClearForest task progresses at a rate of 5 + building_skill per tick, completing at 100:

Building SkillProgress/TickTicks to Complete
1617 ticks
3813 ticks
51010 ticks
10157 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:

  1. Spring penalty: Clearing in Spring costs +7 instead of +5. Plan heavy expansion for Summer or Autumn.
  2. 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:

  1. Clear existing zones -- zones are recalculated from scratch each time.
  2. Collect all buildings -- every entity with a Position and Building component.
  3. 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.
  4. Classify clusters -- only clusters with 2 or more buildings become zones. Single buildings are ignored.
  5. 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 TypeClassification RuleTypical Use
CommonsContains a Feast HallSocial gathering area
Art QuarterContains both a Workshop AND a GardenCreative district
ResidentialContains 2+ Dwellings (no Feast Hall, not Workshop+Garden)Living quarters
MixedEverything else with 2+ buildingsGeneral-purpose area

The rules are evaluated in priority order:

  1. If the cluster contains a Feast Hall --> Commons (regardless of other buildings).
  2. Else if the cluster contains both a Workshop and a Garden --> Art Quarter.
  3. Else if the cluster contains 2 or more Dwellings --> Residential.
  4. 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:

BuildingZone InfluenceOther Effects
DwellingResidential (if 2+)Shelter from weather. Faster rest recovery.
WorkshopArt Quarter (with Garden)Composing station. Stimulation boost.
GardenArt Quarter (with Workshop)Beauty +3/tick. Spirit calming -2/dawn.
Feast HallCommons (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 CenterPrefix
AncientForest"The Groveward"
YoungForest"The Verdant"
Meadow"The Sunlit"
Stone"The Hearthstone"
Other"The Central"

Classification suffix:

Zone TypeSuffix
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:

FieldTypeDescription
centerPositionArithmetic mean of all building positions in the cluster
radiusu8Always 4 (the cluster detection radius)
kindZoneKindArt Quarter, Residential, Commons, or Mixed
nameStringAuto-generated or patron-overridden name
building_countu32Number 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
NoYes(any)Art Quarter
NoNoYesResidential
NoNoNoMixed

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:

  1. Patron (you) -- provides taste, values, and broad artistic direction via ArtisticDirection settings and direct messages.
  2. Curator (AI advisor) -- reads the settlement state and issues CulturalCommands. Two implementations: a rule-based DummyCurator (always available) and an LLM-backed LlmCurator (native only, uses claude CLI).
  3. 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:

MethodPurpose
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:

EventCondition
RevelEndedAlways
ArtCompletedAlways
InspirationCrisisAlways
ResourceLowWhen amount < 5
SpiritStateChangedWhen 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:

CommandFieldsEffect
AssignRolename: String, role: ElfRoleSets an elf's workshop assignment. Roles: Gatherer, Builder, Composer, Unassigned
SetResourcePriorityVec<ResourceType>Reorders the settlement's gathering priority. Types: Food, Wood, Stone, FineWood
QueueBuildkind: BuildingTypeAdds a building to the construction queue. Types: Dwelling, Garden, Workshop, FeastHall
ClearForestposition: PositionAssigns 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
Messagetext: StringAdds curator commentary to the UI event log -- no gameplay effect

Command Dispatch

Commands are applied via apply_commands(world, commands, curator). Each command:

  1. Mutates the GameWorld directly (e.g., inserting into policies.workshop_assignments, pushing to policies.build_queue).
  2. Returns a CulturalEvent for 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 YoungForest tile, which gets cleared first (assigns an idle builder to ClearForest task). The DummyCurator limits this to one clearing per consultation via its cleared_today flag.

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:

SectionData
HeaderDay, tick, season, day-in-season, year, weather, temperature
ResourcesName, current amount, daily rate -- for Food, Wood, Stone, FineWood
ElvesName, 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 centerSettlement-wide average on four axes: structure, tradition, emotion, social
CompositionsName, composer, genre, quality tier, mastery/originality/emotional scores, aesthetic position, properties, patron-favorite flag. Capped at 15 most recent
Revel stateCurrent phase (None / Gathering / Performing / Aftermath) plus history of last 5 revels with highlight composition and average score
BuildingsCount by type (Dwelling, Garden, Workshop, FeastHall) plus build queue size
SpiritState label and meter value (0-100)
Current policiesRole 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.
  • Message commands are free -- they have no gameplay cost. The LLM curator uses them to explain its reasoning in the event log.
  • The QueueBuild command 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 claude is not found, it permanently falls back to the DummyCurator for 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

FieldTypePurpose
bootstrappedboolWhether initial role assignment has run (once per game)
clearings_since_gardenu32Forest tiles cleared since the last garden was built; triggers garden construction at 2
cleared_todayboolLimits 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:

  1. Query all elves with their gathering skill value.
  2. Sort by gathering skill, descending (best gatherer first).
  3. Assign roles by index:
IndexRoleRationale
0 (best gatherer)GathererMost efficient food/resource collector
1-2BuilderConstruction workforce
3+GathererRemaining 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.

ConditionPriority Order
Food < 20Food > Wood > Stone
Food >= 20Wood > 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:

PriorityBuildingConditionPurpose
1Dwellingdwelling_count < 3 AND wood >= 10Elf rest and shelter
2Gardengarden_count == 0 AND wood >= 5Spirit appeasement
3Workshopworkshop_count == 0 AND wood >= 10 AND stone >= 10Enables composing
4FeastHallfeast_hall_count == 0 AND wood >= 15 AND stone >= 5Enables 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:

ConditionCheck
No revel activerevel_state == RevelState::None
Feast hall existsAt least one FeastHall building
Compositions existworld.compositions is non-empty
Enough elves availableAt least 5 elves without CreativeBlock
Sufficient foodFood >= 5 normally; food >= 15 in Winter
Cooldown elapsedEither 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):

  1. Find the elf with the highest music skill.
  2. If no elf currently holds the Composer role, assign the best musician as Composer.
  3. 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

ResourceThresholdEffect
Food< 20Priority shifts to Food-first
Wood>= 5Can build Garden
Wood>= 10Can build Dwelling or Workshop
Wood>= 15Can build FeastHall
Stone>= 5Can build FeastHall
Stone>= 10Can build Workshop

Building Material Costs (Required to Queue)

BuildingWoodStone
Dwelling10--
Garden5--
Workshop1010
FeastHall155

Revel Preconditions

ParameterValue
Minimum elves (no CreativeBlock)5
Food (non-winter)>= 5
Food (winter)>= 15
Cooldown between revels400 ticks (4 days)
Required buildingFeastHall
Required contentAt least 1 composition

Spirit Management

ParameterValue
Clearings before forced garden2
Max clearings per consultation1 (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_garden counter persists across days and is tracked via the on_forest_cleared callback. Both direct ClearForest commands 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

  1. Install the claude CLI and ensure it is on your PATH. The curator spawns it as a subprocess.
  2. The default model is haiku (fast, cheap). The model name is stored in self.model and 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

TargetBehavior
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_fallback is set to true.

Budget and Rate Limiting

The LLM curator enforces two limits to control API costs:

ParameterValueDescription
max_per_day5Maximum LLM consultations per game day
min_consult_gap50 ticksMinimum 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:

TriggerCondition
Day boundarytick / 100 changes (same as dummy)
Pending resultBackground thread has a result ready -- always process immediately
Revel endedRevelEnded event
Art completedArtCompleted event
Inspiration crisisInspirationCrisis event
Resource criticalResourceLow event with amount < 5
Spirit angerSpiritStateChanged event where new state is "Anger"

Player Messages

When called with consult_with_message, the LLM curator:

  1. Checks for a pending background result first (returns it if available).
  2. Appends the player's message to the system prompt as a "Patron's Direct Message" section.
  3. Sends the request to the background thread.
  4. 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

ActionFieldsValid Values
assign_rolename, rolerole: "Gatherer", "Builder", "Composer", "Unassigned"
set_resource_prioritypriority (array)"Food", "Wood", "Stone", "FineWood"
queue_buildkind"Dwelling", "Garden", "Workshop", "FeastHall"
clear_forestx, y (integers)Map coordinates
schedule_revel(none)
messagetextFree-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:

SectionContent
Aesthetic AxesExplains the four-axis model (structure, tradition, emotion, social) and how aesthetic distance drives departure
Patron's DirectionInserts the current ArtisticDirection value (Balanced / FavorMastery / FavorOriginality / FavorEmotion)
ToolsDocuments each command with usage guidance
Critical AwarenessLists 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 CulturalAdvisor trait.
  • 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 reasoning field, 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_gap of 50 ticks prevents rapid-fire requests even when many events trigger in quick succession.
  • Token costs are tracked in total_input_tokens and total_output_tokens on 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 claude is 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

SeasonDaysForagingRegenCharacter
Spring (Awakening)0-24×1.0+2/dawnRenewal. Spirit heals fastest.
Summer (Radiance)25-49×1.5+1/dawnAbundance. Best foraging season.
Autumn (Fading)50-74×1.25+1/dawnGood foraging still. Peak beauty terrain.
Winter (Stillness)75-99×0.5Skip odd daysScarcity. 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:

SeasonGenre Tendency
SpringLeans Traditional
SummerFavors Radical
AutumnMost varied
WinterFavors 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

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

ActionAnger AddedNotes
Clear Young Forest+5Standard penalty
Clear Ancient Forest+5Same penalty, but you lose more beauty
Clear any forest in Spring+7Spring penalty is higher (forest is awakening)

Calming Sources

SourceAnger RemovedWhen
Natural dawn decay-1Every dawn (except Winter)
Natural dawn decay (Spring)-2Spring doubles natural healing
Garden-2 per gardenEvery 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:

GardensDays to Harmony (from 76)Season Assumed
056 daysNon-Winter
126 daysNon-Winter
215 daysNon-Winter
310 daysNon-Winter
0Never (in Winter)Winter
219 daysWinter

State Consequences

StateMeterEffect
Harmony0-20No effects. All is well.
Tension21-50Warning events in the event log.
Displeasure51-75Terrain effects, reduced resource yields.
Anger76+Active interference with the settlement.

Interactions

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:

  1. Check their needs — address the most critical deficit
  2. Schedule a revel — the satisfaction spike can buy time
  3. Assign them near friends — the Company need matters
  4. You have 500 ticks after the warning before departure

Interactions

Role Allocation

Choosing the right role for each elf is a balancing act between resource production, artistic output, and construction progress.

The Four Roles

RolePrimary OutputSkill Used
GathererWood, Stone, Food, FineWoodGathering
BuilderBuilding construction progressBuilding
ComposerMusical compositionsMusic
UnassignedIdle — forages, socializes, restsNone 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