diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index 916fa9254bd8f07b9c071e5848dff3e5d215f132..a59ba546e9bd5e4d9da6206759d05788f456a0ff 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -1671,6 +1671,26 @@ static int lib_pSwitchShield(lua_State *L)
 	return 0;
 }
 
+static int lib_pPlayerCanEnterSpinGaps(lua_State *L)
+{
+	player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
+	INLEVEL
+	if (!player)
+		return LUA_ErrInvalid(L, "player_t");
+	lua_pushboolean(L, P_PlayerCanEnterSpinGaps(player));
+	return 1;
+}
+
+static int lib_pPlayerShouldUseSpinHeight(lua_State *L)
+{
+	player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
+	INLEVEL
+	if (!player)
+		return LUA_ErrInvalid(L, "player_t");
+	lua_pushboolean(L, P_PlayerShouldUseSpinHeight(player));
+	return 1;
+}
+
 // P_MAP
 ///////////
 
@@ -3872,6 +3892,8 @@ static luaL_Reg lib[] = {
 	{"P_SpawnSpinMobj",lib_pSpawnSpinMobj},
 	{"P_Telekinesis",lib_pTelekinesis},
 	{"P_SwitchShield",lib_pSwitchShield},
+	{"P_PlayerCanEnterSpinGaps",lib_pPlayerCanEnterSpinGaps},
+	{"P_PlayerShouldUseSpinHeight",lib_pPlayerShouldUseSpinHeight},
 
 	// p_map
 	{"P_CheckPosition",lib_pCheckPosition},
diff --git a/src/p_local.h b/src/p_local.h
index 8caab0d2716f97f376580b7fc53f6660e5e7082d..8568dd4f8c2d58481c1b15e7b5a9e65a864f77bc 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -143,6 +143,8 @@ angle_t P_GetLocalAngle(player_t *player);
 void P_SetLocalAngle(player_t *player, angle_t angle);
 void P_ForceLocalAngle(player_t *player, angle_t angle);
 boolean P_PlayerFullbright(player_t *player);
+boolean P_PlayerCanEnterSpinGaps(player_t *player);
+boolean P_PlayerShouldUseSpinHeight(player_t *player);
 
 boolean P_IsObjectInGoop(mobj_t *mo);
 boolean P_IsObjectOnGround(mobj_t *mo);
diff --git a/src/p_map.c b/src/p_map.c
index a1cad524e14e2739af074b9ea24887b0d077eb95..7eec0937c20d27f0d192879be94b7ecbadeeff9f 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -1955,6 +1955,12 @@ static boolean PIT_CheckLine(line_t *ld)
 	// set openrange, opentop, openbottom
 	P_LineOpening(ld, tmthing);
 
+	// players should not always cross into sectors that they could not at full height
+	if (tmthing->player
+		&& openrange < P_GetPlayerHeight(tmthing->player)
+		&& !P_PlayerCanEnterSpinGaps(tmthing->player))
+			return false;
+
 	// adjust floor / ceiling heights
 	if (opentop < tmceilingz)
 	{
@@ -3331,6 +3337,11 @@ static boolean PTR_LineIsBlocking(line_t *li)
 	if (openbottom - slidemo->z > FixedMul(MAXSTEPMOVE, slidemo->scale))
 		return true; // too big a step up
 
+	if (slidemo->player
+		&& openrange < P_GetPlayerHeight(slidemo->player)
+		&& !P_PlayerCanEnterSpinGaps(slidemo->player))
+			return true; // nonspin character should not take this path
+
 	return false;
 }
 
diff --git a/src/p_user.c b/src/p_user.c
index 02592053d051eef2d72cc34de5a4f3cb4237b1fa..38f13f8fdf4e73faacc3a80407cbcd3515ca1176 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -8653,12 +8653,7 @@ void P_MovePlayer(player_t *player)
 		fixed_t oldheight = player->mo->height;
 
 		// Less height while spinning. Good for spinning under things...?
-		if ((player->mo->state == &states[player->mo->info->painstate])
-		|| ((player->pflags & PF_JUMPED) && !(player->pflags & PF_NOJUMPDAMAGE))
-		|| (player->pflags & PF_SPINNING)
-		|| player->powers[pw_tailsfly] || player->pflags & PF_GLIDING
-		|| (player->charability == CA_GLIDEANDCLIMB && player->mo->state-states == S_PLAY_GLIDE_LANDING)
-		|| (player->charability == CA_FLY && player->mo->state-states == S_PLAY_FLY_TIRED))
+		if (P_PlayerShouldUseSpinHeight(player))
 		{
 			player->mo->height = P_GetPlayerSpinHeight(player);
 			atspinheight = true;
@@ -12953,3 +12948,20 @@ boolean P_PlayerFullbright(player_t *player)
 			|| !(player->mo->state >= &states[S_PLAY_NIGHTS_TRANS1]
 			&& player->mo->state < &states[S_PLAY_NIGHTS_TRANS6])))); // Note the < instead of <=
 }
+
+// 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)
+{
+	return ((player->pflags & (PF_SPINNING|PF_GLIDING))
+		|| (player->charability == CA_GLIDEANDCLIMB && player->mo->state-states == S_PLAY_GLIDE_LANDING));
+}
+
+// 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)
+		|| (player->mo->state == &states[player->mo->info->painstate])
+		|| ((player->pflags & PF_JUMPED) && !(player->pflags & PF_NOJUMPDAMAGE))
+		|| player->powers[pw_tailsfly]
+		|| (player->charability == CA_FLY && player->mo->state-states == S_PLAY_FLY_TIRED));
+}