diff --git a/src/d_player.h b/src/d_player.h
index fd41c762125ac5f9b6b09a5a0f91d635d52711ff..dd0643bd41bc4a95cb5c257ed8b2774721abd850 100644
--- a/src/d_player.h
+++ b/src/d_player.h
@@ -382,7 +382,7 @@ typedef struct player_s
 	fixed_t height; // Bounding box changes.
 	fixed_t spinheight;
 
-	SINT8 lives;
+	SINT8 lives; // number of lives - if == INFLIVES, the player has infinite lives
 	SINT8 continues; // continues that player has acquired
 
 	SINT8 xtralife; // Ring Extra Life counter
@@ -500,4 +500,7 @@ typedef struct player_s
 #endif
 } player_t;
 
+// Value for infinite lives
+#define INFLIVES 0x7F
+
 #endif
diff --git a/src/dehacked.c b/src/dehacked.c
index 1a2f2e239828209a02b94336c9a47a300e4f72de..9b2cf7fc0bd57adfc4a2c370ae173f8e5c43b4c7 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -7614,6 +7614,9 @@ struct {
 	{"WEP_RAIL",WEP_RAIL},
 	{"NUM_WEAPONS",NUM_WEAPONS},
 
+	// Value for infinite lives
+	{"INFLIVES", INFLIVES},
+
 	// Got Flags, for player->gotflag!
 	// Used to be MF_ for some stupid reason, now they're GF_ to stop them looking like mobjflags
 	{"GF_REDFLAG",GF_REDFLAG},
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index 28438599c7c8c9f624f572665d2790614b0b192f..6697eb09bdd9ea8966ab721b7b37a81c8c3c4ff2 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -1275,7 +1275,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
 			}
 		}
 
-		if (G_GametypeUsesLives() && !(gametype == GT_COOP && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != 0x7f)) //show lives
+		if (G_GametypeUsesLives() && !(gametype == GT_COOP && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != INFLIVES)) //show lives
 			V_DrawRightAlignedString(x, y+4, V_ALLOWLOWERCASE|(greycheck ? V_60TRANS : 0), va("%dx", players[tab[i].num].lives));
 		else if (G_TagGametype() && players[tab[i].num].pflags & PF_TAGIT)
 		{
@@ -1413,7 +1413,7 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
 		             | (greycheck ? V_TRANSLUCENT : 0)
 		             | V_ALLOWLOWERCASE, name);
 
-		if (G_GametypeUsesLives() && !(gametype == GT_COOP && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != 0x7f)) //show lives
+		if (G_GametypeUsesLives() && !(gametype == GT_COOP && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != INFLIVES)) //show lives
 			V_DrawRightAlignedString(x, y+4, V_ALLOWLOWERCASE, va("%dx", players[tab[i].num].lives));
 		else if (G_TagGametype() && players[tab[i].num].pflags & PF_TAGIT)
 			V_DrawSmallScaledPatch(x-28, y-4, 0, tagico);
diff --git a/src/m_cheat.c b/src/m_cheat.c
index 0937e40fe7ed819dc4bf0e4458c0c595d265e42e..473209350899e4a5390c5ba73adb2d4dc43a3599 100644
--- a/src/m_cheat.c
+++ b/src/m_cheat.c
@@ -909,7 +909,7 @@ void Command_Setlives_f(void)
 	{
 		SINT8 lives = atoi(COM_Argv(1));
 		if (lives == -1)
-			players[consoleplayer].lives = 0x7f; // infinity!
+			players[consoleplayer].lives = INFLIVES; // infinity!
 		else
 		{
 			// P_GivePlayerLives does value clamping
diff --git a/src/m_menu.c b/src/m_menu.c
index f99f5d860c73e3db25f8b746840d23f60d3e2a4b..778f24ad985027c4bd6533ea6fa02441be29918c 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -5277,7 +5277,7 @@ static void M_PandorasBox(INT32 choice)
 		CV_StealthSetValue(&cv_dummyrings, max(players[consoleplayer].spheres, 0));
 	else
 		CV_StealthSetValue(&cv_dummyrings, max(players[consoleplayer].rings, 0));
-	if (players[consoleplayer].lives == 0x7f)
+	if (players[consoleplayer].lives == INFLIVES)
 		CV_StealthSetValue(&cv_dummylives, -1);
 	else
 		CV_StealthSetValue(&cv_dummylives, players[consoleplayer].lives);
@@ -6334,7 +6334,7 @@ skipsign:
 			y += 25;
 
 			tempx = x + 10;
-			if (savegameinfo[savetodraw].lives != 0x7f
+			if (savegameinfo[savetodraw].lives != INFLIVES
 			&& savegameinfo[savetodraw].lives > 9)
 				tempx -= 4;
 
@@ -6361,7 +6361,7 @@ skiplife:
 
 			V_DrawScaledPatch(tempx + 9, y + 2, 0, patch);
 			tempx += 16;
-			if (savegameinfo[savetodraw].lives == 0x7f)
+			if (savegameinfo[savetodraw].lives == INFLIVES)
 				V_DrawCharacter(tempx, y + 1, '\x16', false);
 			else
 				V_DrawString(tempx, y, 0, va("%d", savegameinfo[savetodraw].lives));
diff --git a/src/p_inter.c b/src/p_inter.c
index bcb20ab6aa75650867316a814667c542e6eee169..57b47f1c6ae74bdb70d687b723e57a587632f2dd 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -2249,7 +2249,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 
 		if ((target->player->lives <= 1) && (netgame || multiplayer) && (gametype == GT_COOP) && (cv_cooplives.value == 0))
 			;
-		else if (!target->player->bot && !target->player->spectator && !G_IsSpecialStage(gamemap) && (target->player->lives != 0x7f)
+		else if (!target->player->bot && !target->player->spectator && !G_IsSpecialStage(gamemap) && (target->player->lives != INFLIVES)
 		 && G_GametypeUsesLives())
 		{
 			target->player->lives -= 1; // Lose a life Tails 03-11-2000
diff --git a/src/p_user.c b/src/p_user.c
index f9c8866437d97e84f85c55c57ac57c5fd9bd2395..e7f5f3248e8ed219d2efece775eaeb70cc79acfa 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -952,7 +952,7 @@ void P_GivePlayerRings(player_t *player, INT32 num_rings)
 		player->rings = 0;
 
 	// Now extra life bonuses are handled here instead of in P_MovePlayer, since why not?
-	if (!ultimatemode && !modeattacking && !G_IsSpecialStage(gamemap) && G_GametypeUsesLives() && player->lives != 0x7f)
+	if (!ultimatemode && !modeattacking && !G_IsSpecialStage(gamemap) && G_GametypeUsesLives() && player->lives != INFLIVES)
 	{
 		INT32 gainlives = 0;
 
@@ -1011,7 +1011,7 @@ void P_GivePlayerLives(player_t *player, INT32 numlives)
 
 	if (gamestate == GS_LEVEL)
 	{
-		if (player->lives == 0x7f || (gametype != GT_COOP && gametype != GT_COMPETITION))
+		if (player->lives == INFLIVES || (gametype != GT_COOP && gametype != GT_COMPETITION))
 		{
 			P_GivePlayerRings(player, 100*numlives);
 			return;
@@ -8401,7 +8401,7 @@ boolean P_GetLives(player_t *player)
 	if (!(netgame || multiplayer)
 	|| (gametype != GT_COOP)
 	|| (cv_cooplives.value == 1)
-	|| (player->lives == 0x7f))
+	|| (player->lives == INFLIVES))
 		return true;
 
 	if ((cv_cooplives.value == 2 || cv_cooplives.value == 0) && player->lives > 0)
@@ -8428,7 +8428,7 @@ boolean P_GetLives(player_t *player)
 	{
 		if (cv_cooplives.value == 2 && (P_IsLocalPlayer(player) || P_IsLocalPlayer(&players[maxlivesplayer])))
 			S_StartSound(NULL, sfx_jshard); // placeholder
-		if (players[maxlivesplayer].lives != 0x7f)
+		if (players[maxlivesplayer].lives != INFLIVES)
 			players[maxlivesplayer].lives--;
 		player->lives++;
 		if (player->lives < 1)
diff --git a/src/st_stuff.c b/src/st_stuff.c
index 0079b8da9dfdf377172ad280b89678821206a772..c4bec135f3847b13a666ff8dcbaf0ecf88884523 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -807,7 +807,7 @@ static void ST_drawLivesArea(void)
 		// lives number
 		if (gametype == GT_RACE)
 		{
-			livescount = 0x7f;
+			livescount = INFLIVES;
 			notgreyedout = true;
 		}
 		else if ((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 3)
@@ -826,9 +826,9 @@ static void ST_drawLivesArea(void)
 				if (players[i].lives > 1)
 					notgreyedout = true;
 
-				if (players[i].lives == 0x7f)
+				if (players[i].lives == INFLIVES)
 				{
-					livescount = 0x7f;
+					livescount = INFLIVES;
 					break;
 				}
 				else if (livescount < 99)
@@ -837,11 +837,11 @@ static void ST_drawLivesArea(void)
 		}
 		else
 		{
-			livescount = (((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 0) ? 0x7f : stplyr->lives);
+			livescount = (((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 0) ? INFLIVES : stplyr->lives);
 			notgreyedout = true;
 		}
 
-		if (livescount == 0x7f)
+		if (livescount == INFLIVES)
 			V_DrawCharacter(hudinfo[HUD_LIVES].x+50, hudinfo[HUD_LIVES].y+8,
 				'\x16' | 0x80 | hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, false);
 		else
@@ -1205,6 +1205,10 @@ static void ST_drawPowerupHUD(void)
 	if (stplyr->spectator || stplyr->playerstate != PST_LIVE)
 		return;
 
+// -------
+// Shields
+// -------
+
 	// Graue 06-18-2004: no V_NOSCALESTART, no SCX, no SCY, snap to right
 	if (stplyr->powers[pw_shield] & SH_NOSTACK)
 	{
@@ -1247,6 +1251,10 @@ static void ST_drawPowerupHUD(void)
 
 	offs -= shieldoffs[q];
 
+// ---------
+// CTF flags
+// ---------
+
 	// YOU have a flag. Display a monitor-like icon for it.
 	if (stplyr->gotflag)
 	{
@@ -1264,11 +1272,20 @@ static void ST_drawPowerupHUD(void)
 
 	offs -= flagoffs[q];
 
+// --------------------
+// Timer-based powerups
+// --------------------
+
+#define DRAWTIMERICON(patch, timer) \
+	V_DrawSmallScaledPatch(offs, hudinfo[HUD_POWERUPS].y, V_PERPLAYER|hudinfo[HUD_POWERUPS].f|V_HUDTRANS, patch); \
+	V_DrawRightAlignedThinString(offs + 16, hudinfo[HUD_POWERUPS].y + 8, V_PERPLAYER|hudinfo[HUD_POWERUPS].f, va("%d", timer/TICRATE));
+
+	// Invincibility, both from monitor and after being hit
 	invulntime = stplyr->powers[pw_flashing] ? stplyr->powers[pw_flashing] : stplyr->powers[pw_invulnerability];
+	// Note: pw_flashing always makes the icon flicker regardless of time, unlike pw_invulnerability
 	if (stplyr->powers[pw_invulnerability] > 3*TICRATE || (invulntime && leveltime & 1))
 	{
-		V_DrawSmallScaledPatch(offs, hudinfo[HUD_POWERUPS].y, V_PERPLAYER|hudinfo[HUD_POWERUPS].f|V_HUDTRANS, invincibility);
-		V_DrawRightAlignedThinString(offs + 16, hudinfo[HUD_POWERUPS].y + 8, V_PERPLAYER|hudinfo[HUD_POWERUPS].f, va("%d", invulntime/TICRATE));
+		DRAWTIMERICON(invincibility, invulntime)
 	}
 
 	if (invulntime > 7)
@@ -1281,10 +1298,10 @@ static void ST_drawPowerupHUD(void)
 		offs -= a;
 	}
 
+	// Super Sneakers
 	if (stplyr->powers[pw_sneakers] > 3*TICRATE || (stplyr->powers[pw_sneakers] && leveltime & 1))
 	{
-		V_DrawSmallScaledPatch(offs, hudinfo[HUD_POWERUPS].y, V_PERPLAYER|hudinfo[HUD_POWERUPS].f|V_HUDTRANS, sneakers);
-		V_DrawRightAlignedThinString(offs + 16, hudinfo[HUD_POWERUPS].y + 8, V_PERPLAYER|hudinfo[HUD_POWERUPS].f, va("%d", stplyr->powers[pw_sneakers]/TICRATE));
+		DRAWTIMERICON(sneakers, stplyr->powers[pw_sneakers])
 	}
 
 	if (stplyr->powers[pw_sneakers] > 7)
@@ -1297,12 +1314,13 @@ static void ST_drawPowerupHUD(void)
 		offs -= a;
 	}
 
+	// Gravity Boots
 	if (stplyr->powers[pw_gravityboots] > 3*TICRATE || (stplyr->powers[pw_gravityboots] && leveltime & 1))
 	{
-		V_DrawSmallScaledPatch(offs, hudinfo[HUD_POWERUPS].y, V_PERPLAYER|hudinfo[HUD_POWERUPS].f|V_HUDTRANS, gravboots);
-		V_DrawRightAlignedThinString(offs + 16, hudinfo[HUD_POWERUPS].y + 8, V_PERPLAYER|hudinfo[HUD_POWERUPS].f, va("%d", stplyr->powers[pw_gravityboots]/TICRATE));
+		DRAWTIMERICON(gravboots, stplyr->powers[pw_gravityboots])
 	}
 
+#undef DRAWTIMERICON
 #undef ICONSEP
 }
 
@@ -1361,60 +1379,64 @@ static void ST_drawNightsRecords(void)
 	if (stplyr->texttimer < TICRATE/2)
 		aflag |= (9 - 9*stplyr->texttimer/(TICRATE/2)) << V_ALPHASHIFT;
 
-	// A "Bonus Time Start" by any other name...
-	if (stplyr->textvar == 1)
+	switch (stplyr->textvar)
 	{
-		V_DrawCenteredString(BASEVIDWIDTH/2, 52, V_GREENMAP|aflag, M_GetText("GET TO THE GOAL!"));
-		V_DrawCenteredString(BASEVIDWIDTH/2, 60,            aflag, M_GetText("SCORE MULTIPLIER START!"));
-
-		if (stplyr->finishedtime)
+		case 1: // A "Bonus Time Start" by any other name...
 		{
-			V_DrawString(BASEVIDWIDTH/2 - 48, 140, aflag, "TIME:");
-			V_DrawString(BASEVIDWIDTH/2 - 48, 148, aflag, "BONUS:");
-			V_DrawRightAlignedString(BASEVIDWIDTH/2 + 48, 140, V_ORANGEMAP|aflag, va("%d", (stplyr->startedtime - stplyr->finishedtime)/TICRATE));
-			V_DrawRightAlignedString(BASEVIDWIDTH/2 + 48, 148, V_ORANGEMAP|aflag, va("%d", (stplyr->finishedtime/TICRATE) * 100));
-		}
-	}
-
-	// Get n [more] Spheres
-	else if (stplyr->textvar <= 3 && stplyr->textvar >= 2)
-	{
-		if (!stplyr->capsule)
-			return;
-
-		// Yes, this string is an abomination.
-		V_DrawCenteredString(BASEVIDWIDTH/2, 60, aflag,
-		                     va(M_GetText("\x80GET\x82 %d\x80 %s%s%s!"), stplyr->capsule->health,
-		                        (stplyr->textvar == 3) ? M_GetText("MORE ") : "",
-		                        (G_IsSpecialStage(gamemap)) ? "SPHERE" : "CHIP",
-		                        (stplyr->capsule->health > 1) ? "S" : ""));
-	}
-
-	// End Bonus
-	else if (stplyr->textvar == 4)
-	{
-		V_DrawString(BASEVIDWIDTH/2 - 56, 140, aflag, (G_IsSpecialStage(gamemap)) ? "SPHERES:" : "CHIPS:");
-		V_DrawString(BASEVIDWIDTH/2 - 56, 148, aflag, "BONUS:");
-		V_DrawRightAlignedString(BASEVIDWIDTH/2 + 56, 140, V_ORANGEMAP|aflag, va("%d", stplyr->finishedspheres));
-		V_DrawRightAlignedString(BASEVIDWIDTH/2 + 56, 148, V_ORANGEMAP|aflag, va("%d", stplyr->finishedspheres * 50));
-		ST_DrawNightsOverlayNum((BASEVIDWIDTH/2 + 56)<<FRACBITS, 160<<FRACBITS, FRACUNIT, aflag, stplyr->lastmarescore, nightsnum, SKINCOLOR_AZURE);
+			V_DrawCenteredString(BASEVIDWIDTH/2, 52, V_GREENMAP|aflag, M_GetText("GET TO THE GOAL!"));
+			V_DrawCenteredString(BASEVIDWIDTH/2, 60,            aflag, M_GetText("SCORE MULTIPLIER START!"));
 
-		// If new record, say so!
-		if (!(netgame || multiplayer) && G_GetBestNightsScore(gamemap, stplyr->lastmare + 1) <= stplyr->lastmarescore)
-		{
-			if (stplyr->texttimer & 16)
-				V_DrawCenteredString(BASEVIDWIDTH/2, 184, V_YELLOWMAP|aflag, "* NEW RECORD *");
+			if (stplyr->finishedtime)
+			{
+				V_DrawString(BASEVIDWIDTH/2 - 48, 140, aflag, "TIME:");
+				V_DrawString(BASEVIDWIDTH/2 - 48, 148, aflag, "BONUS:");
+				V_DrawRightAlignedString(BASEVIDWIDTH/2 + 48, 140, V_ORANGEMAP|aflag, va("%d", (stplyr->startedtime - stplyr->finishedtime)/TICRATE));
+				V_DrawRightAlignedString(BASEVIDWIDTH/2 + 48, 148, V_ORANGEMAP|aflag, va("%d", (stplyr->finishedtime/TICRATE) * 100));
+			}
+			break;
 		}
+		case 2: // Get n Spheres
+		case 3: // Get n more Spheres
+		{
+			if (!stplyr->capsule)
+				return;
 
-		if (P_HasGrades(gamemap, stplyr->lastmare + 1))
+			// Yes, this string is an abomination.
+			V_DrawCenteredString(BASEVIDWIDTH/2, 60, aflag,
+								 va(M_GetText("\x80GET\x82 %d\x80 %s%s%s!"), stplyr->capsule->health,
+									(stplyr->textvar == 3) ? M_GetText("MORE ") : "",
+									(G_IsSpecialStage(gamemap)) ? "SPHERE" : "CHIP",
+									(stplyr->capsule->health > 1) ? "S" : ""));
+			break;
+		}
+		case 4: // End Bonus
 		{
-			if (aflag)
-				V_DrawTranslucentPatch(BASEVIDWIDTH/2 + 60, 160, aflag,
-				                       ngradeletters[P_GetGrade(stplyr->lastmarescore, gamemap, stplyr->lastmare)]);
-			else
-				V_DrawScaledPatch(BASEVIDWIDTH/2 + 60, 160, 0,
-				                  ngradeletters[P_GetGrade(stplyr->lastmarescore, gamemap, stplyr->lastmare)]);
+			V_DrawString(BASEVIDWIDTH/2 - 56, 140, aflag, (G_IsSpecialStage(gamemap)) ? "SPHERES:" : "CHIPS:");
+			V_DrawString(BASEVIDWIDTH/2 - 56, 148, aflag, "BONUS:");
+			V_DrawRightAlignedString(BASEVIDWIDTH/2 + 56, 140, V_ORANGEMAP|aflag, va("%d", stplyr->finishedspheres));
+			V_DrawRightAlignedString(BASEVIDWIDTH/2 + 56, 148, V_ORANGEMAP|aflag, va("%d", stplyr->finishedspheres * 50));
+			ST_DrawNightsOverlayNum((BASEVIDWIDTH/2 + 56)<<FRACBITS, 160<<FRACBITS, FRACUNIT, aflag, stplyr->lastmarescore, nightsnum, SKINCOLOR_AZURE);
+
+			// If new record, say so!
+			if (!(netgame || multiplayer) && G_GetBestNightsScore(gamemap, stplyr->lastmare + 1) <= stplyr->lastmarescore)
+			{
+				if (stplyr->texttimer & 16)
+					V_DrawCenteredString(BASEVIDWIDTH/2, 184, V_YELLOWMAP|aflag, "* NEW RECORD *");
+			}
+
+			if (P_HasGrades(gamemap, stplyr->lastmare + 1))
+			{
+				if (aflag)
+					V_DrawTranslucentPatch(BASEVIDWIDTH/2 + 60, 160, aflag,
+										   ngradeletters[P_GetGrade(stplyr->lastmarescore, gamemap, stplyr->lastmare)]);
+				else
+					V_DrawScaledPatch(BASEVIDWIDTH/2 + 60, 160, 0,
+									  ngradeletters[P_GetGrade(stplyr->lastmarescore, gamemap, stplyr->lastmare)]);
+			}
+			break;
 		}
+		default:
+			break;
 	}
 }
 
@@ -1450,20 +1472,60 @@ static skincolors_t linkColor[2][NUMLINKCOLORS] = {
 {SKINCOLOR_SEAFOAM, SKINCOLOR_CYAN, SKINCOLOR_WAVE, SKINCOLOR_SAPPHIRE, SKINCOLOR_VAPOR, SKINCOLOR_BUBBLEGUM,
  SKINCOLOR_VIOLET, SKINCOLOR_RUBY, SKINCOLOR_FLAME, SKINCOLOR_SUNSET, SKINCOLOR_SANDY, SKINCOLOR_LIME}};
 
+static void ST_drawNiGHTSLink(void)
+{
+	static INT32 prevsel[2] = {0, 0}, prevtime[2] = {0, 0};
+	const UINT8 q = ((splitscreen && stplyr == &players[secondarydisplayplayer]) ? 1 : 0);
+	INT32 sel = ((stplyr->linkcount-1) / 5) % NUMLINKCOLORS, aflag = V_PERPLAYER, mag = ((stplyr->linkcount-1 >= 300) ? 1 : 0);
+	skincolors_t colornum;
+	fixed_t x, y, scale;
+
+	if (sel != prevsel[q])
+	{
+		prevsel[q] = sel;
+		prevtime[q] = 2 + mag;
+	}
+
+	if (stplyr->powers[pw_nights_linkfreeze] && (!(stplyr->powers[pw_nights_linkfreeze] & 2) || (stplyr->powers[pw_nights_linkfreeze] > flashingtics)))
+		colornum = SKINCOLOR_ICY;
+	else
+		colornum = linkColor[mag][sel];
+
+	aflag |= ((stplyr->linktimer < 2*TICRATE/3)
+	? (9 - 9*stplyr->linktimer/(2*TICRATE/3)) << V_ALPHASHIFT
+	: 0);
+
+	y = (160+11)<<FRACBITS;
+	aflag |= V_SNAPTOBOTTOM;
+
+	x = (160+4)<<FRACBITS;
+
+	if (prevtime[q])
+	{
+		scale = ((32 + prevtime[q])<<FRACBITS)/32;
+		prevtime[q]--;
+	}
+	else
+		scale = FRACUNIT;
+
+	y -= (11*scale);
+
+	ST_DrawNightsOverlayNum(x-(4*scale), y, scale, aflag, (stplyr->linkcount-1), nightsnum, colornum);
+	V_DrawFixedPatch(x+(4*scale), y, scale, aflag, nightslink,
+		colornum == 0 ? colormaps : R_GetTranslationColormap(TC_DEFAULT, colornum, GTC_CACHE));
+
+	// Show remaining link time left in debug
+	if (cv_debug & DBG_NIGHTSBASIC)
+		V_DrawCenteredString(BASEVIDWIDTH/2, 180, V_SNAPTOBOTTOM, va("End in %d.%02d", stplyr->linktimer/TICRATE, G_TicsToCentiseconds(stplyr->linktimer)));
+}
+
+
 static void ST_drawNiGHTSHUD(void)
 {
 	INT32 origamount;
-	INT32 minlink = 1;
 	INT32 total_spherecount;
 	const boolean oldspecialstage = (G_IsSpecialStage(gamemap) && !(maptol & TOL_NIGHTS));
 
-	// Cheap hack: don't display when the score is showing (it popping up for a split second when exiting a map is intentional)
-	if (oldspecialstage || (stplyr->texttimer && stplyr->textvar == 4))
-		minlink = INT32_MAX;
-	// When debugging, show "0 Link".
-	else if (cv_debug & DBG_NIGHTSBASIC)
-		minlink = 0;
-
 	// Drill meter
 	if (
 #ifdef HAVE_BLUA
@@ -1508,55 +1570,15 @@ static void ST_drawNiGHTSHUD(void)
 	}*/
 
 	// Link drawing
-	if (
+	if (!oldspecialstage
+	// Don't display when the score is showing (it popping up for a split second when exiting a map is intentional)
+	&& !(stplyr->texttimer && stplyr->textvar == 4)
 #ifdef HAVE_BLUA
-	LUA_HudEnabled(hud_nightslink) &&
+	&& LUA_HudEnabled(hud_nightslink)
 #endif
-	stplyr->linkcount > minlink)
+	&& ((cv_debug & DBG_NIGHTSBASIC) || stplyr->linkcount > 1)) // When debugging, show "0 Link".
 	{
-		static INT32 prevsel[2] = {0, 0}, prevtime[2] = {0, 0};
-		const UINT8 q = ((splitscreen && stplyr == &players[secondarydisplayplayer]) ? 1 : 0);
-		INT32 sel = ((stplyr->linkcount-1) / 5) % NUMLINKCOLORS, aflag = V_PERPLAYER, mag = ((stplyr->linkcount-1 >= 300) ? 1 : 0);
-		skincolors_t colornum;
-		fixed_t x, y, scale;
-
-		if (sel != prevsel[q])
-		{
-			prevsel[q] = sel;
-			prevtime[q] = 2 + mag;
-		}
-
-		if (stplyr->powers[pw_nights_linkfreeze] && (!(stplyr->powers[pw_nights_linkfreeze] & 2) || (stplyr->powers[pw_nights_linkfreeze] > flashingtics)))
-			colornum = SKINCOLOR_ICY;
-		else
-			colornum = linkColor[mag][sel];
-
-		aflag |= ((stplyr->linktimer < 2*TICRATE/3)
-		? (9 - 9*stplyr->linktimer/(2*TICRATE/3)) << V_ALPHASHIFT
-		: 0);
-
-		y = (160+11)<<FRACBITS;
-		aflag |= V_SNAPTOBOTTOM;
-
-		x = (160+4)<<FRACBITS;
-
-		if (prevtime[q])
-		{
-			scale = ((32 + prevtime[q])<<FRACBITS)/32;
-			prevtime[q]--;
-		}
-		else
-			scale = FRACUNIT;
-
-		y -= (11*scale);
-
-		ST_DrawNightsOverlayNum(x-(4*scale), y, scale, aflag, (stplyr->linkcount-1), nightsnum, colornum);
-		V_DrawFixedPatch(x+(4*scale), y, scale, aflag, nightslink,
-			colornum == 0 ? colormaps : R_GetTranslationColormap(TC_DEFAULT, colornum, GTC_CACHE));
-
-		// Show remaining link time left in debug
-		if (cv_debug & DBG_NIGHTSBASIC)
-			V_DrawCenteredString(BASEVIDWIDTH/2, 180, V_SNAPTOBOTTOM, va("End in %d.%02d", stplyr->linktimer/TICRATE, G_TicsToCentiseconds(stplyr->linktimer)));
+		ST_drawNiGHTSLink();
 	}
 
 	if (gametype == GT_RACE || gametype == GT_COMPETITION)
@@ -2448,15 +2470,26 @@ void ST_Drawer(void)
 #ifdef SEENAMES
 	if (cv_seenames.value && cv_allowseenames.value && displayplayer == consoleplayer && seenplayer && seenplayer->mo)
 	{
-		if (cv_seenames.value == 1)
-			V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2 + 15, V_HUDTRANSHALF, player_names[seenplayer-players]);
-		else if (cv_seenames.value == 2)
-			V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2 + 15, V_HUDTRANSHALF,
-			va("%s%s", G_GametypeHasTeams() ? ((seenplayer->ctfteam == 1) ? "\x85" : "\x84") : "", player_names[seenplayer-players]));
-		else //if (cv_seenames.value == 3)
-			V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2 + 15, V_HUDTRANSHALF,
-			va("%s%s", !G_RingSlingerGametype() || (G_GametypeHasTeams() && players[consoleplayer].ctfteam == seenplayer->ctfteam)
-			 ? "\x83" : "\x85", player_names[seenplayer-players]));
+		INT32 c = 0;
+		switch (cv_seenames.value)
+		{
+			case 1: // Colorless
+				break;
+			case 2: // Team
+				if (G_GametypeHasTeams())
+					c = (seenplayer->ctfteam == 1) ? V_REDMAP : V_BLUEMAP;
+				break;
+			case 3: // Ally/Foe
+			default:
+				// Green = Ally, Red = Foe
+				if (G_GametypeHasTeams())
+					c = (players[consoleplayer].ctfteam == seenplayer->ctfteam) ? V_GREENMAP : V_REDMAP;
+				else // Everyone is an ally, or everyone is a foe!
+					c = (G_RingSlingerGametype()) ? V_REDMAP : V_GREENMAP;
+				break;
+		}
+
+		V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2 + 15, V_HUDTRANSHALF|c, player_names[seenplayer-players]);
 	}
 #endif
 
diff --git a/src/y_inter.c b/src/y_inter.c
index e1fd14a790f51ab33b40f7590ecf247afe276f8c..598f3c8ab9c4bec44b395aed28ccc2fc321e7b20 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -1875,7 +1875,7 @@ static void Y_AwardCoopBonuses(void)
 		}
 
 		ptlives = min(
-			((!ultimatemode && !modeattacking && players[i].lives != 0x7f) ? max((players[i].score/50000) - (oldscore/50000), 0) : 0),
+			((!ultimatemode && !modeattacking && players[i].lives != INFLIVES) ? max((players[i].score/50000) - (oldscore/50000), 0) : 0),
 			(mapheaderinfo[prevmap]->maxbonuslives < 0 ? INT32_MAX : mapheaderinfo[prevmap]->maxbonuslives));
 		if (ptlives)
 			P_GivePlayerLives(&players[i], ptlives);
@@ -1921,7 +1921,7 @@ static void Y_AwardSpecialStageBonus(void)
 
 		// grant extra lives right away since tally is faked
 		ptlives = min(
-			((!ultimatemode && !modeattacking && players[i].lives != 0x7f) ? max((players[i].score/50000) - (oldscore/50000), 0) : 0),
+			((!ultimatemode && !modeattacking && players[i].lives != INFLIVES) ? max((players[i].score/50000) - (oldscore/50000), 0) : 0),
 			(mapheaderinfo[prevmap]->maxbonuslives < 0 ? INT32_MAX : mapheaderinfo[prevmap]->maxbonuslives));
 		if (ptlives)
 			P_GivePlayerLives(&players[i], ptlives);