Gave NPCs names

This commit is contained in:
Joshua Barretto 2023-04-05 18:07:36 +01:00
parent 74610833d0
commit a7a08763f2
8 changed files with 59 additions and 33 deletions

View File

@ -256,7 +256,7 @@ impl EntityInfo {
self = self.with_name(name);
},
NameKind::Automatic => {
self = self.with_automatic_name();
self = self.with_automatic_name(None);
},
NameKind::Uninit => {},
}
@ -408,7 +408,7 @@ impl EntityInfo {
}
#[must_use]
pub fn with_automatic_name(mut self) -> Self {
pub fn with_automatic_name(mut self, alias: Option<String>) -> Self {
let npc_names = NPC_NAMES.read();
let name = match &self.body {
Body::Humanoid(body) => Some(get_npc_name(&npc_names.humanoid, body.species)),
@ -430,7 +430,23 @@ impl EntityInfo {
Body::Arthropod(body) => Some(get_npc_name(&npc_names.arthropod, body.species)),
_ => None,
};
self.name = name.map(str::to_owned);
self.name = name.map(|name| {
if let Some(alias) = alias {
format!("{alias} ({name})")
} else {
name.to_string()
}
});
self
}
#[must_use]
pub fn with_alias(mut self, alias: String) -> Self {
self.name = Some(if let Some(name) = self.name {
format!("{alias} ({name})")
} else {
alias
});
self
}

View File

@ -287,25 +287,6 @@ pub enum Profession {
Captain,
}
impl Profession {
pub fn to_name(&self) -> String {
match self {
Self::Farmer => "Farmer".to_string(),
Self::Hunter => "Hunter".to_string(),
Self::Merchant => "Merchant".to_string(),
Self::Guard => "Guard".to_string(),
Self::Adventurer(_) => "Adventurer".to_string(),
Self::Blacksmith => "Blacksmith".to_string(),
Self::Chef => "Chef".to_string(),
Self::Alchemist => "Alchemist".to_string(),
Self::Pirate => "Pirate".to_string(),
Self::Cultist => "Cultist".to_string(),
Self::Herbalist => "Herbalist".to_string(),
Self::Captain => "Captain".to_string(),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct WorldSettings {
pub start_time: f64,

View File

@ -1,11 +1,10 @@
use crate::ai::Action;
use crate::{ai::Action, gen::name};
pub use common::rtsim::{NpcId, Profession};
use common::{
comp,
grid::Grid,
rtsim::{
Actor, ChunkResource, FactionId, NpcAction, NpcActivity, NpcMsg, Personality, SiteId,
VehicleId,
Actor, ChunkResource, FactionId, NpcAction, NpcActivity, Personality, SiteId, VehicleId,
},
store::Id,
terrain::TerrainChunkSize,
@ -136,6 +135,9 @@ impl Clone for Npc {
}
impl Npc {
pub const PERM_ENTITY_CONFIG: u32 = 1;
const PERM_NAME: u32 = 0;
pub fn new(seed: u32, wpos: Vec3<f32>, body: comp::Body) -> Self {
Self {
seed,
@ -191,6 +193,8 @@ impl Npc {
}
pub fn rng(&self, perm: u32) -> impl Rng { RandomPerm::new(self.seed.wrapping_add(perm)) }
pub fn get_name(&self) -> String { name::generate(&mut self.rng(Self::PERM_NAME)) }
}
#[derive(Clone, Serialize, Deserialize)]
@ -297,10 +301,11 @@ impl Npcs {
pub fn nearby(
&self,
this_npc: Option<NpcId>,
wpos: Vec2<f32>,
wpos: Vec3<f32>,
radius: f32,
) -> impl Iterator<Item = Actor> + '_ {
let chunk_pos = wpos
.xy()
.as_::<i32>()
.map2(TerrainChunkSize::RECT_SIZE.as_::<i32>(), |e, sz| {
e.div_euclid(sz)
@ -316,7 +321,7 @@ impl Npcs {
.filter(move |npc| {
self.npcs
.get(*npc)
.map_or(false, |npc| npc.wpos.xy().distance_squared(wpos) < r_sqr)
.map_or(false, |npc| npc.wpos.distance_squared(wpos) < r_sqr)
&& Some(*npc) != this_npc
})
.map(Actor::Npc)
@ -328,7 +333,7 @@ impl Npcs {
.get(&chunk_pos)
.map(|characters| {
characters.iter().filter_map(move |(character, c_wpos)| {
if c_wpos.xy().distance_squared(wpos) < r_sqr {
if c_wpos.distance_squared(wpos) < r_sqr {
Some(Actor::Character(*character))
} else {
None

View File

@ -1,4 +1,5 @@
pub mod faction;
pub mod name;
pub mod site;
use crate::data::{

22
rtsim/src/gen/name.rs Normal file
View File

@ -0,0 +1,22 @@
use rand::prelude::*;
pub fn generate(rng: &mut impl Rng) -> String {
let starts = ["ad", "tr", "b", "l", "p", "d", "r", "w", "t", "fr", "s"];
let vowels = ["o", "e", "a", "i"];
let cons = ["m", "d", "st", "n", "y", "gh", "s"];
let mut name = String::new();
name += starts.choose(rng).unwrap();
for _ in 0..thread_rng().gen_range(1..=3) {
name += vowels.choose(rng).unwrap();
name += cons.choose(rng).unwrap();
}
// Make the first letter uppercase (hacky)
name.chars()
.enumerate()
.map(|(i, c)| if i == 0 { c.to_ascii_uppercase() } else { c })
.collect()
}

View File

@ -464,7 +464,7 @@ fn socialize() -> impl Action {
.state
.data()
.npcs
.nearby(Some(ctx.npc_id), ctx.npc.wpos.xy(), 8.0)
.nearby(Some(ctx.npc_id), ctx.npc.wpos, 8.0)
.choose(&mut ctx.rng)
{
just(move |ctx| ctx.controller.say(other, "npc-speech-villager_open")).boxed()
@ -654,7 +654,7 @@ fn villager(visiting_site: SiteId) -> impl Action {
.state
.data()
.npcs
.nearby(Some(ctx.npc_id), ctx.npc.wpos.xy(), 8.0)
.nearby(Some(ctx.npc_id), ctx.npc.wpos, 8.0)
.choose(&mut ctx.rng)
{
(Some(other), &[

View File

@ -130,7 +130,7 @@ fn profession_agent_mark(profession: Option<&Profession>) -> Option<comp::agent:
fn get_npc_entity_info(npc: &Npc, sites: &Sites, index: IndexRef) -> EntityInfo {
let pos = comp::Pos(npc.wpos);
let mut rng = npc.rng(3);
let mut rng = npc.rng(Npc::PERM_ENTITY_CONFIG);
if let Some(ref profession) = npc.profession {
let economy = npc.home.and_then(|home| {
let site = sites.get(home)?.world_site?;
@ -150,6 +150,7 @@ fn get_npc_entity_info(npc: &Npc, sites: &Sites, index: IndexRef) -> EntityInfo
})
.with_economy(economy.as_ref())
.with_lazy_loadout(profession_extra_loadout(npc.profession.as_ref()))
.with_alias(npc.get_name())
.with_agent_mark(profession_agent_mark(npc.profession.as_ref()))
} else {
let config_asset = match npc.body {

View File

@ -973,7 +973,7 @@ fn barnyard(pos: Vec3<f32>, dynamic_rng: &mut impl Rng) -> EntityInfo {
quadruped_small::Body::random_with(dynamic_rng, &species),
))
.with_alignment(comp::Alignment::Tame)
.with_automatic_name()
.with_automatic_name(None)
}
fn bird(pos: Vec3<f32>, dynamic_rng: &mut impl Rng) -> EntityInfo {
@ -990,7 +990,7 @@ fn bird(pos: Vec3<f32>, dynamic_rng: &mut impl Rng) -> EntityInfo {
&species,
)))
.with_alignment(comp::Alignment::Tame)
.with_automatic_name()
.with_automatic_name(None)
}
fn humanoid(pos: Vec3<f32>, economy: &SiteInformation, dynamic_rng: &mut impl Rng) -> EntityInfo {