#![allow(unused_imports)] // mods mod util; mod world; // namespacing use ggez::{ conf::{WindowMode, WindowSetup}, event::{self, EventHandler, KeyCode, KeyMods}, graphics, Context, ContextBuilder, GameResult, }; use std::collections::VecDeque; 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 pub struct MainState { world: World, action_buffer: Vec>, action_history: VecDeque>, action_history_max_length: usize, } impl MainState { // create a new gamestate pub fn new(ctx: &mut Context) -> Result { let world = World::new(ctx, "/data/tile_conf.toml".into(), 20, 20, 1)?; Ok(MainState { world, action_history_max_length: 100, action_history: VecDeque::new(), action_buffer: Vec::new(), }) } } impl EventHandler for MainState { fn update(&mut self, ctx: &mut Context) -> GameResult<()> { // check through the input buffer while let Some(action) = self.action_buffer.pop() { action.execute(ctx, self).expect("failed to execute action"); if self.action_history.len() < self.action_history_max_length { self.action_history.push_back(action); } else { self.action_history.pop_front(); self.action_history.push_back(action); } } Ok(()) } fn key_down_event( &mut self, _ctx: &mut Context, key_code: KeyCode, _keymods: KeyMods, repeat: bool, ) { use world::{OffsetAction, ZoomAction}; if !repeat { let action_maybe: Option> = match key_code { // movement key codes KeyCode::Up => Some(Box::new(OffsetAction::new([0.0, 1.0].into()))), KeyCode::Down => Some(Box::new(OffsetAction::new([0.0, -1.0].into()))), KeyCode::Left => Some(Box::new(OffsetAction::new([1.0, 0.0].into()))), KeyCode::Right => Some(Box::new(OffsetAction::new([-1.0, 0.0].into()))), // zoom key codes KeyCode::Equals => Some(Box::new(ZoomAction::new([0.1, 0.1].into()))), KeyCode::Minus => Some(Box::new(ZoomAction::new([-0.1, -0.1].into()))), // if escape, quit program (for now) // !! isabelle please remove this eventually KeyCode::Escape => Some(Box::new(QuitAction::new())), // ignore all other key codes _ => None, }; if let Some(action) = action_maybe { self.action_buffer.push(action); } } } fn draw(&mut self, ctx: &mut Context) -> GameResult<()> { // clear the screen graphics::clear(ctx, graphics::BLACK); self.world.render(ctx).expect("failed to render"); // render the context graphics::present(ctx) } } trait Action: std::fmt::Debug { fn execute(&self, ctx: &mut Context, state: &mut MainState) -> Result<()>; fn undo(&self, ctx: &mut Context, state: &mut MainState) -> Result<()>; } /// struct to wrap quitting the game #[derive(Debug)] struct QuitAction; impl QuitAction { fn new() -> QuitAction { QuitAction } } impl Action for QuitAction { fn execute(&self, ctx: &mut Context, _state: &mut MainState) -> Result<()> { event::quit(ctx); Ok(()) } fn undo(&self, _ctx: &mut Context, _state: &mut MainState) -> Result<()> { unreachable!() } } fn main_wrapper() -> Result<()> { let win_mode = WindowMode { width: 1024.0, height: 768.0, ..Default::default() }; let win_setup = WindowSetup { title: "unnamed game thinger".to_owned(), ..Default::default() }; let cb = ContextBuilder::new("unnamed", "Isabelle L.") .add_resource_path("./") .window_mode(win_mode) .window_setup(win_setup); let (mut ctx, mut event_loop) = cb.build()?; let mut state = MainState::new(&mut ctx)?; event::run(&mut ctx, &mut event_loop, &mut state)?; Ok(()) } fn main() { if let Err(err) = main_wrapper() { panic!("err occured: {:?}", err); } }