mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
allow entity -> chunk anchor chains
This commit is contained in:
parent
40fed95760
commit
504e45ebdb
@ -8,6 +8,6 @@
|
||||
loadout: FromBody,
|
||||
),
|
||||
// Added for testing
|
||||
pets: ["common.entity.wild.peaceful.crab"],
|
||||
pets: [("common.entity.wild.peaceful.crab", 5, 10)],
|
||||
meta: [],
|
||||
)
|
||||
|
@ -59,7 +59,7 @@ pub struct NpcBuilder {
|
||||
pub scale: comp::Scale,
|
||||
pub anchor: Option<comp::Anchor>,
|
||||
pub loot: LootSpec<String>,
|
||||
pub pets: Vec<NpcBuilder>,
|
||||
pub pets: Vec<(NpcBuilder, Vec3<f32>)>,
|
||||
pub rtsim_entity: Option<RtSimEntity>,
|
||||
pub projectile: Option<comp::Projectile>,
|
||||
}
|
||||
@ -134,7 +134,7 @@ impl NpcBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_pets(mut self, pets: Vec<NpcBuilder>) -> Self {
|
||||
pub fn with_pets(mut self, pets: Vec<(NpcBuilder, Vec3<f32>)>) -> Self {
|
||||
self.pets = pets;
|
||||
self
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ pub struct EntityConfig {
|
||||
|
||||
/// Pets to spawn with this entity (specified as a list of asset paths)
|
||||
#[serde(default)]
|
||||
pub pets: Vec<String>,
|
||||
pub pets: Vec<(String, usize, usize)>,
|
||||
|
||||
/// Meta Info for optional fields
|
||||
/// Possible fields:
|
||||
@ -320,17 +320,18 @@ impl EntityInfo {
|
||||
// NOTE: set loadout after body, as it's used with default equipement
|
||||
self = self.with_inventory(inventory, config_asset, loadout_rng, time);
|
||||
|
||||
self.pets = pets
|
||||
.into_iter()
|
||||
.map(|pet_config| {
|
||||
EntityInfo::at(self.pos).with_entity_config(
|
||||
EntityConfig::load_expect_cloned(&pet_config),
|
||||
config_asset,
|
||||
loadout_rng,
|
||||
time,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
for (pet_asset, start, end) in pets {
|
||||
let config = EntityConfig::load_expect_cloned(&pet_asset);
|
||||
self.pets
|
||||
.extend((0..loadout_rng.gen_range(start..=end)).map(|_| {
|
||||
EntityInfo::at(self.pos).with_entity_config(
|
||||
config.clone(),
|
||||
config_asset,
|
||||
loadout_rng,
|
||||
time,
|
||||
)
|
||||
}));
|
||||
}
|
||||
|
||||
// Prefer the new configuration, if possible
|
||||
let AgentConfig {
|
||||
@ -692,10 +693,11 @@ mod tests {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn validate_pets(pets: Vec<String>, config_asset: &str) {
|
||||
for pet in pets.into_iter().map(|pet_asset| {
|
||||
EntityConfig::load_cloned(&pet_asset)
|
||||
.expect(&format!("Pet asset path invalid, in {config_asset}"))
|
||||
fn validate_pets(pets: Vec<(String, usize, usize)>, config_asset: &str) {
|
||||
for pet in pets.into_iter().map(|(pet_asset, _, _)| {
|
||||
EntityConfig::load_cloned(&pet_asset).unwrap_or_else(|_| {
|
||||
panic!("Pet asset path invalid: \"{pet_asset}\", in {config_asset}")
|
||||
})
|
||||
}) {
|
||||
if !pet.pets.is_empty() {
|
||||
panic!("Pets must not be owners of pets: {config_asset}");
|
||||
|
@ -40,8 +40,8 @@ use common::{
|
||||
depot,
|
||||
effect::Effect,
|
||||
event::{
|
||||
ClientDisconnectEvent, CreateWaypointEvent, EventBus, ExplosionEvent, GroupManipEvent,
|
||||
InitiateInviteEvent, TamePetEvent,
|
||||
ClientDisconnectEvent, CreateNpcEvent, CreateWaypointEvent, EventBus, ExplosionEvent,
|
||||
GroupManipEvent, InitiateInviteEvent, TamePetEvent,
|
||||
},
|
||||
generation::{EntityConfig, EntityInfo},
|
||||
link::Is,
|
||||
@ -733,63 +733,21 @@ fn handle_make_npc(
|
||||
NpcData::Teleporter(_, _) => {
|
||||
return Err(Content::localized("command-unimplemented-teleporter-spawn"));
|
||||
},
|
||||
NpcData::Data {
|
||||
inventory,
|
||||
pos,
|
||||
stats,
|
||||
skill_set,
|
||||
poise,
|
||||
health,
|
||||
body,
|
||||
agent,
|
||||
alignment,
|
||||
scale,
|
||||
// TODO
|
||||
pets: _,
|
||||
loot,
|
||||
} => {
|
||||
// Spread about spawned npcs
|
||||
let vel = Vec3::new(
|
||||
thread_rng().gen_range(-2.0..3.0),
|
||||
thread_rng().gen_range(-2.0..3.0),
|
||||
10.0,
|
||||
);
|
||||
data @ NpcData::Data { .. } => {
|
||||
let (npc_builder, _pos) = data
|
||||
.to_npc_builder()
|
||||
.expect("We know this NpcData is valid");
|
||||
|
||||
let mut entity_builder = server
|
||||
server
|
||||
.state
|
||||
.create_npc(
|
||||
pos,
|
||||
comp::Ori::default(),
|
||||
stats,
|
||||
skill_set,
|
||||
health,
|
||||
poise,
|
||||
inventory,
|
||||
body,
|
||||
)
|
||||
.with(alignment)
|
||||
.with(scale)
|
||||
.with(comp::Vel(vel));
|
||||
|
||||
if let Some(agent) = agent {
|
||||
entity_builder = entity_builder.with(agent);
|
||||
}
|
||||
|
||||
if let Some(drop_items) = loot.to_items() {
|
||||
entity_builder = entity_builder.with(comp::ItemDrops(drop_items));
|
||||
}
|
||||
|
||||
// Some would say it's a hack, some would say it's incomplete
|
||||
// simulation. But this is what we do to avoid PvP between npc.
|
||||
let npc_group = match alignment {
|
||||
Alignment::Enemy => Some(comp::group::ENEMY),
|
||||
Alignment::Npc | Alignment::Tame => Some(comp::group::NPC),
|
||||
Alignment::Wild | Alignment::Passive | Alignment::Owned(_) => None,
|
||||
};
|
||||
if let Some(group) = npc_group {
|
||||
entity_builder = entity_builder.with(group);
|
||||
}
|
||||
entity_builder.build();
|
||||
.ecs()
|
||||
.read_resource::<EventBus<CreateNpcEvent>>()
|
||||
.emit_now(CreateNpcEvent {
|
||||
pos: comp::Pos(pos),
|
||||
ori: comp::Ori::default(),
|
||||
npc: npc_builder,
|
||||
rider: None,
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -227,10 +227,10 @@ pub fn handle_create_npc(server: &mut Server, mut ev: CreateNpcEvent) -> EcsEnti
|
||||
.expect("We just created these entities");
|
||||
}
|
||||
|
||||
for pet in ev.npc.pets {
|
||||
for (pet, offset) in ev.npc.pets {
|
||||
let pet_entity = handle_create_npc(server, CreateNpcEvent {
|
||||
pos: ev.pos,
|
||||
ori: Ori::default(),
|
||||
pos: comp::Pos(ev.pos.0 + offset),
|
||||
ori: Ori::from_unnormalized_vec(offset).unwrap_or_default(),
|
||||
npc: pet,
|
||||
rider: None,
|
||||
});
|
||||
|
@ -2252,17 +2252,14 @@ pub fn transform_entity(
|
||||
}
|
||||
|
||||
// Spawn pets
|
||||
let position = server
|
||||
.state
|
||||
.ecs()
|
||||
.read_storage::<comp::Pos>()
|
||||
.get(entity)
|
||||
.copied();
|
||||
let position = server.state.read_component_copied::<comp::Pos>(entity);
|
||||
if let Some(pos) = position {
|
||||
for (pet, _pos) in pets.into_iter().filter_map(|pet| pet.to_npc_builder().ok()) {
|
||||
for (pet, offset) in pets.into_iter().filter_map(|(pet, offset)| {
|
||||
pet.to_npc_builder().map(|(pet, _)| (pet, offset)).ok()
|
||||
}) {
|
||||
let pet_entity = handle_create_npc(server, CreateNpcEvent {
|
||||
pos,
|
||||
ori: comp::Ori::default(),
|
||||
pos: comp::Pos(pos.0 + offset),
|
||||
ori: comp::Ori::from_unnormalized_vec(offset).unwrap_or_default(),
|
||||
npc: pet,
|
||||
rider: None,
|
||||
});
|
||||
|
@ -849,7 +849,8 @@ impl Server {
|
||||
Anchor::Entity(anchor_entity) => Some(*anchor_entity),
|
||||
_ => None,
|
||||
})
|
||||
.filter(|anchor_entity| anchors.get(*anchor_entity).is_some())
|
||||
// We allow Anchor::Entity(_) -> Anchor::Chunk(_) chains
|
||||
.filter(|anchor_entity| matches!(anchors.get(*anchor_entity), Some(Anchor::Entity(_))))
|
||||
.collect();
|
||||
drop(anchors);
|
||||
|
||||
|
@ -39,7 +39,7 @@ use specs::{
|
||||
storage::GenericReadStorage, Entities, Entity, Join, LendJoin, ParJoin, Read, ReadExpect,
|
||||
ReadStorage, Write, WriteExpect, WriteStorage,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use std::{f32::consts::TAU, sync::Arc};
|
||||
use vek::*;
|
||||
|
||||
#[cfg(feature = "persistent_world")]
|
||||
@ -210,7 +210,6 @@ impl<'a> System<'a> for Sys {
|
||||
emitters.emit(CreateWaypointEvent(pos));
|
||||
},
|
||||
data @ NpcData::Data { .. } => {
|
||||
// TODO: Investigate anchor chains with pets
|
||||
let (npc_builder, pos) = data
|
||||
.to_npc_builder()
|
||||
.expect("This NpcData is known to be valid");
|
||||
@ -419,7 +418,7 @@ pub enum NpcData {
|
||||
alignment: comp::Alignment,
|
||||
scale: comp::Scale,
|
||||
loot: LootSpec<String>,
|
||||
pets: Vec<NpcData>,
|
||||
pets: Vec<(NpcData, Vec3<f32>)>,
|
||||
},
|
||||
Waypoint(Vec3<f32>),
|
||||
Teleporter(Vec3<f32>, PortalData),
|
||||
@ -561,7 +560,21 @@ impl NpcData {
|
||||
alignment,
|
||||
scale: comp::Scale(scale),
|
||||
loot,
|
||||
pets: pets.into_iter().map(NpcData::from_entity_info).collect(),
|
||||
pets: {
|
||||
let pet_count = pets.len() as f32;
|
||||
pets.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, pet)| {
|
||||
(
|
||||
NpcData::from_entity_info(pet),
|
||||
Vec2::one()
|
||||
.rotated_z(TAU * (i as f32 / pet_count))
|
||||
.with_z(0.0)
|
||||
* ((pet_count * 3.0) / TAU),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -592,13 +605,12 @@ impl NpcData {
|
||||
.with_loot(loot)
|
||||
.with_pets(
|
||||
pets.into_iter()
|
||||
.map(|pet_data| pet_data.to_npc_builder().map(|(npc, _pos)| npc))
|
||||
.map(|(pet, offset)| pet.to_npc_builder().map(|(pet, _)| (pet, offset)))
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
),
|
||||
pos,
|
||||
)),
|
||||
NpcData::Waypoint(_) => Err(self),
|
||||
NpcData::Teleporter(_, _) => Err(self),
|
||||
NpcData::Waypoint(_) | NpcData::Teleporter(_, _) => Err(self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user