diff --git a/src/lua_hook.h b/src/lua_hook.h
index 88867db2b8b051891518f51566489b39aa909dda..fe5706f56a6b3c90051ba2bf84f49d5da274b122 100644
--- a/src/lua_hook.h
+++ b/src/lua_hook.h
@@ -46,6 +46,7 @@ enum hook {
 	hook_ShieldSpawn,
 	hook_ShieldSpecial,
 	hook_MobjMoveBlocked,
+	hook_MapThingSpawn,
 
 	hook_MAX // last hook
 };
@@ -83,5 +84,6 @@ boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8
 #define LUAh_ShieldSpawn(player) LUAh_PlayerHook(player, hook_ShieldSpawn) // Hook for P_SpawnShieldOrb
 #define LUAh_ShieldSpecial(player) LUAh_PlayerHook(player, hook_ShieldSpecial) // Hook for shield abilities
 #define LUAh_MobjMoveBlocked(mo) LUAh_MobjHook(mo, hook_MobjMoveBlocked) // Hook for P_XYMovement (when movement is blocked)
+boolean LUAh_MapThingSpawn(mobj_t *mo, mapthing_t *mthing); // Hook for P_SpawnMapThing by mobj type
 
 #endif
diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c
index dadc1861ac879ce3ffd9dba3db3bee65b9af11d2..04efac070f52f722334df2e363038dc6673f39c7 100644
--- a/src/lua_hooklib.c
+++ b/src/lua_hooklib.c
@@ -57,6 +57,7 @@ const char *const hookNames[hook_MAX+1] = {
 	"ShieldSpawn",
 	"ShieldSpecial",
 	"MobjMoveBlocked",
+	"MapThingSpawn",
 	NULL
 };
 
@@ -128,6 +129,7 @@ static int lib_addHook(lua_State *L)
 	case hook_MobjRemoved:
 	case hook_HurtMsg:
 	case hook_MobjMoveBlocked:
+	case hook_MapThingSpawn:
 		hook.s.mt = MT_NULL;
 		if (lua_isnumber(L, 2))
 			hook.s.mt = lua_tonumber(L, 2);
@@ -187,6 +189,7 @@ static int lib_addHook(lua_State *L)
 	case hook_BossDeath:
 	case hook_MobjRemoved:
 	case hook_MobjMoveBlocked:
+	case hook_MapThingSpawn:
 		lastp = &mobjhooks[hook.s.mt];
 		break;
 	case hook_JumpSpecial:
@@ -1073,4 +1076,66 @@ void LUAh_NetArchiveHook(lua_CFunction archFunc)
 	// stack: tables
 }
 
+boolean LUAh_MapThingSpawn(mobj_t *mo, mapthing_t *mthing)
+{
+	hook_p hookp;
+	boolean hooked = false;
+	if (!gL || !(hooksAvailable[hook_MapThingSpawn/8] & (1<<(hook_MapThingSpawn%8))))
+		return false;
+
+	lua_settop(gL, 0);
+
+	// Look for all generic mobj map thing spawn hooks
+	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
+		if (hookp->type == hook_MapThingSpawn)
+		{
+			if (lua_gettop(gL) == 0)
+			{
+				LUA_PushUserdata(gL, mo, META_MOBJ);
+				LUA_PushUserdata(gL, mthing, META_MAPTHING);
+			}
+			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+			lua_gettable(gL, LUA_REGISTRYINDEX);
+			lua_pushvalue(gL, -3);
+			lua_pushvalue(gL, -3);
+			if (lua_pcall(gL, 2, 1, 0)) {
+				if (!hookp->error || cv_debug & DBG_LUA)
+					CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+				lua_pop(gL, 1);
+				hookp->error = true;
+				continue;
+			}
+			if (lua_toboolean(gL, -1))
+				hooked = true;
+			lua_pop(gL, 1);
+		}
+
+	for (hookp = mobjhooks[mo->type]; hookp; hookp = hookp->next)
+		if (hookp->type == hook_MapThingSpawn)
+		{
+			if (lua_gettop(gL) == 0)
+			{
+				LUA_PushUserdata(gL, mo, META_MOBJ);
+				LUA_PushUserdata(gL, mthing, META_MAPTHING);
+			}
+			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+			lua_gettable(gL, LUA_REGISTRYINDEX);
+			lua_pushvalue(gL, -3);
+			lua_pushvalue(gL, -3);
+			if (lua_pcall(gL, 2, 1, 0)) {
+				if (!hookp->error || cv_debug & DBG_LUA)
+					CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+				lua_pop(gL, 1);
+				hookp->error = true;
+				continue;
+			}
+			if (lua_toboolean(gL, -1))
+				hooked = true;
+			lua_pop(gL, 1);
+		}
+
+	lua_settop(gL, 0);
+	return hooked;
+}
+
 #endif
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 520f9ad07d45a1da1f4b102e9ad7886029861862..e38551548f83859312b7578de76d43823f31d615 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -9698,6 +9698,16 @@ void P_SpawnMapThing(mapthing_t *mthing)
 	mobj = P_SpawnMobj(x, y, z, i);
 	mobj->spawnpoint = mthing;
 
+#ifdef HAVE_BLUA
+	if (LUAh_MapThingSpawn(mobj, mthing))
+	{
+		if (P_MobjWasRemoved(mobj))
+			return;
+	}
+	else if (P_MobjWasRemoved(mobj))
+		return;
+	else
+#endif
 	switch(mobj->type)
 	{
 	case MT_SKYBOX: