diff --git a/extras/conf/udb/Includes/SRB222_linedefs.cfg b/extras/conf/udb/Includes/SRB222_linedefs.cfg
index 9fbcc0a224cc8936b6da69ae8ffff24260a5645e..88f40b8acf8d1d549b28dff13e08a46dd5aa62ce 100644
--- a/extras/conf/udb/Includes/SRB222_linedefs.cfg
+++ b/extras/conf/udb/Includes/SRB222_linedefs.cfg
@@ -3169,6 +3169,52 @@ udmf
 			}
 		}
 
+		461
+		{
+			title = "Spawn Object";
+			prefix = "(461)";
+			arg0
+			{
+				title = "X position";
+			}
+			arg1
+			{
+				title = "Y position";
+			}
+			arg2
+			{
+				title = "Z position";
+			}
+			arg3
+			{
+				title = "Angle";
+				type = 8;
+			}
+			arg4
+			{
+				title = "Randomize position?";
+				type = 11;
+				enum = "noyes";
+			}
+			arg5
+			{
+				title = "Max X position";
+			}
+			arg6
+			{
+				title = "Max Y position";
+			}
+			arg7
+			{
+				title = "Max Z position";
+			}
+			stringarg0
+			{
+				title = "Object type";
+				type = 2;
+			}
+		}
+
 		462
 		{
 			title = "Stop Timer/Exit Stage in Record Attack";
@@ -3224,6 +3270,56 @@ udmf
 	{
 		title = "Linedef Executor (misc.)";
 
+		413
+		{
+			title = "Change Music";
+			prefix = "(413)";
+			arg0
+			{
+				title = "Flags";
+				type = 12;
+				enum
+				{
+					1 = "For all players";
+					2 = "Seek offset from current position";
+					4 = "Fade to custom volume";
+					8 = "Don't reload after death";
+					16 = "Force music reload";
+					32 = "Don't loop";
+				}
+			}
+			arg1
+			{
+				title = "Position";
+			}
+			arg2
+			{
+				title = "Fade out time";
+			}
+			arg3
+			{
+				title = "Fade in time";
+			}
+			arg4
+			{
+				title = "Fade destination volume";
+			}
+			arg5
+			{
+				title = "Fade start volume";
+				default = -1;
+			}
+			arg6
+			{
+				title = "Track number";
+			}
+			stringarg0
+			{
+				title = "Music name";
+				type = 2;
+			}
+		}
+
 		414
 		{
 			title = "Play Sound Effect";
diff --git a/extras/conf/udb/Includes/SRB222_misc.cfg b/extras/conf/udb/Includes/SRB222_misc.cfg
index 8e8b1d71a57234805c9170f618e22c290d51a03b..1fd8f68b886175038c7ae24b7677da6d12c20b92 100644
--- a/extras/conf/udb/Includes/SRB222_misc.cfg
+++ b/extras/conf/udb/Includes/SRB222_misc.cfg
@@ -232,6 +232,16 @@ universalfields
 			type = 0;
 			default = 0;
 		}
+		arg6
+		{
+			type = 0;
+			default = 0;
+		}
+		arg7
+		{
+			type = 0;
+			default = 0;
+		}
 		stringarg0
 		{
 			type = 2;
diff --git a/src/doomdata.h b/src/doomdata.h
index e317fec1b352e21ab571810d2911ffbf4c03f7f5..7ba159a7cbf20086e2df037836598b5bcfb8e3e4 100644
--- a/src/doomdata.h
+++ b/src/doomdata.h
@@ -196,7 +196,7 @@ typedef struct
 #pragma pack()
 #endif
 
-#define NUMMAPTHINGARGS 6
+#define NUMMAPTHINGARGS 8
 #define NUMMAPTHINGSTRINGARGS 2
 
 // Thing definition, position, orientation and type,
diff --git a/src/p_setup.c b/src/p_setup.c
index 0e393d2677b6cd63e353bdb656eea7601009dffa..7b7396f74f1564a82dbb089a8161de06dcbc5e04 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1305,7 +1305,6 @@ static void P_LoadSidedefs(UINT8 *data)
 			case 334: // Trigger linedef executor: Object dye - Continuous
 			case 335: // Trigger linedef executor: Object dye - Each time
 			case 336: // Trigger linedef executor: Object dye - Once
-			case 461: // Spawns an object on the map based on texture offsets
 			{
 				char process[8*3+1];
 				memset(process,0,8*3+1);
@@ -1330,6 +1329,7 @@ static void P_LoadSidedefs(UINT8 *data)
 			case 442: // Calls P_SetMobjState on mobjs of a given type in the tagged sectors
 			case 443: // Calls a named Lua function
 			case 459: // Control text prompt (named tag)
+			case 461: // Spawns an object on the map based on texture offsets
 			case 463: // Colorizes an object
 			{
 				char process[8*3+1];
@@ -3761,6 +3761,31 @@ static void P_ConvertBinaryMap(void)
 			lines[i].args[3] = sides[lines[i].sidenum[0]].rowoffset >> FRACBITS;
 			lines[i].args[4] = lines[i].frontsector->ceilingheight >> FRACBITS;
 			break;
+		case 413: //Change music
+			if (lines[i].flags & ML_NOCLIMB)
+				lines[i].args[0] |= TMM_ALLPLAYERS;
+			if (lines[i].flags & ML_EFFECT1)
+				lines[i].args[0] |= TMM_OFFSET;
+			if (lines[i].flags & ML_EFFECT2)
+				lines[i].args[0] |= TMM_FADE;
+			if (lines[i].flags & ML_BLOCKMONSTERS)
+				lines[i].args[0] |= TMM_NORELOAD;
+			if (lines[i].flags & ML_BOUNCY)
+				lines[i].args[0] |= TMM_FORCERESET;
+			if (lines[i].flags & ML_EFFECT4)
+				lines[i].args[0] |= TMM_NOLOOP;
+			lines[i].args[1] = sides[lines[i].sidenum[0]].midtexture;
+			lines[i].args[2] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS;
+			lines[i].args[3] = sides[lines[i].sidenum[0]].rowoffset >> FRACBITS;
+			lines[i].args[4] = (lines[i].sidenum[1] != 0xffff) ? sides[lines[i].sidenum[1]].textureoffset >> FRACBITS : 0;
+			lines[i].args[5] = (lines[i].sidenum[1] != 0xffff) ? sides[lines[i].sidenum[1]].rowoffset >> FRACBITS : -1;
+			lines[i].args[6] = sides[lines[i].sidenum[0]].bottomtexture;
+			if (sides[lines[i].sidenum[0]].text)
+			{
+				lines[i].stringargs[0] = Z_Malloc(strlen(sides[lines[i].sidenum[0]].text) + 1, PU_LEVEL, NULL);
+				M_Memcpy(lines[i].stringargs[0], sides[lines[i].sidenum[0]].text, strlen(sides[lines[i].sidenum[0]].text) + 1);
+			}
+			break;
 		case 414: //Play sound effect
 			lines[i].args[2] = tag;
 			if (tag != 0)
@@ -4170,6 +4195,34 @@ static void P_ConvertBinaryMap(void)
 			lines[i].args[0] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS;
 			lines[i].args[1] = sides[lines[i].sidenum[0]].rowoffset >> FRACBITS;
 			break;
+		case 461: //Spawn object
+			lines[i].args[0] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS;
+			lines[i].args[1] = sides[lines[i].sidenum[0]].rowoffset >> FRACBITS;
+			lines[i].args[2] = lines[i].frontsector->floorheight >> FRACBITS;
+			lines[i].args[3] = (lines[i].flags & ML_EFFECT1) ? AngleFixed(R_PointToAngle2(lines[i].v1->x, lines[i].v1->y, lines[i].v2->x, lines[i].v2->y)) >> FRACBITS : 0;
+			if (lines[i].flags & ML_NOCLIMB)
+			{
+				if (lines[i].sidenum[1] != 0xffff) // Make sure the linedef has a back side
+				{
+					lines[i].args[4] = 1;
+					lines[i].args[5] = sides[lines[i].sidenum[1]].textureoffset >> FRACBITS;
+					lines[i].args[6] = sides[lines[i].sidenum[1]].rowoffset >> FRACBITS;
+					lines[i].args[7] = lines[i].frontsector->ceilingheight >> FRACBITS;
+				}
+				else
+				{
+					CONS_Alert(CONS_WARNING, "Linedef Type %d - Spawn Object: Linedef is set for random range but has no back side.\n", lines[i].special);
+					lines[i].args[4] = 0;
+				}
+			}
+			else
+				lines[i].args[4] = 0;
+			if (sides[lines[i].sidenum[0]].text)
+			{
+				lines[i].stringargs[0] = Z_Malloc(strlen(sides[lines[i].sidenum[0]].text) + 1, PU_LEVEL, NULL);
+				M_Memcpy(lines[i].stringargs[0], sides[lines[i].sidenum[0]].text, strlen(sides[lines[i].sidenum[0]].text) + 1);
+			}
+			break;
 		case 463: //Dye object
 			if (sides[lines[i].sidenum[0]].text)
 			{
diff --git a/src/p_spec.c b/src/p_spec.c
index 812577ed5219f96b1da6f2933bcb14279aebef63..ef0703c50f7e47aea1981d3c282ea07812689df8 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -2459,19 +2459,19 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 			break;
 
 		case 413: // Change music
-			// console player only unless NOCLIMB is set
-			if ((line->flags & ML_NOCLIMB) || (mo && mo->player && P_IsLocalPlayer(mo->player)) || titlemapinaction)
+			// console player only unless TMM_ALLPLAYERS is set
+			if ((line->args[0] & TMM_ALLPLAYERS) || (mo && mo->player && P_IsLocalPlayer(mo->player)) || titlemapinaction)
 			{
-				boolean musicsame = (!sides[line->sidenum[0]].text[0] || !strnicmp(sides[line->sidenum[0]].text, S_MusicName(), 7));
-				UINT16 tracknum = (UINT16)max(sides[line->sidenum[0]].bottomtexture, 0);
-				INT32 position = (INT32)max(sides[line->sidenum[0]].midtexture, 0);
-				UINT32 prefadems = (UINT32)max(sides[line->sidenum[0]].textureoffset >> FRACBITS, 0);
-				UINT32 postfadems = (UINT32)max(sides[line->sidenum[0]].rowoffset >> FRACBITS, 0);
-				UINT8 fadetarget = (UINT8)max((line->sidenum[1] != 0xffff) ? sides[line->sidenum[1]].textureoffset >> FRACBITS : 0, 0);
-				INT16 fadesource = (INT16)max((line->sidenum[1] != 0xffff) ? sides[line->sidenum[1]].rowoffset >> FRACBITS : -1, -1);
+				boolean musicsame = (!line->stringargs[0] || !line->stringargs[0][0] || !strnicmp(line->stringargs[0], S_MusicName(), 7));
+				UINT16 tracknum = (UINT16)max(line->args[6], 0);
+				INT32 position = (INT32)max(line->args[1], 0);
+				UINT32 prefadems = (UINT32)max(line->args[2], 0);
+				UINT32 postfadems = (UINT32)max(line->args[3], 0);
+				UINT8 fadetarget = (UINT8)max(line->args[4], 0);
+				INT16 fadesource = (INT16)max(line->args[5], -1);
 
 				// Seek offset from current song position
-				if (line->flags & ML_EFFECT1)
+				if (line->args[0] & TMM_OFFSET)
 				{
 					// adjust for loop point if subtracting
 					if (position < 0 && S_GetMusicLength() &&
@@ -2483,7 +2483,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 				}
 
 				// Fade current music to target volume (if music won't be changed)
-				if ((line->flags & ML_EFFECT2) && fadetarget && musicsame)
+				if ((line->args[0] & TMM_FADE) && fadetarget && musicsame)
 				{
 					// 0 fadesource means fade from current volume.
 					// meaning that we can't specify volume 0 as the source volume -- this starts at 1.
@@ -2501,22 +2501,25 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 				// Change the music and apply position/fade operations
 				else
 				{
-					strncpy(mapmusname, sides[line->sidenum[0]].text, 7);
+					if (!line->stringargs[0])
+						break;
+
+					strncpy(mapmusname, line->stringargs[0], 7);
 					mapmusname[6] = 0;
 
 					mapmusflags = tracknum & MUSIC_TRACKMASK;
-					if (!(line->flags & ML_BLOCKMONSTERS))
+					if (!(line->args[0] & TMM_NORELOAD))
 						mapmusflags |= MUSIC_RELOADRESET;
-					if (line->flags & ML_BOUNCY)
+					if (line->args[0] & TMM_FORCERESET)
 						mapmusflags |= MUSIC_FORCERESET;
 
 					mapmusposition = position;
 
-					S_ChangeMusicEx(mapmusname, mapmusflags, !(line->flags & ML_EFFECT4), position,
-						!(line->flags & ML_EFFECT2) ? prefadems : 0,
-						!(line->flags & ML_EFFECT2) ? postfadems : 0);
+					S_ChangeMusicEx(mapmusname, mapmusflags, !(line->args[0] & TMM_NOLOOP), position,
+						!(line->args[0] & TMM_FADE) ? prefadems : 0,
+						!(line->args[0] & TMM_FADE) ? postfadems : 0);
 
-					if ((line->flags & ML_EFFECT2) && fadetarget)
+					if ((line->args[0] & TMM_FADE) && fadetarget)
 					{
 						if (!postfadems)
 							S_SetInternalMusicVolume(fadetarget);
@@ -2525,7 +2528,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 					}
 				}
 
-				// Except, you can use the ML_BLOCKMONSTERS flag to change this behavior.
+				// Except, you can use the TMM_NORELOAD flag to change this behavior.
 				// if (mapmusflags & MUSIC_RELOADRESET) then it will reset the music in G_PlayerReborn.
 			}
 			break;
@@ -3552,34 +3555,28 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 
 		case 461: // Spawns an object on the map based on texture offsets
 			{
-				const mobjtype_t type = (mobjtype_t)(sides[line->sidenum[0]].toptexture);
+				const mobjtype_t type = line->stringargs[0] ? get_number(line->stringargs[0]) : MT_NULL;
 				mobj_t *mobj;
 
 				fixed_t x, y, z;
-				x = sides[line->sidenum[0]].textureoffset;
-				y = sides[line->sidenum[0]].rowoffset;
-				z = line->frontsector->floorheight;
 
-				if (line->flags & ML_NOCLIMB) // If noclimb is set, spawn randomly within a range
+				if (line->args[4]) // If args[4] is set, spawn randomly within a range
 				{
-					if (line->sidenum[1] != 0xffff) // Make sure the linedef has a back side
-					{
-						x = P_RandomRange(sides[line->sidenum[0]].textureoffset>>FRACBITS, sides[line->sidenum[1]].textureoffset>>FRACBITS)<<FRACBITS;
-						y = P_RandomRange(sides[line->sidenum[0]].rowoffset>>FRACBITS, sides[line->sidenum[1]].rowoffset>>FRACBITS)<<FRACBITS;
-						z = P_RandomRange(line->frontsector->floorheight>>FRACBITS, line->frontsector->ceilingheight>>FRACBITS)<<FRACBITS;
-					}
-					else
-					{
-						CONS_Alert(CONS_WARNING,"Linedef Type %d - Spawn Object: Linedef is set for random range but has no back side.\n", line->special);
-						break;
-					}
+					x = P_RandomRange(line->args[0], line->args[5])<<FRACBITS;
+					y = P_RandomRange(line->args[1], line->args[6])<<FRACBITS;
+					z = P_RandomRange(line->args[2], line->args[7])<<FRACBITS;
+				}
+				else
+				{
+					x = line->args[0] << FRACBITS;
+					y = line->args[1] << FRACBITS;
+					z = line->args[2] << FRACBITS;
 				}
 
 				mobj = P_SpawnMobj(x, y, z, type);
 				if (mobj)
 				{
-					if (line->flags & ML_EFFECT1)
-						mobj->angle = R_PointToAngle2(line->v1->x, line->v1->y, line->v2->x, line->v2->y);
+					mobj->angle = FixedAngle(line->args[3] << FRACBITS);
 					CONS_Debug(DBG_GAMELOGIC, "Linedef Type %d - Spawn Object: %d spawned at (%d, %d, %d)\n", line->special, mobj->type, mobj->x>>FRACBITS, mobj->y>>FRACBITS, mobj->z>>FRACBITS); //TODO: Convert mobj->type to a string somehow.
 				}
 				else
diff --git a/src/p_spec.h b/src/p_spec.h
index b8fbc3734ae522513d7c5626829d6feba4ba05ff..74a2d438efffe9fb6c8a593f2ae93f943d26c6e0 100644
--- a/src/p_spec.h
+++ b/src/p_spec.h
@@ -127,6 +127,16 @@ typedef enum
 	TMT_RELATIVE     = 1<<3,
 } textmapteleportflags_t;
 
+typedef enum
+{
+	TMM_ALLPLAYERS = 1,
+	TMM_OFFSET = 1<<1,
+	TMM_FADE = 1<<2,
+	TMM_NORELOAD = 1<<3,
+	TMM_FORCERESET = 1<<4,
+	TMM_NOLOOP = 1<<5,
+} textmapmusicflags_t;
+
 typedef enum
 {
 	TMSS_TRIGGERMOBJ   = 0,
diff --git a/src/r_defs.h b/src/r_defs.h
index ab2b9fb2fb4204af63f1fb2a0d3df1f2ff38786a..02937fd0712511ceea5cb8b23600b63ceba701f3 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -409,7 +409,7 @@ typedef enum
 
 #define HORIZONSPECIAL 41
 
-#define NUMLINEARGS 6
+#define NUMLINEARGS 8
 #define NUMLINESTRINGARGS 2
 
 typedef struct line_s