people traveling
This commit is contained in:
parent
6318767e32
commit
aa32eaec30
17
src/app.rs
17
src/app.rs
|
@ -1,9 +1,7 @@
|
|||
use std::{rc::Rc};
|
||||
|
||||
use crossterm::event::{read, Event, KeyCode};
|
||||
use tui::{Terminal, backend::Backend, Frame, layout::Layout};
|
||||
use tui::{backend::Backend, Frame, layout::Layout};
|
||||
|
||||
use crate::{state::{self, GameState}, person::Person};
|
||||
use crate::{state::GameState, person::Person, entity::Entity};
|
||||
|
||||
/**
|
||||
* |........................|
|
||||
|
@ -17,7 +15,7 @@ use crate::{state::{self, GameState}, person::Person};
|
|||
enum AppStatus {
|
||||
Initial,
|
||||
GuestSelection,
|
||||
TalkToGuest(Rc<Person>),
|
||||
TalkToGuest(Option<u32>),
|
||||
}
|
||||
|
||||
pub struct App<'a> {
|
||||
|
@ -68,7 +66,7 @@ impl<'a> App<'a> {
|
|||
KeyCode::Enter => {
|
||||
let selected = self.guest_state.selected().unwrap();
|
||||
let guest = &self.state.guests()[selected];
|
||||
self.status = AppStatus::TalkToGuest(guest.clone());
|
||||
self.status = AppStatus::TalkToGuest(guest.id());
|
||||
},
|
||||
KeyCode::Char('.') => {
|
||||
self.state.step();
|
||||
|
@ -109,8 +107,8 @@ impl<'a> App<'a> {
|
|||
AppStatus::GuestSelection => {
|
||||
self.draw_guest_selection(f);
|
||||
},
|
||||
AppStatus::TalkToGuest(guest) => {
|
||||
self.draw_talk_to_guest(f, guest.clone());
|
||||
AppStatus::TalkToGuest(guest_id) => {
|
||||
self.draw_talk_to_guest(f, *guest_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,7 +159,8 @@ impl<'a> App<'a> {
|
|||
f.render_widget(controls, chunks[1]);
|
||||
}
|
||||
|
||||
fn draw_talk_to_guest<B: Backend>(&self, f: &mut Frame<B>, guest: Rc<Person>) {
|
||||
fn draw_talk_to_guest<B: Backend>(&self, f: &mut Frame<B>, guest: Option<u32>) {
|
||||
let guest = self.state.get_person(guest.unwrap()).unwrap();
|
||||
let main_window = tui::widgets::Paragraph::new(guest.name.clone())
|
||||
.block(tui::widgets::Block::default().title("Guests").borders(tui::widgets::Borders::ALL))
|
||||
.style(tui::style::Style::default().fg(tui::style::Color::White));
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
pub trait Entity {
|
||||
fn id(&self) -> Option<u32>;
|
||||
fn set_id(&mut self, id: u32);
|
||||
}
|
||||
|
|
@ -4,12 +4,12 @@ use crate::{state::{GameState, Effect}, world::Town, person::Person};
|
|||
|
||||
pub struct PersonGenesis {
|
||||
pub town: Rc<Town>,
|
||||
pub person: Rc<Person>,
|
||||
pub person: Person,
|
||||
}
|
||||
|
||||
impl Effect for PersonGenesis {
|
||||
fn apply(&self, state: &mut GameState) {
|
||||
state.add_person(self.person.clone());
|
||||
state.add_person(&mut self.person.clone());
|
||||
}
|
||||
|
||||
fn description(&self) -> String {
|
||||
|
|
|
@ -5,6 +5,7 @@ mod time;
|
|||
mod generators;
|
||||
mod person;
|
||||
mod app;
|
||||
mod entity;
|
||||
|
||||
use app::App;
|
||||
use noise::{Perlin, ScalePoint, Add, NoiseFn, ScaleBias};
|
||||
|
@ -132,10 +133,10 @@ fn main() -> Result<(), io::Error> {
|
|||
|
||||
let mut rng = rand::thread_rng();
|
||||
for _ in 0..N_TOWNS {
|
||||
|
||||
world_state.step_n(rng.gen_range(0..360*3));
|
||||
world_state.found_town();
|
||||
}
|
||||
world_state.build_tavern();
|
||||
|
||||
|
||||
// setup terminal
|
||||
|
|
|
@ -1,21 +1,24 @@
|
|||
use std::{rc::Rc, fmt::Display};
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::prelude::*;
|
||||
|
||||
use crate::{time::Time, world::Town, generators::PersonNameGenerator, state::GameState};
|
||||
use crate::{time::Time, world::Town, generators::PersonNameGenerator, state::GameState, entity::Entity};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Profession {
|
||||
Peasant,
|
||||
Adventurer,
|
||||
Blacksmith,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Agenda {
|
||||
Idle,
|
||||
Idle(u32),
|
||||
Traveling([usize; 2]),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Person {
|
||||
pub id: Option<u32>,
|
||||
pub name: String,
|
||||
pub birth_date: Time,
|
||||
pub birth_location: Rc<Town>,
|
||||
|
@ -27,23 +30,28 @@ pub struct Person {
|
|||
impl Person {
|
||||
pub fn new(birth_date: Time, birth_location: Rc<Town>, location: [usize; 2]) -> Person {
|
||||
Person {
|
||||
id: None,
|
||||
name: PersonNameGenerator::name(),
|
||||
birth_date: birth_date,
|
||||
birth_location: birth_location,
|
||||
profession: Profession::Peasant,
|
||||
location: [0, 0],
|
||||
agenda: Agenda::Idle,
|
||||
location: location,
|
||||
agenda: Agenda::Idle(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn step(&mut self, state: &mut GameState) {
|
||||
match &self.agenda {
|
||||
Agenda::Idle => {
|
||||
Agenda::Idle(days) => {
|
||||
// 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());
|
||||
if *days <= 0 {
|
||||
// pick random destination
|
||||
let mut rng = rand::thread_rng();
|
||||
let dest = state.world.structures.keys().choose(&mut rng);
|
||||
self.agenda = Agenda::Traveling(*dest.unwrap());
|
||||
} else {
|
||||
self.agenda = Agenda::Idle(days - 1);
|
||||
}
|
||||
},
|
||||
Agenda::Traveling(destination) => {
|
||||
// TDOO: A* pathfinding with terrain costs
|
||||
|
@ -59,7 +67,7 @@ impl Person {
|
|||
self.location[1] -= 1;
|
||||
}
|
||||
if self.location == *destination {
|
||||
self.agenda = Agenda::Idle;
|
||||
self.agenda = Agenda::Idle(10);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -70,4 +78,50 @@ impl Display for Person {
|
|||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl Entity for Person {
|
||||
fn id(&self) -> Option<u32> {
|
||||
self.id
|
||||
}
|
||||
|
||||
fn set_id(&mut self, id: u32) {
|
||||
self.id = Some(id);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use crate::world::{World, Structure};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_person_creation() {
|
||||
let person = Person::new(Time { time: 0 }, Rc::new(Town::new()), [0, 0]);
|
||||
assert_ne!(person.name, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_traveling() {
|
||||
let mut person = Person::new(Time { time: 0 }, Rc::new(Town::new()), [0, 0]);
|
||||
person.agenda = Agenda::Traveling([10, 0]);
|
||||
person.step(&mut GameState::new(World::new(32)));
|
||||
assert_eq!(person.location, [1, 0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_start_traveling() {
|
||||
let mut person = Person::new(Time { time: 0 }, Rc::new(Town::new()), [0, 0]);
|
||||
person.agenda = Agenda::Idle(0);
|
||||
let mut state = GameState::new(World::new(32));
|
||||
state.world.add_structure(10, 10, Structure::Town(Rc::new(Town::new())));
|
||||
person.step(&mut state);
|
||||
match &person.agenda {
|
||||
Agenda::Traveling(_) => {},
|
||||
_ => { panic!("Person should be traveling") },
|
||||
|
||||
}
|
||||
}
|
||||
}
|
86
src/state.rs
86
src/state.rs
|
@ -1,15 +1,17 @@
|
|||
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
use rand::prelude::*;
|
||||
use crate::entity::Entity;
|
||||
use crate::person::Person;
|
||||
use crate::time::Time;
|
||||
use crate::world::{World, Terrain, Town, Tavern, Structure};
|
||||
use crate::world::{World, Terrain, Town, Tavern};
|
||||
use crate::events::{FoundTown, WorldGenesis, PersonGenesis, TavernBuilt};
|
||||
pub struct GameState {
|
||||
pub time: Time,
|
||||
pub world: World,
|
||||
pub people: Vec<Rc<Person>>,
|
||||
pub people: HashMap<u32, Person>,
|
||||
pub events: Vec<Box<Event>>,
|
||||
pub tavern: Option<Rc<Tavern>>,
|
||||
}
|
||||
|
@ -41,7 +43,7 @@ impl GameState {
|
|||
|
||||
GameState {
|
||||
time: Time { time: 0 },
|
||||
people: Vec::new(),
|
||||
people: HashMap::new(),
|
||||
world: world,
|
||||
events: events,
|
||||
tavern: None,
|
||||
|
@ -58,9 +60,12 @@ impl GameState {
|
|||
}
|
||||
|
||||
pub fn step(&mut self) {
|
||||
// for p in self.people.iter() {
|
||||
// p.step(self);
|
||||
// }
|
||||
let ids: Vec<u32> = self.people.keys().map(|id| *id).collect();
|
||||
for id in ids {
|
||||
let mut person = self.people.get_mut(&id).unwrap().clone();
|
||||
person.step(self);
|
||||
self.people.insert(id, person);
|
||||
}
|
||||
self.time.time += 1;
|
||||
}
|
||||
|
||||
|
@ -81,17 +86,20 @@ impl GameState {
|
|||
* Getters
|
||||
*/
|
||||
|
||||
pub fn guests(&self) -> Vec<Rc<Person>> {
|
||||
pub fn guests(&self) -> Vec<Person> {
|
||||
match &self.tavern {
|
||||
Some(tavern) => {
|
||||
let loc = self.world.get_tavern_location(tavern);
|
||||
self.people.iter().filter(|person| {
|
||||
self.people.values().filter(|person| {
|
||||
person.location == loc
|
||||
}).map(|p| p.clone()).collect()
|
||||
},
|
||||
None => Vec::new(),
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_person(&self, id: u32) -> Option<&Person> {
|
||||
self.people.get(&id)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -124,11 +132,11 @@ impl GameState {
|
|||
time: self.time,
|
||||
effect: Box::new(PersonGenesis {
|
||||
town: town.clone(),
|
||||
person: Rc::new(Person::new(
|
||||
person: Person::new(
|
||||
self.time.substract_years(rng.gen_range(18..30)),
|
||||
town.clone(),
|
||||
self.world.get_town_location(&town)
|
||||
)),
|
||||
),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
@ -137,8 +145,10 @@ impl GameState {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn add_person(&mut self, person: Rc<Person>) {
|
||||
self.people.push(person);
|
||||
pub fn add_person(&mut self, person: &mut Person) {
|
||||
let id = (self.people.len() + 1) as u32; // avoid 0 id
|
||||
person.set_id(id);
|
||||
self.people.insert(id, person.clone());
|
||||
}
|
||||
|
||||
pub fn build_tavern(&mut self) {
|
||||
|
@ -158,7 +168,55 @@ impl GameState {
|
|||
tavern: Rc::new(Tavern::new()),
|
||||
})
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_step() {
|
||||
let mut state = GameState::new(World::new(100));
|
||||
state.step();
|
||||
assert_eq!(state.time.time, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_step_n() {
|
||||
let mut state = GameState::new(World::new(100));
|
||||
state.step_n(10);
|
||||
assert_eq!(state.time.time, 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_step_year() {
|
||||
let mut state = GameState::new(World::new(100));
|
||||
state.step_year();
|
||||
assert_eq!(state.time.time, 360);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_found_town() {
|
||||
let mut state = GameState::new(World::new(1));
|
||||
state.world.map[0][0].terrain = Terrain::Flats;
|
||||
state.time = Time { time: 1e6 as u32 };
|
||||
state.found_town();
|
||||
assert_ne!(state.people.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_tavern() {
|
||||
let mut state = GameState::new(World::new(2));
|
||||
state.world.map[0][0].terrain = Terrain::Flats;
|
||||
state.world.map[0][1].terrain = Terrain::Flats;
|
||||
state.world.map[1][0].terrain = Terrain::Hills;
|
||||
state.world.map[1][1].terrain = Terrain::Hills;
|
||||
state.build_tavern();
|
||||
assert_eq!(state.tavern.is_some(), true);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue