diff --git a/src/main.rs b/src/main.rs index 9367ed0..dd598b8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -81,6 +81,7 @@ struct Object { fighter: Option, ai: Option, item: Option, + always_visible: bool, } impl Object { @@ -96,6 +97,7 @@ impl Object { fighter: None, ai: None, item: None, + always_visible: false, } } @@ -209,6 +211,7 @@ struct Game { map: Map, messages: Messages, inventory: Vec, + dungeon_level: u32, } // A rectangle on the map, used to characterise a room. @@ -394,6 +397,17 @@ fn handle_keys(tcod: &mut Tcod, game: &mut Game, objects: &mut Vec) -> P DidntTakeTurn } + (Key { code: Text, .. }, "<", true) => { + // Go down stairs, if the player is on them + let player_on_stairs = objects + .iter() + .any(|object| object.pos() == objects[PLAYER].pos() && object.name == "stairs"); + if player_on_stairs { + next_level(tcod, game, objects); + } + DidntTakeTurn + } + _ => DidntTakeTurn, } } @@ -402,6 +416,11 @@ fn make_map(objects: &mut Vec) -> Map { // Fill the map with unblocked tiles let mut map = vec![vec![Tile::wall(); MAP_HEIGHT as usize]; MAP_WIDTH as usize]; + // Player is the first element, remove everything else. + // NOTE: works only when the player is the first object! + assert_eq!(&objects[PLAYER] as *const _, &objects[0] as *const _); + objects.truncate(1); + let mut rooms = vec![]; for _ in 0..MAX_ROOMS { @@ -437,6 +456,12 @@ fn make_map(objects: &mut Vec) -> Map { } } + // Create stairs at the center of the last room + let (last_room_x, last_room_y) = rooms[rooms.len() - 1].center(); + let mut stairs = Object::new(last_room_x, last_room_y, '<', "stairs", WHITE, false); + stairs.always_visible = true; + objects.push(stairs); + map } @@ -515,7 +540,7 @@ fn place_objects(room: Rect, map: &Map, objects: &mut Vec) { // Only place it if the tile is note blocked if !is_blocked(x, y, map, objects) { let dice = rand::random::(); - let item = if dice < 0.7 { + let mut item = if dice < 0.7 { // Create a healing potion (70% chance) let mut object = Object::new(x, y, '!', "healing potion", VIOLET, false); object.item = Some(Item::Heal); @@ -537,6 +562,7 @@ fn place_objects(room: Rect, map: &Map, objects: &mut Vec) { object }; + item.always_visible = true; objects.push(item); } } @@ -1100,7 +1126,10 @@ fn render_all(tcod: &mut Tcod, game: &mut Game, objects: &[Object], fov_recomput // Only render objects in fov let mut to_draw: Vec<_> = objects .iter() - .filter(|o| tcod.fov.is_in_fov(o.x, o.y)) + .filter(|o| { + tcod.fov.is_in_fov(o.x, o.y) + || (o.always_visible && game.map[o.x as usize][o.y as usize].explored) + }) .collect(); // Sort so that non-blocking objects are rendered behind blocking objects @@ -1166,6 +1195,15 @@ fn render_all(tcod: &mut Tcod, game: &mut Game, objects: &[Object], fov_recomput DARKER_RED, ); + // Display the current level + tcod.panel.print_ex( + 1, + 3, + BackgroundFlag::None, + TextAlignment::Left, + format!("Dungeon level: {}", game.dungeon_level), + ); + // Display names of objects under the mouse tcod.panel.set_default_foreground(LIGHT_GREY); tcod.panel.print_ex( @@ -1235,6 +1273,7 @@ fn new_game(tcod: &mut Tcod) -> (Game, Vec) { map: make_map(&mut objects), messages: Messages::new(), inventory: vec![], + dungeon_level: 1, }; initialise_fov(tcod, &game.map); @@ -1365,6 +1404,26 @@ fn msgbox(text: &str, width: i32, root: &mut Root) { menu(text, options, width, root); } +// Advance to the next level +fn next_level(tcod: &mut Tcod, game: &mut Game, objects: &mut Vec) { + game.messages.add( + "You take a moment to rest, and recover your strength.", + VIOLET, + ); + + let heal_hp = objects[PLAYER].fighter.map_or(0, |f| f.max_hp / 2); + objects[PLAYER].heal(heal_hp); + + game.messages.add( + "After a rare moment of peace, you descend deeper into \ + the heart of the dungeon...", + RED, + ); + game.dungeon_level += 1; + game.map = make_map(objects); + initialise_fov(tcod, &game.map); +} + fn main() { tcod::system::set_fps(FPS);