Allow OnDeath event to handle all actors

This commit is contained in:
Joshua Barretto 2023-04-05 23:24:16 +01:00
parent ce5ef481e1
commit d7ba4ecef7
7 changed files with 158 additions and 116 deletions

View File

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

View File

@ -45,10 +45,10 @@ 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 { if let Actor::Npc(npc_id) = ctx.event.actor
return; && let Some(npc) = data.npcs.get(npc_id)
}; {
let mut rng = ChaChaRng::from_seed(thread_rng().gen::<[u8; 32]>()); let mut rng = ChaChaRng::from_seed(thread_rng().gen::<[u8; 32]>());
// Respawn dead NPCs // Respawn dead NPCs
@ -128,6 +128,7 @@ fn on_death(ctx: EventCtx<SimulateNpcs, OnDeath>) {
}, },
_ => unimplemented!(), _ => unimplemented!(),
} }
}
} }
fn on_tick(ctx: EventCtx<SimulateNpcs, OnTick>) { fn on_tick(ctx: EventCtx<SimulateNpcs, OnTick>) {

View File

@ -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();
if let Actor::Npc(npc_id) = ctx.event.actor {
// Remove NPC from home population // Remove NPC from home population
if let Some(home) = data if let Some(home) = data
.npcs .npcs
.get(ctx.event.npc_id) .get(npc_id)
.and_then(|npc| npc.home) .and_then(|npc| npc.home)
.and_then(|home| data.sites.get_mut(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);
} }
} }

View File

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

View File

@ -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,22 +518,30 @@ 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)),
); );
} }

View File

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

View File

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