Skip to main content

proof_engine/curves/
curve_anim.rs

1//! Animation state machine for curve entities.
2
3use glam::Vec2;
4use super::entity_curves::CurveEntity;
5
6/// Animation states a curve entity can be in.
7#[derive(Debug, Clone, Copy, PartialEq)]
8pub enum CurveAnimState {
9    Idle,
10    Cast { duration: f32 },
11    Hit { direction: Vec2, magnitude: f32 },
12    Defend,
13    LowHP,
14    Channel { duration: f32 },
15    Dissolving,
16}
17
18/// Manages animation state transitions with blending.
19#[derive(Debug, Clone)]
20pub struct CurveAnimator {
21    pub current: CurveAnimState,
22    pub previous: CurveAnimState,
23    pub blend: f32,
24    pub blend_speed: f32,
25    pub state_time: f32,
26}
27
28impl CurveAnimator {
29    pub fn new() -> Self {
30        Self { current: CurveAnimState::Idle, previous: CurveAnimState::Idle, blend: 1.0, blend_speed: 5.0, state_time: 0.0 }
31    }
32
33    /// Transition to a new state.
34    pub fn transition(&mut self, new_state: CurveAnimState) {
35        if self.current != new_state {
36            self.previous = self.current;
37            self.current = new_state;
38            self.blend = 0.0;
39            self.state_time = 0.0;
40        }
41    }
42
43    /// Update the animator and apply to entity.
44    pub fn update(&mut self, entity: &mut CurveEntity, dt: f32) {
45        self.state_time += dt;
46        self.blend = (self.blend + self.blend_speed * dt).min(1.0);
47
48        // Auto-transitions
49        match self.current {
50            CurveAnimState::Cast { duration } => {
51                if self.state_time > duration { self.transition(CurveAnimState::Idle); }
52            }
53            CurveAnimState::Hit { direction, magnitude } => {
54                // Apply recoil at start
55                if self.state_time < dt * 2.0 {
56                    entity.apply_hit(direction, magnitude);
57                }
58                if self.state_time > 0.6 { self.transition(CurveAnimState::Idle); }
59            }
60            CurveAnimState::Channel { duration } => {
61                // Add tangential velocity while channeling
62                for curve in &mut entity.curves {
63                    for (i, vel) in curve.point_velocities.iter_mut().enumerate() {
64                        let pt = curve.control_points[i];
65                        let tangent = Vec2::new(-pt.y, pt.x).normalize_or_zero();
66                        *vel += tangent * 0.5 * dt;
67                    }
68                }
69                if self.state_time > duration { self.transition(CurveAnimState::Idle); }
70            }
71            CurveAnimState::Defend => {
72                // Stiffness is handled externally via brace/unbrace
73            }
74            CurveAnimState::LowHP => {
75                // Entity.tick() already adds noise based on hp_ratio
76            }
77            CurveAnimState::Dissolving => {
78                // Entity.tick() handles dissolution
79            }
80            CurveAnimState::Idle => {}
81        }
82
83        // Breathing intensity varies by state
84        entity.breath_amplitude = match self.current {
85            CurveAnimState::Idle => 0.03,
86            CurveAnimState::Cast { .. } => 0.06,
87            CurveAnimState::Hit { .. } => 0.01,
88            CurveAnimState::Defend => 0.01,
89            CurveAnimState::LowHP => 0.05,
90            CurveAnimState::Channel { .. } => 0.04,
91            CurveAnimState::Dissolving => 0.0,
92        };
93    }
94
95    pub fn is_idle(&self) -> bool { self.current == CurveAnimState::Idle }
96    pub fn is_blending(&self) -> bool { self.blend < 1.0 }
97}