start of professions and items

This commit is contained in:
Niko Abeler 2023-01-05 20:14:27 +01:00
parent 4b5ce102e0
commit 717be3a32f
7 changed files with 185 additions and 18 deletions

View File

@ -1,17 +1,20 @@
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}}; use crate::{time::Time, world::{World}, generators::PersonNameGenerator, state::{GameState, Action}, entity::{Entity, Location, EntityId}, item::ItemGenerator, events::ItemCrafted};
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum Profession { pub enum Profession {
Peasant, Peasant,
Adventurer,
Blacksmith,
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum Agenda { pub enum Agenda {
Idle(u32), Idle(u32), // number of days to idle
Traveling(Location), Traveling(Location), // destination
Craft,
} }
#[derive(Clone)] #[derive(Clone)]
@ -21,16 +24,75 @@ pub struct Creature {
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 armor: Option<EntityId>,
} }
pub struct CreatureGenerator;
impl Display for Profession {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Profession::Peasant => write!(f, "Peasant"),
Profession::Adventurer => write!(f, "Adventurer"),
Profession::Blacksmith => write!(f, "Blacksmith"),
}
}
}
impl Creature { impl Creature {
pub fn new(birth_date: Time, location: Location) -> Creature { pub fn new(birth_date: Time, location: Location, profession: Profession) -> Creature {
Creature { Creature {
entity: Entity { id: 0, loc: location }, entity: Entity { id: 0, loc: location },
name: PersonNameGenerator::name(), name: PersonNameGenerator::name(),
birth_date: birth_date, birth_date: birth_date,
profession: Profession::Peasant, profession: profession,
agenda: Agenda::Idle(0), agenda: Agenda::Idle(0),
inventory: Vec::new(),
weapon: None,
armor: None,
}
}
fn travel_to_random_location(&self, world: &World) -> Agenda {
let mut rng = rand::thread_rng();
let dest = world.sites.keys().choose(&mut rng);
if let Some(dest) = dest {
Agenda::Traveling(*dest)
} else {
Agenda::Idle(1)
}
}
fn select_peasant_agenda(&self, _world: &World) -> Agenda {
Agenda::Idle(10)
}
fn select_adventurer_agenda(&self, world: &World) -> Agenda {
self.travel_to_random_location(world)
}
fn select_blacksmith_agenda(&self, _world: &World) -> Agenda {
let mut rng = rand::thread_rng();
let p = rng.gen_range(0.0..1.0);
if p < 0.1 {
Agenda::Idle(10)
} else if p < 0.55 {
Agenda::Craft
} else {
self.travel_to_random_location(_world)
}
}
fn select_agenda(&self, world: &World) -> Agenda {
match self.profession {
Profession::Peasant => self.select_peasant_agenda(world),
Profession::Adventurer => self.select_adventurer_agenda(world),
Profession::Blacksmith => self.select_blacksmith_agenda(world),
} }
} }
@ -40,9 +102,7 @@ impl Creature {
// do nothing // do nothing
if *days <= 0 { if *days <= 0 {
// pick random destination // pick random destination
let mut rng = rand::thread_rng(); self.agenda = self.select_agenda(world);
let dest = world.sites.keys().choose(&mut rng);
self.agenda = Agenda::Traveling(*dest.unwrap());
} else { } else {
self.agenda = Agenda::Idle(days - 1); self.agenda = Agenda::Idle(days - 1);
} }
@ -66,6 +126,14 @@ impl Creature {
} }
Vec::new() Vec::new()
}, },
Agenda::Craft => {
vec![
Box::new(ItemCrafted {
crafter: self.entity.id,
item: ItemGenerator::generate_item(),
})
]
}
} }
} }
@ -85,6 +153,9 @@ impl Creature {
}, },
None => return format!("I'm traveling to an unknown location"), None => return format!("I'm traveling to an unknown location"),
} }
},
Agenda::Craft => {
return format!("I'm crafting");
} }
} }
} }
@ -96,6 +167,23 @@ impl Display for Creature {
} }
} }
impl CreatureGenerator {
pub fn create_human(birth_date: Time, location: Location) -> Creature {
// pick random profession
let mut rng = rand::thread_rng();
let profession = rng.gen_range(0..3);
let profession = match profession {
0 => Profession::Peasant,
1 => Profession::Adventurer,
2 => Profession::Blacksmith,
_ => panic!("Invalid profession"),
};
Creature::new(birth_date, location, profession)
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{world::{World}, site::{Site, Town, Structure}}; use crate::{world::{World}, site::{Site, Town, Structure}};
@ -104,13 +192,13 @@ mod tests {
#[test] #[test]
fn test_person_creation() { fn test_person_creation() {
let person = Creature::new(Time { time: 0 }, Location{x: 0, y: 0}); let person = Creature::new(Time { time: 0 }, Location{x: 0, y: 0}, Profession::Peasant);
assert_ne!(person.name, ""); assert_ne!(person.name, "");
} }
#[test] #[test]
fn test_traveling() { fn test_traveling() {
let mut person = Creature::new(Time { time: 0 }, Location{x: 0, y: 0}); 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.entity.loc, Location{x: 1, y: 0});
@ -118,7 +206,7 @@ mod tests {
#[test] #[test]
fn test_start_traveling() { fn test_start_traveling() {
let mut person = Creature::new(Time { time: 0 }, Location{x: 0, y: 0}); let mut person = Creature::new(Time { time: 0 }, Location{x: 0, y: 0}, Profession::Peasant);
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{
@ -132,4 +220,4 @@ mod tests {
} }
} }
} }

20
src/events/item.rs Normal file
View File

@ -0,0 +1,20 @@
use crate::{entity::EntityId, item::Item, state::{Action, GameState, self}};
pub struct ItemCrafted {
pub crafter: EntityId,
pub item: Item,
}
impl Action for ItemCrafted {
fn apply(&self, state: &mut GameState) {
state.add_item(self.item.clone());
}
fn description(&self) -> String {
format!("{} crafted", self.item.name)
}
fn notable(&self) -> bool {
true
}
}

View File

@ -2,8 +2,10 @@ mod found_town;
mod world_genesis; mod world_genesis;
mod person_genesis; mod person_genesis;
mod tavern_built; mod tavern_built;
mod item;
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; pub use tavern_built::TavernBuilt;
pub use item::ItemCrafted;

44
src/item.rs Normal file
View File

@ -0,0 +1,44 @@
use crate::entity::{Entity, Location};
#[derive(Clone)]
pub enum ItemType {
Weapon(Weapon),
Armor(Armor),
}
#[derive(Clone)]
pub struct Item {
pub entity: Entity,
pub name: String,
pub item_type: ItemType,
}
#[derive(Clone)]
pub struct Weapon {
pub damage_base: u32,
pub damage_dice: u32,
pub damage_sides: u32,
}
#[derive(Clone)]
pub struct Armor {
pub armor_class: u32,
}
pub struct ItemGenerator;
impl ItemGenerator {
pub fn generate_item() -> Item {
Item {
entity: Entity { id: 0, loc: Location{ x: 0, y: 0 } },
name: "Sword".to_string(),
item_type: ItemType::Weapon(Weapon {
damage_base: 0,
damage_dice: 1,
damage_sides: 6,
}),
}
}
}

View File

@ -8,6 +8,7 @@ mod site;
mod ui; mod ui;
mod entity; mod entity;
mod game; mod game;
mod item;
use ui::App; use ui::App;
use noise::{Perlin, ScalePoint, Add, NoiseFn, ScaleBias}; use noise::{Perlin, ScalePoint, Add, NoiseFn, ScaleBias};

View File

@ -3,7 +3,8 @@ use std::collections::HashMap;
use rand::prelude::*; use rand::prelude::*;
use crate::entity::{Location, EntityId}; use crate::entity::{Location, EntityId};
use crate::creature::Creature; use crate::creature::{Creature, Profession, CreatureGenerator};
use crate::item::Item;
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,6 +13,7 @@ pub struct GameState {
pub time: Time, pub time: Time,
pub world: World, pub world: World,
pub creatures: HashMap<u32, Creature>, pub creatures: HashMap<u32, Creature>,
pub items: HashMap<u32, Item>,
pub events: Vec<Box<Event>>, pub events: Vec<Box<Event>>,
pub tavern: Option<EntityId>, pub tavern: Option<EntityId>,
} }
@ -52,6 +54,7 @@ impl GameState {
GameState { GameState {
time: Time { time: 0 }, time: Time { time: 0 },
creatures: HashMap::new(), creatures: HashMap::new(),
items: HashMap::new(),
world: world, world: world,
events: events, events: events,
tavern: None, tavern: None,
@ -119,10 +122,10 @@ impl GameState {
time: self.time, time: self.time,
effect: Box::new(PersonGenesis { effect: Box::new(PersonGenesis {
town: town.clone(), town: town.clone(),
person: Creature::new( person: CreatureGenerator::create_human(
self.time.substract_years(rng.gen_range(18..30)), self.time.substract_years(rng.gen_range(18..30)),
Location { x: x as i32, y: y as i32 } Location { x: x as i32, y: y as i32 },
), )
}) })
}); });
} }
@ -137,6 +140,13 @@ impl GameState {
self.creatures.insert(id, creature); self.creatures.insert(id, creature);
} }
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 = id;
self.items.insert(id, item);
id
}
pub fn build_tavern(&mut self) { pub fn build_tavern(&mut self) {
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
loop { loop {
@ -157,6 +167,7 @@ impl GameState {
} }
} }
} }
} }
#[cfg(test)] #[cfg(test)]

View File

@ -238,7 +238,8 @@ impl<'a> App<'a> {
self.conversation.add_line(ChatLine::new( self.conversation.add_line(ChatLine::new(
guest_id.unwrap(), guest_id.unwrap(),
guest.name.clone(), guest.name.clone(),
"Hello, I'm ".to_owned() + &guest.name + ".", "Hello, I'm ".to_owned() + &guest.name + ", nice to meet you! "
+ "I'm a " + &guest.profession.to_string() + ".",
false false
)); ));
self.conversation_scroll = 0; self.conversation_scroll = 0;