diff --git a/src/main.rs b/src/main.rs index 8d5c183..7f16e88 100644 --- a/src/main.rs +++ b/src/main.rs @@ -47,6 +47,7 @@ const MSG_X: i32 = BAR_WIDTH + 2; const MSG_WIDTH: i32 = SCREEN_WIDTH - BAR_WIDTH - 2; const MSG_HEIGHT: usize = PANEL_HEIGHT as usize - 1; const MAX_ROOM_ITEMS: i32 = 2; +const INVENTORY_WIDTH: i32 = 50; struct Tcod { root: Root, @@ -68,6 +69,7 @@ struct Object { alive: bool, fighter: Option, ai: Option, + item: Option, } impl Object { @@ -82,6 +84,7 @@ impl Object { alive: false, fighter: None, ai: None, + item: None, } } @@ -316,6 +319,27 @@ fn handle_keys(tcod: &mut Tcod, game: &mut Game, objects: &mut Vec) -> P TookTurn } + (Key { code: Text, .. }, "g", true) => { + // Pick up an item + let item_id = objects + .iter() + .position(|object| object.pos() == objects[PLAYER].pos() && object.item.is_some()); + if let Some(item_id) = item_id { + pick_item_up(item_id, game, objects); + } + DidntTakeTurn + } + + (Key { code: Text, .. }, "i", true) => { + // Show the inventory + inventory_menu( + &game.inventory, + "Press the key next to an item to use it, or any other to cancel.\n", + &mut tcod.root, + ); + TookTurn + } + _ => DidntTakeTurn, } } @@ -438,6 +462,7 @@ fn place_objects(room: Rect, map: &Map, objects: &mut Vec) { if !is_blocked(x, y, map, objects) { // Create a healing potion let mut object = Object::new(x, y, '!', "healing potion", VIOLET, false); + object.item = Some(Item::Heal); objects.push(object); } } @@ -546,6 +571,85 @@ fn monster_death(monster: &mut Object, game: &mut Game) { monster.name = format!("remains of {}", monster.name); } +fn menu>(header: &str, options: &[T], width: i32, root: &mut Root) -> Option { + // Limit the amount of menu options + assert!( + options.len() <= 26, + "Cannot have a menu with more than 26 options." + ); + + // Calculate total height for the header (after auto-wrap) and one line per option + let header_height = root.get_height_rect(0, 0, width, SCREEN_HEIGHT, header); + let height = options.len() as i32 + header_height; + + // Create an off-screen console that represents the menu's window + let mut window = Offscreen::new(width, height); + + // Print the header, with auto-wrap + window.set_default_foreground(WHITE); + window.print_rect_ex( + 0, + 0, + width, + height, + BackgroundFlag::None, + TextAlignment::Left, + header, + ); + + // Print all the options + for (index, option_text) in options.iter().enumerate() { + let menu_letter = (b'a' + index as u8) as char; + let text = format!("({}) {}", menu_letter, option_text.as_ref()); + window.print_ex( + 0, + header_height + index as i32, + BackgroundFlag::None, + TextAlignment::Left, + text, + ); + } + + // Blit the contents of the menu window to the root console + let x = SCREEN_WIDTH / 2 - width / 2; + let y = SCREEN_HEIGHT / 2 - height / 2; + blit(&window, (0, 0), (width, height), root, (x, y), 1.0, 0.7); + + // Present the root console to the player and wait for a key-press + root.flush(); + let key = root.wait_for_keypress(true); + + // Convert the ASCII code to an index; if it corresponds to an option, return it + if key.printable.is_alphabetic() { + let index = key.printable.to_ascii_lowercase() as usize - 'a' as usize; + if index < options.len() { + Some(index) + } else { + None + } + } else { + None + } +} + +fn inventory_menu(inventory: &[Object], header: &str, root: &mut Root) -> Option { + // Show a menu with each inventory item as an option + let options = if inventory.len() == 0 { + vec!["Inventory is empty.".into()] + } else { + inventory.iter().map(|item| item.name.clone()).collect() + }; + + let inventory_index = menu(header, &options, INVENTORY_WIDTH, root); + + // If an item was chosen, return it + if inventory.len() > 0 { + inventory_index + } else { + None + } +} + fn render_bar( panel: &mut Offscreen, x: i32,