Skip to main content

game_toolkit_platformer/anim/
graph.rs

1//! [`Graph`]: a compiled, validated state graph: states with clips, transitions by
2//! priority, the parameter template. Build one with [`GraphSpec`](super::GraphSpec).
3
4use super::{Clip, Cond, Name, ParamId, Params};
5
6/// A state, by index into its [`Graph`].
7#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
8pub struct StateId(pub(crate) u16);
9
10/// How fast a state plays its clip.
11#[derive(Clone, Debug, PartialEq)]
12pub enum Rate<P = ParamId> {
13    /// A fixed multiplier.
14    Fixed(f32),
15    /// Scaled by a float parameter's magnitude: `|param| / per`, clamped to `min..=max`
16    /// (a run cycle that keeps pace with the speed; a climb that stops when the hero does).
17    Param {
18        /// The float parameter.
19        param: P,
20        /// The parameter value that plays at rate 1.
21        per: f32,
22        /// Slowest rate.
23        min: f32,
24        /// Fastest rate.
25        max: f32,
26    },
27}
28
29/// A state of the graph.
30#[derive(Clone, Debug, PartialEq)]
31pub struct State {
32    /// Its name.
33    pub name: Name,
34    /// What it plays.
35    pub clip: Clip,
36    /// How fast.
37    pub rate: Rate,
38    /// Seconds after entering during which only lock-breaking transitions are considered.
39    pub lock: f32,
40    /// Where to go when the clip finishes and no transition fired.
41    pub next: Option<StateId>,
42}
43
44/// Where a transition can start.
45#[derive(Clone, Debug, PartialEq)]
46pub enum Source {
47    /// From these states.
48    States(Vec<StateId>),
49    /// From any state except these.
50    Any {
51        /// States it doesn't apply to.
52        except: Vec<StateId>,
53    },
54}
55
56impl Source {
57    /// Whether the transition applies in state `s`.
58    pub fn applies(&self, s: StateId) -> bool {
59        match self {
60            Source::States(v) => v.contains(&s),
61            Source::Any { except } => !except.contains(&s),
62        }
63    }
64}
65
66/// A transition between states.
67#[derive(Clone, Debug, PartialEq)]
68pub struct Transition {
69    /// Where it can start.
70    pub from: Source,
71    /// Where it goes.
72    pub to: StateId,
73    /// When it fires.
74    pub when: Cond,
75    /// Higher goes first; ties in declaration order.
76    pub priority: i32,
77    /// Considered even while the current state is locked.
78    pub breaks_lock: bool,
79    /// May fire into the state it starts from, restarting it.
80    pub restart: bool,
81}
82
83/// A compiled state graph.
84#[derive(Clone, Debug, PartialEq)]
85pub struct Graph {
86    /// The parameters, with their initial values; every animator gets a copy.
87    pub params: Params,
88    /// The states.
89    pub states: Vec<State>,
90    /// The transitions, highest priority first.
91    pub transitions: Vec<Transition>,
92    /// The starting state.
93    pub entry: StateId,
94}
95
96impl Graph {
97    /// The state `id`.
98    pub fn state(&self, id: StateId) -> &State {
99        &self.states[id.0 as usize]
100    }
101
102    /// The state called `name`.
103    pub fn find(&self, name: &str) -> Option<StateId> {
104        self.states
105            .iter()
106            .position(|s| s.name == name)
107            .map(|i| StateId(i as u16))
108    }
109
110    /// Every state id.
111    pub fn ids(&self) -> impl Iterator<Item = StateId> {
112        (0..self.states.len()).map(|i| StateId(i as u16))
113    }
114}