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 common::resources::{Time, TimeOfDay};
use crate::{RtState, Rule};
use common::{
resources::{Time, TimeOfDay},
rtsim::Actor,
};
use world::{IndexRef, World};
pub trait Event: Clone + 'static {}
@ -27,6 +30,7 @@ impl Event for OnTick {}
#[derive(Clone)]
pub struct OnDeath {
pub npc_id: NpcId,
pub actor: Actor,
pub killer: Option<Actor>,
}
impl Event for OnDeath {}

View File

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

View File

@ -2,7 +2,7 @@ use crate::{
event::{EventCtx, OnDeath, OnSetup, OnTick},
RtState, Rule, RuleError,
};
use common::{grid::Grid, terrain::TerrainChunkSize, vol::RectVolSize};
use common::{grid::Grid, rtsim::Actor, terrain::TerrainChunkSize, vol::RectVolSize};
pub struct SyncNpcs;
@ -33,14 +33,20 @@ fn on_setup(ctx: EventCtx<SyncNpcs, OnSetup>) {
fn on_death(ctx: EventCtx<SyncNpcs, OnDeath>) {
let data = &mut *ctx.state.data_mut();
if let Actor::Npc(npc_id) = ctx.event.actor {
// Remove NPC from home population
if let Some(home) = data
.npcs
.get(ctx.event.npc_id)
.get(npc_id)
.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);
}
}

View File

@ -42,6 +42,7 @@ use common::{
outcome::Outcome,
parse_cmd_args,
resources::{BattleMode, PlayerPhysicsSettings, Secs, Time, TimeOfDay},
rtsim::Actor,
terrain::{Block, BlockKind, CoordinateConversions, SpriteKind, TerrainChunkSize},
uid::{Uid, UidAllocator},
vol::ReadVol,
@ -2090,10 +2091,11 @@ fn handle_kill_npcs(
.copied()
{
ecs.write_resource::<crate::rtsim::RtSim>()
.hook_rtsim_entity_delete(
.hook_rtsim_actor_death(
&ecs.read_resource::<Arc<world::World>>(),
ecs.read_resource::<world::IndexOwned>().as_index_ref(),
rtsim_entity,
Actor::Npc(rtsim_entity.0),
None,
);
}
Some(entity)

View File

@ -26,7 +26,6 @@ use common::{
event::{EventBus, ServerEvent},
outcome::{HealthChangeInfo, Outcome},
resources::{Secs, Time},
rtsim::RtSimEntity,
states::utils::StageSection,
terrain::{Block, BlockKind, TerrainGrid},
uid::{Uid, UidAllocator},
@ -519,22 +518,30 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, last_change: Healt
}
if should_delete {
if let Some(rtsim_entity) = state
.ecs()
.read_storage::<RtSimEntity>()
.get(entity)
.copied()
{
if let Some(actor) = state.entity_as_actor(entity) {
state
.ecs()
.write_resource::<rtsim::RtSim>()
.hook_rtsim_entity_delete(
.hook_rtsim_actor_death(
&state.ecs().read_resource::<Arc<world::World>>(),
state
.ecs()
.read_resource::<world::IndexOwned>()
.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 common::{
grid::Grid,
rtsim::{ChunkResource, RtSimEntity, RtSimVehicle, WorldSettings},
rtsim::{Actor, ChunkResource, RtSimEntity, RtSimVehicle, WorldSettings},
terrain::Block,
};
use common_ecs::dispatch;
@ -164,15 +164,14 @@ impl RtSim {
}
}
pub fn hook_rtsim_entity_delete(
pub fn hook_rtsim_actor_death(
&mut self,
world: &World,
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 { npc_id: entity.0 }, world, index);
self.state.data_mut().npcs.remove(entity.0);
self.state.emit(OnDeath { actor, killer }, world, index);
}
pub fn save(&mut self, /* slowjob_pool: &SlowJobPool, */ wait_until_finished: bool) {

View File

@ -25,6 +25,7 @@ use common::{
link::{Link, LinkHandle},
mounting::Mounting,
resources::{Secs, Time, TimeOfDay},
rtsim::{Actor, RtSimEntity},
slowjob::SlowJobPool,
uid::{Uid, UidAllocator},
LoadoutBuilder, ViewDistances,
@ -139,6 +140,8 @@ pub trait StateExt {
&mut self,
entity: EcsEntity,
) -> 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 {
@ -1103,6 +1106,26 @@ impl StateExt for State {
}
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) {