diff --git a/src/gui.rs b/src/gui.rs index 303312a..560c080 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -66,6 +66,10 @@ fn draw_tooltips(ecs: &World, ctx: &mut Rltk) { pub fn draw_ui(ecs: &World, ctx: &mut Rltk) { ctx.draw_box(0, 43, 79, 6, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); + let map = ecs.fetch::(); + let depth = format!("Depth: {}", map.depth); + ctx.print_color(2, 43, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), &depth); + let combat_stats = ecs.read_storage::(); let players = ecs.read_storage::(); for (_player, stats) in (&players, &combat_stats).join() { diff --git a/src/main.rs b/src/main.rs index 2640848..0acfb9f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -61,6 +61,7 @@ pub enum RunState { menu_selection: gui::MainMenuSelection, }, SaveGame, + NextLevel, } pub struct State { @@ -242,6 +243,10 @@ impl GameState for State { saveload_system::save_game(&mut self.ecs); newrunstate = RunState::MainMenu { menu_selection: gui::MainMenuSelection::LoadGame }; } + RunState::NextLevel => { + self.goto_next_level(); + newrunstate = RunState::PreRun; + } } { @@ -253,6 +258,89 @@ impl GameState for State { } } +impl State { + fn entities_to_remove_on_level_change(&mut self) -> Vec { + let entities = self.ecs.entities(); + let player = self.ecs.read_storage::(); + let backpack = self.ecs.read_storage::(); + let player_entity = self.ecs.fetch::(); + + let mut to_delete : Vec = Vec::new(); + for entity in entities.join() { + let mut should_delete = true; + + // Don't delete the player + let p = player.get(entity); + if let Some(_p) = p { + should_delete = false; + } + + // Don't delete the players equipment + let bp = backpack.get(entity); + if let Some(bp) = bp { + if bp.owner == *player_entity { + should_delete = false; + } + } + + if should_delete { + to_delete.push(entity); + } + } + to_delete + } + + fn goto_next_level(&mut self) { + // Delete entities that are not the player or their equipment + let to_delete = self.entities_to_remove_on_level_change(); + for target in to_delete { + self.ecs.delete_entity(target).expect("Unable to delete entity"); + } + + // Build a new map and place the player + let worldmap; + { + let mut worldmap_resource = self.ecs.write_resource::(); + let current_depth = worldmap_resource.depth; + *worldmap_resource = Map::new_map_rooms_and_corridors(current_depth + 1); + worldmap = worldmap_resource.clone(); + } + + // Spawn bad guys + for room in worldmap.rooms.iter().skip(1) { + spawner::spawn_room(&mut self.ecs, room); + } + + // Place the player and update the resources + let (player_x, player_y) = worldmap.rooms[0].center(); + let mut player_position = self.ecs.write_resource::(); + *player_position = Point::new(player_x, player_y); + let mut position_components = self.ecs.write_storage::(); + let player_entity = self.ecs.fetch::(); + let player_pos_comp = position_components.get_mut(*player_entity); + if let Some(player_pos_comp) = player_pos_comp { + player_pos_comp.x = player_x; + player_pos_comp.y = player_y; + } + + // Mark the players visibility as dirty + let mut viewshed_components = self.ecs.write_storage::(); + let vs = viewshed_components.get_mut(*player_entity); + if let Some(vs) = vs { + vs.dirty = true; + } + + // Notify the player and give them some health + let mut gamelog = self.ecs.fetch_mut::(); + gamelog.entries.push("You descend to the next level, and take a moment to heal.".to_string()); + let mut player_health_store = self.ecs.write_storage::(); + let player_health = player_health_store.get_mut(*player_entity); + if let Some(player_health) = player_health { + player_health.hp = i32::max(player_health.hp, player_health.max_hp / 2); + } + } +} + fn main() -> rltk::BError { use rltk::RltkBuilder; let mut context = RltkBuilder::simple80x50() diff --git a/src/map.rs b/src/map.rs index eac9051..265a05f 100644 --- a/src/map.rs +++ b/src/map.rs @@ -12,6 +12,7 @@ pub const MAPCOUNT : usize = MAPHIEGHT * MAPWIDTH; pub enum TileType { Wall, Floor, + DownStairs } #[derive(Default, Serialize, Deserialize, Clone)] @@ -110,6 +111,10 @@ impl Map { } } + let stairs_position = map.rooms[map.rooms.len()-1].center(); + let stairs_idx = map.xy_idx(stairs_position.0, stairs_position.1); + map.tiles[stairs_idx] = TileType::DownStairs; + map } @@ -233,6 +238,10 @@ pub fn draw_map(ecs: &World, ctx: &mut Rltk) { glyph = rltk::to_cp437('#'); fg = RGB::from_f32(0., 1.0, 0.); } + TileType::DownStairs => { + glyph = rltk::to_cp437('>'); + fg = RGB::from_f32(0., 1.0, 1.0) + } } if !map.visible_tiles[idx] { fg = fg.to_greyscale() diff --git a/src/player.rs b/src/player.rs index c175f1b..f43b0c8 100644 --- a/src/player.rs +++ b/src/player.rs @@ -1,4 +1,4 @@ -use super::{Map, Player, Position, RunState, State, Viewshed, CombatStats, WantsToMelee, Item, GameLog, WantsToPickupItem, spawner}; +use super::{Map, Player, Position, RunState, State, Viewshed, CombatStats, WantsToMelee, Item, GameLog, WantsToPickupItem, spawner, TileType}; use rltk::{Point, Rltk, VirtualKeyCode}; use specs::prelude::*; use std::cmp::{max, min}; @@ -35,6 +35,19 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) { } } +pub fn try_next_level(ecs: &mut World) -> bool { + let player_pos = ecs.fetch::(); + let map = ecs.fetch::(); + let player_idx = map.xy_idx(player_pos.x, player_pos.y); + if map.tiles[player_idx] == TileType::DownStairs { + true + } else { + let mut gamelog = ecs.fetch_mut::(); + gamelog.entries.push("There is no way down from here.".to_string()); + false + } +} + fn get_item(ecs: &mut World) { let player_pos = ecs.fetch::(); let player_entity = ecs.fetch::(); @@ -94,6 +107,11 @@ pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState { return RunState::AwaitingInput; } VirtualKeyCode::Escape => return RunState::SaveGame, + VirtualKeyCode::Period => { + if try_next_level(&mut gs.ecs) { + return RunState::NextLevel; + } + } _ => return RunState::AwaitingInput, }, }