Added CheckReplacement to event handlers, a function inspired by its namesake in Unreal's Mutator class.

Performs runtime replacement of actor classes.
Takes priority over the "replaces" keyword in both DECORATE and ZScript.
This commit is contained in:
Marisa Kirisame 2018-08-15 17:46:03 +02:00 committed by Christoph Oelckers
parent 7d40edd6ac
commit e18b17217f
4 changed files with 51 additions and 1 deletions

View file

@ -38,6 +38,7 @@
#include "actor.h" #include "actor.h"
#include "c_dispatch.h" #include "c_dispatch.h"
#include "d_net.h" #include "d_net.h"
#include "info.h"
DStaticEventHandler* E_FirstEventHandler = nullptr; DStaticEventHandler* E_FirstEventHandler = nullptr;
DStaticEventHandler* E_LastEventHandler = nullptr; DStaticEventHandler* E_LastEventHandler = nullptr;
@ -506,6 +507,12 @@ bool E_CheckRequireMouse()
return false; return false;
} }
void E_CheckReplacement( PClassActor *replacee, PClassActor **replacement )
{
for (DStaticEventHandler *handler = E_FirstEventHandler; handler; handler = handler->next)
handler->CheckReplacement(replacee,replacement);
}
// normal event loopers (non-special, argument-less) // normal event loopers (non-special, argument-less)
DEFINE_EVENT_LOOPER(RenderFrame) DEFINE_EVENT_LOOPER(RenderFrame)
DEFINE_EVENT_LOOPER(WorldLightning) DEFINE_EVENT_LOOPER(WorldLightning)
@ -571,6 +578,9 @@ DEFINE_FIELD_X(ConsoleEvent, FConsoleEvent, Name)
DEFINE_FIELD_X(ConsoleEvent, FConsoleEvent, Args) DEFINE_FIELD_X(ConsoleEvent, FConsoleEvent, Args)
DEFINE_FIELD_X(ConsoleEvent, FConsoleEvent, IsManual) DEFINE_FIELD_X(ConsoleEvent, FConsoleEvent, IsManual)
DEFINE_FIELD_X(ReplaceEvent, FReplaceEvent, Replacee)
DEFINE_FIELD_X(ReplaceEvent, FReplaceEvent, Replacement)
DEFINE_ACTION_FUNCTION(DStaticEventHandler, SetOrder) DEFINE_ACTION_FUNCTION(DStaticEventHandler, SetOrder)
{ {
PARAM_SELF_PROLOGUE(DStaticEventHandler); PARAM_SELF_PROLOGUE(DStaticEventHandler);
@ -653,6 +663,8 @@ DEFINE_EMPTY_HANDLER(DStaticEventHandler, PostUiTick);
DEFINE_EMPTY_HANDLER(DStaticEventHandler, ConsoleProcess); DEFINE_EMPTY_HANDLER(DStaticEventHandler, ConsoleProcess);
DEFINE_EMPTY_HANDLER(DStaticEventHandler, NetworkProcess); DEFINE_EMPTY_HANDLER(DStaticEventHandler, NetworkProcess);
DEFINE_EMPTY_HANDLER(DStaticEventHandler, CheckReplacement);
// =========================================== // ===========================================
// //
// Event handlers // Event handlers
@ -1123,6 +1135,21 @@ void DStaticEventHandler::ConsoleProcess(int player, FString name, int arg1, int
} }
} }
void DStaticEventHandler::CheckReplacement( PClassActor *replacee, PClassActor **replacement )
{
IFVIRTUAL(DStaticEventHandler, CheckReplacement)
{
// don't create excessive DObjects if not going to be processed anyway
if (func == DStaticEventHandler_CheckReplacement_VMPtr)
return;
FReplaceEvent e = { replacee, *replacement };
VMValue params[2] = { (DStaticEventHandler*)this, &e };
VMCall(func, params, 2, nullptr, 0);
if ( e.Replacement != replacee ) // prevent infinite recursion
*replacement = e.Replacement;
}
}
// //
void DStaticEventHandler::OnDestroy() void DStaticEventHandler::OnDestroy()
{ {

View file

@ -69,6 +69,9 @@ bool E_Responder(const event_t* ev); // splits events into InputProcess and UiPr
// this executes on console/net events. // this executes on console/net events.
void E_Console(int player, FString name, int arg1, int arg2, int arg3, bool manual); void E_Console(int player, FString name, int arg1, int arg2, int arg3, bool manual);
// called when looking up the replacement for an actor class
void E_CheckReplacement(PClassActor* replacee, PClassActor** replacement);
// send networked event. unified function. // send networked event. unified function.
bool E_SendNetworkEvent(FString name, int arg1, int arg2, int arg3, bool manual); bool E_SendNetworkEvent(FString name, int arg1, int arg2, int arg3, bool manual);
@ -166,6 +169,9 @@ public:
// //
void ConsoleProcess(int player, FString name, int arg1, int arg2, int arg3, bool manual); void ConsoleProcess(int player, FString name, int arg1, int arg2, int arg3, bool manual);
//
void CheckReplacement(PClassActor* replacee, PClassActor** replacement);
}; };
class DEventHandler : public DStaticEventHandler class DEventHandler : public DStaticEventHandler
{ {
@ -262,4 +268,10 @@ struct FConsoleEvent
bool IsManual; bool IsManual;
}; };
struct FReplaceEvent
{
PClassActor* Replacee;
PClassActor* Replacement;
};
#endif #endif

View file

@ -425,7 +425,9 @@ PClassActor *PClassActor::GetReplacement(bool lookskill)
lookskill = false; skillrepname = NAME_None; lookskill = false; skillrepname = NAME_None;
} }
} }
auto Replacement = ActorInfo()->Replacement; // [MK] ZScript replacement through Event Handlers, has priority over others
PClassActor *Replacement = ActorInfo()->Replacement;
E_CheckReplacement(this,&Replacement);
if (Replacement == nullptr && (!lookskill || skillrepname == NAME_None)) if (Replacement == nullptr && (!lookskill || skillrepname == NAME_None))
{ {
return this; return this;

View file

@ -285,6 +285,12 @@ struct ConsoleEvent native version("2.4")
native readonly bool IsManual; native readonly bool IsManual;
} }
struct ReplaceEvent native version("2.4")
{
native readonly Class<Actor> Replacee;
native Class<Actor> Replacement;
}
class StaticEventHandler : Object native play version("2.4") class StaticEventHandler : Object native play version("2.4")
{ {
// static event handlers CAN register other static event handlers. // static event handlers CAN register other static event handlers.
@ -329,6 +335,9 @@ class StaticEventHandler : Object native play version("2.4")
virtual native ui void ConsoleProcess(ConsoleEvent e); virtual native ui void ConsoleProcess(ConsoleEvent e);
virtual native void NetworkProcess(ConsoleEvent e); virtual native void NetworkProcess(ConsoleEvent e);
//
virtual native void CheckReplacement(ReplaceEvent e);
// this value will be queried on Register() to decide the relative order of this handler to every other. // this value will be queried on Register() to decide the relative order of this handler to every other.
// this is most useful in UI systems. // this is most useful in UI systems.
// default is 0. // default is 0.