Skip to main content

pfx_core/sim/shots/
follow.rs

1use super::super::sqrt;
2use super::emit::round;
3
4#[must_use]
5pub fn toward<const N: usize>(from: [i32; N], to: [i32; N], step: i32) -> [i32; N] {
6    if step <= 0 {
7        return from;
8    }
9    let d: [i64; N] = std::array::from_fn(|i| i64::from(to[i]) - i64::from(from[i]));
10    let dist2: i128 = d.iter().map(|&c| i128::from(c) * i128::from(c)).sum();
11    if dist2 <= i128::from(step) * i128::from(step) {
12        return to;
13    }
14    let scale = f64::from(step) / sqrt(dist2 as f64);
15    std::array::from_fn(|i| from[i].saturating_add(round(d[i] as f64 * scale)))
16}
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20pub struct Member<const N: usize> {
21    #[cfg_attr(feature = "serde", serde(with = "super::arr"))]
22    pub pos: [i32; N],
23    #[cfg_attr(feature = "serde", serde(with = "super::arr"))]
24    pub offset: [i32; N],
25    pub limit: Option<i32>,
26}
27
28impl<const N: usize> Member<N> {
29    pub fn slot(&self, leader: [i32; N]) -> [i32; N] {
30        std::array::from_fn(|i| leader[i].saturating_add(self.offset[i]))
31    }
32
33    pub fn slot_scaled(&self, leader: [i32; N], num: i32, den: i32) -> [i32; N] {
34        std::array::from_fn(|i| leader[i].saturating_add(scale(self.offset[i], num, den)))
35    }
36
37    pub fn step(&mut self, leader: [i32; N]) {
38        self.step_scaled(leader, 1, 1);
39    }
40
41    pub fn step_scaled(&mut self, leader: [i32; N], num: i32, den: i32) {
42        let slot = self.slot_scaled(leader, num, den);
43        self.pos = match self.limit {
44            Some(limit) => toward(self.pos, slot, limit),
45            None => slot,
46        };
47    }
48}
49
50pub fn follow<const N: usize>(members: &mut [Member<N>], leader: [i32; N]) {
51    follow_scaled(members, leader, 1, 1);
52}
53
54pub fn follow_scaled<const N: usize>(
55    members: &mut [Member<N>],
56    leader: [i32; N],
57    num: i32,
58    den: i32,
59) {
60    for member in members {
61        member.step_scaled(leader, num, den);
62    }
63}
64
65fn scale(offset: i32, num: i32, den: i32) -> i32 {
66    if den == 0 {
67        return offset;
68    }
69    let (mut p, mut q) = (i64::from(offset) * i64::from(num), i64::from(den));
70    if q < 0 {
71        p = -p;
72        q = -q;
73    }
74    let near = if p >= 0 {
75        (2 * p + q) / (2 * q)
76    } else {
77        -((-2 * p + q) / (2 * q))
78    };
79    near.clamp(i64::from(i32::MIN), i64::from(i32::MAX)) as i32
80}
81
82#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
83#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
84pub struct Slow {
85    pub num: u32,
86    pub den: u32,
87    pub ticks: u32,
88}
89
90#[derive(Clone, Debug, PartialEq, Eq, Hash)]
91#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
92pub struct Mover<const N: usize> {
93    #[cfg_attr(feature = "serde", serde(with = "super::arr"))]
94    pub pos: [i32; N],
95    pub top: i32,
96    stun: u32,
97    slows: Vec<Slow>,
98}
99
100impl<const N: usize> Mover<N> {
101    pub fn new(pos: [i32; N], top: i32) -> Self {
102        Self {
103            pos,
104            top,
105            stun: 0,
106            slows: Vec::new(),
107        }
108    }
109
110    pub fn stun(&mut self, ticks: u32) {
111        self.stun = self.stun.max(ticks);
112    }
113
114    pub fn slow(&mut self, num: u32, den: u32, ticks: u32) {
115        if den == 0 || ticks == 0 {
116            return;
117        }
118        let same =
119            |s: &Slow| u64::from(s.num) * u64::from(den) == u64::from(num) * u64::from(s.den);
120        match self.slows.iter_mut().find(|s| same(s)) {
121            Some(slow) => slow.ticks = slow.ticks.max(ticks),
122            None => self.slows.push(Slow { num, den, ticks }),
123        }
124    }
125
126    pub fn clear_effects(&mut self) {
127        self.stun = 0;
128        self.slows.clear();
129    }
130
131    pub const fn stunned(&self) -> u32 {
132        self.stun
133    }
134
135    pub fn slows(&self) -> &[Slow] {
136        &self.slows
137    }
138
139    pub fn speed(&self) -> i32 {
140        if self.stun > 0 {
141            return 0;
142        }
143        let strongest = self.slows.iter().min_by(|a, b| {
144            (u64::from(a.num) * u64::from(b.den)).cmp(&(u64::from(b.num) * u64::from(a.den)))
145        });
146        match strongest {
147            Some(slow) => {
148                let speed = i64::from(self.top) * i64::from(slow.num) / i64::from(slow.den);
149                speed.clamp(i64::from(i32::MIN), i64::from(i32::MAX)) as i32
150            }
151            None => self.top,
152        }
153    }
154
155    pub fn step(&mut self, target: [i32; N]) -> [i32; N] {
156        self.pos = toward(self.pos, target, self.speed());
157        self.stun = self.stun.saturating_sub(1);
158        for slow in &mut self.slows {
159            slow.ticks -= 1;
160        }
161        self.slows.retain(|slow| slow.ticks > 0);
162        self.pos
163    }
164}