diff --git a/Cargo.lock b/Cargo.lock index 4b466ea..777258b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2099,6 +2099,7 @@ name = "unnamed" version = "0.1.0" dependencies = [ "ggez", + "mint", "serde", "thiserror", "toml", diff --git a/Cargo.toml b/Cargo.toml index f11a2b1..f32065d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,3 +11,4 @@ ggez = "0.5.1" thiserror = "1.0.20" serde = { version = "1.0.114", features = ["derive"] } toml = "0.5.6" +mint = "0.5.5" diff --git a/data/tiles.toml b/data/tiles.toml deleted file mode 100644 index cb62861..0000000 --- a/data/tiles.toml +++ /dev/null @@ -1,3 +0,0 @@ -[nothing] -path = "img/tiles/nothing.png" -blocking = true \ No newline at end of file diff --git a/img/nothing.png b/img/nothing.png deleted file mode 100644 index 2ee4ff7..0000000 Binary files a/img/nothing.png and /dev/null differ diff --git a/conf.toml b/resources/data/conf.toml similarity index 100% rename from conf.toml rename to resources/data/conf.toml diff --git a/resources/data/tiles.toml b/resources/data/tiles.toml new file mode 100644 index 0000000..2d7e957 --- /dev/null +++ b/resources/data/tiles.toml @@ -0,0 +1,3 @@ +[nothing] +path = "/img/tiles/nothing.png" +blocking = true \ No newline at end of file diff --git a/resources/img/building.png b/resources/img/building.png new file mode 100644 index 0000000..286a1b7 Binary files /dev/null and b/resources/img/building.png differ diff --git a/resources/img/tiles/nothing.png b/resources/img/tiles/nothing.png new file mode 100644 index 0000000..67fc5cb Binary files /dev/null and b/resources/img/tiles/nothing.png differ diff --git a/src/main.rs b/src/main.rs index a3a9eb3..87becb6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,30 +1,44 @@ +#![allow(unused_imports)] + +// mods mod util; mod world; +// namespacing use ggez::{ event::{self, EventHandler}, graphics, Context, ContextBuilder, GameResult, }; use world::World; +/// lazy result type pub type Result = std::result::Result; +// error types #[derive(Debug, thiserror::Error)] pub enum Error { + // external errors #[error(transparent)] Stdio(#[from] std::io::Error), #[error(transparent)] Toml(#[from] toml::de::Error), + #[error(transparent)] + Ggez(#[from] ggez::error::GameError), + // internal errors + #[error("could not find {expected} MasterTile on TileBuilder")] + NoMasterTile { expected: String }, } +// primary game state struct MainState { world: World, } impl MainState { - pub fn new(_ctx: &mut Context) -> MainState { + // create a new gamestate + pub fn new(ctx: &mut Context) -> MainState { MainState { - world: World::new(20, 20, 3), + world: World::new(ctx, "/data/tiles.toml".into(), 5, 5, 1).expect("failed init world"), } } } @@ -35,15 +49,19 @@ impl EventHandler for MainState { } fn draw(&mut self, ctx: &mut Context) -> GameResult<()> { - graphics::clear(ctx, graphics::WHITE); + // clear the screen + graphics::clear(ctx, graphics::BLACK); + + self.world.render(ctx).expect("failed to render"); + + // render the context graphics::present(ctx) } } fn main() { - let (mut ctx, mut event_loop) = ContextBuilder::new("unnamed", "Isabelle L.") - .build() - .expect("failed to start context"); + let cb = ContextBuilder::new("unnamed", "Isabelle L.").add_resource_path("./"); + let (mut ctx, mut event_loop) = cb.build().expect("failed to start context"); let mut state = MainState::new(&mut ctx); diff --git a/src/util.rs b/src/util.rs index cbc34b8..6358265 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,69 +1,62 @@ -use std::convert::From; +pub struct IsometricVector2(mint::Point2); +pub struct CartesianVector2(mint::Point2); -pub struct CartesianVector(usize, usize); -pub struct IsometricVector(usize, usize); -pub struct Vector3d(usize, usize, usize); - -impl Vector3d { - pub fn x(&self) -> usize { - self.0 - } - - pub fn y(&self) -> usize { - self.1 - } - - pub fn z(&self) -> usize { - self.2 - } -} - -impl From<(usize, usize, usize)> for Vector3d { - fn from(tuple: (usize, usize, usize)) -> Vector3d { - Vector3d(tuple.0, tuple.1, tuple.2) - } -} - -impl CartesianVector { - pub fn x(&self) -> usize { - self.0 +impl IsometricVector2 { + pub fn x(&self) -> f32 { + self.0.x } - pub fn y(&self) -> usize { - self.1 + pub fn y(&self) -> f32 { + self.0.y } } -impl IsometricVector { - pub fn x(&self) -> usize { - self.0 +impl CartesianVector2 { + pub fn x(&self) -> f32 { + self.0.x } - pub fn y(&self) -> usize { - self.1 + pub fn y(&self) -> f32 { + self.0.y } } -impl From<(usize, usize)> for CartesianVector { - fn from(tuple: (usize, usize)) -> CartesianVector { - CartesianVector(tuple.0, tuple.1) +impl From<(T, T)> for CartesianVector2 +where + T: std::convert::Into, +{ + fn from(tuple: (T, T)) -> CartesianVector2 { + CartesianVector2(mint::Point2 { + x: tuple.0.into(), + y: tuple.1.into(), + }) } } -impl From for CartesianVector { - fn from(iso: IsometricVector) -> CartesianVector { - CartesianVector((2 * iso.y() + iso.x()) / 2, (2 * iso.y() - iso.x()) / 2) +impl From for IsometricVector2 { + fn from(cart: CartesianVector2) -> IsometricVector2 { + let x = cart.x() - cart.y(); + let y = (cart.x() + cart.y()) / 2.0; + IsometricVector2(mint::Point2 { x, y }) } } -impl From for IsometricVector { - fn from(cart: CartesianVector) -> IsometricVector { - IsometricVector(cart.x() - cart.y(), (cart.x() + cart.y()) / 2) +impl From for CartesianVector2 { + fn from(iso: IsometricVector2) -> CartesianVector2 { + let x = (2.0 * iso.y() + iso.x()) / 2.0; + let y = (2.0 * iso.y() - iso.x()) / 2.0; + CartesianVector2(mint::Point2 { x, y }) } } -impl From<(usize, usize)> for IsometricVector { - fn from(tuple: (usize, usize)) -> IsometricVector { - IsometricVector(tuple.0, tuple.1) +impl From<(T, T)> for IsometricVector2 +where + T: std::convert::Into, +{ + fn from(tuple: (T, T)) -> IsometricVector2 { + IsometricVector2(mint::Point2 { + x: tuple.0.into(), + y: tuple.1.into(), + }) } } diff --git a/src/world.rs b/src/world.rs index dcdbe88..d8f8590 100644 --- a/src/world.rs +++ b/src/world.rs @@ -1,23 +1,76 @@ +// modules mod tile; -use crate::util::Vector3d; -use std::collections::HashMap; +// namespacing +use crate::{util::*, Result}; +use ggez::{ + graphics::{self, DrawParam}, + Context, +}; +use mint::Vector3; +use std::{collections::HashMap, path::PathBuf}; use tile::*; +/// represent a whole rendered world pub struct World { - width: usize, - height: usize, - depth: usize, - data: HashMap, + width: isize, + depth: isize, + height: isize, + data: HashMap, Tile>, + _builder: TileBuilder, + offset: CartesianVector2, } impl World { - pub fn new(width: usize, height: usize, depth: usize) -> World { - World { + /// create a new world instance + pub fn new( + ctx: &mut Context, + tile_config: PathBuf, + width: isize, + depth: isize, + height: isize, + ) -> Result { + let builder = TileBuilder::new(ctx, tile_config)?; + let mut data: HashMap, Tile> = HashMap::new(); + let offset: CartesianVector2 = (350.0f32, 100.0f32).into(); + + for x in 0..width { + for y in 0..depth { + for z in 0..height { + data.insert([x, y, z].into(), builder.build("nothing".to_owned())?); + } + } + } + + Ok(World { width, height, depth, - data: HashMap::new(), + data, + _builder: builder, + offset, + }) + } + + /// renders the world to the window + pub fn render(&self, ctx: &mut Context) -> Result<()> { + for x in 0..self.width { + for y in 0..self.depth { + for z in 0..self.height { + let tile = self.data.get(&[x, y, z].into()).unwrap(); + let iso_coord: IsometricVector2 = + CartesianVector2::from((x as f32, y as f32)).into(); + let dest = [ + (iso_coord.x() * 16.0 + self.offset.x()) as f32, + (iso_coord.y() * 16.0 + self.offset.y()) as f32, + ]; + let param = DrawParam::default().dest(mint::Point2::from_slice(&dest)); + + graphics::draw(ctx, tile.texture(), param)?; + } + } } + + Ok(()) } } diff --git a/src/world/tile.rs b/src/world/tile.rs index fe60f13..35a1490 100644 --- a/src/world/tile.rs +++ b/src/world/tile.rs @@ -1,24 +1,27 @@ -use crate::Result; -use ggez::graphics::Image; -use ggez::Context; +use crate::{Error, Result}; +use ggez::{graphics::Image, Context}; use serde::Deserialize; -use std::collections::HashMap; -use std::path::PathBuf; -use std::rc::Rc; +use std::io::Read; +use std::{collections::HashMap, path::PathBuf, rc::Rc}; +/// used to contain all of the textures and tile data pub struct TileBuilder { textures: HashMap, } impl TileBuilder { + /// create a new tile builder pub fn new(ctx: &mut Context, tiles_config: PathBuf) -> Result { - let raw_data = std::fs::read_to_string(tiles_config)?; + let mut file = ggez::filesystem::open(ctx, tiles_config)?; + let mut raw_data = String::new(); + file.read_to_string(&mut raw_data)?; + let raw_map: HashMap = toml::from_str(&raw_data)?; let mut textures: HashMap = HashMap::new(); raw_map.iter().for_each(|(kind, raw_tile)| { let tile = MasterTile { - texture: Image::new(ctx, &raw_tile.path).expect("failed to load image"), + texture: Rc::new(Image::new(ctx, &raw_tile.path).expect("failed to load image")), kind: kind.to_owned(), blocking: raw_tile.blocking, }; @@ -27,20 +30,43 @@ impl TileBuilder { Ok(TileBuilder { textures: textures }) } + + /// build a new tile + pub fn build(&self, kind: String) -> Result { + if let Some(master_tile) = self.textures.get(&kind) { + Ok(Tile { + texture: master_tile.texture.clone(), + kind: master_tile.kind.clone(), + blocking: master_tile.blocking, + }) + } else { + Err(Error::NoMasterTile { expected: kind }) + } + } } +/// tile for external use in rendering and etc +#[allow(dead_code)] pub struct Tile { texture: Rc, kind: String, blocking: bool, } +impl Tile { + pub fn texture(&self) -> &Image { + &*self.texture + } +} + +// internal tile type that contains the image texture data struct MasterTile { - texture: Image, + texture: Rc, kind: String, blocking: bool, } +// for deserializing from the config file #[derive(Deserialize)] struct RawTile { path: PathBuf,