From e16648a72bf63dd85ac94f8fcf415858da2d3356 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Fri, 23 Sep 2016 23:48:48 +0100
Subject: [PATCH] Introducing player->powers[pw_carry]! Also, I made rope hangs
 a lot smoother.

* PF_ITEMHANG -> CR_GENERIC
* PF_CARRIED -> CR_PLAYER
* (mo->tracer == MT_TUBEWAYPOINT) -> CR_ZOOMTUBE
* PF_ROPEHANG -> CR_ROPEHANG
* PF_MACESPIN -> CR_MACESPIN
---
 src/b_bot.c    |  12 +-
 src/d_player.h |  51 ++++----
 src/dehacked.c |  21 ++--
 src/p_enemy.c  |  32 +++--
 src/p_inter.c  |  14 ++-
 src/p_map.c    | 310 +++++++++++++++++--------------------------------
 src/p_mobj.c   |  29 ++---
 src/p_spec.c   |  15 ++-
 src/p_telept.c |   4 +-
 src/p_user.c   |  98 +++++++++-------
 10 files changed, 254 insertions(+), 332 deletions(-)

diff --git a/src/b_bot.c b/src/b_bot.c
index c59c084e9..0f2c80d55 100644
--- a/src/b_bot.c
+++ b/src/b_bot.c
@@ -41,12 +41,13 @@ static inline void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cm
 		return;
 #endif
 
-	if (tails->player->pflags & (PF_MACESPIN|PF_ITEMHANG))
+	if (tails->player->powers[pw_carry] == CR_MACESPIN || tails->player->powers[pw_carry] == CR_GENERIC)
 	{
+		boolean isrelevant = (sonic->player->powers[pw_carry] == CR_MACESPIN || sonic->player->powers[pw_carry] == CR_GENERIC);
 		dist = P_AproxDistance(tails->x-sonic->x, tails->y-sonic->y);
-		if (sonic->player->cmd.buttons & BT_JUMP && sonic->player->pflags & (PF_JUMPED|PF_MACESPIN|PF_ITEMHANG))
+		if (sonic->player->cmd.buttons & BT_JUMP && (sonic->player->pflags & PF_JUMPED) && isrelevant)
 			cmd->buttons |= BT_JUMP;
-		if (sonic->player->pflags & (PF_MACESPIN|PF_ITEMHANG))
+		if (isrelevant)
 		{
 			cmd->forwardmove = sonic->player->cmd.forwardmove;
 			cmd->angleturn = abs((signed)(tails->angle - sonic->angle))>>16;
@@ -211,8 +212,9 @@ boolean B_CheckRespawn(player_t *player)
 
 	// Check if Sonic is busy first.
 	// If he's doing any of these things, he probably doesn't want to see us.
-	if (sonic->player->pflags & (PF_ROPEHANG|PF_GLIDING|PF_CARRIED|PF_SLIDING|PF_ITEMHANG|PF_MACESPIN|PF_NIGHTSMODE)
-	|| (sonic->player->panim != PA_IDLE && sonic->player->panim != PA_WALK))
+	if (sonic->player->pflags & (PF_GLIDING|PF_SLIDING|PF_NIGHTSMODE)
+	|| (sonic->player->panim != PA_IDLE && sonic->player->panim != PA_WALK)
+	|| (sonic->player->powers[pw_carry]))
 		return false;
 
 	// Low ceiling, do not want!
diff --git a/src/d_player.h b/src/d_player.h
index 59875b4cc..f1d4d4395 100644
--- a/src/d_player.h
+++ b/src/d_player.h
@@ -44,6 +44,7 @@ typedef enum
 	SF_STOMPDAMAGE      = 1<<9, // Always damage enemies, etc by landing on them, no matter your vunerability?
 	SF_MARIODAMAGE      = SF_NOJUMPDAMAGE|SF_STOMPDAMAGE, // The Mario method of being able to damage enemies, etc.
 	SF_MACHINE          = 1<<10, // Beep boop. Are you a robot?
+	// free up to and including 1<<31
 } skinflags_t;
 
 //Primary and secondary skin abilities
@@ -128,40 +129,28 @@ typedef enum
 	// Are you gliding?
 	PF_GLIDING   = 1<<16,
 
-	// Tails pickup!
-	PF_CARRIED   = 1<<17,
-
 	// Sliding (usually in water) like Labyrinth/Oil Ocean
-	PF_SLIDING   = 1<<18,
-
-	// Hanging on a rope
-	PF_ROPEHANG = 1<<19,
-
-	// Hanging on an item of some kind - zipline, chain, etc. (->tracer)
-	PF_ITEMHANG = 1<<20,
-
-	// On the mace chain spinning around (->tracer)
-	PF_MACESPIN = 1<<21,
+	PF_SLIDING   = 1<<17,
 
 	/*** NIGHTS STUFF ***/
 	// Is the player in NiGHTS mode?
-	PF_NIGHTSMODE        = 1<<22,
-	PF_TRANSFERTOCLOSEST = 1<<23,
+	PF_NIGHTSMODE        = 1<<18,
+	PF_TRANSFERTOCLOSEST = 1<<19,
 
 	// Spill rings after falling
-	PF_NIGHTSFALL        = 1<<24,
-	PF_DRILLING          = 1<<25,
-	PF_SKIDDOWN          = 1<<26,
+	PF_NIGHTSFALL        = 1<<20,
+	PF_DRILLING          = 1<<21,
+	PF_SKIDDOWN          = 1<<22,
 
 	/*** TAG STUFF ***/
-	PF_TAGGED            = 1<<27, // Player has been tagged and awaits the next round in hide and seek.
-	PF_TAGIT             = 1<<28, // The player is it! For Tag Mode
+	PF_TAGGED            = 1<<23, // Player has been tagged and awaits the next round in hide and seek.
+	PF_TAGIT             = 1<<24, // The player is it! For Tag Mode
 
 	/*** misc ***/
-	PF_FORCESTRAFE       = 1<<29, // Turning inputs are translated into strafing inputs
-	PF_ANALOGMODE        = 1<<30, // Analog mode?
+	PF_FORCESTRAFE       = 1<<25, // Turning inputs are translated into strafing inputs
+	PF_ANALOGMODE        = 1<<26, // Analog mode?
 
-	// free: 1<<30 and 1<<31
+	// free up to and including 1<<31
 } pflags_t;
 
 typedef enum
@@ -204,7 +193,20 @@ typedef enum
 
 	SH_STACK = SH_FIREFLOWER,
 	SH_NOSTACK = ~SH_STACK
-} shieldtype_t;
+} shieldtype_t; // pw_shield
+
+typedef enum
+{
+	CR_NONE = 0,
+	// The generic case is suitable for most objects.
+	CR_GENERIC,
+	// Tails carry.
+	CR_PLAYER,
+	// Specific level gimmicks.
+	CR_ZOOMTUBE,
+	CR_ROPEHANG,
+	CR_MACESPIN
+} carrytype_t; // pw_carry
 
 // Player powers. (don't edit this comment)
 typedef enum
@@ -213,6 +215,7 @@ typedef enum
 	pw_sneakers,
 	pw_flashing,
 	pw_shield,
+	pw_carry,
 	pw_tailsfly, // tails flying
 	pw_underwater, // underwater timer
 	pw_spacetime, // In space, no one can hear you spin!
diff --git a/src/dehacked.c b/src/dehacked.c
index a512e1029..b4cc8f519 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -6750,21 +6750,9 @@ static const char *const PLAYERFLAG_LIST[] = {
 	// Are you gliding?
 	"GLIDING",
 
-	// Tails pickup!
-	"CARRIED",
-
 	// Sliding (usually in water) like Labyrinth/Oil Ocean
 	"SLIDING",
 
-	// Hanging on a rope
-	"ROPEHANG",
-
-	// Hanging on an item of some kind - zipline, chain, etc. (->tracer)
-	"ITEMHANG",
-
-	// On the mace chain spinning around (->tracer)
-	"MACESPIN",
-
 	/*** NIGHTS STUFF ***/
 	// Is the player in NiGHTS mode?
 	"NIGHTSMODE",
@@ -6903,6 +6891,7 @@ static const char *const POWERS_LIST[] = {
 	"SNEAKERS",
 	"FLASHING",
 	"SHIELD",
+	"CARRY",
 	"TAILSFLY", // tails flying
 	"UNDERWATER", // underwater timer
 	"SPACETIME", // In space, no one can hear you spin!
@@ -7138,6 +7127,14 @@ struct {
 	{"SH_STACK",SH_STACK},
 	{"SH_NOSTACK",SH_NOSTACK},
 
+	// Carrying
+	{"CR_NONE",CR_NONE},
+	{"CR_GENERIC",CR_GENERIC},
+	{"CR_PLAYER",CR_PLAYER},
+	{"CR_ZOOMTUBE",CR_ZOOMTUBE},
+	{"CR_ROPEHANG",CR_ROPEHANG},
+	{"CR_MACESPIN",CR_MACESPIN},
+
 	// Ring weapons (ringweapons_t)
 	// Useful for A_GiveWeapon
 	{"RW_AUTO",RW_AUTO},
diff --git a/src/p_enemy.c b/src/p_enemy.c
index 3790633ea..73ec2a79d 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -5428,8 +5428,8 @@ void A_MixUp(mobj_t *actor)
 
 		// Zoom tube stuff
 		mobj_t *tempthing = NULL; //tracer
-		pflags_t flags1,flags2;   //player pflags
-		INT32 transspeed;          //player speed
+		UINT16 carry1,carry2;     //carry
+		INT32 transspeed;         //player speed
 
 		// Starpost stuff
 		INT16 starpostx, starposty, starpostz;
@@ -5466,8 +5466,8 @@ void A_MixUp(mobj_t *actor)
 		players[two].speed = transspeed;
 
 		//set flags variables now but DON'T set them.
-		flags1 = (players[one].pflags & (PF_ITEMHANG|PF_MACESPIN|PF_ROPEHANG));
-		flags2 = (players[two].pflags & (PF_ITEMHANG|PF_MACESPIN|PF_ROPEHANG));
+		carry1 = (players[one].powers[pw_carry] == CR_PLAYER ? CR_NONE : players[one].powers[pw_carry]);
+		carry2 = (players[two].powers[pw_carry] == CR_PLAYER ? CR_NONE : players[two].powers[pw_carry]);
 
 		x = players[one].mo->x;
 		y = players[one].mo->y;
@@ -5492,12 +5492,10 @@ void A_MixUp(mobj_t *actor)
 				starpostnum, starposttime, starpostangle,
 				mflags2);
 
-		//flags set after mixup.  Stupid P_ResetPlayer() takes away some of the flags we look for...
-		//but not all of them!  So we need to make sure they aren't set wrong or anything.
-		players[one].pflags &= ~(PF_ITEMHANG|PF_MACESPIN|PF_ROPEHANG);
-		players[one].pflags |= flags2;
-		players[two].pflags &= ~(PF_ITEMHANG|PF_MACESPIN|PF_ROPEHANG);
-		players[two].pflags |= flags1;
+		//carry set after mixup.  Stupid P_ResetPlayer() takes away some of the stuff we look for...
+		//but not all of it!  So we need to make sure they aren't set wrong or anything.
+		players[one].powers[pw_carry] = carry2;
+		players[two].powers[pw_carry] = carry1;
 
 		teleported[one] = true;
 		teleported[two] = true;
@@ -5509,8 +5507,9 @@ void A_MixUp(mobj_t *actor)
 		INT32 pindex[MAXPLAYERS], counter = 0, teleportfrom = 0;
 
 		// Zoom tube stuff
-		mobj_t *transtracer[MAXPLAYERS]; //tracer
-		pflags_t transflag[MAXPLAYERS];  //player pflags
+		mobj_t *transtracer[MAXPLAYERS];  //tracer
+		//pflags_t transflag[MAXPLAYERS]; //cyan pink white pink cyan
+		UINT16 transcarry[MAXPLAYERS];    //player carry
 		INT32 transspeed[MAXPLAYERS];     //player speed
 
 		// Star post stuff
@@ -5544,7 +5543,7 @@ void A_MixUp(mobj_t *actor)
 					players[i].rmomx = players[i].rmomy = 1;
 				players[i].cmomx = players[i].cmomy = 0;
 
-				transflag[counter] = (players[i].pflags & (PF_ITEMHANG|PF_MACESPIN|PF_ROPEHANG));
+				transcarry[counter] = (players[i].powers[pw_carry] == CR_PLAYER ? CR_NONE : players[i].powers[pw_carry]);
 				transspeed[counter] = players[i].speed;
 				transtracer[counter] = players[i].mo->tracer;
 
@@ -5596,9 +5595,8 @@ void A_MixUp(mobj_t *actor)
 					starpostnum[teleportfrom], starposttime[teleportfrom], starpostangle[teleportfrom],
 					flags2[teleportfrom]);
 
-				//...flags after.  same reasoning.
-				players[i].pflags &= ~(PF_ITEMHANG|PF_MACESPIN|PF_ROPEHANG);
-				players[i].pflags |= transflag[teleportfrom];
+				//...carry after.  same reasoning.
+				players[i].powers[pw_carry] = transcarry[teleportfrom];
 
 				teleported[i] = true;
 				counter++;
@@ -6165,7 +6163,7 @@ void A_Boss7Chase(mobj_t *actor)
 	if (actor->health <= actor->info->damage
 		&& actor->target
 		&& actor->target->player
-		&& (actor->target->player->pflags & PF_ITEMHANG))
+		&& (actor->target->player->powers[pw_carry] == CR_GENERIC))
 	{
 		A_FaceTarget(actor);
 		P_SetMobjState(actor, S_BLACKEGG_SHOOT1);
diff --git a/src/p_inter.c b/src/p_inter.c
index 936747ce6..4349be579 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -1249,10 +1249,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 
 				player->powers[pw_ingoop] = 2;
 
-				if (player->pflags & PF_ITEMHANG)
+				if (player->powers[pw_carry] == CR_GENERIC)
 				{
 					P_SetTarget(&toucher->tracer, NULL);
-					player->pflags &= ~PF_ITEMHANG;
+					player->powers[pw_carry] = CR_NONE;
 				}
 
 				P_ResetPlayer(player);
@@ -1337,7 +1337,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 		case MT_BIGMACECHAIN:
 			// Is this the last link in the chain?
 			if (toucher->momz > 0 || !(special->flags & MF_AMBUSH)
-				|| (player->pflags & PF_ITEMHANG) || (player->pflags & PF_MACESPIN))
+				|| (player->powers[pw_carry]))
 				return;
 
 			if (toucher->z > special->z + special->height/2)
@@ -1354,12 +1354,12 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 
 			if (special->target && (special->target->type == MT_SPINMACEPOINT || special->target->type == MT_HIDDEN_SLING))
 			{
-				player->pflags |= PF_MACESPIN;
+				player->powers[pw_carry] = CR_MACESPIN;
 				S_StartSound(toucher, sfx_spin);
 				P_SetPlayerMobjState(toucher, S_PLAY_SPIN);
 			}
 			else
-				player->pflags |= PF_ITEMHANG;
+				player->powers[pw_carry] = CR_GENERIC;
 
 			// Can't jump first frame
 			player->pflags |= PF_JUMPSTASIS;
@@ -2623,7 +2623,9 @@ static inline boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj
 
 static void P_KillPlayer(player_t *player, mobj_t *source, INT32 damage)
 {
-	player->pflags &= ~(PF_CARRIED|PF_SLIDING|PF_ITEMHANG|PF_MACESPIN|PF_ROPEHANG|PF_NIGHTSMODE);
+	player->pflags &= ~(PF_SLIDING|PF_NIGHTSMODE);
+
+	player->powers[pw_carry] = CR_NONE;
 
 	// Burst weapons and emeralds in Match/CTF only
 	if (source && (gametype == GT_MATCH || gametype == GT_TEAMMATCH || gametype == GT_CTF))
diff --git a/src/p_map.c b/src/p_map.c
index 6d4acf1a2..92ed14f57 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -302,9 +302,9 @@ static void P_DoTailsCarry(player_t *sonic, player_t *tails)
 	INT32 p;
 	fixed_t zdist; // z distance between the two players' bottoms
 
-	if ((tails->pflags & PF_CARRIED) && tails->mo->tracer == sonic->mo)
+	if (tails->powers[pw_carry] == CR_PLAYER)// && tails->mo->tracer == sonic->mo) <-- why was this here?
 		return;
-	if ((sonic->pflags & PF_CARRIED) && sonic->mo->tracer == tails->mo)
+	if (sonic->powers[pw_carry] == CR_PLAYER && sonic->mo->tracer == tails->mo)
 		return;
 
 	if (tails->charability != CA_FLY && tails->charability != CA_SWIM)
@@ -319,8 +319,7 @@ static void P_DoTailsCarry(player_t *sonic, player_t *tails)
 	if (sonic->pflags & PF_NIGHTSMODE)
 		return;
 
-	if (sonic->mo->tracer && sonic->mo->tracer->type == MT_TUBEWAYPOINT
-	&& !(sonic->pflags & PF_ROPEHANG))
+	if (sonic->mo->tracer && sonic->powers[pw_carry] == CR_ZOOMTUBE)
 		return; // don't steal players from zoomtubes!
 
 	if ((sonic->mo->eflags & MFE_VERTICALFLIP) != (tails->mo->eflags & MFE_VERTICALFLIP))
@@ -337,7 +336,7 @@ static void P_DoTailsCarry(player_t *sonic, player_t *tails)
 	// Search in case another player is already being carried by this fox.
 	for (p = 0; p < MAXPLAYERS; p++)
 		if (playeringame[p] && players[p].mo
-		&& players[p].pflags & PF_CARRIED && players[p].mo->tracer == tails->mo)
+		&& players[p].powers[pw_carry] == CR_PLAYER && players[p].mo->tracer == tails->mo)
 			return;
 
 	if (tails->mo->eflags & MFE_VERTICALFLIP)
@@ -357,16 +356,16 @@ static void P_DoTailsCarry(player_t *sonic, player_t *tails)
 			|| (G_TagGametype() && (!(tails->pflags & PF_TAGIT) != !(sonic->pflags & PF_TAGIT)))
 			|| (gametype == GT_MATCH)
 			|| (G_GametypeHasTeams() && tails->ctfteam != sonic->ctfteam))
-			sonic->pflags &= ~PF_CARRIED; */
+			sonic->powers[pw_carry] = CR_NONE; */
 		if (tails->spectator || sonic->spectator)
-			sonic->pflags &= ~PF_CARRIED;
+			sonic->powers[pw_carry] = CR_NONE;
 		else
 		{
 			if (sonic-players == consoleplayer && botingame)
 				CV_SetValue(&cv_analog2, false);
 			P_ResetPlayer(sonic);
 			P_SetTarget(&sonic->mo->tracer, tails->mo);
-			sonic->pflags |= PF_CARRIED;
+			sonic->powers[pw_carry] = CR_PLAYER;
 			S_StartSound(sonic->mo, sfx_s3k4a);
 			P_UnsetThingPosition(sonic->mo);
 			sonic->mo->x = tails->mo->x;
@@ -377,199 +376,10 @@ static void P_DoTailsCarry(player_t *sonic, player_t *tails)
 	else {
 		if (sonic-players == consoleplayer && botingame)
 			CV_SetValue(&cv_analog2, true);
-		sonic->pflags &= ~PF_CARRIED;
+		sonic->powers[pw_carry] = CR_NONE;
 	}
 }
 
-//
-// P_ConsiderSolids (moved out of PIT_CheckThing in order to have additional flexibility)
-//
-
-static boolean P_ConsiderSolids(mobj_t *thing)
-{
-	// Monitors are not treated as solid to players who are jumping, spinning or gliding,
-	// unless it's a CTF team monitor and you're on the wrong team
-	if (thing->flags & MF_MONITOR && tmthing->player
-	&& (tmthing->player->pflags & (PF_SPINNING|PF_GLIDING)
-		|| ((tmthing->player->pflags & PF_JUMPED)
-			&& !(tmthing->player->charflags & SF_NOJUMPDAMAGE
-			&& !(tmthing->player->charability == CA_TWINSPIN && tmthing->player->panim == PA_ABILITY)))
-		|| (tmthing->player->charability2 == CA2_MELEE && tmthing->player->panim == PA_ABILITY2)
-		|| ((tmthing->player->charflags & SF_STOMPDAMAGE)
-			&& (P_MobjFlip(tmthing)*(tmthing->z - (thing->z + thing->height/2)) > 0) && (P_MobjFlip(tmthing)*tmthing->momz < 0)))
-	&& !((thing->type == MT_REDRINGBOX && tmthing->player->ctfteam != 1) || (thing->type == MT_BLUERINGBOX && tmthing->player->ctfteam != 2)))
-		;
-	// z checking at last
-	// Treat noclip things as non-solid!
-	else if ((thing->flags & (MF_SOLID|MF_NOCLIP)) == MF_SOLID
-		&& (tmthing->flags & (MF_SOLID|MF_NOCLIP)) == MF_SOLID)
-	{
-		fixed_t topz, tmtopz;
-
-		if (tmthing->eflags & MFE_VERTICALFLIP)
-		{
-			// pass under
-			tmtopz = tmthing->z;
-
-			if (tmtopz > thing->z + thing->height)
-			{
-				if (thing->z + thing->height > tmfloorz)
-				{
-					tmfloorz = thing->z + thing->height;
-#ifdef ESLOPE
-					tmfloorslope = NULL;
-#endif
-				}
-				return true;
-			}
-
-			topz = thing->z - FixedMul(FRACUNIT, thing->scale);
-
-			// block only when jumping not high enough,
-			// (dont climb max. 24units while already in air)
-			// if not in air, let P_TryMove() decide if it's not too high
-			if (tmthing->player && tmthing->z + tmthing->height > topz
-				&& tmthing->z + tmthing->height < tmthing->ceilingz)
-				return false; // block while in air
-
-			if (thing->flags & MF_SPRING)
-				;
-			else if (topz < tmceilingz && tmthing->z+tmthing->height <= thing->z+thing->height)
-			{
-				tmceilingz = topz;
-#ifdef ESLOPE
-				tmceilingslope = NULL;
-#endif
-				tmfloorthing = thing; // thing we may stand on
-			}
-		}
-		else
-		{
-			// pass under
-			tmtopz = tmthing->z + tmthing->height;
-
-			if (tmtopz < thing->z)
-			{
-				if (thing->z < tmceilingz)
-				{
-					tmceilingz = thing->z;
-#ifdef ESLOPE
-					tmceilingslope = NULL;
-#endif
-				}
-				return true;
-			}
-
-			topz = thing->z + thing->height + FixedMul(FRACUNIT, thing->scale);
-
-			// block only when jumping not high enough,
-			// (dont climb max. 24units while already in air)
-			// if not in air, let P_TryMove() decide if it's not too high
-			if (tmthing->player && tmthing->z < topz && tmthing->z > tmthing->floorz)
-				return false; // block while in air
-
-			if (thing->flags & MF_SPRING)
-				;
-			else if (topz > tmfloorz && tmthing->z >= thing->z)
-			{
-				tmfloorz = topz;
-#ifdef ESLOPE
-				tmfloorslope = NULL;
-#endif
-				tmfloorthing = thing; // thing we may stand on
-			}
-		}
-	}
-
-	// not solid not blocked
-	return true;
-}
-
-#if 0
-//
-// PIT_CheckSolid (PIT_CheckThing, but for solids only, and guaranteed no side effects)
-//
-static boolean PIT_CheckSolid(mobj_t *thing)
-{
-	fixed_t blockdist;
-
-	// don't clip against self
-	if (thing == tmthing)
-		return true;
-
-	// Ignore... things.
-	if (!tmthing || !thing || P_MobjWasRemoved(thing))
-		return true;
-
-	I_Assert(!P_MobjWasRemoved(tmthing));
-	I_Assert(!P_MobjWasRemoved(thing));
-
-	if (!(thing->flags & MF_SOLID))
-		return true;
-
-	// Ignore spectators
-	if ((tmthing->player && tmthing->player->spectator)
-	|| (thing->player && thing->player->spectator))
-		return true;
-
-	// Metal Sonic destroys tiny baby objects.
-	if (tmthing->type == MT_METALSONIC_RACE
-	&& (thing->flags & (MF_MISSILE|MF_ENEMY|MF_BOSS) || thing->type == MT_SPIKE))
-		return true;
-
-	// CA_DASHMODE users destroy spikes and monitors, CA_TWINSPIN users and CA2_MELEE users destroy spikes.
-	if ((tmthing->player)
-		&& (((tmthing->player->charability == CA_DASHMODE) && (tmthing->player->dashmode >= 3*TICRATE)
-		&& (thing->flags & (MF_MONITOR) || thing->type == MT_SPIKE))
-	|| ((((tmthing->player->charability == CA_TWINSPIN) && (tmthing->player->panim == PA_ABILITY))
-	|| (tmthing->player->charability2 == CA2_MELEE && tmthing->player->panim == PA_ABILITY2))
-		&& (thing->type == MT_SPIKE))))
-		return true;
-
-	// Don't collide with your buddies while NiGHTS-flying.
-	if (tmthing->player && thing->player && (maptol & TOL_NIGHTS)
-		&& ((tmthing->player->pflags & PF_NIGHTSMODE) || (thing->player->pflags & PF_NIGHTSMODE)))
-		return true;
-
-	blockdist = thing->radius + tmthing->radius;
-
-	if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
-		return true; // didn't hit it
-
-	// Force solid players in hide and seek to avoid corner stacking.
-	if (cv_tailspickup.value && gametype != GT_HIDEANDSEEK)
-	{
-		if (tmthing->player && thing->player)
-			return true;
-	}
-
-	if (tmthing->player) // Is the moving/interacting object the player?
-	{
-		if (!tmthing->health)
-			return true;
-
-		// Are you touching the side of the object you're interacting with?
-		else if (thing->z - FixedMul(FRACUNIT, thing->scale) <= tmthing->z + tmthing->height
-			&& thing->z + thing->height + FixedMul(FRACUNIT, thing->scale) >= tmthing->z)
-		{
-			if (thing->flags & MF_MONITOR
-				&& (tmthing->player->pflags & (PF_SPINNING|PF_GLIDING)
-				|| ((tmthing->player->pflags & PF_JUMPED)
-					&& !(tmthing->player->charflags & SF_NOJUMPDAMAGE
-					&& !(tmthing->player->charability == CA_TWINSPIN && tmthing->player->panim == PA_ABILITY)))
-				|| (tmthing->player->charability2 == CA2_MELEE && tmthing->player->panim == PA_ABILITY2)
-				|| ((tmthing->player->charflags & SF_STOMPDAMAGE)
-					&& (P_MobjFlip(tmthing)*(tmthing->z - (thing->z + thing->height/2)) > 0) && (P_MobjFlip(tmthing)*tmthing->momz < 0))))
-			{
-				return false;
-			}
-		}
-	}
-
-	return P_ConsiderSolids(thing);
-}
-#endif
-
 //
 // PIT_CheckThing
 //
@@ -858,7 +668,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 
 		if (tmthing->flags & MF_MISSILE && thing->player && tmthing->target && tmthing->target->player
 		&& thing->player->ctfteam == tmthing->target->player->ctfteam
-		&& thing->player->pflags & PF_CARRIED && thing->tracer == tmthing->target)
+		&& thing->player->powers[pw_carry] == CR_PLAYER && thing->tracer == tmthing->target)
 			return true; // Don't give rings to your carry player by accident.
 
 		if (thing->type == MT_EGGSHIELD)
@@ -901,7 +711,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 			&& tmthing->target != thing)
 		{
 			// Hop on the missile for a ride!
-			thing->player->pflags |= PF_ITEMHANG;
+			thing->player->powers[pw_carry] = CR_GENERIC;
 			thing->player->pflags &= ~PF_JUMPED;
 			P_SetTarget(&thing->tracer, tmthing);
 			P_SetTarget(&tmthing->target, thing); // Set owner to the player
@@ -920,7 +730,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 
 			return true;
 		}
-		else if (tmthing->type == MT_BLACKEGGMAN_MISSILE && thing->player && ((thing->player->pflags & PF_ITEMHANG) || (thing->player->pflags & PF_JUMPED)))
+		else if (tmthing->type == MT_BLACKEGGMAN_MISSILE && thing->player && ((thing->player->powers[pw_carry] == CR_GENERIC) || (thing->player->pflags & PF_JUMPED)))
 		{
 			// Ignore
 		}
@@ -1121,7 +931,8 @@ static boolean PIT_CheckThing(mobj_t *thing)
 	else if (thing->player) {
 		if (thing->player-players == consoleplayer && botingame)
 			CV_SetValue(&cv_analog2, true);
-		thing->player->pflags &= ~PF_CARRIED;
+		if (thing->player->powers[pw_carry] == CR_PLAYER)
+			thing->player->powers[pw_carry] = CR_NONE;
 	}
 
 	if (thing->player)
@@ -1199,8 +1010,99 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		if (iwassprung) // this spring caused you to gain MFE_SPRUNG just now...
 			return false; // "cancel" P_TryMove via blocking so you keep your current position
 	}
-	else
-		return P_ConsiderSolids(thing);
+	// Monitors are not treated as solid to players who are jumping, spinning or gliding,
+	// unless it's a CTF team monitor and you're on the wrong team
+	else if (thing->flags & MF_MONITOR && tmthing->player
+	&& (tmthing->player->pflags & (PF_SPINNING|PF_GLIDING)
+		|| ((tmthing->player->pflags & PF_JUMPED)
+			&& !(tmthing->player->charflags & SF_NOJUMPDAMAGE
+			&& !(tmthing->player->charability == CA_TWINSPIN && tmthing->player->panim == PA_ABILITY)))
+		|| (tmthing->player->charability2 == CA2_MELEE && tmthing->player->panim == PA_ABILITY2)
+		|| ((tmthing->player->charflags & SF_STOMPDAMAGE)
+			&& (P_MobjFlip(tmthing)*(tmthing->z - (thing->z + thing->height/2)) > 0) && (P_MobjFlip(tmthing)*tmthing->momz < 0)))
+	&& !((thing->type == MT_REDRINGBOX && tmthing->player->ctfteam != 1) || (thing->type == MT_BLUERINGBOX && tmthing->player->ctfteam != 2)))
+		;
+	// z checking at last
+	// Treat noclip things as non-solid!
+	else if ((thing->flags & (MF_SOLID|MF_NOCLIP)) == MF_SOLID
+		&& (tmthing->flags & (MF_SOLID|MF_NOCLIP)) == MF_SOLID)
+	{
+		fixed_t topz, tmtopz;
+
+		if (tmthing->eflags & MFE_VERTICALFLIP)
+		{
+			// pass under
+			tmtopz = tmthing->z;
+
+			if (tmtopz > thing->z + thing->height)
+			{
+				if (thing->z + thing->height > tmfloorz)
+				{
+					tmfloorz = thing->z + thing->height;
+#ifdef ESLOPE
+					tmfloorslope = NULL;
+#endif
+				}
+				return true;
+			}
+
+			topz = thing->z - FixedMul(FRACUNIT, thing->scale);
+
+			// block only when jumping not high enough,
+			// (dont climb max. 24units while already in air)
+			// if not in air, let P_TryMove() decide if it's not too high
+			if (tmthing->player && tmthing->z + tmthing->height > topz
+				&& tmthing->z + tmthing->height < tmthing->ceilingz)
+				return false; // block while in air
+
+			if (thing->flags & MF_SPRING)
+				;
+			else if (topz < tmceilingz && tmthing->z+tmthing->height <= thing->z+thing->height)
+			{
+				tmceilingz = topz;
+#ifdef ESLOPE
+				tmceilingslope = NULL;
+#endif
+				tmfloorthing = thing; // thing we may stand on
+			}
+		}
+		else
+		{
+			// pass under
+			tmtopz = tmthing->z + tmthing->height;
+
+			if (tmtopz < thing->z)
+			{
+				if (thing->z < tmceilingz)
+				{
+					tmceilingz = thing->z;
+#ifdef ESLOPE
+					tmceilingslope = NULL;
+#endif
+				}
+				return true;
+			}
+
+			topz = thing->z + thing->height + FixedMul(FRACUNIT, thing->scale);
+
+			// block only when jumping not high enough,
+			// (dont climb max. 24units while already in air)
+			// if not in air, let P_TryMove() decide if it's not too high
+			if (tmthing->player && tmthing->z < topz && tmthing->z > tmthing->floorz)
+				return false; // block while in air
+
+			if (thing->flags & MF_SPRING)
+				;
+			else if (topz > tmfloorz && tmthing->z >= thing->z)
+			{
+				tmfloorz = topz;
+#ifdef ESLOPE
+				tmfloorslope = NULL;
+#endif
+				tmfloorthing = thing; // thing we may stand on
+			}
+		}
+	}
 
 	// not solid not blocked
 	return true;
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 7d9c32451..c156581c0 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -3998,20 +3998,23 @@ static void P_PlayerMobjThinker(mobj_t *mobj)
 	mobj->eflags &= ~MFE_JUSTSTEPPEDDOWN;
 
 	// Zoom tube
-	if (mobj->tracer && mobj->tracer->type == MT_TUBEWAYPOINT)
+	if (mobj->tracer)
 	{
-		P_UnsetThingPosition(mobj);
-		mobj->x += mobj->momx;
-		mobj->y += mobj->momy;
-		mobj->z += mobj->momz;
-		P_SetThingPosition(mobj);
-		P_CheckPosition(mobj, mobj->x, mobj->y);
-		goto animonly;
-	}
-	else if (mobj->player->pflags & PF_MACESPIN && mobj->tracer)
-	{
-		P_CheckPosition(mobj, mobj->x, mobj->y);
-		goto animonly;
+		if (mobj->player->powers[pw_carry] == CR_ZOOMTUBE)
+		{
+			P_UnsetThingPosition(mobj);
+			mobj->x += mobj->momx;
+			mobj->y += mobj->momy;
+			mobj->z += mobj->momz;
+			P_SetThingPosition(mobj);
+			P_CheckPosition(mobj, mobj->x, mobj->y);
+			goto animonly;
+		}
+		else if (mobj->player->powers[pw_carry] == CR_MACESPIN)
+		{
+			P_CheckPosition(mobj, mobj->x, mobj->y);
+			goto animonly;
+		}
 	}
 
 	// Needed for gravity boots
diff --git a/src/p_spec.c b/src/p_spec.c
index a3d6cecfa..ed8a12a5d 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -3889,7 +3889,7 @@ DoneSection2:
 				mobj_t *mo2;
 				angle_t an;
 
-				if (player->mo->tracer && player->mo->tracer->type == MT_TUBEWAYPOINT)
+				if (player->mo->tracer && player->mo->tracer->type == MT_TUBEWAYPOINT && player->powers[pw_carry] == CR_ZOOMTUBE)
 					break;
 
 				// Find line #3 tagged to this sector
@@ -3938,6 +3938,7 @@ DoneSection2:
 					break; // behind back
 
 				P_SetTarget(&player->mo->tracer, waypoint);
+				player->powers[pw_carry] = CR_ZOOMTUBE;
 				player->speed = speed;
 				player->pflags |= PF_SPINNING;
 				player->pflags &= ~PF_JUMPED;
@@ -3962,7 +3963,7 @@ DoneSection2:
 				mobj_t *mo2;
 				angle_t an;
 
-				if (player->mo->tracer && player->mo->tracer->type == MT_TUBEWAYPOINT)
+				if (player->mo->tracer && player->mo->tracer->type == MT_TUBEWAYPOINT && player->powers[pw_carry] == CR_ZOOMTUBE)
 					break;
 
 				// Find line #3 tagged to this sector
@@ -4012,6 +4013,7 @@ DoneSection2:
 					break; // behind back
 
 				P_SetTarget(&player->mo->tracer, waypoint);
+				player->powers[pw_carry] = CR_ZOOMTUBE;
 				player->speed = speed;
 				player->pflags |= PF_SPINNING;
 				player->pflags &= ~PF_JUMPED;
@@ -4084,7 +4086,7 @@ DoneSection2:
 				vertex_t v1, v2, resulthigh, resultlow;
 				mobj_t *highest = NULL;
 
-				if (player->mo->tracer && player->mo->tracer->type == MT_TUBEWAYPOINT)
+				if (player->mo->tracer && player->mo->tracer->type == MT_TUBEWAYPOINT && player->powers[pw_carry] == CR_ROPEHANG)
 					break;
 
 				if (player->mo->momz > 0)
@@ -4305,6 +4307,7 @@ DoneSection2:
 				}
 
 				P_SetTarget(&player->mo->tracer, closest);
+				player->powers[pw_carry] = CR_ROPEHANG;
 
 				// Option for static ropes.
 				if (lines[lineindex].flags & ML_NOCLIMB)
@@ -4312,7 +4315,7 @@ DoneSection2:
 				else
 					player->speed = speed;
 
-				player->pflags |= PF_ROPEHANG;
+				player->powers[pw_carry] = CR_ROPEHANG;
 
 				S_StartSound(player->mo, sfx_s3k4a);
 
@@ -7165,7 +7168,7 @@ static inline boolean PIT_PushThing(mobj_t *thing)
 	if (thing->eflags & MFE_PUSHED)
 		return false;
 
-	if (thing->player && thing->player->pflags & PF_ROPEHANG)
+	if (thing->player && thing->player->powers[pw_carry] == CR_ROPEHANG)
 		return false;
 
 	// Allow this to affect pushable objects at some point?
@@ -7398,7 +7401,7 @@ void T_Pusher(pusher_t *p)
 		if (thing->eflags & MFE_PUSHED)
 			continue;
 
-		if (thing->player && thing->player->pflags & PF_ROPEHANG)
+		if (thing->player && thing->player->powers[pw_carry] == CR_ROPEHANG)
 			continue;
 
 		if (thing->player && (thing->state == &states[thing->info->painstate]) && (thing->player->powers[pw_flashing] > (flashingtics/4)*3 && thing->player->powers[pw_flashing] <= flashingtics))
diff --git a/src/p_telept.c b/src/p_telept.c
index 4921040b4..671accb0f 100644
--- a/src/p_telept.c
+++ b/src/p_telept.c
@@ -160,9 +160,9 @@ boolean P_Teleport(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle
 			INT32 p;
 			// Search for any players you might be carrying, so you can get them off before they end up being taken with you!
 			for (p = 0; p < MAXPLAYERS; p++)
-				if (playeringame[p] && players[p].mo && players[p].pflags & PF_CARRIED && players[p].mo->tracer == thing)
+				if (playeringame[p] && players[p].mo && players[p].powers[pw_carry] == CR_PLAYER && players[p].mo->tracer == thing)
 				{
-					players[p].pflags &= ~PF_CARRIED;
+					players[p].powers[pw_carry] = CR_NONE;
 					break;
 				}
 			thing->player->cmomx = thing->player->cmomy = 0;
diff --git a/src/p_user.c b/src/p_user.c
index f6e9270ec..4f8a96de0 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -873,7 +873,7 @@ void P_DoPlayerPain(player_t *player, mobj_t *source, mobj_t *inflictor)
 
 	P_InstaThrust(player->mo, ang, fallbackspeed);
 
-	if (player->pflags & PF_ROPEHANG)
+	if (player->powers[pw_carry] == CR_ROPEHANG)
 		P_SetTarget(&player->mo->tracer, NULL);
 
 	// Point penalty for hitting a hazard during tag.
@@ -900,7 +900,8 @@ void P_DoPlayerPain(player_t *player, mobj_t *source, mobj_t *inflictor)
 // Useful when you want to kill everything the player is doing.
 void P_ResetPlayer(player_t *player)
 {
-	player->pflags &= ~(PF_ROPEHANG|PF_ITEMHANG|PF_MACESPIN|PF_SPINNING|PF_STARTDASH|PF_JUMPED|PF_GLIDING|PF_THOKKED|PF_CARRIED);
+	player->pflags &= ~(PF_SPINNING|PF_STARTDASH|PF_JUMPED|PF_GLIDING|PF_THOKKED);
+	player->powers[pw_carry] = CR_NONE;
 	player->jumping = 0;
 	player->secondjump = 0;
 	player->glidetime = 0;
@@ -3415,7 +3416,7 @@ static void P_DoSuperStuff(player_t *player)
 		? (SKINCOLOR_SUPERSILVER1 + 5*((leveltime >> 1) % 7)) // A wholesome easter egg.
 		: skins[player->skin].supercolor + (unsigned)abs( ( (signed)(leveltime >> 1) % 9) - 4); // This is where super flashing is handled.
 
-		if ((cmd->forwardmove != 0 || cmd->sidemove != 0 || player->pflags & (PF_CARRIED|PF_ROPEHANG|PF_ITEMHANG|PF_MACESPIN))
+		if ((cmd->forwardmove != 0 || cmd->sidemove != 0 || player->powers[pw_carry])
 		&& !(leveltime % TICRATE) && (player->mo->momx || player->mo->momy))
 		{
 			spark = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SUPERSPARK);
@@ -3583,22 +3584,22 @@ void P_DoJump(player_t *player, boolean soundandstate)
 			return;
 
 		// Jump this high.
-		if (player->pflags & PF_CARRIED)
+		if (player->powers[pw_carry] == CR_PLAYER)
 		{
 			player->mo->momz = 9*FRACUNIT;
-			player->pflags &= ~PF_CARRIED;
+			player->powers[pw_carry] = CR_NONE;
 			if (player-players == consoleplayer && botingame)
 				CV_SetValue(&cv_analog2, true);
 		}
-		else if (player->pflags & PF_ITEMHANG)
+		else if (player->powers[pw_carry] == CR_GENERIC)
 		{
 			player->mo->momz = 9*FRACUNIT;
-			player->pflags &= ~PF_ITEMHANG;
+			player->powers[pw_carry] = CR_NONE;
 		}
-		else if (player->pflags & PF_ROPEHANG)
+		else if (player->powers[pw_carry] == CR_ROPEHANG)
 		{
 			player->mo->momz = 12*FRACUNIT;
-			player->pflags &= ~PF_ROPEHANG;
+			player->powers[pw_carry] = CR_NONE;
 			P_SetTarget(&player->mo->tracer, NULL);
 		}
 		else if (player->mo->eflags & MFE_GOOWATER)
@@ -3935,9 +3936,9 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
 
 	if (cmd->buttons & BT_USE && !(player->pflags & PF_JUMPDOWN) && !player->exiting && !P_PlayerInPain(player))
 	{
-		if (onground || player->climbing || player->pflags & (PF_CARRIED|PF_ITEMHANG|PF_ROPEHANG))
+		if (player->mo->tracer && player->powers[pw_carry] == CR_MACESPIN)
 		{}
-		else if (player->pflags & PF_MACESPIN && player->mo->tracer)
+		else if (onground || player->climbing || (player->mo->tracer && player->powers[pw_carry]))
 		{}
 		else if (!(player->pflags & PF_SLIDING) && ((gametype != GT_CTF) || (!player->gotflag)))
 		{
@@ -4010,19 +4011,19 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
 			player->secondjump = 0;
 			player->pflags &= ~PF_THOKKED;
 		}
+		else if (player->powers[pw_carry] == CR_MACESPIN && player->mo->tracer)
+		{
+			player->powers[pw_carry] = CR_NONE;
+			player->powers[pw_flashing] = TICRATE/4;
+		}
 		else
 		// can't jump while in air, can't jump while jumping
-		if (onground || player->climbing || player->pflags & (PF_CARRIED|PF_ITEMHANG|PF_ROPEHANG))
+		if (onground || player->climbing || player->powers[pw_carry])
 		{
 			P_DoJump(player, true);
 			player->secondjump = 0;
 			player->pflags &= ~PF_THOKKED;
 		}
-		else if (player->pflags & PF_MACESPIN && player->mo->tracer)
-		{
-			player->pflags &= ~PF_MACESPIN;
-			player->powers[pw_flashing] = TICRATE/4;
-		}
 		else if (player->pflags & PF_SLIDING || (gametype == GT_CTF && player->gotflag))
 			;
 		else if (P_SuperReady(player))
@@ -6973,7 +6974,7 @@ static void P_MovePlayer(player_t *player)
 
 	// Check for teeter!
 	if (!(player->mo->momz || player->mo->momx || player->mo->momy) && !(player->mo->eflags & MFE_GOOWATER)
-	&& player->panim == PA_IDLE && !(player->pflags & (PF_CARRIED|PF_ITEMHANG|PF_ROPEHANG)))
+	&& player->panim == PA_IDLE && !(player->powers[pw_carry]))
 		P_DoTeeter(player);
 
 	// Toss a flag
@@ -7244,6 +7245,7 @@ static void P_DoZoomTube(player_t *player)
 		else
 		{
 			P_SetTarget(&player->mo->tracer, NULL); // Else, we just let him fly.
+			player->powers[pw_carry] = CR_NONE;
 
 			CONS_Debug(DBG_GAMELOGIC, "Next waypoint not found, releasing from track...\n");
 		}
@@ -7291,7 +7293,7 @@ static void P_DoRopeHang(player_t *player)
 		P_SetTarget(&player->mo->tracer, NULL);
 
 		player->pflags |= PF_JUMPED;
-		player->pflags &= ~PF_ROPEHANG;
+		player->powers[pw_carry] = CR_NONE;
 
 		if (!(player->pflags & PF_SLIDING) && (player->pflags & PF_JUMPED)
 		&& !(player->panim == PA_JUMP))
@@ -7309,6 +7311,10 @@ static void P_DoRopeHang(player_t *player)
 
 	sequence = player->mo->tracer->threshold;
 
+	// If not allowed to move, we're done here.
+	if (!speed)
+		return;
+
 	// change slope
 	dist = P_AproxDistance(P_AproxDistance(player->mo->tracer->x - player->mo->x, player->mo->tracer->y - player->mo->y), player->mo->tracer->z - playerz);
 
@@ -7319,22 +7325,28 @@ static void P_DoRopeHang(player_t *player)
 	speedy = FixedMul(FixedDiv(player->mo->tracer->y - player->mo->y, dist), (speed));
 	speedz = FixedMul(FixedDiv(player->mo->tracer->z - playerz, dist), (speed));
 
-	// If not allowed to move, we're done here.
-	if (!speed)
-		return;
-
 	// Calculate the distance between the player and the waypoint
 	// 'dist' already equals this.
 
-	// Will the player be FURTHER away if the momx/momy/momz is added to
-	// his current coordinates, or closer? (shift down to fracunits to avoid approximation errors)
-	if (dist>>FRACBITS <= P_AproxDistance(P_AproxDistance(player->mo->tracer->x - player->mo->x - speedx, player->mo->tracer->y - player->mo->y - speedy), player->mo->tracer->z - playerz - speedz)>>FRACBITS)
+	// Will the player go past the waypoint?
+	if (
+		((speedx || speedy)
+		&& (R_PointToAngle2(player->mo->x, player->mo->y, player->mo->tracer->x, player->mo->tracer->y)
+			- R_PointToAngle2(player->mo->x + speedx, player->mo->y + speedy, player->mo->tracer->x, player->mo->tracer->y)) > ANG1)
+	|| ((speedz)
+		&& ((playerz - player->mo->tracer->z) > 0) != ((playerz + speedz - player->mo->tracer->z) > 0))
+		)
 	{
+		if (speed > dist)
+			speed -= dist;
+		else
+			speed = 0;
 		// If further away, set XYZ of player to waypoint location
 		P_UnsetThingPosition(player->mo);
 		player->mo->x = player->mo->tracer->x;
 		player->mo->y = player->mo->tracer->y;
 		player->mo->z = player->mo->tracer->z - player->mo->height;
+		playerz = player->mo->tracer->z;
 		P_SetThingPosition(player->mo);
 
 		CONS_Debug(DBG_GAMELOGIC, "Looking for next waypoint...\n");
@@ -7390,6 +7402,8 @@ static void P_DoRopeHang(player_t *player)
 		{
 			CONS_Debug(DBG_GAMELOGIC, "Found waypoint (sequence %d, number %d).\n", waypoint->threshold, waypoint->health);
 
+			P_SetTarget(&player->mo->tracer, waypoint);
+
 			// calculate MOMX/MOMY/MOMZ for next waypoint
 			// change slope
 			dist = P_AproxDistance(P_AproxDistance(player->mo->tracer->x - player->mo->x, player->mo->tracer->y - player->mo->y), player->mo->tracer->z - playerz);
@@ -7400,15 +7414,12 @@ static void P_DoRopeHang(player_t *player)
 			player->mo->momx = FixedMul(FixedDiv(player->mo->tracer->x - player->mo->x, dist), (speed));
 			player->mo->momy = FixedMul(FixedDiv(player->mo->tracer->y - player->mo->y, dist), (speed));
 			player->mo->momz = FixedMul(FixedDiv(player->mo->tracer->z - playerz, dist), (speed));
-
-			P_SetTarget(&player->mo->tracer, waypoint);
 		}
 		else
 		{
 			if (player->mo->tracer->flags & MF_SLIDEME)
 			{
 				player->pflags |= PF_JUMPED;
-				player->pflags &= ~PF_ROPEHANG;
 
 				if (!(player->pflags & PF_SLIDING) && (player->pflags & PF_JUMPED)
 				&& !(player->panim == PA_JUMP))
@@ -7416,6 +7427,7 @@ static void P_DoRopeHang(player_t *player)
 			}
 
 			P_SetTarget(&player->mo->tracer, NULL);
+			player->powers[pw_carry] = CR_NONE;
 
 			CONS_Debug(DBG_GAMELOGIC, "Next waypoint not found!\n");
 		}
@@ -8109,7 +8121,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
 	{
 		dist = camdist;
 
-		if (player->climbing || player->exiting || player->playerstate == PST_DEAD || (player->pflags & (PF_MACESPIN|PF_ITEMHANG|PF_ROPEHANG)))
+		if (player->climbing || player->exiting || player->playerstate == PST_DEAD || (player->powers[pw_carry] && player->powers[pw_carry] != CR_PLAYER))
 			dist <<= 1;
 	}
 
@@ -8979,9 +8991,9 @@ void P_PlayerThink(player_t *player)
 	//  for a bit after a teleport.
 	if (player->mo->reactiontime)
 		player->mo->reactiontime--;
-	else if (player->mo->tracer && player->mo->tracer->type == MT_TUBEWAYPOINT)
+	else if (player->mo->tracer && player->mo->tracer->type == MT_TUBEWAYPOINT && (player->powers[pw_carry] == CR_ROPEHANG || player->powers[pw_carry] == CR_ZOOMTUBE))
 	{
-		if (player->pflags & PF_ROPEHANG)
+		if (player->powers[pw_carry] == CR_ROPEHANG)
 		{
 			if (!P_AnalogMove(player))
 				player->mo->angle = (cmd->angleturn<<16 /* not FRACBITS */);
@@ -8994,7 +9006,7 @@ void P_PlayerThink(player_t *player)
 			P_SetPlayerMobjState(player->mo, S_PLAY_RIDE);
 			P_DoJumpStuff(player, &player->cmd);
 		}
-		else
+		else if (player->powers[pw_carry] == CR_ZOOMTUBE)
 		{
 			P_DoZoomTube(player);
 			if (!(player->panim == PA_ROLL))
@@ -9406,14 +9418,14 @@ void P_PlayerAfterThink(player_t *player)
 	&& !(player->charflags & SF_NOJUMPSPIN))
 		P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
 
-	if (player->pflags & PF_CARRIED && player->mo->tracer)
+	if (player->powers[pw_carry] == CR_PLAYER && player->mo->tracer)
 	{
 		player->mo->height = FixedDiv(P_GetPlayerHeight(player), FixedDiv(14*FRACUNIT,10*FRACUNIT));
 
 		if (player->mo->tracer->player
 			&& !player->mo->tracer->player->powers[pw_tailsfly]
 			&& player->mo->tracer->state-states != S_PLAY_FLY_TIRED)
-				player->pflags &= ~PF_CARRIED;
+				player->powers[pw_carry] = CR_NONE;
 
 		if (player->mo->eflags & MFE_VERTICALFLIP)
 		{
@@ -9421,7 +9433,7 @@ void P_PlayerAfterThink(player_t *player)
 				&& (player->mo->tracer->eflags & MFE_VERTICALFLIP)) // Reverse gravity check for the carrier - Flame
 				player->mo->z = player->mo->tracer->z + player->mo->tracer->height + FixedMul(FRACUNIT, player->mo->scale);
 			else
-				player->pflags &= ~PF_CARRIED;
+				player->powers[pw_carry] = CR_NONE;
 		}
 		else
 		{
@@ -9429,11 +9441,11 @@ void P_PlayerAfterThink(player_t *player)
 				&& !(player->mo->tracer->eflags & MFE_VERTICALFLIP)) // Correct gravity check for the carrier - Flame
 				player->mo->z = player->mo->tracer->z - player->mo->height - FixedMul(FRACUNIT, player->mo->scale);
 			else
-				player->pflags &= ~PF_CARRIED;
+				player->powers[pw_carry] = CR_NONE;
 		}
 
 		if (player->mo->tracer->health <= 0)
-			player->pflags &= ~PF_CARRIED;
+			player->powers[pw_carry] = CR_NONE;
 		else
 		{
 			P_TryMove(player->mo, player->mo->tracer->x, player->mo->tracer->y, true);
@@ -9456,14 +9468,14 @@ void P_PlayerAfterThink(player_t *player)
 		}
 
 		if (P_AproxDistance(player->mo->x - player->mo->tracer->x, player->mo->y - player->mo->tracer->y) > player->mo->radius)
-			player->pflags &= ~PF_CARRIED;
+			player->powers[pw_carry] = CR_NONE;
 
 		P_SetPlayerMobjState(player->mo, S_PLAY_RIDE);
 
 		if (player-players == consoleplayer && botingame)
-			CV_SetValue(&cv_analog2, !(player->pflags & PF_CARRIED));
+			CV_SetValue(&cv_analog2, (player->powers[pw_carry] != CR_PLAYER));
 	}
-	else if (player->pflags & PF_ITEMHANG && player->mo->tracer)
+	else if (player->powers[pw_carry] == CR_GENERIC && player->mo->tracer)
 	{
 		// tracer is what you're hanging onto
 		P_UnsetThingPosition(player->mo);
@@ -9491,12 +9503,12 @@ void P_PlayerAfterThink(player_t *player)
 			if (player->mo->z <= player->mo->floorz
 				|| player->mo->tracer->health <= 0)
 			{
-				player->pflags &= ~PF_ITEMHANG;
+				player->powers[pw_carry] = CR_NONE;
 				P_SetTarget(&player->mo->tracer, NULL);
 			}
 		}
 	}
-	else if (player->pflags & PF_MACESPIN && player->mo->tracer && player->mo->tracer->target)
+	else if (player->powers[pw_carry] == CR_MACESPIN && player->mo->tracer && player->mo->tracer->target)
 	{
 		player->mo->height = P_GetPlayerSpinHeight(player);
 		// tracer is what you're hanging onto....