twmap 0.12.5

Parse, edit and save Teeworlds and DDNet maps
Documentation
use crate::*;

impl TileFlags {
    /// Mirrors the tile, switches left <-> right.
    /// **Doesn't work with directional physics tiles!**
    ///
    /// Use [TileFlips::flip_x] instead!
    pub fn flip_x(&mut self) {
        match self.contains(TileFlags::ROTATE) {
            false => self.toggle(TileFlags::FLIP_X),
            true => self.toggle(TileFlags::FLIP_Y),
        }
    }

    /// Mirrors the tile vertically, switches up <-> down.
    /// **Doesn't work with directional physics tiles!**
    ///
    /// Use [TileFlips::flip_y] instead!
    pub fn flip_y(&mut self) {
        match self.contains(TileFlags::ROTATE) {
            false => self.toggle(TileFlags::FLIP_Y),
            true => self.toggle(TileFlags::FLIP_X),
        }
    }

    /// Performs a 90° clockwise rotation.
    ///
    /// For generic programming, use [TileFlips::rotate_cw] instead!
    pub fn rotate_cw(&mut self) {
        if self.contains(TileFlags::ROTATE) {
            self.toggle(TileFlags::FLIP_Y | TileFlags::FLIP_X);
        }
        self.toggle(TileFlags::ROTATE);
    }

    /// Performs a 90° counterclockwise rotation.
    ///
    /// For generic programming, use [TileFlips::rotate_ccw] instead!
    pub fn rotate_ccw(&mut self) {
        if !self.contains(TileFlags::ROTATE) {
            self.toggle(TileFlags::FLIP_Y | TileFlags::FLIP_X);
        }
        self.toggle(TileFlags::ROTATE);
    }

    /// In contrast to the [`TilesLayer`], the directional tiles of physics layers only have 4 valid rotation.
    /// The rotate methods keep the correct state, `flip_h` and `flip_v` on the other hand don't.
    pub fn flip_x_physics(&mut self) {
        if self.contains(TileFlags::ROTATE) {
            self.toggle(TileFlags::FLIP_Y | TileFlags::FLIP_X);
        }
    }

    /// In contrast to the [`TilesLayer`], the directional tiles of physics layers only have 4 valid rotation.
    /// The rotate methods keep the correct state, `flip_h` and `flip_v` on the other hand don't.
    pub fn flip_y_physics(&mut self) {
        if !self.contains(TileFlags::ROTATE) {
            self.toggle(TileFlags::FLIP_Y | TileFlags::FLIP_X);
        }
    }
}

trait TilePrivate {
    fn mirror_id(id: u8) -> u8 {
        id
    }
    fn directional_physics_tile(_id: u8) -> bool {
        false
    }
}

impl TilePrivate for Tile {}

fn mirror_lasers(mut id: u8) -> u8 {
    if (203..=205).contains(&id) {
        id += (206 - id) * 2;
    } else if (207..=209).contains(&id) {
        id -= (id - 206) * 2;
    }
    id
}

impl TilePrivate for GameTile {
    fn mirror_id(id: u8) -> u8 {
        mirror_lasers(id)
    }

    fn directional_physics_tile(id: u8) -> bool {
        matches!(id, 60 | 61 | 64 | 65 | 67 | 224 | 225)
    }
}
impl TilePrivate for Tele {}
impl TilePrivate for Switch {
    fn mirror_id(id: u8) -> u8 {
        mirror_lasers(id)
    }
    fn directional_physics_tile(id: u8) -> bool {
        matches!(id, 224 | 225)
    }
}
impl TilePrivate for Tune {}

/// Generic tile flips and rotates for every tile type.
pub trait TileFlips: AnyTile {
    /// Left <-> Right
    fn flip_x(&mut self);
    /// Up <-> Down
    fn flip_y(&mut self);
    /// Rotates clockwise
    fn rotate_cw(&mut self);
    /// Rotates counterclockwise
    fn rotate_ccw(&mut self);
}

impl<T: AnyTile + TilePrivate> TileFlips for T {
    fn flip_x(&mut self) {
        let id = self.id();
        if let Some(flags) = self.flags_mut() {
            if T::directional_physics_tile(id) {
                flags.flip_x_physics();
            } else {
                flags.flip_x();
            }
            *self.id_mut() = T::mirror_id(*self.id_mut());
        }
    }

    fn flip_y(&mut self) {
        let id = self.id();
        if let Some(flags) = self.flags_mut() {
            if T::directional_physics_tile(id) {
                flags.flip_y_physics();
            } else {
                flags.flip_y();
            }
            *self.id_mut() = T::mirror_id(*self.id_mut());
        }
    }

    fn rotate_cw(&mut self) {
        if let Some(flags) = self.flags_mut() {
            flags.rotate_cw();
        }
    }

    fn rotate_ccw(&mut self) {
        if let Some(flags) = self.flags_mut() {
            flags.rotate_ccw();
        }
    }
}

impl TileFlips for Speedup {
    fn flip_x(&mut self) {
        let mut angle = i16::from(self.angle);
        angle = 180 - angle;
        if angle < 0 {
            angle += 360;
        }
        self.angle = angle.into();
    }

    fn flip_y(&mut self) {
        let mut angle = i16::from(self.angle);
        angle = 180 - angle;
        angle *= -1;
        angle = 180 - angle;
        angle %= 360;
        self.angle = angle.into();
    }

    fn rotate_cw(&mut self) {
        let mut angle = i16::from(self.angle);
        angle += 90;
        angle %= 360;
        self.angle = angle.into();
    }

    fn rotate_ccw(&mut self) {
        let mut angle = i16::from(self.angle);
        angle += 270;
        angle %= 360;
        self.angle = angle.into();
    }
}