game_toolkit_platformer/anim/
params.rs1use super::Name;
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
7pub struct ParamId(pub(crate) u16);
8
9#[derive(Clone, Copy, Debug, PartialEq)]
11pub enum ParamKind {
12 Bool,
14 Float,
16 Trigger {
19 ttl: f32,
21 },
22}
23
24#[derive(Clone, Debug, PartialEq)]
26struct Slot {
27 name: Name,
29 kind: ParamKind,
31 value: f32,
33}
34
35const PENDING: f32 = 1e-6;
38
39#[derive(Clone, Debug, Default, PartialEq)]
41pub struct Params {
42 slots: Vec<Slot>,
44}
45
46impl Params {
47 pub fn declare(&mut self, name: impl Into<Name>, kind: ParamKind) -> ParamId {
50 let name = name.into();
51 if let Some(id) = self.id(&name) {
52 return id;
53 }
54 self.slots.push(Slot {
55 name,
56 kind,
57 value: 0.0,
58 });
59 ParamId((self.slots.len() - 1) as u16)
60 }
61
62 pub fn id(&self, name: &str) -> Option<ParamId> {
64 self.slots
65 .iter()
66 .position(|s| s.name == name)
67 .map(|i| ParamId(i as u16))
68 }
69
70 pub fn name(&self, id: ParamId) -> &str {
72 &self.slots[id.0 as usize].name
73 }
74
75 pub fn kind(&self, id: ParamId) -> ParamKind {
77 self.slots[id.0 as usize].kind
78 }
79
80 pub fn set_bool(&mut self, id: ParamId, v: bool) {
82 self.slots[id.0 as usize].value = if v { 1.0 } else { 0.0 };
83 }
84
85 pub fn set_float(&mut self, id: ParamId, v: f32) {
87 self.slots[id.0 as usize].value = v;
88 }
89
90 pub fn fire(&mut self, id: ParamId) {
92 let slot = &mut self.slots[id.0 as usize];
93 if let ParamKind::Trigger { ttl } = slot.kind {
94 slot.value = ttl.max(PENDING);
95 }
96 }
97
98 pub fn get_bool(&self, id: ParamId) -> bool {
100 self.slots[id.0 as usize].value != 0.0
101 }
102
103 pub fn get_float(&self, id: ParamId) -> f32 {
105 self.slots[id.0 as usize].value
106 }
107
108 pub fn pending(&self, id: ParamId) -> bool {
110 self.slots[id.0 as usize].value > 0.0
111 }
112
113 pub(crate) fn reset_to(&mut self, defaults: &Params) {
116 for (i, s) in self.slots.iter_mut().enumerate() {
117 s.value = defaults.slots.get(i).map_or(0.0, |d| d.value);
118 }
119 }
120
121 pub(crate) fn consume(&mut self, id: ParamId) {
123 self.slots[id.0 as usize].value = 0.0;
124 }
125
126 pub(crate) fn decay(&mut self, dt: f32) {
128 for s in &mut self.slots {
129 if matches!(s.kind, ParamKind::Trigger { .. }) {
130 s.value = (s.value - dt).max(0.0);
131 if s.value < PENDING {
132 s.value = 0.0;
133 }
134 }
135 }
136 }
137
138 pub fn iter(&self) -> impl Iterator<Item = (ParamId, &str, ParamKind, f32)> {
140 self.slots
141 .iter()
142 .enumerate()
143 .map(|(i, s)| (ParamId(i as u16), s.name.as_ref(), s.kind, s.value))
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 fn triggers_wait_their_ttl_then_expire() {
153 let mut p = Params::default();
154 let t = p.declare("jump", ParamKind::Trigger { ttl: 0.1 });
155 p.fire(t);
156 p.decay(0.05);
157 assert!(p.pending(t));
158 p.decay(0.06);
159 assert!(!p.pending(t));
160 }
161
162 #[test]
163 fn a_zero_ttl_trigger_lives_one_update() {
164 let mut p = Params::default();
165 let t = p.declare("land", ParamKind::Trigger { ttl: 0.0 });
166 p.fire(t);
167 assert!(p.pending(t));
168 p.decay(1.0 / 120.0);
169 assert!(!p.pending(t));
170 }
171
172 #[test]
173 fn redeclaring_returns_the_same_id() {
174 let mut p = Params::default();
175 let a = p.declare("x", ParamKind::Float);
176 assert_eq!(p.declare("x", ParamKind::Bool), a);
177 assert_eq!(p.kind(a), ParamKind::Float);
178 }
179}