Compare commits
3 Commits
1471751231
...
aa32eaec30
Author | SHA1 | Date |
---|---|---|
Niko Abeler | aa32eaec30 | |
Niko Abeler | 6318767e32 | |
Niko Abeler | 556c6a869f |
60
src/app.rs
60
src/app.rs
|
@ -1,9 +1,7 @@
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use crossterm::event::{read, Event, KeyCode};
|
use crossterm::event::{read, Event, KeyCode};
|
||||||
use tui::{Terminal, backend::Backend, Frame};
|
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 {
|
enum AppStatus {
|
||||||
Initial,
|
Initial,
|
||||||
GuestSelection,
|
GuestSelection,
|
||||||
TalkToGuest(Rc<Person>),
|
TalkToGuest(Option<u32>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct App<'a> {
|
pub struct App<'a> {
|
||||||
|
@ -41,7 +39,7 @@ impl<'a> App<'a> {
|
||||||
match &self.status {
|
match &self.status {
|
||||||
AppStatus::Initial => {
|
AppStatus::Initial => {
|
||||||
// determine guests
|
// 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())
|
tui::widgets::ListItem::new(guest.name.clone())
|
||||||
}).collect();
|
}).collect();
|
||||||
self.guest_state = tui::widgets::ListState::default();
|
self.guest_state = tui::widgets::ListState::default();
|
||||||
|
@ -67,8 +65,12 @@ impl<'a> App<'a> {
|
||||||
},
|
},
|
||||||
KeyCode::Enter => {
|
KeyCode::Enter => {
|
||||||
let selected = self.guest_state.selected().unwrap();
|
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());
|
self.status = AppStatus::TalkToGuest(guest.id());
|
||||||
|
},
|
||||||
|
KeyCode::Char('.') => {
|
||||||
|
self.state.step();
|
||||||
|
self.status = AppStatus::Initial;
|
||||||
},
|
},
|
||||||
KeyCode::Esc => {
|
KeyCode::Esc => {
|
||||||
return false;
|
return false;
|
||||||
|
@ -105,8 +107,8 @@ impl<'a> App<'a> {
|
||||||
AppStatus::GuestSelection => {
|
AppStatus::GuestSelection => {
|
||||||
self.draw_guest_selection(f);
|
self.draw_guest_selection(f);
|
||||||
},
|
},
|
||||||
AppStatus::TalkToGuest(guest) => {
|
AppStatus::TalkToGuest(guest_id) => {
|
||||||
self.draw_talk_to_guest(f, guest.clone());
|
self.draw_talk_to_guest(f, *guest_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,16 +117,50 @@ impl<'a> App<'a> {
|
||||||
pub fn draw_initial<B: Backend>(&mut self, f: &mut Frame<B>) {}
|
pub fn draw_initial<B: Backend>(&mut self, f: &mut Frame<B>) {}
|
||||||
|
|
||||||
pub fn draw_guest_selection<B: Backend>(&mut self, f: &mut Frame<B>) {
|
pub fn draw_guest_selection<B: Backend>(&mut self, f: &mut Frame<B>) {
|
||||||
|
let chunks = Layout::default()
|
||||||
|
.direction(tui::layout::Direction::Vertical)
|
||||||
|
.constraints(
|
||||||
|
[
|
||||||
|
tui::layout::Constraint::Min(3),
|
||||||
|
tui::layout::Constraint::Length(2)
|
||||||
|
]
|
||||||
|
.as_ref(),
|
||||||
|
)
|
||||||
|
.split(f.size());
|
||||||
|
|
||||||
let main_window = tui::widgets::List::new(self.guest_list.clone())
|
let main_window = tui::widgets::List::new(self.guest_list.clone())
|
||||||
.block(tui::widgets::Block::default().title("Guests").borders(tui::widgets::Borders::ALL))
|
.block(tui::widgets::Block::default().title("Guests").borders(tui::widgets::Borders::ALL))
|
||||||
.style(tui::style::Style::default().fg(tui::style::Color::White))
|
.style(tui::style::Style::default().fg(tui::style::Color::White))
|
||||||
.highlight_style(tui::style::Style::default().add_modifier(tui::style::Modifier::ITALIC))
|
.highlight_style(tui::style::Style::default().add_modifier(tui::style::Modifier::ITALIC))
|
||||||
.highlight_symbol(">>");
|
.highlight_symbol(">>");
|
||||||
|
|
||||||
f.render_stateful_widget(main_window, f.size(), &mut self.guest_state);
|
let key_style = tui::style::Style::default()
|
||||||
|
.add_modifier(tui::style::Modifier::BOLD)
|
||||||
|
.fg(tui::style::Color::Green)
|
||||||
|
.bg(tui::style::Color::Black);
|
||||||
|
let control_text = tui::text::Spans::from(vec![
|
||||||
|
tui::text::Span::styled("↑↓", key_style),
|
||||||
|
tui::text::Span::raw(" select guest"),
|
||||||
|
tui::text::Span::raw(" "),
|
||||||
|
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"),
|
||||||
|
|
||||||
|
]);
|
||||||
|
let controls = tui::widgets::Paragraph::new(control_text)
|
||||||
|
.style(tui::style::Style::default().fg(tui::style::Color::White));
|
||||||
|
|
||||||
|
f.render_stateful_widget(main_window, chunks[0], &mut self.guest_state);
|
||||||
|
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())
|
let main_window = tui::widgets::Paragraph::new(guest.name.clone())
|
||||||
.block(tui::widgets::Block::default().title("Guests").borders(tui::widgets::Borders::ALL))
|
.block(tui::widgets::Block::default().title("Guests").borders(tui::widgets::Borders::ALL))
|
||||||
.style(tui::style::Style::default().fg(tui::style::Color::White));
|
.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);
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
mod found_town;
|
mod found_town;
|
||||||
mod world_genesis;
|
mod world_genesis;
|
||||||
mod person_genesis;
|
mod person_genesis;
|
||||||
|
mod tavern_built;
|
||||||
|
|
||||||
pub use found_town::FoundTown;
|
pub use found_town::FoundTown;
|
||||||
pub use world_genesis::WorldGenesis;
|
pub use world_genesis::WorldGenesis;
|
||||||
pub use person_genesis::PersonGenesis;
|
pub use person_genesis::PersonGenesis;
|
||||||
|
pub use tavern_built::TavernBuilt;
|
|
@ -4,12 +4,12 @@ use crate::{state::{GameState, Effect}, world::Town, person::Person};
|
||||||
|
|
||||||
pub struct PersonGenesis {
|
pub struct PersonGenesis {
|
||||||
pub town: Rc<Town>,
|
pub town: Rc<Town>,
|
||||||
pub person: Rc<Person>,
|
pub person: Person,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Effect for PersonGenesis {
|
impl Effect for PersonGenesis {
|
||||||
fn apply(&self, state: &mut GameState) {
|
fn apply(&self, state: &mut GameState) {
|
||||||
state.add_person(self.person.clone());
|
state.add_person(&mut self.person.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn description(&self) -> String {
|
fn description(&self) -> String {
|
||||||
|
|
|
@ -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<Tavern>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ mod time;
|
||||||
mod generators;
|
mod generators;
|
||||||
mod person;
|
mod person;
|
||||||
mod app;
|
mod app;
|
||||||
|
mod entity;
|
||||||
|
|
||||||
use app::App;
|
use app::App;
|
||||||
use noise::{Perlin, ScalePoint, Add, NoiseFn, ScaleBias};
|
use noise::{Perlin, ScalePoint, Add, NoiseFn, ScaleBias};
|
||||||
|
@ -132,10 +133,10 @@ fn main() -> Result<(), io::Error> {
|
||||||
|
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
for _ in 0..N_TOWNS {
|
for _ in 0..N_TOWNS {
|
||||||
|
|
||||||
world_state.step_n(rng.gen_range(0..360*3));
|
world_state.step_n(rng.gen_range(0..360*3));
|
||||||
world_state.found_town();
|
world_state.found_town();
|
||||||
}
|
}
|
||||||
|
world_state.build_tavern();
|
||||||
|
|
||||||
|
|
||||||
// setup terminal
|
// setup terminal
|
||||||
|
|
|
@ -1,29 +1,75 @@
|
||||||
use std::{rc::Rc, fmt::Display};
|
use std::{rc::Rc, fmt::Display};
|
||||||
|
use rand::prelude::*;
|
||||||
|
|
||||||
use crate::{time::Time, world::Town, generators::PersonNameGenerator};
|
use crate::{time::Time, world::Town, generators::PersonNameGenerator, state::GameState, entity::Entity};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
pub enum Profession {
|
pub enum Profession {
|
||||||
Peasant,
|
Peasant,
|
||||||
Adventurer,
|
Adventurer,
|
||||||
Blacksmith,
|
Blacksmith,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum Agenda {
|
||||||
|
Idle(u32),
|
||||||
|
Traveling([usize; 2]),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Person {
|
pub struct Person {
|
||||||
|
pub id: Option<u32>,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub birth_date: Time,
|
pub birth_date: Time,
|
||||||
pub birth_location: Rc<Town>,
|
pub birth_location: Rc<Town>,
|
||||||
pub profession: Profession,
|
pub profession: Profession,
|
||||||
pub location: [usize; 2],
|
pub location: [usize; 2],
|
||||||
|
pub agenda: Agenda,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Person {
|
impl Person {
|
||||||
pub fn new(birth_date: Time, birth_location: Rc<Town>, location: [usize; 2]) -> Person {
|
pub fn new(birth_date: Time, birth_location: Rc<Town>, location: [usize; 2]) -> Person {
|
||||||
Person {
|
Person {
|
||||||
|
id: None,
|
||||||
name: PersonNameGenerator::name(),
|
name: PersonNameGenerator::name(),
|
||||||
birth_date: birth_date,
|
birth_date: birth_date,
|
||||||
birth_location: birth_location,
|
birth_location: birth_location,
|
||||||
profession: Profession::Peasant,
|
profession: Profession::Peasant,
|
||||||
location: [0, 0],
|
location: location,
|
||||||
|
agenda: Agenda::Idle(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn step(&mut self, state: &mut GameState) {
|
||||||
|
match &self.agenda {
|
||||||
|
Agenda::Idle(days) => {
|
||||||
|
// do nothing
|
||||||
|
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
|
||||||
|
// 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(10);
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,4 +78,50 @@ impl Display for Person {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{}", self.name)
|
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") },
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
128
src/state.rs
128
src/state.rs
|
@ -1,16 +1,19 @@
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
use crate::entity::Entity;
|
||||||
use crate::person::Person;
|
use crate::person::Person;
|
||||||
use crate::time::Time;
|
use crate::time::Time;
|
||||||
use crate::world::{World, Terrain, Town};
|
use crate::world::{World, Terrain, Town, Tavern};
|
||||||
use crate::events::{FoundTown, WorldGenesis, PersonGenesis};
|
use crate::events::{FoundTown, WorldGenesis, PersonGenesis, TavernBuilt};
|
||||||
pub struct GameState {
|
pub struct GameState {
|
||||||
pub time: Time,
|
pub time: Time,
|
||||||
pub world: World,
|
pub world: World,
|
||||||
pub people: Vec<Rc<Person>>,
|
pub people: HashMap<u32, Person>,
|
||||||
pub events: Vec<Box<Event>>
|
pub events: Vec<Box<Event>>,
|
||||||
|
pub tavern: Option<Rc<Tavern>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Event {
|
pub struct Event {
|
||||||
|
@ -40,18 +43,29 @@ impl GameState {
|
||||||
|
|
||||||
GameState {
|
GameState {
|
||||||
time: Time { time: 0 },
|
time: Time { time: 0 },
|
||||||
people: Vec::new(),
|
people: HashMap::new(),
|
||||||
world: world,
|
world: world,
|
||||||
events: events,
|
events: events,
|
||||||
|
tavern: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_tavern(&mut self, tavern: Rc<Tavern>) {
|
||||||
|
self.tavern = Some(tavern);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_event(&mut self, event: Event) {
|
pub fn add_event(&mut self, event: Event) {
|
||||||
event.effect.apply(self);
|
event.effect.apply(self);
|
||||||
self.events.push(Box::new(event));
|
self.events.push(Box::new(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn step(&mut self) {
|
pub fn step(&mut 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;
|
self.time.time += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,6 +81,31 @@ impl GameState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getters
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub fn guests(&self) -> Vec<Person> {
|
||||||
|
match &self.tavern {
|
||||||
|
Some(tavern) => {
|
||||||
|
let loc = self.world.get_tavern_location(tavern);
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to populate the world at genesis
|
||||||
|
*/
|
||||||
|
|
||||||
pub fn found_town(&mut self) {
|
pub fn found_town(&mut self) {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
loop {
|
loop {
|
||||||
|
@ -93,11 +132,11 @@ impl GameState {
|
||||||
time: self.time,
|
time: self.time,
|
||||||
effect: Box::new(PersonGenesis {
|
effect: Box::new(PersonGenesis {
|
||||||
town: town.clone(),
|
town: town.clone(),
|
||||||
person: Rc::new(Person::new(
|
person: Person::new(
|
||||||
self.time.substract_years(rng.gen_range(18..30)),
|
self.time.substract_years(rng.gen_range(18..30)),
|
||||||
town.clone(),
|
town.clone(),
|
||||||
self.world.get_town_location(&town)
|
self.world.get_town_location(&town)
|
||||||
)),
|
),
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -106,7 +145,78 @@ impl GameState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_person(&mut self, person: Rc<Person>) {
|
pub fn add_person(&mut self, person: &mut Person) {
|
||||||
self.people.push(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) {
|
||||||
|
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()),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
47
src/world.rs
47
src/world.rs
|
@ -1,4 +1,4 @@
|
||||||
use std::rc::Rc;
|
use std::{rc::Rc, collections::HashMap};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
|
||||||
use crate::generators::TownNameGenerator;
|
use crate::generators::TownNameGenerator;
|
||||||
|
@ -19,25 +19,29 @@ pub struct Town {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Tavern {
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
pub enum Structure {
|
pub enum Structure {
|
||||||
Town(Rc<Town>),
|
Town(Rc<Town>),
|
||||||
|
Tavern(Rc<Tavern>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WorldCell {
|
pub struct WorldCell {
|
||||||
pub terrain: Terrain,
|
pub terrain: Terrain,
|
||||||
pub structure: Option<Structure>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct World {
|
pub struct World {
|
||||||
pub map: Vec<Vec<WorldCell>>,
|
pub map: Vec<Vec<WorldCell>>,
|
||||||
pub size: usize,
|
pub size: usize,
|
||||||
|
pub structures: HashMap<[usize; 2], Structure>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorldCell {
|
impl WorldCell {
|
||||||
pub fn new(terrain: Terrain) -> WorldCell {
|
pub fn new(terrain: Terrain) -> WorldCell {
|
||||||
WorldCell {
|
WorldCell {
|
||||||
terrain: terrain,
|
terrain: terrain,
|
||||||
structure: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,26 +59,43 @@ impl World {
|
||||||
World {
|
World {
|
||||||
map: map,
|
map: map,
|
||||||
size: size,
|
size: size,
|
||||||
|
structures: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_structure(&mut self, x: usize, y: usize, structure: Structure) {
|
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<Town>) -> [usize; 2] {
|
pub fn get_town_location(&self, town: &Rc<Town>) -> [usize; 2] {
|
||||||
for x in 0..self.size {
|
for (location, structure) in self.structures.iter() {
|
||||||
for y in 0..self.size {
|
match structure {
|
||||||
if let Some(Structure::Town(t)) = &self.map[x][y].structure {
|
Structure::Town(t) => {
|
||||||
if Rc::ptr_eq(&t, town) {
|
if Rc::ptr_eq(t, town) {
|
||||||
return [x, y];
|
return *location;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
panic!("Town not found");
|
panic!("Town not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_tavern_location(&self, tavern: &Rc<Tavern>) -> [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 {
|
impl Terrain {
|
||||||
|
@ -117,3 +138,11 @@ impl Town {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Tavern {
|
||||||
|
pub fn new() -> Tavern {
|
||||||
|
Tavern {
|
||||||
|
name: TownNameGenerator::name(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue