From 1fe967250e97f082884e26bb9c3dac31a96d1538 Mon Sep 17 00:00:00 2001 From: Kasper Juul Hermansen Date: Wed, 26 Jan 2022 00:29:38 +0100 Subject: [PATCH] Added 2.7 --- src/damage_system.rs | 13 +++- src/gamelog.rs | 3 + src/gui.rs | 152 +++++++++++++++++++++++++++++++++++++ src/main.rs | 10 ++- src/map.rs | 18 +++-- src/melee_combat_system.rs | 8 +- 6 files changed, 192 insertions(+), 12 deletions(-) create mode 100644 src/gamelog.rs create mode 100644 src/gui.rs diff --git a/src/damage_system.rs b/src/damage_system.rs index a18532e..6e7e948 100644 --- a/src/damage_system.rs +++ b/src/damage_system.rs @@ -1,3 +1,5 @@ +use crate::gamelog::GameLog; +use crate::Name; use rltk::console; use specs::prelude::*; @@ -27,12 +29,21 @@ pub fn delete_the_dead(ecs: &mut World) { { let combat_stats = ecs.read_storage::(); let players = ecs.read_storage::(); + let names = ecs.read_storage::(); let entities = ecs.entities(); + let mut log = ecs.write_resource::(); for (entity, stats) in (&entities, &combat_stats).join() { if stats.hp < 1 { let player = players.get(entity); match player { - None => dead.push(entity), + None => { + let victim_name = names.get(entity); + if let Some(victim_name) = victim_name { + log.entries.push(format!("{} is dead", &victim_name.name)); + } + dead.push(entity) + } + Some(_) => console::log("You are dead"), } } diff --git a/src/gamelog.rs b/src/gamelog.rs new file mode 100644 index 0000000..dbe28c0 --- /dev/null +++ b/src/gamelog.rs @@ -0,0 +1,3 @@ +pub struct GameLog { + pub entries: Vec, +} diff --git a/src/gui.rs b/src/gui.rs new file mode 100644 index 0000000..8e0134d --- /dev/null +++ b/src/gui.rs @@ -0,0 +1,152 @@ +use crate::gamelog::GameLog; +use crate::CombatStats; +use crate::Map; +use crate::Name; +use crate::Player; +use crate::Position; +use rltk::Point; +use rltk::Rltk; +use rltk::RGB; +use specs::prelude::*; + +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 combat_stats = ecs.read_storage::(); + let players = ecs.read_storage::(); + let log = ecs.fetch::(); + + for (_player, stats) in (&players, &combat_stats).join() { + let health = format!(" HP: {} / {}", stats.hp, stats.max_hp); + ctx.print_color( + 12, + 43, + RGB::named(rltk::YELLOW), + RGB::named(rltk::BLACK), + &health, + ); + + ctx.draw_bar_horizontal( + 28, + 43, + 51, + stats.hp, + stats.max_hp, + RGB::named(rltk::RED), + RGB::named(rltk::BLACK), + ); + } + + let mut y = 44; + for s in log.entries.iter().rev() { + if y < 49 { + ctx.print(2, y, s); + } + y += 1; + } + + // Draw mouse + let mouse_pos = ctx.mouse_pos(); + ctx.set_bg(mouse_pos.0, mouse_pos.1, RGB::named(rltk::MAGENTA)); + draw_tooltips(ecs, ctx); +} + +fn draw_tooltips(ecs: &World, ctx: &mut Rltk) { + let map = ecs.fetch::(); + let names = ecs.read_storage::(); + let positions = ecs.read_storage::(); + + let mouse_pos = ctx.mouse_pos(); + if mouse_pos.0 >= map.width || mouse_pos.1 >= map.height { + return; + } + + let mut tooltip: Vec = Vec::new(); + for (name, position) in (&names, &positions).join() { + let idx = map.xy_idx(position.x, position.y); + if position.x == mouse_pos.0 && position.y == mouse_pos.1 && map.visible_tiles[idx] { + tooltip.push(name.name.to_string()); + } + } + + if !tooltip.is_empty() { + let mut width: i32 = 0; + for s in tooltip.iter() { + if width < s.len() as i32 { + width = s.len() as i32; + } + } + width += 3; + + if mouse_pos.0 > 40 { + let arrow_pos = Point::new(mouse_pos.0 - 2, mouse_pos.1); + let left_x = mouse_pos.0 - width; + let mut y = mouse_pos.1; + for s in tooltip.iter() { + ctx.print_color( + left_x, + y, + RGB::named(rltk::WHITE), + RGB::named(rltk::GREY), + s, + ); + let padding = (width - s.len() as i32) - 1; + for i in 0..padding { + ctx.print_color( + arrow_pos.x - i, + y, + RGB::named(rltk::WHITE), + RGB::named(rltk::GREY), + &" ".to_string(), + ); + } + y += 1; + } + ctx.print_color( + arrow_pos.x, + arrow_pos.y, + RGB::named(rltk::WHITE), + RGB::named(rltk::GREY), + &"->".to_string(), + ); + } else { + let arrow_pos = Point::new(mouse_pos.0 + 1, mouse_pos.1); + let left_x = mouse_pos.0 + 3; + let mut y = mouse_pos.1; + for s in tooltip.iter() { + ctx.print_color( + left_x + 1, + y, + RGB::named(rltk::WHITE), + RGB::named(rltk::GREY), + s, + ); + let padding = (width - s.len() as i32) - 1; + for i in 0..padding { + ctx.print_color( + arrow_pos.x + i + 1, + y, + RGB::named(rltk::WHITE), + RGB::named(rltk::GREY), + &" ".to_string(), + ); + } + y += 1; + } + ctx.print_color( + arrow_pos.x, + arrow_pos.y, + RGB::named(rltk::WHITE), + RGB::named(rltk::GREY), + &"->".to_string(), + ); + } + } +} diff --git a/src/main.rs b/src/main.rs index ae9bda5..b61c925 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,8 @@ use specs::prelude::*; mod components; mod damage_system; +mod gamelog; +mod gui; mod map; mod map_indexing_system; mod melee_combat_system; @@ -97,19 +99,25 @@ impl GameState for State { ctx.set(pos.x, pos.y, render.fg, render.bg, render.glyph) } } + + gui::draw_ui(&self.ecs, ctx); } } fn main() -> rltk::BError { use rltk::RltkBuilder; - let context = RltkBuilder::simple80x50() + let mut context = RltkBuilder::simple80x50() .with_title("Roguelike Tutorial") .with_automatic_console_resize(false) .with_dimensions(80 * 3, 50 * 3) .build()?; + context.with_post_scanlines(true); let mut gs = State { ecs: World::new() }; gs.ecs.insert(RunState::PreRun); + gs.ecs.insert(gamelog::GameLog { + entries: vec!["Welcome to Rusty Roguelike".to_string()], + }); gs.ecs.register::(); gs.ecs.register::(); diff --git a/src/map.rs b/src/map.rs index e1a0e7d..84ccec5 100644 --- a/src/map.rs +++ b/src/map.rs @@ -11,6 +11,10 @@ pub enum TileType { Floor, } +const MAP_WIDTH: usize = 80; +const MAP_HEIGHT: usize = 43; +const MAP_COUNT: usize = MAP_HEIGHT * MAP_WIDTH; + pub struct Map { pub tiles: Vec, pub rooms: Vec, @@ -56,14 +60,14 @@ impl Map { pub fn new_map_rooms_and_corridors() -> Map { let mut map = Map { - tiles: vec![TileType::Wall; 80 * 50], + tiles: vec![TileType::Wall; MAP_COUNT], rooms: Vec::new(), - width: 80, - height: 50, - revealed_tiles: vec![false; 80 * 50], - visible_tiles: vec![false; 80 * 50], - blocked: vec![false; 80 * 50], - tile_content: vec![Vec::new(); 80 * 50], + width: MAP_WIDTH as i32, + height: MAP_HEIGHT as i32, + revealed_tiles: vec![false; MAP_COUNT], + visible_tiles: vec![false; MAP_COUNT], + blocked: vec![false; MAP_COUNT], + tile_content: vec![Vec::new(); MAP_COUNT], }; const MAX_ROOMS: i32 = 30; diff --git a/src/melee_combat_system.rs b/src/melee_combat_system.rs index 84be607..0e689bf 100644 --- a/src/melee_combat_system.rs +++ b/src/melee_combat_system.rs @@ -1,3 +1,4 @@ +use crate::gamelog::GameLog; use rltk::console; use specs::prelude::*; @@ -8,6 +9,7 @@ pub struct MeleeCombatSystem {} impl<'a> System<'a> for MeleeCombatSystem { type SystemData = ( Entities<'a>, + WriteExpect<'a, GameLog>, WriteStorage<'a, WantsToMelee>, ReadStorage<'a, Name>, ReadStorage<'a, CombatStats>, @@ -15,7 +17,7 @@ impl<'a> System<'a> for MeleeCombatSystem { ); fn run(&mut self, data: Self::SystemData) { - let (entities, mut wants_melee, names, combat_stats, mut inflict_damage) = data; + let (entities, mut log, mut wants_melee, names, combat_stats, mut inflict_damage) = data; for (_entity, wants_melee, name, stats) in (&entities, &wants_melee, &names, &combat_stats).join() @@ -28,12 +30,12 @@ impl<'a> System<'a> for MeleeCombatSystem { let damage = i32::max(0, stats.power - target_stats.defense); if damage == 0 { - console::log(&format!( + log.entries.push(format!( "{} is unable to hurt {}", &name.name, &target_name.name )); } else { - console::log(&format!( + log.entries.push(format!( "{} hits {}, for {} hp.", &name.name, &target_name.name, damage ));