| @@ -11,28 +11,12 @@ use ggez::{ | |||||
| graphics, Context, ContextBuilder, GameResult, | graphics, Context, ContextBuilder, GameResult, | ||||
| }; | }; | ||||
| use std::collections::VecDeque; | use std::collections::VecDeque; | ||||
| pub use util::{Error, Result}; | |||||
| use world::World; | 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 | // primary game state | ||||
| pub struct MainState { | |||||
| struct MainState { | |||||
| debug_mode: bool, | |||||
| world: World, | world: World, | ||||
| action_buffer: Vec<Box<dyn Action>>, | action_buffer: Vec<Box<dyn Action>>, | ||||
| action_history: VecDeque<Box<dyn Action>>, | action_history: VecDeque<Box<dyn Action>>, | ||||
| @@ -45,6 +29,7 @@ impl MainState { | |||||
| let world = World::new(ctx, "/data/tile_conf.toml".into(), 20, 20, 1)?; | let world = World::new(ctx, "/data/tile_conf.toml".into(), 20, 20, 1)?; | ||||
| Ok(MainState { | Ok(MainState { | ||||
| world, | world, | ||||
| debug_mode: true, | |||||
| action_history_max_length: 100, | action_history_max_length: 100, | ||||
| action_history: VecDeque::new(), | action_history: VecDeque::new(), | ||||
| action_buffer: Vec::new(), | action_buffer: Vec::new(), | ||||
| @@ -56,7 +41,13 @@ impl EventHandler for MainState { | |||||
| fn update(&mut self, ctx: &mut Context) -> GameResult<()> { | fn update(&mut self, ctx: &mut Context) -> GameResult<()> { | ||||
| // check through the input buffer | // check through the input buffer | ||||
| while let Some(action) = self.action_buffer.pop() { | while let Some(action) = self.action_buffer.pop() { | ||||
| action.execute(ctx, self).expect("failed to execute action"); | |||||
| if self.debug_mode { | |||||
| action | |||||
| .execute_debug(ctx, self) | |||||
| .expect("fialed to execute action"); | |||||
| } else { | |||||
| action.execute(ctx, self).expect("failed to execute action"); | |||||
| } | |||||
| if self.action_history.len() < self.action_history_max_length { | if self.action_history.len() < self.action_history_max_length { | ||||
| self.action_history.push_back(action); | self.action_history.push_back(action); | ||||
| @@ -69,6 +60,17 @@ impl EventHandler for MainState { | |||||
| Ok(()) | Ok(()) | ||||
| } | } | ||||
| fn mouse_wheel_event(&mut self, _ctx: &mut Context, _x: f32, y: f32) { | |||||
| use world::ZoomAction; | |||||
| if y > 0.0 { | |||||
| self.action_buffer | |||||
| .push(Box::new(ZoomAction::new([0.1, 0.1].into()))); | |||||
| } else if y < 0.0 { | |||||
| self.action_buffer | |||||
| .push(Box::new(ZoomAction::new([-0.1, -0.1].into()))); | |||||
| } | |||||
| } | |||||
| fn key_down_event( | fn key_down_event( | ||||
| &mut self, | &mut self, | ||||
| _ctx: &mut Context, | _ctx: &mut Context, | ||||
| @@ -88,7 +90,7 @@ impl EventHandler for MainState { | |||||
| // zoom key codes | // zoom key codes | ||||
| KeyCode::Equals => Some(Box::new(ZoomAction::new([0.1, 0.1].into()))), | KeyCode::Equals => Some(Box::new(ZoomAction::new([0.1, 0.1].into()))), | ||||
| KeyCode::Minus => Some(Box::new(ZoomAction::new([-0.1, -0.1].into()))), | |||||
| KeyCode::Subtract => Some(Box::new(ZoomAction::new([-0.1, -0.1].into()))), | |||||
| // if escape, quit program (for now) | // if escape, quit program (for now) | ||||
| // !! isabelle please remove this eventually | // !! isabelle please remove this eventually | ||||
| @@ -118,6 +120,16 @@ impl EventHandler for MainState { | |||||
| trait Action: std::fmt::Debug { | trait Action: std::fmt::Debug { | ||||
| fn execute(&self, ctx: &mut Context, state: &mut MainState) -> Result<()>; | fn execute(&self, ctx: &mut Context, state: &mut MainState) -> Result<()>; | ||||
| fn undo(&self, ctx: &mut Context, state: &mut MainState) -> Result<()>; | fn undo(&self, ctx: &mut Context, state: &mut MainState) -> Result<()>; | ||||
| fn execute_debug(&self, ctx: &mut Context, state: &mut MainState) -> Result<()> { | |||||
| println!("executing: {:?}", self); | |||||
| self.execute(ctx, state) | |||||
| } | |||||
| fn undo_debug(&self, ctx: &mut Context, state: &mut MainState) -> Result<()> { | |||||
| println!("undoing: {:?}", self); | |||||
| self.execute(ctx, state) | |||||
| } | |||||
| } | } | ||||
| /// struct to wrap quitting the game | /// struct to wrap quitting the game | ||||
| @@ -1,5 +1,22 @@ | |||||
| use mint::Point2; | use mint::Point2; | ||||
| /// 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 }, | |||||
| } | |||||
| pub struct IsometricVector2(Point2<f32>); | pub struct IsometricVector2(Point2<f32>); | ||||
| impl IsometricVector2 { | impl IsometricVector2 { | ||||
| @@ -1,8 +1,10 @@ | |||||
| // modules | // modules | ||||
| mod actions; | |||||
| mod tile; | mod tile; | ||||
| // namespacing | // namespacing | ||||
| use crate::{util::*, MainState, Result}; | use crate::{util::*, MainState, Result}; | ||||
| pub use actions::*; | |||||
| use ggez::{ | use ggez::{ | ||||
| graphics::{self, DrawParam}, | graphics::{self, DrawParam}, | ||||
| Context, | Context, | ||||
| @@ -34,7 +36,7 @@ impl World { | |||||
| let builder = TileBuilder::new(ctx, tile_config)?; | let builder = TileBuilder::new(ctx, tile_config)?; | ||||
| let mut data: HashMap<Vector3<isize>, Tile> = HashMap::new(); | let mut data: HashMap<Vector3<isize>, Tile> = HashMap::new(); | ||||
| let offset: Point2<f32> = [350.0f32, 100.0f32].into(); | let offset: Point2<f32> = [350.0f32, 100.0f32].into(); | ||||
| let zoom: Vector2<f32> = [1.0f32, 1.0f32].into(); | |||||
| let zoom: Vector2<f32> = [2.0f32, 2.0f32].into(); | |||||
| for x in 0..width { | for x in 0..width { | ||||
| for y in 0..depth { | for y in 0..depth { | ||||
| @@ -49,7 +51,7 @@ impl World { | |||||
| height, | height, | ||||
| depth, | depth, | ||||
| data, | data, | ||||
| builder: builder, | |||||
| builder, | |||||
| offset, | offset, | ||||
| zoom, | zoom, | ||||
| }) | }) | ||||
| @@ -82,55 +84,3 @@ impl World { | |||||
| Ok(()) | Ok(()) | ||||
| } | } | ||||
| } | } | ||||
| /// action struct for wrapping offsetting input | |||||
| #[derive(Debug)] | |||||
| pub struct OffsetAction(Vector2<f32>); | |||||
| impl OffsetAction { | |||||
| /// construct a new offset action | |||||
| pub fn new(offset: Vector2<f32>) -> OffsetAction { | |||||
| OffsetAction(offset) | |||||
| } | |||||
| } | |||||
| impl crate::Action for OffsetAction { | |||||
| fn execute(&self, _ctx: &mut Context, state: &mut MainState) -> Result<()> { | |||||
| state.world.offset.x = | |||||
| self.0.x * (state.world.builder.tile_width() / 2.0) + state.world.offset.x; | |||||
| state.world.offset.y = | |||||
| self.0.y * (state.world.builder.tile_height() / 2.0) + state.world.offset.y; | |||||
| Ok(()) | |||||
| } | |||||
| fn undo(&self, _ctx: &mut Context, state: &mut MainState) -> Result<()> { | |||||
| state.world.offset.x = state.world.offset.x - self.0.x; | |||||
| state.world.offset.y = state.world.offset.y - self.0.y; | |||||
| Ok(()) | |||||
| } | |||||
| } | |||||
| /// action struct for wrapping zoom input | |||||
| #[derive(Debug)] | |||||
| pub struct ZoomAction(Vector2<f32>); | |||||
| impl ZoomAction { | |||||
| /// construct a new scale action | |||||
| pub fn new(scale: Vector2<f32>) -> ZoomAction { | |||||
| ZoomAction(scale) | |||||
| } | |||||
| } | |||||
| impl crate::Action for ZoomAction { | |||||
| fn execute(&self, _ctx: &mut Context, state: &mut MainState) -> Result<()> { | |||||
| state.world.zoom.x = self.0.x + state.world.zoom.x; | |||||
| state.world.zoom.y = self.0.y + state.world.zoom.y; | |||||
| Ok(()) | |||||
| } | |||||
| fn undo(&self, _ctx: &mut Context, state: &mut MainState) -> Result<()> { | |||||
| state.world.zoom.x = state.world.zoom.x - self.0.x; | |||||
| state.world.zoom.y = state.world.zoom.y - self.0.y; | |||||
| Ok(()) | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,57 @@ | |||||
| use crate::{MainState, Result}; | |||||
| use ggez::Context; | |||||
| use mint::Vector2; | |||||
| /// action struct for wrapping offsetting input | |||||
| #[derive(Debug)] | |||||
| pub struct OffsetAction(Vector2<f32>); | |||||
| impl OffsetAction { | |||||
| /// construct a new offset action | |||||
| pub fn new(offset: Vector2<f32>) -> OffsetAction { | |||||
| OffsetAction(offset) | |||||
| } | |||||
| } | |||||
| impl crate::Action for OffsetAction { | |||||
| fn execute(&self, _ctx: &mut Context, state: &mut MainState) -> Result<()> { | |||||
| let x: f32 = self.0.x * (state.world.builder.tile_width() / 2.0) + state.world.offset.x; | |||||
| let y: f32 = self.0.y * (state.world.builder.tile_height() / 2.0) + state.world.offset.y; | |||||
| state.world.offset = [x, y].into(); | |||||
| Ok(()) | |||||
| } | |||||
| fn undo(&self, _ctx: &mut Context, state: &mut MainState) -> Result<()> { | |||||
| let x: f32 = state.world.offset.x - self.0.x * (state.world.builder.tile_width() / 2.0); | |||||
| let y: f32 = state.world.offset.y - self.0.y * (state.world.builder.tile_height() / 2.0); | |||||
| state.world.offset = [x, y].into(); | |||||
| Ok(()) | |||||
| } | |||||
| } | |||||
| /// action struct for wrapping zoom input | |||||
| #[derive(Debug)] | |||||
| pub struct ZoomAction(Vector2<f32>); | |||||
| impl ZoomAction { | |||||
| /// construct a new scale action | |||||
| pub fn new(scale: Vector2<f32>) -> ZoomAction { | |||||
| ZoomAction(scale) | |||||
| } | |||||
| } | |||||
| impl crate::Action for ZoomAction { | |||||
| fn execute(&self, _ctx: &mut Context, state: &mut MainState) -> Result<()> { | |||||
| let x: f32 = state.world.zoom.x + (self.0.x * (state.world.builder.tile_width() / 2.0)); | |||||
| let y: f32 = state.world.zoom.y + (self.0.y * (state.world.builder.tile_height() / 2.0)); | |||||
| state.world.zoom = [x, y].into(); | |||||
| Ok(()) | |||||
| } | |||||
| fn undo(&self, _ctx: &mut Context, state: &mut MainState) -> Result<()> { | |||||
| let x: f32 = state.world.zoom.x - self.0.x * (state.world.builder.tile_width() / 2.0); | |||||
| let y: f32 = state.world.zoom.y - self.0.y * (state.world.builder.tile_height() / 2.0); | |||||
| state.world.zoom = [x, y].into(); | |||||
| Ok(()) | |||||
| } | |||||
| } | |||||
| @@ -1,5 +1,8 @@ | |||||
| use crate::{Error, Result}; | use crate::{Error, Result}; | ||||
| use ggez::{graphics::Image, Context}; | |||||
| use ggez::{ | |||||
| graphics::{FilterMode, Image}, | |||||
| Context, | |||||
| }; | |||||
| use mint::Vector2; | use mint::Vector2; | ||||
| use serde::Deserialize; | use serde::Deserialize; | ||||
| use std::io::Read; | use std::io::Read; | ||||
| @@ -34,7 +37,11 @@ impl TileBuilder { | |||||
| raw_map.iter().for_each(|(kind, raw_tile)| { | raw_map.iter().for_each(|(kind, raw_tile)| { | ||||
| let tile = MasterTile { | let tile = MasterTile { | ||||
| texture: Rc::new(Image::new(ctx, &raw_tile.path).expect("failed to load image")), | |||||
| texture: Rc::new({ | |||||
| let mut image = Image::new(ctx, &raw_tile.path).expect("failed to load image"); | |||||
| image.set_filter(FilterMode::Nearest); | |||||
| image | |||||
| }), | |||||
| kind: kind.to_owned(), | kind: kind.to_owned(), | ||||
| blocking: raw_tile.blocking, | blocking: raw_tile.blocking, | ||||
| }; | }; | ||||
| @@ -42,7 +49,7 @@ impl TileBuilder { | |||||
| }); | }); | ||||
| Ok(TileBuilder { | Ok(TileBuilder { | ||||
| textures: textures, | |||||
| textures, | |||||
| tile_dimensions, | tile_dimensions, | ||||
| }) | }) | ||||
| } | } | ||||