2022-12-31 07:54:18 +00:00
|
|
|
|
2022-12-31 14:22:44 +00:00
|
|
|
use std::collections::HashMap;
|
2022-12-31 07:54:18 +00:00
|
|
|
|
|
|
|
use rand::prelude::*;
|
2023-01-08 10:57:13 +00:00
|
|
|
use crate::entity::{Location, EntityId, Entity, EntityType};
|
2023-01-05 19:14:27 +00:00
|
|
|
use crate::creature::{Creature, Profession, CreatureGenerator};
|
2023-01-07 09:08:42 +00:00
|
|
|
use crate::item::{Item, ItemOwner};
|
2023-01-08 10:57:13 +00:00
|
|
|
use crate::site::{Town, Tavern, Site};
|
2022-12-31 07:54:18 +00:00
|
|
|
use crate::time::Time;
|
2023-01-04 16:52:58 +00:00
|
|
|
use crate::world::{World, Terrain};
|
2022-12-31 13:03:24 +00:00
|
|
|
use crate::events::{FoundTown, WorldGenesis, PersonGenesis, TavernBuilt};
|
2022-12-30 20:30:12 +00:00
|
|
|
pub struct GameState {
|
2022-12-31 07:54:18 +00:00
|
|
|
pub time: Time,
|
2022-12-30 20:30:12 +00:00
|
|
|
pub world: World,
|
2023-01-07 09:08:42 +00:00
|
|
|
pub creatures: HashMap<EntityId, Creature>,
|
|
|
|
pub items: HashMap<EntityId, Item>,
|
2022-12-31 13:03:24 +00:00
|
|
|
pub events: Vec<Box<Event>>,
|
2023-01-04 16:52:58 +00:00
|
|
|
pub tavern: Option<EntityId>,
|
2022-12-31 07:54:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Event {
|
|
|
|
pub time: Time,
|
2023-01-04 13:37:25 +00:00
|
|
|
pub effect: Box<dyn Action>,
|
2022-12-31 07:54:18 +00:00
|
|
|
}
|
|
|
|
|
2023-01-04 13:37:25 +00:00
|
|
|
pub trait Action {
|
|
|
|
// apply the effect to the game state
|
2022-12-31 07:54:18 +00:00
|
|
|
fn apply(&self, state: &mut GameState);
|
2023-01-04 13:37:25 +00:00
|
|
|
// 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
|
2022-12-31 07:54:18 +00:00
|
|
|
fn description(&self) -> String;
|
2023-01-04 13:37:25 +00:00
|
|
|
// if true, the event will be added to the event log
|
|
|
|
// otherwise, it will only be used for internal purposes
|
|
|
|
fn notable(&self) -> bool;
|
2022-12-31 07:54:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Event {
|
2023-01-04 18:02:44 +00:00
|
|
|
#[allow(dead_code)]
|
2022-12-31 07:54:18 +00:00
|
|
|
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 },
|
2023-01-04 13:37:25 +00:00
|
|
|
creatures: HashMap::new(),
|
2023-01-05 19:14:27 +00:00
|
|
|
items: HashMap::new(),
|
2022-12-31 07:54:18 +00:00
|
|
|
world: world,
|
|
|
|
events: events,
|
2022-12-31 13:03:24 +00:00
|
|
|
tavern: None,
|
2022-12-31 07:54:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-04 16:52:58 +00:00
|
|
|
pub fn set_tavern(&mut self, tavern: EntityId) {
|
2022-12-31 13:03:24 +00:00
|
|
|
self.tavern = Some(tavern);
|
|
|
|
}
|
|
|
|
|
2023-01-08 10:57:13 +00:00
|
|
|
pub fn apply_action(&mut self, action: Box<dyn Action>) {
|
|
|
|
let mut event = Event {
|
|
|
|
time: self.time,
|
|
|
|
effect: action,
|
|
|
|
};
|
2022-12-31 07:54:18 +00:00
|
|
|
event.effect.apply(self);
|
|
|
|
self.events.push(Box::new(event));
|
|
|
|
}
|
|
|
|
|
2022-12-31 13:03:24 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Getters
|
|
|
|
*/
|
|
|
|
|
2023-01-04 13:37:25 +00:00
|
|
|
pub fn guests(&self) -> Vec<EntityId> {
|
2022-12-31 13:03:24 +00:00
|
|
|
match &self.tavern {
|
|
|
|
Some(tavern) => {
|
2023-01-04 16:52:58 +00:00
|
|
|
let loc = self.world.get_site_location(*tavern);
|
2023-01-04 13:37:25 +00:00
|
|
|
self.creatures.values().filter(|c| {
|
2023-01-07 09:08:42 +00:00
|
|
|
c.loc == loc
|
2023-01-04 13:37:25 +00:00
|
|
|
}).map(|p| p.entity.id).collect::<Vec<_>>()
|
|
|
|
// Vec::new()
|
2022-12-31 13:03:24 +00:00
|
|
|
},
|
|
|
|
None => Vec::new(),
|
2022-12-31 14:22:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-07 09:08:42 +00:00
|
|
|
pub fn get_creature(&self, id: EntityId) -> Option<&Creature> {
|
2023-01-04 13:37:25 +00:00
|
|
|
self.creatures.get(&id)
|
2022-12-31 13:03:24 +00:00
|
|
|
}
|
|
|
|
|
2022-12-31 15:04:07 +00:00
|
|
|
|
2022-12-31 13:03:24 +00:00
|
|
|
/**
|
|
|
|
* Function to populate the world at genesis
|
|
|
|
*/
|
|
|
|
|
2022-12-31 07:54:18 +00:00
|
|
|
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
|
|
|
|
{
|
2023-01-04 16:52:58 +00:00
|
|
|
let town = Town::new();
|
2023-01-08 10:57:13 +00:00
|
|
|
self.apply_action(Box::new(FoundTown {
|
|
|
|
loc: Location{ x: x as i32, y: y as i32 },
|
|
|
|
town: town.clone(),
|
|
|
|
}));
|
2022-12-31 08:38:01 +00:00
|
|
|
|
|
|
|
let pop_size = rng.gen_range(20..200);
|
|
|
|
|
|
|
|
for _ in 0..pop_size {
|
2023-01-08 10:57:13 +00:00
|
|
|
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 },
|
|
|
|
)
|
|
|
|
}));
|
2022-12-31 08:38:01 +00:00
|
|
|
}
|
2022-12-31 07:54:18 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-12-31 08:38:01 +00:00
|
|
|
|
2023-01-07 09:08:42 +00:00
|
|
|
pub fn add_person(&mut self, mut creature: Creature) -> EntityId {
|
2023-01-04 13:37:25 +00:00
|
|
|
let id = (self.creatures.len() + 1) as u32; // avoid 0 id
|
2023-01-07 09:08:42 +00:00
|
|
|
creature.entity.id.1 = id;
|
|
|
|
let id = creature.entity.id;
|
2023-01-04 13:37:25 +00:00
|
|
|
self.creatures.insert(id, creature);
|
2023-01-07 09:08:42 +00:00
|
|
|
id
|
2022-12-31 08:38:01 +00:00
|
|
|
}
|
2022-12-31 13:03:24 +00:00
|
|
|
|
2023-01-05 19:14:27 +00:00
|
|
|
pub(crate) fn add_item(&mut self, mut item: Item) -> EntityId {
|
|
|
|
let id = (self.items.len() + 1) as u32; // avoid 0 id
|
2023-01-07 09:08:42 +00:00
|
|
|
item.entity.id.1 = id;
|
|
|
|
let id = item.entity.id;
|
2023-01-05 19:14:27 +00:00
|
|
|
self.items.insert(id, item);
|
2023-01-07 09:08:42 +00:00
|
|
|
id
|
2023-01-05 19:14:27 +00:00
|
|
|
}
|
|
|
|
|
2022-12-31 13:03:24 +00:00
|
|
|
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
|
|
|
|
{
|
2023-01-08 10:57:13 +00:00
|
|
|
self.apply_action(Box::new(TavernBuilt {
|
|
|
|
loc: Location{ x: x as i32, y: y as i32 },
|
|
|
|
tavern: Tavern::new(),
|
|
|
|
}));
|
2022-12-31 14:22:44 +00:00
|
|
|
break;
|
2022-12-31 13:03:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-01-05 19:14:27 +00:00
|
|
|
|
2023-01-07 08:50:09 +00:00
|
|
|
pub fn claim_item(&mut self, new_owner_id: EntityId, item_id: EntityId) {
|
|
|
|
let mut item = self.items.get_mut(&item_id).unwrap();
|
2023-01-07 09:08:42 +00:00
|
|
|
item.owner = ItemOwner::Held(new_owner_id.clone());
|
2023-01-07 08:50:09 +00:00
|
|
|
}
|
|
|
|
|
2023-01-08 10:57:13 +00:00
|
|
|
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 => {},
|
|
|
|
}
|
|
|
|
}
|
2022-12-31 14:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[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;
|
2023-01-01 20:11:27 +00:00
|
|
|
state.time = Time { time: 1e6 as i32 };
|
2022-12-31 14:22:44 +00:00
|
|
|
state.found_town();
|
2023-01-04 13:37:25 +00:00
|
|
|
assert_ne!(state.creatures.len(), 0);
|
2022-12-31 14:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[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);
|
|
|
|
}
|
2023-01-04 13:37:25 +00:00
|
|
|
|
2022-12-30 20:30:12 +00:00
|
|
|
}
|