Compare commits

...

2 Commits

Author SHA1 Message Date
Niko Abeler 427595edb1 refactoring EntityId 2023-01-07 10:08:42 +01:00
Niko Abeler a1050c1440 reverse inventory definition 2023-01-07 09:50:09 +01:00
11 changed files with 108 additions and 54 deletions

View File

@ -1,7 +1,7 @@
use std::{fmt::Display}; use std::{fmt::Display};
use rand::prelude::*; use rand::prelude::*;
use crate::{time::Time, world::{World}, generators::PersonNameGenerator, state::{GameState, Action}, entity::{Entity, Location, EntityId}, item::ItemGenerator, events::ItemCrafted}; use crate::{time::Time, world::{World}, generators::PersonNameGenerator, state::{GameState, Action}, entity::{Entity, Location, EntityId, EntityType}, item::ItemGenerator, events::ItemCrafted};
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum Profession { pub enum Profession {
@ -20,11 +20,11 @@ pub enum Agenda {
#[derive(Clone)] #[derive(Clone)]
pub struct Creature { pub struct Creature {
pub entity: Entity, pub entity: Entity,
pub loc: Location,
pub name: String, pub name: String,
pub birth_date: Time, pub birth_date: Time,
pub profession: Profession, pub profession: Profession,
pub agenda: Agenda, pub agenda: Agenda,
pub inventory: Vec<EntityId>,
pub weapon: Option<EntityId>, pub weapon: Option<EntityId>,
pub armor: Option<EntityId>, pub armor: Option<EntityId>,
} }
@ -45,12 +45,12 @@ impl Display for Profession {
impl Creature { impl Creature {
pub fn new(birth_date: Time, location: Location, profession: Profession) -> Creature { pub fn new(birth_date: Time, location: Location, profession: Profession) -> Creature {
Creature { Creature {
entity: Entity { id: 0, loc: location }, entity: Entity::new_creature(),
loc: location,
name: PersonNameGenerator::name(), name: PersonNameGenerator::name(),
birth_date: birth_date, birth_date: birth_date,
profession: profession, profession: profession,
agenda: Agenda::Idle(0), agenda: Agenda::Idle(0),
inventory: Vec::new(),
weapon: None, weapon: None,
armor: None, armor: None,
} }
@ -111,17 +111,17 @@ impl Creature {
Agenda::Traveling(destination) => { Agenda::Traveling(destination) => {
// TDOO: A* pathfinding with terrain costs // TDOO: A* pathfinding with terrain costs
// move towards destination // move towards destination
if self.entity.loc.x < destination.x { if self.loc.x < destination.x {
self.entity.loc.x += 1; self.loc.x += 1;
} else if self.entity.loc.x > destination.x { } else if self.loc.x > destination.x {
self.entity.loc.x -= 1; self.loc.x -= 1;
} }
if self.entity.loc.y < destination.y { if self.loc.y < destination.y {
self.entity.loc.y += 1; self.loc.y += 1;
} else if self.entity.loc.y > destination.y { } else if self.loc.y > destination.y {
self.entity.loc.y -= 1; self.loc.y -= 1;
} }
if self.entity.loc == *destination { if self.loc == *destination {
self.agenda = Agenda::Idle(10); self.agenda = Agenda::Idle(10);
} }
Vec::new() Vec::new()
@ -201,7 +201,7 @@ mod tests {
let mut person = Creature::new(Time { time: 0 }, Location{x: 0, y: 0}, Profession::Peasant); let mut person = Creature::new(Time { time: 0 }, Location{x: 0, y: 0}, Profession::Peasant);
person.agenda = Agenda::Traveling(Location{x: 10, y: 0}); person.agenda = Agenda::Traveling(Location{x: 10, y: 0});
person.step(&World::new(32)); person.step(&World::new(32));
assert_eq!(person.entity.loc, Location{x: 1, y: 0}); assert_eq!(person.loc, Location{x: 1, y: 0});
} }
#[test] #[test]
@ -210,7 +210,8 @@ mod tests {
person.agenda = Agenda::Idle(0); person.agenda = Agenda::Idle(0);
let mut world = World::new(32); let mut world = World::new(32);
world.add_site(Site{ world.add_site(Site{
entity: Entity { id: 0, loc: Location{x: 10, y: 10} }, entity: Entity::new_creature(),
loc: Location{x: 10, y: 10},
structure: Structure::Town(Town::new()), structure: Structure::Town(Town::new()),
}); });
person.step(&world); person.step(&world);

View File

@ -1,7 +1,16 @@
use std::fmt; use std::fmt;
pub type EntityId = u32; pub type EntityId = (EntityType, u32);
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum EntityType {
Creature,
Site,
Item,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Location { pub struct Location {
@ -12,9 +21,23 @@ pub struct Location {
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Entity { pub struct Entity {
pub id: EntityId, pub id: EntityId,
pub loc: Location
} }
impl Entity {
pub fn new_creature() -> Entity {
Entity { id: (EntityType::Creature, 0) }
}
pub fn new_site() -> Entity {
Entity { id: (EntityType::Site, 0) }
}
pub fn new_item() -> Entity {
Entity { id: (EntityType::Item, 0) }
}
}
impl fmt::Display for Location { impl fmt::Display for Location {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y) write!(f, "({}, {})", self.x, self.y)

View File

@ -9,10 +9,8 @@ impl Action for FoundTown {
fn apply(&self, state: &mut GameState) { fn apply(&self, state: &mut GameState) {
state.world.add_site( state.world.add_site(
Site{ Site{
entity: Entity{ entity: Entity::new_site(),
loc: self.loc, loc: self.loc,
id: 0,
},
structure: Structure::Town(self.town.clone()), structure: Structure::Town(self.town.clone()),
} }
); );

View File

@ -7,7 +7,8 @@ pub struct ItemCrafted {
impl Action for ItemCrafted { impl Action for ItemCrafted {
fn apply(&self, state: &mut GameState) { fn apply(&self, state: &mut GameState) {
state.add_item(self.item.clone()); let item_id = state.add_item(self.item.clone());
state.claim_item(self.crafter, item_id);
} }
fn description(&self) -> String { fn description(&self) -> String {

View File

@ -9,7 +9,8 @@ pub struct TavernBuilt {
impl Action for TavernBuilt { impl Action for TavernBuilt {
fn apply(&self, state: &mut GameState) { fn apply(&self, state: &mut GameState) {
let tavern_id = state.world.add_site(Site{ let tavern_id = state.world.add_site(Site{
entity: Entity{ loc: self.loc, id: 0}, entity: Entity::new_site(),
loc: self.loc,
structure: Structure::Tavern(self.tavern.clone()), structure: Structure::Tavern(self.tavern.clone()),
}); });
state.set_tavern(tavern_id); state.set_tavern(tavern_id);

View File

@ -1,4 +1,4 @@
use crate::state::{GameState, Event}; use crate::{state::{GameState, Event}, entity::EntityId};
/** /**
* This is the main game struct. * This is the main game struct.
@ -18,7 +18,7 @@ impl Game {
pub fn step(&mut self) { pub fn step(&mut self) {
// get list of all people ids // get list of all people ids
let ids: Vec<u32> = self.state.creatures.keys().map(|id| *id).collect(); let ids: Vec<EntityId> = self.state.creatures.keys().map(|id| *id).collect();
// step each person // step each person
for id in ids { for id in ids {
@ -74,13 +74,13 @@ mod tests {
crate::creature::Profession::Adventurer, crate::creature::Profession::Adventurer,
); );
creature.set_agenda(Agenda::Traveling(Location { x: 2, y: 2 })); creature.set_agenda(Agenda::Traveling(Location { x: 2, y: 2 }));
state.add_person(creature); let id = state.add_person(creature);
let mut game = Game::new(state); let mut game = Game::new(state);
game.step(); game.step();
assert_eq!(game.state.creatures.len(), 1); assert_eq!(game.state.creatures.len(), 1);
assert_eq!(game.state.creatures[&1].entity.loc, Location { x: 1, y: 1 }); assert_eq!(game.state.creatures[&id].loc, Location { x: 1, y: 1 });
} }

View File

@ -1,6 +1,6 @@
use std::fmt; use std::fmt;
use crate::entity::{Entity, Location}; use crate::entity::{Entity, Location, EntityId};
#[derive(Clone)] #[derive(Clone)]
pub enum ItemType { pub enum ItemType {
@ -8,9 +8,18 @@ pub enum ItemType {
Armor(Armor), Armor(Armor),
} }
#[derive(Clone)]
pub enum ItemOwner {
Held(EntityId),
Dropped(Location),
Lost,
}
#[derive(Clone)] #[derive(Clone)]
pub struct Item { pub struct Item {
pub entity: Entity, pub entity: Entity,
pub owner: ItemOwner,
pub name: String, pub name: String,
pub item_type: ItemType, pub item_type: ItemType,
} }
@ -31,13 +40,14 @@ pub struct ItemGenerator;
impl ItemGenerator { impl ItemGenerator {
pub fn generate_item() -> Item { pub fn generate_item() -> Item {
Item { Item {
entity: Entity { id: 0, loc: Location{ x: 0, y: 0 } }, entity: Entity::new_item(),
name: "Sword".to_string(), name: "Sword".to_string(),
item_type: ItemType::Weapon(Weapon { item_type: ItemType::Weapon(Weapon {
damage_base: 0, damage_base: 0,
damage_dice: 1, damage_dice: 1,
damage_sides: 6, damage_sides: 6,
}), }),
owner: ItemOwner::Lost,
} }
} }
} }
@ -50,3 +60,13 @@ impl fmt::Display for ItemType {
} }
} }
} }
impl fmt::Display for ItemOwner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ItemOwner::Held(entity_id) => write!(f, "Held by {:?}", entity_id),
ItemOwner::Dropped(location) => write!(f, "Dropped at {}", location),
ItemOwner::Lost => write!(f, "Lost"),
}
}
}

View File

@ -1,6 +1,6 @@
use std::fmt; use std::fmt;
use crate::{entity::Entity, generators::TownNameGenerator}; use crate::{entity::{Entity, Location}, generators::TownNameGenerator};
#[derive(Clone)] #[derive(Clone)]
pub struct Town { pub struct Town {
@ -20,6 +20,7 @@ pub enum Structure {
pub struct Site { pub struct Site {
pub entity: Entity, pub entity: Entity,
pub loc: Location,
pub structure: Structure, pub structure: Structure,
} }

View File

@ -2,9 +2,9 @@
use std::collections::HashMap; use std::collections::HashMap;
use rand::prelude::*; use rand::prelude::*;
use crate::entity::{Location, EntityId}; use crate::entity::{Location, EntityId, Entity};
use crate::creature::{Creature, Profession, CreatureGenerator}; use crate::creature::{Creature, Profession, CreatureGenerator};
use crate::item::Item; use crate::item::{Item, ItemOwner};
use crate::site::{Town, Tavern}; use crate::site::{Town, Tavern};
use crate::time::Time; use crate::time::Time;
use crate::world::{World, Terrain}; use crate::world::{World, Terrain};
@ -12,8 +12,8 @@ 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 creatures: HashMap<u32, Creature>, pub creatures: HashMap<EntityId, Creature>,
pub items: HashMap<u32, Item>, pub items: HashMap<EntityId, Item>,
pub events: Vec<Box<Event>>, pub events: Vec<Box<Event>>,
pub tavern: Option<EntityId>, pub tavern: Option<EntityId>,
} }
@ -80,7 +80,7 @@ impl GameState {
Some(tavern) => { Some(tavern) => {
let loc = self.world.get_site_location(*tavern); let loc = self.world.get_site_location(*tavern);
self.creatures.values().filter(|c| { self.creatures.values().filter(|c| {
c.entity.loc == loc c.loc == loc
}).map(|p| p.entity.id).collect::<Vec<_>>() }).map(|p| p.entity.id).collect::<Vec<_>>()
// Vec::new() // Vec::new()
}, },
@ -88,7 +88,7 @@ impl GameState {
} }
} }
pub fn get_creature(&self, id: u32) -> Option<&Creature> { pub fn get_creature(&self, id: EntityId) -> Option<&Creature> {
self.creatures.get(&id) self.creatures.get(&id)
} }
@ -134,15 +134,18 @@ impl GameState {
} }
} }
pub fn add_person(&mut self, mut creature: Creature) { pub fn add_person(&mut self, mut creature: Creature) -> EntityId {
let id = (self.creatures.len() + 1) as u32; // avoid 0 id let id = (self.creatures.len() + 1) as u32; // avoid 0 id
creature.entity.id = id; creature.entity.id.1 = id;
let id = creature.entity.id;
self.creatures.insert(id, creature); self.creatures.insert(id, creature);
id
} }
pub(crate) fn add_item(&mut self, mut item: Item) -> EntityId { pub(crate) fn add_item(&mut self, mut item: Item) -> EntityId {
let id = (self.items.len() + 1) as u32; // avoid 0 id let id = (self.items.len() + 1) as u32; // avoid 0 id
item.entity.id = id; item.entity.id.1 = id;
let id = item.entity.id;
self.items.insert(id, item); self.items.insert(id, item);
id id
} }
@ -168,6 +171,11 @@ impl GameState {
} }
} }
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());
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -4,7 +4,7 @@ mod controls;
use crossterm::event::{read, Event, KeyCode}; use crossterm::event::{read, Event, KeyCode};
use tui::{backend::Backend, Frame, layout::Layout}; use tui::{backend::Backend, Frame, layout::Layout};
use crate::{game::Game, entity::EntityId, world::Terrain}; use crate::{game::Game, entity::{EntityId, EntityType}, world::Terrain};
use self::{chat::{Chat, ChatLine}, controls::Controls}; use self::{chat::{Chat, ChatLine}, controls::Controls};
@ -20,7 +20,7 @@ use self::{chat::{Chat, ChatLine}, controls::Controls};
enum AppStatus { enum AppStatus {
Initial, Initial,
GuestSelection, GuestSelection,
TalkToGuest(Option<u32>), TalkToGuest(Option<EntityId>),
Debug, Debug,
} }
@ -240,16 +240,16 @@ impl<'a> App<'a> {
let mut item_list = vec![]; let mut item_list = vec![];
for (id, creature) in self.game.state.creatures.iter() { for (id, creature) in self.game.state.creatures.iter() {
list.push(tui::widgets::ListItem::new(format!("{}: {} ({}) at {}", list.push(tui::widgets::ListItem::new(format!("{}: {} ({}) at {}",
id, creature.name, id.1, creature.name,
creature.profession, creature.profession,
creature.entity.loc, creature.loc,
))); )));
} }
for (id, item) in self.game.state.items.iter() { for (id, item) in self.game.state.items.iter() {
item_list.push(tui::widgets::ListItem::new(format!("{}: {} ({}) at {}", item_list.push(tui::widgets::ListItem::new(format!("{}: {} ({}) at {}",
id, item.name, id.1, item.name,
item.item_type, item.item_type,
item.entity.loc, item.owner,
))); )));
} }
@ -319,7 +319,7 @@ impl<'a> App<'a> {
f.render_widget(controls, chunks[1]); f.render_widget(controls, chunks[1]);
} }
fn draw_talk_to_guest<B: Backend>(&self, f: &mut Frame<B>, guest: Option<u32>) { fn draw_talk_to_guest<B: Backend>(&self, f: &mut Frame<B>, guest: Option<EntityId>) {
let chunks = self.default_layout().split(f.size()); let chunks = self.default_layout().split(f.size());
let guest = self.game.state.get_creature(guest.unwrap()).unwrap(); let guest = self.game.state.get_creature(guest.unwrap()).unwrap();
@ -435,7 +435,7 @@ impl<'a> App<'a> {
fn greet_guest(&mut self, guest_id: Option<EntityId>) { fn greet_guest(&mut self, guest_id: Option<EntityId>) {
let guest = self.game.state.get_creature(guest_id.unwrap()).unwrap(); let guest = self.game.state.get_creature(guest_id.unwrap()).unwrap();
self.conversation.add_line(ChatLine::new( self.conversation.add_line(ChatLine::new(
0, (EntityType::Creature, 0),
"You".to_owned(), "You".to_owned(),
"Greetings traveller!".to_owned(), "Greetings traveller!".to_owned(),
false false
@ -453,7 +453,7 @@ impl<'a> App<'a> {
fn ask_business(&mut self, guest: Option<EntityId>) { fn ask_business(&mut self, guest: Option<EntityId>) {
let guest = self.game.state.get_creature(guest.unwrap()).unwrap(); let guest = self.game.state.get_creature(guest.unwrap()).unwrap();
self.conversation.add_line(ChatLine::new( self.conversation.add_line(ChatLine::new(
0, (EntityType::Creature, 0),
"You".to_owned(), "You".to_owned(),
"What's your business?".to_owned(), "What's your business?".to_owned(),
false false

View File

@ -50,10 +50,11 @@ impl World {
} }
pub fn add_site(&mut self, mut site: Site) -> EntityId{ pub fn add_site(&mut self, mut site: Site) -> EntityId{
let id = self.sites.len() as EntityId; let id = self.sites.len() as u32;
site.entity.id = id; site.entity.id.1 = id;
self.sites.insert(site.entity.loc, site); let id = site.entity.id.clone();
return id; self.sites.insert(site.loc, site);
id
} }