use ::rltk::{FontCharType, 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, ObfuscatedName, 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) } }, } } pub fn identify_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option) { use ::rltk::to_cp437; 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 names = gs.ecs.read_storage::(); let dm = gs.ecs.fetch::(); let obfuscated = gs.ecs.read_storage::(); let build_cursed_iterator = || { (&entities, &items).join().filter(|(item_entity, _item)| { 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 obfuscated.get(*item_entity).is_some() && !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 { if let Some(name) = names.get(*item_entity) { if obfuscated.get(*item_entity).is_some() && !dm.identified_items.contains(&name.name) { 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, colors::WHITE, colors::BLACK, ); ctx.print_color( 18, y - 2, colors::YELLOW, colors::BLACK, "Identify Which Item?", ); 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, _item)) in build_cursed_iterator().enumerate() { ctx.set(17, y, colors::WHITE, colors::BLACK, to_cp437('(')); ctx.set(18, y, colors::YELLOW, colors::BLACK, 97 + j as FontCharType); ctx.set(19, y, colors::WHITE, colors::BLACK, 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; } 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) } }, } }