mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'tormod-personalities' into 'master'
Give NPCs random Big Five personalities. See merge request veloren/veloren!3280
This commit is contained in:
commit
50fd55daef
@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Furniture and waypoints in site2 towns
|
||||
- text input for trading
|
||||
- Themed Site CliffTown, hoodoo/arabic inspired stone structures inhabited by mountaineer NPCs.
|
||||
- NPCs now have rudimentary personalities
|
||||
|
||||
### Changed
|
||||
|
||||
|
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -6727,6 +6727,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"crossbeam-channel",
|
||||
"enumset",
|
||||
"futures-util",
|
||||
"hashbrown 0.11.2",
|
||||
"humantime",
|
||||
|
@ -7,40 +7,88 @@
|
||||
|
||||
vector_map: {
|
||||
"npc.speech.villager": [
|
||||
"Isn't it such a lovely day?",
|
||||
"How are you today?",
|
||||
"Top of the morning to you!",
|
||||
"I love cheese.",
|
||||
],
|
||||
"npc.speech.villager_open": [
|
||||
"I wonder what the Catoblepas thinks when it eats grass.",
|
||||
"What do you think about this weather?",
|
||||
"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?",
|
||||
"What do you suppose makes Glowing Remains glow?",
|
||||
"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.",
|
||||
"I'm just crackers about cheese!",
|
||||
"Won't you come in? We were just about to have some cheese!",
|
||||
"I just can't understand where those Sauroks keep coming from.",
|
||||
],
|
||||
"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.",
|
||||
"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!",
|
||||
"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 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.",
|
||||
],
|
||||
"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.",
|
||||
],
|
||||
"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": [
|
||||
"Sorry, I don't have anything to trade.",
|
||||
"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?"
|
||||
],
|
||||
"npc.speech.merchant_busy": [
|
||||
"Hey, wait your turn.",
|
||||
"Please wait, I'm only one person.",
|
||||
"Do you see the other person in front of you?",
|
||||
"Just a moment, let me finish.",
|
||||
"No cutting in line.",
|
||||
"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": [
|
||||
"Thank you for trading with me!",
|
||||
"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" }
|
||||
slab = "0.4"
|
||||
rand_distr = "0.4.0"
|
||||
enumset = "1.0.8"
|
||||
|
||||
rusqlite = { version = "0.24.2", features = ["array", "vtab", "bundled", "trace"] }
|
||||
refinery = { git = "https://gitlab.com/veloren/refinery.git", rev = "8ecf4b4772d791e6c8c0a3f9b66a7530fad1af3e", features = ["rusqlite"] }
|
||||
|
@ -6,6 +6,7 @@ use common::{
|
||||
terrain::TerrainGrid,
|
||||
trade, LoadoutBuilder,
|
||||
};
|
||||
use enumset::*;
|
||||
use rand_distr::{Distribution, Normal};
|
||||
use std::f32::consts::PI;
|
||||
use tracing::warn;
|
||||
@ -647,7 +648,7 @@ impl Entity {
|
||||
}
|
||||
|
||||
#[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
|
||||
// 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.
|
||||
@ -689,31 +690,157 @@ enum Travel {
|
||||
Idle,
|
||||
}
|
||||
|
||||
impl Default for Travel {
|
||||
fn default() -> Self { Self::Lost }
|
||||
// Based on https://en.wikipedia.org/wiki/Big_Five_personality_traits
|
||||
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 {
|
||||
begin: Option<Id<Site>>,
|
||||
tgt: Option<Id<Site>>,
|
||||
route: Travel,
|
||||
last_visited: Option<Id<Site>>,
|
||||
memories: Vec<Memory>,
|
||||
pub begin: Option<Id<Site>>,
|
||||
pub tgt: Option<Id<Site>>,
|
||||
pub route: Travel,
|
||||
pub last_visited: Option<Id<Site>>,
|
||||
pub memories: Vec<Memory>,
|
||||
pub personality: Personality,
|
||||
}
|
||||
|
||||
impl Brain {
|
||||
pub fn idle() -> Self {
|
||||
pub fn idle(rng: &mut impl Rng) -> Self {
|
||||
Self {
|
||||
begin: None,
|
||||
tgt: None,
|
||||
route: Travel::Idle,
|
||||
last_visited: None,
|
||||
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 {
|
||||
begin: None,
|
||||
tgt: None,
|
||||
@ -725,36 +852,54 @@ impl Brain {
|
||||
},
|
||||
last_visited: None,
|
||||
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 {
|
||||
begin: Some(home_id),
|
||||
tgt: None,
|
||||
route: Travel::Idle,
|
||||
last_visited: None,
|
||||
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 {
|
||||
begin: Some(home_id),
|
||||
tgt: None,
|
||||
route: Travel::Idle,
|
||||
last_visited: None,
|
||||
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 {
|
||||
begin: Some(home_id),
|
||||
tgt: None,
|
||||
route: Travel::Idle,
|
||||
last_visited: None,
|
||||
memories: Vec::new(),
|
||||
personality: Personality::random(rng),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,13 @@
|
||||
#![allow(dead_code)] // TODO: Remove this when rtsim is fleshed out
|
||||
|
||||
mod chunks;
|
||||
mod entity;
|
||||
pub(crate) mod entity;
|
||||
mod load_chunks;
|
||||
mod tick;
|
||||
mod unload_chunks;
|
||||
|
||||
use crate::rtsim::entity::{Personality, Travel};
|
||||
|
||||
use self::chunks::Chunks;
|
||||
use common::{
|
||||
comp,
|
||||
@ -134,7 +136,14 @@ pub fn init(
|
||||
controller: RtSimController::default(),
|
||||
last_time_ticked: 0.0,
|
||||
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
|
||||
@ -190,7 +199,7 @@ pub fn init(
|
||||
controller: RtSimController::default(),
|
||||
last_time_ticked: 0.0,
|
||||
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(),
|
||||
last_time_ticked: 0.0,
|
||||
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(),
|
||||
last_time_ticked: 0.0,
|
||||
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(),
|
||||
last_time_ticked: 0.0,
|
||||
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(),
|
||||
last_time_ticked: 0.0,
|
||||
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;
|
||||
|
||||
use crate::{
|
||||
rtsim::RtSim,
|
||||
rtsim::{entity::PersonalityTrait, RtSim},
|
||||
sys::agent::{
|
||||
consts::{
|
||||
AVG_FOLLOW_DIST, DAMAGE_MEMORY_DURATION, DEFAULT_ATTACK_RANGE, FLEE_DURATION,
|
||||
@ -1010,6 +1010,25 @@ impl<'a> AgentData<'a> {
|
||||
Some(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 =
|
||||
if let Some(tgt_stats) = read_data.stats.get(target) {
|
||||
agent.rtsim_controller.events.push(
|
||||
@ -1024,23 +1043,33 @@ impl<'a> AgentData<'a> {
|
||||
.brain
|
||||
.remembers_character(&tgt_stats.name)
|
||||
{
|
||||
format!(
|
||||
"Greetings fair {}! It has been far too \
|
||||
long since last I saw you. I'm going to \
|
||||
{} right now.",
|
||||
&tgt_stats.name, destination_name
|
||||
)
|
||||
if personality
|
||||
.personality_traits
|
||||
.contains(PersonalityTrait::Extroverted)
|
||||
{
|
||||
format!(
|
||||
"Greetings fair {}! It has been far \
|
||||
too long since last I saw you. I'm \
|
||||
going to {} right now.",
|
||||
&tgt_stats.name, destination_name
|
||||
)
|
||||
} else if personality
|
||||
.personality_traits
|
||||
.contains(PersonalityTrait::Disagreeable)
|
||||
{
|
||||
"Oh. It's you again.".to_string()
|
||||
} else {
|
||||
format!(
|
||||
"Hi again {}! Unfortunately I'm in a \
|
||||
hurry right now. See you!",
|
||||
&tgt_stats.name
|
||||
)
|
||||
}
|
||||
} else {
|
||||
format!(
|
||||
"I'm heading to {}! Want to come along?",
|
||||
destination_name
|
||||
)
|
||||
standard_response_msg()
|
||||
}
|
||||
} else {
|
||||
format!(
|
||||
"I'm heading to {}! Want to come along?",
|
||||
destination_name
|
||||
)
|
||||
standard_response_msg()
|
||||
};
|
||||
self.chat_npc(msg, event_emitter);
|
||||
} else if agent.behavior.can_trade() {
|
||||
@ -1051,13 +1080,81 @@ impl<'a> AgentData<'a> {
|
||||
event_emitter,
|
||||
);
|
||||
} else {
|
||||
self.chat_npc(
|
||||
"npc.speech.merchant_busy",
|
||||
event_emitter,
|
||||
);
|
||||
let default_msg = "npc.speech.merchant_busy";
|
||||
let msg = self.rtsim_entity.map_or(default_msg, |e| {
|
||||
if e.brain
|
||||
.personality
|
||||
.personality_traits
|
||||
.contains(PersonalityTrait::Disagreeable)
|
||||
{
|
||||
"npc.speech.merchant_busy_rude"
|
||||
} else {
|
||||
default_msg
|
||||
}
|
||||
});
|
||||
self.chat_npc(msg, event_emitter);
|
||||
}
|
||||
} else {
|
||||
self.chat_npc("npc.speech.villager", event_emitter);
|
||||
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 {
|
||||
self.chat_npc("npc.speech.villager", event_emitter);
|
||||
}
|
||||
}
|
||||
},
|
||||
Subject::Trade => {
|
||||
|
Loading…
Reference in New Issue
Block a user