diff --git a/src/events.cpp b/src/events.cpp
index c9aba1660..23a8a8800 100755
--- a/src/events.cpp
+++ b/src/events.cpp
@@ -7,6 +7,7 @@
 #include "actor.h"
 
 DStaticEventHandler* E_FirstEventHandler = nullptr;
+DStaticEventHandler* E_LastEventHandler = nullptr;
 
 bool E_RegisterHandler(DStaticEventHandler* handler)
 {
@@ -14,17 +15,53 @@ bool E_RegisterHandler(DStaticEventHandler* handler)
 		return false;
 	if (E_CheckHandler(handler))
 		return false;
+	
 	// link into normal list
-	handler->prev = nullptr;
-	handler->next = E_FirstEventHandler;
-	if (handler->next)
-		handler->next->prev = handler;
-	E_FirstEventHandler = handler;
+	// update: link at specific position based on order.
+	DStaticEventHandler* before = nullptr;
+	for (DStaticEventHandler* existinghandler = E_FirstEventHandler; existinghandler; existinghandler = existinghandler->next)
+	{
+		if (existinghandler->GetOrder() > handler->GetOrder())
+		{
+			before = existinghandler;
+			break;
+		}
+	}
+
+	// 1. MyHandler2->1:
+	//    E_FirstEventHandler = MyHandler2, E_LastEventHandler = MyHandler2
+	// 2. MyHandler3->2:
+	//    E_FirstEventHandler = MyHandler2, E_LastEventHandler = MyHandler3
+
+	if (before != nullptr)
+	{
+		// if before is not null, link it before the existing handler.
+		// note that before can be first handler, check for this.
+		handler->next = before;
+		handler->prev = before->prev;
+		before->prev = handler;
+		if (before == E_FirstEventHandler)
+			E_FirstEventHandler = handler;
+	}
+	else
+	{
+		// so if before is null, it means add last.
+		// it can also mean that we have no handlers at all yet.
+		handler->prev = E_LastEventHandler;
+		handler->next = nullptr;
+		if (E_FirstEventHandler == nullptr)
+			E_FirstEventHandler = handler;
+		E_LastEventHandler = handler;
+		if (handler->prev != nullptr)
+			handler->prev->next = handler;
+	}
+
 	if (handler->IsStatic())
 	{
 		handler->ObjectFlags |= OF_Fixed;
 		handler->ObjectFlags |= OF_Transient;
 	}
+
 	return true;
 }
 
@@ -41,6 +78,8 @@ bool E_UnregisterHandler(DStaticEventHandler* handler)
 		handler->next->prev = handler->prev;
 	if (handler == E_FirstEventHandler)
 		E_FirstEventHandler = handler->next;
+	if (handler == E_LastEventHandler)
+		E_LastEventHandler = handler->prev;
 	if (handler->IsStatic())
 	{
 		handler->ObjectFlags &= ~(OF_Fixed|OF_Transient);
@@ -224,7 +263,7 @@ void E_WorldLoaded()
 
 void E_WorldUnloaded()
 {
-	for (DStaticEventHandler* handler = E_FirstEventHandler; handler; handler = handler->next)
+	for (DStaticEventHandler* handler = E_LastEventHandler; handler; handler = handler->prev)
 	{
 		if (handler->IsStatic()) continue;
 		handler->WorldUnloaded();
@@ -242,7 +281,7 @@ void E_WorldLoadedUnsafe()
 
 void E_WorldUnloadedUnsafe()
 {
-	for (DStaticEventHandler* handler = E_FirstEventHandler; handler; handler = handler->next)
+	for (DStaticEventHandler* handler = E_LastEventHandler; handler; handler = handler->prev)
 	{
 		if (!handler->IsStatic()) continue;
 		handler->WorldUnloaded();
@@ -294,7 +333,7 @@ void E_WorldThingDestroyed(AActor* actor)
 	// this is because Destroyed should be reverse of Spawned. we don't want to catch random inventory give failures.
 	if (!(actor->ObjectFlags & OF_Spawned))
 		return;
-	for (DStaticEventHandler* handler = E_FirstEventHandler; handler; handler = handler->next)
+	for (DStaticEventHandler* handler = E_LastEventHandler; handler; handler = handler->prev)
 		handler->WorldThingDestroyed(actor);
 }
 
@@ -449,6 +488,12 @@ DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldLightning)
 DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldTick)
 DEFINE_EMPTY_HANDLER(DStaticEventHandler, RenderFrame)
 
+DEFINE_ACTION_FUNCTION(DStaticEventHandler, GetOrder)
+{
+	PARAM_SELF_PROLOGUE(DStaticEventHandler);
+	ACTION_RETURN_INT(0);
+}
+
 // ===========================================
 //
 //  Event handlers
@@ -630,3 +675,22 @@ void DStaticEventHandler::OnDestroy()
 	E_UnregisterHandler(this);
 	Super::OnDestroy();
 }
+
+//
+int DStaticEventHandler::GetOrder()
+{
+	// if we have cached order, return it.
+	// otherwise call VM.
+	if (haveorder) 
+		return order;
+
+	IFVIRTUAL(DStaticEventHandler, GetOrder)
+	{
+		VMReturn results[1] = { &order };
+		VMValue params[1] = { (DStaticEventHandler*)this };
+		GlobalVMStack.Call(func, params, 1, results, 1, nullptr);
+		haveorder = true;
+	}
+
+	return order;
+}
\ No newline at end of file
diff --git a/src/events.h b/src/events.h
index 4bbeb6174..50a227c77 100755
--- a/src/events.h
+++ b/src/events.h
@@ -59,12 +59,18 @@ public:
 	{
 		prev = 0;
 		next = 0;
+		order = 0;
+		haveorder = false;
 	}
 
 	DStaticEventHandler* prev;
 	DStaticEventHandler* next;
 	virtual bool IsStatic() { return true; }
 
+	// order is cached to avoid calling the VM for sorting too much
+	int order;
+	bool haveorder;
+
 	// serialization handler. let's keep it here so that I don't get lost in serialized/not serialized fields
 	void Serialize(FSerializer& arc) override
 	{
@@ -93,6 +99,9 @@ public:
 	virtual void WorldLightning();
 	virtual void WorldTick();
 	virtual void RenderFrame();
+
+	// gets the order of this item.
+	int GetOrder();
 };
 class DEventHandler : public DStaticEventHandler
 {
diff --git a/wadsrc/static/zscript/events.txt b/wadsrc/static/zscript/events.txt
index a1029ff93..3f155c145 100755
--- a/wadsrc/static/zscript/events.txt
+++ b/wadsrc/static/zscript/events.txt
@@ -49,6 +49,11 @@ class StaticEventHandler : Object native
     virtual native void WorldTick(WorldEvent e);
 
     virtual native void RenderFrame(RenderEvent e);
+    
+    // this function should return a value that will be queried on Register() to decide the relative order of this handler to every other.
+    // this is most useful in UI systems.
+    // default is 0.
+    virtual native int GetOrder();
 }
 
 class EventHandler : StaticEventHandler native