mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
added professions, and loadouts
This commit is contained in:
parent
ac0e62df8e
commit
21f9bcb8e2
@ -13,6 +13,15 @@ use common::{
|
||||
use world::util::RandomPerm;
|
||||
pub use common::rtsim::NpcId;
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub enum Profession {
|
||||
Farmer,
|
||||
Hunter,
|
||||
Merchant,
|
||||
Guard,
|
||||
Adventurer(u32),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default)]
|
||||
pub enum NpcMode {
|
||||
/// The NPC is unloaded and is being simulated via rtsim.
|
||||
@ -30,6 +39,9 @@ pub struct Npc {
|
||||
pub seed: u32,
|
||||
pub wpos: Vec3<f32>,
|
||||
|
||||
pub profession: Option<Profession>,
|
||||
pub home: Option<SiteId>,
|
||||
|
||||
// Unpersisted state
|
||||
|
||||
/// (wpos, speed_factor)
|
||||
@ -50,12 +62,24 @@ impl Npc {
|
||||
Self {
|
||||
seed,
|
||||
wpos,
|
||||
profession: None,
|
||||
home: None,
|
||||
target: None,
|
||||
mode: NpcMode::Simulated,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rng(&self, perm: u32) -> impl Rng { RandomPerm::new(self.seed + perm) }
|
||||
pub fn with_profession(mut self, profession: Profession) -> Self {
|
||||
self.profession = Some(profession);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_home(mut self, home: SiteId) -> Self {
|
||||
self.home = Some(home);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn rng(&self, perm: u32) -> impl Rng { RandomPerm::new(self.seed.wrapping_add(perm)) }
|
||||
|
||||
pub fn get_body(&self) -> comp::Body {
|
||||
let species = *(&comp::humanoid::ALL_SPECIES)
|
||||
|
@ -1,7 +1,7 @@
|
||||
pub mod site;
|
||||
|
||||
use crate::data::{
|
||||
npc::{Npcs, Npc},
|
||||
npc::{Npcs, Npc, Profession},
|
||||
site::{Sites, Site},
|
||||
Data,
|
||||
Nature,
|
||||
@ -41,12 +41,20 @@ impl Data {
|
||||
|
||||
// Spawn some test entities at the sites
|
||||
for (site_id, site) in this.sites.iter() {
|
||||
for _ in 0..10 {
|
||||
let rand_wpos = |rng: &mut SmallRng| {
|
||||
let wpos2d = site.wpos.map(|e| e + rng.gen_range(-10..10));
|
||||
let wpos = wpos2d.map(|e| e as f32 + 0.5)
|
||||
.with_z(world.sim().get_alt_approx(wpos2d).unwrap_or(0.0));
|
||||
this.npcs.create(Npc::new(rng.gen(), wpos));
|
||||
wpos2d.map(|e| e as f32 + 0.5)
|
||||
.with_z(world.sim().get_alt_approx(wpos2d).unwrap_or(0.0))
|
||||
};
|
||||
for _ in 0..10 {
|
||||
|
||||
this.npcs.create(Npc::new(rng.gen(), rand_wpos(&mut rng)).with_home(site_id).with_profession(match rng.gen_range(0..10) {
|
||||
0 => Profession::Hunter,
|
||||
1..=4 => Profession::Farmer,
|
||||
_ => Profession::Guard,
|
||||
}));
|
||||
}
|
||||
this.npcs.create(Npc::new(rng.gen(), rand_wpos(&mut rng)).with_home(site_id).with_profession(Profession::Merchant));
|
||||
}
|
||||
|
||||
this
|
||||
|
@ -12,8 +12,9 @@ pub struct Setup;
|
||||
impl Rule for Setup {
|
||||
fn start(rtstate: &mut RtState) -> Result<Self, RuleError> {
|
||||
rtstate.bind::<Self, OnSetup>(|ctx| {
|
||||
let data = &mut *ctx.state.data_mut();
|
||||
// Delete rtsim sites that don't correspond to a world site
|
||||
ctx.state.data_mut().sites.retain(|site_id, site| {
|
||||
data.sites.retain(|site_id, site| {
|
||||
if let Some((world_site_id, _)) = ctx.index.sites
|
||||
.iter()
|
||||
.find(|(_, world_site)| world_site.get_origin() == site.wpos)
|
||||
@ -26,15 +27,19 @@ impl Rule for Setup {
|
||||
}
|
||||
});
|
||||
|
||||
for npc in data.npcs.values_mut() {
|
||||
// TODO: Consider what to do with homeless npcs.
|
||||
npc.home = npc.home.filter(|home| data.sites.contains_key(*home));
|
||||
}
|
||||
|
||||
// Generate rtsim sites for world sites that don't have a corresponding rtsim site yet
|
||||
for (world_site_id, _) in ctx.index.sites.iter() {
|
||||
if !ctx.state.data().sites
|
||||
if !data.sites
|
||||
.values()
|
||||
.any(|site| site.world_site.expect("Rtsim site not assigned to world site") == world_site_id)
|
||||
{
|
||||
warn!("{:?} is new and does not have a corresponding rtsim site. One will now be generated afresh.", world_site_id);
|
||||
ctx.state
|
||||
.data_mut()
|
||||
data
|
||||
.sites
|
||||
.create(Site::generate(world_site_id, ctx.world, ctx.index));
|
||||
}
|
||||
|
@ -3,17 +3,19 @@
|
||||
use super::*;
|
||||
use crate::sys::terrain::NpcData;
|
||||
use common::{
|
||||
comp,
|
||||
comp::{self, inventory::loadout::Loadout},
|
||||
event::{EventBus, ServerEvent},
|
||||
generation::{BodyBuilder, EntityConfig, EntityInfo},
|
||||
resources::{DeltaTime, Time},
|
||||
rtsim::{RtSimController, RtSimEntity},
|
||||
slowjob::SlowJobPool,
|
||||
rtsim::{RtSimEntity, RtSimController},
|
||||
LoadoutBuilder,
|
||||
};
|
||||
use common_ecs::{Job, Origin, Phase, System};
|
||||
use rtsim2::data::npc::NpcMode;
|
||||
use rtsim2::data::npc::{NpcMode, Profession};
|
||||
use specs::{Join, Read, ReadExpect, ReadStorage, WriteExpect, WriteStorage};
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use world::site::settlement::merchant_loadout;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Sys;
|
||||
@ -37,35 +39,78 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
fn run(
|
||||
_job: &mut Job<Self>,
|
||||
(dt, time, mut server_event_bus, mut rtsim, world, index, slow_jobs, positions, rtsim_entities, mut agents): Self::SystemData,
|
||||
(
|
||||
dt,
|
||||
time,
|
||||
mut server_event_bus,
|
||||
mut rtsim,
|
||||
world,
|
||||
index,
|
||||
slow_jobs,
|
||||
positions,
|
||||
rtsim_entities,
|
||||
mut agents,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
let mut emitter = server_event_bus.emitter();
|
||||
let rtsim = &mut *rtsim;
|
||||
|
||||
rtsim.state.tick(&world, index.as_index_ref(), dt.0);
|
||||
|
||||
if rtsim.last_saved.map_or(true, |ls| ls.elapsed() > Duration::from_secs(60)) {
|
||||
if rtsim
|
||||
.last_saved
|
||||
.map_or(true, |ls| ls.elapsed() > Duration::from_secs(60))
|
||||
{
|
||||
rtsim.save(&slow_jobs);
|
||||
}
|
||||
|
||||
let chunk_states = rtsim.state.resource::<ChunkStates>();
|
||||
for (npc_id, npc) in rtsim.state.data_mut().npcs.iter_mut() {
|
||||
let chunk = npc.wpos
|
||||
.xy()
|
||||
.map2(TerrainChunk::RECT_SIZE, |e, sz| (e as i32).div_euclid(sz as i32));
|
||||
let data = &mut *rtsim.state.data_mut();
|
||||
for (npc_id, npc) in data.npcs.iter_mut() {
|
||||
let chunk = npc.wpos.xy().map2(TerrainChunk::RECT_SIZE, |e, sz| {
|
||||
(e as i32).div_euclid(sz as i32)
|
||||
});
|
||||
|
||||
// Load the NPC into the world if it's in a loaded chunk and is not already loaded
|
||||
if matches!(npc.mode, NpcMode::Simulated) && chunk_states.0.get(chunk).map_or(false, |c| c.is_some()) {
|
||||
// Load the NPC into the world if it's in a loaded chunk and is not already
|
||||
// loaded
|
||||
if matches!(npc.mode, NpcMode::Simulated)
|
||||
&& chunk_states.0.get(chunk).map_or(false, |c| c.is_some())
|
||||
{
|
||||
npc.mode = NpcMode::Loaded;
|
||||
|
||||
let body = npc.get_body();
|
||||
let mut loadout_builder = LoadoutBuilder::from_default(&body);
|
||||
let mut rng = npc.rng(3);
|
||||
|
||||
if let Some(ref profession) = npc.profession {
|
||||
loadout_builder = match profession {
|
||||
Profession::Guard => loadout_builder
|
||||
.with_asset_expect("common.loadout.village.guard", &mut rng),
|
||||
|
||||
Profession::Merchant => {
|
||||
merchant_loadout(
|
||||
loadout_builder,
|
||||
npc.home
|
||||
.and_then(|home| {
|
||||
let site = data.sites.get(home)?.world_site?;
|
||||
index.sites.get(site).trade_information(site.id())
|
||||
}).as_ref(),
|
||||
)
|
||||
}
|
||||
|
||||
Profession::Farmer | Profession::Hunter => loadout_builder
|
||||
.with_asset_expect("common.loadout.village.villager", &mut rng),
|
||||
|
||||
Profession::Adventurer(level) => todo!(),
|
||||
};
|
||||
}
|
||||
|
||||
emitter.emit(ServerEvent::CreateNpc {
|
||||
pos: comp::Pos(npc.wpos),
|
||||
stats: comp::Stats::new("Rtsim NPC".to_string()),
|
||||
skill_set: comp::SkillSet::default(),
|
||||
health: None,
|
||||
poise: comp::Poise::new(body),
|
||||
inventory: comp::Inventory::with_empty(),
|
||||
inventory: comp::Inventory::with_loadout(loadout_builder.build(), body),
|
||||
body,
|
||||
agent: Some(comp::Agent::from_body(&body)),
|
||||
alignment: comp::Alignment::Wild,
|
||||
@ -79,10 +124,10 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
|
||||
// Synchronise rtsim NPC with entity data
|
||||
for (pos, rtsim_entity, agent) in (&positions, &rtsim_entities, (&mut agents).maybe()).join() {
|
||||
rtsim
|
||||
.state
|
||||
.data_mut()
|
||||
for (pos, rtsim_entity, agent) in
|
||||
(&positions, &rtsim_entities, (&mut agents).maybe()).join()
|
||||
{
|
||||
data
|
||||
.npcs
|
||||
.get_mut(rtsim_entity.0)
|
||||
.filter(|npc| matches!(npc.mode, NpcMode::Loaded))
|
||||
|
Loading…
Reference in New Issue
Block a user