twgame 0.11.0

DDNet physics implementation
Documentation
use crate::entities::tee::{Tee, TeeCore, TEE_PROXIMITY};
use crate::ids::PlayerUid;
use crate::state::tee_order::TeeOrder;
use slotmap::SecondaryMap;
use vek::num_traits::clamp;
use vek::Vec2;

#[derive(Debug)]
pub struct Tees<T> {
    tees: SecondaryMap<PlayerUid, T>,
}

impl<T> Tees<T> {
    pub(super) fn new() -> Self {
        Self {
            tees: Default::default(),
        }
    }

    pub(super) fn reset(&mut self) {
        self.tees.clear();
    }
}

impl<T> Tees<T> {
    pub(super) fn add_tee(&mut self, tee_uid: PlayerUid, tee: T) {
        self.tees.insert(tee_uid, tee);
    }

    pub(super) fn remove_tee(&mut self, tee_uid: PlayerUid) -> Option<T> {
        self.tees.remove(tee_uid)
    }

    pub(crate) fn get_tee(&self, tee_uid: PlayerUid) -> Option<&T> {
        self.tees.get(tee_uid)
    }

    pub(crate) fn get_tee_mut(&mut self, tee_uid: PlayerUid) -> Option<&mut T> {
        self.tees.get_mut(tee_uid)
    }

    pub(crate) fn get_tees_mut<const N: usize>(
        &mut self,
        tee_uids: [PlayerUid; N],
    ) -> Option<[&mut T; N]> {
        self.tees.get_disjoint_mut(tee_uids)
    }
}
impl Tees<Tee> {
    pub(crate) fn reset_hook(&mut self, tee_uid: PlayerUid) {
        for tee in self.tees.values_mut() {
            tee.reset_hook(tee_uid);
        }
    }
}

pub fn closest_point_on_line(
    line: (Vec2<f32>, Vec2<f32>),
    target_point: Vec2<f32>,
) -> Option<Vec2<f32>> {
    let ab = line.1 - line.0;
    let squared_magnitude_ab = ab.dot(ab);
    if squared_magnitude_ab > 0.0 {
        let ap = target_point - line.0;
        let ap_dot_ab = ap.dot(ab);
        let t = ap_dot_ab / squared_magnitude_ab;
        Some(line.0 + ab * clamp(t, 0.0, 1.0))
    } else {
        None
    }
}

impl Tees<TeeCore> {
    pub fn intersect_tees(
        &self,
        tee_order: &TeeOrder,
        ignore: Option<PlayerUid>,
        from: Vec2<f32>,
        to: Vec2<f32>,
        radius: f32,
    ) -> Option<(PlayerUid, Vec2<f32>)> {
        // collide with tees
        let mut closest_len = from.distance(to) * 100.0;
        let mut explosion = None;

        for (tee_uid, tee) in tee_order.spawn_order().tees(self) {
            if ignore.map(|tid| tid == tee_uid).unwrap_or(false) || tee.is_solo() {
                continue;
            }
            if let Some(intersect_pos) = closest_point_on_line((from, to), tee.pos()) {
                let len = tee.pos().distance(intersect_pos);
                // TODO: different radius on freeze
                if len < TEE_PROXIMITY + radius {
                    let len = from.distance(intersect_pos);
                    if len < closest_len {
                        explosion = Some((tee_uid, intersect_pos));
                        closest_len = len;
                    }
                }
            }
        }
        explosion
    }
}