use crate::prelude::{glam::Vec3Swizzles, *};
use rand::Rng;
use crate::utils::math::rot;
#[derive(Debug, Default)]
pub struct Tween {
last_offset: Vec2,
last_angle_offset: f32,
shakes: Vec<Shake>,
punches: Vec<RotationPunch>,
}
impl Tween {
pub fn add_shake(&mut self, shake: Shake) {
self.shakes.push(shake);
}
pub fn add_punch(&mut self, punch: RotationPunch) {
self.punches.push(punch);
}
pub fn get_tween(&mut self, tick: f32, mut tf: Transform) -> Option<Transform> {
let mut offset = Vec2::ZERO;
let mut angle_offset = 0.0;
self.shakes.retain_mut(|shake| {
shake.update(tick);
offset += shake.get_amplitude();
shake.shaking()
});
self.punches.retain_mut(|punch| {
punch.update(tick);
angle_offset = rot(angle_offset, punch.get_angle_offset());
punch.timer > 0.0
});
if offset == Vec2::ZERO
&& angle_offset == 0.0
&& self.last_offset == Vec2::ZERO
&& self.last_angle_offset == 0.0
{
return None;
}
tf.pos = (tf.pos.xy() + offset - self.last_offset).extend(tf.pos.z);
tf.angle = rot(rot(tf.angle, angle_offset), -self.last_angle_offset);
self.last_offset = offset;
self.last_angle_offset = angle_offset;
Some(tf)
}
pub fn update(
g: &mut Engine,
transforms: &mut Component<Transform>,
tweens: &mut Component<Tween>,
) -> Result<()> {
for e in tweens.iter_mut() {
let id = e.id;
let tween_state = &mut e.value;
let tf = &mut transforms[id];
if let Some(new_tf) = tween_state.get_tween(g.tick, *tf) {
*tf = new_tf;
}
}
Ok(())
}
}
#[derive(Debug)]
pub struct RotationPunch {
timer: f32,
vibration: f32,
samples: Vec<f32>,
}
impl RotationPunch {
pub fn new(dir: Vec2, duration: f32, vibration: f32, elasticity: f32) -> Self {
let sample_count = ((duration * vibration) as usize).max(2);
let mut strength = dir.length();
let decay_x_tween = strength / sample_count as f32;
let mut total_duration = 0.0;
let mut durations: Vec<f32> = (0..sample_count)
.map(|i| {
let perc = (i as f32 + 1.0) / sample_count as f32;
let t = duration * perc;
total_duration += t;
t
})
.collect();
let duration_multiplier = duration / total_duration;
for t in &mut durations {
*t *= duration_multiplier;
}
let samples: Vec<_> = durations
.iter()
.enumerate()
.map(|(i, _t)| {
if i < durations.len() - 1 {
let sample = if i == 0 {
dir.to_angle()
} else if i % 2 != 0 {
-dir.clamp_length_max(strength * elasticity).to_angle()
} else {
dir.clamp_length_max(strength).to_angle()
};
strength -= decay_x_tween;
sample
} else {
0.0
}
})
.collect();
Self {
timer: duration,
vibration,
samples,
}
}
pub fn update(&mut self, tick: f32) {
self.timer -= tick;
}
pub fn get_angle_offset(&self) -> f32 {
if self.timer <= 0.0 {
return 0.0;
}
let s = self.timer * self.vibration;
let s0 = s.floor() as usize;
let s1 = s0 + 1;
let angle_s0 = self.samples.get(s0).copied().unwrap_or(0.0);
let angle_s1 = self.samples.get(s1).copied().unwrap_or(0.0);
rot(angle_s0, (s - s0 as f32) * (angle_s1 - angle_s0))
}
}
#[derive(Debug)]
pub struct Shake {
amplitude: f32,
frequency: f32,
samples: Vec<Vec2>,
timer: f32,
duration: f32,
}
impl Shake {
pub fn new(amplitude: f32, duration: f32, frequency: f32) -> Self {
let sample_count = (duration * frequency) as usize;
let mut rng = rand::rng();
let samples = (0..sample_count)
.map(|_| Vec2::new(rng.random_range(-1.0..1.0), rng.random_range(-1.0..1.0)))
.collect();
Shake {
amplitude,
frequency,
samples,
timer: duration,
duration,
}
}
fn shaking(&self) -> bool {
self.timer > 0.0
}
fn update(&mut self, tick: f32) {
self.timer -= tick;
}
fn get_amplitude(&self) -> Vec2 {
if !self.shaking() {
return Vec2::ZERO;
}
let s = self.timer * self.frequency;
let s0 = s.floor() as usize;
let s1 = s0 + 1;
let noise_s0 = self.samples.get(s0).copied().unwrap_or(Vec2::ZERO);
let noise_s1 = self.samples.get(s1).copied().unwrap_or(Vec2::ZERO);
let k = self.decay_factor();
let interp = noise_s0 + (s - s0 as f32) * (noise_s1 - noise_s0);
self.amplitude * interp * k
}
fn decay_factor(&self) -> f32 {
if self.timer < 0.0 {
0.0
} else {
self.timer / self.duration
}
}
}