proof_engine/narrative/
drama.rs1#[derive(Debug, Clone)]
5pub struct DramaState {
6 pub tension: f32,
7 pub target_tension: f32,
8 pub arc_position: f32,
9 pub beats_since_action: u32,
10 pub beats_since_quiet: u32,
11}
12
13impl DramaState {
14 pub fn new() -> Self {
15 Self { tension: 0.2, target_tension: 0.2, arc_position: 0.0, beats_since_action: 0, beats_since_quiet: 0 }
16 }
17
18 pub fn tick(&mut self, dt: f32) {
20 self.arc_position = (self.arc_position + dt * 0.01).min(1.0);
21 self.target_tension = narrative_arc(self.arc_position);
22 let rate = if self.tension < self.target_tension { 0.3 } else { 0.15 };
23 self.tension += (self.target_tension - self.tension) * rate * dt;
24 self.tension = self.tension.clamp(0.0, 1.0);
25 }
26
27 pub fn needs_action(&self) -> bool {
29 self.beats_since_action > 5 && self.tension < self.target_tension
30 }
31
32 pub fn needs_quiet(&self) -> bool {
34 self.beats_since_quiet > 3 && self.tension > self.target_tension + 0.2
35 }
36
37 pub fn on_action(&mut self) { self.beats_since_action = 0; self.beats_since_quiet += 1; }
38 pub fn on_quiet(&mut self) { self.beats_since_quiet = 0; self.beats_since_action += 1; }
39}
40
41fn narrative_arc(t: f32) -> f32 {
43 if t < 0.15 { t * 2.0 }
44 else if t < 0.6 { 0.3 + (t - 0.15) * 1.2 }
45 else if t < 0.75 { 0.84 + (t - 0.6) * 1.0 }
46 else { 1.0 - (t - 0.75) * 3.0 }
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52
53 #[test]
54 fn test_arc_shape() {
55 assert!(narrative_arc(0.0) < narrative_arc(0.5));
56 assert!(narrative_arc(0.7) > narrative_arc(0.0));
57 assert!(narrative_arc(1.0) < narrative_arc(0.7));
58 }
59
60 #[test]
61 fn test_drama_tick() {
62 let mut d = DramaState::new();
63 for _ in 0..100 { d.tick(1.0); }
64 assert!(d.tension > 0.0);
65 }
66}