From d2b02b91c59984016ff59e08376820b0879dfdb3 Mon Sep 17 00:00:00 2001 From: Niko Abeler Date: Mon, 9 Jan 2023 20:14:19 +0100 Subject: [PATCH] WIP buy items --- src/item.rs | 2 +- src/state.rs | 24 +++++++++++++++++ src/ui/mod.rs | 73 ++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 88 insertions(+), 11 deletions(-) diff --git a/src/item.rs b/src/item.rs index dc77922..734d591 100644 --- a/src/item.rs +++ b/src/item.rs @@ -9,7 +9,7 @@ pub enum ItemType { } -#[derive(Clone)] +#[derive(Clone, PartialEq, Eq)] pub enum ItemOwner { Held(EntityId), Dropped(Location), diff --git a/src/state.rs b/src/state.rs index 647afc3..c5119ed 100644 --- a/src/state.rs +++ b/src/state.rs @@ -96,6 +96,15 @@ impl GameState { self.creatures.get(&id) } + pub fn get_inventory(&self, id: EntityId) -> Vec { + self.items.iter().filter(|(_, item)| { + item.owner == ItemOwner::Held(id) + }).map(|(id, _)| *id).collect::>() + } + + pub fn get_item(&self, id: EntityId) -> Option<&Item> { + self.items.get(&id) + } /** * Function to populate the world at genesis @@ -212,6 +221,8 @@ impl GameState { #[cfg(test)] mod tests { + use crate::item::ItemGenerator; + use super::*; #[test] @@ -234,4 +245,17 @@ mod tests { assert_eq!(state.tavern.is_some(), true); } + #[test] + fn test_claim_and_inventory() { + let mut state = GameState::new(World::new(2)); + let creature = CreatureGenerator::create_human(state.time, Location { x: 0, y: 0 }); + let creature_id = state.add_person(creature); + let item = ItemGenerator::generate_item(); + let item_id = state.add_item(item); + state.claim_item(creature_id, item_id); + let inventory = state.get_inventory(creature_id); + assert_eq!(inventory.len(), 1); + assert_eq!(inventory[0], item_id); + } + } \ No newline at end of file diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 3ec3ded..6d7eaf8 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -21,6 +21,7 @@ enum AppStatus { Initial, GuestSelection, TalkToGuest(Option), + BuyFromGuest(EntityId), Debug, } @@ -104,7 +105,11 @@ impl<'a> App<'a> { // determine guests self.guest_list = self.game.state.guests().iter().map(|creature_id| { let creature = self.game.state.get_creature(*creature_id).unwrap(); - tui::widgets::ListItem::new(creature.name.clone()) + tui::widgets::ListItem::new(format!( + "{} ({}, {} items)", + creature.name, + creature.profession, self.game.state.get_inventory(creature.entity.id).len() + )) }).collect(); self.guest_list_state = tui::widgets::ListState::default(); self.guest_list_state.select(Some(0)); @@ -167,6 +172,9 @@ impl<'a> App<'a> { KeyCode::Char('a') => { self.ask_business(*guest); }, + KeyCode::Char('b') => { + self.status = AppStatus::BuyFromGuest(guest.unwrap()); + }, KeyCode::Up => { self.conversation_scroll += 1; }, @@ -182,6 +190,20 @@ impl<'a> App<'a> { } true }, + AppStatus::BuyFromGuest(guest_id) => { + match read() { + Ok(Event::Key(event)) => { + match event.code { + KeyCode::Esc => { + self.status = AppStatus::TalkToGuest(Some(*guest_id)); + }, + _ => {} + } + }, + _ => {} + } + true + }, AppStatus::Debug => { match read() { Ok(Event::Key(event)) => { @@ -308,9 +330,12 @@ impl<'a> App<'a> { AppStatus::TalkToGuest(guest_id) => { self.draw_talk_to_guest(f, *guest_id); }, + AppStatus::BuyFromGuest(guest_id) => { + self.draw_buy_from_guest(f, *guest_id); + }, AppStatus::Debug => { self.draw_debug(f); - } + }, } } @@ -373,24 +398,22 @@ impl<'a> App<'a> { let mut full_text = self.conversation.to_spans(); full_text.push(tui::text::Spans::from(vec![ tui::text::Span::styled("(a) ", key_style), - tui::text::Span::raw("What's your business?\n\n"), + tui::text::Span::raw("What's your business?"), ])); full_text.push(tui::text::Spans::from(vec![ tui::text::Span::styled("(b) ", key_style), - tui::text::Span::raw("Let's trade?\n\n"), + tui::text::Span::raw("What do you have to sell?"), ])); full_text.push(tui::text::Spans::from(vec![ tui::text::Span::styled("(c) ", key_style), - tui::text::Span::raw("Heard of anything interesting?\n\n"), + tui::text::Span::raw("Heard of anything interesting?"), ])); let text = tui::text::Text::from(full_text); let scroll: u16 = ((text.lines.len() as u16) ) - .checked_sub( - (chunks.main.height as u16).checked_sub(2).unwrap_or(0) // 2 lines for the border + .saturating_sub( + (chunks.main.height as u16).saturating_sub(2) // 2 lines for the border ) - .unwrap_or(0) - .checked_sub(self.conversation_scroll) - .unwrap_or(0); + .saturating_sub(self.conversation_scroll); let main_window = tui::widgets::Paragraph::new(text) .block(tui::widgets::Block::default().title(guest.name.clone()).borders(tui::widgets::Borders::ALL)) @@ -408,6 +431,34 @@ impl<'a> App<'a> { f.render_widget(controls, chunks.controls); } + fn draw_buy_from_guest(&self, f: &mut Frame, guest_id: EntityId) { + let chunks = DefaultLayout::default(f.size()); + + let guest = self.game.state.get_creature(guest_id).unwrap(); + + let inventory = self.game.state.get_inventory(guest_id); + let mut list_items = vec![]; + for item in inventory { + let item = self.game.state.get_item(item).unwrap(); + list_items.push(tui::widgets::ListItem::new(format!("{} ({} gold)", item.name, item.value()))); + } + + let main_window = tui::widgets::List::new(list_items) + .block(tui::widgets::Block::default().title(guest.name.clone()).borders(tui::widgets::Borders::ALL)) + .style(tui::style::Style::default().fg(tui::style::Color::White)) + .highlight_style(tui::style::Style::default().add_modifier(tui::style::Modifier::ITALIC)) + .highlight_symbol(">>"); + + let mut binding = Controls::new(); + let controls = binding + .add("a-z".to_owned(), "Select".to_owned()) + .add("Esc".to_owned(), "Back".to_owned()) + .render(); + + self.draw_status(f, chunks.status); + f.render_widget(main_window, chunks.main); + f.render_widget(controls, chunks.controls); } + fn draw_debug(&mut self, f: &mut Frame) { let chunks = DefaultLayout::default(f.size()); @@ -472,6 +523,7 @@ impl<'a> App<'a> { f.render_widget(controls, chunks.controls); } + /** * Conversation */ @@ -509,4 +561,5 @@ impl<'a> App<'a> { )); self.conversation_scroll = 0; } + } \ No newline at end of file