ryot 0.2.2

MMORPG library based on the concepts of open tibia written in rust and bevy.
Documentation
use crate::position::TilePosition;
use bevy::prelude::{Deref, DerefMut, Resource};

mod diamond;
pub use diamond::*;

mod line;
pub use line::*;

mod round;
pub use round::*;

mod rectangle;
pub use rectangle::*;

mod random;
pub use random::*;

#[cfg(feature = "egui")]
#[macro_export]
macro_rules! include_svg {
    ($svg_content: literal) => {
        egui::ImageSource::Bytes {
            uri: ::std::borrow::Cow::Borrowed(concat!("bytes://", $svg_content, ".svg")),
            bytes: egui::load::Bytes::Static($svg_content.as_bytes()),
        }
    };
}

pub trait BrushItem: PartialEq + Copy + Clone {
    fn from_position(original: Self, pos: TilePosition) -> Self;
    fn get_position(&self) -> TilePosition;
}

pub enum BrushParams<E: BrushItem> {
    Size(i32),
    Position(TilePosition),
    Element(E),
}

impl<E: BrushItem> BrushParams<E> {
    pub fn get_size(&self, center: E) -> i32 {
        let get_distance = |pos: TilePosition| center.get_position().distance(pos).abs() as i32;

        match self {
            BrushParams::Size(size) => *size,
            BrushParams::Position(pos) => get_distance(*pos),
            BrushParams::Element(e) => get_distance(e.get_position()),
        }
    }

    pub fn get_ranges(
        &self,
        center: E,
    ) -> (std::ops::RangeInclusive<i32>, std::ops::RangeInclusive<i32>) {
        let center_pos = center.get_position();
        let get_range_for_pos = |pos: TilePosition| {
            (
                center_pos.x.min(pos.x)..=center_pos.x.max(pos.x),
                center_pos.y.min(pos.y)..=center_pos.y.max(pos.y),
            )
        };

        match self {
            BrushParams::Size(size) => (
                center_pos.x.saturating_sub(*size)..=center_pos.x.saturating_add(*size),
                center_pos.y.saturating_sub(*size)..=center_pos.y.saturating_add(*size),
            ),
            BrushParams::Position(pos) => get_range_for_pos(*pos),
            BrushParams::Element(e) => get_range_for_pos(e.get_position()),
        }
    }
}

#[derive(Clone)]
pub struct Brush<E: BrushItem> {
    func: fn(size: BrushParams<E>, center: E) -> Vec<E>,
    #[cfg(feature = "egui")]
    icon: egui::ImageSource<'static>,
    name: String,
}

impl<E: BrushItem> Brush<E> {
    pub fn new(
        func: fn(BrushParams<E>, E) -> Vec<E>,
        name: &str,
        #[cfg(feature = "egui")] icon: egui::ImageSource<'static>,
    ) -> Self {
        #[cfg(feature = "egui")]
        let brush = Self {
            func,
            icon,
            name: name.into(),
        };
        #[cfg(not(feature = "egui"))]
        let brush = Self {
            func,
            name: name.into(),
        };
        brush
    }

    pub fn name(&self) -> &str {
        &self.name
    }

    #[cfg(feature = "egui")]
    pub fn icon(&self) -> egui::ImageSource<'static> {
        self.icon.clone()
    }
}

impl<E: BrushItem> Default for Brush<E> {
    fn default() -> Self {
        Rectangle.into()
    }
}

#[derive(Resource, Deref, DerefMut)]
pub struct Brushes<E: BrushItem + std::cmp::PartialEq>(pub Vec<Brush<E>>);

impl<E: BrushItem + std::cmp::PartialEq> Default for Brushes<E> {
    fn default() -> Self {
        Self::new()
            .insert(Rectangle)
            .insert(Diamond)
            .insert(Round)
            .insert(Line)
            .insert(Random)
    }
}

impl<E: BrushItem> Brushes<E> {
    pub fn new() -> Self {
        Self(Vec::new())
    }

    pub fn insert(mut self, brush: impl Into<Brush<E>>) -> Self {
        self.push(brush.into());
        self
    }

    pub fn next_index(&self, current: usize) -> usize {
        match current + 1 {
            next if next < self.len() => next,
            _ => 0,
        }
    }
}

impl<E: BrushItem> FnOnce<(BrushParams<E>, E)> for Brush<E> {
    type Output = Vec<E>;

    extern "rust-call" fn call_once(self, args: (BrushParams<E>, E)) -> Self::Output {
        (self.func)(args.0, args.1)
    }
}

impl<E: BrushItem> FnMut<(BrushParams<E>, E)> for Brush<E> {
    extern "rust-call" fn call_mut(&mut self, args: (BrushParams<E>, E)) -> Self::Output {
        (self.func)(args.0, args.1)
    }
}

impl<E: BrushItem> Fn<(BrushParams<E>, E)> for Brush<E> {
    extern "rust-call" fn call(&self, args: (BrushParams<E>, E)) -> Self::Output {
        (self.func)(args.0, args.1)
    }
}

impl<E: BrushItem> FnOnce<(usize, BrushParams<E>, E)> for Brushes<E> {
    type Output = Vec<E>;

    extern "rust-call" fn call_once(self, args: (usize, BrushParams<E>, E)) -> Self::Output {
        match self.0.get(args.0) {
            Some(brush) => brush.call_once((args.1, args.2)),
            None => Vec::new(),
        }
    }
}

impl<E: BrushItem> FnMut<(usize, BrushParams<E>, E)> for Brushes<E> {
    extern "rust-call" fn call_mut(&mut self, args: (usize, BrushParams<E>, E)) -> Self::Output {
        match self.0.get(args.0) {
            Some(mut brush) => brush.call_mut((args.1, args.2)),
            None => Vec::new(),
        }
    }
}

impl<E: BrushItem> Fn<(usize, BrushParams<E>, E)> for Brushes<E> {
    extern "rust-call" fn call(&self, args: (usize, BrushParams<E>, E)) -> Self::Output {
        match self.0.get(args.0) {
            Some(brush) => brush.call((args.1, args.2)),
            None => Vec::new(),
        }
    }
}