diff --git a/src/dobjgc.cpp b/src/dobjgc.cpp
index 3d683aff1..4f0aa20db 100644
--- a/src/dobjgc.cpp
+++ b/src/dobjgc.cpp
@@ -78,6 +78,7 @@
 #include "menu/menu.h"
 #include "intermission/intermission.h"
 #include "g_levellocals.h"
+#include "events.h"
 
 // MACROS ------------------------------------------------------------------
 
@@ -331,6 +332,8 @@ static void MarkRoot()
 	DThinker::MarkRoots();
 	FCanvasTextureInfo::Mark();
 	Mark(DACSThinker::ActiveThinker);
+	Mark(E_FirstEventHandler);
+	Mark(E_LastEventHandler);
 	for (auto &s : level.sectorPortals)
 	{
 		Mark(s.mSkybox);
diff --git a/src/events.cpp b/src/events.cpp
index 4522f1989..afa98c99d 100755
--- a/src/events.cpp
+++ b/src/events.cpp
@@ -37,32 +37,42 @@ bool E_RegisterHandler(DStaticEventHandler* handler)
 	// 2. MyHandler3->2:
 	//    E_FirstEventHandler = MyHandler2, E_LastEventHandler = MyHandler3
 
+	// (Yes, all those write barriers here are really needed!)
 	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;
+		GC::WriteBarrier(handler, before);
 		handler->prev = before->prev;
+		GC::WriteBarrier(handler, before->prev);
 		before->prev = handler;
+		GC::WriteBarrier(before, handler);
 		if (before == E_FirstEventHandler)
+		{
 			E_FirstEventHandler = handler;
+			GC::WriteBarrier(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;
+		GC::WriteBarrier(handler, E_LastEventHandler);
 		handler->next = nullptr;
-		if (E_FirstEventHandler == nullptr)
-			E_FirstEventHandler = handler;
+		if (E_FirstEventHandler == nullptr)	E_FirstEventHandler = handler;
 		E_LastEventHandler = handler;
+		GC::WriteBarrier(handler);
 		if (handler->prev != nullptr)
+		{
 			handler->prev->next = handler;
+			GC::WriteBarrier(handler->prev, handler);
+		}
 	}
 
 	if (handler->IsStatic())
 	{
-		handler->ObjectFlags |= OF_Fixed;
 		handler->ObjectFlags |= OF_Transient;
 	}
 
@@ -80,16 +90,28 @@ bool E_UnregisterHandler(DStaticEventHandler* handler)
 
 	// link out of normal list
 	if (handler->prev)
+	{
 		handler->prev->next = handler->next;
+		GC::WriteBarrier(handler->prev, handler->next);
+	}
 	if (handler->next)
+	{
 		handler->next->prev = handler->prev;
+		GC::WriteBarrier(handler->next, handler->prev);
+	}
 	if (handler == E_FirstEventHandler)
+	{
 		E_FirstEventHandler = handler->next;
+		GC::WriteBarrier(handler->next);
+	}
 	if (handler == E_LastEventHandler)
+	{
 		E_LastEventHandler = handler->prev;
+		GC::WriteBarrier(handler->prev);
+	}
 	if (handler->IsStatic())
 	{
-		handler->ObjectFlags &= ~(OF_Fixed|OF_Transient);
+		handler->ObjectFlags &= ~OF_Transient;
 		handler->Destroy();
 	}
 	return true;
diff --git a/src/events.h b/src/events.h
index 71c8a27f6..46ed88b95 100755
--- a/src/events.h
+++ b/src/events.h
@@ -77,6 +77,7 @@ void E_SerializeEvents(FSerializer& arc);
 class DStaticEventHandler : public DObject // make it a part of normal GC process
 {
 	DECLARE_CLASS(DStaticEventHandler, DObject)
+	HAS_OBJECT_POINTERS
 public:
 	DStaticEventHandler()
 	{
@@ -99,6 +100,7 @@ public:
 	void Serialize(FSerializer& arc) override
 	{
 		Super::Serialize(arc);
+		/*
 		if (arc.isReading())
 		{
 			Printf("DStaticEventHandler::Serialize: reading object %s\n", GetClass()->TypeName.GetChars());
@@ -107,6 +109,7 @@ public:
 		{
 			Printf("DStaticEventHandler::Serialize: store object %s\n", GetClass()->TypeName.GetChars());
 		}
+		*/
 
 		arc("Order", Order);
 		arc("IsUiProcessor", IsUiProcessor);
@@ -155,6 +158,7 @@ public:
 	bool IsStatic() override { return false; }
 };
 extern DStaticEventHandler* E_FirstEventHandler;
+extern DStaticEventHandler* E_LastEventHandler;
 
 // we cannot call this DEvent because in ZScript, 'event' is a keyword
 class DBaseEvent : public DObject
diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp
index e4920ef55..71c0ff645 100644
--- a/src/menu/menu.cpp
+++ b/src/menu/menu.cpp
@@ -496,7 +496,7 @@ DEFINE_ACTION_FUNCTION(DMenu, SetMenu)
 {
 	PARAM_PROLOGUE;
 	PARAM_NAME(menu);
-	PARAM_INT(mparam);
+	PARAM_INT_DEF(mparam);
 	M_SetMenu(menu, mparam);
 	return 0;
 }
diff --git a/src/menu/messagebox.cpp b/src/menu/messagebox.cpp
index b3b0e1fe7..a1a0fda76 100644
--- a/src/menu/messagebox.cpp
+++ b/src/menu/messagebox.cpp
@@ -267,7 +267,7 @@ DEFINE_ACTION_FUNCTION(DMenu, StartMessage)
 {
 	PARAM_PROLOGUE;
 	PARAM_STRING(msg);
-	PARAM_INT(mode);
+	PARAM_INT_DEF(mode);
 	PARAM_NAME_DEF(action);
 	M_StartMessage(msg, mode, action);
 	return 0;
diff --git a/src/p_sight.cpp b/src/p_sight.cpp
index 74b9fe165..68bf2ed3d 100644
--- a/src/p_sight.cpp
+++ b/src/p_sight.cpp
@@ -107,6 +107,7 @@ class SightCheck
 	bool P_SightCheckLine (line_t *ld);
 	int P_SightBlockLinesIterator (int x, int y);
 	bool P_SightTraverseIntercepts ();
+	bool LineBlocksSight(line_t *ld);
 
 public:
 	bool P_SightPathTraverse ();
@@ -211,7 +212,14 @@ bool SightCheck::PTR_SightTraverse (intercept_t *in)
 
 	double trX = Trace.x + Trace.dx * in->frac;
 	double trY = Trace.y + Trace.dy * in->frac;
-	P_SightOpening (open, li, trX, trY);
+
+	P_SightOpening(open, li, trX, trY);
+	if (LineBlocksSight(in->d.line))
+	{
+		// This may not skip P_SightOpening, but only reduce the open range to 0.
+		open.range = 0;
+		open.bottom = open.top;
+	}
 
 	FLinePortal *lport = li->getPortal();
 
@@ -362,6 +370,42 @@ bool SightCheck::PTR_SightTraverse (intercept_t *in)
 }
 
 
+// performs trivial visibility checks.
+bool SightCheck::LineBlocksSight(line_t *ld)
+{
+	// try to early out the check
+	if (!ld->backsector || !(ld->flags & ML_TWOSIDED) || (ld->flags & ML_BLOCKSIGHT))
+		return true;	// stop checking
+
+						// [RH] don't see past block everything lines
+	if (ld->flags & ML_BLOCKEVERYTHING)
+	{
+		if (!(Flags & SF_SEEPASTBLOCKEVERYTHING))
+		{
+			return true;
+		}
+		// Pretend the other side is invisible if this is not an impact line
+		// that runs a script on the current map. Used to prevent monsters
+		// from trying to attack through a block everything line unless
+		// there's a chance their attack will make it nonblocking.
+		if (!(Flags & SF_SEEPASTSHOOTABLELINES))
+		{
+			if (!(ld->activation & SPAC_Impact))
+			{
+				return true;
+			}
+			if (ld->special != ACS_Execute && ld->special != ACS_ExecuteAlways)
+			{
+				return true;
+			}
+			if (ld->args[1] != 0 && ld->args[1] != level.levelnum)
+			{
+				return true;
+			}
+		}
+	}
+	return false;
+}
 
 /*
 ==================
@@ -392,36 +436,9 @@ bool SightCheck::P_SightCheckLine (line_t *ld)
 		return true;		// line isn't crossed
 	}
 
-	// try to early out the check
-	if (!ld->backsector || !(ld->flags & ML_TWOSIDED) || (ld->flags & ML_BLOCKSIGHT))
-		return false;	// stop checking
-
-	// [RH] don't see past block everything lines
-	if (ld->flags & ML_BLOCKEVERYTHING)
+	if (!portalfound)	// when portals come into play, the quick-outs here may not be performed
 	{
-		if (!(Flags & SF_SEEPASTBLOCKEVERYTHING))
-		{
-			return false;
-		}
-		// Pretend the other side is invisible if this is not an impact line
-		// that runs a script on the current map. Used to prevent monsters
-		// from trying to attack through a block everything line unless
-		// there's a chance their attack will make it nonblocking.
-		if (!(Flags & SF_SEEPASTSHOOTABLELINES))
-		{
-			if (!(ld->activation & SPAC_Impact))
-			{
-				return false;
-			}
-			if (ld->special != ACS_Execute && ld->special != ACS_ExecuteAlways)
-			{
-				return false;
-			}
-			if (ld->args[1] != 0 && ld->args[1] != level.levelnum)
-			{
-				return false;
-			}
-		}
+		if (LineBlocksSight(ld)) return false;
 	}
 
 	sightcounts[3]++;
diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt
index 3a169c585..ab83adc62 100644
--- a/wadsrc/static/zscript/actor.txt
+++ b/wadsrc/static/zscript/actor.txt
@@ -788,7 +788,7 @@ class Actor : Thinker native
 	native void A_SpawnDebris(class<Actor> spawntype, bool transfer_translation = false, double mult_h = 1, double mult_v = 1);
 	native void A_SpawnParticle(color color1, int flags = 0, int lifetime = 35, double size = 1, double angle = 0, double xoff = 0, double yoff = 0, double zoff = 0, double velx = 0, double vely = 0, double velz = 0, double accelx = 0, double accely = 0, double accelz = 0, double startalphaf = 1, double fadestepf = -1, double sizestep = 0);
 	native void A_ExtChase(bool usemelee, bool usemissile, bool playactive = true, bool nightmarefast = false);
-	native void A_DropInventory(class<Inventory> itemtype, int amount);
+	native void A_DropInventory(class<Inventory> itemtype, int amount = -1);
 	native void A_SetBlend(color color1, double alpha, int tics, color color2 = 0);
 	deprecated native void A_ChangeFlag(string flagname, bool value);
 	native void A_ChangeCountFlags(int kill = FLAG_NO_CHANGE, int item = FLAG_NO_CHANGE, int secret = FLAG_NO_CHANGE);