Make Rtsim entity config work

This commit is contained in:
juliancoffee 2021-09-25 22:30:17 +03:00
parent 27823ce8fa
commit 8af4cf1a37
10 changed files with 194 additions and 68 deletions

1
Cargo.lock generated
View File

@ -6195,6 +6195,7 @@ dependencies = [
"slab",
"specs",
"specs-idvs",
"strum",
"tokio",
"tracing",
"vek",

View File

@ -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: [],
)

View File

@ -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: [],
)

View File

@ -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"),
],
)

View File

@ -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"),

View File

@ -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?

View File

@ -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"

View File

@ -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));
}
}
}

View File

@ -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)| {

View File

@ -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