# Life Arcs Life arcs are state machines that model how characters or other entities evolve over time. They define discrete states and the conditions under which an entity transitions between states. Life arcs capture character development, quest progress, relationship phases, and any other finite-state processes. ## What is a Life Arc? A life arc is a finite state machine (FSM) composed of: - **States**: Discrete phases or modes (e.g., "happy", "angry", "sleeping") - **Transitions**: Conditional edges between states (e.g., "when health < 20, go to 'fleeing'") - **On-enter actions**: Field updates that occur when entering a state ``` [State A] --condition--> [State B] --condition--> [State C] | | | on enter on enter on enter (set fields) (set fields) (set fields) ``` Life arcs run continuously, evaluating transitions every tick and moving between states as conditions become true. ## Syntax ```bnf ::= "life_arc" ::= "{" * + "}" ::= "state" "{" * "}" ::= | | ::= "on" "enter" "{" + "}" ::= "on" "->" ::= "---" "---" ``` ## States A state represents a discrete phase in an entity's lifecycle. ### Basic State ```storybook life_arc SimpleArc { state idle { ---narrative The character is doing nothing, waiting for something to happen. --- } state active { ---narrative The character is engaged in their primary activity. --- } } ``` ### State Names State names are identifiers that must be unique within the life arc. Use descriptive names: - **Good**: `idle`, `combat`, `sleeping`, `enlightened` - **Avoid**: `state1`, `s`, `temp` ## On-Enter Actions When entering a state, the `on enter` block updates entity fields. ### Syntax ```storybook state state_name { on enter { EntityName.field_name: value EntityName.other_field: other_value } } ``` ### Examples ```storybook life_arc CharacterMood { state happy { on enter { Martha.emotional_state: "happy" Martha.energy: 100 } } state tired { on enter { Martha.emotional_state: "exhausted" Martha.energy: 20 } } } ``` ### Field Update Semantics - **Target**: `EntityName.field_name` must reference an existing character/entity - **Value**: Any valid [value type](./18-value-types.md) - **Effect**: Field is set to the specified value when state is entered - **Timing**: Happens immediately upon transition to the state ### Multiple Field Updates ```storybook state exhausted_baker { on enter { Martha.energy: 0.1 Martha.mood: "stressed" Martha.quality_output: 0.7 Martha.needs_break: true } ---narrative After a sixteen-hour shift during the harvest festival, Martha's hands are shaking. She knows her bread quality is suffering and reluctantly steps away from the oven. --- } ``` ## Transitions Transitions define conditional edges between states. When a transition's condition becomes true, the state machine moves to the target state. ### Syntax ```storybook state source_state { on condition_expression -> target_state on another_condition -> another_target } ``` ### Basic Transitions ```storybook life_arc Combat { state fighting { on health < 20 -> fleeing on enemy_defeated -> victorious } state fleeing { on safe_distance_reached -> idle } state victorious { on celebration_complete -> idle } state idle {} } ``` ### Expression Conditions Transitions use the full [expression language](./17-expressions.md): **Comparisons:** ```storybook on health < 20 -> low_health on distance > 100 -> far_away on count == 0 -> empty ``` **Boolean fields:** ```storybook on is_hostile -> combat on completed -> done on not ready -> waiting ``` **Logical operators:** ```storybook on health < 20 and not has_potion -> desperate on is_day or is_lit -> visible ``` **Complex conditions:** ```storybook on (health < 50 and enemy_count > 3) or surrounded -> retreat on forall e in enemies: e.defeated -> victory ``` ### Multiple Transitions A state can have multiple outgoing transitions: ```storybook state monitoring { on status is "active" -> active_state on status is "inactive" -> inactive_state on status is "error" -> error_state on shutdown_requested -> shutting_down } ``` **Evaluation order:** - Transitions are evaluated in declaration order (top to bottom) - First transition with a true condition is taken - If no conditions are true, state remains unchanged ### Self-Transitions A state can transition to itself: ```storybook state patrolling { on enemy_spotted -> combat on checkpoint_reached -> patrolling // Reset patrol state } ``` ## Complete Examples ### Elena's Career Journey ```storybook life_arc ElenaCareer { ---description Tracks Elena's progression from nervous apprentice to confident master baker. Each state represents a key phase of her career. --- state early_apprentice { on enter { Elena.skill_level: novice Elena.confidence: timid } on recipes_mastered > 5 -> growing_apprentice ---narrative Elena's hands shake as she measures flour. She checks the recipe three times before adding each ingredient. Martha patiently corrects her technique. --- } state growing_apprentice { on enter { Elena.skill_level: beginner Elena.confidence: uncertain } on recipes_mastered > 15 -> journeyman ---narrative The shaking stops. Elena can make basic breads without looking at the recipe. She still doubts herself but Martha's encouragement is taking root. --- } state journeyman { on enter { Elena.skill_level: intermediate Elena.confidence: growing Elena.can_work_independently: true } on recipes_mastered > 30 -> senior_journeyman ---narrative Elena runs the morning shift alone while Martha handles special orders. Customers start asking for "Elena's rolls." She begins experimenting with her own recipes. --- } state senior_journeyman { on enter { Elena.skill_level: advanced Elena.confidence: steady } on recipes_mastered > 50 -> master ---narrative Elena develops her signature recipe: rosemary olive bread that becomes the bakery's bestseller. She handles difficult customers with grace and trains new helpers. --- } state master { on enter { Elena.skill_level: master Elena.confidence: commanding Elena.can_teach: true } ---narrative Master Baker Elena. She has earned it. The guild acknowledges her mastery, and Martha beams with pride. Elena begins mentoring her own apprentice. --- } } ``` ### Quest Progress ```storybook life_arc HeroQuest { state not_started { on talked_to_elder -> received_quest } state received_quest { on enter { Hero.quest_active: true Hero.quest_step: 0 } on found_first_artifact -> collecting } state collecting { on enter { Hero.quest_step: 1 } on artifact_count == 3 -> returning on failed_trial -> failed } state returning { on enter { Hero.quest_step: 2 } on reached_elder -> completed } state completed { on enter { Hero.quest_active: false Hero.quest_completed: true Hero.reputation: 100 } ---narrative The hero returns triumphant, artifacts in hand. The elder bestows great rewards and the village celebrates. --- } state failed { on enter { Hero.quest_active: false Hero.quest_failed: true } on retry_accepted -> received_quest } } ``` ### Name Checker (Equality Examples) ```storybook life_arc NameCheck { state checking { on name is "Martha" -> found_martha on name is "Jane" -> found_jane } state found_martha { on ready -> checking } state found_jane { on ready -> checking } } ``` ### Status Monitor ```storybook life_arc StatusMonitor { state monitoring { on status is active -> active_state on status is inactive -> inactive_state on status is error -> error_state } state active_state { on enter { System.led_color: "green" } on status is inactive -> inactive_state on status is error -> error_state } state inactive_state { on enter { System.led_color: "yellow" } on status is active -> active_state on status is error -> error_state } state error_state { on enter { System.led_color: "red" System.alarm: true } on error_cleared -> monitoring } } ``` ### Character Mood Swings ```storybook life_arc MoodSwings { state neutral { on provoked -> angry on complimented -> happy on tired -> sleepy } state angry { on enter { Character.aggression: 0.9 Character.willingness_to_talk: 0.1 } on calmed_down -> neutral on escalated -> furious } state furious { on enter { Character.aggression: 1.0 Character.will_attack: true } on timeout_elapsed -> angry on apologized_to -> neutral } state happy { on enter { Character.aggression: 0.0 Character.willingness_to_talk: 1.0 Character.gives_discounts: true } on insulted -> neutral on bored -> neutral } state sleepy { on enter { Character.responsiveness: 0.2 } on woke_up -> neutral on fell_asleep -> sleeping } state sleeping { on enter { Character.responsiveness: 0.0 Character.is_vulnerable: true } on woke_up -> neutral } } ``` ## Execution Semantics ### Tick-Based Evaluation Life arcs evaluate transitions every "tick" (typically once per frame): 1. **Current state**: Start in the current state 2. **Evaluate transitions**: Check each outgoing transition's condition (in order) 3. **First true transition**: Take the first transition with a true condition 4. **Enter new state**: Execute the new state's `on enter` block 5. **Repeat next tick** ### Transition Priority When multiple transitions could fire, the **first one in declaration order** is taken: ```storybook state combat { on health < 10 -> desperate // Checked first on health < 50 -> defensive // Checked second on surrounded -> retreat // Checked third } ``` If health is 5, only `desperate` transition fires (even though `defensive` condition is also true). ### State Persistence The current state persists across ticks until a transition fires. ```storybook state waiting { on signal_received -> active } ``` The entity remains in `waiting` state indefinitely until `signal_received` becomes true. ### On-Enter Execution `on enter` blocks execute **once** when entering the state, not every tick: ```storybook state combat { on enter { Character.weapon_drawn: true // Runs once when entering combat } } ``` ## Validation Rules 1. **At least one state**: Life arc must contain at least one state 2. **Unique state names**: State names must be unique within the life arc 3. **Valid transitions**: Transition targets must reference defined states 4. **No orphan states**: All states should be reachable (warning, not error) 5. **Expression validity**: Transition conditions must be well-formed expressions 6. **Field targets**: On-enter field updates must reference valid entities/fields 7. **No cyclic immediate transitions**: Avoid transitions that fire immediately in a loop ## Design Patterns ### 1. Hub-and-Spoke A central "hub" state with transitions to specialized states: ```storybook life_arc HubPattern { state idle { on combat_triggered -> combat on quest_accepted -> questing on entered_shop -> shopping } state combat { on combat_ended -> idle } state questing { on quest_completed -> idle } state shopping { on left_shop -> idle } } ``` ### 2. Linear Progression States form a linear sequence (quests, tutorials): ```storybook life_arc Tutorial { state intro { on clicked_start -> movement } state movement { on moved_forward -> combat } state combat { on defeated_enemy -> inventory } state inventory { on opened_inventory -> complete } state complete {} } ``` ### 3. Cyclic States States form a cycle (day/night, seasons): ```storybook life_arc DayNightCycle { state dawn { on hour >= 8 -> day } state day { on hour >= 18 -> dusk } state dusk { on hour >= 20 -> night } state night { on hour >= 6 -> dawn } } ``` ### 4. Hierarchical (Simulated) Use multiple life arcs for hierarchical state: ```storybook // Top-level life arc life_arc CharacterState { state alive { on health <= 0 -> dead } state dead {} } // Nested life arc (only active when alive) life_arc CombatState { state idle { on enemy_nearby -> combat } state combat { on enemy_defeated -> idle } } ``` ## Best Practices ### 1. Use Descriptive State Names **Avoid:** ```storybook state s1 { ... } state s2 { ... } ``` **Prefer:** ```storybook state waiting_for_player { ... } state engaged_in_combat { ... } ``` ### 2. Add Narrative Prose Blocks ```storybook state master_baker { ---narrative Master Baker Elena. She has earned it. The guild acknowledges her mastery, and Martha beams with pride. Elena begins mentoring her own apprentice. --- } ``` ### 3. Order Transitions by Priority ```storybook state health_check { on health <= 0 -> dead // Most urgent on health < 20 -> critical // Very urgent on health < 50 -> wounded // Moderate } ``` ### 4. Avoid Orphan States Ensure all states are reachable from the initial state. **Avoid:** ```storybook life_arc Broken { state start { on ready -> middle } state middle { on done -> end } state unreachable {} // No transition leads here! state end {} } ``` ### 5. Use On-Enter for State Initialization ```storybook state combat { on enter { Character.weapon_drawn: true Character.combat_stance: "aggressive" Character.target: nearest_enemy } } ``` ## Cross-References - [Characters](./10-characters.md) - Characters can have associated life arcs - [Expression Language](./17-expressions.md) - Transition condition syntax - [Value Types](./18-value-types.md) - On-enter field value types - [Validation Rules](./19-validation.md) - Life arc validation constraints ## Related Concepts - **Finite State Machines (FSM)**: Life arcs are FSMs - **Character development**: Track character growth over time - **Quest states**: Model quest progression - **Mood systems**: Model emotional states - **Lifecycle modeling**: Birth, growth, aging, death