1use std::{
4 collections::VecDeque,
5 time::{Duration, SystemTime},
6};
7
8use glam::Vec2;
9use rand::{seq::IteratorRandom, thread_rng};
10
11use crate::particle::{LifeState, Particle, ParticleConfig};
12
13pub struct Firework {
15 pub init_time: SystemTime,
17 pub spawn_after: Duration,
19 pub time_elapsed: Duration,
20 pub center: Vec2,
21 pub state: FireworkState,
22 pub config: FireworkConfig,
23 pub form: ExplosionForm,
24 pub particles: Vec<ParticleConfig>,
25 pub current_particles: Vec<Particle>,
26}
27
28impl Default for Firework {
29 fn default() -> Self {
30 Self {
31 init_time: SystemTime::now(),
32 spawn_after: Duration::ZERO,
33 time_elapsed: Duration::ZERO,
34 center: Vec2::ZERO,
35 state: FireworkState::Waiting,
36 config: FireworkConfig::default(),
37 form: ExplosionForm::Instant { used: false },
38 particles: Vec::new(),
39 current_particles: Vec::new(),
40 }
41 }
42}
43
44impl Firework {
45 pub fn update(&mut self, now: SystemTime, delta_time: Duration) {
52 if now >= self.init_time + self.spawn_after {
54 self.time_elapsed += delta_time;
55 match &mut self.form {
56 ExplosionForm::Instant { used } => {
57 if !*used {
58 self.particles.iter().for_each(|p| {
59 self.current_particles.push(Particle {
60 pos: p.init_pos,
61 vel: p.init_vel,
62 trail: init_trail(p.init_pos, p.trail_length),
63 life_state: LifeState::Alive,
64 time_elapsed: Duration::ZERO,
65 config: *p,
66 })
67 })
68 }
69 *used = true;
70 }
71 ExplosionForm::Sustained {
72 lasts,
73 time_interval,
74 timer,
75 } => {
76 if self.time_elapsed <= *lasts {
77 if *timer + delta_time <= *time_interval {
78 *timer += delta_time;
79 } else {
80 let n =
81 (*timer + delta_time).as_millis() / (*time_interval).as_millis();
82 self.particles
83 .iter()
84 .choose_multiple(&mut thread_rng(), n as usize)
85 .iter()
86 .for_each(|p| {
87 self.current_particles.push(Particle {
88 pos: p.init_pos,
89 vel: p.init_vel,
90 trail: init_trail(p.init_pos, p.trail_length),
91 life_state: LifeState::Alive,
92 time_elapsed: Duration::ZERO,
93 config: **p,
94 })
95 });
96 *timer = Duration::from_millis(
97 ((*timer + delta_time).as_millis() % (*time_interval).as_millis())
98 as u64,
99 );
100 }
101 }
102 }
103 }
104 self.state = FireworkState::Alive;
105 }
106
107 self.current_particles
108 .iter_mut()
109 .for_each(|p| p.update(delta_time, &self.config));
110
111 self.current_particles
113 .retain(|p| p.life_state != LifeState::Dead);
114
115 match self.form {
116 ExplosionForm::Instant { used } => {
117 if used && self.state == FireworkState::Alive && self.current_particles.is_empty() {
118 self.state = FireworkState::Gone;
119 }
120 }
121 ExplosionForm::Sustained { lasts, .. } => {
122 if self.time_elapsed > lasts
123 && self.state == FireworkState::Alive
124 && self.current_particles.is_empty()
125 {
126 self.state = FireworkState::Gone;
127 }
128 }
129 }
130 }
131
132 pub fn is_gone(&self) -> bool {
134 self.state == FireworkState::Gone
135 }
136
137 pub fn reset(&mut self) {
139 self.init_time = SystemTime::now();
140 self.state = FireworkState::Waiting;
141 self.time_elapsed = Duration::ZERO;
142 self.current_particles = Vec::new();
143 match &mut self.form {
144 ExplosionForm::Instant { used } => {
145 *used = false;
146 }
147 ExplosionForm::Sustained { timer, .. } => {
148 *timer = Duration::ZERO;
149 }
150 }
151 }
152}
153
154#[derive(Debug, PartialEq, Default)]
163pub enum FireworkState {
164 #[default]
165 Waiting,
166 Alive,
167 Gone,
168}
169
170#[derive(Debug, PartialEq, Eq)]
172pub enum ExplosionForm {
173 Instant {
174 used: bool,
175 },
176 Sustained {
177 lasts: Duration,
179 time_interval: Duration,
181 timer: Duration,
182 },
183}
184
185pub struct FireworkConfig {
189 pub gravity_scale: f32,
191 pub ar_scale: f32,
194 pub additional_force: Box<dyn Fn(&Particle) -> Vec2>,
195 pub gradient_scale: fn(f32) -> f32,
201 pub enable_gradient: bool,
208}
209
210impl Default for FireworkConfig {
211 fn default() -> Self {
212 Self {
213 gravity_scale: 1.,
214 ar_scale: 0.28,
215 additional_force: Box::new(move |_| Vec2::ZERO),
216 gradient_scale: |_| 1.,
217 enable_gradient: false,
218 }
219 }
220}
221
222impl FireworkConfig {
223 #[inline]
225 #[must_use]
226 pub fn with_gradient_scale(mut self, f: fn(f32) -> f32) -> Self {
227 self.gradient_scale = f;
228 self
229 }
230
231 #[inline]
233 #[must_use]
234 pub fn with_gravity_scale(mut self, s: f32) -> Self {
235 self.gravity_scale = s;
236 self
237 }
238
239 #[inline]
241 #[must_use]
242 pub fn with_ar_scale(mut self, s: f32) -> Self {
243 self.ar_scale = s;
244 self
245 }
246
247 #[inline]
249 #[must_use]
250 pub fn with_additional_force(mut self, af: impl Fn(&Particle) -> Vec2 + 'static) -> Self {
251 self.additional_force = Box::new(af);
252 self
253 }
254
255 pub fn set_enable_gradient(&mut self, enable_gradient: bool) {
257 self.enable_gradient = enable_gradient;
258 }
259}
260
261pub struct FireworkManager {
263 pub fireworks: Vec<Firework>,
264 pub enable_loop: bool,
266 pub install_form: FireworkInstallForm,
268}
269
270impl Default for FireworkManager {
271 fn default() -> Self {
272 Self {
273 fireworks: Vec::new(),
274 enable_loop: false,
275 install_form: FireworkInstallForm::StaticInstall,
276 }
277 }
278}
279
280impl FireworkManager {
281 pub fn new(fireworks: Vec<Firework>) -> Self {
283 Self {
284 fireworks,
285 enable_loop: false,
286 install_form: FireworkInstallForm::StaticInstall,
287 }
288 }
289
290 pub fn add_firework(&mut self, firework: Firework) {
292 self.fireworks.push(firework);
293 }
294
295 pub fn add_fireworks(&mut self, mut fireworks: Vec<Firework>) {
297 self.fireworks.append(&mut fireworks);
298 }
299
300 #[inline]
302 #[must_use]
303 pub fn with_firework(mut self, firework: Firework) -> Self {
304 self.fireworks.push(firework);
305 self
306 }
307
308 #[inline]
310 #[must_use]
311 pub fn with_fireworks(mut self, mut fireworks: Vec<Firework>) -> Self {
312 self.fireworks.append(&mut fireworks);
313 self
314 }
315
316 #[inline]
318 #[must_use]
319 pub fn enable_loop(mut self) -> Self {
320 self.enable_loop = true;
321 self
322 }
323
324 #[inline]
326 #[must_use]
327 pub fn disable_loop(mut self) -> Self {
328 self.enable_loop = false;
329 self
330 }
331
332 pub fn reset(&mut self) {
334 for ele in self.fireworks.iter_mut() {
335 ele.reset();
336 }
337 }
338
339 pub fn set_enable_loop(&mut self, enable_loop: bool) {
340 self.enable_loop = enable_loop;
341 }
342
343 pub fn update(&mut self, now: SystemTime, delta_time: Duration) {
345 for ele in self.fireworks.iter_mut() {
346 ele.update(now, delta_time);
347 }
348 if self.install_form == FireworkInstallForm::DynamicInstall {
349 self.fireworks.retain(|f| f.state != FireworkState::Gone);
350 }
351 if self.install_form == FireworkInstallForm::StaticInstall
352 && self.enable_loop
353 && self.fireworks.iter().all(|f| f.is_gone())
354 {
355 self.reset();
356 }
357 }
358
359 pub fn enable_dyn_install(mut self) -> Self {
361 self.install_form = FireworkInstallForm::DynamicInstall;
362 self
363 }
364}
365
366#[derive(Debug, PartialEq)]
374pub enum FireworkInstallForm {
375 StaticInstall,
376 DynamicInstall,
377}
378
379fn init_trail(init_pos: Vec2, n: usize) -> VecDeque<Vec2> {
380 VecDeque::from(vec![init_pos; n])
381}