rsmod-pathfinder 5.0.4

A breadth-first search path finder.
Documentation
#![allow(non_camel_case_types)]

use wasm_bindgen::prelude::wasm_bindgen;

use crate::rsmod::collision_flag::CollisionFlag;

#[repr(u8)]
#[wasm_bindgen]
pub enum CollisionType {
    NORMAL = 0,
    BLOCKED = 1,
    INDOORS = 2,
    OUTDOORS = 3,
    LINE_OF_SIGHT = 4,
}

pub trait CollisionStrategy {
    fn can_move(&self, tile_flag: u32, block_flag: u32) -> bool;
}

pub struct Normal;

impl CollisionStrategy for Normal {
    #[inline(always)]
    fn can_move(&self, tile_flag: u32, block_flag: u32) -> bool {
        return (tile_flag & block_flag) == CollisionFlag::OPEN as u32;
    }
}

pub struct Blocked;

impl CollisionStrategy for Blocked {
    #[inline(always)]
    fn can_move(&self, tile_flag: u32, block_flag: u32) -> bool {
        let flag: u32 = block_flag & !(CollisionFlag::FLOOR as u32);
        return (tile_flag & flag) == CollisionFlag::OPEN as u32
            && (tile_flag & CollisionFlag::FLOOR as u32) != CollisionFlag::OPEN as u32;
    }
}

pub struct Indoors;

impl CollisionStrategy for Indoors {
    #[inline(always)]
    fn can_move(&self, tile_flag: u32, block_flag: u32) -> bool {
        return (tile_flag & block_flag) == CollisionFlag::OPEN as u32
            && (tile_flag & CollisionFlag::ROOF as u32) != CollisionFlag::OPEN as u32;
    }
}

pub struct Outdoors;

impl CollisionStrategy for Outdoors {
    #[inline(always)]
    fn can_move(&self, tile_flag: u32, block_flag: u32) -> bool {
        return (tile_flag & (block_flag | CollisionFlag::ROOF as u32))
            == CollisionFlag::OPEN as u32;
    }
}

pub struct LineOfSight;

impl LineOfSight {
    const MOVEMENT: u32 = CollisionFlag::WALL_NORTH_WEST as u32
        | CollisionFlag::WALL_NORTH as u32
        | CollisionFlag::WALL_NORTH_EAST as u32
        | CollisionFlag::WALL_EAST as u32
        | CollisionFlag::WALL_SOUTH_EAST as u32
        | CollisionFlag::WALL_SOUTH as u32
        | CollisionFlag::WALL_SOUTH_WEST as u32
        | CollisionFlag::WALL_WEST as u32
        | CollisionFlag::LOC as u32;

    const ROUTE: u32 = CollisionFlag::WALL_NORTH_WEST_ROUTE_BLOCKER as u32
        | CollisionFlag::WALL_NORTH_ROUTE_BLOCKER as u32
        | CollisionFlag::WALL_NORTH_EAST_ROUTE_BLOCKER as u32
        | CollisionFlag::WALL_EAST_ROUTE_BLOCKER as u32
        | CollisionFlag::WALL_SOUTH_EAST_ROUTE_BLOCKER as u32
        | CollisionFlag::WALL_SOUTH_ROUTE_BLOCKER as u32
        | CollisionFlag::WALL_SOUTH_WEST_ROUTE_BLOCKER as u32
        | CollisionFlag::WALL_WEST_ROUTE_BLOCKER as u32
        | CollisionFlag::LOC_ROUTE_BLOCKER as u32;
}

impl CollisionStrategy for LineOfSight {
    #[inline(always)]
    fn can_move(&self, tile_flag: u32, block_flag: u32) -> bool {
        let movement_flags: u32 = (block_flag & LineOfSight::MOVEMENT) << 9;
        let route_flags: u32 = (block_flag & LineOfSight::ROUTE) >> 13;
        let final_flag: u32 = movement_flags | route_flags;
        return (tile_flag & final_flag) == CollisionFlag::OPEN as u32;
    }
}

pub enum CollisionStrategies {
    Normal(Normal),
    Blocked(Blocked),
    Indoors(Indoors),
    Outdoors(Outdoors),
    LineOfSight(LineOfSight),
}

impl CollisionStrategy for CollisionStrategies {
    #[rustfmt::skip]
    #[inline(always)]
    fn can_move(&self, tile_flag: u32, block_flag: u32) -> bool {
        match self {
            CollisionStrategies::Normal(strategy) => strategy.can_move(tile_flag, block_flag),
            CollisionStrategies::Blocked(strategy) => strategy.can_move(tile_flag, block_flag),
            CollisionStrategies::Indoors(strategy) => strategy.can_move(tile_flag, block_flag),
            CollisionStrategies::Outdoors(strategy) => strategy.can_move(tile_flag, block_flag),
            CollisionStrategies::LineOfSight(strategy) => strategy.can_move(tile_flag, block_flag)
        }
    }
}