diff --git a/src/gui.rs b/src/gui.rs index aae6e00..cf4b92f 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -1,18 +1,20 @@ +mod enums; +mod tooltip; + use std::cmp::Ordering; use ::rltk::{Point, Rltk, VirtualKeyCode, RGB}; use ::specs::prelude::*; +pub use enums::*; +use tooltip::draw_tooltips; use crate::components::{ - Attribute, Attributes, Consumable, HungerClock, HungerState, InBackpack, Item, MagicItemClass, - Name, Pools, Position, Vendor, Viewshed, + Attribute, Attributes, Consumable, Equipped, HungerClock, HungerState, InBackpack, Item, + MagicItem, MagicItemClass, Name, ObfuscatedName, Pools, Vendor, Viewshed, }; use crate::game_log::GameLog; use crate::rex_assets::RexAssets; -use crate::{ - camera, colors, Equipped, Hidden, MagicItem, Map, MasterDungeonMap, ObfuscatedName, RunState, - State, VendorMode, -}; +use crate::{camera, colors, Map, MasterDungeonMap, RunState, State, VendorMode}; pub fn get_item_color(ecs: &World, item: Entity) -> RGB { if let Some(magic) = ecs.read_storage::().get(item) { @@ -267,178 +269,6 @@ fn draw_attribute(name: &str, attribute: &Attribute, y: i32, ctx: &mut Rltk) { } } -struct Tooltip { - lines: Vec, -} - -impl Tooltip { - fn new() -> Tooltip { - Tooltip { lines: Vec::new() } - } - - fn add(&mut self, line: S) { - self.lines.push(line.to_string()); - } - - fn width(&self) -> i32 { - let mut max = 0; - for s in self.lines.iter() { - if s.len() > max { - max = s.len(); - } - } - - max as i32 + 2 - } - - fn height(&self) -> i32 { - self.lines.len() as i32 + 2 - } - - fn render(&self, ctx: &mut Rltk, x: i32, y: i32) { - ctx.draw_box( - x, - y, - self.width() - 1, - self.height() - 1, - colors::WHITE, - colors::BOX_GRAY, - ); - - for (i, s) in self.lines.iter().enumerate() { - let col = if i == 0 { - colors::WHITE - } else { - colors::LIGHT_GRAY - }; - ctx.print_color(x + 1, y + i as i32 + 1, col, colors::BLACK, &s); - } - } -} - -fn draw_tooltips(ecs: &World, ctx: &mut Rltk) { - use rltk::to_cp437; - - let (min_x, _max_x, min_y, _max_y) = camera::get_screen_bounds(ecs, ctx); - let map = ecs.fetch::(); - let names = ecs.read_storage::(); - let positions = ecs.read_storage::(); - let hidden = ecs.read_storage::(); - let attributes = ecs.read_storage::(); - let pools = ecs.read_storage::(); - let entities = ecs.entities(); - let mouse_pos = ctx.mouse_pos(); - let mut mouse_map_pos = mouse_pos; - mouse_map_pos.0 += min_x; - mouse_map_pos.1 += min_y; - - if mouse_map_pos.0 >= map.width - 1 - || mouse_map_pos.1 >= map.height - 1 - || mouse_map_pos.0 < 1 - || mouse_map_pos.1 < 1 - { - return; - } - - if !map.visible_tiles[map.xy_idx(mouse_map_pos.0, mouse_map_pos.1)] { - return; - } - - let mut tip_boxes: Vec = Vec::new(); - for (entity, name, position, _hidden) in (&entities, &names, &positions, !&hidden).join() { - if position.x == mouse_map_pos.0 && position.y == mouse_map_pos.1 { - let mut tip = Tooltip::new(); - tip.add(name.name.to_string()); - - // Comment on attributes - if let Some(attr) = attributes.get(entity) { - let mut s = String::new(); - if attr.might.bonus < 0 { - s += "Weak. " - } - if attr.might.bonus > 0 { - s += "String. " - } - if attr.quickness.bonus < 0 { - s += "Clumsy. " - } - if attr.quickness.bonus > 0 { - s += "Agile. " - } - if attr.fitness.bonus < 0 { - s += "Unhealthy. " - } - if attr.fitness.bonus > 0 { - s += "Healthy. " - } - if attr.intelligence.bonus < 0 { - s += "Unintelligent. " - } - if attr.intelligence.bonus > 0 { - s += "Smart. " - } - if s.is_empty() { - s = "Quite Average".to_string(); - } - - tip.add(s); - } - - // Comment on pools - if let Some(stat) = pools.get(entity) { - tip.add(format!("Level: {}", stat.level)); - } - - tip_boxes.push(tip); - } - } - - if tip_boxes.is_empty() { - return; - } - - let arrow; - let arrow_x; - let arrow_y = mouse_pos.1; - if mouse_pos.0 < 40 { - // Render to the left - arrow = to_cp437('→'); - arrow_x = mouse_pos.0 - 1; - } else { - // Render to the right - arrow = to_cp437('←'); - arrow_x = mouse_pos.0 + 1; - } - ctx.set(arrow_x, arrow_y, colors::WHITE, colors::BOX_GRAY, arrow); - - let mut total_height = 0; - for tt in tip_boxes.iter() { - total_height += tt.height(); - } - - let mut y = mouse_pos.1 - (total_height / 2); - while y + (total_height / 2) > 50 { - y -= 1; - } - - for tt in tip_boxes.iter() { - let x = if mouse_pos.0 < 40 { - mouse_pos.0 - (1 + tt.width()) - } else { - mouse_pos.0 + (1 + tt.width()) - }; - tt.render(ctx, x, y); - y += tt.height(); - } -} - -#[derive(PartialEq, Copy, Clone)] -pub enum ItemMenuResult { - Cancel, - NoResponse, - Selected, -} - pub fn show_inventory(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option) { let player_entity = gs.ecs.fetch::(); let names = gs.ecs.read_storage::(); @@ -740,19 +570,6 @@ pub fn ranged_target( (ItemMenuResult::NoResponse, None) } -#[derive(PartialEq, Copy, Clone)] -pub enum MainMenuSelection { - NewGame, - LoadGame, - Quit, -} - -#[derive(PartialEq, Copy, Clone)] -pub enum MainMenuResult { - NoSelection { selected: MainMenuSelection }, - Selected { selected: MainMenuSelection }, -} - pub fn main_menu(gs: &mut State, ctx: &mut Rltk) -> MainMenuResult { let save_exists = crate::saveload_system::does_save_exist(); let runstate = gs.ecs.fetch::(); @@ -846,12 +663,6 @@ pub fn main_menu(gs: &mut State, ctx: &mut Rltk) -> MainMenuResult { } } -#[derive(PartialEq, Copy, Clone)] -pub enum GameOverResult { - NoSelection, - QuitToMenu, -} - pub fn game_over(ctx: &mut Rltk) -> GameOverResult { ctx.print_color_centered(15, colors::YELLOW, colors::BLACK, "Your journey has ended!"); ctx.print_color_centered( @@ -880,16 +691,6 @@ pub fn game_over(ctx: &mut Rltk) -> GameOverResult { } } -#[derive(PartialEq, Copy, Clone)] -pub enum CheatMenuResult { - NoResponse, - Cancel, - TeleportToExit, - Heal, - Reveal, - GodMode, -} - pub fn show_cheat_mode(_gs: &mut State, ctx: &mut Rltk) -> CheatMenuResult { let count = 4; let mut y = (25 - (count / 2)) as i32; @@ -947,16 +748,6 @@ pub fn show_cheat_mode(_gs: &mut State, ctx: &mut Rltk) -> CheatMenuResult { } } -#[derive(PartialEq, Copy, Clone)] -pub enum VendorResult { - NoResponse, - Cancel, - Sell, - BuyMode, - SellMode, - Buy, -} - fn vendor_sell_menu( gs: &mut State, ctx: &mut Rltk, diff --git a/src/gui/enums.rs b/src/gui/enums.rs new file mode 100644 index 0000000..577bd34 --- /dev/null +++ b/src/gui/enums.rs @@ -0,0 +1,45 @@ +#[derive(PartialEq, Copy, Clone)] +pub enum ItemMenuResult { + Cancel, + NoResponse, + Selected, +} + +#[derive(PartialEq, Copy, Clone)] +pub enum MainMenuSelection { + NewGame, + LoadGame, + Quit, +} + +#[derive(PartialEq, Copy, Clone)] +pub enum MainMenuResult { + NoSelection { selected: MainMenuSelection }, + Selected { selected: MainMenuSelection }, +} + +#[derive(PartialEq, Copy, Clone)] +pub enum GameOverResult { + NoSelection, + QuitToMenu, +} + +#[derive(PartialEq, Copy, Clone)] +pub enum CheatMenuResult { + NoResponse, + Cancel, + TeleportToExit, + Heal, + Reveal, + GodMode, +} + +#[derive(PartialEq, Copy, Clone)] +pub enum VendorResult { + NoResponse, + Cancel, + Sell, + BuyMode, + SellMode, + Buy, +} diff --git a/src/gui/tooltip.rs b/src/gui/tooltip.rs new file mode 100644 index 0000000..4eb0b01 --- /dev/null +++ b/src/gui/tooltip.rs @@ -0,0 +1,170 @@ +use ::rltk::Rltk; +use ::specs::prelude::*; + +use crate::components::{Attributes, Hidden, Name, Pools, Position}; +use crate::{camera, colors, Map}; + +struct Tooltip { + lines: Vec, +} + +impl Tooltip { + fn new() -> Tooltip { + Tooltip { lines: Vec::new() } + } + + fn add(&mut self, line: S) { + self.lines.push(line.to_string()); + } + + fn width(&self) -> i32 { + let mut max = 0; + for s in self.lines.iter() { + if s.len() > max { + max = s.len(); + } + } + + max as i32 + 2 + } + + fn height(&self) -> i32 { + self.lines.len() as i32 + 2 + } + + fn render(&self, ctx: &mut Rltk, x: i32, y: i32) { + ctx.draw_box( + x, + y, + self.width() - 1, + self.height() - 1, + colors::WHITE, + colors::BOX_GRAY, + ); + + for (i, s) in self.lines.iter().enumerate() { + let col = if i == 0 { + colors::WHITE + } else { + colors::LIGHT_GRAY + }; + ctx.print_color(x + 1, y + i as i32 + 1, col, colors::BLACK, &s); + } + } +} + +pub(super) fn draw_tooltips(ecs: &World, ctx: &mut Rltk) { + use rltk::to_cp437; + + let (min_x, _max_x, min_y, _max_y) = camera::get_screen_bounds(ecs, ctx); + let map = ecs.fetch::(); + let names = ecs.read_storage::(); + let positions = ecs.read_storage::(); + let hidden = ecs.read_storage::(); + let attributes = ecs.read_storage::(); + let pools = ecs.read_storage::(); + let entities = ecs.entities(); + let mouse_pos = ctx.mouse_pos(); + let mut mouse_map_pos = mouse_pos; + mouse_map_pos.0 += min_x; + mouse_map_pos.1 += min_y; + + if mouse_map_pos.0 >= map.width - 1 + || mouse_map_pos.1 >= map.height - 1 + || mouse_map_pos.0 < 1 + || mouse_map_pos.1 < 1 + { + return; + } + + if !map.visible_tiles[map.xy_idx(mouse_map_pos.0, mouse_map_pos.1)] { + return; + } + + let mut tip_boxes: Vec = Vec::new(); + for (entity, name, position, _hidden) in (&entities, &names, &positions, !&hidden).join() { + if position.x == mouse_map_pos.0 && position.y == mouse_map_pos.1 { + let mut tip = Tooltip::new(); + tip.add(name.name.to_string()); + + // Comment on attributes + if let Some(attr) = attributes.get(entity) { + let mut s = String::new(); + if attr.might.bonus < 0 { + s += "Weak. " + } + if attr.might.bonus > 0 { + s += "String. " + } + if attr.quickness.bonus < 0 { + s += "Clumsy. " + } + if attr.quickness.bonus > 0 { + s += "Agile. " + } + if attr.fitness.bonus < 0 { + s += "Unhealthy. " + } + if attr.fitness.bonus > 0 { + s += "Healthy. " + } + if attr.intelligence.bonus < 0 { + s += "Unintelligent. " + } + if attr.intelligence.bonus > 0 { + s += "Smart. " + } + if s.is_empty() { + s = "Quite Average".to_string(); + } + + tip.add(s); + } + + // Comment on pools + if let Some(stat) = pools.get(entity) { + tip.add(format!("Level: {}", stat.level)); + } + + tip_boxes.push(tip); + } + } + + if tip_boxes.is_empty() { + return; + } + + let arrow; + let arrow_x; + let arrow_y = mouse_pos.1; + if mouse_pos.0 < 40 { + // Render to the left + arrow = to_cp437('→'); + arrow_x = mouse_pos.0 - 1; + } else { + // Render to the right + arrow = to_cp437('←'); + arrow_x = mouse_pos.0 + 1; + } + ctx.set(arrow_x, arrow_y, colors::WHITE, colors::BOX_GRAY, arrow); + + let mut total_height = 0; + for tt in tip_boxes.iter() { + total_height += tt.height(); + } + + let mut y = mouse_pos.1 - (total_height / 2); + while y + (total_height / 2) > 50 { + y -= 1; + } + + for tt in tip_boxes.iter() { + let x = if mouse_pos.0 < 40 { + mouse_pos.0 - (1 + tt.width()) + } else { + mouse_pos.0 + (1 + tt.width()) + }; + tt.render(ctx, x, y); + y += tt.height(); + } +} diff --git a/src/state.rs b/src/state.rs index 2d49687..17b32d6 100644 --- a/src/state.rs +++ b/src/state.rs @@ -60,7 +60,7 @@ pub struct State { } impl State { - pub fn new() -> Self { + pub(super) fn new() -> Self { State { ecs: World::new(), mapgen_next_state: Some(RunState::MainMenu { @@ -186,7 +186,7 @@ impl State { self.generate_world_map(1, 0); } - pub fn generate_world_map(&mut self, new_depth: i32, offset: i32) { + pub(super) fn generate_world_map(&mut self, new_depth: i32, offset: i32) { self.mapgen_index = 0; self.mapgen_timer = 0.0; self.mapgen_history.clear();