use std::collections::HashMap; use rand::prelude::*; use crate::entity::{Location, EntityId, Entity, EntityType}; use crate::creature::{Creature, Profession, CreatureGenerator}; use crate::item::{Item, ItemOwner}; use crate::site::{Town, Tavern, Site}; use crate::time::Time; use crate::world::{World, Terrain}; use crate::events::{FoundTown, WorldGenesis, PersonGenesis, TavernBuilt}; pub struct GameState { pub time: Time, pub world: World, pub creatures: HashMap, pub items: HashMap, pub events: Vec>, pub tavern: Option, } pub struct Event { pub time: Time, pub effect: Box, } 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 { #[allow(dead_code)] 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(), items: HashMap::new(), world: world, events: events, tavern: None, } } pub fn set_tavern(&mut self, tavern: EntityId) { self.tavern = Some(tavern); } pub fn apply_action(&mut self, action: Box) { let mut event = Event { time: self.time, effect: action, }; event.effect.apply(self); self.events.push(Box::new(event)); } /** * Getters */ pub fn guests(&self) -> Vec { match &self.tavern { Some(tavern) => { let loc = self.world.get_site_location(*tavern); self.creatures.values().filter(|c| { c.loc == loc }).map(|p| p.entity.id).collect::>() // Vec::new() }, None => Vec::new(), } } pub fn get_creature(&self, id: EntityId) -> 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 = Town::new(); self.apply_action(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.apply_action(Box::new(PersonGenesis { town: town.clone(), person: CreatureGenerator::create_human( self.time.substract_years(rng.gen_range(18..30)), Location { x: x as i32, y: y as i32 }, ) })); } break; } } } pub fn add_person(&mut self, mut creature: Creature) -> EntityId { let id = (self.creatures.len() + 1) as u32; // avoid 0 id creature.entity.id.1 = id; let id = creature.entity.id; self.creatures.insert(id, creature); id } pub(crate) fn add_item(&mut self, mut item: Item) -> EntityId { let id = (self.items.len() + 1) as u32; // avoid 0 id item.entity.id.1 = id; let id = item.entity.id; self.items.insert(id, item); id } 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.apply_action(Box::new(TavernBuilt { loc: Location{ x: x as i32, y: y as i32 }, tavern: Tavern::new(), })); break; } } } pub fn claim_item(&mut self, new_owner_id: EntityId, item_id: EntityId) { let mut item = self.items.get_mut(&item_id).unwrap(); item.owner = ItemOwner::Held(new_owner_id.clone()); } pub fn add_coins(&mut self, id: EntityId, coins: u32) { match id.0 { EntityType::Creature => { let mut creature = self.creatures.get(&id).unwrap().clone(); creature.coins += coins; self.creatures.insert(id, creature); }, EntityType::Item => {}, EntityType::Site => { self.world.add_coins(id, coins); }, } } pub fn pay_upkeep(&mut self, id: EntityId) { match id.0 { EntityType::Creature => { let mut creature = self.creatures.get(&id).unwrap().clone(); let site = self.world.get_site_at(creature.loc); match site { Some(site) => { let upkeep = 1; if creature.coins >= upkeep { creature.coins -= upkeep; self.creatures.insert(id, creature); self.add_coins(site.entity.id, upkeep); } else { // TODO: punish } }, None => {}, } }, EntityType::Item => {}, EntityType::Site => {}, } } } #[cfg(test)] mod tests { 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); } }