tavern_keeper/src/state.rs

189 lines
5.4 KiB
Rust

use std::collections::HashMap;
use std::rc::Rc;
use rand::prelude::*;
use crate::entity::{Location, EntityId};
use crate::person::Creature;
use crate::time::Time;
use crate::world::{World, Terrain, Town, Tavern};
use crate::events::{FoundTown, WorldGenesis, PersonGenesis, TavernBuilt};
pub struct GameState {
pub time: Time,
pub world: World,
pub creatures: HashMap<u32, Creature>,
pub events: Vec<Box<Event>>,
pub tavern: Option<Rc<Tavern>>,
}
pub struct Event {
pub time: Time,
pub effect: Box<dyn Action>,
}
pub trait Action {
// apply the effect to the game state
fn apply(&self, state: &mut GameState);
// description of the event that will be displayed in the event log
// TODO: thils needs a more complex return type.
// A notable event needs to be related to entities
fn description(&self) -> String;
// if true, the event will be added to the event log
// otherwise, it will only be used for internal purposes
fn notable(&self) -> bool;
}
impl Event {
pub fn description(&self) -> String {
format!("{}: {}", self.time, self.effect.description())
}
}
impl GameState {
pub fn new(world: World) -> GameState {
let mut events = Vec::new();
events.push(Box::new(Event {
time: Time { time: 0 },
effect: Box::new(WorldGenesis),
}));
GameState {
time: Time { time: 0 },
creatures: HashMap::new(),
world: world,
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) {
event.effect.apply(self);
self.events.push(Box::new(event));
}
/**
* Getters
*/
pub fn guests(&self) -> Vec<EntityId> {
match &self.tavern {
Some(tavern) => {
let loc = self.world.get_tavern_location(tavern);
self.creatures.values().filter(|c| {
c.entity.loc == loc
}).map(|p| p.entity.id).collect::<Vec<_>>()
// Vec::new()
},
None => Vec::new(),
}
}
pub fn get_creature(&self, id: u32) -> Option<&Creature> {
self.creatures.get(&id)
}
/**
* Function to populate the world at genesis
*/
pub fn found_town(&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
{
let town = Rc::new(Town::new());
self.add_event(Event {
time: self.time,
effect: Box::new(FoundTown {
loc: Location{ x: x as i32, y: y as i32 },
town: town.clone(),
})
});
let pop_size = rng.gen_range(20..200);
for _ in 0..pop_size {
self.add_event(Event {
time: self.time,
effect: Box::new(PersonGenesis {
town: town.clone(),
person: Creature::new(
self.time.substract_years(rng.gen_range(18..30)),
town.clone(),
self.world.get_town_location(&town)
),
})
});
}
break;
}
}
}
pub fn add_person(&mut self, mut creature: Creature) {
let id = (self.creatures.len() + 1) as u32; // avoid 0 id
creature.entity.id = id;
self.creatures.insert(id, creature);
}
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 {
loc: Location{ x: x as i32, y: y as i32 },
tavern: Rc::new(Tavern::new()),
})
});
break;
}
}
}
}
#[cfg(test)]
mod tests {
use crate::person::Agenda;
use super::*;
#[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 i32 };
state.found_town();
assert_ne!(state.creatures.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);
}
}