| @@ -2099,6 +2099,7 @@ name = "unnamed" | |||||
| version = "0.1.0" | version = "0.1.0" | ||||
| dependencies = [ | dependencies = [ | ||||
| "ggez", | "ggez", | ||||
| "mint", | |||||
| "serde", | "serde", | ||||
| "thiserror", | "thiserror", | ||||
| "toml", | "toml", | ||||
| @@ -11,3 +11,4 @@ ggez = "0.5.1" | |||||
| thiserror = "1.0.20" | thiserror = "1.0.20" | ||||
| serde = { version = "1.0.114", features = ["derive"] } | serde = { version = "1.0.114", features = ["derive"] } | ||||
| toml = "0.5.6" | 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 util; | ||||
| mod world; | mod world; | ||||
| // namespacing | |||||
| use ggez::{ | use ggez::{ | ||||
| event::{self, EventHandler}, | event::{self, EventHandler}, | ||||
| graphics, Context, ContextBuilder, GameResult, | graphics, Context, ContextBuilder, GameResult, | ||||
| }; | }; | ||||
| use world::World; | use world::World; | ||||
| /// lazy result type | |||||
| pub type Result<T> = std::result::Result<T, Error>; | pub type Result<T> = std::result::Result<T, Error>; | ||||
| // error types | |||||
| #[derive(Debug, thiserror::Error)] | #[derive(Debug, thiserror::Error)] | ||||
| pub enum Error { | pub enum Error { | ||||
| // external errors | |||||
| #[error(transparent)] | #[error(transparent)] | ||||
| Stdio(#[from] std::io::Error), | Stdio(#[from] std::io::Error), | ||||
| #[error(transparent)] | #[error(transparent)] | ||||
| Toml(#[from] toml::de::Error), | 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 { | struct MainState { | ||||
| world: World, | world: World, | ||||
| } | } | ||||
| impl MainState { | impl MainState { | ||||
| pub fn new(_ctx: &mut Context) -> MainState { | |||||
| // create a new gamestate | |||||
| pub fn new(ctx: &mut Context) -> MainState { | |||||
| 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<()> { | 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) | graphics::present(ctx) | ||||
| } | } | ||||
| } | } | ||||
| fn main() { | 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); | 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; | 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::*; | use tile::*; | ||||
| /// represent a whole rendered world | |||||
| pub struct 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 { | 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, | width, | ||||
| height, | height, | ||||
| depth, | 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 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 { | pub struct TileBuilder { | ||||
| textures: HashMap<String, MasterTile>, | textures: HashMap<String, MasterTile>, | ||||
| } | } | ||||
| impl TileBuilder { | impl TileBuilder { | ||||
| /// create a new tile builder | |||||
| pub fn new(ctx: &mut Context, tiles_config: PathBuf) -> Result<TileBuilder> { | 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 raw_map: HashMap<String, RawTile> = toml::from_str(&raw_data)?; | ||||
| let mut textures: HashMap<String, MasterTile> = HashMap::new(); | let mut textures: HashMap<String, MasterTile> = HashMap::new(); | ||||
| raw_map.iter().for_each(|(kind, raw_tile)| { | raw_map.iter().for_each(|(kind, raw_tile)| { | ||||
| let tile = MasterTile { | 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(), | kind: kind.to_owned(), | ||||
| blocking: raw_tile.blocking, | blocking: raw_tile.blocking, | ||||
| }; | }; | ||||
| @@ -27,20 +30,43 @@ impl TileBuilder { | |||||
| Ok(TileBuilder { textures: textures }) | 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 { | pub struct Tile { | ||||
| texture: Rc<Image>, | texture: Rc<Image>, | ||||
| kind: String, | kind: String, | ||||
| blocking: bool, | blocking: bool, | ||||
| } | } | ||||
| impl Tile { | |||||
| pub fn texture(&self) -> &Image { | |||||
| &*self.texture | |||||
| } | |||||
| } | |||||
| // internal tile type that contains the image texture data | |||||
| struct MasterTile { | struct MasterTile { | ||||
| texture: Image, | |||||
| texture: Rc<Image>, | |||||
| kind: String, | kind: String, | ||||
| blocking: bool, | blocking: bool, | ||||
| } | } | ||||
| // for deserializing from the config file | |||||
| #[derive(Deserialize)] | #[derive(Deserialize)] | ||||
| struct RawTile { | struct RawTile { | ||||
| path: PathBuf, | path: PathBuf, | ||||