From 8af4cf1a37e350fa7b79da6405482177d90e94b9 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sat, 25 Sep 2021 22:30:17 +0300 Subject: [PATCH] Make Rtsim entity config work --- Cargo.lock | 1 + assets/common/entity/wild/aggressive/roc.ron | 11 ++ .../common/entity/wild/peaceful/phoenix.ron | 11 ++ assets/common/entity/world/traveler.ron | 18 +++ assets/common/loadout/world/traveler.ron | 6 - common/src/generation.rs | 4 +- server/Cargo.toml | 1 + server/src/rtsim/entity.rs | 137 ++++++++++++++---- server/src/rtsim/mod.rs | 3 +- server/src/rtsim/tick.rs | 70 +++++---- 10 files changed, 194 insertions(+), 68 deletions(-) create mode 100644 assets/common/entity/wild/aggressive/roc.ron create mode 100644 assets/common/entity/wild/peaceful/phoenix.ron create mode 100644 assets/common/entity/world/traveler.ron diff --git a/Cargo.lock b/Cargo.lock index d592098e18..1faa8b3121 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6195,6 +6195,7 @@ dependencies = [ "slab", "specs", "specs-idvs", + "strum", "tokio", "tracing", "vek", diff --git a/assets/common/entity/wild/aggressive/roc.ron b/assets/common/entity/wild/aggressive/roc.ron new file mode 100644 index 0000000000..89dc8d71b8 --- /dev/null +++ b/assets/common/entity/wild/aggressive/roc.ron @@ -0,0 +1,11 @@ +EntityConfig ( + name: Automatic, + body: RandomWith("roc"), + alignment: Alignment(Enemy), + + loot: LootTable("common.loot_tables.creature.bird_large.roc"), + + hands: Uninit, + + meta: [], +) diff --git a/assets/common/entity/wild/peaceful/phoenix.ron b/assets/common/entity/wild/peaceful/phoenix.ron new file mode 100644 index 0000000000..a434f6c5f6 --- /dev/null +++ b/assets/common/entity/wild/peaceful/phoenix.ron @@ -0,0 +1,11 @@ +EntityConfig ( + name: Automatic, + body: RandomWith("phoenix"), + alignment: Alignment(Wild), + + loot: LootTable("common.loot_tables.creature.bird_large.phoenix"), + + hands: Uninit, + + meta: [], +) diff --git a/assets/common/entity/world/traveler.ron b/assets/common/entity/world/traveler.ron new file mode 100644 index 0000000000..6d9e0fd1b4 --- /dev/null +++ b/assets/common/entity/world/traveler.ron @@ -0,0 +1,18 @@ +EntityConfig ( + name: Automatic, + body: RandomWith("humanoid"), + alignment: Alignment(Npc), + + loot: LootTable("common.loot_tables.creature.humanoid"), + + hands: TwoHanded(Choice([ + (1.0, Some(Item("common.items.weapons.sword.iron-0"))), + (1.0, Some(Item("common.items.weapons.bow.hardwood-2"))), + (1.0, Some(Item("common.items.weapons.axe.steel_axe-0"))), + (1.0, Some(Item("common.items.weapons.staff.frostwood_torch"))), + ])), + + meta: [ + LoadoutAsset("common.loadout.world.traveler"), + ], +) diff --git a/assets/common/loadout/world/traveler.ron b/assets/common/loadout/world/traveler.ron index c4759de18a..1b54dc663c 100644 --- a/assets/common/loadout/world/traveler.ron +++ b/assets/common/loadout/world/traveler.ron @@ -1,10 +1,4 @@ ({ - ActiveMainhand: Choice([ - (1.0, Some(Item("common.items.weapons.sword.iron-0"))), - (1.0, Some(Item("common.items.weapons.bow.hardwood-2"))), - (1.0, Some(Item("common.items.weapons.axe.steel_axe-0"))), - (1.0, Some(Item("common.items.weapons.staff.frostwood_torch"))), - ]), Armor(Chest): Item("common.items.npc_armor.chest.leather_blue"), Armor(Legs): Item("common.items.npc_armor.pants.leather_blue"), diff --git a/common/src/generation.rs b/common/src/generation.rs index b4686a510f..167fe8da67 100644 --- a/common/src/generation.rs +++ b/common/src/generation.rs @@ -110,8 +110,8 @@ pub struct EntityConfig { /// Hands: /// - TwoHanded(ItemSpec) for one 2h or 1h weapon, /// - Paired(ItemSpec) for two 1h weapons aka berserker mode, - /// - Mix { mainhand: ItemSpec, offhand: ItemSpec, - /// } for two different 1h weapons, + /// - Mix { mainhand: ItemSpec, offhand: ItemSpec, } + /// for two different 1h weapons, /// - Uninit which means that tool should be specified somewhere in code, /// Where ItemSpec is taken from loadout_builder module // TODO: better name for this? diff --git a/server/Cargo.toml b/server/Cargo.toml index 2d1ae645b1..0d331fada3 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -28,6 +28,7 @@ network = { package = "veloren-network", path = "../network", features = ["metri specs = { git = "https://github.com/amethyst/specs.git", features = ["shred-derive"], rev = "f985bec5d456f7b0dd8aae99848f9473c2cd9d46" } specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git", rev = "8be2abcddf8f524cb5876e8dd20a7e47cfaf7573" } +strum = { version = "0.21", features = ["derive"] } bincode = "1.3.2" num_cpus = "1.0" tracing = "0.1" diff --git a/server/src/rtsim/entity.rs b/server/src/rtsim/entity.rs index 09230a1c61..8ffff701f5 100644 --- a/server/src/rtsim/entity.rs +++ b/server/src/rtsim/entity.rs @@ -1,9 +1,11 @@ use super::*; use common::{ + comp::inventory::{loadout_builder::make_potion_bag, slot::ArmorSlot}, resources::Time, rtsim::{Memory, MemoryItem}, store::Id, terrain::TerrainGrid, + trade, LoadoutBuilder, }; use rand_distr::{Distribution, Normal}; use std::f32::consts::PI; @@ -24,11 +26,19 @@ pub struct Entity { pub brain: Brain, } +#[derive(Clone, Copy, strum::EnumIter)] pub enum RtSimEntityKind { Random, Cultist, } +const BIRD_LARGE_ROSTER: &[comp::bird_large::Species] = &[ + // Flame Wyvern not incuded until proper introduction + comp::bird_large::Species::Phoenix, + comp::bird_large::Species::Cockatrice, + comp::bird_large::Species::Roc, +]; + const PERM_SPECIES: u32 = 0; const PERM_BODY: u32 = 1; const PERM_LOADOUT: u32 = 2; @@ -54,12 +64,7 @@ impl Entity { .into() }, x if x < 0.50 => { - let species = *(&[ - // Flame Wyvern not incuded until proper introduction - comp::bird_large::Species::Phoenix, - comp::bird_large::Species::Cockatrice, - comp::bird_large::Species::Roc, - ]) + let species = *BIRD_LARGE_ROSTER .choose(&mut self.rng(PERM_SPECIES)) .unwrap(); comp::bird_large::Body::random_with(&mut self.rng(PERM_BODY), &species) @@ -84,31 +89,26 @@ impl Entity { pub fn get_entity_config(&self) -> &str { match self.get_body() { - comp::Body::Humanoid(_) => match self.kind { - RtSimEntityKind::Cultist => "", - RtSimEntityKind::Random => "", - }, - comp::Body::BirdMedium(b) => match b.species { - comp::bird_medium::Species::Duck => "", - comp::bird_medium::Species::Chicken => "", - comp::bird_medium::Species::Goose => "", - comp::bird_medium::Species::Peacock => "", - comp::bird_medium::Species::Eagle => "", - comp::bird_medium::Species::Owl => "", - comp::bird_medium::Species::Parrot => "", - }, - comp::Body::BirdLarge(b) => match b.species { - comp::bird_large::Species::Phoenix => "", - comp::bird_large::Species::Cockatrice => "", - comp::bird_large::Species::Roc => "", - // Wildcard match used here as there is an array above which limits what species are - // used - _ => unimplemented!(), - }, + comp::Body::Humanoid(_) => humanoid_config(self.kind), + comp::Body::BirdMedium(b) => bird_medium_config(b), + comp::Body::BirdLarge(b) => bird_large_config(b), _ => unimplemented!(), } } + pub fn get_adhoc_loadout( + &self, + ) -> fn(LoadoutBuilder, Option<&trade::SiteInformation>) -> LoadoutBuilder { + let body = self.get_body(); + let kind = &self.kind; + // give potions to traveler humanoids or return loadout as is otherwise + if let (comp::Body::Humanoid(_), RtSimEntityKind::Random) = (body, kind) { + |l, _| l.bag(ArmorSlot::Bag1, Some(make_potion_bag(100))) + } else { + |l, _| l + } + } + pub fn tick(&mut self, time: &Time, terrain: &TerrainGrid, world: &World, index: &IndexRef) { self.brain.route = match self.brain.route.clone() { Travel::Lost => { @@ -706,3 +706,86 @@ impl Brain { }) } } + +fn humanoid_config(kind: RtSimEntityKind) -> &'static str { + match kind { + RtSimEntityKind::Cultist => "common.entity.dungeon.tier-5.cultist", + RtSimEntityKind::Random => "common.entity.world.traveler", + } +} + +fn bird_medium_config(body: comp::bird_medium::Body) -> &'static str { + match body.species { + comp::bird_medium::Species::Duck => "common.entity.wild.peaceful.duck", + comp::bird_medium::Species::Chicken => "common.entity.wild.peaceful.chicken", + comp::bird_medium::Species::Goose => "common.entity.wild.peaceful.goose", + comp::bird_medium::Species::Peacock => "common.entity.wild.peaceful.peacock", + comp::bird_medium::Species::Eagle => "common.entity.wild.peaceful.eagle", + comp::bird_medium::Species::Owl => "common.entity.wild.peaceful.owl", + comp::bird_medium::Species::Parrot => "common.entity.wild.peaceful.parrot", + } +} + +fn bird_large_config(body: comp::bird_large::Body) -> &'static str { + match body.species { + comp::bird_large::Species::Phoenix => "common.entity.wild.peaceful.phoenix", + comp::bird_large::Species::Cockatrice => "common.entity.wild.aggressive.cockatrice", + comp::bird_large::Species::Roc => "common.entity.wild.aggressive.roc", + // Wildcard match used here as there is an array above + // which limits what species are used + _ => unimplemented!(), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use common::generation::EntityInfo; + use strum::IntoEnumIterator; + + // Brief, Incomplete and Mostly Wrong Test that all entity configs do exist. + // + // NOTE: Doesn't checks for ships, because we don't produce entity configs + // for them yet. + #[test] + fn test_entity_configs() { + let dummy_pos = Vec3::new(0.0, 0.0, 0.0); + // Bird Large test + for bird_large_species in BIRD_LARGE_ROSTER { + let female_body = comp::bird_large::Body { + species: *bird_large_species, + body_type: comp::bird_large::BodyType::Female, + }; + let male_body = comp::bird_large::Body { + species: *bird_large_species, + body_type: comp::bird_large::BodyType::Male, + }; + + let female_config = bird_large_config(female_body); + std::mem::drop(EntityInfo::at(dummy_pos).with_asset_expect(female_config)); + let male_config = bird_large_config(male_body); + std::mem::drop(EntityInfo::at(dummy_pos).with_asset_expect(male_config)); + } + // Bird Medium test + for bird_med_species in comp::bird_medium::ALL_SPECIES { + let female_body = comp::bird_medium::Body { + species: bird_med_species, + body_type: comp::bird_medium::BodyType::Female, + }; + let male_body = comp::bird_medium::Body { + species: bird_med_species, + body_type: comp::bird_medium::BodyType::Male, + }; + + let female_config = bird_medium_config(female_body); + std::mem::drop(EntityInfo::at(dummy_pos).with_asset_expect(female_config)); + let male_config = bird_medium_config(male_body); + std::mem::drop(EntityInfo::at(dummy_pos).with_asset_expect(male_config)); + } + // Humanoid test + for kind in RtSimEntityKind::iter() { + let config = humanoid_config(kind); + std::mem::drop(EntityInfo::at(dummy_pos).with_asset_expect(config)); + } + } +} diff --git a/server/src/rtsim/mod.rs b/server/src/rtsim/mod.rs index c48a40f514..e81598d810 100644 --- a/server/src/rtsim/mod.rs +++ b/server/src/rtsim/mod.rs @@ -166,7 +166,8 @@ pub fn init( .iter() .filter(|&(site_id, site)| { site.is_settlement() - // TODO: Remove this later, starting town should not be special-cased + // TODO: Remove this later, starting town should not be + // special-cased && spawn_town_id.map_or(false, |spawn_id| spawn_id != site_id) }) .min_by_key(|(_, site)| { diff --git a/server/src/rtsim/tick.rs b/server/src/rtsim/tick.rs index aedbf66b77..abd0657f1b 100644 --- a/server/src/rtsim/tick.rs +++ b/server/src/rtsim/tick.rs @@ -108,44 +108,49 @@ impl<'a> System<'a> for Sys { for id in to_reify { rtsim.reify_entity(id); let entity = &rtsim.entities[id]; - let entity_config = entity.get_entity_config(); let rtsim_entity = Some(RtSimEntity(id)); + let body = entity.get_body(); let spawn_pos = terrain .find_space(entity.pos.map(|e| e.floor() as i32)) .map(|e| e as f32) + Vec3::new(0.5, 0.5, body.flying_height()); + let pos = comp::Pos(spawn_pos); - let mut loadout_rng = entity.loadout_rng(); - let entity_info = NpcData::from_entity_info( - EntityInfo::at(pos.0).with_asset_expect(entity_config), - &mut loadout_rng, - ); - if let NpcData::Data { - pos, - stats, - skill_set, - health, - poise, - loadout, - agent, - // Body discarded here so that species and body type are consistent between - // reifications - body: _, - alignment, - scale, - drop_item, - } = entity_info - { - let event = match body { - comp::Body::Ship(ship) => ServerEvent::CreateShip { + + let event = if let comp::Body::Ship(ship) = body { + ServerEvent::CreateShip { + pos, + ship, + mountable: false, + agent: Some(comp::Agent::from_body(&body)), + rtsim_entity, + } + } else { + let entity_config = entity.get_entity_config(); + let mut loadout_rng = entity.loadout_rng(); + let ad_hoc_loadout = entity.get_adhoc_loadout(); + // Body is rewritten so that body parameters + // are consistent between reifications + let entity_info = EntityInfo::at(pos.0) + .with_asset_expect(entity_config) + .with_lazy_loadout(ad_hoc_loadout) + .with_body(body) + .with_health_scaling(10); + match NpcData::from_entity_info(entity_info, &mut loadout_rng) { + NpcData::Data { pos, - ship, - mountable: false, + stats, + skill_set, + health, + poise, + loadout, agent, - rtsim_entity, - }, - _ => ServerEvent::CreateNpc { + body, + alignment, + scale, + drop_item, + } => ServerEvent::CreateNpc { pos, stats, skill_set, @@ -161,9 +166,10 @@ impl<'a> System<'a> for Sys { rtsim_entity, projectile: None, }, - }; - server_emitter.emit(event); - } + NpcData::Waypoint(_) => unimplemented!(), + } + }; + server_emitter.emit(event); } // Update rtsim with real entity data