diff --git a/src/events.cpp b/src/events.cpp index dc10827dd..80788f072 100755 --- a/src/events.cpp +++ b/src/events.cpp @@ -239,8 +239,27 @@ void E_WorldThingSpawned(AActor* actor) handler->WorldThingSpawned(actor); } +void E_WorldThingDied(AActor* actor, AActor* inflictor) +{ + // don't call anything if actor was destroyed on PostBeginPlay/BeginPlay/whatever. + if (actor->ObjectFlags & OF_EuthanizeMe) + return; + for (DStaticEventHandler* handler = E_FirstEventHandler; handler; handler = handler->next) + handler->WorldThingDied(actor, inflictor); +} + +void E_WorldThingDestroyed(AActor* actor) +{ + // don't call anything if actor was destroyed on PostBeginPlay/BeginPlay/whatever. + if (actor->ObjectFlags & OF_EuthanizeMe) + return; + for (DStaticEventHandler* handler = E_FirstEventHandler; handler; handler = handler->next) + handler->WorldThingDestroyed(actor); +} + // normal event loopers (non-special, argument-less) DEFINE_EVENT_LOOPER(RenderFrame) +DEFINE_EVENT_LOOPER(WorldLightning) // declarations IMPLEMENT_CLASS(DStaticEventHandler, false, false); @@ -258,6 +277,7 @@ DEFINE_FIELD_X(RenderEvent, DRenderEvent, Camera); DEFINE_FIELD_X(WorldEvent, DWorldEvent, IsSaveGame); DEFINE_FIELD_X(WorldEvent, DWorldEvent, Thing); +DEFINE_FIELD_X(WorldEvent, DWorldEvent, Inflictor); DEFINE_ACTION_FUNCTION(DEventHandler, Create) @@ -375,14 +395,24 @@ DEFINE_ACTION_FUNCTION(DStaticEventHandler, Unregister) DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldLoaded) DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldUnloaded) DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldThingSpawned) +DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldThingDied) +DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldThingDestroyed) +DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldLightning) DEFINE_EMPTY_HANDLER(DStaticEventHandler, RenderFrame) +// =========================================== +// +// Event handlers +// +// =========================================== + static DWorldEvent* E_SetupWorldEvent() { static DWorldEvent* e = nullptr; if (!e) e = (DWorldEvent*)RUNTIME_CLASS(DWorldEvent)->CreateNew(); e->IsSaveGame = savegamerestore; e->Thing = nullptr; + e->Inflictor = nullptr; return e; } @@ -426,6 +456,48 @@ void DStaticEventHandler::WorldThingSpawned(AActor* actor) } } +void DStaticEventHandler::WorldThingDied(AActor* actor, AActor* inflictor) +{ + IFVIRTUAL(DStaticEventHandler, WorldThingDied) + { + // don't create excessive DObjects if not going to be processed anyway + if (func == DStaticEventHandler_WorldThingDied_VMPtr) + return; + DWorldEvent* e = E_SetupWorldEvent(); + e->Thing = actor; + e->Inflictor = inflictor; + VMValue params[2] = { (DStaticEventHandler*)this, e }; + GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + } +} + +void DStaticEventHandler::WorldThingDestroyed(AActor* actor) +{ + IFVIRTUAL(DStaticEventHandler, WorldThingDestroyed) + { + // don't create excessive DObjects if not going to be processed anyway + if (func == DStaticEventHandler_WorldThingDestroyed_VMPtr) + return; + DWorldEvent* e = E_SetupWorldEvent(); + e->Thing = actor; + VMValue params[2] = { (DStaticEventHandler*)this, e }; + GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + } +} + +void DStaticEventHandler::WorldLightning() +{ + IFVIRTUAL(DStaticEventHandler, WorldLightning) + { + // don't create excessive DObjects if not going to be processed anyway + if (func == DStaticEventHandler_WorldLightning_VMPtr) + return; + DWorldEvent* e = E_SetupWorldEvent(); + VMValue params[2] = { (DStaticEventHandler*)this, e }; + GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + } +} + static DRenderEvent* E_SetupRenderEvent() { static DRenderEvent* e = nullptr; diff --git a/src/events.h b/src/events.h index a012730e6..5399443a7 100755 --- a/src/events.h +++ b/src/events.h @@ -27,6 +27,12 @@ void E_WorldLoadedUnsafe(); void E_WorldUnloadedUnsafe(); // called around PostBeginPlay of each actor. void E_WorldThingSpawned(AActor* actor); +// called after AActor::Die of each actor. +void E_WorldThingDied(AActor* actor, AActor* inflictor); +// called before AActor::Destroy of each actor. +void E_WorldThingDestroyed(AActor* actor); +// same as ACS SCRIPT_Lightning +void E_WorldLightning(); // called on each render frame once. void E_RenderFrame(); @@ -77,6 +83,9 @@ public: virtual void WorldLoaded(); virtual void WorldUnloaded(); virtual void WorldThingSpawned(AActor*); + virtual void WorldThingDied(AActor*, AActor*); + virtual void WorldThingDestroyed(AActor*); + virtual void WorldLightning(); virtual void RenderFrame(); }; class DEventHandler : public DStaticEventHandler @@ -141,11 +150,14 @@ public: bool IsSaveGame; // for thingspawned, thingdied, thingdestroyed AActor* Thing; + // for thingdied + AActor* Inflictor; // can be null DWorldEvent() { IsSaveGame = false; Thing = nullptr; + Inflictor = nullptr; } void Serialize(FSerializer& arc) override @@ -153,6 +165,7 @@ public: Super::Serialize(arc); arc("IsSaveGame", IsSaveGame); arc("Thing", Thing); + arc("Inflictor", Inflictor); } }; diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index c33cfb080..fbdac3f8f 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -60,6 +60,7 @@ #include "a_morph.h" #include "virtual.h" #include "g_levellocals.h" +#include "events.h" static FRandom pr_obituary ("Obituary"); static FRandom pr_botrespawn ("BotRespawn"); @@ -384,6 +385,9 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags) target = source; } + // [ZZ] Fire WorldThingDied script hook. + E_WorldThingDied(this, inflictor); + // [JM] Fire KILL type scripts for actor. Not needed for players, since they have the "DEATH" script type. if (!player && !(flags7 & MF7_NOKILLSCRIPTS) && ((flags7 & MF7_USEKILLSCRIPTS) || gameinfo.forcekillscripts)) { diff --git a/wadsrc/static/zscript/events.txt b/wadsrc/static/zscript/events.txt index 3a0e8d5eb..68f77ed9e 100755 --- a/wadsrc/static/zscript/events.txt +++ b/wadsrc/static/zscript/events.txt @@ -16,6 +16,8 @@ class WorldEvent : BaseEvent native native readonly bool IsSaveGame; // for thingspawned/thingdied/thingdestroyed native readonly Actor Thing; + // for thingdied. can be null + native readonly Actor Inflictor; } class StaticEventHandler : Object native @@ -33,6 +35,9 @@ class StaticEventHandler : Object native virtual native void WorldLoaded(WorldEvent e); virtual native void WorldUnloaded(WorldEvent e); virtual native void WorldThingSpawned(WorldEvent e); + virtual native void WorldThingDied(WorldEvent e); + virtual native void WorldThingDestroyed(WorldEvent e); + virtual native void WorldLightning(WorldEvent e); // for the sake of completeness. virtual native void RenderFrame(RenderEvent e); }