diff --git a/extras/conf/udb/Includes/SRB222_linedefs.cfg b/extras/conf/udb/Includes/SRB222_linedefs.cfg
index 065c9dc46b0b2436432e55a15b113b2f16667324..eef89d144600b98120386afb671fbc9762f7a190 100644
--- a/extras/conf/udb/Includes/SRB222_linedefs.cfg
+++ b/extras/conf/udb/Includes/SRB222_linedefs.cfg
@@ -3001,6 +3001,30 @@ udmf
 				}
 			}
 		}
+
+		331
+		{
+			title = "Player Skin";
+			prefix = "(331)";
+			arg0
+			{
+				title = "Trigger type";
+				type = 11;
+				enum = "triggertype";
+			}
+			arg1
+			{
+				title = "Invert choice?";
+				type = 11;
+				enum = "noyes";
+			}
+			stringarg0
+			{
+				title = "Skin name";
+				type = 2;
+			}
+		}
+
 		399
 		{
 			title = "Level Load";
diff --git a/src/p_setup.c b/src/p_setup.c
index e4080f25657d07c26909709d3343211c39a5977d..abdc643c55747151ab3777862c9eec189a530004 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -3881,6 +3881,23 @@ static void P_ConvertBinaryMap(void)
 					lines[i].args[7] |= TMI_ENTER;
 			}
 			break;
+		case 331: // Player skin - continuous
+		case 332: // Player skin - each time
+		case 333: // Player skin - once
+			if (lines[i].special == 303)
+				lines[i].args[0] = TMT_ONCE;
+			else if (lines[i].special == 302)
+				lines[i].args[0] = (lines[i].flags & ML_BOUNCY) ? TMT_EACHTIMEENTERANDEXIT : TMT_EACHTIMEENTER;
+			else
+				lines[i].args[0] = TMT_CONTINUOUS;
+			lines[i].args[1] = !!(lines[i].flags & ML_NOCLIMB);
+			if (lines[i].text)
+			{
+				lines[i].stringargs[0] = Z_Malloc(strlen(lines[i].text) + 1, PU_LEVEL, NULL);
+				M_Memcpy(lines[i].stringargs[0], lines[i].text, strlen(lines[i].text) + 1);
+			}
+			lines[i].special = 331;
+			break;
 		case 400: //Set tagged sector's floor height/texture
 		case 401: //Set tagged sector's ceiling height/texture
 			lines[i].args[0] = tag;
diff --git a/src/p_spec.c b/src/p_spec.c
index 013d7230082eaf1a76b6dd83d968b827e6254009..94a4cc7da90bfa245e320fe91b1a26f225b25f7d 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -1806,10 +1806,12 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
 			if (!P_CheckNightsTriggerLine(triggerline, actor))
 				return false;
 			break;
-		case 331: // continuous
-		case 332: // each time
-		case 333: // once
-			if (!(actor && actor->player && ((stricmp(triggerline->text, skins[actor->player->skin].name) == 0) ^ ((triggerline->flags & ML_NOCLIMB) == ML_NOCLIMB))))
+		case 331:
+			if (!(actor && actor->player))
+				return false;
+			if (!triggerline->stringargs[0])
+				return false;
+			if (!(stricmp(triggerline->stringargs[0], skins[actor->player->skin].name) == 0) ^ !!(triggerline->args[1]))
 				return false;
 			break;
 		case 334: // object dye - continuous
@@ -1853,7 +1855,7 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
 	 || (specialtype == 325 && triggerline->args[0]) // DeNightserize - Once
 	 || (specialtype == 327 && triggerline->args[0]) // Nights lap - Once
 	 || (specialtype == 329 && triggerline->args[0]) // Nights Bonus Time - Once
-	 || specialtype == 333 // Skin - Once
+	 || (specialtype == 333 && triggerline->args[0] == TMT_ONCE)  // Player skin
 	 || specialtype == 336 // Dye - Once
 	 || specialtype == 399) // Level Load
 		triggerline->special = 0; // Clear it out
@@ -1900,15 +1902,15 @@ void P_LinedefExecute(INT16 tag, mobj_t *actor, sector_t *caller)
 			|| lines[masterline].special == 309 // CTF team
 			|| lines[masterline].special == 314 // Number of pushables
 			|| lines[masterline].special == 317 // Condition set trigger
-			|| lines[masterline].special == 319) // Unlockable trigger
+			|| lines[masterline].special == 319 // Unlockable trigger
+			|| lines[masterline].special == 331) // Player skin
 			&& lines[masterline].args[0] > TMT_EACHTIMEMASK)
 			continue;
 
 		if (lines[masterline].special == 321 && lines[masterline].args[0] > TMXT_EACHTIMEMASK) // Trigger after X calls
 			continue;
 
-		if (lines[masterline].special == 332 // Skin
-		 || lines[masterline].special == 335)// Dye
+		if (lines[masterline].special == 335) // Dye
 			continue;
 
 		if (!P_RunTriggerLinedef(&lines[masterline], actor, caller))
@@ -6689,6 +6691,7 @@ void P_SpawnSpecials(boolean fromnetsave)
 			case 314: // Pushable linedef executors (count # of pushables)
 			case 317: // Condition set trigger
 			case 319: // Unlockable trigger
+			case 331: // Player skin
 				if (lines[i].args[0] > TMT_EACHTIMEMASK)
 					P_AddEachTimeThinker(&lines[i], lines[i].args[0] == TMT_EACHTIMEENTERANDEXIT);
 				break;
@@ -6717,8 +6720,6 @@ void P_SpawnSpecials(boolean fromnetsave)
 				break;
 
 			// Each time executors
-			case 306:
-			case 332:
 			case 335:
 				P_AddEachTimeThinker(&lines[i], !!(lines[i].flags & ML_BOUNCY));
 				break;
@@ -6742,11 +6743,6 @@ void P_SpawnSpecials(boolean fromnetsave)
 			case 329:
 				break;
 
-			// Skin trigger executors
-			case 331:
-			case 333:
-				break;
-
 			// Object dye executors
 			case 334:
 			case 336: