blizz_ui/components/mascot/
mod.rs1mod frames;
4
5pub mod bobbing;
6pub mod fx;
7
8pub use bobbing::{
9 AnimationExit, AnimationOptions, BobbingFrame, BobbingPhase, bobbing_frame, bobbing_origin,
10 cycle_duration, render_cycle_frame,
11};
12pub use frames::{
13 MASCOT, MascotFrames, centered_origin, lines, queue_centered, queue_frame, queue_frame_owned,
14 size,
15};
16pub use fx::*;
17
18#[cfg(not(tarpaulin_include))]
19pub use bobbing::run_bobbing_animation;
20
21use std::io::Write;
22
23use rand::rngs::ThreadRng;
24
25use crate::RenderContext;
26use crate::layout::{centered_block_origin, top_two_thirds};
27
28#[derive(Clone, Copy, Debug)]
33pub struct MascotEffectFrame<'a> {
34 pub dissolve_state: Option<&'a fx::DissolveState>,
35 pub entrance_tick: usize,
36 pub dissolve_tick: usize,
37}
38
39#[derive(Clone, Debug)]
41pub struct MascotComponent {
42 pub frames: MascotFrames,
43 pub entrance: f64,
44 pub dissolve: f64,
45 pub floating: bool,
46 distance_map: fx::DistanceMap,
47}
48
49impl MascotComponent {
50 pub fn blizz() -> Self {
51 let frames = MascotFrames::blizz();
52 let distance_map = fx::build_distance_map(frames.lines());
53 Self {
54 frames,
55 entrance: 0.0,
56 dissolve: 0.0,
57 floating: false,
58 distance_map,
59 }
60 }
61
62 #[cfg(not(tarpaulin_include))]
63 pub fn render<W: Write>(
64 &self,
65 writer: &mut W,
66 ctx: &RenderContext,
67 rng: &mut ThreadRng,
68 effect: &MascotEffectFrame<'_>,
69 ) -> std::io::Result<()> {
70 if self.entrance <= 0.0 && self.dissolve <= 0.0 {
71 return Ok(());
72 }
73
74 if self.dissolve >= 1.0 {
75 return Ok(());
76 }
77
78 let region = top_two_thirds(ctx.terminal_size);
79 let origin = if self.floating {
80 let bob = bobbing::bobbing_frame(ctx.elapsed);
81 bobbing::bobbing_origin(region, self.frames.size(), bob.row_offset)
82 } else {
83 centered_block_origin(region, self.frames.size())
84 };
85
86 let lines = self.frames.lines();
87 if self.dissolve > 0.0
88 && let Some(state) = effect.dissolve_state
89 {
90 let frame = fx::dissolve_frame(lines, state, effect.dissolve_tick, rng);
91 return queue_frame_owned(writer, origin, &frame);
92 }
93
94 if self.entrance < 1.0 {
95 let frame = fx::entrance_frame(lines, &self.distance_map, effect.entrance_tick, rng);
96 return queue_frame_owned(writer, origin, &frame);
97 }
98
99 queue_frame(writer, origin, lines)
100 }
101}