mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Allow OnDeath event to handle all actors
This commit is contained in:
parent
ce5ef481e1
commit
d7ba4ecef7
@ -1,5 +1,8 @@
|
|||||||
use crate::{data::NpcId, RtState, Rule};
|
use crate::{RtState, Rule};
|
||||||
use common::resources::{Time, TimeOfDay};
|
use common::{
|
||||||
|
resources::{Time, TimeOfDay},
|
||||||
|
rtsim::Actor,
|
||||||
|
};
|
||||||
use world::{IndexRef, World};
|
use world::{IndexRef, World};
|
||||||
|
|
||||||
pub trait Event: Clone + 'static {}
|
pub trait Event: Clone + 'static {}
|
||||||
@ -27,6 +30,7 @@ impl Event for OnTick {}
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct OnDeath {
|
pub struct OnDeath {
|
||||||
pub npc_id: NpcId,
|
pub actor: Actor,
|
||||||
|
pub killer: Option<Actor>,
|
||||||
}
|
}
|
||||||
impl Event for OnDeath {}
|
impl Event for OnDeath {}
|
||||||
|
@ -45,88 +45,89 @@ fn on_setup(ctx: EventCtx<SimulateNpcs, OnSetup>) {
|
|||||||
|
|
||||||
fn on_death(ctx: EventCtx<SimulateNpcs, OnDeath>) {
|
fn on_death(ctx: EventCtx<SimulateNpcs, OnDeath>) {
|
||||||
let data = &mut *ctx.state.data_mut();
|
let data = &mut *ctx.state.data_mut();
|
||||||
let npc_id = ctx.event.npc_id;
|
|
||||||
let Some(npc) = data.npcs.get(npc_id) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let mut rng = ChaChaRng::from_seed(thread_rng().gen::<[u8; 32]>());
|
|
||||||
|
|
||||||
// Respawn dead NPCs
|
if let Actor::Npc(npc_id) = ctx.event.actor
|
||||||
match npc.body {
|
&& let Some(npc) = data.npcs.get(npc_id)
|
||||||
Body::Humanoid(_) => {
|
{
|
||||||
if let Some((site_id, site)) = data
|
let mut rng = ChaChaRng::from_seed(thread_rng().gen::<[u8; 32]>());
|
||||||
.sites
|
|
||||||
.iter()
|
// Respawn dead NPCs
|
||||||
.filter(|(id, site)| {
|
match npc.body {
|
||||||
Some(*id) != npc.home
|
Body::Humanoid(_) => {
|
||||||
&& site.faction == npc.faction
|
if let Some((site_id, site)) = data
|
||||||
&& site.world_site.map_or(false, |s| {
|
.sites
|
||||||
matches!(ctx.index.sites.get(s).kind, SiteKind::Refactor(_))
|
.iter()
|
||||||
})
|
.filter(|(id, site)| {
|
||||||
})
|
Some(*id) != npc.home
|
||||||
.min_by_key(|(_, site)| site.population.len())
|
&& site.faction == npc.faction
|
||||||
{
|
&& site.world_site.map_or(false, |s| {
|
||||||
let rand_wpos = |rng: &mut ChaChaRng| {
|
matches!(ctx.index.sites.get(s).kind, SiteKind::Refactor(_))
|
||||||
let wpos2d = site.wpos.map(|e| e + rng.gen_range(-10..10));
|
})
|
||||||
wpos2d
|
})
|
||||||
.map(|e| e as f32 + 0.5)
|
.min_by_key(|(_, site)| site.population.len())
|
||||||
.with_z(ctx.world.sim().get_alt_approx(wpos2d).unwrap_or(0.0))
|
{
|
||||||
};
|
let rand_wpos = |rng: &mut ChaChaRng| {
|
||||||
let random_humanoid = |rng: &mut ChaChaRng| {
|
let wpos2d = site.wpos.map(|e| e + rng.gen_range(-10..10));
|
||||||
let species = comp::humanoid::ALL_SPECIES.choose(&mut *rng).unwrap();
|
wpos2d
|
||||||
Body::Humanoid(comp::humanoid::Body::random_with(rng, species))
|
.map(|e| e as f32 + 0.5)
|
||||||
};
|
.with_z(ctx.world.sim().get_alt_approx(wpos2d).unwrap_or(0.0))
|
||||||
data.spawn_npc(
|
};
|
||||||
Npc::new(rng.gen(), rand_wpos(&mut rng), random_humanoid(&mut rng))
|
let random_humanoid = |rng: &mut ChaChaRng| {
|
||||||
.with_personality(Personality::random(&mut rng))
|
let species = comp::humanoid::ALL_SPECIES.choose(&mut *rng).unwrap();
|
||||||
.with_home(site_id)
|
Body::Humanoid(comp::humanoid::Body::random_with(rng, species))
|
||||||
.with_faction(npc.faction)
|
};
|
||||||
.with_profession(npc.profession.clone()),
|
data.spawn_npc(
|
||||||
);
|
Npc::new(rng.gen(), rand_wpos(&mut rng), random_humanoid(&mut rng))
|
||||||
} else {
|
.with_personality(Personality::random(&mut rng))
|
||||||
warn!("No site found for respawning humaniod");
|
.with_home(site_id)
|
||||||
}
|
.with_faction(npc.faction)
|
||||||
},
|
.with_profession(npc.profession.clone()),
|
||||||
Body::BirdLarge(_) => {
|
);
|
||||||
if let Some((site_id, site)) = data
|
} else {
|
||||||
.sites
|
warn!("No site found for respawning humaniod");
|
||||||
.iter()
|
}
|
||||||
.filter(|(id, site)| {
|
},
|
||||||
Some(*id) != npc.home
|
Body::BirdLarge(_) => {
|
||||||
&& site.world_site.map_or(false, |s| {
|
if let Some((site_id, site)) = data
|
||||||
matches!(ctx.index.sites.get(s).kind, SiteKind::Dungeon(_))
|
.sites
|
||||||
})
|
.iter()
|
||||||
})
|
.filter(|(id, site)| {
|
||||||
.min_by_key(|(_, site)| site.population.len())
|
Some(*id) != npc.home
|
||||||
{
|
&& site.world_site.map_or(false, |s| {
|
||||||
let rand_wpos = |rng: &mut ChaChaRng| {
|
matches!(ctx.index.sites.get(s).kind, SiteKind::Dungeon(_))
|
||||||
let wpos2d = site.wpos.map(|e| e + rng.gen_range(-10..10));
|
})
|
||||||
wpos2d
|
})
|
||||||
.map(|e| e as f32 + 0.5)
|
.min_by_key(|(_, site)| site.population.len())
|
||||||
.with_z(ctx.world.sim().get_alt_approx(wpos2d).unwrap_or(0.0))
|
{
|
||||||
};
|
let rand_wpos = |rng: &mut ChaChaRng| {
|
||||||
let species = [
|
let wpos2d = site.wpos.map(|e| e + rng.gen_range(-10..10));
|
||||||
comp::body::bird_large::Species::Phoenix,
|
wpos2d
|
||||||
comp::body::bird_large::Species::Cockatrice,
|
.map(|e| e as f32 + 0.5)
|
||||||
comp::body::bird_large::Species::Roc,
|
.with_z(ctx.world.sim().get_alt_approx(wpos2d).unwrap_or(0.0))
|
||||||
]
|
};
|
||||||
.choose(&mut rng)
|
let species = [
|
||||||
.unwrap();
|
comp::body::bird_large::Species::Phoenix,
|
||||||
data.npcs.create_npc(
|
comp::body::bird_large::Species::Cockatrice,
|
||||||
Npc::new(
|
comp::body::bird_large::Species::Roc,
|
||||||
rng.gen(),
|
]
|
||||||
rand_wpos(&mut rng),
|
.choose(&mut rng)
|
||||||
Body::BirdLarge(comp::body::bird_large::Body::random_with(
|
.unwrap();
|
||||||
&mut rng, species,
|
data.npcs.create_npc(
|
||||||
)),
|
Npc::new(
|
||||||
)
|
rng.gen(),
|
||||||
.with_home(site_id),
|
rand_wpos(&mut rng),
|
||||||
);
|
Body::BirdLarge(comp::body::bird_large::Body::random_with(
|
||||||
} else {
|
&mut rng, species,
|
||||||
warn!("No site found for respawning bird");
|
)),
|
||||||
}
|
)
|
||||||
},
|
.with_home(site_id),
|
||||||
_ => unimplemented!(),
|
);
|
||||||
|
} else {
|
||||||
|
warn!("No site found for respawning bird");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ use crate::{
|
|||||||
event::{EventCtx, OnDeath, OnSetup, OnTick},
|
event::{EventCtx, OnDeath, OnSetup, OnTick},
|
||||||
RtState, Rule, RuleError,
|
RtState, Rule, RuleError,
|
||||||
};
|
};
|
||||||
use common::{grid::Grid, terrain::TerrainChunkSize, vol::RectVolSize};
|
use common::{grid::Grid, rtsim::Actor, terrain::TerrainChunkSize, vol::RectVolSize};
|
||||||
|
|
||||||
pub struct SyncNpcs;
|
pub struct SyncNpcs;
|
||||||
|
|
||||||
@ -33,14 +33,20 @@ fn on_setup(ctx: EventCtx<SyncNpcs, OnSetup>) {
|
|||||||
fn on_death(ctx: EventCtx<SyncNpcs, OnDeath>) {
|
fn on_death(ctx: EventCtx<SyncNpcs, OnDeath>) {
|
||||||
let data = &mut *ctx.state.data_mut();
|
let data = &mut *ctx.state.data_mut();
|
||||||
|
|
||||||
// Remove NPC from home population
|
if let Actor::Npc(npc_id) = ctx.event.actor {
|
||||||
if let Some(home) = data
|
// Remove NPC from home population
|
||||||
.npcs
|
if let Some(home) = data
|
||||||
.get(ctx.event.npc_id)
|
.npcs
|
||||||
.and_then(|npc| npc.home)
|
.get(npc_id)
|
||||||
.and_then(|home| data.sites.get_mut(home))
|
.and_then(|npc| npc.home)
|
||||||
{
|
.and_then(|home| data.sites.get_mut(home))
|
||||||
home.population.remove(&ctx.event.npc_id);
|
{
|
||||||
|
home.population.remove(&npc_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Maybe death should be a marker flag instead of outright deleting the
|
||||||
|
// NPC so that we can still query details about them?
|
||||||
|
data.npcs.remove(npc_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,6 +42,7 @@ use common::{
|
|||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
parse_cmd_args,
|
parse_cmd_args,
|
||||||
resources::{BattleMode, PlayerPhysicsSettings, Secs, Time, TimeOfDay},
|
resources::{BattleMode, PlayerPhysicsSettings, Secs, Time, TimeOfDay},
|
||||||
|
rtsim::Actor,
|
||||||
terrain::{Block, BlockKind, CoordinateConversions, SpriteKind, TerrainChunkSize},
|
terrain::{Block, BlockKind, CoordinateConversions, SpriteKind, TerrainChunkSize},
|
||||||
uid::{Uid, UidAllocator},
|
uid::{Uid, UidAllocator},
|
||||||
vol::ReadVol,
|
vol::ReadVol,
|
||||||
@ -2090,10 +2091,11 @@ fn handle_kill_npcs(
|
|||||||
.copied()
|
.copied()
|
||||||
{
|
{
|
||||||
ecs.write_resource::<crate::rtsim::RtSim>()
|
ecs.write_resource::<crate::rtsim::RtSim>()
|
||||||
.hook_rtsim_entity_delete(
|
.hook_rtsim_actor_death(
|
||||||
&ecs.read_resource::<Arc<world::World>>(),
|
&ecs.read_resource::<Arc<world::World>>(),
|
||||||
ecs.read_resource::<world::IndexOwned>().as_index_ref(),
|
ecs.read_resource::<world::IndexOwned>().as_index_ref(),
|
||||||
rtsim_entity,
|
Actor::Npc(rtsim_entity.0),
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Some(entity)
|
Some(entity)
|
||||||
|
@ -26,7 +26,6 @@ use common::{
|
|||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
outcome::{HealthChangeInfo, Outcome},
|
outcome::{HealthChangeInfo, Outcome},
|
||||||
resources::{Secs, Time},
|
resources::{Secs, Time},
|
||||||
rtsim::RtSimEntity,
|
|
||||||
states::utils::StageSection,
|
states::utils::StageSection,
|
||||||
terrain::{Block, BlockKind, TerrainGrid},
|
terrain::{Block, BlockKind, TerrainGrid},
|
||||||
uid::{Uid, UidAllocator},
|
uid::{Uid, UidAllocator},
|
||||||
@ -519,23 +518,31 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, last_change: Healt
|
|||||||
}
|
}
|
||||||
|
|
||||||
if should_delete {
|
if should_delete {
|
||||||
if let Some(rtsim_entity) = state
|
if let Some(actor) = state.entity_as_actor(entity) {
|
||||||
.ecs()
|
|
||||||
.read_storage::<RtSimEntity>()
|
|
||||||
.get(entity)
|
|
||||||
.copied()
|
|
||||||
{
|
|
||||||
state
|
state
|
||||||
.ecs()
|
.ecs()
|
||||||
.write_resource::<rtsim::RtSim>()
|
.write_resource::<rtsim::RtSim>()
|
||||||
.hook_rtsim_entity_delete(
|
.hook_rtsim_actor_death(
|
||||||
&state.ecs().read_resource::<Arc<world::World>>(),
|
&state.ecs().read_resource::<Arc<world::World>>(),
|
||||||
state
|
state
|
||||||
.ecs()
|
.ecs()
|
||||||
.read_resource::<world::IndexOwned>()
|
.read_resource::<world::IndexOwned>()
|
||||||
.as_index_ref(),
|
.as_index_ref(),
|
||||||
rtsim_entity,
|
actor,
|
||||||
);
|
last_change
|
||||||
|
.by
|
||||||
|
.as_ref()
|
||||||
|
.and_then(
|
||||||
|
|(DamageContributor::Solo(entity_uid)
|
||||||
|
| DamageContributor::Group { entity_uid, .. })| {
|
||||||
|
state
|
||||||
|
.ecs()
|
||||||
|
.read_resource::<UidAllocator>()
|
||||||
|
.retrieve_entity_internal((*entity_uid).into())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.and_then(|killer| state.entity_as_actor(killer)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = state.delete_entity_recorded(entity) {
|
if let Err(e) = state.delete_entity_recorded(entity) {
|
||||||
|
@ -5,7 +5,7 @@ pub mod tick;
|
|||||||
use atomicwrites::{AtomicFile, OverwriteBehavior};
|
use atomicwrites::{AtomicFile, OverwriteBehavior};
|
||||||
use common::{
|
use common::{
|
||||||
grid::Grid,
|
grid::Grid,
|
||||||
rtsim::{ChunkResource, RtSimEntity, RtSimVehicle, WorldSettings},
|
rtsim::{Actor, ChunkResource, RtSimEntity, RtSimVehicle, WorldSettings},
|
||||||
terrain::Block,
|
terrain::Block,
|
||||||
};
|
};
|
||||||
use common_ecs::dispatch;
|
use common_ecs::dispatch;
|
||||||
@ -164,15 +164,14 @@ impl RtSim {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hook_rtsim_entity_delete(
|
pub fn hook_rtsim_actor_death(
|
||||||
&mut self,
|
&mut self,
|
||||||
world: &World,
|
world: &World,
|
||||||
index: IndexRef,
|
index: IndexRef,
|
||||||
entity: RtSimEntity,
|
actor: Actor,
|
||||||
|
killer: Option<Actor>,
|
||||||
) {
|
) {
|
||||||
// Should entity deletion be death? They're not exactly the same thing...
|
self.state.emit(OnDeath { actor, killer }, world, index);
|
||||||
self.state.emit(OnDeath { npc_id: entity.0 }, world, index);
|
|
||||||
self.state.data_mut().npcs.remove(entity.0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save(&mut self, /* slowjob_pool: &SlowJobPool, */ wait_until_finished: bool) {
|
pub fn save(&mut self, /* slowjob_pool: &SlowJobPool, */ wait_until_finished: bool) {
|
||||||
|
@ -25,6 +25,7 @@ use common::{
|
|||||||
link::{Link, LinkHandle},
|
link::{Link, LinkHandle},
|
||||||
mounting::Mounting,
|
mounting::Mounting,
|
||||||
resources::{Secs, Time, TimeOfDay},
|
resources::{Secs, Time, TimeOfDay},
|
||||||
|
rtsim::{Actor, RtSimEntity},
|
||||||
slowjob::SlowJobPool,
|
slowjob::SlowJobPool,
|
||||||
uid::{Uid, UidAllocator},
|
uid::{Uid, UidAllocator},
|
||||||
LoadoutBuilder, ViewDistances,
|
LoadoutBuilder, ViewDistances,
|
||||||
@ -139,6 +140,8 @@ pub trait StateExt {
|
|||||||
&mut self,
|
&mut self,
|
||||||
entity: EcsEntity,
|
entity: EcsEntity,
|
||||||
) -> Result<(), specs::error::WrongGeneration>;
|
) -> Result<(), specs::error::WrongGeneration>;
|
||||||
|
/// Get the given entity as an [`Actor`], if it is one.
|
||||||
|
fn entity_as_actor(&self, entity: EcsEntity) -> Option<Actor>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StateExt for State {
|
impl StateExt for State {
|
||||||
@ -1103,6 +1106,26 @@ impl StateExt for State {
|
|||||||
}
|
}
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn entity_as_actor(&self, entity: EcsEntity) -> Option<Actor> {
|
||||||
|
if let Some(rtsim_entity) = self
|
||||||
|
.ecs()
|
||||||
|
.read_storage::<RtSimEntity>()
|
||||||
|
.get(entity)
|
||||||
|
.copied()
|
||||||
|
{
|
||||||
|
Some(Actor::Npc(rtsim_entity.0))
|
||||||
|
} else if let Some(PresenceKind::Character(character)) = self
|
||||||
|
.ecs()
|
||||||
|
.read_storage::<Presence>()
|
||||||
|
.get(entity)
|
||||||
|
.map(|p| p.kind)
|
||||||
|
{
|
||||||
|
Some(Actor::Character(character))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_to_group(g: &Group, ecs: &specs::World, msg: &comp::ChatMsg) {
|
fn send_to_group(g: &Group, ecs: &specs::World, msg: &comp::ChatMsg) {
|
||||||
|
Loading…
Reference in New Issue
Block a user