diff --git a/src/gui.rs b/src/gui.rs index e3635ee..62dbcd9 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -1,20 +1,21 @@ mod enums; +mod menu; mod tooltip; use std::cmp::Ordering; -use ::rltk::{Point, Rltk, VirtualKeyCode, RGB}; +use ::rltk::{Point, Rltk, RGB}; use ::specs::prelude::*; pub use enums::*; +pub use menu::*; use tooltip::draw_tooltips; use crate::components::{ - Attribute, Attributes, Consumable, CursedItem, Equipped, HungerClock, HungerState, InBackpack, - Item, MagicItem, MagicItemClass, Name, ObfuscatedName, Pools, Vendor, Viewshed, + Attribute, Attributes, Consumable, CursedItem, Equipped, HungerClock, HungerState, InBackpack, MagicItem, MagicItemClass, Name, ObfuscatedName, Pools, Viewshed, }; use crate::game_log::GameLog; -use crate::rex_assets::RexAssets; -use crate::{camera, colors, Map, MasterDungeonMap, RunState, State, VendorMode}; + +use crate::{camera, colors, Map, MasterDungeonMap, State}; pub fn get_item_color(ecs: &World, item: Entity) -> RGB { let dm = ecs.fetch::(); @@ -280,242 +281,6 @@ fn draw_attribute(name: &str, attribute: &Attribute, y: i32, ctx: &mut Rltk) { } } -pub fn show_inventory(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option) { - let player_entity = gs.ecs.fetch::(); - let names = gs.ecs.read_storage::(); - let backpack = gs.ecs.read_storage::(); - let entities = gs.ecs.entities(); - - let inventory = (&backpack, &names) - .join() - .filter(|item| item.0.owner == *player_entity); - let count = inventory.count(); - - let mut y = (25 - (count / 2)) as i32; - ctx.draw_box( - 15, - y - 2, - 31, - (count + 3) as i32, - colors::WHITE, - colors::BLACK, - ); - ctx.print_color(18, y - 2, colors::YELLOW, colors::BLACK, "Inventory"); - ctx.print_color( - 18, - y + count as i32 + 1, - colors::YELLOW, - colors::BLACK, - "ESCAPE to cancel", - ); - - let mut equippable: Vec = Vec::new(); - let mut j = 0; - #[allow(clippy::explicit_counter_loop)] - for (entity, _pack) in (&entities, &backpack) - .join() - .filter(|item| item.1.owner == *player_entity) - { - ctx.set(17, y, colors::WHITE, colors::BLACK, rltk::to_cp437('(')); - ctx.set( - 18, - y, - colors::YELLOW, - colors::BLACK, - 97 + j as rltk::FontCharType, - ); - ctx.set(19, y, colors::WHITE, colors::BLACK, rltk::to_cp437(')')); - - ctx.print_color( - 21, - y, - get_item_color(&gs.ecs, entity), - colors::BLACK, - &get_item_display_name(&gs.ecs, entity), - ); - equippable.push(entity); - y += 1; - j += 1; - } - - match ctx.key { - None => (ItemMenuResult::NoResponse, None), - Some(key) => match key { - VirtualKeyCode::Escape => (ItemMenuResult::Cancel, None), - _ => { - let selection = rltk::letter_to_option(key); - if selection > -1 && selection < count as i32 { - return ( - ItemMenuResult::Selected, - Some(equippable[selection as usize]), - ); - } - (ItemMenuResult::NoResponse, None) - } - }, - } -} - -pub fn drop_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option) { - let player_entity = gs.ecs.fetch::(); - let names = gs.ecs.read_storage::(); - let backpack = gs.ecs.read_storage::(); - let entities = gs.ecs.entities(); - - let inventory = (&backpack, &names) - .join() - .filter(|item| item.0.owner == *player_entity); - let count = inventory.count(); - - let mut y = (25 - (count / 2)) as i32; - ctx.draw_box( - 15, - y - 2, - 31, - (count + 3) as i32, - colors::WHITE, - colors::BLACK, - ); - ctx.print_color(18, y - 2, colors::YELLOW, colors::BLACK, "Drop Which Item?"); - ctx.print_color( - 18, - y + count as i32 + 1, - colors::YELLOW, - colors::BLACK, - "ESCAPE to cancel", - ); - - let mut equippable: Vec = Vec::new(); - let mut j = 0; - #[allow(clippy::explicit_counter_loop)] - for (entity, _pack) in (&entities, &backpack) - .join() - .filter(|item| item.1.owner == *player_entity) - { - ctx.set(17, y, colors::WHITE, colors::BLACK, rltk::to_cp437('(')); - ctx.set( - 18, - y, - colors::YELLOW, - colors::BLACK, - 97 + j as rltk::FontCharType, - ); - ctx.set(19, y, colors::WHITE, colors::BLACK, rltk::to_cp437(')')); - - ctx.print_color( - 21, - y, - get_item_color(&gs.ecs, entity), - colors::BLACK, - &get_item_display_name(&gs.ecs, entity), - ); - equippable.push(entity); - y += 1; - j += 1; - } - - match ctx.key { - None => (ItemMenuResult::NoResponse, None), - Some(key) => match key { - VirtualKeyCode::Escape => (ItemMenuResult::Cancel, None), - _ => { - let selection = rltk::letter_to_option(key); - if selection > -1 && selection < count as i32 { - return ( - ItemMenuResult::Selected, - Some(equippable[selection as usize]), - ); - } - (ItemMenuResult::NoResponse, None) - } - }, - } -} - -pub fn remove_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option) { - let player_entity = gs.ecs.fetch::(); - let names = gs.ecs.read_storage::(); - let backpack = gs.ecs.read_storage::(); - let entities = gs.ecs.entities(); - - let inventory = (&backpack, &names) - .join() - .filter(|item| item.0.owner == *player_entity); - let count = inventory.count(); - - let mut y = (25 - (count / 2)) as i32; - ctx.draw_box( - 15, - y - 2, - 31, - (count + 3) as i32, - colors::WHITE, - colors::BLACK, - ); - ctx.print_color( - 18, - y - 2, - colors::YELLOW, - colors::BLACK, - "Remove Which Item?", - ); - ctx.print_color( - 18, - y + count as i32 + 1, - colors::YELLOW, - colors::BLACK, - "ESCAPE to cancel", - ); - - let mut equippable: Vec = Vec::new(); - let mut j = 0; - #[allow(clippy::explicit_counter_loop)] - for (entity, _pack) in (&entities, &backpack) - .join() - .filter(|item| item.1.owner == *player_entity) - { - ctx.set(17, y, colors::WHITE, colors::BLACK, rltk::to_cp437('(')); - ctx.set( - 18, - y, - colors::YELLOW, - colors::BLACK, - 97 + j as rltk::FontCharType, - ); - ctx.set(19, y, colors::WHITE, colors::BLACK, rltk::to_cp437(')')); - - ctx.print_color( - 21, - y, - get_item_color(&gs.ecs, entity), - colors::BLACK, - &get_item_display_name(&gs.ecs, entity), - ); - - equippable.push(entity); - y += 1; - j += 1; - } - - match ctx.key { - None => (ItemMenuResult::NoResponse, None), - Some(key) => match key { - VirtualKeyCode::Escape => (ItemMenuResult::Cancel, None), - _ => { - let selection = rltk::letter_to_option(key); - if selection > -1 && selection < count as i32 { - return ( - ItemMenuResult::Selected, - Some(equippable[selection as usize]), - ); - } - - (ItemMenuResult::NoResponse, None) - } - }, - } -} - pub fn ranged_target( gs: &mut State, ctx: &mut Rltk, @@ -580,355 +345,3 @@ pub fn ranged_target( (ItemMenuResult::NoResponse, None) } - -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::(); - let assets = gs.ecs.fetch::(); - ctx.render_xp_sprite(&assets.menu, 0, 0); - - ctx.draw_box_double(24, 18, 31, 10, colors::WHEAT, colors::BLACK); - - ctx.print_color_centered(20, colors::YELLOW, colors::BLACK, "Rust Roguelike"); - ctx.print_color_centered(21, colors::CYAN, colors::BLACK, "by Timothy J. Warren"); - ctx.print_color_centered( - 22, - colors::GRAY, - colors::BLACK, - "Use Up/Down Arrows and Enter", - ); - - let mut y = 24; - if let RunState::MainMenu { - menu_selection: selection, - } = *runstate - { - if selection == MainMenuSelection::NewGame { - ctx.print_color_centered(y, colors::MAGENTA, colors::BLACK, "Begin New Game"); - } else { - ctx.print_color_centered(y, colors::WHITE, colors::BLACK, "Begin New Game"); - } - y += 1; - - if save_exists { - if selection == MainMenuSelection::LoadGame { - ctx.print_color_centered(y, colors::MAGENTA, colors::BLACK, "Load Game"); - } else { - ctx.print_color_centered(y, colors::WHITE, colors::BLACK, "Load Game"); - } - y += 1; - } - - if selection == MainMenuSelection::Quit { - ctx.print_color_centered(y, colors::MAGENTA, colors::BLACK, "Quit"); - } else { - ctx.print_color_centered(y, colors::WHITE, colors::BLACK, "Quit"); - } - - return match ctx.key { - None => MainMenuResult::NoSelection { - selected: selection, - }, - Some(key) => match key { - VirtualKeyCode::Escape => MainMenuResult::NoSelection { - selected: MainMenuSelection::Quit, - }, - VirtualKeyCode::Up => MainMenuResult::NoSelection { - selected: match selection { - MainMenuSelection::NewGame => MainMenuSelection::Quit, - MainMenuSelection::LoadGame => MainMenuSelection::NewGame, - MainMenuSelection::Quit => { - if save_exists { - MainMenuSelection::LoadGame - } else { - MainMenuSelection::NewGame - } - } - }, - }, - VirtualKeyCode::Down => MainMenuResult::NoSelection { - selected: match selection { - MainMenuSelection::NewGame => { - if save_exists { - MainMenuSelection::LoadGame - } else { - MainMenuSelection::Quit - } - } - MainMenuSelection::LoadGame => MainMenuSelection::Quit, - MainMenuSelection::Quit => MainMenuSelection::NewGame, - }, - }, - VirtualKeyCode::Return => MainMenuResult::Selected { - selected: selection, - }, - _ => MainMenuResult::NoSelection { - selected: selection, - }, - }, - }; - } - - MainMenuResult::NoSelection { - selected: MainMenuSelection::NewGame, - } -} - -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( - 17, - colors::WHITE, - colors::BLACK, - "One day, we'll tell you all about how you did.", - ); - ctx.print_color_centered( - 18, - colors::WHITE, - colors::BLACK, - "That day, sadly, is not in this chapter...", - ); - - ctx.print_color_centered( - 20, - colors::MAGENTA, - colors::BLACK, - "Press any key to return to the menu.", - ); - - match ctx.key { - None => GameOverResult::NoSelection, - Some(_) => GameOverResult::QuitToMenu, - } -} - -pub fn show_cheat_mode(_gs: &mut State, ctx: &mut Rltk) -> CheatMenuResult { - let count = 4; - let mut y = (25 - (count / 2)) as i32; - ctx.draw_box( - 15, - y - 2, - 31, - (count + 3) as i32, - colors::WHITE, - colors::BLACK, - ); - ctx.print_color(18, y - 2, colors::YELLOW, colors::BLACK, "Cheating!"); - ctx.print_color( - 18, - y + count as i32 + 1, - colors::YELLOW, - colors::BLACK, - "ESCAPE to cancel", - ); - - ctx.set(17, y, colors::WHITE, colors::BLACK, rltk::to_cp437('(')); - ctx.set(18, y, colors::YELLOW, colors::BLACK, rltk::to_cp437('T')); - ctx.set(19, y, colors::WHITE, colors::BLACK, rltk::to_cp437(')')); - - ctx.print(21, y, "Teleport to next level"); - - y += 1; - ctx.set(17, y, colors::WHITE, colors::BLACK, rltk::to_cp437('(')); - ctx.set(18, y, colors::YELLOW, colors::BLACK, rltk::to_cp437('H')); - ctx.set(19, y, colors::WHITE, colors::BLACK, rltk::to_cp437(')')); - ctx.print(21, y, "Heal all wounds"); - - y += 1; - ctx.set(17, y, colors::WHITE, colors::BLACK, rltk::to_cp437('(')); - ctx.set(18, y, colors::YELLOW, colors::BLACK, rltk::to_cp437('R')); - ctx.set(19, y, colors::WHITE, colors::BLACK, rltk::to_cp437(')')); - ctx.print(21, y, "Reveal the map"); - - y += 1; - ctx.set(17, y, colors::WHITE, colors::BLACK, rltk::to_cp437('(')); - ctx.set(18, y, colors::YELLOW, colors::BLACK, rltk::to_cp437('G')); - ctx.set(19, y, colors::WHITE, colors::BLACK, rltk::to_cp437(')')); - ctx.print(21, y, "God Mode (No Death)"); - - match ctx.key { - None => CheatMenuResult::NoResponse, - Some(key) => match key { - VirtualKeyCode::T => CheatMenuResult::TeleportToExit, - VirtualKeyCode::H => CheatMenuResult::Heal, - VirtualKeyCode::R => CheatMenuResult::Reveal, - VirtualKeyCode::G => CheatMenuResult::GodMode, - VirtualKeyCode::Escape => CheatMenuResult::Cancel, - _ => CheatMenuResult::NoResponse, - }, - } -} - -fn vendor_sell_menu( - gs: &mut State, - ctx: &mut Rltk, - _vendor: Entity, - _mode: VendorMode, -) -> (VendorResult, Option, Option, Option) { - let player_entity = gs.ecs.fetch::(); - let names = gs.ecs.read_storage::(); - let backpack = gs.ecs.read_storage::(); - let items = gs.ecs.read_storage::(); - let entities = gs.ecs.entities(); - - let inventory = (&backpack, &names) - .join() - .filter(|item| item.0.owner == *player_entity); - let count = inventory.count(); - - let mut y = (25 - (count / 2)) as i32; - ctx.draw_box( - 15, - y - 2, - 51, - (count + 3) as i32, - colors::WHITE, - colors::BLACK, - ); - ctx.print_color( - 18, - y - 2, - colors::YELLOW, - colors::BLACK, - "Sell Which Item? (space to switch to buy mode)", - ); - ctx.print_color( - 18, - y + count as i32 + 1, - colors::YELLOW, - colors::BLACK, - "ESCAPE to cancel", - ); - - let mut equippable: Vec = Vec::new(); - for (j, (entity, _pack, name, item)) in (&entities, &backpack, &names, &items) - .join() - .filter(|item| item.1.owner == *player_entity) - .enumerate() - { - ctx.set(17, y, colors::WHITE, colors::BLACK, rltk::to_cp437('(')); - ctx.set( - 18, - y, - colors::YELLOW, - colors::BLACK, - 97 + j as rltk::FontCharType, - ); - ctx.set(19, y, colors::WHITE, colors::BLACK, rltk::to_cp437(')')); - - ctx.print(21, y, &name.name.to_string()); - ctx.print(50, y, &format!("{:.1} gp", item.base_value * 0.8)); - equippable.push(entity); - y += 1; - } - - match ctx.key { - None => (VendorResult::NoResponse, None, None, None), - Some(key) => match key { - VirtualKeyCode::Space => (VendorResult::BuyMode, None, None, None), - VirtualKeyCode::Escape => (VendorResult::Cancel, None, None, None), - _ => { - let selection = rltk::letter_to_option(key); - if selection > -1 && selection < count as i32 { - return ( - VendorResult::Sell, - Some(equippable[selection as usize]), - None, - None, - ); - } - (VendorResult::NoResponse, None, None, None) - } - }, - } -} - -fn vendor_buy_menu( - gs: &mut State, - ctx: &mut Rltk, - vendor: Entity, - _mode: VendorMode, -) -> (VendorResult, Option, Option, Option) { - use crate::raws::*; - - let vendors = gs.ecs.read_storage::(); - - let inventory = get_vendor_items( - &vendors.get(vendor).unwrap().categories, - &RAWS.lock().unwrap(), - ); - let count = inventory.len(); - - let mut y = (25 - (count / 2)) as i32; - ctx.draw_box( - 15, - y - 2, - 51, - (count + 3) as i32, - colors::WHITE, - colors::BLACK, - ); - ctx.print_color( - 18, - y - 2, - colors::YELLOW, - colors::BLACK, - "Buy Which Item? (space to switch to sell mode)", - ); - ctx.print_color( - 18, - y + count as i32 + 1, - colors::YELLOW, - colors::BLACK, - "ESCAPE to cancel", - ); - - for (j, sale) in inventory.iter().enumerate() { - ctx.set(17, y, colors::WHITE, colors::BLACK, rltk::to_cp437('(')); - ctx.set( - 18, - y, - colors::YELLOW, - colors::BLACK, - 97 + j as rltk::FontCharType, - ); - ctx.set(19, y, colors::WHITE, colors::BLACK, rltk::to_cp437(')')); - - ctx.print(21, y, &sale.0); - ctx.print(50, y, &format!("{:.1} gp", sale.1 * 1.2)); - y += 1; - } - - match ctx.key { - None => (VendorResult::NoResponse, None, None, None), - Some(key) => match key { - VirtualKeyCode::Space => (VendorResult::SellMode, None, None, None), - VirtualKeyCode::Escape => (VendorResult::Cancel, None, None, None), - _ => { - let selection = ::rltk::letter_to_option(key); - if selection > -1 && selection < count as i32 { - return ( - VendorResult::Buy, - None, - Some(inventory[selection as usize].0.clone()), - Some(inventory[selection as usize].1), - ); - } - - (VendorResult::NoResponse, None, None, None) - } - }, - } -} - -pub fn show_vendor_menu( - gs: &mut State, - ctx: &mut Rltk, - vendor: Entity, - mode: VendorMode, -) -> (VendorResult, Option, Option, Option) { - match mode { - VendorMode::Buy => vendor_buy_menu(gs, ctx, vendor, mode), - VendorMode::Sell => vendor_sell_menu(gs, ctx, vendor, mode), - } -} diff --git a/src/gui/menu.rs b/src/gui/menu.rs new file mode 100644 index 0000000..5ca368a --- /dev/null +++ b/src/gui/menu.rs @@ -0,0 +1,715 @@ + + +use ::rltk::{Rltk, VirtualKeyCode, RGB}; +use ::specs::prelude::*; + +use super::enums::*; +use super::{get_item_color, get_item_display_name}; +use crate::components::{ + CursedItem, Equipped, InBackpack, + Item, Name, Vendor, +}; + +use crate::rex_assets::RexAssets; +use crate::{colors, MasterDungeonMap, RunState, State, VendorMode}; + +pub fn show_inventory(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option) { + let player_entity = gs.ecs.fetch::(); + let names = gs.ecs.read_storage::(); + let backpack = gs.ecs.read_storage::(); + let entities = gs.ecs.entities(); + + let inventory = (&backpack, &names) + .join() + .filter(|item| item.0.owner == *player_entity); + let count = inventory.count(); + + let mut y = (25 - (count / 2)) as i32; + ctx.draw_box( + 15, + y - 2, + 31, + (count + 3) as i32, + colors::WHITE, + colors::BLACK, + ); + ctx.print_color(18, y - 2, colors::YELLOW, colors::BLACK, "Inventory"); + ctx.print_color( + 18, + y + count as i32 + 1, + colors::YELLOW, + colors::BLACK, + "ESCAPE to cancel", + ); + + let mut equippable: Vec = Vec::new(); + let mut j = 0; + #[allow(clippy::explicit_counter_loop)] + for (entity, _pack) in (&entities, &backpack) + .join() + .filter(|item| item.1.owner == *player_entity) + { + ctx.set(17, y, colors::WHITE, colors::BLACK, rltk::to_cp437('(')); + ctx.set( + 18, + y, + colors::YELLOW, + colors::BLACK, + 97 + j as rltk::FontCharType, + ); + ctx.set(19, y, colors::WHITE, colors::BLACK, rltk::to_cp437(')')); + + ctx.print_color( + 21, + y, + get_item_color(&gs.ecs, entity), + colors::BLACK, + &get_item_display_name(&gs.ecs, entity), + ); + equippable.push(entity); + y += 1; + j += 1; + } + + match ctx.key { + None => (ItemMenuResult::NoResponse, None), + Some(key) => match key { + VirtualKeyCode::Escape => (ItemMenuResult::Cancel, None), + _ => { + let selection = rltk::letter_to_option(key); + if selection > -1 && selection < count as i32 { + return ( + ItemMenuResult::Selected, + Some(equippable[selection as usize]), + ); + } + (ItemMenuResult::NoResponse, None) + } + }, + } +} + +pub fn drop_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option) { + let player_entity = gs.ecs.fetch::(); + let names = gs.ecs.read_storage::(); + let backpack = gs.ecs.read_storage::(); + let entities = gs.ecs.entities(); + + let inventory = (&backpack, &names) + .join() + .filter(|item| item.0.owner == *player_entity); + let count = inventory.count(); + + let mut y = (25 - (count / 2)) as i32; + ctx.draw_box( + 15, + y - 2, + 31, + (count + 3) as i32, + colors::WHITE, + colors::BLACK, + ); + ctx.print_color(18, y - 2, colors::YELLOW, colors::BLACK, "Drop Which Item?"); + ctx.print_color( + 18, + y + count as i32 + 1, + colors::YELLOW, + colors::BLACK, + "ESCAPE to cancel", + ); + + let mut equippable: Vec = Vec::new(); + let mut j = 0; + #[allow(clippy::explicit_counter_loop)] + for (entity, _pack) in (&entities, &backpack) + .join() + .filter(|item| item.1.owner == *player_entity) + { + ctx.set(17, y, colors::WHITE, colors::BLACK, rltk::to_cp437('(')); + ctx.set( + 18, + y, + colors::YELLOW, + colors::BLACK, + 97 + j as rltk::FontCharType, + ); + ctx.set(19, y, colors::WHITE, colors::BLACK, rltk::to_cp437(')')); + + ctx.print_color( + 21, + y, + get_item_color(&gs.ecs, entity), + colors::BLACK, + &get_item_display_name(&gs.ecs, entity), + ); + equippable.push(entity); + y += 1; + j += 1; + } + + match ctx.key { + None => (ItemMenuResult::NoResponse, None), + Some(key) => match key { + VirtualKeyCode::Escape => (ItemMenuResult::Cancel, None), + _ => { + let selection = rltk::letter_to_option(key); + if selection > -1 && selection < count as i32 { + return ( + ItemMenuResult::Selected, + Some(equippable[selection as usize]), + ); + } + (ItemMenuResult::NoResponse, None) + } + }, + } +} + +pub fn remove_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option) { + let player_entity = gs.ecs.fetch::(); + let names = gs.ecs.read_storage::(); + let backpack = gs.ecs.read_storage::(); + let entities = gs.ecs.entities(); + + let inventory = (&backpack, &names) + .join() + .filter(|item| item.0.owner == *player_entity); + let count = inventory.count(); + + let mut y = (25 - (count / 2)) as i32; + ctx.draw_box( + 15, + y - 2, + 31, + (count + 3) as i32, + colors::WHITE, + colors::BLACK, + ); + ctx.print_color( + 18, + y - 2, + colors::YELLOW, + colors::BLACK, + "Remove Which Item?", + ); + ctx.print_color( + 18, + y + count as i32 + 1, + colors::YELLOW, + colors::BLACK, + "ESCAPE to cancel", + ); + + let mut equippable: Vec = Vec::new(); + let mut j = 0; + #[allow(clippy::explicit_counter_loop)] + for (entity, _pack) in (&entities, &backpack) + .join() + .filter(|item| item.1.owner == *player_entity) + { + ctx.set(17, y, colors::WHITE, colors::BLACK, rltk::to_cp437('(')); + ctx.set( + 18, + y, + colors::YELLOW, + colors::BLACK, + 97 + j as rltk::FontCharType, + ); + ctx.set(19, y, colors::WHITE, colors::BLACK, rltk::to_cp437(')')); + + ctx.print_color( + 21, + y, + get_item_color(&gs.ecs, entity), + colors::BLACK, + &get_item_display_name(&gs.ecs, entity), + ); + + equippable.push(entity); + y += 1; + j += 1; + } + + match ctx.key { + None => (ItemMenuResult::NoResponse, None), + Some(key) => match key { + VirtualKeyCode::Escape => (ItemMenuResult::Cancel, None), + _ => { + let selection = rltk::letter_to_option(key); + if selection > -1 && selection < count as i32 { + return ( + ItemMenuResult::Selected, + Some(equippable[selection as usize]), + ); + } + + (ItemMenuResult::NoResponse, None) + } + }, + } +} + +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::(); + let assets = gs.ecs.fetch::(); + ctx.render_xp_sprite(&assets.menu, 0, 0); + + ctx.draw_box_double(24, 18, 31, 10, colors::WHEAT, colors::BLACK); + + ctx.print_color_centered(20, colors::YELLOW, colors::BLACK, "Rust Roguelike"); + ctx.print_color_centered(21, colors::CYAN, colors::BLACK, "by Timothy J. Warren"); + ctx.print_color_centered( + 22, + colors::GRAY, + colors::BLACK, + "Use Up/Down Arrows and Enter", + ); + + let mut y = 24; + if let RunState::MainMenu { + menu_selection: selection, + } = *runstate + { + if selection == MainMenuSelection::NewGame { + ctx.print_color_centered(y, colors::MAGENTA, colors::BLACK, "Begin New Game"); + } else { + ctx.print_color_centered(y, colors::WHITE, colors::BLACK, "Begin New Game"); + } + y += 1; + + if save_exists { + if selection == MainMenuSelection::LoadGame { + ctx.print_color_centered(y, colors::MAGENTA, colors::BLACK, "Load Game"); + } else { + ctx.print_color_centered(y, colors::WHITE, colors::BLACK, "Load Game"); + } + y += 1; + } + + if selection == MainMenuSelection::Quit { + ctx.print_color_centered(y, colors::MAGENTA, colors::BLACK, "Quit"); + } else { + ctx.print_color_centered(y, colors::WHITE, colors::BLACK, "Quit"); + } + + return match ctx.key { + None => MainMenuResult::NoSelection { + selected: selection, + }, + Some(key) => match key { + VirtualKeyCode::Escape => MainMenuResult::NoSelection { + selected: MainMenuSelection::Quit, + }, + VirtualKeyCode::Up => MainMenuResult::NoSelection { + selected: match selection { + MainMenuSelection::NewGame => MainMenuSelection::Quit, + MainMenuSelection::LoadGame => MainMenuSelection::NewGame, + MainMenuSelection::Quit => { + if save_exists { + MainMenuSelection::LoadGame + } else { + MainMenuSelection::NewGame + } + } + }, + }, + VirtualKeyCode::Down => MainMenuResult::NoSelection { + selected: match selection { + MainMenuSelection::NewGame => { + if save_exists { + MainMenuSelection::LoadGame + } else { + MainMenuSelection::Quit + } + } + MainMenuSelection::LoadGame => MainMenuSelection::Quit, + MainMenuSelection::Quit => MainMenuSelection::NewGame, + }, + }, + VirtualKeyCode::Return => MainMenuResult::Selected { + selected: selection, + }, + _ => MainMenuResult::NoSelection { + selected: selection, + }, + }, + }; + } + + MainMenuResult::NoSelection { + selected: MainMenuSelection::NewGame, + } +} + +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( + 17, + colors::WHITE, + colors::BLACK, + "One day, we'll tell you all about how you did.", + ); + ctx.print_color_centered( + 18, + colors::WHITE, + colors::BLACK, + "That day, sadly, is not in this chapter...", + ); + + ctx.print_color_centered( + 20, + colors::MAGENTA, + colors::BLACK, + "Press any key to return to the menu.", + ); + + match ctx.key { + None => GameOverResult::NoSelection, + Some(_) => GameOverResult::QuitToMenu, + } +} + +pub fn show_cheat_mode(_gs: &mut State, ctx: &mut Rltk) -> CheatMenuResult { + let count = 4; + let mut y = (25 - (count / 2)) as i32; + ctx.draw_box( + 15, + y - 2, + 31, + (count + 3) as i32, + colors::WHITE, + colors::BLACK, + ); + ctx.print_color(18, y - 2, colors::YELLOW, colors::BLACK, "Cheating!"); + ctx.print_color( + 18, + y + count as i32 + 1, + colors::YELLOW, + colors::BLACK, + "ESCAPE to cancel", + ); + + ctx.set(17, y, colors::WHITE, colors::BLACK, rltk::to_cp437('(')); + ctx.set(18, y, colors::YELLOW, colors::BLACK, rltk::to_cp437('T')); + ctx.set(19, y, colors::WHITE, colors::BLACK, rltk::to_cp437(')')); + + ctx.print(21, y, "Teleport to next level"); + + y += 1; + ctx.set(17, y, colors::WHITE, colors::BLACK, rltk::to_cp437('(')); + ctx.set(18, y, colors::YELLOW, colors::BLACK, rltk::to_cp437('H')); + ctx.set(19, y, colors::WHITE, colors::BLACK, rltk::to_cp437(')')); + ctx.print(21, y, "Heal all wounds"); + + y += 1; + ctx.set(17, y, colors::WHITE, colors::BLACK, rltk::to_cp437('(')); + ctx.set(18, y, colors::YELLOW, colors::BLACK, rltk::to_cp437('R')); + ctx.set(19, y, colors::WHITE, colors::BLACK, rltk::to_cp437(')')); + ctx.print(21, y, "Reveal the map"); + + y += 1; + ctx.set(17, y, colors::WHITE, colors::BLACK, rltk::to_cp437('(')); + ctx.set(18, y, colors::YELLOW, colors::BLACK, rltk::to_cp437('G')); + ctx.set(19, y, colors::WHITE, colors::BLACK, rltk::to_cp437(')')); + ctx.print(21, y, "God Mode (No Death)"); + + match ctx.key { + None => CheatMenuResult::NoResponse, + Some(key) => match key { + VirtualKeyCode::T => CheatMenuResult::TeleportToExit, + VirtualKeyCode::H => CheatMenuResult::Heal, + VirtualKeyCode::R => CheatMenuResult::Reveal, + VirtualKeyCode::G => CheatMenuResult::GodMode, + VirtualKeyCode::Escape => CheatMenuResult::Cancel, + _ => CheatMenuResult::NoResponse, + }, + } +} + +fn vendor_sell_menu( + gs: &mut State, + ctx: &mut Rltk, + _vendor: Entity, + _mode: VendorMode, +) -> (VendorResult, Option, Option, Option) { + let player_entity = gs.ecs.fetch::(); + let names = gs.ecs.read_storage::(); + let backpack = gs.ecs.read_storage::(); + let items = gs.ecs.read_storage::(); + let entities = gs.ecs.entities(); + + let inventory = (&backpack, &names) + .join() + .filter(|item| item.0.owner == *player_entity); + let count = inventory.count(); + + let mut y = (25 - (count / 2)) as i32; + ctx.draw_box( + 15, + y - 2, + 51, + (count + 3) as i32, + colors::WHITE, + colors::BLACK, + ); + ctx.print_color( + 18, + y - 2, + colors::YELLOW, + colors::BLACK, + "Sell Which Item? (space to switch to buy mode)", + ); + ctx.print_color( + 18, + y + count as i32 + 1, + colors::YELLOW, + colors::BLACK, + "ESCAPE to cancel", + ); + + let mut equippable: Vec = Vec::new(); + for (j, (entity, _pack, name, item)) in (&entities, &backpack, &names, &items) + .join() + .filter(|item| item.1.owner == *player_entity) + .enumerate() + { + ctx.set(17, y, colors::WHITE, colors::BLACK, rltk::to_cp437('(')); + ctx.set( + 18, + y, + colors::YELLOW, + colors::BLACK, + 97 + j as rltk::FontCharType, + ); + ctx.set(19, y, colors::WHITE, colors::BLACK, rltk::to_cp437(')')); + + ctx.print(21, y, &name.name.to_string()); + ctx.print(50, y, &format!("{:.1} gp", item.base_value * 0.8)); + equippable.push(entity); + y += 1; + } + + match ctx.key { + None => (VendorResult::NoResponse, None, None, None), + Some(key) => match key { + VirtualKeyCode::Space => (VendorResult::BuyMode, None, None, None), + VirtualKeyCode::Escape => (VendorResult::Cancel, None, None, None), + _ => { + let selection = rltk::letter_to_option(key); + if selection > -1 && selection < count as i32 { + return ( + VendorResult::Sell, + Some(equippable[selection as usize]), + None, + None, + ); + } + (VendorResult::NoResponse, None, None, None) + } + }, + } +} + +fn vendor_buy_menu( + gs: &mut State, + ctx: &mut Rltk, + vendor: Entity, + _mode: VendorMode, +) -> (VendorResult, Option, Option, Option) { + use crate::raws::*; + + let vendors = gs.ecs.read_storage::(); + + let inventory = get_vendor_items( + &vendors.get(vendor).unwrap().categories, + &RAWS.lock().unwrap(), + ); + let count = inventory.len(); + + let mut y = (25 - (count / 2)) as i32; + ctx.draw_box( + 15, + y - 2, + 51, + (count + 3) as i32, + colors::WHITE, + colors::BLACK, + ); + ctx.print_color( + 18, + y - 2, + colors::YELLOW, + colors::BLACK, + "Buy Which Item? (space to switch to sell mode)", + ); + ctx.print_color( + 18, + y + count as i32 + 1, + colors::YELLOW, + colors::BLACK, + "ESCAPE to cancel", + ); + + for (j, sale) in inventory.iter().enumerate() { + ctx.set(17, y, colors::WHITE, colors::BLACK, rltk::to_cp437('(')); + ctx.set( + 18, + y, + colors::YELLOW, + colors::BLACK, + 97 + j as rltk::FontCharType, + ); + ctx.set(19, y, colors::WHITE, colors::BLACK, rltk::to_cp437(')')); + + ctx.print(21, y, &sale.0); + ctx.print(50, y, &format!("{:.1} gp", sale.1 * 1.2)); + y += 1; + } + + match ctx.key { + None => (VendorResult::NoResponse, None, None, None), + Some(key) => match key { + VirtualKeyCode::Space => (VendorResult::SellMode, None, None, None), + VirtualKeyCode::Escape => (VendorResult::Cancel, None, None, None), + _ => { + let selection = ::rltk::letter_to_option(key); + if selection > -1 && selection < count as i32 { + return ( + VendorResult::Buy, + None, + Some(inventory[selection as usize].0.clone()), + Some(inventory[selection as usize].1), + ); + } + + (VendorResult::NoResponse, None, None, None) + } + }, + } +} + +pub fn show_vendor_menu( + gs: &mut State, + ctx: &mut Rltk, + vendor: Entity, + mode: VendorMode, +) -> (VendorResult, Option, Option, Option) { + match mode { + VendorMode::Buy => vendor_buy_menu(gs, ctx, vendor, mode), + VendorMode::Sell => vendor_sell_menu(gs, ctx, vendor, mode), + } +} + +pub fn remove_curse_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option) { + let player_entity = gs.ecs.fetch::(); + let equipped = gs.ecs.read_storage::(); + let backpack = gs.ecs.read_storage::(); + let entities = gs.ecs.entities(); + let items = gs.ecs.read_storage::(); + let cursed = gs.ecs.read_storage::(); + let names = gs.ecs.read_storage::(); + let dm = gs.ecs.fetch::(); + + let build_cursed_iterator = || { + (&entities, &items, &cursed) + .join() + .filter(|(item_entity, _item, _cursed)| { + let mut keep = false; + if let Some(bp) = backpack.get(*item_entity) { + if bp.owner == *player_entity { + if let Some(name) = names.get(*item_entity) { + if dm.identified_items.contains(&name.name) { + keep = true; + } + } + } + } + // It's equipped, so we know it's cursed + if let Some(equip) = equipped.get(*item_entity) { + if equip.owner == *player_entity { + keep = true; + } + } + keep + }) + }; + + let count = build_cursed_iterator().count(); + + let mut y = (25 - (count / 2)) as i32; + ctx.draw_box( + 15, + y - 2, + 31, + (count + 3) as i32, + RGB::named(rltk::WHITE), + RGB::named(rltk::BLACK), + ); + ctx.print_color( + 18, + y - 2, + RGB::named(rltk::YELLOW), + RGB::named(rltk::BLACK), + "Remove Curse From Which Item?", + ); + ctx.print_color( + 18, + y + count as i32 + 1, + RGB::named(rltk::YELLOW), + RGB::named(rltk::BLACK), + "ESCAPE to cancel", + ); + + let mut equippable: Vec = Vec::new(); + for (j, (entity, _item, _cursed)) in build_cursed_iterator().enumerate() { + ctx.set( + 17, + y, + RGB::named(rltk::WHITE), + RGB::named(rltk::BLACK), + rltk::to_cp437('('), + ); + ctx.set( + 18, + y, + RGB::named(rltk::YELLOW), + RGB::named(rltk::BLACK), + 97 + j as rltk::FontCharType, + ); + ctx.set( + 19, + y, + RGB::named(rltk::WHITE), + RGB::named(rltk::BLACK), + rltk::to_cp437(')'), + ); + + ctx.print_color( + 21, + y, + get_item_color(&gs.ecs, entity), + RGB::from_f32(0.0, 0.0, 0.0), + &get_item_display_name(&gs.ecs, entity), + ); + equippable.push(entity); + y += 1; + } + + match ctx.key { + None => (ItemMenuResult::NoResponse, None), + Some(key) => match key { + VirtualKeyCode::Escape => (ItemMenuResult::Cancel, None), + _ => { + let selection = rltk::letter_to_option(key); + if selection > -1 && selection < count as i32 { + return ( + ItemMenuResult::Selected, + Some(equippable[selection as usize]), + ); + } + (ItemMenuResult::NoResponse, None) + } + }, + } +}