Skip to main content

narrative_engine/schema/
event.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4use super::entity::{EntityId, Value};
5use super::narrative_fn::NarrativeFunction;
6
7/// The emotional tone of an event.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9pub enum Mood {
10    Neutral,
11    Tense,
12    Warm,
13    Dread,
14    Euphoric,
15    Somber,
16    Chaotic,
17    Intimate,
18}
19
20impl Mood {
21    /// Returns the tag string for this mood (e.g., "mood:tense").
22    pub fn tag(&self) -> &'static str {
23        match self {
24            Self::Neutral => "mood:neutral",
25            Self::Tense => "mood:tense",
26            Self::Warm => "mood:warm",
27            Self::Dread => "mood:dread",
28            Self::Euphoric => "mood:euphoric",
29            Self::Somber => "mood:somber",
30            Self::Chaotic => "mood:chaotic",
31            Self::Intimate => "mood:intimate",
32        }
33    }
34}
35
36/// The level of consequences at play.
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
38pub enum Stakes {
39    Trivial,
40    Low,
41    Medium,
42    High,
43    Critical,
44}
45
46impl Stakes {
47    /// Returns the tag string for this stakes level (e.g., "stakes:high").
48    pub fn tag(&self) -> &'static str {
49        match self {
50            Self::Trivial => "stakes:trivial",
51            Self::Low => "stakes:low",
52            Self::Medium => "stakes:medium",
53            Self::High => "stakes:high",
54            Self::Critical => "stakes:critical",
55        }
56    }
57}
58
59/// The result of an event.
60#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
61pub enum Outcome {
62    Success,
63    Failure,
64    Partial,
65    Ambiguous,
66}
67
68/// A lightweight reference to an entity participating in an event.
69#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct EntityRef {
71    pub entity_id: EntityId,
72    pub role: String,
73}
74
75/// A structured record of something that happened in the game simulation.
76/// Events are the sole input to the narrative pipeline.
77#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct Event {
79    pub event_type: String,
80    pub participants: Vec<EntityRef>,
81    pub location: Option<EntityRef>,
82    pub mood: Mood,
83    pub stakes: Stakes,
84    pub outcome: Option<Outcome>,
85    pub narrative_fn: NarrativeFunction,
86    pub metadata: HashMap<String, Value>,
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn event_creation() {
95        let event = Event {
96            event_type: "accusation".to_string(),
97            participants: vec![
98                EntityRef {
99                    entity_id: EntityId(1),
100                    role: "subject".to_string(),
101                },
102                EntityRef {
103                    entity_id: EntityId(2),
104                    role: "target".to_string(),
105                },
106            ],
107            location: Some(EntityRef {
108                entity_id: EntityId(100),
109                role: "location".to_string(),
110            }),
111            mood: Mood::Tense,
112            stakes: Stakes::High,
113            outcome: None,
114            narrative_fn: NarrativeFunction::Confrontation,
115            metadata: HashMap::from([(
116                "held_item".to_string(),
117                Value::String("wine glass".to_string()),
118            )]),
119        };
120        assert_eq!(event.mood, Mood::Tense);
121        assert_eq!(event.stakes, Stakes::High);
122        assert_eq!(event.participants.len(), 2);
123        assert_eq!(event.participants[0].role, "subject");
124        assert!(event.location.is_some());
125    }
126
127    #[test]
128    fn mood_tags() {
129        assert_eq!(Mood::Tense.tag(), "mood:tense");
130        assert_eq!(Mood::Neutral.tag(), "mood:neutral");
131        assert_eq!(Mood::Dread.tag(), "mood:dread");
132        assert_eq!(Mood::Intimate.tag(), "mood:intimate");
133    }
134
135    #[test]
136    fn stakes_tags() {
137        assert_eq!(Stakes::Trivial.tag(), "stakes:trivial");
138        assert_eq!(Stakes::Critical.tag(), "stakes:critical");
139        assert_eq!(Stakes::High.tag(), "stakes:high");
140    }
141
142    #[test]
143    fn outcome_variants() {
144        assert_eq!(Outcome::Success, Outcome::Success);
145        assert_ne!(Outcome::Success, Outcome::Failure);
146    }
147
148    #[test]
149    fn entity_ref_roles() {
150        let witness = EntityRef {
151            entity_id: EntityId(3),
152            role: "witness".to_string(),
153        };
154        assert_eq!(witness.role, "witness");
155    }
156}