Skip to main content

shuyi_ui/input/
frame.rs

1//! [`FrameInput`]: one frame's input as a screen sees it. Generic over the
2//! game's [`ActionSet`] (ticket 1106), and [`frame_seconds`], which makes
3//! a frame time a platform reports safe to add up.
4//!
5//! In tests, a screen sees only the actions it declares
6//! ([`Screen::actions`](crate::screen::Screen::actions), ADR-0008 §3):
7//! [`declared`](FrameInput::declared) filters a frame's input down to
8//! them, and asking whether an action it didn't declare is held fails the
9//! test.
10
11use crate::prelude::*;
12use shuyi_assets::keymap::{ActionSet, Button, Chord};
13
14/// `dt` as seconds that can pass: itself when finite and at least 0, else
15/// 0 (a NaN, an infinity or a negative frame time is a platform's
16/// nonsense, and no time passes).
17pub fn frame_seconds(dt: f32) -> f32 {
18    if dt.is_finite() { dt.max(0.0) } else { 0.0 }
19}
20
21/// One frame's input as a screen sees it.
22#[derive(Debug, Clone, PartialEq)]
23pub struct FrameInput<A: ActionSet> {
24    /// Actions this frame, in order: key presses, then key repeats. A fixed
25    /// step reads [`presses`](Self::presses) instead, which have no repeats.
26    pub actions: Vec<A>,
27    /// Seconds since the previous frame: finite and at least 0
28    /// ([`frame_seconds`]).
29    dt: f32,
30    /// Actions whose key is currently held down.
31    held: Vec<A>,
32    /// The chords pressed this frame, in order.
33    pressed: Vec<Chord>,
34    /// The characters typed this frame, in order.
35    text: Vec<char>,
36    /// Whether a controller button went down this frame.
37    pad: bool,
38    /// The controller buttons that went down this frame, in order.
39    buttons_down: Vec<Button>,
40    /// The controller buttons that went up this frame, in order.
41    buttons_up: Vec<Button>,
42    /// The fixed simulation steps due this frame.
43    ticks: u32,
44    /// The actions pressed this frame, in order, without the key repeats.
45    presses: Vec<A>,
46    /// The actions let go this frame, in order.
47    released: Vec<A>,
48    /// Whether the game came back from a suspend just before this frame.
49    resumed: bool,
50    /// The screen this input was [filtered](Self::declared) for, and the
51    /// actions it declares: asking about any other is a bug in the
52    /// screen's declaration.
53    declared: Option<(&'static str, Vec<A>)>,
54}
55
56impl<A: ActionSet> FrameInput<A> {
57    /// Input for one frame; `held` lists the actions whose keys are down.
58    /// No [`pressed_chords`](Self::pressed_chords). A `dt` that is not
59    /// finite or is negative counts as 0 ([`frame_seconds`]). Each of
60    /// `actions` is a press ([`presses`](Self::presses)), and one that
61    /// isn't held is a tap, let go too ([`released`](Self::released)),
62    /// until [`with_presses`](Self::with_presses) says otherwise.
63    pub fn new(actions: Vec<A>, dt: f32, held: Vec<A>) -> Self {
64        let mut released: Vec<A> = Vec::new();
65        for &action in &actions {
66            if !held.contains(&action) && !released.contains(&action) {
67                released.push(action);
68            }
69        }
70        Self {
71            presses: actions.clone(),
72            released,
73            actions,
74            dt: frame_seconds(dt),
75            held,
76            pressed: Vec::new(),
77            text: Vec::new(),
78            pad: false,
79            buttons_down: Vec::new(),
80            buttons_up: Vec::new(),
81            ticks: 0,
82            resumed: false,
83            declared: None,
84        }
85    }
86
87    /// This input as `screen` sees it when it declares that it reacts to
88    /// `declared` alone ([`Screen::actions`](crate::screen::Screen::actions)):
89    /// only those actions, with the actions held back (in order, as often
90    /// as they came). Asking the result whether an action outside
91    /// `declared` is [held](Self::is_held) panics: the screen reads an
92    /// action it didn't declare. Its [`presses`](Self::presses),
93    /// [`released`](Self::released) and [`held`](Self::held) actions are
94    /// `declared`'s alone. Keys, buttons and text stay as they were.
95    pub fn declared(&self, screen: &'static str, declared: &[A]) -> (Self, Vec<A>) {
96        let (actions, missed) = self.actions.iter().partition(|a| declared.contains(a));
97        let only = |list: &[A]| {
98            list.iter()
99                .copied()
100                .filter(|a| declared.contains(a))
101                .collect()
102        };
103        let input = Self {
104            actions,
105            presses: only(&self.presses),
106            released: only(&self.released),
107            held: only(&self.held),
108            declared: Some((screen, declared.to_vec())),
109            ..self.clone()
110        };
111        (input, missed)
112    }
113
114    /// The same input with the actions `presses`, pressed this frame in
115    /// order without the key repeats, and `released`, let go this frame in
116    /// order, as [`InputState`](super::InputState) gives them.
117    #[must_use]
118    pub fn with_presses(mut self, presses: Vec<A>, released: Vec<A>) -> Self {
119        self.presses = presses;
120        self.released = released;
121        self
122    }
123
124    /// The actions pressed this frame, in order, without the key repeats
125    /// that [`actions`](Self::actions) has after them: what a fixed step
126    /// reads. A tick stage reads them through its
127    /// [`TickDriver`](crate::tick::TickDriver), which gives each to one
128    /// step.
129    pub fn presses(&self) -> &[A] {
130        &self.presses
131    }
132
133    /// The actions let go this frame, in order: each when no key or button
134    /// holds it any more.
135    pub fn released(&self) -> &[A] {
136        &self.released
137    }
138
139    /// The actions whose key or button is down at the end of this frame, in
140    /// the game's order ([`ActionSet::ALL`]).
141    pub fn held(&self) -> &[A] {
142        &self.held
143    }
144
145    /// The same input with whether the game came back from a suspend just
146    /// before this frame.
147    #[must_use]
148    pub fn with_resumed(mut self, resumed: bool) -> Self {
149        self.resumed = resumed;
150        self
151    }
152
153    /// Whether the game came back from a suspend just before this frame
154    /// (the platform's `SystemEvent::Resume`): a tick stage forgets the
155    /// presses it kept from before ([`TickDriver`](crate::tick::TickDriver)
156    /// does).
157    pub fn resumed(&self) -> bool {
158        self.resumed
159    }
160
161    /// The same input with the controller buttons that went `down` and
162    /// `up` this frame (and so [`pad_pressed`](Self::pad_pressed) if any
163    /// went down).
164    #[must_use]
165    pub fn with_buttons(mut self, down: Vec<Button>, up: Vec<Button>) -> Self {
166        self.pad = !down.is_empty();
167        self.buttons_down = down;
168        self.buttons_up = up;
169        self
170    }
171
172    /// Seconds since the previous frame: always finite and at least 0, so
173    /// a screen adds it up as it is. It is for what only looks: rules count
174    /// [`ticks`](Self::ticks) or turns, with `shuyi_core::timer`.
175    pub fn dt(&self) -> f32 {
176        self.dt
177    }
178
179    /// The same input with `ticks` fixed simulation steps due.
180    #[must_use]
181    pub fn with_ticks(mut self, ticks: u32) -> Self {
182        self.ticks = ticks;
183        self
184    }
185
186    /// How many fixed simulation steps are due this frame (ADR-0003 §2: 60
187    /// a second, at most 4 a frame): a stage with a tick simulation steps
188    /// it this many times. The platform's `App` counts them from its clock
189    /// (`shuyi_platform::tick`); 0 while the game is suspended, in an
190    /// update with no time passing ([`Game::act`](crate::Game::act)) and
191    /// in a frame no `App` ran. Rules time things in these steps, with
192    /// `shuyi_core::timer`, and never with [`dt`](Self::dt).
193    pub fn ticks(&self) -> u32 {
194        self.ticks
195    }
196
197    /// The controller buttons that went down this frame (bound or not; by
198    /// binding position, stick directions included), for the Key bindings
199    /// screen, which captures the button for a slot. Anything else reacts
200    /// to [`actions`](Self::actions).
201    pub fn pressed_buttons(&self) -> &[Button] {
202        &self.buttons_down
203    }
204
205    /// The controller buttons that went up this frame, as
206    /// [`pressed_buttons`](Self::pressed_buttons).
207    pub fn released_buttons(&self) -> &[Button] {
208        &self.buttons_up
209    }
210
211    /// The same input with whether a controller button went down this
212    /// frame.
213    #[must_use]
214    pub fn with_pad(mut self, pad: bool) -> Self {
215        self.pad = pad;
216        self
217    }
218
219    /// Whether this frame's input came from a controller (a button went
220    /// down, or a [button chord](super::ButtonChord)'s button gave its own
221    /// action as it was let go): the player is on a controller, so a
222    /// screen that needs text shows a letter grid instead of asking them to
223    /// type. A game sets it with [`with_pad`](Self::with_pad);
224    /// [`with_buttons`](Self::with_buttons) sets it for a button that
225    /// went down.
226    pub fn pad_pressed(&self) -> bool {
227        self.pad
228    }
229
230    /// The same input with the chords `pressed` and the characters `text`
231    /// typed this frame.
232    #[must_use]
233    pub fn with_typing(mut self, pressed: Vec<Chord>, text: Vec<char>) -> Self {
234        self.pressed = pressed;
235        self.text = text;
236        self
237    }
238
239    /// The chords pressed this frame (bound or not; presses only, no
240    /// repeats), for the few screens that read keys themselves: a text
241    /// box's fixed keys ([`text_key`](super::fixed::text_key)) and the Key bindings
242    /// screen, which captures the key for a slot.
243    /// Anything else reacts to [`actions`](Self::actions).
244    pub fn pressed_chords(&self) -> &[Chord] {
245        &self.pressed
246    }
247
248    /// The characters typed this frame (printable only), for text boxes.
249    pub fn text(&self) -> &[char] {
250        &self.text
251    }
252
253    /// Whether a key bound to `action` is held (e.g. hold Confirm to
254    /// fast-forward).
255    ///
256    /// # Panics
257    ///
258    /// On input [`declared`](Self::declared) for a screen that doesn't
259    /// declare `action`.
260    pub fn is_held(&self, action: A) -> bool {
261        if let Some((screen, declared)) = &self.declared {
262            assert!(
263                declared.contains(&action),
264                "screen \"{screen}\" asks whether {action:?} is held, but doesn't declare it \
265                 (Screen::actions)"
266            );
267        }
268        self.held.contains(&action)
269    }
270}