| @@ -2099,6 +2099,7 @@ name = "unnamed" | |||
| version = "0.1.0" | |||
| dependencies = [ | |||
| "ggez", | |||
| "mint", | |||
| "serde", | |||
| "thiserror", | |||
| "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" | |||
| @@ -1,3 +0,0 @@ | |||
| [nothing] | |||
| path = "img/tiles/nothing.png" | |||
| blocking = true | |||
| @@ -0,0 +1,3 @@ | |||
| [nothing] | |||
| path = "/img/tiles/nothing.png" | |||
| blocking = true | |||
| @@ -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<T> = std::result::Result<T, Error>; | |||
| // 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); | |||
| @@ -1,69 +1,62 @@ | |||
| use std::convert::From; | |||
| pub struct IsometricVector2(mint::Point2<f32>); | |||
| pub struct CartesianVector2(mint::Point2<f32>); | |||
| 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<T> From<(T, T)> for CartesianVector2 | |||
| where | |||
| T: std::convert::Into<f32>, | |||
| { | |||
| fn from(tuple: (T, T)) -> CartesianVector2 { | |||
| CartesianVector2(mint::Point2 { | |||
| x: tuple.0.into(), | |||
| y: tuple.1.into(), | |||
| }) | |||
| } | |||
| } | |||
| impl From<IsometricVector> for CartesianVector { | |||
| fn from(iso: IsometricVector) -> CartesianVector { | |||
| CartesianVector((2 * iso.y() + iso.x()) / 2, (2 * iso.y() - iso.x()) / 2) | |||
| impl From<CartesianVector2> 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<CartesianVector> for IsometricVector { | |||
| fn from(cart: CartesianVector) -> IsometricVector { | |||
| IsometricVector(cart.x() - cart.y(), (cart.x() + cart.y()) / 2) | |||
| impl From<IsometricVector2> 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<T> From<(T, T)> for IsometricVector2 | |||
| where | |||
| T: std::convert::Into<f32>, | |||
| { | |||
| fn from(tuple: (T, T)) -> IsometricVector2 { | |||
| IsometricVector2(mint::Point2 { | |||
| x: tuple.0.into(), | |||
| y: tuple.1.into(), | |||
| }) | |||
| } | |||
| } | |||
| @@ -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<Vector3d, Tile>, | |||
| width: isize, | |||
| depth: isize, | |||
| height: isize, | |||
| data: HashMap<Vector3<isize>, 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<World> { | |||
| let builder = TileBuilder::new(ctx, tile_config)?; | |||
| let mut data: HashMap<Vector3<isize>, 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(()) | |||
| } | |||
| } | |||
| @@ -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<String, MasterTile>, | |||
| } | |||
| impl TileBuilder { | |||
| /// create a new tile builder | |||
| pub fn new(ctx: &mut Context, tiles_config: PathBuf) -> Result<TileBuilder> { | |||
| 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<String, RawTile> = toml::from_str(&raw_data)?; | |||
| let mut textures: HashMap<String, MasterTile> = 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<Tile> { | |||
| 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<Image>, | |||
| 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<Image>, | |||
| kind: String, | |||
| blocking: bool, | |||
| } | |||
| // for deserializing from the config file | |||
| #[derive(Deserialize)] | |||
| struct RawTile { | |||
| path: PathBuf, | |||