Skip to main content

banished_ui/
animation.rs

1//! The interface's own clock: animated images, the containers' wheel and the edit caret.
2
3use super::*;
4
5impl Ui {
6    /// True if the current tree contains an animated image with frames and a positive frame rate.
7    pub fn has_animations(&self) -> bool {
8        self.nodes.iter().any(|node| {
9            matches!(
10                &node.kind,
11                Kind::AnimatedImage {
12                    sprites,
13                    frames_per_second,
14                    ..
15                } if sprites.len() > 1 && *frames_per_second > 0
16            )
17        })
18    }
19
20    /// Select each animated image's frame at the supplied elapsed time in seconds.
21    /// The game's UI step: each element's own update (`AnimatedImageElement::Update`, the
22    /// containers' wheel) by `dt` seconds.
23    pub fn update(&mut self, dt: f32) {
24        self.update_containers();
25        self.update_caret(dt);
26        for node in &mut self.nodes {
27            if let Kind::AnimatedImage {
28                sprites,
29                frames_per_second,
30                frame,
31                clock,
32                ..
33            } = &mut node.kind
34            {
35                // `AnimatedImageElement::Update`: one loop lasts frames / rate seconds; past
36                // it the clock goes back by one loop, and the frame is its share of the loop.
37                if sprites.is_empty() || *frames_per_second <= 0 {
38                    continue;
39                }
40                *clock += dt as f64;
41                let length = sprites.len() as f32 / *frames_per_second as f32;
42                let mut t = *clock as f32;
43                if length <= t {
44                    t -= length;
45                    *clock = t as f64;
46                }
47                *frame = ((t / length * sprites.len() as f32) as usize).min(sprites.len() - 1);
48            }
49        }
50    }
51
52    /// The viewer's preview: every animation at `elapsed_seconds`.
53    pub fn update_animations(&mut self, elapsed_seconds: f32) {
54        for node in &mut self.nodes {
55            if let Kind::AnimatedImage {
56                sprites,
57                frames_per_second,
58                frame,
59                ..
60            } = &mut node.kind
61            {
62                if !sprites.is_empty() && *frames_per_second > 0 {
63                    *frame = ((elapsed_seconds.max(0.0) * *frames_per_second as f32).floor()
64                        as usize)
65                        % sprites.len();
66                }
67            }
68        }
69    }
70
71    /// Current frame, frame count, and frame rate for the first animated image in the tree.
72    pub fn animation_info(&self) -> Option<(usize, usize, i32)> {
73        self.nodes.iter().find_map(|node| match &node.kind {
74            Kind::AnimatedImage {
75                sprites,
76                frames_per_second,
77                frame,
78                ..
79            } if !sprites.is_empty() => Some((*frame, sprites.len(), *frames_per_second)),
80            _ => None,
81        })
82    }
83
84    /// Select the same frame in every animated image, wrapping each image's own frame count.
85    pub fn set_animation_frame(&mut self, frame: usize) {
86        for node in &mut self.nodes {
87            if let Kind::AnimatedImage {
88                sprites,
89                frame: current,
90                ..
91            } = &mut node.kind
92            {
93                if !sprites.is_empty() {
94                    *current = frame % sprites.len();
95                }
96            }
97        }
98    }
99}