diff --git a/src/app.rs b/src/app.rs index d4933a1..a62b250 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,4 +1,4 @@ -use std::rc::Rc; +use std::{rc::Rc}; use crossterm::event::{read, Event, KeyCode}; use tui::{Terminal, backend::Backend, Frame, layout::Layout}; @@ -41,7 +41,7 @@ impl<'a> App<'a> { match &self.status { AppStatus::Initial => { // determine guests - self.guest_list = self.state.people.iter().map(|guest| { + self.guest_list = self.state.guests().iter().map(|guest| { tui::widgets::ListItem::new(guest.name.clone()) }).collect(); self.guest_state = tui::widgets::ListState::default(); @@ -67,9 +67,13 @@ impl<'a> App<'a> { }, KeyCode::Enter => { let selected = self.guest_state.selected().unwrap(); - let guest = &self.state.people[selected]; + let guest = &self.state.guests()[selected]; self.status = AppStatus::TalkToGuest(guest.clone()); }, + KeyCode::Char('.') => { + self.state.step(); + self.status = AppStatus::Initial; + }, KeyCode::Esc => { return false; }, @@ -143,6 +147,9 @@ impl<'a> App<'a> { tui::text::Span::styled("⏎", key_style), tui::text::Span::raw(" talk to guest"), tui::text::Span::raw(" "), + tui::text::Span::styled(".", key_style), + tui::text::Span::raw(" pass one day"), + tui::text::Span::raw(" "), tui::text::Span::styled("Esc", key_style), tui::text::Span::raw(" quit"), diff --git a/src/events/mod.rs b/src/events/mod.rs index 290e236..3ba0597 100644 --- a/src/events/mod.rs +++ b/src/events/mod.rs @@ -1,7 +1,9 @@ mod found_town; mod world_genesis; mod person_genesis; +mod tavern_built; pub use found_town::FoundTown; pub use world_genesis::WorldGenesis; -pub use person_genesis::PersonGenesis; \ No newline at end of file +pub use person_genesis::PersonGenesis; +pub use tavern_built::TavernBuilt; \ No newline at end of file diff --git a/src/events/tavern_built.rs b/src/events/tavern_built.rs new file mode 100644 index 0000000..e032c0a --- /dev/null +++ b/src/events/tavern_built.rs @@ -0,0 +1,20 @@ +use std::rc::Rc; + +use crate::{state::{GameState, Effect}, world::{Structure, Tavern}}; + +pub struct TavernBuilt { + pub x: usize, + pub y: usize, + pub tavern: Rc, +} + +impl Effect for TavernBuilt { + fn apply(&self, state: &mut GameState) { + state.world.add_structure(self.x, self.y, Structure::Tavern(self.tavern.clone())); + state.set_tavern(self.tavern.clone()); + } + + fn description(&self) -> String { + format!("{} was built", self.tavern.name) + } +} \ No newline at end of file diff --git a/src/person.rs b/src/person.rs index 3a54ad9..efd08e4 100644 --- a/src/person.rs +++ b/src/person.rs @@ -1,6 +1,8 @@ use std::{rc::Rc, fmt::Display}; +use rand::seq::SliceRandom; +use rand::prelude::*; -use crate::{time::Time, world::Town, generators::PersonNameGenerator}; +use crate::{time::Time, world::Town, generators::PersonNameGenerator, state::GameState}; pub enum Profession { Peasant, @@ -8,12 +10,18 @@ pub enum Profession { Blacksmith, } +pub enum Agenda { + Idle, + Traveling([usize; 2]), +} + pub struct Person { pub name: String, pub birth_date: Time, pub birth_location: Rc, pub profession: Profession, pub location: [usize; 2], + pub agenda: Agenda, } impl Person { @@ -24,6 +32,36 @@ impl Person { birth_location: birth_location, profession: Profession::Peasant, location: [0, 0], + agenda: Agenda::Idle, + } + } + + pub fn step(&mut self, state: &mut GameState) { + match &self.agenda { + Agenda::Idle => { + // do nothing + // pick random destination + let mut rng = rand::thread_rng(); + let dest = state.world.structures.keys().choose(&mut rng); + self.agenda = Agenda::Traveling(*dest.unwrap()); + }, + Agenda::Traveling(destination) => { + // TDOO: A* pathfinding with terrain costs + // move towards destination + if self.location[0] < destination[0] { + self.location[0] += 1; + } else if self.location[0] > destination[0] { + self.location[0] -= 1; + } + if self.location[1] < destination[1] { + self.location[1] += 1; + } else if self.location[1] > destination[1] { + self.location[1] -= 1; + } + if self.location == *destination { + self.agenda = Agenda::Idle; + } + }, } } } diff --git a/src/state.rs b/src/state.rs index 5670cdf..d45e986 100644 --- a/src/state.rs +++ b/src/state.rs @@ -4,13 +4,14 @@ use std::rc::Rc; use rand::prelude::*; use crate::person::Person; use crate::time::Time; -use crate::world::{World, Terrain, Town}; -use crate::events::{FoundTown, WorldGenesis, PersonGenesis}; +use crate::world::{World, Terrain, Town, Tavern, Structure}; +use crate::events::{FoundTown, WorldGenesis, PersonGenesis, TavernBuilt}; pub struct GameState { pub time: Time, pub world: World, pub people: Vec>, - pub events: Vec> + pub events: Vec>, + pub tavern: Option>, } pub struct Event { @@ -43,15 +44,23 @@ impl GameState { people: Vec::new(), world: world, events: events, + tavern: None, } } + pub fn set_tavern(&mut self, tavern: Rc) { + self.tavern = Some(tavern); + } + pub fn add_event(&mut self, event: Event) { event.effect.apply(self); self.events.push(Box::new(event)); } pub fn step(&mut self) { + // for p in self.people.iter() { + // p.step(self); + // } self.time.time += 1; } @@ -67,6 +76,28 @@ impl GameState { } } + + /** + * Getters + */ + + pub fn guests(&self) -> Vec> { + match &self.tavern { + Some(tavern) => { + let loc = self.world.get_tavern_location(tavern); + self.people.iter().filter(|person| { + person.location == loc + }).map(|p| p.clone()).collect() + }, + None => Vec::new(), + } + + } + + /** + * Function to populate the world at genesis + */ + pub fn found_town(&mut self) { let mut rng = rand::thread_rng(); loop { @@ -109,4 +140,25 @@ impl GameState { pub fn add_person(&mut self, person: Rc) { self.people.push(person); } + + pub fn build_tavern(&mut self) { + let mut rng = rand::thread_rng(); + loop { + let x = rng.gen_range(0..self.world.size); + let y = rng.gen_range(0..self.world.size); + if + self.world.map[x][y].terrain == Terrain::Flats || + self.world.map[x][y].terrain == Terrain::Hills + { + self.add_event(Event { + time: self.time, + effect: Box::new(TavernBuilt { + x: x, + y: y, + tavern: Rc::new(Tavern::new()), + }) + }); + } + } + } } \ No newline at end of file diff --git a/src/world.rs b/src/world.rs index c1aa5cf..26184c5 100644 --- a/src/world.rs +++ b/src/world.rs @@ -1,4 +1,4 @@ -use std::rc::Rc; +use std::{rc::Rc, collections::HashMap}; use rand::prelude::*; use crate::generators::TownNameGenerator; @@ -19,25 +19,29 @@ pub struct Town { pub name: String, } +pub struct Tavern { + pub name: String, +} + pub enum Structure { Town(Rc), + Tavern(Rc), } pub struct WorldCell { pub terrain: Terrain, - pub structure: Option, } pub struct World { pub map: Vec>, pub size: usize, + pub structures: HashMap<[usize; 2], Structure> } impl WorldCell { pub fn new(terrain: Terrain) -> WorldCell { WorldCell { terrain: terrain, - structure: None, } } } @@ -55,26 +59,43 @@ impl World { World { map: map, size: size, + structures: HashMap::new(), } } pub fn add_structure(&mut self, x: usize, y: usize, structure: Structure) { - self.map[x][y].structure = Some(structure); + self.structures.insert([x, y], structure); } pub fn get_town_location(&self, town: &Rc) -> [usize; 2] { - for x in 0..self.size { - for y in 0..self.size { - if let Some(Structure::Town(t)) = &self.map[x][y].structure { - if Rc::ptr_eq(&t, town) { - return [x, y]; + for (location, structure) in self.structures.iter() { + match structure { + Structure::Town(t) => { + if Rc::ptr_eq(t, town) { + return *location; } } + _ => {} } } panic!("Town not found"); } + + pub fn get_tavern_location(&self, tavern: &Rc) -> [usize; 2] { + for (location, structure) in self.structures.iter() { + match structure { + Structure::Tavern(t) => { + if Rc::ptr_eq(t, tavern) { + return *location; + } + } + _ => {} + } + } + panic!("Tavern not found"); + } + } impl Terrain { @@ -117,3 +138,11 @@ impl Town { } } } + +impl Tavern { + pub fn new() -> Tavern { + Tavern { + name: TownNameGenerator::name(), + } + } +} \ No newline at end of file