diff --git a/src/ui/debug_view.rs b/src/ui/debug_view.rs new file mode 100644 index 0000000..60fc60e --- /dev/null +++ b/src/ui/debug_view.rs @@ -0,0 +1,201 @@ +use crossterm::event::{read, Event, KeyCode}; +use tui::{backend::Backend, Frame}; + +use crate::{world::Terrain, game::Game}; + +use super::{controls::Controls, DefaultLayout, status_line::StatusLine, AppStatus}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +enum DebugTab { + Creatures, + Items, + Map, +} +pub struct DebugView<'a> { + list: Vec>, + list_state: tui::widgets::ListState, + + item_list: Vec>, + item_list_state: tui::widgets::ListState, + + selected: DebugTab, + map_scroll: (u16, u16), +} + +impl<'a> DebugView<'a> { + pub fn new(game: &Game) -> DebugView<'a> { + let mut list = vec![]; + let mut item_list = vec![]; + for (id, creature) in game.state.creatures.iter() { + list.push(tui::widgets::ListItem::new(format!("{}: {} ({}) at {}", + id.1, creature.name, + creature.profession, + creature.loc, + ))); + } + for (id, item) in game.state.items.iter() { + item_list.push(tui::widgets::ListItem::new(format!("{}: {} ({}) at {}", + id.1, item.name, + item.item_type, + item.owner, + ))); + } + + let mut view = DebugView { + list, + list_state: tui::widgets::ListState::default(), + item_list, + item_list_state: tui::widgets::ListState::default(), + selected: DebugTab::Creatures, + map_scroll: (0, 0), + }; + + view.list_state.select(Some(0)); + view.item_list_state.select(Some(0)); + + view + } + + pub fn control(&mut self) -> (bool, Option) { + match read() { + Ok(Event::Key(event)) => { + match event.code { + KeyCode::Esc => { + return (true, Some(AppStatus::GuestSelection)) + }, + KeyCode::Up => { + if self.selected == DebugTab::Map { + let (x, y) = self.map_scroll; + self.map_scroll = (x.saturating_sub(4), y); + } else { + let ref mut state = match self.selected { + DebugTab::Creatures => &mut self.list_state, + DebugTab::Items => &mut self.item_list_state, + _ => &mut self.list_state, + }; + let selected = state.selected().unwrap(); + if selected > 0 { + state.select(Some(selected - 1)); + } + } + }, + KeyCode::Down => { + if self.selected == DebugTab::Map { + let (x, y) = self.map_scroll; + self.map_scroll = (x + 4, y); + } else { + let mut max = 0; + let ref mut state = match self.selected { + DebugTab::Creatures => { + max = self.list.len() - 1; + &mut self.list_state + }, + DebugTab::Items => { + max = self.item_list.len() - 1; + &mut self.item_list_state + }, + _ => { + max = self.list.len() - 1; + &mut self.list_state + }, + }; + let selected = state.selected().unwrap(); + if selected < max { + state.select(Some(selected + 1)); + } + } + }, + KeyCode::Left => { + if self.selected == DebugTab::Map { + let (x, y) = self.map_scroll; + self.map_scroll = (x, y.saturating_sub(4)); + } + }, + KeyCode::Right => { + if self.selected == DebugTab::Map { + let (x, y) = self.map_scroll; + self.map_scroll = (x, y + 4); + } + }, + KeyCode::Char('i') => { + self.selected = DebugTab::Items; + }, + KeyCode::Char('c') => { + self.selected = DebugTab::Creatures; + }, + KeyCode::Char('m') => { + self.selected = DebugTab::Map; + }, + _ => {} + } + }, + _ => {} + } + return (true, None) + } + + pub fn draw(&mut self, f: &mut Frame, game: &Game) { + let chunks = DefaultLayout::default(f.size()); + + let data = self; + + match data.selected { + DebugTab::Creatures => { + let main_window = tui::widgets::List::new(data.list.clone()) + .block(tui::widgets::Block::default().title("Guests").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(">>"); + f.render_stateful_widget(main_window, chunks.main, &mut data.list_state); + }, + DebugTab::Items => { + let main_window = tui::widgets::List::new(data.item_list.clone()) + .block(tui::widgets::Block::default().title("Items").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(">>"); + f.render_stateful_widget(main_window, chunks.main, &mut data.item_list_state); + }, + DebugTab::Map => { + let mut rows = Vec::new(); + for row in game.state.world.map.iter() { + rows.push(tui::text::Spans::from( + row.iter().map(|tile| { + match tile.terrain { + Terrain::Void => tui::text::Span::styled(" ", tui::style::Style::default().bg(tui::style::Color::Rgb(0, 0, 0)).fg(tui::style::Color::Rgb(0, 0, 0))), + Terrain::DeepOcean => tui::text::Span::styled("~", tui::style::Style::default().bg(tui::style::Color::Rgb(0, 0, 128)).fg(tui::style::Color::Rgb(32, 32, 128))), + Terrain::Ocean => tui::text::Span::styled("~", tui::style::Style::default().bg(tui::style::Color::Rgb(32, 32, 128)).fg(tui::style::Color::Rgb(128, 128, 128))), + Terrain::Beach => tui::text::Span::styled("-", tui::style::Style::default().bg(tui::style::Color::Rgb(200, 200, 10)).fg(tui::style::Color::Rgb(100, 100, 10))), + Terrain::Flats => tui::text::Span::styled("-", tui::style::Style::default().bg(tui::style::Color::Rgb(30, 150, 30)).fg(tui::style::Color::Rgb(30, 200, 30))), + Terrain::Hills => tui::text::Span::styled("~", tui::style::Style::default().bg(tui::style::Color::Rgb(120, 150, 30)).fg(tui::style::Color::Rgb(120, 150, 30))), + Terrain::Mountains => tui::text::Span::styled("A", tui::style::Style::default().bg(tui::style::Color::Rgb(120,120,120)).fg(tui::style::Color::Rgb(120,120,120))), + Terrain::HighMountains => tui::text::Span::styled("A", tui::style::Style::default().bg(tui::style::Color::Rgb(200,200,200)).fg(tui::style::Color::Rgb(200,200,200))), + } + }).collect::>() + )); + } + + let main_window = tui::widgets::Paragraph::new( + tui::text::Text::from(rows) + ) + .block(tui::widgets::Block::default().title("Map").borders(tui::widgets::Borders::ALL)) + .style(tui::style::Style::default().fg(tui::style::Color::White)) + .scroll(data.map_scroll); + + f.render_widget(main_window, chunks.main); + }, + } + + StatusLine::draw(f, chunks.status, game); + let mut binding = Controls::new(); + let controls = binding + .add("↑↓".to_owned(), "select guest".to_owned()) + .add("c".to_owned(), "Creatures".to_owned()) + .add("i".to_owned(), "Items".to_owned()) + .add("m".to_owned(), "Map".to_owned()) + .add("Esc".to_owned(), "back".to_owned()) + .render(); + f.render_widget(controls, chunks.controls); + } + +} \ No newline at end of file diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 6d7eaf8..93a2103 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -1,12 +1,14 @@ mod chat; mod controls; +mod debug_view; +mod status_line; use crossterm::event::{read, Event, KeyCode}; use tui::{backend::Backend, Frame, layout::Layout}; use crate::{game::Game, entity::{EntityId, EntityType}, world::Terrain}; -use self::{chat::{Chat, ChatLine}, controls::Controls}; +use self::{chat::{Chat, ChatLine}, controls::Controls, debug_view::DebugView}; /** * |........................| @@ -17,7 +19,7 @@ use self::{chat::{Chat, ChatLine}, controls::Controls}; * |........................| */ -enum AppStatus { +pub enum AppStatus { Initial, GuestSelection, TalkToGuest(Option), @@ -25,24 +27,6 @@ enum AppStatus { Debug, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -enum DebugView { - Creatures, - Items, - Map, -} - -struct DebugData<'a> { - list: Vec>, - list_state: tui::widgets::ListState, - - item_list: Vec>, - item_list_state: tui::widgets::ListState, - - selected: DebugView, - map_scroll: (u16, u16), -} - pub struct App<'a> { game: Game, status: AppStatus, @@ -56,7 +40,7 @@ pub struct App<'a> { conversation_scroll: u16, // Debug - debug_data: Option>, + debug_view: Option>, } pub struct DefaultLayout { @@ -95,7 +79,7 @@ impl<'a> App<'a> { status: AppStatus::Initial, conversation: Chat::new(), conversation_scroll: 0, - debug_data: None, + debug_view: None, } } @@ -205,113 +189,18 @@ impl<'a> App<'a> { true }, AppStatus::Debug => { - match read() { - Ok(Event::Key(event)) => { - match event.code { - KeyCode::Esc => { - self.status = AppStatus::GuestSelection; - }, - KeyCode::Up => { - if self.debug_data.as_ref().unwrap().selected == DebugView::Map { - let (x, y) = self.debug_data.as_ref().unwrap().map_scroll; - self.debug_data.as_mut().unwrap().map_scroll = (x.saturating_sub(1), y); - } else { - let ref mut state = match self.debug_data.as_ref().unwrap().selected { - DebugView::Creatures => &mut self.debug_data.as_mut().unwrap().list_state, - DebugView::Items => &mut self.debug_data.as_mut().unwrap().item_list_state, - _ => &mut self.debug_data.as_mut().unwrap().list_state, - }; - let selected = state.selected().unwrap(); - if selected > 0 { - state.select(Some(selected - 1)); - } - } - }, - KeyCode::Down => { - if self.debug_data.as_ref().unwrap().selected == DebugView::Map { - let (x, y) = self.debug_data.as_ref().unwrap().map_scroll; - self.debug_data.as_mut().unwrap().map_scroll = (x + 1, y); - } else { - let mut max = 0; - let ref mut state = match self.debug_data.as_ref().unwrap().selected { - DebugView::Creatures => { - max = self.debug_data.as_ref().unwrap().list.len() - 1; - &mut self.debug_data.as_mut().unwrap().list_state - }, - DebugView::Items => { - max = self.debug_data.as_ref().unwrap().item_list.len() - 1; - &mut self.debug_data.as_mut().unwrap().item_list_state - }, - _ => { - max = self.debug_data.as_ref().unwrap().list.len() - 1; - &mut self.debug_data.as_mut().unwrap().list_state - }, - }; - let selected = state.selected().unwrap(); - if selected < max { - state.select(Some(selected + 1)); - } - } - }, - KeyCode::Left => { - if self.debug_data.as_ref().unwrap().selected == DebugView::Map { - let (x, y) = self.debug_data.as_ref().unwrap().map_scroll; - self.debug_data.as_mut().unwrap().map_scroll = (x, y.saturating_sub(1)); - } - }, - KeyCode::Right => { - if self.debug_data.as_ref().unwrap().selected == DebugView::Map { - let (x, y) = self.debug_data.as_ref().unwrap().map_scroll; - self.debug_data.as_mut().unwrap().map_scroll = (x, y + 1); - } - }, - KeyCode::Char('i') => { - self.debug_data.as_mut().unwrap().selected = DebugView::Items; - }, - KeyCode::Char('c') => { - self.debug_data.as_mut().unwrap().selected = DebugView::Creatures; - }, - KeyCode::Char('m') => { - self.debug_data.as_mut().unwrap().selected = DebugView::Map; - }, - _ => {} - } - }, - _ => {} + let ret = self.debug_view.as_mut().unwrap().control(); + if let Some(next) = ret.1 { + self.debug_view = None; + self.status = next; } - true + ret.0 } } } fn open_debug(&mut self) { - let mut list = vec![]; - let mut item_list = vec![]; - for (id, creature) in self.game.state.creatures.iter() { - list.push(tui::widgets::ListItem::new(format!("{}: {} ({}) at {}", - id.1, creature.name, - creature.profession, - creature.loc, - ))); - } - for (id, item) in self.game.state.items.iter() { - item_list.push(tui::widgets::ListItem::new(format!("{}: {} ({}) at {}", - id.1, item.name, - item.item_type, - item.owner, - ))); - } - - self.debug_data = Some(DebugData { - list, - list_state: tui::widgets::ListState::default(), - item_list, - item_list_state: tui::widgets::ListState::default(), - selected: DebugView::Creatures, - map_scroll: (0, 0), - }); - self.debug_data.as_mut().unwrap().list_state.select(Some(0)); - self.debug_data.as_mut().unwrap().item_list_state.select(Some(0)); + self.debug_view = Some(DebugView::new(&self.game)); self.status = AppStatus::Debug; } @@ -334,7 +223,7 @@ impl<'a> App<'a> { self.draw_buy_from_guest(f, *guest_id); }, AppStatus::Debug => { - self.draw_debug(f); + self.debug_view.as_mut().unwrap().draw(f, &self.game); }, } @@ -457,73 +346,9 @@ impl<'a> App<'a> { 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()); - - let data = self.debug_data.as_mut().unwrap(); - - match data.selected { - DebugView::Creatures => { - let main_window = tui::widgets::List::new(data.list.clone()) - .block(tui::widgets::Block::default().title("Guests").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(">>"); - f.render_stateful_widget(main_window, chunks.main, &mut data.list_state); - }, - DebugView::Items => { - let main_window = tui::widgets::List::new(data.item_list.clone()) - .block(tui::widgets::Block::default().title("Items").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(">>"); - f.render_stateful_widget(main_window, chunks.main, &mut data.item_list_state); - }, - DebugView::Map => { - let mut rows = Vec::new(); - for row in self.game.state.world.map.iter() { - rows.push(tui::text::Spans::from( - row.iter().map(|tile| { - match tile.terrain { - Terrain::Void => tui::text::Span::styled(" ", tui::style::Style::default().bg(tui::style::Color::Rgb(0, 0, 0)).fg(tui::style::Color::Rgb(0, 0, 0))), - Terrain::DeepOcean => tui::text::Span::styled("~", tui::style::Style::default().bg(tui::style::Color::Rgb(0, 0, 128)).fg(tui::style::Color::Rgb(32, 32, 128))), - Terrain::Ocean => tui::text::Span::styled("~", tui::style::Style::default().bg(tui::style::Color::Rgb(32, 32, 128)).fg(tui::style::Color::Rgb(128, 128, 128))), - Terrain::Beach => tui::text::Span::styled("-", tui::style::Style::default().bg(tui::style::Color::Rgb(200, 200, 10)).fg(tui::style::Color::Rgb(100, 100, 10))), - Terrain::Flats => tui::text::Span::styled("-", tui::style::Style::default().bg(tui::style::Color::Rgb(30, 150, 30)).fg(tui::style::Color::Rgb(30, 200, 30))), - Terrain::Hills => tui::text::Span::styled("~", tui::style::Style::default().bg(tui::style::Color::Rgb(120, 150, 30)).fg(tui::style::Color::Rgb(120, 150, 30))), - Terrain::Mountains => tui::text::Span::styled("A", tui::style::Style::default().bg(tui::style::Color::Rgb(120,120,120)).fg(tui::style::Color::Rgb(120,120,120))), - Terrain::HighMountains => tui::text::Span::styled("A", tui::style::Style::default().bg(tui::style::Color::Rgb(200,200,200)).fg(tui::style::Color::Rgb(200,200,200))), - } - }).collect::>() - )); - } - - let main_window = tui::widgets::Paragraph::new( - tui::text::Text::from(rows) - ) - .block(tui::widgets::Block::default().title("Map").borders(tui::widgets::Borders::ALL)) - .style(tui::style::Style::default().fg(tui::style::Color::White)) - .scroll(data.map_scroll); - - f.render_widget(main_window, chunks.main); - }, - } - - self.draw_status(f, chunks.status); - let mut binding = Controls::new(); - let controls = binding - .add("↑↓".to_owned(), "select guest".to_owned()) - .add("c".to_owned(), "Creatures".to_owned()) - .add("i".to_owned(), "Items".to_owned()) - .add("m".to_owned(), "Map".to_owned()) - .add("Esc".to_owned(), "back".to_owned()) - .render(); - f.render_widget(controls, chunks.controls); + f.render_widget(controls, chunks.controls); } - /** * Conversation */ diff --git a/src/ui/status_line.rs b/src/ui/status_line.rs new file mode 100644 index 0000000..244527e --- /dev/null +++ b/src/ui/status_line.rs @@ -0,0 +1,29 @@ +use tui::{Frame, backend::Backend}; + +use crate::game::Game; + +pub struct StatusLine {} + + +impl StatusLine { + pub fn draw(f: &mut Frame, rect: tui::layout::Rect, game: &Game) { + + let tavern = game.state.world.get_site( + game.state.tavern.unwrap() + ).unwrap(); + + let spans = tui::text::Spans::from(vec![ + tui::text::Span::raw("Date: "), + tui::text::Span::raw(format!("{}", game.state.time)), + tui::text::Span::raw(" "), + tui::text::Span::raw("Funds: "), + tui::text::Span::raw(format!("{} gold coins", tavern.coins)), + ]); + + let status_text = tui::widgets::Paragraph::new(spans) + .block(tui::widgets::Block::default().borders(tui::widgets::Borders::LEFT | tui::widgets::Borders::RIGHT)) + .style(tui::style::Style::default().fg(tui::style::Color::White)); + + f.render_widget(status_text, rect); + } +} \ No newline at end of file