diff --git a/src/lua_hook.h b/src/lua_hook.h
index 5cfcb8360149093bd7340c0301b679b7d2b03698..0d631aa4e275259c5bda256e2dcc0e5824283ea4 100644
--- a/src/lua_hook.h
+++ b/src/lua_hook.h
@@ -61,6 +61,8 @@ enum hook {
 	hook_GameQuit,
 	hook_PlayerCmd,
 	hook_MusicChange,
+	hook_PlayerHeight,
+	hook_PlayerCanEnterSpinGaps,
 
 	hook_MAX // last hook
 };
@@ -118,3 +120,5 @@ boolean LUAh_ShouldJingleContinue(player_t *player, const char *musname); // Hoo
 void LUAh_GameQuit(boolean quitting); // Hook for game quitting
 boolean LUAh_PlayerCmd(player_t *player, ticcmd_t *cmd); // Hook for building player's ticcmd struct (Ported from SRB2Kart)
 boolean LUAh_MusicChange(const char *oldname, char *newname, UINT16 *mflags, boolean *looping, UINT32 *position, UINT32 *prefadems, UINT32 *fadeinms); // Hook for music changes
+fixed_t LUAh_PlayerHeight(player_t *player);
+UINT8 LUAh_PlayerCanEnterSpinGaps(player_t *player);
diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c
index 29c15a4de9887430b90323e505a2d4235bcf0a76..6e9b9d65c42e63a037512822c7a14c727e2be832 100644
--- a/src/lua_hooklib.c
+++ b/src/lua_hooklib.c
@@ -77,6 +77,8 @@ const char *const hookNames[hook_MAX+1] = {
 	"GameQuit",
 	"PlayerCmd",
 	"MusicChange",
+	"PlayerHeight",
+	"PlayerCanEnterSpinGaps",
 	NULL
 };
 
@@ -221,6 +223,8 @@ static int lib_addHook(lua_State *L)
 	case hook_ShieldSpawn:
 	case hook_ShieldSpecial:
 	case hook_PlayerThink:
+	case hook_PlayerHeight:
+	case hook_PlayerCanEnterSpinGaps:
 		lastp = &playerhooks;
 		break;
 	case hook_LinedefExecute:
@@ -1971,3 +1975,89 @@ boolean LUAh_MusicChange(const char *oldname, char *newname, UINT16 *mflags, boo
 	newname[6] = 0;
 	return hooked;
 }
+
+// Hook for determining player height
+fixed_t LUAh_PlayerHeight(player_t *player)
+{
+	hook_p hookp;
+	fixed_t newheight = -1;
+	if (!gL || !(hooksAvailable[hook_PlayerHeight/8] & (1<<(hook_PlayerHeight%8))))
+		return 0;
+
+	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
+
+	for (hookp = playerhooks; hookp; hookp = hookp->next)
+	{
+		if (hookp->type != hook_PlayerHeight)
+			continue;
+
+		ps_lua_mobjhooks++;
+		if (lua_gettop(gL) == 1)
+			LUA_PushUserdata(gL, player, META_PLAYER);
+		PushHook(gL, hookp);
+		lua_pushvalue(gL, -2);
+		if (lua_pcall(gL, 1, 1, 1)) {
+			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_isnil(gL, -1))
+		{
+			fixed_t returnedheight = lua_tonumber(gL, -1);
+			// 0 height has... strange results, but it's not problematic like negative heights are.
+			// when an object's height is set to a negative number directly with lua, it's forced to 0 instead.
+			// here, I think it's better to ignore negatives so that they don't replace any results of previous hooks!
+			if (returnedheight >= 0)
+				newheight = returnedheight;
+		}
+		lua_pop(gL, 1);
+	}
+
+	lua_settop(gL, 0);
+	return newheight;
+}
+
+// Hook for determining whether players are allowed passage through spin gaps
+UINT8 LUAh_PlayerCanEnterSpinGaps(player_t *player)
+{
+	hook_p hookp;
+	UINT8 canEnter = 0; // 0 = default, 1 = force yes, 2 = force no.
+	if (!gL || !(hooksAvailable[hook_PlayerCanEnterSpinGaps/8] & (1<<(hook_PlayerCanEnterSpinGaps%8))))
+		return 0;
+
+	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
+
+	for (hookp = playerhooks; hookp; hookp = hookp->next)
+	{
+		if (hookp->type != hook_PlayerCanEnterSpinGaps)
+			continue;
+
+		ps_lua_mobjhooks++;
+		if (lua_gettop(gL) == 1)
+			LUA_PushUserdata(gL, player, META_PLAYER);
+		PushHook(gL, hookp);
+		lua_pushvalue(gL, -2);
+		if (lua_pcall(gL, 1, 1, 1)) {
+			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_isnil(gL, -1))
+		{ // if nil, leave canEnter = 0.
+			if (lua_toboolean(gL, -1))
+				canEnter = 1; // Force yes
+			else
+				canEnter = 2; // Force no
+		}
+		lua_pop(gL, 1);
+	}
+
+	lua_settop(gL, 0);
+	return canEnter;
+}
diff --git a/src/p_user.c b/src/p_user.c
index 63942e0bede75212eab60cc202289dc57a67ab12..56767f433d7888722c526fb8374bc81b0f3a61d5 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -8651,9 +8651,16 @@ void P_MovePlayer(player_t *player)
 	{
 		boolean atspinheight = false;
 		fixed_t oldheight = player->mo->height;
+		fixed_t luaheight = LUAh_PlayerHeight(player);
 
+		if (luaheight != -1)
+		{
+			player->mo->height = luaheight;
+			if (luaheight <= P_GetPlayerSpinHeight(player))
+				atspinheight = true; // spinning will not save you from being crushed
+		}
 		// Less height while spinning. Good for spinning under things...?
-		if (P_PlayerShouldUseSpinHeight(player))
+		else if (P_PlayerShouldUseSpinHeight(player))
 		{
 			player->mo->height = P_GetPlayerSpinHeight(player);
 			atspinheight = true;
@@ -12958,6 +12965,12 @@ boolean P_PlayerFullbright(player_t *player)
 // returns true if the player can enter a sector that they could not if standing at their skin's full height
 boolean P_PlayerCanEnterSpinGaps(player_t *player)
 {
+	UINT8 canEnter = LUAh_PlayerCanEnterSpinGaps(player);
+	if (canEnter == 1)
+		return true;
+	else if (canEnter == 2)
+		return false;
+
 	return ((player->pflags & (PF_SPINNING|PF_GLIDING)) // players who are spinning or gliding
 		|| (player->charability == CA_GLIDEANDCLIMB && player->mo->state-states == S_PLAY_GLIDE_LANDING) // players who are landing from a glide
 		|| JUMPCURLED(player)); // players who are jumpcurled, but only if they would normally jump that way
@@ -12966,9 +12979,11 @@ boolean P_PlayerCanEnterSpinGaps(player_t *player)
 // returns true if the player should use their skin's spinheight instead of their skin's height
 boolean P_PlayerShouldUseSpinHeight(player_t *player)
 {
-	return (P_PlayerCanEnterSpinGaps(player)
+	return ((player->pflags & (PF_SPINNING|PF_GLIDING))
 		|| (player->mo->state == &states[player->mo->info->painstate])
 		|| (player->panim == PA_ROLL)
 		|| ((player->powers[pw_tailsfly] || (player->charability == CA_FLY && player->mo->state-states == S_PLAY_FLY_TIRED))
-			&& !(player->charflags & SF_NOJUMPSPIN)));
+			&& !(player->charflags & SF_NOJUMPSPIN))
+		|| (player->charability == CA_GLIDEANDCLIMB && player->mo->state-states == S_PLAY_GLIDE_LANDING)
+		|| JUMPCURLED(player));
 }