ryot 0.2.2

MMORPG library based on the concepts of open tibia written in rust and bevy.
Documentation
use std::ops::Deref;

use glam::{IVec2, Vec2, Vec3};
use rand::distributions::Distribution;
use serde::{Deserialize, Serialize};
use strum::{EnumIter, IntoEnumIterator};

#[cfg(feature = "bevy")]
use bevy::ecs::component::Component;

use crate::position::TilePosition;

#[derive(Hash, PartialEq, Eq, Clone, Copy, Default, Debug, Serialize, Deserialize)]
pub enum CardinalDirection {
    #[default]
    North,
    East,
    South,
    West,
}

impl From<CardinalDirection> for IVec2 {
    fn from(value: CardinalDirection) -> Self {
        OrdinalDirection::from(value).into()
    }
}

impl From<IVec2> for CardinalDirection {
    fn from(value: IVec2) -> Self {
        OrdinalDirection::from(value).into()
    }
}

#[derive(Hash, PartialEq, Eq, EnumIter, Default, Clone, Copy, Debug, Serialize, Deserialize)]
#[repr(u8)]
pub enum OrdinalDirection {
    North = 0,
    NorthEast,
    East,
    SouthEast,
    South,
    SouthWest,
    West,
    NorthWest,
    #[default]
    None,
}

impl OrdinalDirection {
    pub fn get_next(&self) -> Self {
        Self::iter()
            .find(|&dir| dir as u8 == (*self as u8 + 1) % 8)
            .unwrap_or_default()
    }

    pub fn get_previous(&self) -> Self {
        Self::iter()
            .find(|&dir| dir as u8 == (*self as u8 + 7) % 8)
            .unwrap_or_default()
    }
}

impl Distribution<OrdinalDirection> for rand::distributions::Standard {
    fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> OrdinalDirection {
        match rng.gen_range(0..8) {
            0 => OrdinalDirection::North,
            1 => OrdinalDirection::NorthEast,
            2 => OrdinalDirection::East,
            3 => OrdinalDirection::SouthEast,
            4 => OrdinalDirection::South,
            5 => OrdinalDirection::SouthWest,
            6 => OrdinalDirection::West,
            7 => OrdinalDirection::NorthWest,
            _ => OrdinalDirection::None,
        }
    }
}

impl From<OrdinalDirection> for CardinalDirection {
    fn from(value: OrdinalDirection) -> Self {
        match value {
            OrdinalDirection::North => CardinalDirection::North,
            OrdinalDirection::NorthEast => CardinalDirection::East,
            OrdinalDirection::East => CardinalDirection::East,
            OrdinalDirection::SouthEast => CardinalDirection::East,
            OrdinalDirection::South => CardinalDirection::South,
            OrdinalDirection::SouthWest => CardinalDirection::West,
            OrdinalDirection::West => CardinalDirection::West,
            OrdinalDirection::NorthWest => CardinalDirection::West,
            OrdinalDirection::None => CardinalDirection::North,
        }
    }
}

impl From<CardinalDirection> for OrdinalDirection {
    fn from(value: CardinalDirection) -> Self {
        match value {
            CardinalDirection::North => OrdinalDirection::North,
            CardinalDirection::East => OrdinalDirection::East,
            CardinalDirection::South => OrdinalDirection::South,
            CardinalDirection::West => OrdinalDirection::West,
        }
    }
}

impl From<OrdinalDirection> for IVec2 {
    fn from(value: OrdinalDirection) -> Self {
        match value {
            OrdinalDirection::North => [0, 1].into(),
            OrdinalDirection::NorthEast => [1, 1].into(),
            OrdinalDirection::East => [1, 0].into(),
            OrdinalDirection::SouthEast => [1, -1].into(),
            OrdinalDirection::South => [0, -1].into(),
            OrdinalDirection::SouthWest => [-1, -1].into(),
            OrdinalDirection::West => [-1, 0].into(),
            OrdinalDirection::NorthWest => [-1, 1].into(),
            OrdinalDirection::None => [0, 0].into(),
        }
    }
}

impl From<IVec2> for OrdinalDirection {
    fn from(value: IVec2) -> Self {
        match value.clamp(IVec2::splat(-1), IVec2::splat(1)) {
            IVec2 { x: 0, y: 1 } => OrdinalDirection::North,
            IVec2 { x: 1, y: 1 } => OrdinalDirection::NorthEast,
            IVec2 { x: 1, y: 0 } => OrdinalDirection::East,
            IVec2 { x: 1, y: -1 } => OrdinalDirection::SouthEast,
            IVec2 { x: 0, y: -1 } => OrdinalDirection::South,
            IVec2 { x: -1, y: -1 } => OrdinalDirection::SouthWest,
            IVec2 { x: -1, y: 0 } => OrdinalDirection::West,
            IVec2 { x: -1, y: 1 } => OrdinalDirection::NorthWest,
            _ => OrdinalDirection::None,
        }
    }
}

impl From<Vec2> for OrdinalDirection {
    fn from(value: Vec2) -> Self {
        match value.clamp(Vec2::splat(-1.0), Vec2::splat(1.0)).as_ivec2() {
            IVec2 { x: 0, y: 1 } => OrdinalDirection::North,
            IVec2 { x: 1, y: 1 } => OrdinalDirection::NorthEast,
            IVec2 { x: 1, y: 0 } => OrdinalDirection::East,
            IVec2 { x: 1, y: -1 } => OrdinalDirection::SouthEast,
            IVec2 { x: 0, y: -1 } => OrdinalDirection::South,
            IVec2 { x: -1, y: -1 } => OrdinalDirection::SouthWest,
            IVec2 { x: -1, y: 0 } => OrdinalDirection::West,
            IVec2 { x: -1, y: 1 } => OrdinalDirection::NorthWest,
            _ => OrdinalDirection::None,
        }
    }
}

impl From<Vec3> for OrdinalDirection {
    fn from(value: Vec3) -> Self {
        value.truncate().into()
    }
}

impl From<TilePosition> for OrdinalDirection {
    fn from(value: TilePosition) -> Self {
        value.deref().truncate().into()
    }
}

impl From<OrdinalDirection> for (u16, u16) {
    fn from(ordinal: OrdinalDirection) -> Self {
        match ordinal {
            OrdinalDirection::East => (0, 1),
            OrdinalDirection::NorthEast => (45, 46),
            OrdinalDirection::North => (90, 91),
            OrdinalDirection::NorthWest => (135, 136),
            OrdinalDirection::West => (180, 181),
            OrdinalDirection::SouthWest => (225, 226),
            OrdinalDirection::South => (270, 271),
            OrdinalDirection::SouthEast => (315, 316),
            _ => (0, 0),
        }
    }
}

#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "bevy", derive(Component))]
pub enum Directional {
    Cardinal(CardinalDirection),
    Ordinal(OrdinalDirection),
}

impl Default for Directional {
    fn default() -> Self {
        Directional::Cardinal(CardinalDirection::default())
    }
}

impl Directional {
    pub fn index(self) -> usize {
        match self {
            Directional::Cardinal(cardinal) => cardinal as usize,
            Directional::Ordinal(ordinal) => ordinal as usize,
        }
    }
}

impl From<Directional> for IVec2 {
    fn from(value: Directional) -> Self {
        match value {
            Directional::Cardinal(cardinal) => cardinal.into(),
            Directional::Ordinal(ordinal) => ordinal.into(),
        }
    }
}