Needs & Mood
Overview
Every elf has five basic needs and a mood stack that together determine their morale -- the single number (0-100) that governs behavior, work speed, and departure risk. Needs decay at different rates; mood modifiers stack additively; and morale tiers have hysteresis bands to prevent flickering when an elf hovers near a boundary.
How It Works
Needs
Needs live on a 0-100 scale. Each need decays automatically every tick (or every Nth tick). When a need drops below a threshold, the elf's behavior tree redirects them toward satisfying it before doing productive work.
| Need | Initial Value | Decay Rate | Decay Frequency |
|---|---|---|---|
| Rest | 100 | -1 | Every tick |
| Sustenance | 100 | -1 | Every 4th tick |
| Beauty | 60 | -1 | Every 2nd tick |
| Stimulation | 60 | -1 | Every 2nd tick |
| Company | 50 | varies | Every 2nd tick (personality) |
(Source: src/sim/components.rs, Needs::full();
src/sim/systems.rs, rest_decay_system(), sustenance_decay_system(),
beauty_decay_system(), stimulation_decay_system(), company_system())
Need Status Labels
The game displays a named tier for each need value:
| Value Range | Label | Compact Char |
|---|---|---|
| 80-100 | Fulfilled | + |
| 50-79 | Fine | ~ |
| 20-49 | Wanting | - |
| 0-19 | Critical | ! |
(Source: src/sim/components.rs, need_label(), need_char())
Company (the 5th Need)
Company is unique: its behavior depends on the elf's Social aesthetic axis (0.0 = Personal, 1.0 = Social).
| Social Axis | Behavior | Recovery |
|---|---|---|
| > 0.7 (Social elf) | Decays -1 per cycle toward 0 | +2 when >= 2 elves within 3 tiles |
| < 0.3 (Personal elf) | Rises toward 100 (wants solitude) | -2 when alone; +1 when >= 3 within 5 tiles |
| 0.3-0.7 (Middle) | Gentle pull toward 50 | +1 if below 50; -1 if above 50 |
The proximity check uses Manhattan distance. Company ticks every 2nd tick, same as Beauty and Stimulation.
(Source: src/sim/systems.rs, company_system())
Mood Stack
Mood is a collection of active MoodModifiers. Each modifier has:
description-- human-readable label (e.g. "Ate a meal")value-- signed integer bonus/penalty (i8)ticks_remaining-- countdown to expiry
Every tick, the mood system decrements ticks_remaining by 1 and removes
expired modifiers. Modifiers stack additively.
Morale is computed as:
morale = clamp(base + sum(modifier.value), 0, 100)
where base = 50 (constant).
(Source: src/sim/components.rs, Mood::compute_morale(), Mood::net_mood())
Common Mood Modifiers
| Source | Value | Duration (ticks) |
|---|---|---|
| Ate a meal | +5 | 50 |
| Slept in dwelling | +3 | 100 |
| Slept on ground | -3 | 50 |
| Caught in rain (outdoors) | -1 | 50 |
| Snow-covered landscape | +2 | 50 |
| Awed by ancient grove | +2 | 200 |
| Grieving (mourning) | -5 | mourning duration |
| Spring optimism (seasonal) | +2 | 1 day (100 ticks) |
| Autumn melancholy (seasonal) | -1 | 1 day (100 ticks) |
| Winter stillness (seasonal) | -1 | 1 day (100 ticks) |
| Favorite spot | +1 | 15 ticks |
| Personal time | +2 | 50 ticks |
(Source: src/sim/systems.rs, eating_system(), resting_system(),
weather_mood_system(), seasonal_mood_system(), favorite_places_system(),
personal_time_system(); src/sim/components.rs, Mourning)
Replace vs Push Semantics
mood.push()-- always adds a new modifier (stacks with existing same-name entries).mood.replace()-- if a modifier with the same description exists, refreshes its value and duration; otherwise inserts. Weather modifiers use replace to avoid stacking.
(Source: src/sim/components.rs, Mood::push(), Mood::replace())
Values & Formulas
Morale Tiers
| Tier | Morale Range | Effect |
|---|---|---|
| Inspired | 80-100 | Work speed +2; gravitates toward creative work |
| Normal | 50-79 | Standard behavior |
| Stressed | 20-49 | Work speed -1 |
| Breaking | 0-19 | Refuses all non-critical work; idles |
Work Speed Modifier = match morale_state: Inspired => +2, Stressed => -1, else => 0
(Source: src/sim/systems.rs, work_speed_modifier();
src/sim/components.rs, morale_state())
Hysteresis Bands
To prevent rapid tier-flickering, transitions require crossing a threshold 3 points beyond the nominal boundary:
| Current Tier | Transition Up | Transition Down |
|---|---|---|
| Breaking | > 23 -> Stressed | -- |
| Stressed | > 53 -> Normal | < 17 -> Breaking |
| Normal | > 83 -> Inspired | < 47 -> Stressed |
| Inspired | -- | < 77 -> Normal |
If no previous tier exists (e.g. first evaluation), the nominal thresholds (80, 50, 20) are used directly.
(Source: src/sim/components.rs, morale_state_with_hysteresis())
Interactions
- Roles -- morale determines which tasks an elf will accept; Breaking-tier elves refuse all non-critical work.
- Buildings -- Dwellings speed rest recovery (+5/tick vs +2 outdoors); buildings within Manhattan distance 2 count as "shelter" for weather mood modifiers.
- Skills -- work speed modifier from morale applies to gathering and building progress rates.
- Terrain -- terrain beauty values passively restore the Beauty need.
- Resources -- eating consumes 1 Food and restores +30 Sustenance (capped at 100).
Tips
- Rest decays fastest (every tick). Prioritize building Dwellings early to keep rest recovery efficient (+5/tick indoors vs +2 outdoors).
- Sustenance decays slowest (every 4th tick). In a pinch, elves can survive a long time on foraging alone.
- Beauty and Stimulation both decay every 2nd tick starting from 60 (not 100). Build a Garden and Workshop early to prevent mid-game mood dips.
- Company is personality-driven. Social elves (Social > 0.7) need companions nearby; personal elves (Social < 0.3) want solitude. Keep an eye on the aesthetic axes of your colony members.
- Hysteresis means momentum matters. An elf who climbs to Inspired will stay there until morale drops below 77. Plan mood boosts in clusters rather than spreading them thin.
- Weather modifiers use replace semantics, so you will never see "Caught in rain" stacked five times. But one-time events (first ancient grove visit) do stack with ongoing weather effects.