use dungeon::{ Dungeon, UpdateResult, entity::PLAYER_INVENTORY_SIZE, player_input::PlayerInput, pos::Direction, }; use graphics::{Key, Window}; pub struct Game { window: Window, dungeon: Dungeon, // to ensure the most recently-pressed direction key is used: current_dir: Option<(Direction, Key)>, } impl Game { pub fn new(window: Window, seed: Option) -> Self { let dungeon = match seed { Some(s) => Dungeon::new(s), None => Dungeon::random(), }; Self { window, dungeon, current_dir: None, } } fn player_dir(&mut self) -> Option { const MOVE_KEYS: [(Direction, [Key; 2]); 4] = [ (Direction::North, [Key::Up, Key::W]), (Direction::West, [Key::Left, Key::A]), (Direction::South, [Key::Down, Key::S]), (Direction::East, [Key::Right, Key::D]), ]; // if a key was just pressed, use it for the new direction to move in for (dir, keys) in MOVE_KEYS { for key in keys { if self.window.is_key_pressed(key) { self.current_dir = Some((dir, key)); return Some(dir); } } } // otherwise, use existing direction, so long as the key is still down match self.current_dir { Some((dir, key)) if self.window.is_key_down(key) => return Some(dir), _ => self.current_dir = None, } // otherwise, use any key that is already down for (dir, keys) in MOVE_KEYS { for key in keys { if self.window.is_key_down(key) { self.current_dir = Some((dir, key)); return Some(dir); } } } // otherwise, no direction key is pressed, so return None None } fn get_player_input(&mut self) -> PlayerInput { let direction = self.player_dir(); let interact = self.window.is_key_pressed(Key::Return); let use_item = self.window.is_key_pressed(Key::E); let attack = self.window.is_key_pressed(Key::F); let drop = self.window.is_key_pressed(Key::Q); let inv_slot = (0..PLAYER_INVENTORY_SIZE) .filter_map(|u16| u8::try_from(u16).ok()) .find(|u8| self.window.is_key_pressed(Key::Number(*u8 + 1))) .map(|u8| u8 as usize); PlayerInput { direction, interact, use_item, attack, drop, inv_slot, } } pub fn run(&mut self) { // Main game loop while self.window.is_open() { // Handle debug keys if self.window.is_key_pressed(Key::F3) { self.window.toggle_debug(); } if self.window.is_key_pressed(Key::F4) { self.dungeon.player.entity.health = 0; } // Update game state let inputs = self.get_player_input(); let result = self .dungeon .update(inputs, self.window.delta_time().as_secs_f32()); match result { UpdateResult::Default(_action) => { // TODO: make noises for player actions } UpdateResult::NextFloor => { // TODO: stairs audio } UpdateResult::MessageUpdated(changed) => { if changed { self.window.audio().speak.play(); } } UpdateResult::GameOver => { // TODO: player game over music, very sad } UpdateResult::Nothing => {} } // Draw a single frame self.window.draw_frame(&self.dungeon); } } }