rsmod-pathfinder 5.0.4

A breadth-first search path finder.
Documentation
use crate::rsmod::collision_flag::CollisionFlag;

#[derive(Clone)]
pub struct CollisionFlagMap {
    pub flags: Vec<Option<Box<[u32; 8 * 8]>>>,
}

impl CollisionFlagMap {
    const ZONE_TILE_COUNT: usize = 8 * 8;
    const TOTAL_ZONE_COUNT: usize = 256 * 256 * 4 * CollisionFlagMap::ZONE_TILE_COUNT;

    #[inline(always)]
    pub fn zone_index(x: i32, z: i32, y: i32) -> usize {
        return (((x >> 3) & 0x7ff) | (((z >> 3) & 0x7ff) << 11) | ((y & 0x3) << 22)) as usize;
    }

    #[inline(always)]
    pub fn tile_index(x: i32, z: i32) -> usize {
        return ((x & 0x7) | ((z & 0x7) << 3)) as usize;
    }

    #[inline(always)]
    pub fn new() -> CollisionFlagMap {
        return CollisionFlagMap {
            flags: vec![None; CollisionFlagMap::TOTAL_ZONE_COUNT],
        };
    }

    #[rustfmt::skip]
    #[inline(always)]
    pub unsafe fn get(&self, x: i32, z: i32, y: i32) -> u32 {
        if let Some(ref flags) = *self.flags.as_ptr().add(CollisionFlagMap::zone_index(x, z, y)) {
            return *flags.as_ptr().add(CollisionFlagMap::tile_index(x, z));
        }
        return CollisionFlag::NULL as u32
    }

    #[inline(always)]
    pub unsafe fn set(&mut self, x: i32, z: i32, y: i32, mask: u32) {
        *self
            .allocate_if_absent_return(CollisionFlagMap::zone_index(x, z, y))
            .as_mut_ptr()
            .add(CollisionFlagMap::tile_index(x, z)) = mask;
    }

    #[inline(always)]
    pub unsafe fn add(&mut self, x: i32, z: i32, y: i32, mask: u32) {
        *self
            .allocate_if_absent_return(CollisionFlagMap::zone_index(x, z, y))
            .as_mut_ptr()
            .add(CollisionFlagMap::tile_index(x, z)) |= mask;
    }

    #[inline(always)]
    pub unsafe fn remove(&mut self, x: i32, z: i32, y: i32, mask: u32) {
        *self
            .allocate_if_absent_return(CollisionFlagMap::zone_index(x, z, y))
            .as_mut_ptr()
            .add(CollisionFlagMap::tile_index(x, z)) &= !mask;
    }

    #[inline(always)]
    pub unsafe fn allocate_if_absent(&mut self, x: i32, z: i32, y: i32) {
        self.allocate_if_absent_return(CollisionFlagMap::zone_index(x, z, y));
    }

    #[inline(always)]
    unsafe fn allocate_if_absent_return(&mut self, zone_idx: usize) -> &mut [u32; 64] {
        return (*self.flags.as_mut_ptr().add(zone_idx)).get_or_insert(Box::new(
            [CollisionFlag::OPEN as u32; CollisionFlagMap::ZONE_TILE_COUNT],
        ));
    }

    #[rustfmt::skip]
    #[inline(always)]
    pub unsafe fn deallocate_if_present(&mut self, x: i32, z: i32, y: i32) {
        *self.flags.as_mut_ptr().add(CollisionFlagMap::zone_index(x, z, y)) = None;
    }

    #[rustfmt::skip]
    #[inline(always)]
    pub unsafe fn is_zone_allocated(&self, x: i32, z: i32, y: i32) -> bool {
        return (*self.flags.as_ptr().add(CollisionFlagMap::zone_index(x, z, y))).is_some();
    }

    #[rustfmt::skip]
    #[inline(always)]
    pub unsafe fn is_flagged(&self, x: i32, z: i32, y: i32, masks: u32) -> bool {
        return match &*self.flags.as_ptr().add(CollisionFlagMap::zone_index(x, z, y)) {
            None => false,
            Some(flags) => {
                *flags.as_ptr().add(CollisionFlagMap::tile_index(x, z)) & masks != CollisionFlag::OPEN as u32
            }
        };
    }
}