mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Give NPCs random Big Five personalities.
This commit is contained in:
parent
73d6d96499
commit
cb88648cca
@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Furniture and waypoints in site2 towns
|
- Furniture and waypoints in site2 towns
|
||||||
- text input for trading
|
- text input for trading
|
||||||
- Themed Site CliffTown, hoodoo/arabic inspired stone structures inhabited by mountaineer NPCs.
|
- Themed Site CliffTown, hoodoo/arabic inspired stone structures inhabited by mountaineer NPCs.
|
||||||
|
- NPCs now have rudimentary personalities
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -6703,6 +6703,7 @@ dependencies = [
|
|||||||
"chrono",
|
"chrono",
|
||||||
"chrono-tz",
|
"chrono-tz",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
|
"enumset",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hashbrown 0.11.2",
|
"hashbrown 0.11.2",
|
||||||
"humantime",
|
"humantime",
|
||||||
|
@ -7,40 +7,88 @@
|
|||||||
|
|
||||||
vector_map: {
|
vector_map: {
|
||||||
"npc.speech.villager": [
|
"npc.speech.villager": [
|
||||||
"Isn't it such a lovely day?",
|
"I love cheese.",
|
||||||
"How are you today?",
|
],
|
||||||
"Top of the morning to you!",
|
"npc.speech.villager_open": [
|
||||||
"I wonder what the Catoblepas thinks when it eats grass.",
|
"I wonder what the Catoblepas thinks when it eats grass.",
|
||||||
"What do you think about this weather?",
|
"What do you suppose makes Glowing Remains glow?",
|
||||||
"Thinking about those dungeons makes me scared. I hope someone will clear them out.",
|
|
||||||
"I'd like to go spelunking in a cave when I'm stronger.",
|
|
||||||
"Have you seen my cat?",
|
|
||||||
"Have you ever heard of the ferocious Land Sharks? I hear they live in deserts.",
|
"Have you ever heard of the ferocious Land Sharks? I hear they live in deserts.",
|
||||||
|
"I wonder what is on the other side of the mountains.",
|
||||||
|
"I left some cheese with my sibling. Now I don't know if it exists or not. I call it Schrödinger's cheese.",
|
||||||
|
"Have you ever caught a firefly?",
|
||||||
"They say shiny gems of all kinds can be found in caves.",
|
"They say shiny gems of all kinds can be found in caves.",
|
||||||
"I'm just crackers about cheese!",
|
"I just can't understand where those Sauroks keep coming from.",
|
||||||
"Won't you come in? We were just about to have some cheese!",
|
],
|
||||||
|
"npc.speech.villager_adventurous": [
|
||||||
|
"I hope to make my own glider someday.",
|
||||||
|
"I'd like to go spelunking in a cave when I'm stronger.",
|
||||||
|
],
|
||||||
|
"npc.speech.villager_closed": [
|
||||||
|
"You're not from around here are you?",
|
||||||
|
"Don't you think our village is the best?",
|
||||||
"They say mushrooms are good for your health. Never eat them myself.",
|
"They say mushrooms are good for your health. Never eat them myself.",
|
||||||
|
"To be, or not to be? I think I'll be a farmer.",
|
||||||
|
],
|
||||||
|
"npc.speech.villager_conscientious": [
|
||||||
|
"I keep busy. There's always something to do.",
|
||||||
|
"I hope it rains soon. Would be good for the crops.",
|
||||||
|
],
|
||||||
|
"npc.speech.villager_busybody": [
|
||||||
|
"People should talk less and work more.",
|
||||||
|
],
|
||||||
|
"npc.speech.villager_unconscientious": [
|
||||||
|
"I think it's time for second breakfast!",
|
||||||
|
"I wish was my house wasn't such a mess. But then I'd have to tidy up! Haha!",
|
||||||
|
"Now where did I leave that thing...",
|
||||||
|
],
|
||||||
|
"npc.speech.villager_extroverted": [
|
||||||
|
"You won't believe what I did this weekend!",
|
||||||
|
"Top of the morning to you!",
|
||||||
|
"What do you think about this weather?",
|
||||||
|
"I'm just crackers about cheese!",
|
||||||
"Don't forget the crackers!",
|
"Don't forget the crackers!",
|
||||||
"I simply adore dwarven cheese. I wish I could make it.",
|
"I simply adore dwarven cheese. I wish I could make it.",
|
||||||
"I wonder what is on the other side of the mountains.",
|
|
||||||
"I hope to make my own glider someday.",
|
|
||||||
"Would you like to see my garden? Okay, maybe some other time.",
|
|
||||||
"Lovely day for a stroll in the woods!",
|
|
||||||
"To be, or not to be? I think I'll be a farmer.",
|
|
||||||
"Don't you think our village is the best?",
|
|
||||||
"What do you suppose makes Glowing Remains glow?",
|
|
||||||
"I think it's time for second breakfast!",
|
|
||||||
"Have you ever caught a firefly?",
|
|
||||||
"I just can't understand where those Sauroks keep coming from.",
|
|
||||||
"I wish someone would keep the wolves away from the village.",
|
|
||||||
"I had a wonderful dream about cheese last night. What does it mean?",
|
"I had a wonderful dream about cheese last night. What does it mean?",
|
||||||
"I left some cheese with my brother. Now I don't know if it exists or not. I call it Schrödinger's cheese.",
|
|
||||||
"I left some cheese with my sister. Now I don't know if it exists or not. I call it Schrödinger's cheese.",
|
|
||||||
"Someone should do something about those cultists. Preferably not me.",
|
|
||||||
"I hope it rains soon. Would be good for the crops.",
|
|
||||||
"I love honey! And I hate bees.",
|
"I love honey! And I hate bees.",
|
||||||
|
],
|
||||||
|
"npc.speech.villager_sociable": [
|
||||||
|
"Won't you come in? We were just about to have some cheese!",
|
||||||
|
"Would you like to see my garden? Okay, maybe some other time.",
|
||||||
|
],
|
||||||
|
"npc.speech.villager_introverted": [
|
||||||
|
"Hi.",
|
||||||
|
"Oh me? I'm nothing special.",
|
||||||
|
],
|
||||||
|
"npc.speech.villager_agreeable": [
|
||||||
|
"How are you today?",
|
||||||
|
"Just tell me if you need anything.",
|
||||||
|
"Have you seen my cat?",
|
||||||
|
],
|
||||||
|
"npc.speech.villager_worried": [
|
||||||
|
"Be careful, alright? There are so many dangers out there.",
|
||||||
|
],
|
||||||
|
"npc.speech.villager_disagreeable": [
|
||||||
|
"I say it like it is. If people don't like that, too bad.",
|
||||||
|
"People are too easily offended.",
|
||||||
|
],
|
||||||
|
"npc.speech.villager_neurotic": [
|
||||||
|
"Thinking about those dungeons makes me scared. I hope someone will clear them out.",
|
||||||
|
"Someone should do something about those cultists. Preferably not me.",
|
||||||
|
"I have the feeling something bad will happen.",
|
||||||
|
"I wish someone would keep the wolves away from the village.",
|
||||||
|
],
|
||||||
|
"npc.speech.villager_sad_loner": [
|
||||||
|
"I'm so lonely.",
|
||||||
|
"... Sorry about this awkward silence. I'm not so good with people.",
|
||||||
|
],
|
||||||
|
"npc.speech.villager_seeker": [
|
||||||
"I want to see the world one day. There's got to be more to life than this village.",
|
"I want to see the world one day. There's got to be more to life than this village.",
|
||||||
],
|
],
|
||||||
|
"npc.speech.villager_stable": [
|
||||||
|
"Isn't it such a lovely day?",
|
||||||
|
"Life's not too bad.",
|
||||||
|
"Lovely day for a stroll in the woods!",
|
||||||
|
],
|
||||||
"npc.speech.villager_decline_trade": [
|
"npc.speech.villager_decline_trade": [
|
||||||
"Sorry, I don't have anything to trade.",
|
"Sorry, I don't have anything to trade.",
|
||||||
"Trade? Like I got anything that may interest you.",
|
"Trade? Like I got anything that may interest you.",
|
||||||
@ -52,13 +100,15 @@
|
|||||||
"I have plenty of goods, Do you want to take a look?"
|
"I have plenty of goods, Do you want to take a look?"
|
||||||
],
|
],
|
||||||
"npc.speech.merchant_busy": [
|
"npc.speech.merchant_busy": [
|
||||||
"Hey, wait your turn.",
|
|
||||||
"Please wait, I'm only one person.",
|
"Please wait, I'm only one person.",
|
||||||
"Do you see the other person in front of you?",
|
|
||||||
"Just a moment, let me finish.",
|
"Just a moment, let me finish.",
|
||||||
"No cutting in line.",
|
|
||||||
"I'm busy, come back later."
|
"I'm busy, come back later."
|
||||||
],
|
],
|
||||||
|
"npc.speech.merchant_busy_rude": [
|
||||||
|
"Hey, wait your turn.",
|
||||||
|
"Do you see the other person in front of you?",
|
||||||
|
"No cutting in line.",
|
||||||
|
],
|
||||||
"npc.speech.merchant_trade_successful": [
|
"npc.speech.merchant_trade_successful": [
|
||||||
"Thank you for trading with me!",
|
"Thank you for trading with me!",
|
||||||
"Thank you!",
|
"Thank you!",
|
||||||
|
@ -57,6 +57,7 @@ portpicker = { git = "https://github.com/xMAC94x/portpicker-rs", rev = "df6b3787
|
|||||||
authc = { git = "https://gitlab.com/veloren/auth.git", rev = "fb3dcbc4962b367253f8f2f92760ef44d2679c9a" }
|
authc = { git = "https://gitlab.com/veloren/auth.git", rev = "fb3dcbc4962b367253f8f2f92760ef44d2679c9a" }
|
||||||
slab = "0.4"
|
slab = "0.4"
|
||||||
rand_distr = "0.4.0"
|
rand_distr = "0.4.0"
|
||||||
|
enumset = "1.0.8"
|
||||||
|
|
||||||
rusqlite = { version = "0.24.2", features = ["array", "vtab", "bundled", "trace"] }
|
rusqlite = { version = "0.24.2", features = ["array", "vtab", "bundled", "trace"] }
|
||||||
refinery = { git = "https://gitlab.com/veloren/refinery.git", rev = "8ecf4b4772d791e6c8c0a3f9b66a7530fad1af3e", features = ["rusqlite"] }
|
refinery = { git = "https://gitlab.com/veloren/refinery.git", rev = "8ecf4b4772d791e6c8c0a3f9b66a7530fad1af3e", features = ["rusqlite"] }
|
||||||
|
@ -6,6 +6,7 @@ use common::{
|
|||||||
terrain::TerrainGrid,
|
terrain::TerrainGrid,
|
||||||
trade, LoadoutBuilder,
|
trade, LoadoutBuilder,
|
||||||
};
|
};
|
||||||
|
use enumset::*;
|
||||||
use rand_distr::{Distribution, Normal};
|
use rand_distr::{Distribution, Normal};
|
||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
@ -647,7 +648,7 @@ impl Entity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum Travel {
|
pub enum Travel {
|
||||||
// The initial state all entities start in, and a fallback for when a state has stopped making
|
// The initial state all entities start in, and a fallback for when a state has stopped making
|
||||||
// sense. Non humanoids will always revert to this state after reaching their goal since the
|
// sense. Non humanoids will always revert to this state after reaching their goal since the
|
||||||
// current site they are in doesn't change their behavior.
|
// current site they are in doesn't change their behavior.
|
||||||
@ -689,31 +690,157 @@ enum Travel {
|
|||||||
Idle,
|
Idle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Travel {
|
// Based on https://en.wikipedia.org/wiki/Big_Five_personality_traits
|
||||||
fn default() -> Self { Self::Lost }
|
pub struct PersonalityBase {
|
||||||
|
openness: u8,
|
||||||
|
conscientiousness: u8,
|
||||||
|
extraversion: u8,
|
||||||
|
agreeableness: u8,
|
||||||
|
neuroticism: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PersonalityBase {
|
||||||
|
/* All thresholds here are arbitrary "seems right" values. The goal is for
|
||||||
|
* most NPCs to have some kind of distinguishing trait - something
|
||||||
|
* interesting about them. We want to avoid Joe Averages. But we also
|
||||||
|
* don't want everyone to be completely weird.
|
||||||
|
*/
|
||||||
|
pub fn to_personality(&self) -> Personality {
|
||||||
|
let mut chat_traits: EnumSet<PersonalityTrait> = EnumSet::new();
|
||||||
|
if self.openness > Personality::HIGH_THRESHOLD {
|
||||||
|
chat_traits.insert(PersonalityTrait::Open);
|
||||||
|
if self.neuroticism < Personality::MID {
|
||||||
|
chat_traits.insert(PersonalityTrait::Adventurous);
|
||||||
|
}
|
||||||
|
} else if self.openness < Personality::LOW_THRESHOLD {
|
||||||
|
chat_traits.insert(PersonalityTrait::Closed);
|
||||||
|
}
|
||||||
|
if self.conscientiousness > Personality::HIGH_THRESHOLD {
|
||||||
|
chat_traits.insert(PersonalityTrait::Conscientious);
|
||||||
|
if self.agreeableness < Personality::LOW_THRESHOLD {
|
||||||
|
chat_traits.insert(PersonalityTrait::Busybody);
|
||||||
|
}
|
||||||
|
} else if self.conscientiousness < Personality::LOW_THRESHOLD {
|
||||||
|
chat_traits.insert(PersonalityTrait::Unconscientious);
|
||||||
|
}
|
||||||
|
if self.extraversion > Personality::HIGH_THRESHOLD {
|
||||||
|
chat_traits.insert(PersonalityTrait::Extroverted);
|
||||||
|
} else if self.extraversion < Personality::LOW_THRESHOLD {
|
||||||
|
chat_traits.insert(PersonalityTrait::Introverted);
|
||||||
|
}
|
||||||
|
if self.agreeableness > Personality::HIGH_THRESHOLD {
|
||||||
|
chat_traits.insert(PersonalityTrait::Agreeable);
|
||||||
|
if self.extraversion > Personality::MID {
|
||||||
|
chat_traits.insert(PersonalityTrait::Sociable);
|
||||||
|
}
|
||||||
|
} else if self.agreeableness < Personality::LOW_THRESHOLD {
|
||||||
|
chat_traits.insert(PersonalityTrait::Disagreeable);
|
||||||
|
}
|
||||||
|
if self.neuroticism > Personality::HIGH_THRESHOLD {
|
||||||
|
chat_traits.insert(PersonalityTrait::Neurotic);
|
||||||
|
if self.openness > Personality::LITTLE_HIGH {
|
||||||
|
chat_traits.insert(PersonalityTrait::Seeker);
|
||||||
|
}
|
||||||
|
if self.agreeableness > Personality::LITTLE_HIGH {
|
||||||
|
chat_traits.insert(PersonalityTrait::Worried);
|
||||||
|
}
|
||||||
|
if self.extraversion < Personality::LITTLE_LOW {
|
||||||
|
chat_traits.insert(PersonalityTrait::SadLoner);
|
||||||
|
}
|
||||||
|
} else if self.neuroticism < Personality::LOW_THRESHOLD {
|
||||||
|
chat_traits.insert(PersonalityTrait::Stable);
|
||||||
|
}
|
||||||
|
Personality {
|
||||||
|
personality_traits: chat_traits,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Personality {
|
||||||
|
pub personality_traits: EnumSet<PersonalityTrait>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(EnumSetType)]
|
||||||
|
pub enum PersonalityTrait {
|
||||||
|
Open,
|
||||||
|
Adventurous,
|
||||||
|
Closed,
|
||||||
|
Conscientious,
|
||||||
|
Busybody,
|
||||||
|
Unconscientious,
|
||||||
|
Extroverted,
|
||||||
|
Introverted,
|
||||||
|
Agreeable,
|
||||||
|
Sociable,
|
||||||
|
Disagreeable,
|
||||||
|
Neurotic,
|
||||||
|
Seeker,
|
||||||
|
Worried,
|
||||||
|
SadLoner,
|
||||||
|
Stable,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Personality {
|
||||||
|
pub const HIGH_THRESHOLD: u8 = Self::MAX - Self::LOW_THRESHOLD;
|
||||||
|
pub const LITTLE_HIGH: u8 = Self::MID + (Self::MAX - Self::MIN) / 20;
|
||||||
|
pub const LITTLE_LOW: u8 = Self::MID - (Self::MAX - Self::MIN) / 20;
|
||||||
|
pub const LOW_THRESHOLD: u8 = (Self::MAX - Self::MIN) / 5 * 2 + Self::MIN;
|
||||||
|
const MAX: u8 = 100;
|
||||||
|
pub const MID: u8 = (Self::MAX - Self::MIN) / 2;
|
||||||
|
const MIN: u8 = 0;
|
||||||
|
|
||||||
|
pub fn random_chat_trait(&self, rng: &mut impl Rng) -> Option<PersonalityTrait> {
|
||||||
|
self.personality_traits.into_iter().choose(rng)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn random_trait_value_bounded(rng: &mut impl Rng, min: u8, max: u8) -> u8 {
|
||||||
|
let max_third = max / 3;
|
||||||
|
let min_third = min / 3;
|
||||||
|
rng.gen_range(min_third..=max_third)
|
||||||
|
+ rng.gen_range(min_third..=max_third)
|
||||||
|
+ rng.gen_range((min - 2 * min_third)..=(max - 2 * max_third))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn random_trait_value(rng: &mut impl Rng) -> u8 {
|
||||||
|
Self::random_trait_value_bounded(rng, Self::MIN, Self::MAX)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn random(rng: &mut impl Rng) -> Personality {
|
||||||
|
let mut random_value =
|
||||||
|
|| rng.gen_range(0..=33) + rng.gen_range(0..=34) + rng.gen_range(0..=33);
|
||||||
|
let base = PersonalityBase {
|
||||||
|
openness: random_value(),
|
||||||
|
conscientiousness: random_value(),
|
||||||
|
extraversion: random_value(),
|
||||||
|
agreeableness: random_value(),
|
||||||
|
neuroticism: random_value(),
|
||||||
|
};
|
||||||
|
base.to_personality()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Brain {
|
pub struct Brain {
|
||||||
begin: Option<Id<Site>>,
|
pub begin: Option<Id<Site>>,
|
||||||
tgt: Option<Id<Site>>,
|
pub tgt: Option<Id<Site>>,
|
||||||
route: Travel,
|
pub route: Travel,
|
||||||
last_visited: Option<Id<Site>>,
|
pub last_visited: Option<Id<Site>>,
|
||||||
memories: Vec<Memory>,
|
pub memories: Vec<Memory>,
|
||||||
|
pub personality: Personality,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Brain {
|
impl Brain {
|
||||||
pub fn idle() -> Self {
|
pub fn idle(rng: &mut impl Rng) -> Self {
|
||||||
Self {
|
Self {
|
||||||
begin: None,
|
begin: None,
|
||||||
tgt: None,
|
tgt: None,
|
||||||
route: Travel::Idle,
|
route: Travel::Idle,
|
||||||
last_visited: None,
|
last_visited: None,
|
||||||
memories: Vec::new(),
|
memories: Vec::new(),
|
||||||
|
personality: Personality::random(rng),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn raid(home_id: Id<Site>, target_id: Id<Site>) -> Self {
|
pub fn raid(home_id: Id<Site>, target_id: Id<Site>, rng: &mut impl Rng) -> Self {
|
||||||
Self {
|
Self {
|
||||||
begin: None,
|
begin: None,
|
||||||
tgt: None,
|
tgt: None,
|
||||||
@ -725,36 +852,54 @@ impl Brain {
|
|||||||
},
|
},
|
||||||
last_visited: None,
|
last_visited: None,
|
||||||
memories: Vec::new(),
|
memories: Vec::new(),
|
||||||
|
personality: Personality::random(rng),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn villager(home_id: Id<Site>) -> Self {
|
pub fn villager(home_id: Id<Site>, rng: &mut impl Rng) -> Self {
|
||||||
Self {
|
Self {
|
||||||
begin: Some(home_id),
|
begin: Some(home_id),
|
||||||
tgt: None,
|
tgt: None,
|
||||||
route: Travel::Idle,
|
route: Travel::Idle,
|
||||||
last_visited: None,
|
last_visited: None,
|
||||||
memories: Vec::new(),
|
memories: Vec::new(),
|
||||||
|
personality: Personality::random(rng),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn merchant(home_id: Id<Site>) -> Self {
|
pub fn merchant(home_id: Id<Site>, rng: &mut impl Rng) -> Self {
|
||||||
|
// Merchants are generally extraverted and agreeable
|
||||||
|
let extraversion_bias = (Personality::MAX - Personality::MIN) / 10 * 3;
|
||||||
|
let extraversion =
|
||||||
|
Personality::random_trait_value_bounded(rng, extraversion_bias, Personality::MAX);
|
||||||
|
let agreeableness_bias = extraversion_bias / 2;
|
||||||
|
let agreeableness =
|
||||||
|
Personality::random_trait_value_bounded(rng, agreeableness_bias, Personality::MAX);
|
||||||
|
let personality_base = PersonalityBase {
|
||||||
|
openness: Personality::random_trait_value(rng),
|
||||||
|
conscientiousness: Personality::random_trait_value(rng),
|
||||||
|
extraversion,
|
||||||
|
agreeableness,
|
||||||
|
neuroticism: Personality::random_trait_value(rng),
|
||||||
|
};
|
||||||
Self {
|
Self {
|
||||||
begin: Some(home_id),
|
begin: Some(home_id),
|
||||||
tgt: None,
|
tgt: None,
|
||||||
route: Travel::Idle,
|
route: Travel::Idle,
|
||||||
last_visited: None,
|
last_visited: None,
|
||||||
memories: Vec::new(),
|
memories: Vec::new(),
|
||||||
|
personality: personality_base.to_personality(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn town_guard(home_id: Id<Site>) -> Self {
|
pub fn town_guard(home_id: Id<Site>, rng: &mut impl Rng) -> Self {
|
||||||
Self {
|
Self {
|
||||||
begin: Some(home_id),
|
begin: Some(home_id),
|
||||||
tgt: None,
|
tgt: None,
|
||||||
route: Travel::Idle,
|
route: Travel::Idle,
|
||||||
last_visited: None,
|
last_visited: None,
|
||||||
memories: Vec::new(),
|
memories: Vec::new(),
|
||||||
|
personality: Personality::random(rng),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
#![allow(dead_code)] // TODO: Remove this when rtsim is fleshed out
|
#![allow(dead_code)] // TODO: Remove this when rtsim is fleshed out
|
||||||
|
|
||||||
mod chunks;
|
mod chunks;
|
||||||
mod entity;
|
pub(crate) mod entity;
|
||||||
mod load_chunks;
|
mod load_chunks;
|
||||||
mod tick;
|
mod tick;
|
||||||
mod unload_chunks;
|
mod unload_chunks;
|
||||||
|
|
||||||
|
use crate::rtsim::entity::{Personality, Travel};
|
||||||
|
|
||||||
use self::chunks::Chunks;
|
use self::chunks::Chunks;
|
||||||
use common::{
|
use common::{
|
||||||
comp,
|
comp,
|
||||||
@ -134,7 +136,14 @@ pub fn init(
|
|||||||
controller: RtSimController::default(),
|
controller: RtSimController::default(),
|
||||||
last_time_ticked: 0.0,
|
last_time_ticked: 0.0,
|
||||||
kind: RtSimEntityKind::Wanderer,
|
kind: RtSimEntityKind::Wanderer,
|
||||||
brain: Default::default(),
|
brain: Brain {
|
||||||
|
begin: None,
|
||||||
|
tgt: None,
|
||||||
|
route: Travel::Lost,
|
||||||
|
last_visited: None,
|
||||||
|
memories: Vec::new(),
|
||||||
|
personality: Personality::random(&mut thread_rng()),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
for (site_id, site) in world
|
for (site_id, site) in world
|
||||||
@ -190,7 +199,7 @@ pub fn init(
|
|||||||
controller: RtSimController::default(),
|
controller: RtSimController::default(),
|
||||||
last_time_ticked: 0.0,
|
last_time_ticked: 0.0,
|
||||||
kind: RtSimEntityKind::Cultist,
|
kind: RtSimEntityKind::Cultist,
|
||||||
brain: Brain::raid(site_id, nearest_village),
|
brain: Brain::raid(site_id, nearest_village, &mut thread_rng()),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -214,7 +223,7 @@ pub fn init(
|
|||||||
controller: RtSimController::default(),
|
controller: RtSimController::default(),
|
||||||
last_time_ticked: 0.0,
|
last_time_ticked: 0.0,
|
||||||
kind: RtSimEntityKind::Villager,
|
kind: RtSimEntityKind::Villager,
|
||||||
brain: Brain::villager(site_id),
|
brain: Brain::villager(site_id, &mut thread_rng()),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,7 +247,7 @@ pub fn init(
|
|||||||
controller: RtSimController::default(),
|
controller: RtSimController::default(),
|
||||||
last_time_ticked: 0.0,
|
last_time_ticked: 0.0,
|
||||||
kind: RtSimEntityKind::TownGuard,
|
kind: RtSimEntityKind::TownGuard,
|
||||||
brain: Brain::town_guard(site_id),
|
brain: Brain::town_guard(site_id, &mut thread_rng()),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,7 +271,7 @@ pub fn init(
|
|||||||
controller: RtSimController::default(),
|
controller: RtSimController::default(),
|
||||||
last_time_ticked: 0.0,
|
last_time_ticked: 0.0,
|
||||||
kind: RtSimEntityKind::Merchant,
|
kind: RtSimEntityKind::Merchant,
|
||||||
brain: Brain::merchant(site_id),
|
brain: Brain::merchant(site_id, &mut thread_rng()),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -287,7 +296,7 @@ pub fn init(
|
|||||||
controller: RtSimController::default(),
|
controller: RtSimController::default(),
|
||||||
last_time_ticked: 0.0,
|
last_time_ticked: 0.0,
|
||||||
kind: RtSimEntityKind::Merchant,
|
kind: RtSimEntityKind::Merchant,
|
||||||
brain: Brain::merchant(site_id),
|
brain: Brain::merchant(site_id, &mut thread_rng()),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -4,7 +4,7 @@ pub mod data;
|
|||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
rtsim::RtSim,
|
rtsim::{entity::PersonalityTrait, RtSim},
|
||||||
sys::agent::{
|
sys::agent::{
|
||||||
consts::{
|
consts::{
|
||||||
AVG_FOLLOW_DIST, DAMAGE_MEMORY_DURATION, DEFAULT_ATTACK_RANGE, FLEE_DURATION,
|
AVG_FOLLOW_DIST, DAMAGE_MEMORY_DURATION, DEFAULT_ATTACK_RANGE, FLEE_DURATION,
|
||||||
@ -1010,6 +1010,25 @@ impl<'a> AgentData<'a> {
|
|||||||
Some(rtsim_entity),
|
Some(rtsim_entity),
|
||||||
) = (&agent.rtsim_controller.travel_to, &self.rtsim_entity)
|
) = (&agent.rtsim_controller.travel_to, &self.rtsim_entity)
|
||||||
{
|
{
|
||||||
|
let personality = &rtsim_entity.brain.personality;
|
||||||
|
let standard_response_msg = || -> String {
|
||||||
|
if personality
|
||||||
|
.personality_traits
|
||||||
|
.contains(PersonalityTrait::Extroverted)
|
||||||
|
{
|
||||||
|
format!(
|
||||||
|
"I'm heading to {}! Want to come along?",
|
||||||
|
destination_name
|
||||||
|
)
|
||||||
|
} else if personality
|
||||||
|
.personality_traits
|
||||||
|
.contains(PersonalityTrait::Disagreeable)
|
||||||
|
{
|
||||||
|
"Hrm.".to_string()
|
||||||
|
} else {
|
||||||
|
"Hello!".to_string()
|
||||||
|
}
|
||||||
|
};
|
||||||
let msg =
|
let msg =
|
||||||
if let Some(tgt_stats) = read_data.stats.get(target) {
|
if let Some(tgt_stats) = read_data.stats.get(target) {
|
||||||
agent.rtsim_controller.events.push(
|
agent.rtsim_controller.events.push(
|
||||||
@ -1023,24 +1042,34 @@ impl<'a> AgentData<'a> {
|
|||||||
if rtsim_entity
|
if rtsim_entity
|
||||||
.brain
|
.brain
|
||||||
.remembers_character(&tgt_stats.name)
|
.remembers_character(&tgt_stats.name)
|
||||||
|
{
|
||||||
|
if personality
|
||||||
|
.personality_traits
|
||||||
|
.contains(PersonalityTrait::Extroverted)
|
||||||
{
|
{
|
||||||
format!(
|
format!(
|
||||||
"Greetings fair {}! It has been far too \
|
"Greetings fair {}! It has been far \
|
||||||
long since last I saw you. I'm going to \
|
too long since last I saw you. I'm \
|
||||||
{} right now.",
|
going to {} right now.",
|
||||||
&tgt_stats.name, destination_name
|
&tgt_stats.name, destination_name
|
||||||
)
|
)
|
||||||
|
} else if personality
|
||||||
|
.personality_traits
|
||||||
|
.contains(PersonalityTrait::Disagreeable)
|
||||||
|
{
|
||||||
|
"Oh. It's you again.".to_string()
|
||||||
} else {
|
} else {
|
||||||
format!(
|
format!(
|
||||||
"I'm heading to {}! Want to come along?",
|
"Hi again {}! Unfortunately I'm in a \
|
||||||
destination_name
|
hurry right now. See you!",
|
||||||
|
&tgt_stats.name
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
format!(
|
standard_response_msg()
|
||||||
"I'm heading to {}! Want to come along?",
|
}
|
||||||
destination_name
|
} else {
|
||||||
)
|
standard_response_msg()
|
||||||
};
|
};
|
||||||
self.chat_npc(msg, event_emitter);
|
self.chat_npc(msg, event_emitter);
|
||||||
} else if agent.behavior.can_trade() {
|
} else if agent.behavior.can_trade() {
|
||||||
@ -1051,14 +1080,82 @@ impl<'a> AgentData<'a> {
|
|||||||
event_emitter,
|
event_emitter,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
self.chat_npc(
|
let default_msg = "npc.speech.merchant_busy";
|
||||||
"npc.speech.merchant_busy",
|
let msg = self.rtsim_entity.map_or(default_msg, |e| {
|
||||||
event_emitter,
|
if e.brain
|
||||||
);
|
.personality
|
||||||
|
.personality_traits
|
||||||
|
.contains(PersonalityTrait::Disagreeable)
|
||||||
|
{
|
||||||
|
"npc.speech.merchant_busy_rude"
|
||||||
|
} else {
|
||||||
|
default_msg
|
||||||
|
}
|
||||||
|
});
|
||||||
|
self.chat_npc(msg, event_emitter);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
if let Some(extreme_trait) =
|
||||||
|
self.rtsim_entity.and_then(|e| {
|
||||||
|
e.brain.personality.random_chat_trait(&mut rng)
|
||||||
|
})
|
||||||
|
{
|
||||||
|
let msg = match extreme_trait {
|
||||||
|
PersonalityTrait::Open => {
|
||||||
|
"npc.speech.villager_open"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Adventurous => {
|
||||||
|
"npc.speech.villager_adventurous"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Closed => {
|
||||||
|
"npc.speech.villager_closed"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Conscientious => {
|
||||||
|
"npc.speech.villager_conscientious"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Busybody => {
|
||||||
|
"npc.speech.villager_busybody"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Unconscientious => {
|
||||||
|
"npc.speech.villager_unconscientious"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Extroverted => {
|
||||||
|
"npc.speech.villager_extroverted"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Introverted => {
|
||||||
|
"npc.speech.villager_introverted"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Agreeable => {
|
||||||
|
"npc.speech.villager_agreeable"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Sociable => {
|
||||||
|
"npc.speech.villager_sociable"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Disagreeable => {
|
||||||
|
"npc.speech.villager_disagreeable"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Neurotic => {
|
||||||
|
"npc.speech.villager_neurotic"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Seeker => {
|
||||||
|
"npc.speech.villager_seeker"
|
||||||
|
},
|
||||||
|
PersonalityTrait::SadLoner => {
|
||||||
|
"npc.speech.villager_sad_loner"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Worried => {
|
||||||
|
"npc.speech.villager_worried"
|
||||||
|
},
|
||||||
|
PersonalityTrait::Stable => {
|
||||||
|
"npc.speech.villager_stable"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
self.chat_npc(msg, event_emitter);
|
||||||
} else {
|
} else {
|
||||||
self.chat_npc("npc.speech.villager", event_emitter);
|
self.chat_npc("npc.speech.villager", event_emitter);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Subject::Trade => {
|
Subject::Trade => {
|
||||||
if agent.behavior.can_trade() {
|
if agent.behavior.can_trade() {
|
||||||
|
Loading…
Reference in New Issue
Block a user