cleaner entity creation

This commit is contained in:
IsseW 2022-08-15 12:02:38 +02:00 committed by Joshua Barretto
parent e7798f2a4e
commit ca02b5e97c
5 changed files with 166 additions and 78 deletions

View File

@ -6,6 +6,7 @@
loot: LootTable("common.loot_tables.creature.humanoid"), loot: LootTable("common.loot_tables.creature.humanoid"),
inventory: ( inventory: (
loadout: Inline(( loadout: Inline((
inherit: Asset("common.loadout.village.merchant"),
active_hands: InHands((Choice([ active_hands: InHands((Choice([
(2, ModularWeapon(tool: Bow, material: Eldwood, hands: None)), (2, ModularWeapon(tool: Bow, material: Eldwood, hands: None)),
(1, ModularWeapon(tool: Sword, material: Steel, hands: None)), (1, ModularWeapon(tool: Sword, material: Steel, hands: None)),

View File

@ -379,6 +379,12 @@ impl EntityInfo {
self.agent_mark = Some(agent_mark); self.agent_mark = Some(agent_mark);
self self
} }
#[must_use]
pub fn with_maybe_agent_mark(mut self, agent_mark: Option<agent::Mark>) -> Self {
self.agent_mark = agent_mark;
self
}
#[must_use] #[must_use]
pub fn with_loot_drop(mut self, loot_drop: LootSpec<String>) -> Self { pub fn with_loot_drop(mut self, loot_drop: LootSpec<String>) -> Self {
@ -441,6 +447,13 @@ impl EntityInfo {
self self
} }
/// map contains price+amount
#[must_use]
pub fn with_maybe_economy(mut self, e: Option<&SiteInformation>) -> Self {
self.trading_information = e.cloned();
self
}
#[must_use] #[must_use]
pub fn with_no_flee(mut self) -> Self { pub fn with_no_flee(mut self) -> Self {
self.no_flee = true; self.no_flee = true;

View File

@ -108,6 +108,14 @@ pub enum Profession {
Adventurer(u32), Adventurer(u32),
#[serde(rename = "5")] #[serde(rename = "5")]
Blacksmith, Blacksmith,
#[serde(rename = "6")]
Chef,
#[serde(rename = "7")]
Alchemist,
#[serde(rename = "8")]
Pirate,
#[serde(rename = "9")]
Cultist,
} }
impl Profession { impl Profession {
@ -119,6 +127,10 @@ impl Profession {
Self::Guard => "Guard".to_string(), Self::Guard => "Guard".to_string(),
Self::Adventurer(_) => "Adventurer".to_string(), Self::Adventurer(_) => "Adventurer".to_string(),
Self::Blacksmith => "Blacksmith".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(),
} }
} }
} }

View File

@ -52,10 +52,12 @@ impl Data {
}; };
for _ in 0..20 { for _ in 0..20 {
this.npcs.create(Npc::new(rng.gen(), rand_wpos(&mut rng)).with_home(site_id).with_profession(match rng.gen_range(0..10) { this.npcs.create(Npc::new(rng.gen(), rand_wpos(&mut rng)).with_home(site_id).with_profession(match rng.gen_range(0..15) {
0 => Profession::Hunter, 0 => Profession::Hunter,
1 => Profession::Blacksmith, 1 => Profession::Blacksmith,
2..=4 => Profession::Farmer, 2 => Profession::Chef,
3 => Profession::Alchemist,
5..=10 => Profession::Farmer,
_ => Profession::Guard, _ => Profession::Guard,
})); }));
} }

View File

@ -9,15 +9,114 @@ use common::{
resources::{DeltaTime, Time, TimeOfDay}, resources::{DeltaTime, Time, TimeOfDay},
rtsim::{RtSimController, RtSimEntity}, rtsim::{RtSimController, RtSimEntity},
slowjob::SlowJobPool, slowjob::SlowJobPool,
trade::Good, trade::{Good, SiteInformation},
LoadoutBuilder, LoadoutBuilder,
SkillSetBuilder, SkillSetBuilder,
}; };
use common_ecs::{Job, Origin, Phase, System}; use common_ecs::{Job, Origin, Phase, System};
use rtsim2::data::npc::{NpcMode, Profession}; use rtsim2::data::{npc::{NpcMode, Profession}, Npc, Sites};
use specs::{Join, Read, ReadExpect, ReadStorage, WriteExpect, WriteStorage}; use specs::{Join, Read, ReadExpect, ReadStorage, WriteExpect, WriteStorage};
use std::{sync::Arc, time::Duration}; use std::{sync::Arc, time::Duration};
use world::site::settlement::{merchant_loadout, trader_loadout}; use world::site::settlement::trader_loadout;
fn humanoid_config(profession: &Profession) -> &'static str {
match profession {
Profession::Farmer | Profession::Hunter => "common.entity.village.villager",
Profession::Merchant => "common.entity.village.merchant",
Profession::Guard => "common.entity.village.guard",
Profession::Adventurer(rank) => match rank {
0 => "common.entity.world.traveler0",
1 => "common.entity.world.traveler1",
2 => "common.entity.world.traveler2",
_ => "common.entity.world.traveler3",
},
Profession::Blacksmith => "common.entity.village.blacksmith",
Profession::Chef => "common.entity.village.chef",
Profession::Alchemist => "common.entity.village.alchemist",
Profession::Pirate => "common.entity.spot.pirate",
Profession::Cultist => "common.entity.dungeon.tier-5.cultist",
}
}
fn loadout_default(loadout: LoadoutBuilder, _economy: Option<&SiteInformation>) -> LoadoutBuilder {
loadout
}
fn merchant_loadout(
loadout_builder: LoadoutBuilder,
economy: Option<&SiteInformation>,
) -> LoadoutBuilder {
trader_loadout(loadout_builder, economy, |_| true)
}
fn farmer_loadout(
loadout_builder: LoadoutBuilder,
economy: Option<&SiteInformation>,
) -> LoadoutBuilder {
trader_loadout(loadout_builder, economy, |good| matches!(good, Good::Food))
}
fn chef_loadout(
loadout_builder: LoadoutBuilder,
economy: Option<&SiteInformation>,
) -> LoadoutBuilder {
trader_loadout(loadout_builder, economy, |good| matches!(good, Good::Food))
}
fn blacksmith_loadout(
loadout_builder: LoadoutBuilder,
economy: Option<&SiteInformation>,
) -> LoadoutBuilder {
trader_loadout(loadout_builder, economy, |good| matches!(good, Good::Tools | Good::Armor))
}
fn profession_extra_loadout(profession: Option<&Profession>) -> fn(LoadoutBuilder, Option<&SiteInformation>) -> LoadoutBuilder {
match profession {
Some(Profession::Merchant) => merchant_loadout,
Some(Profession::Farmer) => farmer_loadout,
Some(Profession::Chef) => chef_loadout,
Some(Profession::Blacksmith) => blacksmith_loadout,
_ => loadout_default,
}
}
fn profession_agent_mark(profession: Option<&Profession>) -> Option<comp::agent::Mark> {
match profession {
Some(Profession::Merchant | Profession::Farmer | Profession::Chef | Profession::Blacksmith) => Some(comp::agent::Mark::Merchant),
Some(Profession::Guard) => Some(comp::agent::Mark::Guard),
_ => None,
}
}
fn get_npc_entity_info(npc: &Npc, sites: &Sites, index: IndexRef) -> EntityInfo {
let body = npc.get_body();
let pos = comp::Pos(npc.wpos);
if let Some(ref profession) = npc.profession {
let economy = npc.home
.and_then(|home| {
let site = sites.get(home)?.world_site?;
index.sites.get(site).trade_information(site.id())
});
let config_asset = humanoid_config(profession);
let entity_config = EntityConfig::from_asset_expect_owned(config_asset).with_body(BodyBuilder::Exact(body));
let mut rng = npc.rng(3);
EntityInfo::at(pos.0)
.with_entity_config(entity_config, Some(config_asset), &mut rng)
.with_alignment(comp::Alignment::Npc)
.with_maybe_economy(economy.as_ref())
.with_lazy_loadout(profession_extra_loadout(npc.profession.as_ref()))
.with_maybe_agent_mark(profession_agent_mark(npc.profession.as_ref()))
} else {
EntityInfo::at(pos.0)
.with_body(body)
.with_alignment(comp::Alignment::Wild)
.with_name("Rtsim NPC")
}
}
#[derive(Default)] #[derive(Default)]
pub struct Sys; pub struct Sys;
@ -46,7 +145,7 @@ impl<'a> System<'a> for Sys {
dt, dt,
time, time,
time_of_day, time_of_day,
mut server_event_bus, server_event_bus,
mut rtsim, mut rtsim,
world, world,
index, index,
@ -82,81 +181,42 @@ impl<'a> System<'a> for Sys {
&& chunk_states.0.get(chunk).map_or(false, |c| c.is_some()) && chunk_states.0.get(chunk).map_or(false, |c| c.is_some())
{ {
npc.mode = NpcMode::Loaded; npc.mode = NpcMode::Loaded;
let body = npc.get_body();
let mut loadout_builder = LoadoutBuilder::from_default(&body);
let mut rng = npc.rng(3);
let economy = npc.home let entity_info = get_npc_entity_info(npc, &data.sites, index.as_index_ref());
.and_then(|home| {
let site = data.sites.get(home)?.world_site?;
index.sites.get(site).trade_information(site.id())
});
if let Some(ref profession) = npc.profession { emitter.emit(match NpcData::from_entity_info(entity_info) {
loadout_builder = match profession { NpcData::Data {
Profession::Guard => loadout_builder pos,
.with_asset_expect("common.loadout.village.guard", &mut rng), stats,
skill_set,
Profession::Merchant => merchant_loadout(loadout_builder, economy.as_ref()), health,
poise,
Profession::Farmer => trader_loadout( inventory,
loadout_builder agent,
.with_asset_expect("common.loadout.village.villager", &mut rng), body,
economy.as_ref(), alignment,
|good| matches!(good, Good::Food), scale,
), loot,
Profession::Blacksmith => trader_loadout( } => ServerEvent::CreateNpc {
loadout_builder pos,
.with_asset_expect("common.loadout.village.blacksmith", &mut rng), stats,
economy.as_ref(), skill_set,
|good| matches!(good, Good::Tools | Good::Armor), health,
), poise,
Profession::Hunter => loadout_builder inventory,
.with_asset_expect("common.loadout.village.villager", &mut rng), agent,
body,
Profession::Adventurer(level) => todo!(), alignment,
}; scale,
} anchor: None,
loot,
let can_speak = npc.profession.is_some(); // TODO: not this rtsim_entity: Some(RtSimEntity(npc_id)),
projectile: None,
let trade_for_site = if let Some(Profession::Merchant | Profession::Farmer | Profession::Blacksmith) = npc.profession {
npc.home.and_then(|home| Some(data.sites.get(home)?.world_site?.id()))
} else {
None
};
let skill_set = SkillSetBuilder::default().build();
let health_level = skill_set
.skill_level(skills::Skill::General(skills::GeneralSkill::HealthIncrease))
.unwrap_or(0);
emitter.emit(ServerEvent::CreateNpc {
pos: comp::Pos(npc.wpos),
stats: comp::Stats::new(npc.profession
.as_ref()
.map(|p| p.to_name())
.unwrap_or_else(|| "Rtsim NPC".to_string())),
skill_set: skill_set,
health: Some(comp::Health::new(body, health_level)),
poise: comp::Poise::new(body),
inventory: comp::Inventory::with_loadout(loadout_builder.build(), body),
body,
agent: Some(comp::Agent::from_body(&body)
.with_behavior(
comp::Behavior::default()
.maybe_with_capabilities(can_speak.then_some(comp::BehaviorCapability::SPEAK))
.with_trade_site(trade_for_site),
)),
alignment: if can_speak {
comp::Alignment::Npc
} else {
comp::Alignment::Wild
}, },
scale: comp::Scale(1.0), // EntityConfig can't represent Waypoints at all
anchor: None, // as of now, and if someone will try to spawn
loot: Default::default(), // rtsim waypoint it is definitely error.
rtsim_entity: Some(RtSimEntity(npc_id)), NpcData::Waypoint(_) => unimplemented!(),
projectile: None,
}); });
} }
} }