From 0598c18ad8cc5a90bf08e1626331fc230b2fb288 Mon Sep 17 00:00:00 2001
From: ZZYZX <zzyzx@virtual>
Date: Mon, 30 Jan 2017 08:47:15 +0200
Subject: [PATCH] Added WorldEventHandler with WorldThingSpawned.
 WorldThingDestroyed is not implemented because you already can attach an
 object that would check master's state.

---
 src/events.cpp                   | 48 +++++++++++++++++++---
 src/events.h                     | 70 ++++++++++++++++++++++++++++++--
 src/g_level.cpp                  |  6 +--
 wadsrc/static/zscript/events.txt | 10 ++++-
 4 files changed, 122 insertions(+), 12 deletions(-)

diff --git a/src/events.cpp b/src/events.cpp
index a4ff68836..f1601911e 100755
--- a/src/events.cpp
+++ b/src/events.cpp
@@ -203,12 +203,12 @@ void E_WorldLoaded()
 	}
 }
 
-void E_WorldUnloading()
+void E_WorldUnloaded()
 {
 	for (DStaticEventHandler* handler = E_FirstEventHandler; handler; handler = handler->next)
 	{
 		if (handler->IsStatic() && !handler->isMapScope) continue;
-		handler->WorldUnloading();
+		handler->WorldUnloaded();
 	}
 }
 
@@ -221,22 +221,31 @@ void E_WorldLoadedUnsafe()
 	}
 }
 
-void E_WorldUnloadingUnsafe()
+void E_WorldUnloadedUnsafe()
 {
 	for (DStaticEventHandler* handler = E_FirstEventHandler; handler; handler = handler->next)
 	{
 		if (!handler->IsStatic() || handler->isMapScope) continue;
-		handler->WorldUnloading();
+		handler->WorldUnloaded();
 	}
 }
 
+void E_WorldThingSpawned(AActor* actor)
+{
+	for (DStaticEventHandler* handler = E_FirstEventHandler; handler; handler = handler->next)
+		handler->WorldThingSpawned(actor);
+}
+
+// normal event loopers (non-special, argument-less)
 DEFINE_EVENT_LOOPER(RenderFrame)
 
 // declarations
 IMPLEMENT_CLASS(DStaticEventHandler, false, false);
 IMPLEMENT_CLASS(DStaticRenderEventHandler, false, false);
+IMPLEMENT_CLASS(DStaticWorldEventHandler, false, false);
 IMPLEMENT_CLASS(DEventHandler, false, false);
 IMPLEMENT_CLASS(DRenderEventHandler, false, false);
+IMPLEMENT_CLASS(DWorldEventHandler, false, false);
 
 DEFINE_FIELD_X(StaticRenderEventHandler, DStaticRenderEventHandler, ViewPos);
 DEFINE_FIELD_X(StaticRenderEventHandler, DStaticRenderEventHandler, ViewAngle);
@@ -245,6 +254,9 @@ DEFINE_FIELD_X(StaticRenderEventHandler, DStaticRenderEventHandler, ViewRoll);
 DEFINE_FIELD_X(StaticRenderEventHandler, DStaticRenderEventHandler, FracTic);
 DEFINE_FIELD_X(StaticRenderEventHandler, DStaticRenderEventHandler, Camera);
 
+DEFINE_FIELD_X(StaticWorldEventHandler, DStaticWorldEventHandler, IsSaveGame);
+DEFINE_FIELD_X(StaticWorldEventHandler, DStaticWorldEventHandler, Thing);
+
 
 DEFINE_ACTION_FUNCTION(DEventHandler, Create)
 {
@@ -321,7 +333,8 @@ void cls::funcname(args) \
 }
 
 DEFINE_EVENT_HANDLER(DStaticEventHandler, WorldLoaded,)
-DEFINE_EVENT_HANDLER(DStaticEventHandler, WorldUnloading,)
+DEFINE_EVENT_HANDLER(DStaticEventHandler, WorldUnloaded,)
+DEFINE_EVENT_HANDLER(DStaticEventHandler, WorldThingSpawned, AActor*)
 DEFINE_EVENT_HANDLER(DStaticEventHandler, RenderFrame, )
 
 //
@@ -346,3 +359,28 @@ void DStaticRenderEventHandler::RenderFrame()
 	Setup();
 	Super::RenderFrame();
 }
+
+void DStaticWorldEventHandler::Setup()
+{
+	IsSaveGame = savegamerestore;
+	Thing = nullptr;
+}
+
+void DStaticWorldEventHandler::WorldLoaded()
+{
+	Setup();
+	Super::WorldLoaded();
+}
+
+void DStaticWorldEventHandler::WorldUnloaded()
+{
+	Setup();
+	Super::WorldUnloaded();
+}
+
+void DStaticWorldEventHandler::WorldThingSpawned(AActor* actor)
+{
+	Setup();
+	Thing = actor;
+	Super::WorldThingSpawned(actor);
+}
\ No newline at end of file
diff --git a/src/events.h b/src/events.h
index 172d14485..fa9b16c32 100755
--- a/src/events.h
+++ b/src/events.h
@@ -20,17 +20,25 @@ void E_InitStaticHandlers(bool map);
 // called right after the map has loaded (approximately same time as OPEN ACS scripts)
 void E_WorldLoaded();
 // called when the map is about to unload (approximately same time as UNLOADING ACS scripts)
-void E_WorldUnloading();
+void E_WorldUnloaded();
 // called right after the map has loaded (every time, UNSAFE VERSION)
 void E_WorldLoadedUnsafe();
 // called right before the map is unloaded (every time, UNSAFE VERSION)
-void E_WorldUnloadingUnsafe();
+void E_WorldUnloadedUnsafe();
+// called around PostBeginPlay of each actor.
+void E_WorldThingSpawned(AActor* actor);
 // called on each render frame once.
 void E_RenderFrame();
 
 // serialization stuff
 void E_SerializeEvents(FSerializer& arc);
 
+// ==============================================
+//
+//  EventHandler - base class
+//
+// ==============================================
+
 class DStaticEventHandler : public DObject // make it a part of normal GC process
 {
 	DECLARE_CLASS(DStaticEventHandler, DObject)
@@ -67,7 +75,8 @@ public:
 	void OnDestroy() override;
 
 	virtual void WorldLoaded();
-	virtual void WorldUnloading();
+	virtual void WorldUnloaded();
+	virtual void WorldThingSpawned(AActor*);
 	virtual void RenderFrame();
 };
 class DEventHandler : public DStaticEventHandler
@@ -78,6 +87,13 @@ public:
 };
 extern DStaticEventHandler* E_FirstEventHandler;
 
+
+// ==============================================
+//
+//  RenderEventHandler - for renderer events
+//
+// ==============================================
+
 class DStaticRenderEventHandler : public DStaticEventHandler
 {
 	DECLARE_CLASS(DStaticRenderEventHandler, DStaticEventHandler)
@@ -90,6 +106,12 @@ public:
 	double FracTic; // 0..1 value that describes where we are inside the current gametic, render-wise.
 	AActor* Camera;
 
+	DStaticRenderEventHandler()
+	{
+		FracTic = 0;
+		Camera = nullptr;
+	}
+
 	// serialization handler for our local stuff
 	void Serialize(FSerializer& arc) override
 	{
@@ -114,4 +136,46 @@ public:
 	bool IsStatic() override { return false; }
 };
 
+// ==============================================
+//
+//  WorldEventHandler - for world events
+//
+// ==============================================
+class DStaticWorldEventHandler : public DStaticEventHandler
+{
+	DECLARE_CLASS(DStaticWorldEventHandler, DStaticEventHandler)
+public:
+	// for WorldLoaded, WorldUnloaded.
+	bool IsSaveGame; // this will be true if world event was triggered during savegame loading.
+	// for WorldThingSpawned
+	AActor* Thing;
+
+	DStaticWorldEventHandler()
+	{
+		IsSaveGame = false;
+		Thing = nullptr;
+	}
+
+	void Serialize(FSerializer& arc) override
+	{
+		Super::Serialize(arc);
+		arc("IsSaveGame", IsSaveGame);
+		arc("Thing", Thing);
+	}
+
+	void WorldLoaded() override;
+	void WorldUnloaded() override;
+	void WorldThingSpawned(AActor*) override;
+
+private:
+	void Setup();
+};
+// not sure if anyone wants non-static world handler, but here it is, just in case.
+class DWorldEventHandler : public DStaticWorldEventHandler
+{
+	DECLARE_CLASS(DWorldEventHandler, DStaticWorldEventHandler)
+public:
+	bool IsStatic() override { return false; }
+};
+
 #endif
\ No newline at end of file
diff --git a/src/g_level.cpp b/src/g_level.cpp
index 0310b3846..19c397437 100644
--- a/src/g_level.cpp
+++ b/src/g_level.cpp
@@ -409,7 +409,7 @@ void G_InitNew (const char *mapname, bool bTitleLevel)
 
 	// did we have any level before?
 	if (level.info != nullptr)
-		E_WorldUnloadingUnsafe();
+		E_WorldUnloadedUnsafe();
 
 	if (!savegamerestore)
 	{
@@ -661,9 +661,9 @@ void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill
 	unloading = true;
 	FBehavior::StaticStartTypedScripts (SCRIPT_Unloading, NULL, false, 0, true);
 	// [ZZ] safe world unload
-	E_WorldUnloading();
+	E_WorldUnloaded();
 	// [ZZ] unsafe world unload (changemap != map)
-	E_WorldUnloadingUnsafe();
+	E_WorldUnloadedUnsafe();
 	unloading = false;
 
 	STAT_ChangeLevel(nextlevel);
diff --git a/wadsrc/static/zscript/events.txt b/wadsrc/static/zscript/events.txt
index c604101ce..e3ee54025 100755
--- a/wadsrc/static/zscript/events.txt
+++ b/wadsrc/static/zscript/events.txt
@@ -1,7 +1,8 @@
 class StaticEventHandler : Object native
 {
 	virtual native void WorldLoaded();
-	virtual native void WorldUnloading();
+	virtual native void WorldUnloaded();
+    virtual native void WorldThingSpawned();
 
 	virtual native void RenderFrame();
 }
@@ -17,6 +18,13 @@ class StaticRenderEventHandler : StaticEventHandler native
 	native readonly Actor Camera;
 }
 
+class StaticWorldEventHandler : StaticEventHandler native
+{
+    // for world
+    native readonly bool IsSaveGame; // this is set to true if static WorldLoaded was triggered during savegame loading.
+    native readonly Actor Thing; // this is for WorldThingSpawned
+}
+
 class EventHandler : StaticEventHandler native
 {
 	static native StaticEventHandler Create(class<StaticEventHandler> type);