use super::Walls;
#[cfg(feature = "bevy_reflect")]
use crate::traits::WorldPositionable;
use crate::traits::{TilePosition, WallStorage};
#[cfg(feature = "bevy")]
use bevy::prelude::*;
use hexx::Hex;
#[cfg(feature = "bevy_reflect")]
use hexx::HexLayout;
use std::fmt::Display;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
#[cfg_attr(feature = "bevy", derive(Component))]
#[cfg_attr(feature = "bevy", reflect(Component))]
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Tile {
pub(crate) pos: Hex,
pub(crate) walls: Walls,
}
impl TilePosition for Tile {
#[inline]
fn pos(&self) -> Hex {
self.pos
}
}
impl WallStorage for Tile {
#[inline]
fn walls(&self) -> &Walls {
&self.walls
}
#[inline]
fn walls_mut(&mut self) -> &mut Walls {
&mut self.walls
}
}
#[cfg(feature = "bevy_reflect")]
impl WorldPositionable for Tile {
#[inline]
fn to_vec2(&self, layout: &HexLayout) -> glam::Vec2 {
layout.hex_to_world_pos(self.pos)
}
#[inline]
fn to_vec3(&self, layout: &HexLayout) -> glam::Vec3 {
let pos = self.to_vec2(layout);
glam::Vec3::new(pos.x, 0., pos.y)
}
}
impl Tile {
#[must_use]
pub fn new(pos: Hex) -> Self {
Self {
pos,
walls: Walls::default(),
}
}
}
impl From<Hex> for Tile {
fn from(value: Hex) -> Self {
Self {
pos: value,
walls: Walls::default(),
}
}
}
impl Display for Tile {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({},{})", self.pos.x, self.pos.y)
}
}
#[cfg(test)]
mod test {
use super::*;
use hexx::EdgeDirection;
use rand::{thread_rng, Rng};
fn random_hex() -> Hex {
let mut rng = thread_rng();
Hex::new(rng.gen(), rng.gen())
}
#[test]
fn different_positions() {
let positions = [Hex::ZERO, Hex::new(1, 0), Hex::new(-1, 1), Hex::new(2, -2)];
let tiles = positions
.iter()
.map(|&pos| Tile::new(pos))
.collect::<Vec<_>>();
for (tile, &pos) in tiles.iter().zip(positions.iter()) {
assert_eq!(tile.pos, pos);
}
}
#[test]
fn hex_boundaries() {
let extreme_positions = [
Hex::new(i32::MAX, i32::MIN),
Hex::new(i32::MIN, i32::MAX),
Hex::new(0, i32::MAX),
Hex::new(i32::MIN, 0),
];
for pos in extreme_positions {
let tile = Tile::new(pos);
assert_eq!(tile.pos, pos);
}
}
#[test]
fn hex_tile_creation_and_properties() {
let hex = random_hex();
let tile = Tile::new(hex);
assert_eq!(tile.pos(), hex);
assert!(tile.walls().is_enclosed());
}
#[test]
fn hex_tile_from_hex() {
let hex = random_hex();
let tile = Tile::from(hex);
assert_eq!(tile.pos, hex);
assert_eq!(tile.walls, Walls::default());
}
#[test]
fn hex_hex_into_tile() {
let hex = random_hex();
let tile: Tile = hex.into();
assert_eq!(tile.pos, hex);
assert_eq!(tile.walls, Walls::default());
}
#[test]
fn hex_tile_display() {
let tile = Tile::new(Hex::new(3, -3));
assert_eq!(format!("{tile}"), "(3,-3)");
}
#[test]
fn hex_tile_wall_modifications() {
let mut tile = Tile::new(Hex::ZERO);
for direction in EdgeDirection::ALL_DIRECTIONS {
tile.walls.insert(direction);
}
assert_eq!(tile.walls.count(), 6);
for direction in EdgeDirection::ALL_DIRECTIONS {
tile.walls.remove(direction);
}
assert_eq!(tile.walls.count(), 0);
}
#[cfg(feature = "bevy_reflect")]
mod bevy_tests {
use super::*;
use glam::{Vec2, Vec3};
#[test]
fn hex_tile_to_vec2() {
let layout = HexLayout::default();
let tile = Tile::new(Hex::new(1, 0));
let vec2 = tile.to_vec2(&layout);
assert_eq!(vec2, Vec2::new(1.5, -0.8660254));
}
#[test]
fn hex_tile_to_vec3() {
let layout = HexLayout::default();
let tile = Tile::new(Hex::new(0, 1));
let vec3 = tile.to_vec3(&layout);
assert_eq!(vec3, Vec3::new(0.0, 0.0, -1.7320508));
}
}
}