diff --git a/src/dehacked.c b/src/dehacked.c
index 3af635e0e..2cbe953e6 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -402,7 +402,7 @@ static void readPlayer(MYFILE *f, INT32 num)
 
 				strncpy(description[num].picname, word2, 8);
 			}
-			else if (fastcmp(word, "NAMETAG"))
+			else if (fastcmp(word, "NAMETAG") || fastcmp(word, "TAGNAME"))
 			{
 				if (!slotfound && (slotfound = findFreeSlot(&num)) == false)
 					goto done;
diff --git a/src/f_finale.c b/src/f_finale.c
index fedc08234..4dee32e96 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -1584,15 +1584,15 @@ void F_StartEnding(void)
 		UINT8 skinnum = players[consoleplayer].skin;
 		spritedef_t *sprdef;
 		spriteframe_t *sprframe;
-		if (skins[skinnum].sprites[SPR2_XTRA].numframes >= 5)
+		if (skins[skinnum].sprites[SPR2_XTRA].numframes >= (XTRA_ENDING+2)+1)
 		{
 			sprdef = &skins[skinnum].sprites[SPR2_XTRA];
 			// character head, skin specific
-			sprframe = &sprdef->spriteframes[2];
+			sprframe = &sprdef->spriteframes[XTRA_ENDING];
 			endfwrk[0] = W_CachePatchNum(sprframe->lumppat[0], PU_LEVEL);
-			sprframe = &sprdef->spriteframes[3];
+			sprframe = &sprdef->spriteframes[XTRA_ENDING+1];
 			endfwrk[1] = W_CachePatchNum(sprframe->lumppat[0], PU_LEVEL);
-			sprframe = &sprdef->spriteframes[4];
+			sprframe = &sprdef->spriteframes[XTRA_ENDING+2];
 			endfwrk[2] = W_CachePatchNum(sprframe->lumppat[0], PU_LEVEL);
 		}
 		else // eh, yknow what? too lazy to put MISSINGs here. eggman wins if you don't give your character an ending firework display.
diff --git a/src/info.h b/src/info.h
index bb27ac3e3..f8eab3631 100644
--- a/src/info.h
+++ b/src/info.h
@@ -842,6 +842,12 @@ typedef enum playersprite
 	NUMPLAYERSPRITES
 } playersprite_t;
 
+// SPR2_XTRA
+#define XTRA_LIFEPIC	0		// Life icon patch
+#define XTRA_CHARSEL	1		// Character select picture
+#define XTRA_NAMETAG	2		// Character select nametag
+#define XTRA_ENDING		3		// Ending finale patches
+
 typedef enum state
 {
 	S_NULL,
diff --git a/src/m_menu.c b/src/m_menu.c
index 666174b7b..3daf1185c 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -3552,7 +3552,8 @@ void M_InitCharacterTables(void)
 		strcpy(description[i].picname, "");
 		strcpy(description[i].skinname, "");
 		description[i].prev = description[i].next = 0;
-		description[i].pic = NULL;
+		description[i].charpic = NULL;
+		description[i].namepic = NULL;
 	}
 }
 
@@ -7630,6 +7631,7 @@ static void M_SetupChoosePlayer(INT32 choice)
 	{
 		if (description[i].used) // If the character's disabled through SOC, there's nothing we can do for it.
 		{
+			char *botskin = strchr(description[i].skinname, '&');
 			name = strtok(Z_StrDup(description[i].skinname), "&");
 			skinnum = R_SkinAvailable(name);
 			if ((skinnum != -1) && (R_SkinUsable(-1, skinnum)))
@@ -7649,31 +7651,44 @@ static void M_SetupChoosePlayer(INT32 choice)
 
 				if (!(description[i].picname[0]))
 				{
-					if (skins[skinnum].sprites[SPR2_XTRA].numframes >= 2)
+					if (skins[skinnum].sprites[SPR2_XTRA].numframes >= XTRA_CHARSEL+1)
 					{
 						spritedef_t *sprdef = &skins[skinnum].sprites[SPR2_XTRA];
-						spriteframe_t *sprframe = &sprdef->spriteframes[1];
-						description[i].pic = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE);
+						spriteframe_t *sprframe = &sprdef->spriteframes[XTRA_CHARSEL];
+						description[i].charpic = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE);
 					}
 					else
-						description[i].pic = W_CachePatchName("MISSING", PU_CACHE);
+						description[i].charpic = W_CachePatchName("MISSING", PU_CACHE);
 				}
 				else
-					description[i].pic = W_CachePatchName(description[i].picname, PU_CACHE);
+					description[i].charpic = W_CachePatchName(description[i].picname, PU_CACHE);
 
-				if (!(description[i].nametag[0]))
+				if (!(description[i].nametag[0]) && (!botskin))
 				{
-					if (skins[skinnum].sprites[SPR2_XTRA].numframes >= 6)
+					if (skins[skinnum].sprites[SPR2_XTRA].numframes >= XTRA_NAMETAG+1)
 					{
 						spritedef_t *sprdef = &skins[skinnum].sprites[SPR2_XTRA];
-						spriteframe_t *sprframe = &sprdef->spriteframes[5];
+						spriteframe_t *sprframe = &sprdef->spriteframes[XTRA_NAMETAG];
 						description[i].namepic = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE);
 					}
 					else
-						description[i].namepic = W_CachePatchName("MISSING", PU_CACHE);
+					{
+						// If no name tag patch was provided,
+						// the character select screen
+						// will simply not draw anything.
+						description[i].namepic = NULL;
+					}
 				}
 				else
-					description[i].namepic = W_CachePatchName(description[i].nametag, PU_CACHE);
+				{
+					const char *nametag = description[i].nametag;
+					// If no name tag patch was provided,
+					// the character select screen
+					// will simply not draw anything.
+					description[i].namepic = NULL;
+					if (W_LumpExists(nametag))
+						description[i].namepic = W_CachePatchName(nametag, PU_CACHE);
+				}
 			}
 			// else -- Technically, character select icons without corresponding skins get bundled away behind this too. Sucks to be them.
 			Z_Free(name);
@@ -7910,11 +7925,11 @@ static void M_DrawSetupChoosePlayerMenu(void)
 	}
 
 	// Character select pictures
-	V_DrawScaledPatch(8-xsh, (my+16) - FixedInt(char_scroll), 0, description[char_on].pic);
+	V_DrawScaledPatch(8-xsh, (my+16) - FixedInt(char_scroll), 0, description[char_on].charpic);
 	if (prev != -1)
-		V_DrawScaledPatch(8-xsh, (my+16) - FixedInt(char_scroll) - 144, 0, description[prev].pic);
+		V_DrawScaledPatch(8-xsh, (my+16) - FixedInt(char_scroll) - 144, 0, description[prev].charpic);
 	if (next != -1)
-		V_DrawScaledPatch(8-xsh, (my+16) - FixedInt(char_scroll) + 144, 0, description[next].pic);
+		V_DrawScaledPatch(8-xsh, (my+16) - FixedInt(char_scroll) + 144, 0, description[next].charpic);
 
 	// Character description
 	V_DrawString(146+xsh, my + 9, V_RETURN8|V_ALLOWLOWERCASE, char_notes);
@@ -7931,8 +7946,9 @@ static void M_DrawSetupChoosePlayerMenu(void)
 		if (next != -1) nextpatch = description[next].namepic;
 
 		txsh = oxsh;
-		ox = (8-xsh) + (description[char_on].pic)->width/2;
-		ox -= (curpatch->width/2);
+		ox = (8-xsh) + (description[char_on].charpic)->width/2;
+		if (curpatch)
+			ox -= (curpatch->width/2);
 		y = my + 144;
 
 		if (char_scroll && (!xsh))
diff --git a/src/m_menu.h b/src/m_menu.h
index de1ba5010..2050f9952 100644
--- a/src/m_menu.h
+++ b/src/m_menu.h
@@ -317,7 +317,7 @@ typedef struct
 	char picname[8];
 	char nametag[8];
 	char skinname[SKINNAMESIZE*2+2]; // skin&skin\0
-	patch_t *pic;
+	patch_t *charpic;
 	patch_t *namepic;
 	UINT8 prev;
 	UINT8 next;
diff --git a/src/st_stuff.c b/src/st_stuff.c
index aefb4c53c..c1a6d5add 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -348,7 +348,7 @@ void ST_LoadFaceGraphics(INT32 skinnum)
 	if (skins[skinnum].sprites[SPR2_XTRA].numframes)
 	{
 		spritedef_t *sprdef = &skins[skinnum].sprites[SPR2_XTRA];
-		spriteframe_t *sprframe = &sprdef->spriteframes[0];
+		spriteframe_t *sprframe = &sprdef->spriteframes[XTRA_LIFEPIC];
 		faceprefix[skinnum] = W_CachePatchNum(sprframe->lumppat[0], PU_HUDGFX);
 		if (skins[skinnum].sprites[(SPR2_XTRA|FF_SPR2SUPER)].numframes)
 		{