diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 657b931..7c23896 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -30,6 +30,8 @@ pub fn include_map(input: TokenStream) -> TokenStream { { use m3_map::Map; use m3_map::tiles::MapBaseTile; + use m3_map::Player; + use m3_map::tiles::ObjectTile; // include the bytes so that the compiler knows to recompile when the // map or tilesets changes const _: &[u8] = ::core::include_bytes!(#path); diff --git a/map/src/lib.rs b/map/src/lib.rs index 57b455a..e11b91f 100644 --- a/map/src/lib.rs +++ b/map/src/lib.rs @@ -1,16 +1,28 @@ use self_rust_tokenize::SelfRustTokenize; -use std::path::Path; +use std::{iter, path::Path}; use thiserror::Error; use tiled::{LayerType, Loader}; pub mod tiles; -use tiles::{InvalidTileID, MapBaseTile}; +use tiles::{InvalidTileID, MapBaseTile, ObjectTile, PlayerTile, Tile}; + +#[derive(Clone, Debug, SelfRustTokenize)] +pub struct Player { + pub start: (u8, u8), + pub goal: Option<(u8, u8)> +} #[derive(Clone, Debug, SelfRustTokenize)] pub struct Map { pub width: u8, pub height: u8, - pub base_layer: Vec> + pub base_layer: Vec>, + pub object_layer: Vec>>, + pub global_goal: Option<(u8, u8)>, + pub player_1: Player, + pub player_2: Option, + pub player_3: Option, + pub player_4: Option } #[derive(Error, Debug)] @@ -28,15 +40,24 @@ pub enum MapError { #[error("Map is to hight. Max size is 255x255 tiles")] ToHight, #[error("{0}")] - InvalidTileId(#[from] InvalidTileID) + InvalidTileId(#[from] InvalidTileID), + #[error("Map needs at least one player")] + NoPlayer } impl Map { + // this is ugly. Should i refactor this? pub fn from_tmx(path: impl AsRef) -> Result { let map = Loader::new().load_tmx_map(path)?; let width: u8 = map.width.try_into().map_err(|_| MapError::ToWidth)?; let height: u8 = map.height.try_into().map_err(|_| MapError::ToHight)?; let mut base_layer = Vec::with_capacity(height as usize); + let mut object_layer = Vec::with_capacity(height as usize); + let mut global_goal = None; + let mut player_1 = None; + let mut player_2 = None; + let mut player_3 = None; + let mut player_4 = None; for (i, layer) in map.layers().enumerate() { match i { 0 => match layer.layer_type() { @@ -55,13 +76,79 @@ impl Map { }, _ => return Err(MapError::WrongLayer(i, "TileLayer".to_owned())) }, + 1 => match layer.layer_type() { + LayerType::Tiles(tile_layer) => { + for x in 0..width { + let mut column = Vec::with_capacity(width as usize); + for y in 0..height { + let tile = match tile_layer.get_tile(x.into(), y.into()) { + Some(tile) => Some(ObjectTile::try_from(tile.id())?), + None => None + }; + column.push(tile); + } + object_layer.push(column); + } + }, + _ => return Err(MapError::WrongLayer(i, "TileLayer".to_owned())) + }, + 2 => match layer.layer_type() { + LayerType::Tiles(tile_layer) => { + for x in 0..width { + for y in 0..height { + if let Some(tile) = + tile_layer.get_tile(x.into(), y.into()) + { + let tile = PlayerTile::try_from(tile.id())?; + match tile { + PlayerTile::Car1 => { + player_1 = Some(Player { + start: (x, y), + goal: None + }) + }, + PlayerTile::Car2 => { + player_2 = Some(Player { + start: (x, y), + goal: None + }) + }, + PlayerTile::Car3 => { + player_3 = Some(Player { + start: (x, y), + goal: None + }) + }, + PlayerTile::Car4 => { + player_4 = Some(Player { + start: (x, y), + goal: None + }) + }, + PlayerTile::GlobalGoal => { + global_goal = Some((x, y)) + }, + } + } + } + } + }, + _ => return Err(MapError::WrongLayer(i, "TileLayer".to_owned())) + }, _ => return Err(MapError::ToManyLayers) } } + let player_1 = player_1.ok_or(MapError::NoPlayer)?; Ok(Map { width, height, - base_layer + base_layer, + object_layer, + global_goal, + player_1, + player_2, + player_3, + player_4 }) } @@ -74,4 +161,35 @@ impl Map { .map(move |(y, item)| (x as u8, y as u8, item)) }) } + + /// return an iterator over all ObjectTiles and its x and y postion + pub fn iter_object_layer(&self) -> impl Iterator + '_ { + self.object_layer.iter().enumerate().flat_map(|(x, y_vec)| { + y_vec + .iter() + .enumerate() + .filter_map(move |(y, item)| item.map(|item| (x as u8, y as u8, item))) + }) + } + + /// return an iterator over all player goals tiles and its x and y postion + pub fn iter_player_goals(&self) -> impl Iterator + '_ { + iter::once(self.global_goal) + .flat_map(|goal| goal.map(|(x, y)| (x, y, PlayerTile::GlobalGoal))) + } + + /// return an iterator over all static Tiles and its x and y postion. + /// starting from the lowest layer + pub fn iter_all(&self) -> impl Iterator + '_ { + let base = self + .iter_base_layer() + .map(|(x, y, tile)| (x, y, Tile::MapBaseTile(tile.to_owned()))); + let objects = self + .iter_object_layer() + .map(|(x, y, tile)| (x, y, Tile::MapObjectTile(tile.to_owned()))); + let goals = self + .iter_player_goals() + .map(|(x, y, tile)| (x, y, Tile::PlayerTile(tile))); + base.chain(objects).chain(goals) + } } diff --git a/map/src/main.rs b/map/src/main.rs index 031f837..d7c64be 100644 --- a/map/src/main.rs +++ b/map/src/main.rs @@ -8,12 +8,12 @@ pub struct Opt { fn main() { let opt = Opt::parse(); - let result = Map::from_tmx(&opt.file); + let result = Map::from_tmx(opt.file); match result { Err(err) => { eprintln!("ERROR: {err}"); std::process::exit(1); }, - Ok(map) => println!("{map:#?}") + Ok(map) => println!("{map:#?}\nmap is valid") } } diff --git a/map/src/tiles.rs b/map/src/tiles.rs index f2090e1..eee896a 100644 --- a/map/src/tiles.rs +++ b/map/src/tiles.rs @@ -2,6 +2,18 @@ use num_enum::TryFromPrimitive; use self_rust_tokenize::SelfRustTokenize; use thiserror::Error; +pub enum Tile { + MapBaseTile(MapBaseTile), + MapObjectTile(ObjectTile), + PlayerTile(PlayerTile) +} + +#[derive(Debug, Copy, Clone, Error)] +pub enum InvalidTileID { + #[error("invalid tiel id {0}")] + InvalidId(u32) +} + ///Store all Tiles, with can be used at the map background #[derive( Clone, Copy, Debug, Default, Eq, SelfRustTokenize, PartialEq, TryFromPrimitive, @@ -10,19 +22,53 @@ use thiserror::Error; pub enum MapBaseTile { //numbers must match them from the Tiled tilesets #[default] - Grass = 0, + Grass = 0 +} + +impl TryFrom for MapBaseTile { + type Error = InvalidTileID; + fn try_from(value: u32) -> Result { + let value_u8: u8 = value + .try_into() + .map_err(|_| Self::Error::InvalidId(value))?; + Self::try_from_primitive(value_u8).map_err(|_| Self::Error::InvalidId(value)) + } +} + +///Store all Tiles, with can be place the layer above the background +#[derive(Clone, Copy, Debug, Eq, SelfRustTokenize, PartialEq, TryFromPrimitive)] +#[repr(u8)] +pub enum ObjectTile { + //numbers must match them from the Tiled tilesets Stone = 1 } -#[derive(Debug, Copy, Clone, Error)] -pub enum InvalidTileID { - #[error("invalid tiel id {0}")] - InvalidId(u32) +impl TryFrom for ObjectTile { + type Error = InvalidTileID; + fn try_from(value: u32) -> Result { + let value_u8: u8 = value + .try_into() + .map_err(|_| Self::Error::InvalidId(value))?; + Self::try_from_primitive(value_u8).map_err(|_| Self::Error::InvalidId(value)) + } } -impl TryFrom for MapBaseTile { +///Store all Tiles, with can be place the layer above the background +#[derive(Clone, Copy, Debug, Eq, SelfRustTokenize, PartialEq, TryFromPrimitive)] +#[repr(u8)] +pub enum PlayerTile { + //numbers must match them from the Tiled tilesets + Car1 = 0, + Car2 = 1, + Car3 = 2, + Car4 = 3, + //goal, which can be used by all players + GlobalGoal = 4 +} + +impl TryFrom for PlayerTile { type Error = InvalidTileID; - fn try_from(value: u32) -> Result { + fn try_from(value: u32) -> Result { let value_u8: u8 = value .try_into() .map_err(|_| Self::Error::InvalidId(value))?; diff --git a/pc/assets/img/BaseTiles/BaseTiles.tsx b/pc/assets/img/BaseTiles/BaseTiles.tsx index 3b8b6d0..6c77715 100644 --- a/pc/assets/img/BaseTiles/BaseTiles.tsx +++ b/pc/assets/img/BaseTiles/BaseTiles.tsx @@ -1,10 +1,7 @@ - + - - - diff --git a/pc/assets/img/BaseTiles/Grass mit Rand.png b/pc/assets/img/BaseTiles/Grass mit Rand.png deleted file mode 100644 index 9b0e368..0000000 Binary files a/pc/assets/img/BaseTiles/Grass mit Rand.png and /dev/null differ diff --git a/pc/assets/img/BaseTiles/Grass mit Rand2.png b/pc/assets/img/BaseTiles/Grass mit Rand2.png deleted file mode 100644 index 838fa62..0000000 Binary files a/pc/assets/img/BaseTiles/Grass mit Rand2.png and /dev/null differ diff --git a/pc/assets/img/BaseTiles/grass.png b/pc/assets/img/BaseTiles/grass.png index b3649cf..838fa62 100644 Binary files a/pc/assets/img/BaseTiles/grass.png and b/pc/assets/img/BaseTiles/grass.png differ diff --git a/pc/assets/img/BaseTiles/stone.png b/pc/assets/img/BaseTiles/stone.png deleted file mode 100644 index 3e2b580..0000000 Binary files a/pc/assets/img/BaseTiles/stone.png and /dev/null differ diff --git a/pc/assets/img/ObjectTiles/ObjectTiles.tsx b/pc/assets/img/ObjectTiles/ObjectTiles.tsx new file mode 100644 index 0000000..dbb2d51 --- /dev/null +++ b/pc/assets/img/ObjectTiles/ObjectTiles.tsx @@ -0,0 +1,7 @@ + + + + + + + diff --git a/pc/assets/img/BaseTiles/Stein ohne Hintergrund.png b/pc/assets/img/ObjectTiles/stone.png similarity index 100% rename from pc/assets/img/BaseTiles/Stein ohne Hintergrund.png rename to pc/assets/img/ObjectTiles/stone.png diff --git a/pc/assets/img/Player/Player.tsx b/pc/assets/img/Player/Player.tsx new file mode 100644 index 0000000..e7cbd19 --- /dev/null +++ b/pc/assets/img/Player/Player.tsx @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/pc/assets/img/goal.png b/pc/assets/img/Player/goal.png similarity index 100% rename from pc/assets/img/goal.png rename to pc/assets/img/Player/goal.png diff --git a/pc/assets/img/Auto1.png b/pc/assets/img/Player/player1_car.png similarity index 100% rename from pc/assets/img/Auto1.png rename to pc/assets/img/Player/player1_car.png diff --git a/pc/assets/img/player2_car.png b/pc/assets/img/Player/player2_car.png similarity index 100% rename from pc/assets/img/player2_car.png rename to pc/assets/img/Player/player2_car.png diff --git a/pc/assets/img/player3_car.png b/pc/assets/img/Player/player3_car.png similarity index 100% rename from pc/assets/img/player3_car.png rename to pc/assets/img/Player/player3_car.png diff --git a/pc/assets/img/player4_car.png b/pc/assets/img/Player/player4_car.png similarity index 100% rename from pc/assets/img/player4_car.png rename to pc/assets/img/Player/player4_car.png diff --git a/pc/assets/img/player1_car.png b/pc/assets/img/player1_car.png deleted file mode 100644 index 12f4a22..0000000 Binary files a/pc/assets/img/player1_car.png and /dev/null differ diff --git a/pc/assets/level/001.tmx b/pc/assets/level/001.tmx index 559f5a4..8b79021 100644 --- a/pc/assets/level/001.tmx +++ b/pc/assets/level/001.tmx @@ -1,17 +1,45 @@ - + - + + + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,2,2,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,2,2,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + + + + +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0, +0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,3,0,0,3,3,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,3,3,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + + + + +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0, +0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,4,0,0,0,0,0,0,0,0,0,0,7,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 diff --git a/pc/src/main.rs b/pc/src/main.rs index 64e197b..a3828fb 100644 --- a/pc/src/main.rs +++ b/pc/src/main.rs @@ -4,6 +4,7 @@ use tetra::{ Context, ContextBuilder, State }; type Vec2 = vek::vec::repr_c::vec2::Vec2; +use log::info; use m3_macro::include_map; use m3_map::Map; use once_cell::sync::Lazy; @@ -11,7 +12,6 @@ use tetra::{ graphics::{DrawParams, Texture}, time::get_delta_time }; -use log::{info}; mod tiles; use tiles::Textures; @@ -60,7 +60,7 @@ impl State for GameState { (window_size.0 / map.width as i32) as f32, (window_size.1 / map.height as i32) as f32 ); - for (x, y, tile) in map.iter_base_layer() { + for (x, y, tile) in map.iter_all() { let texture = tile.texture(&self.textures); texture.draw( ctx, diff --git a/pc/src/tiles.rs b/pc/src/tiles.rs index d7eebf6..bb0754b 100644 --- a/pc/src/tiles.rs +++ b/pc/src/tiles.rs @@ -1,11 +1,17 @@ use crate::GetTexture; pub use m3_map::tiles::MapBaseTile; +use m3_map::tiles::{ObjectTile, PlayerTile, Tile}; use tetra::{graphics::Texture, Context}; ///Store all Textures pub struct Textures { grass: Texture, - stone: Texture + stone: Texture, + player1_car: Texture, + player2_car: Texture, + player3_car: Texture, + player4_car: Texture, + global_goal: Texture } impl Textures { @@ -19,7 +25,32 @@ impl Textures { .unwrap(), stone: Texture::from_encoded( ctx, - include_bytes!("../assets/img/BaseTiles/stone.png") + include_bytes!("../assets/img/ObjectTiles/stone.png") + ) + .unwrap(), + player1_car: Texture::from_encoded( + ctx, + include_bytes!("../assets/img/Player/player1_car.png") + ) + .unwrap(), + player2_car: Texture::from_encoded( + ctx, + include_bytes!("../assets/img/Player/player2_car.png") + ) + .unwrap(), + player3_car: Texture::from_encoded( + ctx, + include_bytes!("../assets/img/Player/player3_car.png") + ) + .unwrap(), + player4_car: Texture::from_encoded( + ctx, + include_bytes!("../assets/img/Player/player4_car.png") + ) + .unwrap(), + global_goal: Texture::from_encoded( + ctx, + include_bytes!("../assets/img/Player/goal.png") ) .unwrap() } @@ -30,12 +61,43 @@ impl<'a> GetTexture<'a> for MapBaseTile { ///get Texture assioated with this Tile fn texture(&self, textures: &'a Textures) -> &'a Texture { match self { - Self::Grass => &textures.grass, + Self::Grass => &textures.grass + } + } +} + +impl<'a> GetTexture<'a> for ObjectTile { + ///get Texture assioated with this Tile + fn texture(&self, textures: &'a Textures) -> &'a Texture { + match self { Self::Stone => &textures.stone } } } +impl<'a> GetTexture<'a> for PlayerTile { + ///get Texture assioated with this Tile + fn texture(&self, textures: &'a Textures) -> &'a Texture { + match self { + Self::Car1 => &textures.player1_car, + Self::Car2 => &textures.player1_car, + Self::Car3 => &textures.player1_car, + Self::Car4 => &textures.player1_car, + Self::GlobalGoal => &textures.global_goal + } + } +} + +impl<'a> GetTexture<'a> for Tile { + fn texture(&self, textures: &'a Textures) -> &'a Texture { + match self { + Tile::MapBaseTile(tile) => tile.texture(textures), + Tile::MapObjectTile(tile) => tile.texture(textures), + Tile::PlayerTile(tile) => tile.texture(textures) + } + } +} + #[cfg(test)] mod tests { use crate::tiles::Textures;