feat(simulation): NPC mood state machine (#323, D-024, D-035)

8-state NPC mood FSM driven by stress, time of day, and interaction events.
Adds MoodState component, derive_mood() pure function, and update_mood() Bevy
system wired into NpcPlugin. Adds Focused as 9th Mood content tag (D-035
Sprint 8 amendment). Syncs CurrentMood for Layer 4 dialogue selection.

31 unit tests covering priority ordering, boundary conditions, and system
integration. All random values route through SimRng (D-010 determinism).
No floats. No HashMap.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-20 18:41:51 +01:00
co-authored by Claude Sonnet 4.6
parent aa0d8ced37
commit 9eba259fd5
4 changed files with 683 additions and 2 deletions
+5
View File
@@ -157,6 +157,7 @@ impl FromStr for Topic {
}
/// D-028 Layer 4: Mood tag — influences weighted selection.
/// D-035 amendment (Sprint 8): `Focused` added as 9th variant.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Mood {
Fond,
@@ -167,6 +168,9 @@ pub enum Mood {
Conflicted,
Concerned,
Relieved,
/// D-035 amendment (Sprint 8): task-focused NPC mood — used at The Terminal
/// and maintenance corridors. Maps from NpcMood::Focused.
Focused,
}
impl FromStr for Mood {
@@ -181,6 +185,7 @@ impl FromStr for Mood {
"conflicted" => Ok(Self::Conflicted),
"concerned" => Ok(Self::Concerned),
"relieved" => Ok(Self::Relieved),
"focused" => Ok(Self::Focused),
_ => Err(ParseEnumError {
kind: "Mood",
value: s.to_string(),