diff --git a/.travis.yml b/.travis.yml
index 4648ae567fe77f0522e7d52c2c7d924b92ea61e4..a9f4ddfb441e17f282d9d488239cb5d3fdd5578f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -57,49 +57,6 @@ matrix:
               - gcc-4.8
           compiler: gcc-4.8
           #gcc-4.8 (Ubuntu 4.8.5-2ubuntu1~14.04.1) 4.8.5
-        - os: linux
-          addons:
-            apt:
-              sources:
-              - ubuntu-toolchain-r-test
-              packages:
-              - libsdl2-mixer-dev
-              - libpng-dev
-              - libgl1-mesa-dev
-              - libgme-dev
-              - p7zip-full
-              - gcc-4.9
-          compiler: gcc-4.9
-          #gcc-4.9 (Ubuntu 4.9.3-8ubuntu2~14.04) 4.9.3
-        - os: linux
-          addons:
-            apt:
-              sources:
-              - ubuntu-toolchain-r-test
-              packages:
-              - libsdl2-mixer-dev
-              - libpng-dev
-              - libgl1-mesa-dev
-              - libgme-dev
-              - p7zip-full
-              - gcc-5
-          compiler: gcc-5
-          #gcc-5 (Ubuntu 5.3.0-3ubuntu1~14.04) 5.3.0 20151204
-        - os: linux
-          addons:
-            apt:
-              sources:
-              - ubuntu-toolchain-r-test
-              packages:
-              - libsdl2-mixer-dev
-              - libpng-dev
-              - libgl1-mesa-dev
-              - libgme-dev
-              - p7zip-full
-              - gcc-6
-          compiler: gcc-6
-          env: WFLAGS="-Wno-tautological-compare"
-          #gcc-6 (Ubuntu 6.1.1-3ubuntu11~14.04.1) 6.1.1 20160511
         - os: linux
           addons:
             apt:
diff --git a/bin/Mingw/Release/.gitignore b/bin/Mingw/Release/.gitignore
index 834f313e3eae612617885430c8071e6e41483d88..3458ff7648f27c14076ff2aee101446a323f5a04 100644
--- a/bin/Mingw/Release/.gitignore
+++ b/bin/Mingw/Release/.gitignore
@@ -1,3 +1,4 @@
 *.exe
 *.mo
 r_opengl.dll
+*.bat
diff --git a/src/Makefile b/src/Makefile
index 322e67bfe5bd87abc1bc6bd911cd3ea3a0d9e136..c33e42daad30003c53b223f4d44d8f81fa718312 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,3 +1,4 @@
+
 #     GNU Make makefile for SRB2
 #############################################################################
 # Copyright (C) 1998-2000 by DooM Legacy Team.
@@ -63,6 +64,7 @@
 #     Compile without 3D sound support, add 'NOHS=1'
 #     Compile with GDBstubs, add 'RDB=1'
 #     Compile without PNG, add 'NOPNG=1'
+#     Compile without zlib, add 'NOZLIB=1'
 #
 # Addon for SDL:
 #     To Cross-Compile, add 'SDL_CONFIG=/usr/*/bin/sdl-config'
@@ -106,6 +108,7 @@ include Makefile.cfg
 
 ifdef DUMMY
 NOPNG=1
+NOZLIB=1
 NONET=1
 NOHW=1
 NOHS=1
@@ -268,13 +271,6 @@ LIBS+=$(PNG_LDFLAGS)
 CFLAGS+=$(PNG_CFLAGS)
 endif
 
-ZLIB_PKGCONFIG?=zlib
-ZLIB_CFLAGS?=$(shell $(PKG_CONFIG) $(ZLIB_PKGCONFIG) --cflags)
-ZLIB_LDFLAGS?=$(shell $(PKG_CONFIG) $(ZLIB_PKGCONFIG) --libs)
-
-LIBS+=$(ZLIB_LDFLAGS)
-CFLAGS+=$(ZLIB_CFLAGS)
-
 ifdef HAVE_LIBGME
 OPTS+=-DHAVE_LIBGME
 
@@ -286,6 +282,18 @@ LIBS+=$(LIBGME_LDFLAGS)
 CFLAGS+=$(LIBGME_CFLAGS)
 endif
 
+ifndef NOZLIB
+OPTS+=-DHAVE_ZLIB
+ZLIB_PKGCONFIG?=zlib
+ZLIB_CFLAGS?=$(shell $(PKG_CONFIG) $(ZLIB_PKGCONFIG) --cflags)
+ZLIB_LDFLAGS?=$(shell $(PKG_CONFIG) $(ZLIB_PKGCONFIG) --libs)
+
+LIBS+=$(ZLIB_LDFLAGS)
+CFLAGS+=$(ZLIB_CFLAGS)
+else
+NOPNG=1
+endif
+
 ifdef STATIC
 LIBS:=-static $(LIBS)
 endif
@@ -357,7 +365,8 @@ endif
 
 ifdef PROFILEMODE
 	# build with profiling information
-	CFLAGS:=-pg $(CFLAGS)
+	CFLAGS+=-pg
+	LDFLAGS+=-pg
 endif
 
 ifdef ZDEBUG
diff --git a/src/android/i_cdmus.c b/src/android/i_cdmus.c
index c5aac8f18a30143eca66f48be31de81ab1fb80e6..426bc5dc9ebdbf130614725e96fadbba42f6dd2c 100644
--- a/src/android/i_cdmus.c
+++ b/src/android/i_cdmus.c
@@ -8,7 +8,7 @@
 
 UINT8 cdaudio_started = 0;
 
-consvar_t cd_volume = {"cd_volume","31",CV_SAVE,soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cd_volume = {"cd_volume","18",CV_SAVE,soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
 consvar_t cdUpdate  = {"cd_update","1",CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
 
 
diff --git a/src/android/i_sound.c b/src/android/i_sound.c
index ecf96f2f053cf2ca8e3cb135b34cfc7e2dccd0c0..2bb304424af8b8dafe3dfc9df46eef877ef4cc89 100644
--- a/src/android/i_sound.c
+++ b/src/android/i_sound.c
@@ -21,13 +21,14 @@ void I_ShutdownSound(void){}
 //  SFX I/O
 //
 
-INT32 I_StartSound(sfxenum_t id, INT32 vol, INT32 sep, INT32 pitch, INT32 priority)
+INT32 I_StartSound(sfxenum_t id, INT32 vol, INT32 sep, INT32 pitch, INT32 priority, INT32 channel)
 {
         (void)id;
         (void)vol;
         (void)sep;
         (void)pitch;
         (void)priority;
+        (void)channel;
         return -1;
 }
 
@@ -55,90 +56,87 @@ void I_SetSfxVolume(INT32 volume)
         (void)volume;
 }
 
-//
-//  MUSIC I/O
-//
+/// ------------------------
+//  MUSIC SYSTEM
+/// ------------------------
+
 UINT8 music_started = 0;
+UINT8 digmusic_started = 0;
 
 void I_InitMusic(void){}
 
 void I_ShutdownMusic(void){}
 
-void I_PauseSong(INT32 handle)
+/// ------------------------
+//  MUSIC PROPERTIES
+/// ------------------------
+
+musictype_t I_SongType(void)
 {
-        (void)handle;
+	return MU_NONE;
 }
 
-void I_ResumeSong(INT32 handle)
+boolean I_SongPlaying(void)
 {
-        (void)handle;
+	return false;
 }
 
-//
-//  MIDI I/O
-//
-
-UINT8 midimusic_started = 0;
-
-void I_InitMIDIMusic(void){}
+boolean I_SongPaused(void)
+{
+	return false;
+}
 
-void I_ShutdownMIDIMusic(void){}
+/// ------------------------
+//  MUSIC EFFECTS
+/// ------------------------
 
-void I_SetMIDIMusicVolume(INT32 volume)
+boolean I_SetSongSpeed(float speed)
 {
-        (void)volume;
+        (void)speed;
+        return false;
 }
 
-INT32 I_RegisterSong(void *data, size_t len)
+/// ------------------------
+//  MUSIC PLAYBACK
+/// ------------------------
+
+UINT8 midimusic_started = 0;
+
+boolean I_LoadSong(char *data, size_t len)
 {
         (void)data;
         (void)len;
         return -1;
 }
 
-boolean I_PlaySong(INT32 handle, INT32 looping)
+void I_UnloadSong()
+{
+
+}
+
+boolean I_PlaySong(boolean looping)
 {
         (void)handle;
         (void)looping;
         return false;
 }
 
-void I_StopSong(INT32 handle)
+void I_StopSong(void)
 {
         (void)handle;
 }
 
-void I_UnRegisterSong(INT32 handle)
+void I_PauseSong(void)
 {
         (void)handle;
 }
 
-//
-//  DIGMUSIC I/O
-//
-
-UINT8 digmusic_started = 0;
-
-void I_InitDigMusic(void){}
-
-void I_ShutdownDigMusic(void){}
-
-boolean I_StartDigSong(const char *musicname, INT32 looping)
+void I_ResumeSong(void)
 {
-        (void)musicname;
-        (void)looping;
-        return false;
+        (void)handle;
 }
 
-void I_StopDigSong(void){}
-
-void I_SetDigMusicVolume(INT32 volume)
+void I_SetMusicVolume(INT32 volume)
 {
         (void)volume;
 }
-
-boolean I_SetSongSpeed(float speed)
-{
-        (void)speed;
-        return false;
-}
diff --git a/src/b_bot.c b/src/b_bot.c
index 543bcb183000d42db13f6a534c5bfad0d96460a2..5f884896f1da79d1238c8d3a0d8a57edfaeba0f3 100644
--- a/src/b_bot.c
+++ b/src/b_bot.c
@@ -275,8 +275,7 @@ void B_RespawnBot(INT32 playernum)
 	player->accelstart = sonic->player->accelstart;
 	player->thrustfactor = sonic->player->thrustfactor;
 	player->normalspeed = sonic->player->normalspeed;
-	player->pflags |= PF_AUTOBRAKE;
-	player->pflags &= ~PF_DIRECTIONCHAR;
+	player->pflags |= PF_AUTOBRAKE|(sonic->player->pflags & PF_DIRECTIONCHAR);
 
 	P_TeleportMove(tails, x, y, z);
 	if (player->charability == CA_FLY)
diff --git a/src/config.h.in b/src/config.h.in
index 7c9ebe6cb5e3982a22a9a588d9f1cb5460595c60..174b34430d04d0a983a4f50cd2b2d65ca265761e 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -16,7 +16,7 @@
 #define ASSET_HASH_RINGS_DTA  "${SRB2_ASSET_rings.dta_HASH}"
 #define ASSET_HASH_ZONES_DTA  "${SRB2_ASSET_zones.dta_HASH}"
 #ifdef USE_PATCH_DTA
-#define ASSET_HASH_PATCH_DTA  "${SRB2_ASSET_patch.dta_HASH}"
+#define ASSET_HASH_PATCH_PK3  "${SRB2_ASSET_patch.pk3_HASH}"
 #endif
 
 #define SRB2_COMP_REVISION    "${SRB2_COMP_REVISION}"
@@ -36,7 +36,7 @@
 #define ASSET_HASH_PLAYER_DTA "cfca0f1c73023cbbd8f844f45480f799"
 #define ASSET_HASH_RINGS_DTA  "85901ad4bf94637e5753d2ac2c03ea26"
 #ifdef USE_PATCH_DTA
-#define ASSET_HASH_PATCH_DTA  "dbbf8bc6121618ee3be2d5b14650429b"
+#define ASSET_HASH_PATCH_PK3  "dbbf8bc6121618ee3be2d5b14650429b"
 #endif
 
 #endif
diff --git a/src/console.c b/src/console.c
index b785aa18a0b5a61d0574345c41b53f75fb5012c3..9bc01cf19e07be4a0a0d6039ded3028318a528bb 100644
--- a/src/console.c
+++ b/src/console.c
@@ -312,7 +312,7 @@ static void CON_SetupColormaps(void)
 	colset(lgreenmap,   97,  98, 106);
 	colset(bluemap,    146, 147, 155);
 	colset(redmap,     210,  32,  39);
-	colset(graymap,      8,  10,  15);
+	colset(graymap,      6,  8,   14);
 	colset(orangemap,   51,  52,  57);
 	colset(skymap,     129, 130, 133);
 	colset(purplemap,  160, 161, 163);
diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 834b0a327d6533fb21c6d0210ef98e3695d34801..92da2492ebdd69f0fe4b49619c61f41ef849dc9c 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -395,8 +395,7 @@ static void ExtraDataTicker(void)
 							DEBFILE(va("player %d kicked [gametic=%u] reason as follows:\n", i, gametic));
 						}
 						CONS_Alert(CONS_WARNING, M_GetText("Got unknown net command [%s]=%d (max %d)\n"), sizeu1(curpos - bufferstart), *curpos, bufferstart[0]);
-						D_FreeTextcmd(gametic);
-						return;
+						break;
 					}
 				}
 			}
@@ -513,7 +512,8 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i)
 		rsp->powers[j] = (UINT16)SHORT(players[i].powers[j]);
 
 	// Score is resynched in the rspfirm resync packet
-	rsp->rings = LONG(players[i].rings);
+	rsp->rings = SHORT(players[i].rings);
+	rsp->spheres = SHORT(players[i].spheres);
 	rsp->lives = players[i].lives;
 	rsp->continues = players[i].continues;
 	rsp->scoreadd = players[i].scoreadd;
@@ -643,7 +643,8 @@ static void resynch_read_player(resynch_pak *rsp)
 		players[i].powers[j] = (UINT16)SHORT(rsp->powers[j]);
 
 	// Score is resynched in the rspfirm resync packet
-	players[i].rings = LONG(rsp->rings);
+	players[i].rings = SHORT(rsp->rings);
+	players[i].spheres = SHORT(rsp->spheres);
 	players[i].lives = rsp->lives;
 	players[i].continues = rsp->continues;
 	players[i].scoreadd = rsp->scoreadd;
@@ -2377,11 +2378,11 @@ static void CL_RemovePlayer(INT32 playernum)
 	if (gametype == GT_CTF)
 		P_PlayerFlagBurst(&players[playernum], false); // Don't take the flag with you!
 
-	// If in a special stage, redistribute the player's rings across
+	// If in a special stage, redistribute the player's spheres across
 	// the remaining players.
 	if (G_IsSpecialStage(gamemap))
 	{
-		INT32 i, count, increment, rings;
+		INT32 i, count, increment, spheres;
 
 		for (i = 0, count = 0; i < MAXPLAYERS; i++)
 		{
@@ -2390,19 +2391,19 @@ static void CL_RemovePlayer(INT32 playernum)
 		}
 
 		count--;
-		rings = players[playernum].rings;
-		increment = rings/count;
+		spheres = players[playernum].spheres;
+		increment = spheres/count;
 
 		for (i = 0; i < MAXPLAYERS; i++)
 		{
 			if (playeringame[i] && i != playernum)
 			{
-				if (rings < increment)
-					P_GivePlayerRings(&players[i], rings);
+				if (spheres < increment)
+					P_GivePlayerSpheres(&players[i], spheres);
 				else
-					P_GivePlayerRings(&players[i], increment);
+					P_GivePlayerSpheres(&players[i], increment);
 
-				rings -= increment;
+				spheres -= increment;
 			}
 		}
 	}
@@ -3326,7 +3327,7 @@ void SV_StopServer(void)
 	localtextcmd[0] = 0;
 	localtextcmd2[0] = 0;
 
-	for (i = 0; i < BACKUPTICS; i++)
+	for (i = firstticstosend; i < firstticstosend + BACKUPTICS; i++)
 		D_Clearticcmd(i);
 
 	consoleplayer = 0;
diff --git a/src/d_clisrv.h b/src/d_clisrv.h
index bdf3326658bd0f15c03ecf2a2744f519f6238295..bdb85a76c2160c09e80224946f44ff81ac0e0c75 100644
--- a/src/d_clisrv.h
+++ b/src/d_clisrv.h
@@ -164,7 +164,8 @@ typedef struct
 	UINT16 powers[NUMPOWERS];
 
 	// Score is resynched in the confirm resync packet
-	INT32 rings;
+	INT16 rings;
+	INT16 spheres;
 	SINT8 lives;
 	SINT8 continues;
 	UINT8 scoreadd;
diff --git a/src/d_main.c b/src/d_main.c
index cdce8885532f6f7a6f8216327fea9a44b72d9d76..9b68891d5e8c945dc894aea4479af3088ac771ab 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -112,13 +112,10 @@ INT32 postimgparam;
 postimg_t postimgtype2 = postimg_none;
 INT32 postimgparam2;
 
-boolean nomidimusic = false, nosound = false;
-boolean nodigimusic = false; // No fmod-based music
-
 // These variables are only true if
-// the respective sound system is initialized
-// and active, but no sounds/music should play.
-boolean music_disabled = false;
+// whether the respective sound system is disabled
+// or they're init'ed, but the player just toggled them
+boolean midi_disabled = false;
 boolean sound_disabled = false;
 boolean digital_disabled = false;
 
@@ -830,7 +827,7 @@ static void IdentifyVersion(void)
 
 #ifdef USE_PATCH_DTA
 	// Add our crappy patches to fix our bugs
-	D_AddFile(va(pandf,srb2waddir,"patch.dta"));
+	D_AddFile(va(pandf,srb2waddir,"patch.pk3"));
 #endif
 
 #if !defined (HAVE_SDL) || defined (HAVE_MIXER)
@@ -1024,15 +1021,6 @@ void D_SRB2Main(void)
 
 	if (M_CheckParm("-password") && M_IsNextParm())
 		D_SetPassword(M_GetNextParm());
-	else
-	{
-		size_t z;
-		char junkpw[25];
-		for (z = 0; z < 24; z++)
-			junkpw[z] = (char)(rand() & 64)+32;
-		junkpw[24] = '\0';
-		D_SetPassword(junkpw);
-	}
 
 	// add any files specified on the command line with -file wadfile
 	// to the wad list
@@ -1116,7 +1104,7 @@ void D_SRB2Main(void)
 	//W_VerifyFileMD5(1, ASSET_HASH_ZONES_DTA); // zones.dta
 	//W_VerifyFileMD5(2, ASSET_HASH_PLAYER_DTA); // player.dta
 #ifdef USE_PATCH_DTA
-	W_VerifyFileMD5(3, ASSET_HASH_PATCH_DTA); // patch.dta
+	//W_VerifyFileMD5(3, ASSET_HASH_PATCH_PK3); // patch.pk3
 #endif
 
 	// don't check music.dta because people like to modify it, and it doesn't matter if they do
@@ -1125,7 +1113,7 @@ void D_SRB2Main(void)
 
 	mainwads = 3; // there are 3 wads not to unload
 #ifdef USE_PATCH_DTA
-	++mainwads; // patch.dta adds one more
+	++mainwads; // patch.pk3 adds one more
 #endif
 #ifdef DEVELOP
 	++mainwads; // music_new, too
@@ -1193,27 +1181,27 @@ void D_SRB2Main(void)
 	// setting up sound
 	if (dedicated)
 	{
-		nosound = true;
-		nomidimusic = nodigimusic = true;
+		sound_disabled = true;
+		midi_disabled = digital_disabled = true;
 	}
 	else
 	{
-		CONS_Printf("S_Init(): Setting up sound.\n");
+		CONS_Printf("S_InitSfxChannels(): Setting up sound channels.\n");
 	}
 	if (M_CheckParm("-nosound"))
-		nosound = true;
+		sound_disabled = true;
 	if (M_CheckParm("-nomusic")) // combines -nomidimusic and -nodigmusic
-		nomidimusic = nodigimusic = true;
+		midi_disabled = digital_disabled = true;
 	else
 	{
 		if (M_CheckParm("-nomidimusic"))
-			nomidimusic = true; ; // WARNING: DOS version initmusic in I_StartupSound
+			midi_disabled = true; ; // WARNING: DOS version initmusic in I_StartupSound
 		if (M_CheckParm("-nodigmusic"))
-			nodigimusic = true; // WARNING: DOS version initmusic in I_StartupSound
+			digital_disabled = true; // WARNING: DOS version initmusic in I_StartupSound
 	}
 	I_StartupSound();
 	I_InitMusic();
-	S_Init(cv_soundvolume.value, cv_digmusicvolume.value, cv_midimusicvolume.value);
+	S_InitSfxChannels(cv_soundvolume.value);
 
 	CONS_Printf("ST_Init(): Init status bar.\n");
 	ST_Init();
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index de994bd695b87e2193a73838f4e2a8a8dd7ff72c..79a423ac37f370fc7b8ec2a4fafec64deda2f43a 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -309,8 +309,8 @@ consvar_t cv_overtime = {"overtime", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NULL,
 
 consvar_t cv_rollingdemos = {"rollingdemos", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
 
-static CV_PossibleValue_t timetic_cons_t[] = {{0, "Normal"}, {1, "Centiseconds"}, {2, "Mania"}, {3, "Tics"}, {0, NULL}};
-consvar_t cv_timetic = {"timerres", "Normal", CV_SAVE, timetic_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; // use tics in display
+static CV_PossibleValue_t timetic_cons_t[] = {{0, "Classic"}, {1, "Centiseconds"}, {2, "Mania"}, {3, "Tics"}, {0, NULL}};
+consvar_t cv_timetic = {"timerres", "Classic", CV_SAVE, timetic_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
 
 static CV_PossibleValue_t powerupdisplay_cons_t[] = {{0, "Never"}, {1, "First-person only"}, {2, "Always"}, {0, NULL}};
 consvar_t cv_powerupdisplay = {"powerupdisplay", "First-person only", CV_SAVE, powerupdisplay_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
@@ -811,6 +811,7 @@ void D_RegisterClientCommands(void)
 	COM_AddCommand("writethings", Command_Writethings_f);
 	CV_RegisterVar(&cv_speed);
 	CV_RegisterVar(&cv_opflags);
+	CV_RegisterVar(&cv_ophoopflags);
 	CV_RegisterVar(&cv_mapthingnum);
 //	CV_RegisterVar(&cv_grid);
 //	CV_RegisterVar(&cv_snapto);
@@ -2682,8 +2683,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
 	// Clear player score and rings if a spectator.
 	if (players[playernum].spectator)
 	{
-		players[playernum].score = 0;
-		players[playernum].rings = 0;
+		players[playernum].score = players[playernum].rings = 0;
 		if (players[playernum].mo)
 			players[playernum].mo->health = 1;
 	}
@@ -2725,10 +2725,12 @@ static void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt,
 
 #define BASESALT "basepasswordstorage"
 static UINT8 adminpassmd5[16];
+static boolean adminpasswordset = false;
 
 void D_SetPassword(const char *pw)
 {
 	D_MD5PasswordPass((const UINT8 *)pw, strlen(pw), BASESALT, &adminpassmd5);
+	adminpasswordset = true;
 }
 
 // Remote Administration
@@ -2764,6 +2766,12 @@ static void Command_Login_f(void)
 	UINT8 finalmd5[16];
 	const char *pw;
 
+	if (!netgame)
+	{
+		CONS_Printf(M_GetText("This only works in a netgame.\n"));
+		return;
+	}
+
 	// If the server uses login, it will effectively just remove admin privileges
 	// from whoever has them. This is good.
 	if (COM_Argc() != 2)
@@ -2800,6 +2808,12 @@ static void Got_Login(UINT8 **cp, INT32 playernum)
 	if (client)
 		return;
 
+	if (!adminpasswordset)
+	{
+		CONS_Printf(M_GetText("Password from %s failed (no password set).\n"), player_names[playernum]);
+		return;
+	}
+
 	// Do the final pass to compare with the sent md5
 	D_MD5PasswordPass(adminpassmd5, 16, va("PNUM%02d", playernum), &finalmd5);
 
@@ -2825,6 +2839,12 @@ static void Command_Verify_f(void)
 		return;
 	}
 
+	if (!netgame)
+	{
+		CONS_Printf(M_GetText("This only works in a netgame.\n"));
+		return;
+	}
+
 	if (COM_Argc() != 2)
 	{
 		CONS_Printf(M_GetText("verify <node>: give admin privileges to a node\n"));
@@ -3136,7 +3156,7 @@ static void Command_Addfile(void)
 		WRITEMEM(buf_p, md5sum, 16);
 	}
 
-	if (adminplayer == consoleplayer) // Request to add file
+	if (adminplayer == consoleplayer && (!server)) // Request to add file
 		SendNetXCmd(XD_REQADDFILE, buf, buf_p - buf);
 	else
 		SendNetXCmd(XD_ADDFILE, buf, buf_p - buf);
@@ -3930,28 +3950,7 @@ static void Command_ExitLevel_f(void)
 	else if (gamestate != GS_LEVEL || demoplayback)
 		CONS_Printf(M_GetText("You must be in a level to use this.\n"));
 	else
-	{
-		if ((netgame || multiplayer)
-		&& ((mapheaderinfo[gamemap-1]->nextlevel <= 0)
-		|| (mapheaderinfo[gamemap-1]->nextlevel > NUMMAPS)
-		|| !(mapvisited[mapheaderinfo[gamemap-1]->nextlevel-1])))
-		{
-			UINT8 i;
-			for (i = 0; i < MAXPLAYERS; i++)
-			{
-				if (playeringame[i] && players[i].exiting)
-					break;
-			}
-
-			if (i == MAXPLAYERS)
-			{
-				CONS_Printf(M_GetText("Someone must finish the level for you to use this.\n"));
-				return;
-			}
-		}
-
 		SendNetXCmd(XD_EXITLEVEL, NULL, 0);
-	}
 }
 
 static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum)
@@ -4062,19 +4061,18 @@ static void Command_RestartAudio_f(void)
 		return;
 
 	S_StopMusic();
+	S_StopSounds();
 	I_ShutdownMusic();
 	I_ShutdownSound();
 	I_StartupSound();
 	I_InitMusic();
-	
+
 // These must be called or no sound and music until manually set.
 
 	I_SetSfxVolume(cv_soundvolume.value);
-	I_SetDigMusicVolume(cv_digmusicvolume.value);
-	I_SetMIDIMusicVolume(cv_midimusicvolume.value);
+	S_SetMusicVolume(cv_digmusicvolume.value, cv_midimusicvolume.value);
 	if (Playing()) // Gotta make sure the player is in a level
 		P_RestoreMusic(&players[consoleplayer]);
-	
 }
 
 /** Quits a game and returns to the title screen.
diff --git a/src/d_player.h b/src/d_player.h
index e1350fe67db507ea2de57c0a146ecb49657a778e..dd0643bd41bc4a95cb5c257ed8b2774721abd850 100644
--- a/src/d_player.h
+++ b/src/d_player.h
@@ -324,7 +324,8 @@ typedef struct player_s
 	angle_t drawangle;
 
 	// player's ring count
-	INT32 rings;
+	INT16 rings;
+	INT16 spheres;
 
 	SINT8 pity; // i pity the fool.
 	INT32 currentweapon; // current weapon selected.
@@ -381,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
@@ -455,15 +456,25 @@ typedef struct player_s
 	boolean bonustime; // Capsule destroyed, now it's bonus time!
 	mobj_t *capsule; // Go inside the capsule
 	UINT8 mare; // Current mare
+	UINT8 marelap; // Current mare lap
+	UINT8 marebonuslap; // Current mare lap starting from bonus time
 
 	// Statistical purposes.
 	tic_t marebegunat; // Leveltime when mare begun
 	tic_t startedtime; // Time which you started this mare with.
 	tic_t finishedtime; // Time it took you to finish the mare (used for display)
-	INT16 finishedrings; // The rings you had left upon finishing the mare
+	tic_t lapbegunat; // Leveltime when lap begun
+	tic_t lapstartedtime; // Time which you started this lap with.
+	INT16 finishedspheres; // The spheres you had left upon finishing the mare
+	INT16 finishedrings; // The rings/stars you had left upon finishing the mare
 	UINT32 marescore; // score for this nights stage
 	UINT32 lastmarescore; // score for the last mare
+	UINT32 totalmarescore; // score for all mares
 	UINT8 lastmare; // previous mare
+	UINT8 lastmarelap; // previous mare lap
+	UINT8 lastmarebonuslap; // previous mare bonus lap
+	UINT8 totalmarelap; // total mare lap
+	UINT8 totalmarebonuslap; // total mare bonus lap
 	INT32 maxlink; // maximum link obtained
 	UINT8 texttimer; // nights_texttime should not be local
 	UINT8 textvar; // which line of NiGHTS text to show -- let's not use cheap hacks
@@ -489,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 d546a8538d126458db24449c4342b157789ecd31..6c39fc1977385d94097d2a1824530ce64798aab1 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -853,25 +853,27 @@ static const struct {
 	const char *name;
 	const mobjtype_t type;
 } FLICKYTYPES[] = {
-	{"BLUEBIRD", MT_FLICKY_01},
-	{"RABBIT",   MT_FLICKY_02},
-	{"CHICKEN",  MT_FLICKY_03},
-	{"SEAL",     MT_FLICKY_04},
-	{"PIG",      MT_FLICKY_05},
-	{"CHIPMUNK", MT_FLICKY_06},
-	{"PENGUIN",  MT_FLICKY_07},
-	{"FISH",     MT_FLICKY_08},
-	{"RAM",      MT_FLICKY_09},
-	{"PUFFIN",   MT_FLICKY_10},
-	{"COW",      MT_FLICKY_11},
-	{"RAT",      MT_FLICKY_12},
-	{"BEAR",     MT_FLICKY_13},
-	{"DOVE",     MT_FLICKY_14},
-	{"CAT",      MT_FLICKY_15},
-	{"CANARY",   MT_FLICKY_16},
+	{"BLUEBIRD", MT_FLICKY_01}, // Flicky (Flicky)
+	{"RABBIT",   MT_FLICKY_02}, // Pocky (1)
+	{"CHICKEN",  MT_FLICKY_03}, // Cucky (1)
+	{"SEAL",     MT_FLICKY_04}, // Rocky (1)
+	{"PIG",      MT_FLICKY_05}, // Picky (1)
+	{"CHIPMUNK", MT_FLICKY_06}, // Ricky (1)
+	{"PENGUIN",  MT_FLICKY_07}, // Pecky (1)
+	{"FISH",     MT_FLICKY_08}, // Nicky (CD)
+	{"RAM",      MT_FLICKY_09}, // Flocky (CD)
+	{"PUFFIN",   MT_FLICKY_10}, // Wicky (CD)
+	{"COW",      MT_FLICKY_11}, // Macky (SRB2)
+	{"RAT",      MT_FLICKY_12}, // Micky (2)
+	{"BEAR",     MT_FLICKY_13}, // Becky (2)
+	{"DOVE",     MT_FLICKY_14}, // Docky (CD)
+	{"CAT",      MT_FLICKY_15}, // Nyannyan (Flicky)
+	{"CANARY",   MT_FLICKY_16}, // Lucky (CD)
 	{"a", 0}, // End of normal flickies - a lower case character so will never fastcmp valid with uppercase tmp
-	//{"FLICKER",  MT_FLICKER},
-	{"SEED",          MT_SEED},
+	//{"FLICKER",          MT_FLICKER}, // Flacky (SRB2)
+	{"SPIDER",   MT_SECRETFLICKY_01}, // Sticky (SRB2)
+	{"BAT",      MT_SECRETFLICKY_02}, // Backy (SRB2)
+	{"SEED",                MT_SEED}, // Seed (CD)
 	{NULL, 0}
 };
 
@@ -1194,13 +1196,17 @@ static void readlevelheader(MYFILE *f, INT32 num)
 				else if (fastcmp(word2, "NORMAL")) i =  0;
 				else if (fastcmp(word2, "BOSS"))   i =  1;
 				else if (fastcmp(word2, "ERZ3"))   i =  2;
+				else if (fastcmp(word2, "NIGHTS")) i =  3;
+				else if (fastcmp(word2, "NIGHTSLINK")) i = 4;
 
-				if (i >= -1 && i <= 2) // -1 for no bonus. Max is 2.
+				if (i >= -1 && i <= 4) // -1 for no bonus. Max is 4.
 					mapheaderinfo[num-1]->bonustype = (SINT8)i;
 				else
 					deh_warning("Level header %d: invalid bonus type number %d", num, i);
 			}
 
+			else if (fastcmp(word, "MAXBONUSLIVES"))
+				mapheaderinfo[num-1]->maxbonuslives = (SINT8)i;
 			else if (fastcmp(word, "LEVELFLAGS"))
 				mapheaderinfo[num-1]->levelflags = (UINT8)i;
 			else if (fastcmp(word, "MENUFLAGS"))
@@ -1616,200 +1622,220 @@ typedef struct
   */
 static actionpointer_t actionpointers[] =
 {
-	{{A_Explode},              "A_EXPLODE"},
-	{{A_Pain},                 "A_PAIN"},
-	{{A_Fall},                 "A_FALL"},
-	{{A_MonitorPop},           "A_MONITORPOP"},
-	{{A_GoldMonitorPop},       "A_GOLDMONITORPOP"},
-	{{A_GoldMonitorRestore},   "A_GOLDMONITORRESTORE"},
-	{{A_GoldMonitorSparkle},   "A_GOLDMONITORSPARKLE"},
-	{{A_Look},                 "A_LOOK"},
-	{{A_Chase},                "A_CHASE"},
-	{{A_FaceStabChase},        "A_FACESTABCHASE"},
-	{{A_FaceTarget},           "A_FACETARGET"},
-	{{A_FaceTracer},           "A_FACETRACER"},
-	{{A_Scream},               "A_SCREAM"},
-	{{A_BossDeath},            "A_BOSSDEATH"},
-	{{A_CustomPower},          "A_CUSTOMPOWER"},
-	{{A_GiveWeapon},           "A_GIVEWEAPON"},
-	{{A_RingBox},              "A_RINGBOX"},
-	{{A_Invincibility},        "A_INVINCIBILITY"},
-	{{A_SuperSneakers},        "A_SUPERSNEAKERS"},
-	{{A_BunnyHop},             "A_BUNNYHOP"},
-	{{A_BubbleSpawn},          "A_BUBBLESPAWN"},
-	{{A_FanBubbleSpawn},       "A_FANBUBBLESPAWN"},
-	{{A_BubbleRise},           "A_BUBBLERISE"},
-	{{A_BubbleCheck},          "A_BUBBLECHECK"},
-	{{A_AwardScore},           "A_AWARDSCORE"},
-	{{A_ExtraLife},            "A_EXTRALIFE"},
-	{{A_GiveShield},           "A_GIVESHIELD"},
-	{{A_GravityBox},           "A_GRAVITYBOX"},
-	{{A_ScoreRise},            "A_SCORERISE"},
-	{{A_ParticleSpawn},        "A_PARTICLESPAWN"},
-	{{A_AttractChase},         "A_ATTRACTCHASE"},
-	{{A_DropMine},             "A_DROPMINE"},
-	{{A_FishJump},             "A_FISHJUMP"},
-	{{A_ThrownRing},           "A_THROWNRING"},
-	{{A_SetSolidSteam},        "A_SETSOLIDSTEAM"},
-	{{A_UnsetSolidSteam},      "A_UNSETSOLIDSTEAM"},
-	{{A_SignPlayer},           "A_SIGNPLAYER"},
-	{{A_OverlayThink},         "A_OVERLAYTHINK"},
-	{{A_JetChase},             "A_JETCHASE"},
-	{{A_JetbThink},            "A_JETBTHINK"},
-	{{A_JetgThink},            "A_JETGTHINK"},
-	{{A_JetgShoot},            "A_JETGSHOOT"},
-	{{A_ShootBullet},          "A_SHOOTBULLET"},
-	{{A_MinusDigging},         "A_MINUSDIGGING"},
-	{{A_MinusPopup},           "A_MINUSPOPUP"},
-	{{A_MinusCheck},           "A_MINUSCHECK"},
-	{{A_ChickenCheck},         "A_CHICKENCHECK"},
-	{{A_MouseThink},           "A_MOUSETHINK"},
-	{{A_DetonChase},           "A_DETONCHASE"},
-	{{A_CapeChase},            "A_CAPECHASE"},
-	{{A_RotateSpikeBall},      "A_ROTATESPIKEBALL"},
-	{{A_SlingAppear},          "A_SLINGAPPEAR"},
-	{{A_UnidusBall},           "A_UNIDUSBALL"},
-	{{A_RockSpawn},            "A_ROCKSPAWN"},
-	{{A_SetFuse},              "A_SETFUSE"},
-	{{A_CrawlaCommanderThink}, "A_CRAWLACOMMANDERTHINK"},
-	{{A_SmokeTrailer},         "A_SMOKETRAILER"},
-	{{A_RingExplode},          "A_RINGEXPLODE"},
-	{{A_OldRingExplode},       "A_OLDRINGEXPLODE"},
-	{{A_MixUp},                "A_MIXUP"},
-	{{A_RecyclePowers},        "A_RECYCLEPOWERS"},
-	{{A_Boss1Chase},           "A_BOSS1CHASE"},
-	{{A_FocusTarget},          "A_FOCUSTARGET"},
-	{{A_Boss2Chase},           "A_BOSS2CHASE"},
-	{{A_Boss2Pogo},            "A_BOSS2POGO"},
-	{{A_BossZoom},             "A_BOSSZOOM"},
-	{{A_BossScream},           "A_BOSSSCREAM"},
-	{{A_Boss2TakeDamage},      "A_BOSS2TAKEDAMAGE"},
-	{{A_Boss7Chase},           "A_BOSS7CHASE"},
-	{{A_GoopSplat},            "A_GOOPSPLAT"},
-	{{A_Boss2PogoSFX},         "A_BOSS2POGOSFX"},
-	{{A_Boss2PogoTarget},      "A_BOSS2POGOTARGET"},
-	{{A_BossJetFume},          "A_BOSSJETFUME"},
-	{{A_EggmanBox},            "A_EGGMANBOX"},
-	{{A_TurretFire},           "A_TURRETFIRE"},
-	{{A_SuperTurretFire},      "A_SUPERTURRETFIRE"},
-	{{A_TurretStop},           "A_TURRETSTOP"},
-	{{A_JetJawRoam},           "A_JETJAWROAM"},
-	{{A_JetJawChomp},          "A_JETJAWCHOMP"},
-	{{A_PointyThink},          "A_POINTYTHINK"},
-	{{A_CheckBuddy},           "A_CHECKBUDDY"},
-	{{A_HoodThink},            "A_HOODTHINK"},
-	{{A_ArrowCheck},           "A_ARROWCHECK"},
-	{{A_SnailerThink},         "A_SNAILERTHINK"},
-	{{A_SharpChase},           "A_SHARPCHASE"},
-	{{A_SharpSpin},            "A_SHARPSPIN"},
-	{{A_VultureVtol},          "A_VULTUREVTOL"},
-	{{A_VultureCheck},         "A_VULTURECHECK"},
-	{{A_SkimChase},            "A_SKIMCHASE"},
-	{{A_1upThinker},           "A_1UPTHINKER"},
-	{{A_SkullAttack},          "A_SKULLATTACK"},
-	{{A_LobShot},              "A_LOBSHOT"},
-	{{A_FireShot},             "A_FIRESHOT"},
-	{{A_SuperFireShot},        "A_SUPERFIRESHOT"},
-	{{A_BossFireShot},         "A_BOSSFIRESHOT"},
-	{{A_Boss7FireMissiles},    "A_BOSS7FIREMISSILES"},
-	{{A_Boss1Laser},           "A_BOSS1LASER"},
-	{{A_Boss4Reverse},         "A_BOSS4REVERSE"},
-	{{A_Boss4SpeedUp},         "A_BOSS4SPEEDUP"},
-	{{A_Boss4Raise},           "A_BOSS4RAISE"},
-	{{A_SparkFollow},          "A_SPARKFOLLOW"},
-	{{A_BuzzFly},              "A_BUZZFLY"},
-	{{A_GuardChase},           "A_GUARDCHASE"},
-	{{A_EggShield},            "A_EGGSHIELD"},
-	{{A_SetReactionTime},      "A_SETREACTIONTIME"},
-	{{A_Boss1Spikeballs},      "A_BOSS1SPIKEBALLS"},
-	{{A_Boss3TakeDamage},      "A_BOSS3TAKEDAMAGE"},
-	{{A_Boss3Path},            "A_BOSS3PATH"},
-	{{A_LinedefExecute},       "A_LINEDEFEXECUTE"},
-	{{A_PlaySeeSound},         "A_PLAYSEESOUND"},
-	{{A_PlayAttackSound},      "A_PLAYATTACKSOUND"},
-	{{A_PlayActiveSound},      "A_PLAYACTIVESOUND"},
-	{{A_SpawnObjectAbsolute},  "A_SPAWNOBJECTABSOLUTE"},
-	{{A_SpawnObjectRelative},  "A_SPAWNOBJECTRELATIVE"},
-	{{A_ChangeAngleRelative},  "A_CHANGEANGLERELATIVE"},
-	{{A_ChangeAngleAbsolute},  "A_CHANGEANGLEABSOLUTE"},
-	{{A_PlaySound},            "A_PLAYSOUND"},
-	{{A_FindTarget},           "A_FINDTARGET"},
-	{{A_FindTracer},           "A_FINDTRACER"},
-	{{A_SetTics},              "A_SETTICS"},
-	{{A_SetRandomTics},        "A_SETRANDOMTICS"},
-	{{A_ChangeColorRelative},  "A_CHANGECOLORRELATIVE"},
-	{{A_ChangeColorAbsolute},  "A_CHANGECOLORABSOLUTE"},
-	{{A_MoveRelative},         "A_MOVERELATIVE"},
-	{{A_MoveAbsolute},         "A_MOVEABSOLUTE"},
-	{{A_Thrust},               "A_THRUST"},
-	{{A_ZThrust},              "A_ZTHRUST"},
-	{{A_SetTargetsTarget},     "A_SETTARGETSTARGET"},
-	{{A_SetObjectFlags},       "A_SETOBJECTFLAGS"},
-	{{A_SetObjectFlags2},      "A_SETOBJECTFLAGS2"},
-	{{A_RandomState},          "A_RANDOMSTATE"},
-	{{A_RandomStateRange},     "A_RANDOMSTATERANGE"},
-	{{A_DualAction},           "A_DUALACTION"},
-	{{A_RemoteAction},         "A_REMOTEACTION"},
-	{{A_ToggleFlameJet},       "A_TOGGLEFLAMEJET"},
-	{{A_OrbitNights},          "A_ORBITNIGHTS"},
-	{{A_GhostMe},              "A_GHOSTME"},
-	{{A_SetObjectState},       "A_SETOBJECTSTATE"},
-	{{A_SetObjectTypeState},   "A_SETOBJECTTYPESTATE"},
-	{{A_KnockBack},            "A_KNOCKBACK"},
-	{{A_PushAway},             "A_PUSHAWAY"},
-	{{A_RingDrain},            "A_RINGDRAIN"},
-	{{A_SplitShot},            "A_SPLITSHOT"},
-	{{A_MissileSplit},         "A_MISSILESPLIT"},
-	{{A_MultiShot},            "A_MULTISHOT"},
-	{{A_InstaLoop},            "A_INSTALOOP"},
-	{{A_Custom3DRotate},       "A_CUSTOM3DROTATE"},
-	{{A_SearchForPlayers},     "A_SEARCHFORPLAYERS"},
-	{{A_CheckRandom},          "A_CHECKRANDOM"},
-	{{A_CheckTargetRings},     "A_CHECKTARGETRINGS"},
-	{{A_CheckRings},           "A_CHECKRINGS"},
-	{{A_CheckTotalRings},      "A_CHECKTOTALRINGS"},
-	{{A_CheckHealth},          "A_CHECKHEALTH"},
-	{{A_CheckRange},           "A_CHECKRANGE"},
-	{{A_CheckHeight},          "A_CHECKHEIGHT"},
-	{{A_CheckTrueRange},       "A_CHECKTRUERANGE"},
-	{{A_CheckThingCount},      "A_CHECKTHINGCOUNT"},
-	{{A_CheckAmbush},          "A_CHECKAMBUSH"},
-	{{A_CheckCustomValue},     "A_CHECKCUSTOMVALUE"},
-	{{A_CheckCusValMemo},      "A_CHECKCUSVALMEMO"},
-	{{A_SetCustomValue},       "A_SETCUSTOMVALUE"},
-	{{A_UseCusValMemo},        "A_USECUSVALMEMO"},
-	{{A_RelayCustomValue},     "A_RELAYCUSTOMVALUE"},
-	{{A_CusValAction},         "A_CUSVALACTION"},
-	{{A_ForceStop},            "A_FORCESTOP"},
-	{{A_ForceWin},             "A_FORCEWIN"},
-	{{A_SpikeRetract},         "A_SPIKERETRACT"},
-	{{A_InfoState},            "A_INFOSTATE"},
-	{{A_Repeat},               "A_REPEAT"},
-	{{A_SetScale},             "A_SETSCALE"},
-	{{A_RemoteDamage},         "A_REMOTEDAMAGE"},
-	{{A_HomingChase},          "A_HOMINGCHASE"},
-	{{A_TrapShot},             "A_TRAPSHOT"},
-	{{A_VileTarget},           "A_VILETARGET"},
-	{{A_VileAttack},           "A_VILEATTACK"},
-	{{A_VileFire},             "A_VILEFIRE"},
-	{{A_BrakChase},            "A_BRAKCHASE"},
-	{{A_BrakFireShot},         "A_BRAKFIRESHOT"},
-	{{A_BrakLobShot},          "A_BRAKLOBSHOT"},
-	{{A_NapalmScatter},        "A_NAPALMSCATTER"},
-	{{A_SpawnFreshCopy},       "A_SPAWNFRESHCOPY"},
-	{{A_FlickySpawn},          "A_FLICKYSPAWN"},
-	{{A_FlickyAim},            "A_FLICKYAIM"},
-	{{A_FlickyFly},            "A_FLICKYFLY"},
-	{{A_FlickySoar},           "A_FLICKYSOAR"},
-	{{A_FlickyCoast},          "A_FLICKYCOAST"},
-	{{A_FlickyHop},            "A_FLICKYHOP"},
-	{{A_FlickyFlounder},       "A_FLICKYFLOUNDER"},
-	{{A_FlickyCheck},          "A_FLICKYCHECK"},
-	{{A_FlickyHeightCheck},    "A_FLICKYHEIGHTCHECK"},
-	{{A_FlickyFlutter},        "A_FLICKYFLUTTER"},
-	{{A_FlameParticle},        "A_FLAMEPARTICLE"},
-	{{A_FadeOverlay},          "A_FADEOVERLAY"},
-	{{A_Boss5Jump},            "A_BOSS5JUMP"},
+	{{A_Explode},                "A_EXPLODE"},
+	{{A_Pain},                   "A_PAIN"},
+	{{A_Fall},                   "A_FALL"},
+	{{A_MonitorPop},             "A_MONITORPOP"},
+	{{A_GoldMonitorPop},         "A_GOLDMONITORPOP"},
+	{{A_GoldMonitorRestore},     "A_GOLDMONITORRESTORE"},
+	{{A_GoldMonitorSparkle},     "A_GOLDMONITORSPARKLE"},
+	{{A_Look},                   "A_LOOK"},
+	{{A_Chase},                  "A_CHASE"},
+	{{A_FaceStabChase},          "A_FACESTABCHASE"},
+	{{A_FaceStabRev},            "A_FACESTABREV"},
+	{{A_FaceStabHurl},           "A_FACESTABHURL"},
+	{{A_FaceStabMiss},           "A_FACESTABMISS"},
+	{{A_StatueBurst},            "A_STATUEBURST"},
+	{{A_FaceTarget},             "A_FACETARGET"},
+	{{A_FaceTracer},             "A_FACETRACER"},
+	{{A_Scream},                 "A_SCREAM"},
+	{{A_BossDeath},              "A_BOSSDEATH"},
+	{{A_CustomPower},            "A_CUSTOMPOWER"},
+	{{A_GiveWeapon},             "A_GIVEWEAPON"},
+	{{A_RingBox},                "A_RINGBOX"},
+	{{A_Invincibility},          "A_INVINCIBILITY"},
+	{{A_SuperSneakers},          "A_SUPERSNEAKERS"},
+	{{A_BunnyHop},               "A_BUNNYHOP"},
+	{{A_BubbleSpawn},            "A_BUBBLESPAWN"},
+	{{A_FanBubbleSpawn},         "A_FANBUBBLESPAWN"},
+	{{A_BubbleRise},             "A_BUBBLERISE"},
+	{{A_BubbleCheck},            "A_BUBBLECHECK"},
+	{{A_AwardScore},             "A_AWARDSCORE"},
+	{{A_ExtraLife},              "A_EXTRALIFE"},
+	{{A_GiveShield},             "A_GIVESHIELD"},
+	{{A_GravityBox},             "A_GRAVITYBOX"},
+	{{A_ScoreRise},              "A_SCORERISE"},
+	{{A_AttractChase},           "A_ATTRACTCHASE"},
+	{{A_DropMine},               "A_DROPMINE"},
+	{{A_FishJump},               "A_FISHJUMP"},
+	{{A_ThrownRing},             "A_THROWNRING"},
+	{{A_SetSolidSteam},          "A_SETSOLIDSTEAM"},
+	{{A_UnsetSolidSteam},        "A_UNSETSOLIDSTEAM"},
+	{{A_SignPlayer},             "A_SIGNPLAYER"},
+	{{A_OverlayThink},           "A_OVERLAYTHINK"},
+	{{A_JetChase},               "A_JETCHASE"},
+	{{A_JetbThink},              "A_JETBTHINK"},
+	{{A_JetgThink},              "A_JETGTHINK"},
+	{{A_JetgShoot},              "A_JETGSHOOT"},
+	{{A_ShootBullet},            "A_SHOOTBULLET"},
+	{{A_MinusDigging},           "A_MINUSDIGGING"},
+	{{A_MinusPopup},             "A_MINUSPOPUP"},
+	{{A_MinusCheck},             "A_MINUSCHECK"},
+	{{A_ChickenCheck},           "A_CHICKENCHECK"},
+	{{A_MouseThink},             "A_MOUSETHINK"},
+	{{A_DetonChase},             "A_DETONCHASE"},
+	{{A_CapeChase},              "A_CAPECHASE"},
+	{{A_RotateSpikeBall},        "A_ROTATESPIKEBALL"},
+	{{A_SlingAppear},            "A_SLINGAPPEAR"},
+	{{A_UnidusBall},             "A_UNIDUSBALL"},
+	{{A_RockSpawn},              "A_ROCKSPAWN"},
+	{{A_SetFuse},                "A_SETFUSE"},
+	{{A_CrawlaCommanderThink},   "A_CRAWLACOMMANDERTHINK"},
+	{{A_SmokeTrailer},           "A_SMOKETRAILER"},
+	{{A_RingExplode},            "A_RINGEXPLODE"},
+	{{A_OldRingExplode},         "A_OLDRINGEXPLODE"},
+	{{A_MixUp},                  "A_MIXUP"},
+	{{A_RecyclePowers},          "A_RECYCLEPOWERS"},
+	{{A_Boss1Chase},             "A_BOSS1CHASE"},
+	{{A_FocusTarget},            "A_FOCUSTARGET"},
+	{{A_Boss2Chase},             "A_BOSS2CHASE"},
+	{{A_Boss2Pogo},              "A_BOSS2POGO"},
+	{{A_BossZoom},               "A_BOSSZOOM"},
+	{{A_BossScream},             "A_BOSSSCREAM"},
+	{{A_Boss2TakeDamage},        "A_BOSS2TAKEDAMAGE"},
+	{{A_Boss7Chase},             "A_BOSS7CHASE"},
+	{{A_GoopSplat},              "A_GOOPSPLAT"},
+	{{A_Boss2PogoSFX},           "A_BOSS2POGOSFX"},
+	{{A_Boss2PogoTarget},        "A_BOSS2POGOTARGET"},
+	{{A_BossJetFume},            "A_BOSSJETFUME"},
+	{{A_EggmanBox},              "A_EGGMANBOX"},
+	{{A_TurretFire},             "A_TURRETFIRE"},
+	{{A_SuperTurretFire},        "A_SUPERTURRETFIRE"},
+	{{A_TurretStop},             "A_TURRETSTOP"},
+	{{A_JetJawRoam},             "A_JETJAWROAM"},
+	{{A_JetJawChomp},            "A_JETJAWCHOMP"},
+	{{A_PointyThink},            "A_POINTYTHINK"},
+	{{A_CheckBuddy},             "A_CHECKBUDDY"},
+	{{A_HoodFire},               "A_HOODFIRE"},
+	{{A_HoodThink},              "A_HOODTHINK"},
+	{{A_HoodFall},               "A_HOODFALL"},
+	{{A_ArrowBonks},             "A_ARROWBONKS"},
+	{{A_SnailerThink},           "A_SNAILERTHINK"},
+	{{A_SharpChase},             "A_SHARPCHASE"},
+	{{A_SharpSpin},              "A_SHARPSPIN"},
+	{{A_SharpDecel},             "A_SHARPDECEL"},
+	{{A_CrushstaceanWalk},       "A_CRUSHSTACEANWALK"},
+	{{A_CrushstaceanPunch},      "A_CRUSHSTACEANPUNCH"},
+	{{A_CrushclawAim},           "A_CRUSHCLAWAIM"},
+	{{A_CrushclawLaunch},        "A_CRUSHCLAWLAUNCH"},
+	{{A_VultureVtol},            "A_VULTUREVTOL"},
+	{{A_VultureCheck},           "A_VULTURECHECK"},
+	{{A_SkimChase},              "A_SKIMCHASE"},
+	{{A_1upThinker},             "A_1UPTHINKER"},
+	{{A_SkullAttack},            "A_SKULLATTACK"},
+	{{A_LobShot},                "A_LOBSHOT"},
+	{{A_FireShot},               "A_FIRESHOT"},
+	{{A_SuperFireShot},          "A_SUPERFIRESHOT"},
+	{{A_BossFireShot},           "A_BOSSFIRESHOT"},
+	{{A_Boss7FireMissiles},      "A_BOSS7FIREMISSILES"},
+	{{A_Boss1Laser},             "A_BOSS1LASER"},
+	{{A_Boss4Reverse},           "A_BOSS4REVERSE"},
+	{{A_Boss4SpeedUp},           "A_BOSS4SPEEDUP"},
+	{{A_Boss4Raise},             "A_BOSS4RAISE"},
+	{{A_SparkFollow},            "A_SPARKFOLLOW"},
+	{{A_BuzzFly},                "A_BUZZFLY"},
+	{{A_GuardChase},             "A_GUARDCHASE"},
+	{{A_EggShield},              "A_EGGSHIELD"},
+	{{A_SetReactionTime},        "A_SETREACTIONTIME"},
+	{{A_Boss1Spikeballs},        "A_BOSS1SPIKEBALLS"},
+	{{A_Boss3TakeDamage},        "A_BOSS3TAKEDAMAGE"},
+	{{A_Boss3Path},              "A_BOSS3PATH"},
+	{{A_LinedefExecute},         "A_LINEDEFEXECUTE"},
+	{{A_PlaySeeSound},           "A_PLAYSEESOUND"},
+	{{A_PlayAttackSound},        "A_PLAYATTACKSOUND"},
+	{{A_PlayActiveSound},        "A_PLAYACTIVESOUND"},
+	{{A_SpawnObjectAbsolute},    "A_SPAWNOBJECTABSOLUTE"},
+	{{A_SpawnObjectRelative},    "A_SPAWNOBJECTRELATIVE"},
+	{{A_ChangeAngleRelative},    "A_CHANGEANGLERELATIVE"},
+	{{A_ChangeAngleAbsolute},    "A_CHANGEANGLEABSOLUTE"},
+	{{A_PlaySound},              "A_PLAYSOUND"},
+	{{A_FindTarget},             "A_FINDTARGET"},
+	{{A_FindTracer},             "A_FINDTRACER"},
+	{{A_SetTics},                "A_SETTICS"},
+	{{A_SetRandomTics},          "A_SETRANDOMTICS"},
+	{{A_ChangeColorRelative},    "A_CHANGECOLORRELATIVE"},
+	{{A_ChangeColorAbsolute},    "A_CHANGECOLORABSOLUTE"},
+	{{A_MoveRelative},           "A_MOVERELATIVE"},
+	{{A_MoveAbsolute},           "A_MOVEABSOLUTE"},
+	{{A_Thrust},                 "A_THRUST"},
+	{{A_ZThrust},                "A_ZTHRUST"},
+	{{A_SetTargetsTarget},       "A_SETTARGETSTARGET"},
+	{{A_SetObjectFlags},         "A_SETOBJECTFLAGS"},
+	{{A_SetObjectFlags2},        "A_SETOBJECTFLAGS2"},
+	{{A_RandomState},            "A_RANDOMSTATE"},
+	{{A_RandomStateRange},       "A_RANDOMSTATERANGE"},
+	{{A_DualAction},             "A_DUALACTION"},
+	{{A_RemoteAction},           "A_REMOTEACTION"},
+	{{A_ToggleFlameJet},         "A_TOGGLEFLAMEJET"},
+	{{A_OrbitNights},            "A_ORBITNIGHTS"},
+	{{A_GhostMe},                "A_GHOSTME"},
+	{{A_SetObjectState},         "A_SETOBJECTSTATE"},
+	{{A_SetObjectTypeState},     "A_SETOBJECTTYPESTATE"},
+	{{A_KnockBack},              "A_KNOCKBACK"},
+	{{A_PushAway},               "A_PUSHAWAY"},
+	{{A_RingDrain},              "A_RINGDRAIN"},
+	{{A_SplitShot},              "A_SPLITSHOT"},
+	{{A_MissileSplit},           "A_MISSILESPLIT"},
+	{{A_MultiShot},              "A_MULTISHOT"},
+	{{A_InstaLoop},              "A_INSTALOOP"},
+	{{A_Custom3DRotate},         "A_CUSTOM3DROTATE"},
+	{{A_SearchForPlayers},       "A_SEARCHFORPLAYERS"},
+	{{A_CheckRandom},            "A_CHECKRANDOM"},
+	{{A_CheckTargetRings},       "A_CHECKTARGETRINGS"},
+	{{A_CheckRings},             "A_CHECKRINGS"},
+	{{A_CheckTotalRings},        "A_CHECKTOTALRINGS"},
+	{{A_CheckHealth},            "A_CHECKHEALTH"},
+	{{A_CheckRange},             "A_CHECKRANGE"},
+	{{A_CheckHeight},            "A_CHECKHEIGHT"},
+	{{A_CheckTrueRange},         "A_CHECKTRUERANGE"},
+	{{A_CheckThingCount},        "A_CHECKTHINGCOUNT"},
+	{{A_CheckAmbush},            "A_CHECKAMBUSH"},
+	{{A_CheckCustomValue},       "A_CHECKCUSTOMVALUE"},
+	{{A_CheckCusValMemo},        "A_CHECKCUSVALMEMO"},
+	{{A_SetCustomValue},         "A_SETCUSTOMVALUE"},
+	{{A_UseCusValMemo},          "A_USECUSVALMEMO"},
+	{{A_RelayCustomValue},       "A_RELAYCUSTOMVALUE"},
+	{{A_CusValAction},           "A_CUSVALACTION"},
+	{{A_ForceStop},              "A_FORCESTOP"},
+	{{A_ForceWin},               "A_FORCEWIN"},
+	{{A_SpikeRetract},           "A_SPIKERETRACT"},
+	{{A_InfoState},              "A_INFOSTATE"},
+	{{A_Repeat},                 "A_REPEAT"},
+	{{A_SetScale},               "A_SETSCALE"},
+	{{A_RemoteDamage},           "A_REMOTEDAMAGE"},
+	{{A_HomingChase},            "A_HOMINGCHASE"},
+	{{A_TrapShot},               "A_TRAPSHOT"},
+	{{A_VileTarget},             "A_VILETARGET"},
+	{{A_VileAttack},             "A_VILEATTACK"},
+	{{A_VileFire},               "A_VILEFIRE"},
+	{{A_BrakChase},              "A_BRAKCHASE"},
+	{{A_BrakFireShot},           "A_BRAKFIRESHOT"},
+	{{A_BrakLobShot},            "A_BRAKLOBSHOT"},
+	{{A_NapalmScatter},          "A_NAPALMSCATTER"},
+	{{A_SpawnFreshCopy},         "A_SPAWNFRESHCOPY"},
+	{{A_FlickySpawn},            "A_FLICKYSPAWN"},
+	{{A_FlickyAim},              "A_FLICKYAIM"},
+	{{A_FlickyFly},              "A_FLICKYFLY"},
+	{{A_FlickySoar},             "A_FLICKYSOAR"},
+	{{A_FlickyCoast},            "A_FLICKYCOAST"},
+	{{A_FlickyHop},              "A_FLICKYHOP"},
+	{{A_FlickyFlounder},         "A_FLICKYFLOUNDER"},
+	{{A_FlickyCheck},            "A_FLICKYCHECK"},
+	{{A_FlickyHeightCheck},      "A_FLICKYHEIGHTCHECK"},
+	{{A_FlickyFlutter},          "A_FLICKYFLUTTER"},
+	{{A_FlameParticle},          "A_FLAMEPARTICLE"},
+	{{A_FadeOverlay},            "A_FADEOVERLAY"},
+	{{A_Boss5Jump},              "A_BOSS5JUMP"},
+	{{A_LightBeamReset},         "A_LIGHTBEAMRESET"},
+	{{A_MineExplode},            "A_MINEEXPLODE"},
+	{{A_MineRange},              "A_MINERANGE"},
+	{{A_ConnectToGround},        "A_CONNECTTOGROUND"},
+	{{A_SpawnParticleRelative},  "A_SPAWNPARTICLERELATIVE"},
+	{{A_MultiShotDist},          "A_MULTISHOTDIST"},
+	{{A_WhoCaresIfYourSonIsABee},"A_WHOCARESIFYOURSONISABEE"},
+	{{A_ParentTriesToSleep},     "A_PARENTTRIESTOSLEEP"},
+	{{A_CryingToMomma},          "A_CRYINGTOMOMMA"},
+	{{A_CheckFlags2},            "A_CHECKFLAGS2"},
 
 	{{NULL},                   "NONE"},
 
@@ -2648,11 +2674,21 @@ static void readmaincfg(MYFILE *f)
 					value = get_number(word2);
 
 				sstage_start = (INT16)value;
-				sstage_end = (INT16)(sstage_start+6); // 7 special stages total
+				sstage_end = (INT16)(sstage_start+7); // 7 special stages total plus one weirdo
 			}
-			else if (fastcmp(word, "USENIGHTSSS"))
+			else if (fastcmp(word, "SMPSTAGE_START"))
 			{
-				useNightsSS = (UINT8)(value || word2[0] == 'T' || word2[0] == 'Y');
+				// Support using the actual map name,
+				// i.e., Level AB, Level FZ, etc.
+
+				// Convert to map number
+				if (word2[0] >= 'A' && word2[0] <= 'Z')
+					value = M_MapNumber(word2[0], word2[1]);
+				else
+					value = get_number(word2);
+
+				smpstage_start = (INT16)value;
+				smpstage_end = (INT16)(smpstage_start+6); // 7 special stages total
 			}
 			else if (fastcmp(word, "REDTEAM"))
 			{
@@ -2698,11 +2734,14 @@ static void readmaincfg(MYFILE *f)
 			{
 				extralifetics = (UINT16)get_number(word2);
 			}
+			else if (fastcmp(word, "NIGHTSLINKTICS"))
+			{
+				nightslinktics = (UINT16)get_number(word2);
+			}
 			else if (fastcmp(word, "GAMEOVERTICS"))
 			{
 				gameovertics = get_number(word2);
 			}
-
 			else if (fastcmp(word, "INTROTOPLAY"))
 			{
 				introtoplay = (UINT8)get_number(word2);
@@ -3577,10 +3616,6 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_RBUZZFLY1",
 	"S_RBUZZFLY2",
 
-	// AquaBuzz
-	"S_BBUZZFLY1",
-	"S_BBUZZFLY2",
-
 	// Jetty-Syn Bomber
 	"S_JETBLOOK1",
 	"S_JETBLOOK2",
@@ -3617,7 +3652,6 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_DETON13",
 	"S_DETON14",
 	"S_DETON15",
-	"S_DETON16",
 
 	// Skim Mine Dropper
 	"S_SKIM1",
@@ -3659,14 +3693,40 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_TURRETPOPDOWN7",
 	"S_TURRETPOPDOWN8",
 
-	// Sharp
-	"S_SHARP_ROAM1",
-	"S_SHARP_ROAM2",
-	"S_SHARP_AIM1",
-	"S_SHARP_AIM2",
-	"S_SHARP_AIM3",
-	"S_SHARP_AIM4",
-	"S_SHARP_SPIN",
+	// Spincushion
+	"S_SPINCUSHION_LOOK",
+	"S_SPINCUSHION_CHASE1",
+	"S_SPINCUSHION_CHASE2",
+	"S_SPINCUSHION_CHASE3",
+	"S_SPINCUSHION_CHASE4",
+	"S_SPINCUSHION_AIM1",
+	"S_SPINCUSHION_AIM2",
+	"S_SPINCUSHION_AIM3",
+	"S_SPINCUSHION_AIM4",
+	"S_SPINCUSHION_AIM5",
+	"S_SPINCUSHION_SPIN1",
+	"S_SPINCUSHION_SPIN2",
+	"S_SPINCUSHION_SPIN3",
+	"S_SPINCUSHION_SPIN4",
+	"S_SPINCUSHION_STOP1",
+	"S_SPINCUSHION_STOP2",
+	"S_SPINCUSHION_STOP3",
+	"S_SPINCUSHION_STOP4",
+
+	// Crushstacean
+	"S_CRUSHSTACEAN_ROAM1",
+	"S_CRUSHSTACEAN_ROAM2",
+	"S_CRUSHSTACEAN_ROAM3",
+	"S_CRUSHSTACEAN_ROAM4",
+	"S_CRUSHSTACEAN_ROAMPAUSE",
+	"S_CRUSHSTACEAN_PUNCH1",
+	"S_CRUSHSTACEAN_PUNCH2",
+	"S_CRUSHCLAW_AIM",
+	"S_CRUSHCLAW_OUT",
+	"S_CRUSHCLAW_STAY",
+	"S_CRUSHCLAW_IN",
+	"S_CRUSHCLAW_WAIT",
+	"S_CRUSHCHAIN",
 
 	// Jet Jaw
 	"S_JETJAW_ROAM1",
@@ -3715,11 +3775,12 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 
 	// Robo-Hood
 	"S_ROBOHOOD_LOOK",
-	"S_ROBOHOOD_STND",
-	"S_ROBOHOOD_SHOOT",
-	"S_ROBOHOOD_JUMP",
+	"S_ROBOHOOD_STAND",
+	"S_ROBOHOOD_FIRE1",
+	"S_ROBOHOOD_FIRE2",
+	"S_ROBOHOOD_JUMP1",
 	"S_ROBOHOOD_JUMP2",
-	"S_ROBOHOOD_FALL",
+	"S_ROBOHOOD_JUMP3",
 
 	// CastleBot FaceStabber
 	"S_FACESTABBER_STND1",
@@ -3732,6 +3793,11 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_FACESTABBER_CHARGE2",
 	"S_FACESTABBER_CHARGE3",
 	"S_FACESTABBER_CHARGE4",
+	"S_FACESTABBER_PAIN",
+	"S_FACESTABBER_DIE1",
+	"S_FACESTABBER_DIE2",
+	"S_FACESTABBER_DIE3",
+	"S_FACESTABBERSPEAR",
 
 	// Egg Guard
 	"S_EGGGUARD_STND",
@@ -3749,6 +3815,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 
 	// Egg Shield for Egg Guard
 	"S_EGGSHIELD",
+	"S_EGGSHIELDBREAK",
 
 	// Green Snapper
 	"S_GSNAPPER_STND",
@@ -3806,13 +3873,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_UNIDUS_BALL",
 
 	// Boss Explosion
-	"S_BPLD1",
-	"S_BPLD2",
-	"S_BPLD3",
-	"S_BPLD4",
-	"S_BPLD5",
-	"S_BPLD6",
-	"S_BPLD7",
+	"S_BOSSEXPLODE",
 
 	// S3&K Boss Explosion
 	"S_SONIC3KBOSSEXPLOSION1",
@@ -4317,17 +4378,27 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_RING",
 
 	// Blue Sphere for special stages
-	"S_BLUEBALL",
-	"S_BLUEBALLSPARK",
+	"S_BLUESPHERE",
+	"S_BLUESPHEREBONUS",
+	"S_BLUESPHERESPARK",
+
+	// Bomb Sphere
+	"S_BOMBSPHERE1",
+	"S_BOMBSPHERE2",
+	"S_BOMBSPHERE3",
+	"S_BOMBSPHERE4",
+
+	// NiGHTS Chip
+	"S_NIGHTSCHIP",
+	"S_NIGHTSCHIPBONUS",
+
+	// NiGHTS Star
+	"S_NIGHTSSTAR",
+	"S_NIGHTSSTARXMAS",
 
 	// Gravity Wells for special stages
 	"S_GRAVWELLGREEN",
-	"S_GRAVWELLGREEN2",
-	"S_GRAVWELLGREEN3",
-
 	"S_GRAVWELLRED",
-	"S_GRAVWELLRED2",
-	"S_GRAVWELLRED3",
 
 	// Individual Team Rings
 	"S_TEAMRING",
@@ -4376,14 +4447,10 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_CEMG6",
 	"S_CEMG7",
 
-	// Emeralds (for hunt)
-	"S_EMER1",
-
-	"S_FAN",
-	"S_FAN2",
-	"S_FAN3",
-	"S_FAN4",
-	"S_FAN5",
+	// Emerald hunt shards
+	"S_SHRD1",
+	"S_SHRD2",
+	"S_SHRD3",
 
 	// Bubble Source
 	"S_BUBBLES1",
@@ -4446,16 +4513,6 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_SIGN52", // Eggman
 	"S_SIGN53",
 
-	// Steam Riser
-	"S_STEAM1",
-	"S_STEAM2",
-	"S_STEAM3",
-	"S_STEAM4",
-	"S_STEAM5",
-	"S_STEAM6",
-	"S_STEAM7",
-	"S_STEAM8",
-
 	// Spike Ball
 	"S_SPIKEBALL1",
 	"S_SPIKEBALL2",
@@ -4503,14 +4560,18 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_STARPOST_ENDSPIN",
 
 	// Big floating mine
-	"S_BIGMINE1",
-	"S_BIGMINE2",
-	"S_BIGMINE3",
-	"S_BIGMINE4",
-	"S_BIGMINE5",
-	"S_BIGMINE6",
-	"S_BIGMINE7",
-	"S_BIGMINE8",
+	"S_BIGMINE_IDLE",
+	"S_BIGMINE_ALERT1",
+	"S_BIGMINE_ALERT2",
+	"S_BIGMINE_ALERT3",
+	"S_BIGMINE_SET1",
+	"S_BIGMINE_SET2",
+	"S_BIGMINE_SET3",
+	"S_BIGMINE_BLAST1",
+	"S_BIGMINE_BLAST2",
+	"S_BIGMINE_BLAST3",
+	"S_BIGMINE_BLAST4",
+	"S_BIGMINE_BLAST5",
 
 	// Cannon Launcher
 	"S_CANNONLAUNCHER1",
@@ -4642,6 +4703,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_THUNDERCOIN_ICON1",
 	"S_THUNDERCOIN_ICON2",
 
+	// ---
+
 	"S_ROCKET",
 
 	"S_LASER",
@@ -4671,22 +4734,17 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 
 	// Arrow
 	"S_ARROW",
-	"S_ARROWUP",
-	"S_ARROWDOWN",
+	"S_ARROWBONK",
 
 	// Trapgoyle Demon fire
-	"S_DEMONFIRE1",
-	"S_DEMONFIRE2",
-	"S_DEMONFIRE3",
-	"S_DEMONFIRE4",
-	"S_DEMONFIRE5",
-	"S_DEMONFIRE6",
+	"S_DEMONFIRE",
 
 	// GFZ flowers
 	"S_GFZFLOWERA",
 	"S_GFZFLOWERB",
 	"S_GFZFLOWERC",
 
+	"S_BLUEBERRYBUSH",
 	"S_BERRYBUSH",
 	"S_BUSH",
 
@@ -4701,10 +4759,28 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_POLYGONTREE",
 	"S_BUSHTREE",
 	"S_BUSHREDTREE",
-
-	// THZ Plant
-	"S_THZFLOWERA",
-	"S_THZFLOWERB",
+	"S_SPRINGTREE",
+
+	// THZ flowers
+	"S_THZFLOWERA", // THZ1 Steam flower
+	"S_THZFLOWERB", // THZ1 Spin flower (red)
+	"S_THZFLOWERC", // THZ1 Spin flower (yellow)
+
+	// THZ Steam Whistle tree/bush
+	"S_THZTREE",
+	"S_THZTREEBRANCH1",
+	"S_THZTREEBRANCH2",
+	"S_THZTREEBRANCH3",
+	"S_THZTREEBRANCH4",
+	"S_THZTREEBRANCH5",
+	"S_THZTREEBRANCH6",
+	"S_THZTREEBRANCH7",
+	"S_THZTREEBRANCH8",
+	"S_THZTREEBRANCH9",
+	"S_THZTREEBRANCH10",
+	"S_THZTREEBRANCH11",
+	"S_THZTREEBRANCH12",
+	"S_THZTREEBRANCH13",
 
 	// THZ Alarm
 	"S_ALARM1",
@@ -4742,18 +4818,33 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	// Blue Crystal
 	"S_BLUECRYSTAL1",
 
+	// Kelp,
+	"S_KELP",
+
+	// DSZ Stalagmites
+	"S_DSZSTALAGMITE",
+	"S_DSZ2STALAGMITE",
+
+	// DSZ Light beam
+	"S_LIGHTBEAM1",
+	"S_LIGHTBEAM2",
+	"S_LIGHTBEAM3",
+	"S_LIGHTBEAM4",
+	"S_LIGHTBEAM5",
+	"S_LIGHTBEAM6",
+	"S_LIGHTBEAM7",
+	"S_LIGHTBEAM8",
+	"S_LIGHTBEAM9",
+	"S_LIGHTBEAM10",
+	"S_LIGHTBEAM11",
+	"S_LIGHTBEAM12",
+
 	// CEZ Chain
 	"S_CEZCHAIN",
 
 	// Flame
-	"S_FLAME1",
-	"S_FLAME2",
-	"S_FLAME3",
-	"S_FLAME4",
-	"S_FLAME5",
-	"S_FLAME6",
+	"S_FLAME",
 	"S_FLAMEPARTICLE",
-
 	"S_FLAMEREST",
 
 	// Eggman Statue
@@ -4768,6 +4859,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_BIGMACECHAIN",
 	"S_SMALLMACE",
 	"S_BIGMACE",
+	"S_SMALLGRABCHAIN",
+	"S_BIGGRABCHAIN",
 
 	// Yellow spring on a ball
 	"S_YELLOWSPRINGBALL",
@@ -4819,7 +4912,24 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_BIGFIREBAR15",
 	"S_BIGFIREBAR16",
 
-	"S_CEZFLOWER1",
+	"S_CEZFLOWER",
+	"S_CEZPOLE",
+	"S_CEZBANNER",
+	"S_PINETREE",
+	"S_CEZBUSH1",
+	"S_CEZBUSH2",
+	"S_CANDLE",
+	"S_CANDLEPRICKET",
+	"S_FLAMEHOLDER",
+	"S_FIRETORCH",
+	"S_WAVINGFLAG",
+	"S_WAVINGFLAGSEG",
+	"S_CRAWLASTATUE",
+	"S_FACESTABBERSTATUE",
+	"S_SUSPICIOUSFACESTABBERSTATUE_WAIT",
+	"S_SUSPICIOUSFACESTABBERSTATUE_BURST1",
+	"S_SUSPICIOUSFACESTABBERSTATUE_BURST2",
+	"S_BRAMBLES",
 
 	// Big Tumbleweed
 	"S_BIGTUMBLEWEED",
@@ -4920,8 +5030,57 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_LAMPPOST2",  // with snow
 	"S_HANGSTAR",
 	// Xmas GFZ bushes
+	"S_XMASBLUEBERRYBUSH",
 	"S_XMASBERRYBUSH",
 	"S_XMASBUSH",
+	// FHZ
+	"S_FHZICE1",
+	"S_FHZICE2",
+
+	// Halloween Scenery
+	// Pumpkins
+	"S_JACKO1",
+	"S_JACKO1OVERLAY_1",
+	"S_JACKO1OVERLAY_2",
+	"S_JACKO1OVERLAY_3",
+	"S_JACKO1OVERLAY_4",
+	"S_JACKO2",
+	"S_JACKO2OVERLAY_1",
+	"S_JACKO2OVERLAY_2",
+	"S_JACKO2OVERLAY_3",
+	"S_JACKO2OVERLAY_4",
+	"S_JACKO3",
+	"S_JACKO3OVERLAY_1",
+	"S_JACKO3OVERLAY_2",
+	"S_JACKO3OVERLAY_3",
+	"S_JACKO3OVERLAY_4",
+	// Dr Seuss Trees
+	"S_HHZTREE_TOP",
+	"S_HHZTREE_TRUNK",
+	"S_HHZTREE_LEAF",
+	// Mushroom
+	"S_HHZSHROOM_1",
+	"S_HHZSHROOM_2",
+	"S_HHZSHROOM_3",
+	"S_HHZSHROOM_4",
+	"S_HHZSHROOM_5",
+	"S_HHZSHROOM_6",
+	"S_HHZSHROOM_7",
+	"S_HHZSHROOM_8",
+	"S_HHZSHROOM_9",
+	"S_HHZSHROOM_10",
+	"S_HHZSHROOM_11",
+	"S_HHZSHROOM_12",
+	"S_HHZSHROOM_13",
+	"S_HHZSHROOM_14",
+	"S_HHZSHROOM_15",
+	"S_HHZSHROOM_16",
+	// Misc
+	"S_HHZGRASS",
+	"S_HHZTENT1",
+	"S_HHZTENT2",
+	"S_HHZSTALAGMITE_TALL",
+	"S_HHZSTALAGMITE_SHORT",
 
 	// Botanic Serenity's loads of scenery states
 	"S_BSZTALLFLOWER_RED",
@@ -5242,6 +5401,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_FLICKY_01_FLAP1",
 	"S_FLICKY_01_FLAP2",
 	"S_FLICKY_01_FLAP3",
+	"S_FLICKY_01_STAND",
+	"S_FLICKY_01_CENTER",
 
 	// Rabbit
 	"S_FLICKY_02_OUT",
@@ -5249,6 +5410,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_FLICKY_02_HOP",
 	"S_FLICKY_02_UP",
 	"S_FLICKY_02_DOWN",
+	"S_FLICKY_02_STAND",
+	"S_FLICKY_02_CENTER",
 
 	// Chicken
 	"S_FLICKY_03_OUT",
@@ -5257,6 +5420,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_FLICKY_03_UP",
 	"S_FLICKY_03_FLAP1",
 	"S_FLICKY_03_FLAP2",
+	"S_FLICKY_03_STAND",
+	"S_FLICKY_03_CENTER",
 
 	// Seal
 	"S_FLICKY_04_OUT",
@@ -5268,6 +5433,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_FLICKY_04_SWIM2",
 	"S_FLICKY_04_SWIM3",
 	"S_FLICKY_04_SWIM4",
+	"S_FLICKY_04_STAND",
+	"S_FLICKY_04_CENTER",
 
 	// Pig
 	"S_FLICKY_05_OUT",
@@ -5275,6 +5442,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_FLICKY_05_HOP",
 	"S_FLICKY_05_UP",
 	"S_FLICKY_05_DOWN",
+	"S_FLICKY_05_STAND",
+	"S_FLICKY_05_CENTER",
 
 	// Chipmunk
 	"S_FLICKY_06_OUT",
@@ -5282,6 +5451,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_FLICKY_06_HOP",
 	"S_FLICKY_06_UP",
 	"S_FLICKY_06_DOWN",
+	"S_FLICKY_06_STAND",
+	"S_FLICKY_06_CENTER",
 
 	// Penguin
 	"S_FLICKY_07_OUT",
@@ -5296,6 +5467,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_FLICKY_07_SWIM1",
 	"S_FLICKY_07_SWIM2",
 	"S_FLICKY_07_SWIM3",
+	"S_FLICKY_07_STAND",
+	"S_FLICKY_07_CENTER",
 
 	// Fish
 	"S_FLICKY_08_OUT",
@@ -5309,6 +5482,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_FLICKY_08_SWIM2",
 	"S_FLICKY_08_SWIM3",
 	"S_FLICKY_08_SWIM4",
+	"S_FLICKY_08_STAND",
+	"S_FLICKY_08_CENTER",
 
 	// Ram
 	"S_FLICKY_09_OUT",
@@ -5316,11 +5491,15 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_FLICKY_09_HOP",
 	"S_FLICKY_09_UP",
 	"S_FLICKY_09_DOWN",
+	"S_FLICKY_09_STAND",
+	"S_FLICKY_09_CENTER",
 
 	// Puffin
 	"S_FLICKY_10_OUT",
 	"S_FLICKY_10_FLAP1",
 	"S_FLICKY_10_FLAP2",
+	"S_FLICKY_10_STAND",
+	"S_FLICKY_10_CENTER",
 
 	// Cow
 	"S_FLICKY_11_OUT",
@@ -5328,6 +5507,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_FLICKY_11_RUN1",
 	"S_FLICKY_11_RUN2",
 	"S_FLICKY_11_RUN3",
+	"S_FLICKY_11_STAND",
+	"S_FLICKY_11_CENTER",
 
 	// Rat
 	"S_FLICKY_12_OUT",
@@ -5335,6 +5516,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_FLICKY_12_RUN1",
 	"S_FLICKY_12_RUN2",
 	"S_FLICKY_12_RUN3",
+	"S_FLICKY_12_STAND",
+	"S_FLICKY_12_CENTER",
 
 	// Bear
 	"S_FLICKY_13_OUT",
@@ -5342,12 +5525,16 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_FLICKY_13_HOP",
 	"S_FLICKY_13_UP",
 	"S_FLICKY_13_DOWN",
+	"S_FLICKY_13_STAND",
+	"S_FLICKY_13_CENTER",
 
 	// Dove
 	"S_FLICKY_14_OUT",
 	"S_FLICKY_14_FLAP1",
 	"S_FLICKY_14_FLAP2",
 	"S_FLICKY_14_FLAP3",
+	"S_FLICKY_14_STAND",
+	"S_FLICKY_14_CENTER",
 
 	// Cat
 	"S_FLICKY_15_OUT",
@@ -5355,26 +5542,79 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_FLICKY_15_HOP",
 	"S_FLICKY_15_UP",
 	"S_FLICKY_15_DOWN",
+	"S_FLICKY_15_STAND",
+	"S_FLICKY_15_CENTER",
 
 	// Canary
 	"S_FLICKY_16_OUT",
 	"S_FLICKY_16_FLAP1",
 	"S_FLICKY_16_FLAP2",
 	"S_FLICKY_16_FLAP3",
+	"S_FLICKY_16_STAND",
+	"S_FLICKY_16_CENTER",
+
+	// Spider
+	"S_SECRETFLICKY_01_OUT",
+	"S_SECRETFLICKY_01_AIM",
+	"S_SECRETFLICKY_01_HOP",
+	"S_SECRETFLICKY_01_UP",
+	"S_SECRETFLICKY_01_DOWN",
+	"S_SECRETFLICKY_01_STAND",
+	"S_SECRETFLICKY_01_CENTER",
+
+	// Bat
+	"S_SECRETFLICKY_02_OUT",
+	"S_SECRETFLICKY_02_FLAP1",
+	"S_SECRETFLICKY_02_FLAP2",
+	"S_SECRETFLICKY_02_FLAP3",
+	"S_SECRETFLICKY_02_STAND",
+	"S_SECRETFLICKY_02_CENTER",
+
+	// Fan
+	"S_FAN",
+	"S_FAN2",
+	"S_FAN3",
+	"S_FAN4",
+	"S_FAN5",
+
+	// Steam Riser
+	"S_STEAM1",
+	"S_STEAM2",
+	"S_STEAM3",
+	"S_STEAM4",
+	"S_STEAM5",
+	"S_STEAM6",
+	"S_STEAM7",
+	"S_STEAM8",
 
+	// Bumpers
+	"S_BUMPER",
+	"S_BUMPERHIT",
+
+	// Balloons
+	"S_BALLOON",
+	"S_BALLOONPOP1",
+	"S_BALLOONPOP2",
+	"S_BALLOONPOP3",
+	"S_BALLOONPOP4",
+	"S_BALLOONPOP5",
+	"S_BALLOONPOP6",
+
+	// Yellow Spring
 	"S_YELLOWSPRING",
 	"S_YELLOWSPRING2",
 	"S_YELLOWSPRING3",
 	"S_YELLOWSPRING4",
 	"S_YELLOWSPRING5",
 
+	// Red Spring
 	"S_REDSPRING",
 	"S_REDSPRING2",
 	"S_REDSPRING3",
 	"S_REDSPRING4",
 	"S_REDSPRING5",
 
-	// Blue Springs
+	// Blue Spring
 	"S_BLUESPRING",
 	"S_BLUESPRING2",
 	"S_BLUESPRING3",
@@ -5401,6 +5641,16 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_RDIAG7",
 	"S_RDIAG8",
 
+	// Blue Diagonal Spring
+	"S_BDIAG1",
+	"S_BDIAG2",
+	"S_BDIAG3",
+	"S_BDIAG4",
+	"S_BDIAG5",
+	"S_BDIAG6",
+	"S_BDIAG7",
+	"S_BDIAG8",
+
 	// Yellow Side Spring
 	"S_YHORIZ1",
 	"S_YHORIZ2",
@@ -5506,7 +5756,6 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_SEED",
 
 	"S_PARTICLE",
-	"S_PARTICLEGEN",
 
 	// Score Logos
 	"S_SCRA", // 100
@@ -5520,6 +5769,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_SCRI", // 4000 (mario)
 	"S_SCRJ", // 8000 (mario)
 	"S_SCRK", // 1UP (mario)
+	"S_SCRL", // 10
 
 	// Drowning Timer Numbers
 	"S_ZERO1",
@@ -5818,9 +6068,6 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_NIGHTSCORE90_2",
 	"S_NIGHTSCORE100_2",
 
-	"S_NIGHTSWING",
-	"S_NIGHTSWING_XMAS",
-
 	// NiGHTS Paraloop Powerups
 	"S_NIGHTSSUPERLOOP",
 	"S_NIGHTSDRILLREFILL",
@@ -5838,14 +6085,11 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_ORBITEM6",
 	"S_ORBITEM7",
 	"S_ORBITEM8",
-	"S_ORBITEM9",
-	"S_ORBITEM10",
-	"S_ORBITEM11",
-	"S_ORBITEM12",
-	"S_ORBITEM13",
-	"S_ORBITEM14",
-	"S_ORBITEM15",
-	"S_ORBITEM16",
+	"S_ORBIDYA1",
+	"S_ORBIDYA2",
+	"S_ORBIDYA3",
+	"S_ORBIDYA4",
+	"S_ORBIDYA5",
 
 	// "Flicky" helper
 	"S_NIGHTOPIANHELPER1",
@@ -5858,6 +6102,141 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_NIGHTOPIANHELPER8",
 	"S_NIGHTOPIANHELPER9",
 
+	// Nightopian
+	"S_PIAN0",
+	"S_PIAN1",
+	"S_PIAN2",
+	"S_PIAN3",
+	"S_PIAN4",
+	"S_PIAN5",
+	"S_PIAN6",
+	"S_PIANSING",
+
+	// Shleep
+	"S_SHLEEP1",
+	"S_SHLEEP2",
+	"S_SHLEEP3",
+	"S_SHLEEP4",
+	"S_SHLEEPBOUNCE1",
+	"S_SHLEEPBOUNCE2",
+	"S_SHLEEPBOUNCE3",
+
+	// Secret badniks and hazards, shhhh
+	"S_PENGUINATOR_LOOK",
+	"S_PENGUINATOR_WADDLE1",
+	"S_PENGUINATOR_WADDLE2",
+	"S_PENGUINATOR_WADDLE3",
+	"S_PENGUINATOR_WADDLE4",
+	"S_PENGUINATOR_SLIDE1",
+	"S_PENGUINATOR_SLIDE2",
+	"S_PENGUINATOR_SLIDE3",
+	"S_PENGUINATOR_SLIDE4",
+	"S_PENGUINATOR_SLIDE5",
+
+	"S_POPHAT_LOOK",
+	"S_POPHAT_SHOOT1",
+	"S_POPHAT_SHOOT2",
+	"S_POPHAT_SHOOT3",
+
+	"S_HIVEELEMENTAL_LOOK",
+	"S_HIVEELEMENTAL_PREPARE1",
+	"S_HIVEELEMENTAL_PREPARE2",
+	"S_HIVEELEMENTAL_SHOOT1",
+	"S_HIVEELEMENTAL_SHOOT2",
+	"S_HIVEELEMENTAL_DORMANT",
+	"S_HIVEELEMENTAL_PAIN",
+	"S_HIVEELEMENTAL_DIE1",
+	"S_HIVEELEMENTAL_DIE2",
+	"S_HIVEELEMENTAL_DIE3",
+
+	"S_BUMBLEBORE_SPAWN",
+	"S_BUMBLEBORE_LOOK1",
+	"S_BUMBLEBORE_LOOK2",
+	"S_BUMBLEBORE_FLY1",
+	"S_BUMBLEBORE_FLY2",
+	"S_BUMBLEBORE_RAISE",
+	"S_BUMBLEBORE_FALL1",
+	"S_BUMBLEBORE_FALL2",
+	"S_BUMBLEBORE_STUCK1",
+	"S_BUMBLEBORE_STUCK2",
+	"S_BUMBLEBORE_DIE",
+
+	"S_BBUZZFLY1",
+	"S_BBUZZFLY2",
+
+	"S_SMASHSPIKE_FLOAT",
+	"S_SMASHSPIKE_EASE1",
+	"S_SMASHSPIKE_EASE2",
+	"S_SMASHSPIKE_FALL",
+	"S_SMASHSPIKE_STOMP1",
+	"S_SMASHSPIKE_STOMP2",
+	"S_SMASHSPIKE_RISE1",
+	"S_SMASHSPIKE_RISE2",
+
+	"S_CACO_LOOK",
+	"S_CACO_WAKE1",
+	"S_CACO_WAKE2",
+	"S_CACO_WAKE3",
+	"S_CACO_WAKE4",
+	"S_CACO_ROAR",
+	"S_CACO_CHASE",
+	"S_CACO_CHASE_REPEAT",
+	"S_CACO_RANDOM",
+	"S_CACO_PREPARE_SOUND",
+	"S_CACO_PREPARE1",
+	"S_CACO_PREPARE2",
+	"S_CACO_PREPARE3",
+	"S_CACO_SHOOT_SOUND",
+	"S_CACO_SHOOT1",
+	"S_CACO_SHOOT2",
+	"S_CACO_CLOSE",
+	"S_CACO_DIE_FLAGS",
+	"S_CACO_DIE_GIB1",
+	"S_CACO_DIE_GIB2",
+	"S_CACO_DIE_SCREAM",
+	"S_CACO_DIE_SHATTER",
+	"S_CACO_DIE_FALL",
+	"S_CACOSHARD_RANDOMIZE",
+	"S_CACOSHARD1_1",
+	"S_CACOSHARD1_2",
+	"S_CACOSHARD2_1",
+	"S_CACOSHARD2_2",
+	"S_CACOFIRE1",
+	"S_CACOFIRE2",
+	"S_CACOFIRE3",
+	"S_CACOFIRE_EXPLODE1",
+	"S_CACOFIRE_EXPLODE2",
+	"S_CACOFIRE_EXPLODE3",
+	"S_CACOFIRE_EXPLODE4",
+
+	"S_SPINBOBERT_MOVE_FLIPUP",
+	"S_SPINBOBERT_MOVE_UP",
+	"S_SPINBOBERT_MOVE_FLIPDOWN",
+	"S_SPINBOBERT_MOVE_DOWN",
+	"S_SPINBOBERT_FIRE_MOVE",
+	"S_SPINBOBERT_FIRE_GHOST",
+	"S_SPINBOBERT_FIRE_TRAIL1",
+	"S_SPINBOBERT_FIRE_TRAIL2",
+	"S_SPINBOBERT_FIRE_TRAIL3",
+
+	"S_HANGSTER_LOOK",
+	"S_HANGSTER_SWOOP1",
+	"S_HANGSTER_SWOOP2",
+	"S_HANGSTER_ARC1",
+	"S_HANGSTER_ARC2",
+	"S_HANGSTER_ARC3",
+	"S_HANGSTER_FLY1",
+	"S_HANGSTER_FLY2",
+	"S_HANGSTER_FLY3",
+	"S_HANGSTER_FLY4",
+	"S_HANGSTER_FLYREPEAT",
+	"S_HANGSTER_ARCUP1",
+	"S_HANGSTER_ARCUP2",
+	"S_HANGSTER_ARCUP3",
+	"S_HANGSTER_RETURN1",
+	"S_HANGSTER_RETURN2",
+	"S_HANGSTER_RETURN3",
+
 	"S_CRUMBLE1",
 	"S_CRUMBLE2",
 
@@ -5897,6 +6276,11 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_WPLD5",
 	"S_WPLD6",
 
+	"S_DUST1",
+	"S_DUST2",
+	"S_DUST3",
+	"S_DUST4",
+
 	"S_ROCKSPAWN",
 
 	"S_ROCKCRUMBLEA",
@@ -5933,32 +6317,35 @@ static const char *const MOBJTYPE_LIST[] = {  // array length left dynamic for s
 	"MT_TAILSOVERLAY", // c:
 
 	// Enemies
-	"MT_BLUECRAWLA",
-	"MT_REDCRAWLA",
-	"MT_GFZFISH", // Greenflower Fish
-	"MT_GOLDBUZZ",
-	"MT_REDBUZZ",
-	"MT_AQUABUZZ",
+	"MT_BLUECRAWLA", // Crawla (Blue)
+	"MT_REDCRAWLA", // Crawla (Red)
+	"MT_GFZFISH", // SDURF
+	"MT_GOLDBUZZ", // Buzz (Gold)
+	"MT_REDBUZZ", // Buzz (Red)
 	"MT_JETTBOMBER", // Jetty-Syn Bomber
 	"MT_JETTGUNNER", // Jetty-Syn Gunner
 	"MT_CRAWLACOMMANDER", // Crawla Commander
 	"MT_DETON", // Deton
 	"MT_SKIM", // Skim mine dropper
-	"MT_TURRET",
-	"MT_POPUPTURRET",
-	"MT_SHARP", // Sharp
+	"MT_TURRET", // Industrial Turret
+	"MT_POPUPTURRET", // Pop-Up Turret
+	"MT_SPINCUSHION", // Spincushion
+	"MT_CRUSHSTACEAN", // Crushstacean
+	"MT_CRUSHCLAW", // Big meaty claw
+	"MT_CRUSHCHAIN", // Chain
 	"MT_JETJAW", // Jet Jaw
 	"MT_SNAILER", // Snailer
-	"MT_VULTURE", // Vulture
+	"MT_VULTURE", // BASH
 	"MT_POINTY", // Pointy
 	"MT_POINTYBALL", // Pointy Ball
 	"MT_ROBOHOOD", // Robo-Hood
-	"MT_FACESTABBER", // CastleBot FaceStabber
+	"MT_FACESTABBER", // Castlebot Facestabber
+	"MT_FACESTABBERSPEAR", // Castlebot Facestabber spear aura
 	"MT_EGGGUARD", // Egg Guard
-	"MT_EGGSHIELD", // Egg Shield for Egg Guard
+	"MT_EGGSHIELD", // Egg Guard's shield
 	"MT_GSNAPPER", // Green Snapper
 	"MT_MINUS", // Minus
-	"MT_SPRINGSHELL", // Spring Shell (no drop)
+	"MT_SPRINGSHELL", // Spring Shell
 	"MT_YELLOWSHELL", // Spring Shell (yellow)
 	"MT_UNIDUS", // Unidus
 	"MT_UNIBALL", // Unidus Ball
@@ -6025,7 +6412,9 @@ static const char *const MOBJTYPE_LIST[] = {  // array length left dynamic for s
 	// Collectible Items
 	"MT_RING",
 	"MT_FLINGRING", // Lost ring
-	"MT_BLUEBALL",  // Blue sphere replacement for special stages
+	"MT_BLUESPHERE",  // Blue sphere for special stages
+	"MT_FLINGBLUESPHERE", // Lost blue sphere
+	"MT_BOMBSPHERE",
 	"MT_REDTEAMRING",  //Rings collectable by red team.
 	"MT_BLUETEAMRING", //Rings collectable by blue team.
 	"MT_TOKEN", // Special Stage Token
@@ -6045,28 +6434,31 @@ static const char *const MOBJTYPE_LIST[] = {  // array length left dynamic for s
 
 	// Springs and others
 	"MT_FAN",
-	"MT_STEAM", // Steam riser
-	"MT_BLUESPRING",
+	"MT_STEAM",
+	"MT_BUMPER",
+	"MT_BALLOON",
+
 	"MT_YELLOWSPRING",
 	"MT_REDSPRING",
-	"MT_YELLOWDIAG", // Yellow Diagonal Spring
-	"MT_REDDIAG", // Red Diagonal Spring
-	"MT_YELLOWHORIZ", // Yellow Side Spring
-	"MT_REDHORIZ", // Red Side Spring
-	"MT_BLUEHORIZ", // Blue Side Spring
+	"MT_BLUESPRING",
+	"MT_YELLOWDIAG",
+	"MT_REDDIAG",
+	"MT_BLUEDIAG",
+	"MT_YELLOWHORIZ",
+	"MT_REDHORIZ",
+	"MT_BLUEHORIZ",
 
 	// Interactive Objects
 	"MT_BUBBLES", // Bubble source
 	"MT_SIGN", // Level end sign
 	"MT_SPIKEBALL", // Spike Ball
-	"MT_SPECIALSPIKEBALL",
 	"MT_SPINFIRE",
 	"MT_SPIKE",
 	"MT_WALLSPIKE",
 	"MT_WALLSPIKEBASE",
 	"MT_STARPOST",
 	"MT_BIGMINE",
-	"MT_BIGAIRMINE",
+	"MT_BLASTEXECUTOR",
 	"MT_CANNONLAUNCHER",
 
 	// Monitor miscellany
@@ -6152,8 +6544,11 @@ static const char *const MOBJTYPE_LIST[] = {  // array length left dynamic for s
 	"MT_GFZFLOWER1",
 	"MT_GFZFLOWER2",
 	"MT_GFZFLOWER3",
+
+	"MT_BLUEBERRYBUSH",
 	"MT_BERRYBUSH",
 	"MT_BUSH",
+
 	// Trees (both GFZ and misc)
 	"MT_GFZTREE",
 	"MT_GFZBERRYTREE",
@@ -6165,10 +6560,14 @@ static const char *const MOBJTYPE_LIST[] = {  // array length left dynamic for s
 	"MT_POLYGONTREE",
 	"MT_BUSHTREE",
 	"MT_BUSHREDTREE",
+	"MT_SPRINGTREE",
 
 	// Techno Hill Scenery
 	"MT_THZFLOWER1",
 	"MT_THZFLOWER2",
+	"MT_THZFLOWER3",
+	"MT_THZTREE", // Steam whistle tree/bush
+	"MT_THZTREEBRANCH", // branch of said tree
 	"MT_ALARM",
 
 	// Deep Sea Scenery
@@ -6181,6 +6580,10 @@ static const char *const MOBJTYPE_LIST[] = {  // array length left dynamic for s
 	"MT_CORAL2", // Coral 2
 	"MT_CORAL3", // Coral 3
 	"MT_BLUECRYSTAL", // Blue Crystal
+	"MT_KELP", // Kelp
+	"MT_DSZSTALAGMITE", // Deep Sea 1 Stalagmite
+	"MT_DSZ2STALAGMITE", // Deep Sea 2 Stalagmite
+	"MT_LIGHTBEAM", // DSZ Light beam
 
 	// Castle Eggman Scenery
 	"MT_CHAIN", // CEZ Chain
@@ -6198,11 +6601,28 @@ static const char *const MOBJTYPE_LIST[] = {  // array length left dynamic for s
 	"MT_BIGMACECHAIN", // Big Mace Chain
 	"MT_SMALLMACE", // Small Mace
 	"MT_BIGMACE", // Big Mace
+	"MT_SMALLGRABCHAIN", // Small Grab Chain
+	"MT_BIGGRABCHAIN", // Big Grab Chain
 	"MT_YELLOWSPRINGBALL", // Yellow spring on a ball
 	"MT_REDSPRINGBALL", // Red spring on a ball
 	"MT_SMALLFIREBAR", // Small Firebar
 	"MT_BIGFIREBAR", // Big Firebar
-	"MT_CEZFLOWER",
+	"MT_CEZFLOWER", // Flower
+	"MT_CEZPOLE", // Pole
+	"MT_CEZBANNER", // Banner
+	"MT_PINETREE", // Pine Tree
+	"MT_CEZBUSH1", // Bush 1
+	"MT_CEZBUSH2", // Bush 2
+	"MT_CANDLE", // Candle
+	"MT_CANDLEPRICKET", // Candle pricket
+	"MT_FLAMEHOLDER", // Flame holder
+	"MT_FIRETORCH", // Fire torch
+	"MT_WAVINGFLAG", // Waving flag
+	"MT_WAVINGFLAGSEG", // Waving flag segment
+	"MT_CRAWLASTATUE", // Crawla statue
+	"MT_FACESTABBERSTATUE", // Facestabber statue
+	"MT_SUSPICIOUSFACESTABBERSTATUE", // :eggthinking:
+	"MT_BRAMBLES", // Brambles
 
 	// Arid Canyon Scenery
 	"MT_BIGTUMBLEWEED",
@@ -6254,8 +6674,28 @@ static const char *const MOBJTYPE_LIST[] = {  // array length left dynamic for s
 	"MT_LAMPPOST2",  // with snow
 	"MT_HANGSTAR",
 	// Xmas GFZ bushes
+	"MT_XMASBLUEBERRYBUSH",
 	"MT_XMASBERRYBUSH",
 	"MT_XMASBUSH",
+	// FHZ
+	"MT_FHZICE1",
+	"MT_FHZICE2",
+
+	// Halloween Scenery
+	// Pumpkins
+	"MT_JACKO1",
+	"MT_JACKO2",
+	"MT_JACKO3",
+	// Dr Seuss Trees
+	"MT_HHZTREE_TOP",
+	"MT_HHZTREE_PART",
+	// Misc
+	"MT_HHZSHROOM",
+	"MT_HHZGRASS",
+	"MT_HHZTENTACLE1",
+	"MT_HHZTENTACLE2",
+	"MT_HHZSTALAGMITE_TALL",
+	"MT_HHZSTALAGMITE_SHORT",
 
 	// Botanic Serenity
 	"MT_BSZTALLFLOWER_RED",
@@ -6327,21 +6767,42 @@ static const char *const MOBJTYPE_LIST[] = {  // array length left dynamic for s
 
 	// Flickies
 	"MT_FLICKY_01", // Bluebird
+	"MT_FLICKY_01_CENTER",
 	"MT_FLICKY_02", // Rabbit
+	"MT_FLICKY_02_CENTER",
 	"MT_FLICKY_03", // Chicken
+	"MT_FLICKY_03_CENTER",
 	"MT_FLICKY_04", // Seal
+	"MT_FLICKY_04_CENTER",
 	"MT_FLICKY_05", // Pig
+	"MT_FLICKY_05_CENTER",
 	"MT_FLICKY_06", // Chipmunk
+	"MT_FLICKY_06_CENTER",
 	"MT_FLICKY_07", // Penguin
+	"MT_FLICKY_07_CENTER",
 	"MT_FLICKY_08", // Fish
+	"MT_FLICKY_08_CENTER",
 	"MT_FLICKY_09", // Ram
+	"MT_FLICKY_09_CENTER",
 	"MT_FLICKY_10", // Puffin
+	"MT_FLICKY_10_CENTER",
 	"MT_FLICKY_11", // Cow
+	"MT_FLICKY_11_CENTER",
 	"MT_FLICKY_12", // Rat
+	"MT_FLICKY_12_CENTER",
 	"MT_FLICKY_13", // Bear
+	"MT_FLICKY_13_CENTER",
 	"MT_FLICKY_14", // Dove
+	"MT_FLICKY_14_CENTER",
 	"MT_FLICKY_15", // Cat
+	"MT_FLICKY_15_CENTER",
 	"MT_FLICKY_16", // Canary
+	"MT_FLICKY_16_CENTER",
+	"MT_SECRETFLICKY_01", // Spider
+	"MT_SECRETFLICKY_01_CENTER",
+	"MT_SECRETFLICKY_02", // Bat
+	"MT_SECRETFLICKY_02_CENTER",
+	"MT_SEED",
 
 	// Environmental Effects
 	"MT_RAIN", // Rain
@@ -6354,7 +6815,6 @@ static const char *const MOBJTYPE_LIST[] = {  // array length left dynamic for s
 	"MT_WATERZAP",
 	"MT_SPINDUST", // Spindash dust
 	"MT_TFOG",
-	"MT_SEED",
 	"MT_PARTICLE",
 	"MT_PARTICLEGEN", // For fans, etc.
 
@@ -6377,6 +6837,7 @@ static const char *const MOBJTYPE_LIST[] = {  // array length left dynamic for s
 	"MT_AWATERH", // Ambient Water Sound 8
 	"MT_RANDOMAMBIENT",
 	"MT_RANDOMAMBIENT2",
+	"MT_MACHINEAMBIENCE",
 
 	"MT_CORK",
 
@@ -6435,7 +6896,9 @@ static const char *const MOBJTYPE_LIST[] = {  // array length left dynamic for s
 	"MT_HOOPCOLLIDE", // Collision detection for NiGHTS hoops
 	"MT_HOOPCENTER", // Center of a hoop
 	"MT_NIGHTSCORE",
-	"MT_NIGHTSWING",
+	"MT_NIGHTSCHIP", // NiGHTS Chip
+	"MT_FLINGNIGHTSCHIP", // Lost NiGHTS Chip
+	"MT_NIGHTSSTAR", // NiGHTS Star
 	"MT_NIGHTSSUPERLOOP",
 	"MT_NIGHTSDRILLREFILL",
 	"MT_NIGHTSHELPER",
@@ -6443,6 +6906,27 @@ static const char *const MOBJTYPE_LIST[] = {  // array length left dynamic for s
 	"MT_NIGHTSLINKFREEZE",
 	"MT_EGGCAPSULE",
 	"MT_NIGHTOPIANHELPER", // the actual helper object that orbits you
+	"MT_PIAN", // decorative singing friend
+	"MT_SHLEEP", // almost-decorative sleeping enemy
+
+	// Secret badniks and hazards, shhhh
+	"MT_PENGUINATOR",
+	"MT_POPHAT",
+	"MT_POPSHOT",
+
+	"MT_HIVEELEMENTAL",
+	"MT_BUMBLEBORE",
+
+	"MT_BUBBLEBUZZ",
+
+	"MT_SMASHINGSPIKEBALL",
+	"MT_CACOLANTERN",
+	"MT_CACOSHARD",
+	"MT_CACOFIRE",
+	"MT_SPINBOBERT",
+	"MT_SPINBOBERT_FIRE1",
+	"MT_SPINBOBERT_FIRE2",
+	"MT_HANGSTER",
 
 	// Utility Objects
 	"MT_TELEPORTMAN",
@@ -6464,6 +6948,7 @@ static const char *const MOBJTYPE_LIST[] = {  // array length left dynamic for s
 	"MT_SPARK", //spark
 	"MT_EXPLODE", // Robot Explosion
 	"MT_UWEXPLODE", // Underwater Explosion
+	"MT_DUST",
 	"MT_ROCKSPAWNER",
 	"MT_FALLINGROCK",
 	"MT_ROCKCRUMBLE1",
@@ -6535,8 +7020,8 @@ static const char *const MOBJFLAG2_LIST[] = {
 	"SCATTER",		  // Thrown ring has scatter properties
 	"BEYONDTHEGRAVE", // Source of this missile has died and has since respawned.
 	"SLIDEPUSH",	  // MF_PUSHABLE that pushes continuously.
-	"CLASSICPUSH",	  // Drops straight down when object has negative Z.
-	"STANDONME",	  // While not pushable, stand on me anyway.
+	"CLASSICPUSH",    // Drops straight down when object has negative momz.
+	"INVERTAIMABLE",  // Flips whether it's targetable by A_LookForEnemies (enemies no, decoys yes)
 	"INFLOAT",		  // Floating to a height for a move, don't auto float to target's height.
 	"DEBRIS",		  // Splash ring from explosion ring
 	"NIGHTSPULL",	  // Attracted from a paraloop
@@ -6892,6 +7377,8 @@ struct {
 	{"CODEBASE",CODEBASE}, // or what release of SRB2 this is.
 	{"VERSION",VERSION}, // Grab the game's version!
 	{"SUBVERSION",SUBVERSION}, // more precise version number
+	{"NEWTICRATE",NEWTICRATE}, // TICRATE*NEWTICRATERATIO
+	{"NEWTICRATERATIO",NEWTICRATERATIO},
 
 	// Special linedef executor tag numbers!
 	{"LE_PINCHPHASE",LE_PINCHPHASE}, // A boss entered pinch phase (and, in most cases, is preparing their pinch phase attack!)
@@ -7143,6 +7630,7 @@ struct {
 	{"DMG_CRUSHED",DMG_CRUSHED},
 	{"DMG_SPECTATOR",DMG_SPECTATOR},
 	//// Masks
+	{"DMG_CANHURTSELF",DMG_CANHURTSELF},
 	{"DMG_DEATHMASK",DMG_DEATHMASK},
 
 	// Gametypes, for use with global var "gametype"
@@ -7185,6 +7673,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},
@@ -8378,17 +8869,17 @@ static int lib_getActionName(lua_State *L)
 	{
 		lua_settop(L, 1); // set top of stack to 1 (removing any extra args, which there shouldn't be)
 		// get the name for this action, if possible.
-		lua_getfield(gL, LUA_REGISTRYINDEX, LREG_ACTIONS);
-		lua_pushnil(gL);
+		lua_getfield(L, LUA_REGISTRYINDEX, LREG_ACTIONS);
+		lua_pushnil(L);
 		// Lua stack at this point:
 		//  1   ...       -2              -1
 		// arg  ...   LREG_ACTIONS        nil
-		while (lua_next(gL, -2))
+		while (lua_next(L, -2))
 		{
 			// Lua stack at this point:
 			//  1   ...       -3              -2           -1
 			// arg  ...   LREG_ACTIONS    "A_ACTION"    function
-			if (lua_rawequal(gL, -1, 1)) // is this the same as the arg?
+			if (lua_rawequal(L, -1, 1)) // is this the same as the arg?
 			{
 				// make sure the key (i.e. "A_ACTION") is a string first
 				// (note: we don't use lua_isstring because it also returns true for numbers)
@@ -8397,12 +8888,12 @@ static int lib_getActionName(lua_State *L)
 					lua_pushvalue(L, -2); // push "A_ACTION" string to top of stack
 					return 1;
 				}
-				lua_pop(gL, 2); // pop the name and function
+				lua_pop(L, 2); // pop the name and function
 				break; // probably should have succeeded but we didn't, so end the loop
 			}
-			lua_pop(gL, 1);
+			lua_pop(L, 1);
 		}
-		lua_pop(gL, 1); // pop LREG_ACTIONS
+		lua_pop(L, 1); // pop LREG_ACTIONS
 		return 0; // return nothing (don't error)
 	}
 
diff --git a/src/djgppdos/i_cdmus.c b/src/djgppdos/i_cdmus.c
index f707add5ef097681f9d2e211b549f4588289501a..2a629ca1738a1ad7e5d36ae6c83a16fdcd2c22e8 100644
--- a/src/djgppdos/i_cdmus.c
+++ b/src/djgppdos/i_cdmus.c
@@ -50,7 +50,7 @@ static boolean wasPlaying;
 static int     cdVolume=0;          // current cd volume (0-31)
 
 // 0-31 like Music & Sfx, though CD hardware volume is 0-255.
-consvar_t cd_volume = {"cd_volume","31",CV_SAVE,soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cd_volume = {"cd_volume","18",CV_SAVE,soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
 
 // allow Update for next/loop track
 // some crap cd drivers take up to
diff --git a/src/djgppdos/i_sound.c b/src/djgppdos/i_sound.c
index 88fc807f408cb4f285086fdabd854658b83063ad..52c90aac2d2450ede35eb9773e8b9a222e661783 100644
--- a/src/djgppdos/i_sound.c
+++ b/src/djgppdos/i_sound.c
@@ -134,21 +134,12 @@ FUNCINLINE static ATTRINLINE int Volset(int vol)
 
 void I_SetSfxVolume(INT32 volume)
 {
-	if (nosound)
+	if (sound_disabled)
 		return;
 
 	set_volume (Volset(volume),-1);
 }
 
-void I_SetMIDIMusicVolume(INT32 volume)
-{
-	if (nomidimusic)
-		return;
-
-	// Now set volume on output device.
-	set_volume (-1, Volset(volume));
-}
-
 //
 // Starting a sound means adding it
 //  to the current list of active sounds
@@ -165,11 +156,13 @@ INT32 I_StartSound ( sfxenum_t     id,
                    INT32         vol,
                    INT32         sep,
                    INT32         pitch,
-                   INT32         priority )
+                   INT32         priority,
+				   INT32         channel)
 {
 	int voice;
+	(void)channel;
 
-	if (nosound)
+	if (sound_disabled)
 	return 0;
 
 	// UNUSED
@@ -190,7 +183,7 @@ void I_StopSound (INT32 handle)
 	//  an setting the channel to zero.
 	int voice=handle & (VIRTUAL_VOICES-1);
 
-	if (nosound)
+	if (sound_disabled)
 		return;
 
 	if (voice_check(voice)==S_sfx[handle>>VOICESSHIFT].data)
@@ -199,7 +192,7 @@ void I_StopSound (INT32 handle)
 
 INT32 I_SoundIsPlaying(INT32 handle)
 {
-	if (nosound)
+	if (sound_disabled)
 		return FALSE;
 
 	if (voice_check(handle & (VIRTUAL_VOICES-1))==S_sfx[handle>>VOICESSHIFT].data)
@@ -229,7 +222,7 @@ void I_UpdateSoundParams( INT32 handle,
 	int voice=handle & (VIRTUAL_VOICES-1);
 	int numsfx=handle>>VOICESSHIFT;
 
-	if (nosound)
+	if (sound_disabled)
 		return;
 
 	if (voice_check(voice)==S_sfx[numsfx].data)
@@ -270,17 +263,17 @@ void I_StartupSound(void)
 	char   err[255];
 #endif
 
-	if (nosound)
+	if (sound_disabled)
 		sfxcard=DIGI_NONE;
 	else
 		sfxcard=DIGI_AUTODETECT;
 
-	if (nomidimusic)
+	if (midi_disabled)
 		midicard=MIDI_NONE;
 	else
 		midicard=MIDI_AUTODETECT; //DetectMusicCard();
 
-	nodigimusic=true; //Alam: No OGG/MP3/IT/MOD support
+	digital_disabled=true; //Alam: No OGG/MP3/IT/MOD support
 
 	// Secure and configure sound device first.
 	CONS_Printf("I_StartupSound: ");
@@ -293,8 +286,8 @@ void I_StartupSound(void)
 	{
 		sprintf (err,"Sound init error : %s\n",allegro_error);
 		CONS_Error (err);
-		nosound=true;
-		nomidimusic=true;
+		sound_disabled=true;
+		midi_disabled=true;
 	}
 	else
 	{
@@ -321,7 +314,11 @@ static MIDI* currsong;   //im assuming only 1 song will be played at once
 static int      islooping=0;
 static int      musicdies=-1;
 UINT8           music_started=0;
+boolean         songpaused=false;
 
+/// ------------------------
+//  MUSIC SYSTEM
+/// ------------------------
 
 /* load_midi_mem:
  *  Loads a standard MIDI file from memory, returning a pointer to
@@ -389,163 +386,162 @@ static MIDI *load_midi_mem(char *mempointer,int *e)
 	return midi;
 }
 
-void I_InitMIDIMusic(void)
+void I_InitMusic(void)
 {
-	if (nomidimusic)
+	if (midi_disabled)
 		return;
 
 	I_AddExitFunc(I_ShutdownMusic);
 	music_started = true;
+	songpaused = false;
 }
 
-void I_ShutdownMIDIMusic(void)
+void I_ShutdownMusic(void)
 {
 	if ( !music_started )
 		return;
 
-	I_StopSong(1);
+	I_StopSong();
 
 	music_started=false;
 }
 
-void I_InitDigMusic(void)
+/// ------------------------
+//  MUSIC PROPERTIES
+/// ------------------------
+
+musictype_t I_SongType(void)
 {
-//	CONS_Printf("Digital music not yet supported under DOS.\n");
+	if (currsong)
+		return MU_MID;
+	else
+		return MU_NONE;
 }
 
-void I_ShutdownDigMusic(void)
+boolean I_SongPlaying()
 {
-//	CONS_Printf("Digital music not yet supported under DOS.\n");
+	return (boolean)currsong;
 }
 
-void I_InitMusic(void)
+boolean I_SongPaused()
 {
-	if (!nodigimusic)
-		I_InitDigMusic();
-	if (!nomidimusic)
-		I_InitMIDIMusic();
+	return songpaused;
 }
 
-void I_ShutdownMusic(void)
+/// ------------------------
+//  MUSIC EFFECTS
+/// ------------------------
+
+boolean I_SetSongSpeed(float speed)
 {
-	I_ShutdownMIDIMusic();
-	I_ShutdownDigMusic();
+	(void)speed;
+	return false;
 }
 
-boolean I_PlaySong(INT32 handle, INT32 looping)
+/// ------------------------
+//  MUSIC PLAYBACK
+/// ------------------------
+
+boolean I_LoadSong(char *data, size_t len)
 {
-	handle = 0;
-	if (nomidimusic)
-		return false;
+	int e = len; //Alam: For error
+	if (midi_disabled)
+		return 0;
 
-	islooping = looping;
-	musicdies = gametic + NEWTICRATE*30;
-	if (play_midi(currsong,looping)==0)
-		return true;
-	return false;
+	if (memcmp(data,"MThd",4)==0) // support mid file in WAD !!!
+	{
+		currsong=load_midi_mem(data,&e);
+	}
+	else
+	{
+		CONS_Printf("Music Lump is not a MIDI lump\n");
+		return 0;
+	}
+
+	if (currsong==NULL)
+	{
+		CONS_Printf("Not a valid mid file : %d\n",e);
+		return 0;
+	}
+
+	return 1;
 }
 
-void I_PauseSong (INT32 handle)
+void I_UnloadSong(void)
 {
 	handle = 0;
-	if (nomidimusic)
+	if (midi_disabled)
 		return;
 
-	midi_pause();
+	//destroy_midi(currsong);
 }
 
-void I_ResumeSong (INT32 handle)
+boolean I_PlaySong(boolean looping)
 {
 	handle = 0;
-	if (nomidimusic)
-		return;
+	if (midi_disabled)
+		return false;
 
-	midi_resume();
+	islooping = looping;
+	musicdies = gametic + NEWTICRATE*30;
+	if (play_midi(currsong,looping)==0)
+		return true;
+	return false;
 }
 
-void I_StopSong(INT32 handle)
+void I_StopSong(void)
 {
 	handle = 0;
-	if (nomidimusic)
+	if (midi_disabled)
 		return;
 
 	islooping = 0;
 	musicdies = 0;
 	stop_midi();
+	songpaused = false;
 }
 
-// Is the song playing?
-#if 0
-int I_QrySongPlaying(int handle)
+void I_PauseSong (INT32 handle)
 {
-	if (nomidimusic)
-		return 0;
-
-	//return islooping || musicdies > gametic;
-	return (midi_pos==-1);
+	handle = 0;
+	if (midi_disabled)
+		return;
+	midi_pause();
+	songpaused = true;
 }
-#endif
 
-void I_UnRegisterSong(INT32 handle)
+void I_ResumeSong (INT32 handle)
 {
 	handle = 0;
-	if (nomidimusic)
+	if (midi_disabled)
 		return;
-
-	//destroy_midi(currsong);
+	midi_resume();
+	songpaused = false;
 }
 
-INT32 I_RegisterSong(void *data, size_t len)
+void I_SetMusicVolume(INT32 volume)
 {
-	int e = len; //Alam: For error
-	if (nomidimusic)
-		return 0;
-
-	if (memcmp(data,"MThd",4)==0) // support mid file in WAD !!!
-	{
-		currsong=load_midi_mem(data,&e);
-	}
-	else
-	{
-		CONS_Printf("Music Lump is not a MIDI lump\n");
-		return 0;
-	}
-
-	if (currsong==NULL)
-	{
-		CONS_Printf("Not a valid mid file : %d\n",e);
-		return 0;
-	}
+	if (midi_disabled)
+		return;
 
-	return 1;
+	// Now set volume on output device.
+	set_volume (-1, Volset(volume));
 }
 
-/// \todo Add OGG/MP3 support for dos
-boolean I_StartDigSong(const char *musicname, INT32 looping)
+boolean I_SetSongTrack(INT32 track)
 {
-	musicname = NULL;
-	looping = 0;
-	//CONS_Printf("I_StartDigSong: Not yet supported under DOS.\n");
+	(void)track;
 	return false;
 }
 
-void I_StopDigSong(void)
-{
-//	CONS_Printf("I_StopDigSong: Not yet supported under DOS.\n");
-}
-
-void I_SetDigMusicVolume(INT32 volume)
+// Is the song playing?
+#if 0
+int I_QrySongPlaying(int handle)
 {
-	volume = 0;
-	if (nodigimusic)
-		return;
-
-	// Now set volume on output device.
-//	CONS_Printf("Digital music not yet supported under DOS.\n");
-}
+	if (midi_disabled)
+		return 0;
 
-boolean I_SetSongSpeed(float speed)
-{
-	(void)speed;
-	return false;
+	//return islooping || musicdies > gametic;
+	return (midi_pos==-1);
 }
+#endif
diff --git a/src/doomdata.h b/src/doomdata.h
index c0586fd65620f44d8b0e0f49e56d629d210772ae..5ee39c5a8b400c7832936be505d1ecd9dc9047c5 100644
--- a/src/doomdata.h
+++ b/src/doomdata.h
@@ -46,6 +46,9 @@ enum
 	ML_BLOCKMAP,  // LUT, motion clipping, walls/grid element
 };
 
+// Extra flag for objects.
+#define MTF_EXTRA 1
+
 // Reverse gravity flag for objects.
 #define MTF_OBJECTFLIP 2
 
diff --git a/src/doomdef.h b/src/doomdef.h
index c5cc1e87c164b55255f16d3c0c702acd48bb9413..9097b7ee8f52493acf39d5a8a4af1a2ab05c5470 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -569,6 +569,6 @@ extern const char *compdate, *comptime, *comprevision, *compbranch;
 
 /// Handle touching sector specials in P_PlayerAfterThink instead of P_PlayerThink.
 /// \note   Required for proper collision with moving sloped surfaces that have sector specials on them.
-//#define SECTORSPECIALSAFTERTHINK
+#define SECTORSPECIALSAFTERTHINK
 
 #endif // __DOOMDEF__
diff --git a/src/doomstat.h b/src/doomstat.h
index d4735f6b218ecf64f2b4db2caec8b1e937d0fc86..7678c86b708d13e8a291b9f453fb3d0a16f4c76a 100644
--- a/src/doomstat.h
+++ b/src/doomstat.h
@@ -90,10 +90,7 @@ extern boolean fromlevelselect;
 // Internal parameters for sound rendering.
 // ========================================
 
-extern boolean nomidimusic; // defined in d_main.c
-extern boolean nosound;
-extern boolean nodigimusic;
-extern boolean music_disabled;
+extern boolean midi_disabled;
 extern boolean sound_disabled;
 extern boolean digital_disabled;
 
@@ -126,15 +123,13 @@ extern INT32 secondarydisplayplayer; // for splitscreen
 
 // Maps of special importance
 extern INT16 spstage_start;
-extern INT16 sstage_start;
-extern INT16 sstage_end;
+extern INT16 sstage_start, sstage_end, smpstage_start, smpstage_end;
 
 extern INT16 titlemap;
 extern boolean hidetitlepics;
 extern INT16 bootmap; //bootmap for loading a map on startup
 
 extern boolean looptitle;
-extern boolean useNightsSS;
 
 // CTF colors.
 extern UINT8 skincolor_redteam, skincolor_blueteam, skincolor_redring, skincolor_bluering;
@@ -175,7 +170,7 @@ extern cutscene_t *cutscenes[128];
 extern INT16 nextmapoverride;
 extern boolean skipstats;
 
-extern UINT32 totalrings; //  Total # of rings in a level
+extern UINT32 ssspheres; //  Total # of spheres in a level
 
 // Fun extra stuff
 extern INT16 lastmap; // Last level you were at (returning from special stages).
@@ -246,6 +241,7 @@ typedef struct
 	SINT8 unlockrequired; ///< Is an unlockable required to play this level? -1 if no.
 	UINT8 levelselect;    ///< Is this map available in the level select? If so, which map list is it available in?
 	SINT8 bonustype;      ///< What type of bonus does this level have? (-1 for null.)
+	SINT8 maxbonuslives;  ///< How many bonus lives to award at Intermission? (-1 for unlimited.)
 
 	UINT8 levelflags;     ///< LF_flags:  merged eight booleans into one UINT8 for space, see below
 	UINT8 menuflags;      ///< LF2_flags: options that affect record attack / nights mode menus
@@ -415,6 +411,7 @@ extern UINT16 tailsflytics;
 extern UINT16 underwatertics;
 extern UINT16 spacetimetics;
 extern UINT16 extralifetics;
+extern UINT16 nightslinktics;
 
 extern UINT8 introtoplay;
 extern UINT8 creditscutscene;
diff --git a/src/dummy/i_sound.c b/src/dummy/i_sound.c
index 51dbb610d13657ab567bfd8314ea53dc6ba40350..7275bb1ae5d9345e035aa775b602b5fb3bf4f8a1 100644
--- a/src/dummy/i_sound.c
+++ b/src/dummy/i_sound.c
@@ -23,13 +23,14 @@ void I_UpdateSound(void){};
 //  SFX I/O
 //
 
-INT32 I_StartSound(sfxenum_t id, UINT8 vol, UINT8 sep, UINT8 pitch, UINT8 priority)
+INT32 I_StartSound(sfxenum_t id, UINT8 vol, UINT8 sep, UINT8 pitch, UINT8 priority, INT32 channel)
 {
 	(void)id;
 	(void)vol;
 	(void)sep;
 	(void)pitch;
 	(void)priority;
+	(void)channel;
 	return -1;
 }
 
@@ -57,91 +58,88 @@ void I_SetSfxVolume(UINT8 volume)
 	(void)volume;
 }
 
-//
-//  MUSIC I/O
-//
+/// ------------------------
+//  MUSIC SYSTEM
+/// ------------------------
 
 void I_InitMusic(void){}
 
 void I_ShutdownMusic(void){}
 
-void I_PauseSong(INT32 handle)
+/// ------------------------
+//  MUSIC PROPERTIES
+/// ------------------------
+
+musictype_t I_SongType(void)
 {
-	(void)handle;
+	return MU_NONE;
 }
 
-void I_ResumeSong(INT32 handle)
+boolean I_SongPlaying(void)
 {
-	(void)handle;
+	return false;
 }
 
-//
-//  MIDI I/O
-//
-
-void I_InitMIDIMusic(void){}
+boolean I_SongPaused(void)
+{
+	return false;
+}
 
-void I_ShutdownMIDIMusic(void){}
+/// ------------------------
+//  MUSIC EFFECTS
+/// ------------------------
 
-void I_SetMIDIMusicVolume(UINT8 volume)
+boolean I_SetSongSpeed(float speed)
 {
-	(void)volume;
+	(void)speed;
+	return false;
 }
 
-INT32 I_RegisterSong(void *data, size_t len)
+/// ------------------------
+//  MUSIC PLAYBACK
+/// ------------------------
+
+boolean I_LoadSong(char *data, size_t len)
 {
 	(void)data;
 	(void)len;
 	return -1;
 }
 
-boolean I_PlaySong(INT32 handle, boolean looping)
+void I_UnloadSong(void)
 {
 	(void)handle;
-	(void)looping;
-	return false;
 }
 
-void I_StopSong(INT32 handle)
+boolean I_PlaySong(boolean looping)
 {
 	(void)handle;
+	(void)looping;
+	return false;
 }
 
-void I_UnRegisterSong(INT32 handle)
+void I_StopSong(void)
 {
 	(void)handle;
 }
 
-//
-//  DIGMUSIC I/O
-//
-
-void I_InitDigMusic(void){}
-
-void I_ShutdownDigMusic(void){}
-
-boolean I_StartDigSong(const char *musicname, boolean looping)
+void I_PauseSong(void)
 {
-	(void)musicname;
-	(void)looping;
-	return false;
+	(void)handle;
 }
 
-void I_StopDigSong(void){}
-
-void I_SetDigMusicVolume(UINT8 volume)
+void I_ResumeSong(void)
 {
-	(void)volume;
+	(void)handle;
 }
 
-boolean I_SetSongSpeed(float speed)
+void I_SetMusicVolume(UINT8 volume)
 {
-	(void)speed;
-	return false;
+	(void)volume;
 }
 
 boolean I_SetSongTrack(int track)
 {
 	(void)track;
 	return false;
-}
+}
\ No newline at end of file
diff --git a/src/f_finale.c b/src/f_finale.c
index db62ddf0924d2fcd389f356a3290b6aa667a51c3..151889c4cad52d9579f3393d9263f1cb6b10930e 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -987,7 +987,6 @@ static const char *credits[] = {
 	"\1Programming",
 	"Alam \"GBC\" Arias",
 	"Logan \"GBA\" Arias",
-	"Colette \"fickle\" Bordelon",
 	"Callum Dickinson",
 	"Scott \"Graue\" Feeney",
 	"Nathan \"Jazz\" Giroux",
@@ -1345,7 +1344,7 @@ void F_GameEvaluationDrawer(void)
 				++timesBeatenUltimate;
 
 			if (M_UpdateUnlockablesAndExtraEmblems())
-				S_StartSound(NULL, sfx_ncitem);
+				S_StartSound(NULL, sfx_s3k68);
 
 			G_SaveGameData();
 		}
diff --git a/src/g_game.c b/src/g_game.c
index 5a0770574d728bf9f76f7a551486ac8c65a1e542..ceeb42eb74fa962a4a650b7bfec52ba75a658499 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -116,20 +116,18 @@ INT32 secondarydisplayplayer; // for splitscreen
 
 tic_t gametic;
 tic_t levelstarttic; // gametic at level start
-UINT32 totalrings; // for intermission
+UINT32 ssspheres; // old special stage
 INT16 lastmap; // last level you were at (returning from special stages)
 tic_t timeinmap; // Ticker for time spent in level (used for levelcard display)
 
 INT16 spstage_start;
-INT16 sstage_start;
-INT16 sstage_end;
+INT16 sstage_start, sstage_end, smpstage_start, smpstage_end;
 
 INT16 titlemap = 0;
 boolean hidetitlepics = false;
 INT16 bootmap; //bootmap for loading a map on startup
 
 boolean looptitle = false;
-boolean useNightsSS = false;
 
 UINT8 skincolor_redteam = SKINCOLOR_RED;
 UINT8 skincolor_blueteam = SKINCOLOR_BLUE;
@@ -203,6 +201,7 @@ UINT16 tailsflytics = 8*TICRATE;
 UINT16 underwatertics = 30*TICRATE;
 UINT16 spacetimetics = 11*TICRATE + (TICRATE/2);
 UINT16 extralifetics = 4*TICRATE;
+UINT16 nightslinktics = 2*TICRATE;
 
 INT32 gameovertics = 15*TICRATE;
 
@@ -2201,7 +2200,7 @@ void G_PlayerReborn(INT32 player)
 	p->pflags |= PF_JUMPDOWN;
 
 	p->playerstate = PST_LIVE;
-	p->rings = 0; // 0 rings
+	p->rings = p->spheres = 0; // 0 rings
 	p->panim = PA_IDLE; // standing animation
 
 	//if ((netgame || multiplayer) && !p->spectator) -- moved into P_SpawnPlayer to account for forced changes there
@@ -2232,6 +2231,8 @@ void G_PlayerReborn(INT32 player)
 	if (p->mare == 255)
 		p->mare = 0;
 
+	p->marelap = p->marebonuslap = 0;
+
 	// Check to make sure their color didn't change somehow...
 	if (G_GametypeHasTeams())
 	{
@@ -2796,7 +2797,11 @@ INT32 G_GetGametypeByName(const char *gametypestr)
 //
 boolean G_IsSpecialStage(INT32 mapnum)
 {
-	if (gametype == GT_COOP && modeattacking != ATTACKING_RECORD && mapnum >= sstage_start && mapnum <= sstage_end)
+	if (gametype != GT_COOP || modeattacking == ATTACKING_RECORD)
+		return false;
+	if (mapnum >= sstage_start && mapnum <= sstage_end)
+		return true;
+	if (mapnum >= smpstage_start && mapnum <= smpstage_end)
 		return true;
 
 	return false;
@@ -3021,21 +3026,14 @@ static void G_DoCompleted(void)
 	{
 		token--;
 
-		if (!(emeralds & EMERALD1))
-			nextmap = (INT16)(sstage_start - 1); // Special Stage 1
-		else if (!(emeralds & EMERALD2))
-			nextmap = (INT16)(sstage_start); // Special Stage 2
-		else if (!(emeralds & EMERALD3))
-			nextmap = (INT16)(sstage_start + 1); // Special Stage 3
-		else if (!(emeralds & EMERALD4))
-			nextmap = (INT16)(sstage_start + 2); // Special Stage 4
-		else if (!(emeralds & EMERALD5))
-			nextmap = (INT16)(sstage_start + 3); // Special Stage 5
-		else if (!(emeralds & EMERALD6))
-			nextmap = (INT16)(sstage_start + 4); // Special Stage 6
-		else if (!(emeralds & EMERALD7))
-			nextmap = (INT16)(sstage_start + 5); // Special Stage 7
-		else
+		for (i = 0; i < 7; i++)
+			if (!(emeralds & (1<<i)))
+			{
+				nextmap = ((netgame || multiplayer) ? smpstage_start : sstage_start) + i - 1; // to special stage!
+				break;
+			}
+
+		if (i == 7)
 			gottoken = false;
 	}
 
@@ -3207,9 +3205,9 @@ void G_LoadGameSettings(void)
 {
 	// defaults
 	spstage_start = 1;
-	sstage_start = 50;
-	sstage_end = 57; // 8 special stages in vanilla SRB2
-	useNightsSS = false; //true;
+	sstage_start = smpstage_start = 50;
+	sstage_end = smpstage_end = 56; // 7 special stages in vanilla SRB2
+	sstage_end++; // plus one weirdo
 
 	// initialize free sfx slots for skin sounds
 	S_InitRuntimeSounds();
@@ -4669,6 +4667,7 @@ void G_GhostTicker(void)
 				p->next = g->next;
 			else
 				ghosts = g->next;
+			Z_Free(g);
 			continue;
 		}
 		p = g;
@@ -5669,29 +5668,28 @@ void G_AddGhost(char *defdemoname)
 	mthing = playerstarts[0];
 	I_Assert(mthing);
 	{ // A bit more complex than P_SpawnPlayer because ghosts aren't solid and won't just push themselves out of the ceiling.
-		fixed_t x,y,z;
-		sector_t *sector;
-		x = mthing->x << FRACBITS;
-		y = mthing->y << FRACBITS;
-		sector = R_PointInSubsector(x, y)->sector;
+		fixed_t z,f,c;
+		gh->mo = P_SpawnMobj(mthing->x << FRACBITS, mthing->y << FRACBITS, 0, MT_GHOST);
+		gh->mo->angle = FixedAngle(mthing->angle*FRACUNIT);
+		f = gh->mo->floorz;
+		c = gh->mo->ceilingz - mobjinfo[MT_PLAYER].height;
 		if (!!(mthing->options & MTF_AMBUSH) ^ !!(mthing->options & MTF_OBJECTFLIP))
 		{
-			z = sector->ceilingheight - mobjinfo[MT_PLAYER].height;
+			z = c;
 			if (mthing->options >> ZSHIFT)
 				z -= ((mthing->options >> ZSHIFT) << FRACBITS);
-			if (z < sector->floorheight)
-				z = sector->floorheight;
+			if (z < f)
+				z = f;
 		}
 		else
 		{
-			z = sector->floorheight;
+			z = f;
 			if (mthing->options >> ZSHIFT)
 				z += ((mthing->options >> ZSHIFT) << FRACBITS);
-			if (z > sector->ceilingheight - mobjinfo[MT_PLAYER].height)
-				z = sector->ceilingheight - mobjinfo[MT_PLAYER].height;
+			if (z > c)
+				z = c;
 		}
-		gh->mo = P_SpawnMobj(x, y, z, MT_GHOST);
-		gh->mo->angle = FixedAngle(mthing->angle*FRACUNIT);
+		gh->mo->z = z;
 	}
 
 	gh->oldmo.x = gh->mo->x;
@@ -5890,8 +5888,14 @@ boolean G_CheckDemoStatus(void)
 {
 	boolean saved;
 
-	if(ghosts) // ... ... ...
-		ghosts = NULL; // :)
+	while (ghosts)
+	{
+		demoghost *next = ghosts->next;
+		Z_Free(ghosts);
+		ghosts = next;
+	}
+	ghosts = NULL;
+
 
 	// DO NOT end metal sonic demos here
 
diff --git a/src/hardware/hw3sound.c b/src/hardware/hw3sound.c
index c68430921fa4053b5e3fc96fe23f94804291a0ea..f7c6e1da025e1db33471bd8775bc6a8ff4c826a3 100644
--- a/src/hardware/hw3sound.c
+++ b/src/hardware/hw3sound.c
@@ -361,7 +361,7 @@ INT32 HW3S_I_StartSound(const void *origin_p, source3D_data_t *source_parm, chan
 
 	if (splitscreen) listenmobj2 = players[secondarydisplayplayer].mo;
 
-	if (nosound)
+	if (sound_disabled)
 		return -1;
 
 	sfx = &S_sfx[sfx_id];
diff --git a/src/hardware/hw_light.c b/src/hardware/hw_light.c
index 267666749baf363ec02652fdb65f342a96f0d8f0..dfb2c43518f653078c18f1aa4495c1e0f4022b34 100644
--- a/src/hardware/hw_light.c
+++ b/src/hardware/hw_light.c
@@ -62,7 +62,7 @@ static dynlights_t *dynlights = &view_dynlights[0];
 light_t lspr[NUMLIGHTS] =
 {
 	// type       offset x,   y  coronas color, c_size,light color,l_radius, sqr radius computed at init
-	// UNDEFINED: 0
+	// NOLIGHT: 0
 	{ UNDEFINED_SPR,  0.0f,   0.0f, 0x00000000,  24.0f, 0x00000000,   0.0f, 0.0f},
 	// weapons
 	// RINGSPARK_L
@@ -151,10 +151,9 @@ light_t *t_lspr[NUMSPRITES] =
 	&lspr[NOLIGHT],     // SPR_POSS
 	&lspr[NOLIGHT],     // SPR_SPOS
 	&lspr[NOLIGHT],     // SPR_FISH
-	&lspr[NOLIGHT],     // SPR_BUZZ Graue 03-10-2004
-	&lspr[NOLIGHT],     // SPR_RBUZ Graue 03-10-2004
+	&lspr[NOLIGHT],     // SPR_BUZZ
+	&lspr[NOLIGHT],     // SPR_RBUZ
 	&lspr[NOLIGHT],     // SPR_JETB
-	&lspr[NOLIGHT],     // SPR_JETW
 	&lspr[NOLIGHT],     // SPR_JETG
 	&lspr[NOLIGHT],     // SPR_CCOM
 	&lspr[NOLIGHT],     // SPR_DETN
@@ -162,19 +161,20 @@ light_t *t_lspr[NUMSPRITES] =
 	&lspr[NOLIGHT],     // SPR_TRET
 	&lspr[NOLIGHT],     // SPR_TURR
 	&lspr[NOLIGHT],     // SPR_SHRP
+	&lspr[NOLIGHT],     // SPR_CRAB
 	&lspr[NOLIGHT],     // SPR_JJAW
 	&lspr[NOLIGHT],     // SPR_SNLR
 	&lspr[NOLIGHT],     // SPR_VLTR
 	&lspr[NOLIGHT],     // SPR_PNTY
 	&lspr[NOLIGHT],     // SPR_ARCH
 	&lspr[NOLIGHT],     // SPR_CBFS
+	&lspr[JETLIGHT_L],  // SPR_STAB
 	&lspr[NOLIGHT],     // SPR_SPSH
 	&lspr[NOLIGHT],     // SPR_ESHI
 	&lspr[NOLIGHT],     // SPR_GSNP
 	&lspr[NOLIGHT],     // SPR_MNUS
 	&lspr[NOLIGHT],     // SPR_SSHL
 	&lspr[NOLIGHT],     // SPR_UNID
-	&lspr[NOLIGHT],     // SPR_BBUZ
 
 	// Generic Boos Items
 	&lspr[JETLIGHT_L],     // SPR_JETF // Boss jet fumes
@@ -227,18 +227,18 @@ light_t *t_lspr[NUMSPRITES] =
 	&lspr[NOLIGHT],     // SPR_RING
 	&lspr[NOLIGHT],     // SPR_TRNG
 	&lspr[NOLIGHT],     // SPR_TOKE
-	&lspr[REDBALL_L],     // SPR_RFLG
-	&lspr[BLUEBALL_L],     // SPR_BFLG
-	&lspr[NOLIGHT],     // SPR_NWNG
+	&lspr[REDBALL_L],   // SPR_RFLG
+	&lspr[BLUEBALL_L],  // SPR_BFLG
+	&lspr[NOLIGHT],     // SPR_SPHR
+	&lspr[NOLIGHT],     // SPR_NCHP
+	&lspr[NOLIGHT],     // SPR_NSTR
 	&lspr[NOLIGHT],     // SPR_EMBM
 	&lspr[NOLIGHT],     // SPR_CEMG
-	&lspr[NOLIGHT],     // SPR_EMER
+	&lspr[NOLIGHT],     // SPR_SHRD
 
 	// Interactive Objects
-	&lspr[NOLIGHT],     // SPR_FANS
 	&lspr[NOLIGHT],     // SPR_BBLS
 	&lspr[NOLIGHT],     // SPR_SIGN
-	&lspr[NOLIGHT],     // SPR_STEM
 	&lspr[NOLIGHT],     // SPR_SPIK
 	&lspr[NOLIGHT],     // SPR_SFLM
 	&lspr[NOLIGHT],     // SPR_USPK
@@ -294,17 +294,19 @@ light_t *t_lspr[NUMSPRITES] =
 	&lspr[NOLIGHT],     // SPR_FWR4
 	&lspr[NOLIGHT],     // SPR_BUS1
 	&lspr[NOLIGHT],     // SPR_BUS2
+	&lspr[NOLIGHT],     // SPR_BUS3
 	// Trees (both GFZ and misc)
 	&lspr[NOLIGHT],     // SPR_TRE1
 	&lspr[NOLIGHT],     // SPR_TRE2
 	&lspr[NOLIGHT],     // SPR_TRE3
 	&lspr[NOLIGHT],     // SPR_TRE4
 	&lspr[NOLIGHT],     // SPR_TRE5
+	&lspr[NOLIGHT],     // SPR_TRE6
 
 	// Techno Hill Scenery
 	&lspr[NOLIGHT],     // SPR_THZP
 	&lspr[NOLIGHT],     // SPR_FWR5
-	&lspr[REDBALL_L],     // SPR_ALRM
+	&lspr[REDBALL_L],   // SPR_ALRM
 
 	// Deep Sea Scenery
 	&lspr[NOLIGHT],     // SPR_GARG
@@ -327,6 +329,15 @@ light_t *t_lspr[NUMSPRITES] =
 	&lspr[NOLIGHT],     // SPR_RSPB
 	&lspr[REDBALL_L],   // SPR_SFBR
 	&lspr[REDBALL_L],   // SPR_BFBR
+	&lspr[NOLIGHT],     // SPR_BANR
+	&lspr[NOLIGHT],     // SPR_PINE
+	&lspr[NOLIGHT],     // SPR_CEZB
+	&lspr[REDBALL_L],   // SPR_CNDL
+	&lspr[NOLIGHT],     // SPR_FLMH
+	&lspr[REDBALL_L],   // SPR_CTRC
+	&lspr[NOLIGHT],     // SPR_CFLG
+	&lspr[NOLIGHT],     // SPR_CSTA
+	&lspr[NOLIGHT],     // SPR_CBBS
 
 	// Arid Canyon Scenery
 	&lspr[NOLIGHT],     // SPR_BTBL
@@ -347,12 +358,25 @@ light_t *t_lspr[NUMSPRITES] =
 	&lspr[NOLIGHT],     // SPR_XMS3
 	&lspr[NOLIGHT],     // SPR_XMS4
 	&lspr[NOLIGHT],     // SPR_XMS5
+	&lspr[NOLIGHT],     // SPR_FHZI
+
+	// Halloween Scenery
+	&lspr[RINGLIGHT_L], // SPR_PUMK
+	&lspr[NOLIGHT],     // SPR_HHPL
+	&lspr[NOLIGHT],     // SPR_SHRM
+	&lspr[NOLIGHT],     // SPR_HHZM
 
 	// Botanic Serenity Scenery
 	&lspr[NOLIGHT],     // SPR_BSZ1
 	&lspr[NOLIGHT],     // SPR_BSZ2
 	&lspr[NOLIGHT],     // SPR_BSZ3
-	&lspr[NOLIGHT],     // SPR_BSZ4
+	//&lspr[NOLIGHT],     -- SPR_BSZ4
+	&lspr[NOLIGHT],     // SPR_BST1
+	&lspr[NOLIGHT],     // SPR_BST2
+	&lspr[NOLIGHT],     // SPR_BST3
+	&lspr[NOLIGHT],     // SPR_BST4
+	&lspr[NOLIGHT],     // SPR_BST5
+	&lspr[NOLIGHT],     // SPR_BST6
 	&lspr[NOLIGHT],     // SPR_BSZ5
 	&lspr[NOLIGHT],     // SPR_BSZ6
 	&lspr[NOLIGHT],     // SPR_BSZ7
@@ -375,8 +399,8 @@ light_t *t_lspr[NUMSPRITES] =
 	&lspr[NOLIGHT],     // SPR_FIRS
 	&lspr[NOLIGHT],     // SPR_BUBS
 	&lspr[NOLIGHT],     // SPR_ZAPS
-	&lspr[INVINCIBLE_L],     // SPR_IVSP
-	&lspr[SUPERSPARK_L],     // SPR_SSPK
+	&lspr[INVINCIBLE_L], // SPR_IVSP
+	&lspr[SUPERSPARK_L], // SPR_SSPK
 
 	&lspr[NOLIGHT],     // SPR_GOAL
 
@@ -398,13 +422,20 @@ light_t *t_lspr[NUMSPRITES] =
 	&lspr[NOLIGHT],     // SPR_FL14
 	&lspr[NOLIGHT],     // SPR_FL15
 	&lspr[NOLIGHT],     // SPR_FL16
+	&lspr[NOLIGHT],     // SPR_FS01
+	&lspr[NOLIGHT],     // SPR_FS02
 
 	// Springs
+	&lspr[NOLIGHT],     // SPR_FANS
+	&lspr[NOLIGHT],     // SPR_STEM
+	&lspr[NOLIGHT],     // SPR_BUMP
+	&lspr[NOLIGHT],     // SPR_BLON
 	&lspr[NOLIGHT],     // SPR_SPRY
 	&lspr[NOLIGHT],     // SPR_SPRR
-	&lspr[NOLIGHT],     // SPR_SPRB Graue
+	&lspr[NOLIGHT],     // SPR_SPRB
 	&lspr[NOLIGHT],     // SPR_YSPR
 	&lspr[NOLIGHT],     // SPR_RSPR
+	&lspr[NOLIGHT],     // SPR_BSPR
 	&lspr[NOLIGHT],     // SPR_SSWY
 	&lspr[NOLIGHT],     // SPR_SSWR
 	&lspr[NOLIGHT],     // SPR_SSWB
@@ -420,7 +451,7 @@ light_t *t_lspr[NUMSPRITES] =
 	&lspr[NOLIGHT],     // SPR_DUST
 	&lspr[NOLIGHT],     // SPR_FPRT
 	&lspr[SUPERSPARK_L], // SPR_TFOG
-	&lspr[NIGHTSLIGHT_L],     // SPR_SEED // Sonic CD flower seed
+	&lspr[NIGHTSLIGHT_L], // SPR_SEED
 	&lspr[NOLIGHT],     // SPR_PRTL
 
 	// Game Indicators
@@ -459,25 +490,43 @@ light_t *t_lspr[NUMSPRITES] =
 	&lspr[NOLIGHT],     // SPR_GOOM
 	&lspr[NOLIGHT],     // SPR_BGOM
 	&lspr[REDBALL_L],     // SPR_FFWR
-	&lspr[SMALLREDBALL_L],     // SPR_FBLL
+	&lspr[SMALLREDBALL_L], // SPR_FBLL
 	&lspr[NOLIGHT],     // SPR_SHLL
-	&lspr[REDBALL_L],     // SPR_PUMA
+	&lspr[REDBALL_L],   // SPR_PUMA
 	&lspr[NOLIGHT],     // SPR_HAMM
 	&lspr[NOLIGHT],     // SPR_KOOP
-	&lspr[REDBALL_L],     // SPR_BFLM
+	&lspr[REDBALL_L],   // SPR_BFLM
 	&lspr[NOLIGHT],     // SPR_MAXE
 	&lspr[NOLIGHT],     // SPR_MUS1
 	&lspr[NOLIGHT],     // SPR_MUS2
 	&lspr[NOLIGHT],     // SPR_TOAD
 
 	// NiGHTS Stuff
-	&lspr[SUPERSONIC_L],     // SPR_NDRN // NiGHTS drone
+	&lspr[SUPERSONIC_L], // SPR_NDRN // NiGHTS drone
 	&lspr[NOLIGHT],     // SPR_NSPK
 	&lspr[NOLIGHT],     // SPR_NBMP
 	&lspr[NOLIGHT],     // SPR_HOOP
 	&lspr[NOLIGHT],     // SPR_HSCR
 	&lspr[NOLIGHT],     // SPR_NPRU
 	&lspr[NOLIGHT],     // SPR_CAPS
+	&lspr[INVINCIBLE_L], // SPR_IDYA
+	&lspr[NOLIGHT],     // SPR_NTPN
+	&lspr[NOLIGHT],     // SPR_SHLP
+
+	// Secret badniks and hazards, shhhh
+	&lspr[NOLIGHT],     // SPR_PENG
+	&lspr[NOLIGHT],     // SPR_POPH,
+	&lspr[NOLIGHT],     // SPR_HIVE
+	&lspr[NOLIGHT],     // SPR_BUMB,
+	&lspr[NOLIGHT],     // SPR_BBUZ
+	&lspr[NOLIGHT],     // SPR_FMCE,
+	&lspr[NOLIGHT],     // SPR_HMCE,
+	&lspr[NOLIGHT],     // SPR_CACO,
+	&lspr[BLUEBALL_L],  // SPR_BAL2,
+	&lspr[NOLIGHT],     // SPR_SBOB,
+	&lspr[BLUEBALL_L],  // SPR_SBFL,
+	&lspr[BLUEBALL_L],  // SPR_SBSK,
+	&lspr[NOLIGHT],     // SPR_BATT,
 
 	// Debris
 	&lspr[RINGSPARK_L],  // SPR_SPRK
@@ -485,6 +534,7 @@ light_t *t_lspr[NUMSPRITES] =
 	&lspr[SUPERSPARK_L], // SPR_BOM2
 	&lspr[SUPERSPARK_L], // SPR_BOM3
 	&lspr[NOLIGHT],      // SPR_BOM4
+	&lspr[REDBALL_L],    // SPR_BMNB
 
 	// Crumbly rocks
 	&lspr[NOLIGHT],     // SPR_ROIA
@@ -504,9 +554,6 @@ light_t *t_lspr[NUMSPRITES] =
 	&lspr[NOLIGHT],     // SPR_ROIO
 	&lspr[NOLIGHT],     // SPR_ROIP
 
-	// Blue Spheres
-	&lspr[NOLIGHT],     // SPR_BBAL
-
 	// Gravity Well Objects
 	&lspr[NOLIGHT],     // SPR_GWLG
 	&lspr[NOLIGHT],     // SPR_GWLR
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index d06cbc10533b58086c59c0cd2918304bcd20b002..6529a0cd051c3388e3730a8a3fcc11598288934f 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -1126,7 +1126,7 @@ static void HWR_SplitWall(sector_t *sector, wallVert3D *wallVerts, INT32 texnum,
 			else
 			{
 				lightnum = *list[i].lightlevel;
-				colormap = list[i].extra_colormap;
+				colormap = *list[i].extra_colormap;
 			}
 		}
 
@@ -2115,27 +2115,64 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
 				}
 				else
 				{
-#ifdef ESLOPE // P.S. this is better-organized than the old version
-					fixed_t offs = sides[(newline ? newline : rover->master)->sidenum[0]].rowoffset;
-					grTex = HWR_GetTexture(texnum);
+					fixed_t texturevpeg;
+					boolean attachtobottom = false;
+#ifdef ESLOPE
+					boolean slopeskew = false; // skew FOF walls with slopes?
+#endif
+
+					// Wow, how was this missing from OpenGL for so long?
+					// ...Oh well, anyway, Lower Unpegged now changes pegging of FOFs like in software
+					// -- Monster Iestyn 26/06/18
+					if (newline)
+					{
+						texturevpeg = sides[newline->sidenum[0]].rowoffset;
+						attachtobottom = !!(newline->flags & ML_DONTPEGBOTTOM);
+#ifdef ESLOPE
+						slopeskew = !!(newline->flags & ML_DONTPEGTOP);
+#endif
+					}
+					else
+					{
+						texturevpeg = sides[rover->master->sidenum[0]].rowoffset;
+						attachtobottom = !!(gr_linedef->flags & ML_DONTPEGBOTTOM);
+#ifdef ESLOPE
+						slopeskew = !!(rover->master->flags & ML_DONTPEGTOP);
+#endif
+					}
 
-					wallVerts[3].t = (*rover->topheight - h + offs) * grTex->scaleY;
-					wallVerts[2].t = (*rover->topheight - hS + offs) * grTex->scaleY;
-					wallVerts[0].t = (*rover->topheight - l + offs) * grTex->scaleY;
-					wallVerts[1].t = (*rover->topheight - lS + offs) * grTex->scaleY;
-#else
 					grTex = HWR_GetTexture(texnum);
 
-					if (newline)
+#ifdef ESLOPE
+					if (!slopeskew) // no skewing
 					{
-						wallVerts[3].t = wallVerts[2].t = (*rover->topheight - h + sides[newline->sidenum[0]].rowoffset) * grTex->scaleY;
-						wallVerts[0].t = wallVerts[1].t = (h - l + (*rover->topheight - h + sides[newline->sidenum[0]].rowoffset)) * grTex->scaleY;
+						if (attachtobottom)
+							texturevpeg -= *rover->topheight - *rover->bottomheight;
+						wallVerts[3].t = (*rover->topheight - h + texturevpeg) * grTex->scaleY;
+						wallVerts[2].t = (*rover->topheight - hS + texturevpeg) * grTex->scaleY;
+						wallVerts[0].t = (*rover->topheight - l + texturevpeg) * grTex->scaleY;
+						wallVerts[1].t = (*rover->topheight - lS + texturevpeg) * grTex->scaleY;
 					}
 					else
 					{
-						wallVerts[3].t = wallVerts[2].t = (*rover->topheight - h + sides[rover->master->sidenum[0]].rowoffset) * grTex->scaleY;
-						wallVerts[0].t = wallVerts[1].t = (h - l + (*rover->topheight - h + sides[rover->master->sidenum[0]].rowoffset)) * grTex->scaleY;
+						if (!attachtobottom) // skew by top
+						{
+							wallVerts[3].t = wallVerts[2].t = texturevpeg * grTex->scaleY;
+							wallVerts[0].t = (h - l + texturevpeg) * grTex->scaleY;
+							wallVerts[1].t = (hS - lS + texturevpeg) * grTex->scaleY;
+						}
+						else // skew by bottom
+						{
+							wallVerts[0].t = wallVerts[1].t = texturevpeg * grTex->scaleY;
+							wallVerts[3].t = wallVerts[0].t - (h - l) * grTex->scaleY;
+							wallVerts[2].t = wallVerts[1].t - (hS - lS) * grTex->scaleY;
+						}
 					}
+#else
+					if (attachtobottom)
+						texturevpeg -= *rover->topheight - *rover->bottomheight;
+					wallVerts[3].t = wallVerts[2].t = (*rover->topheight - h + texturevpeg) * grTex->scaleY;
+					wallVerts[0].t = wallVerts[1].t = (*rover->topheight - l + texturevpeg) * grTex->scaleY;
 #endif
 
 					wallVerts[0].s = wallVerts[3].s = cliplow * grTex->scaleX;
@@ -3479,12 +3516,12 @@ static void HWR_Subsector(size_t num)
 		light = R_GetPlaneLight(gr_frontsector, locFloorHeight, false);
 		if (gr_frontsector->floorlightsec == -1)
 			floorlightlevel = *gr_frontsector->lightlist[light].lightlevel;
-		floorcolormap = gr_frontsector->lightlist[light].extra_colormap;
+		floorcolormap = *gr_frontsector->lightlist[light].extra_colormap;
 
 		light = R_GetPlaneLight(gr_frontsector, locCeilingHeight, false);
 		if (gr_frontsector->ceilinglightsec == -1)
 			ceilinglightlevel = *gr_frontsector->lightlist[light].lightlevel;
-		ceilingcolormap = gr_frontsector->lightlist[light].extra_colormap;
+		ceilingcolormap = *gr_frontsector->lightlist[light].extra_colormap;
 	}
 
 	sub->sector->extra_colormap = gr_frontsector->extra_colormap;
@@ -3610,7 +3647,7 @@ static void HWR_Subsector(size_t num)
 					                       *rover->bottomheight,
 					                       *gr_frontsector->lightlist[light].lightlevel,
 					                       rover->alpha-1 > 255 ? 255 : rover->alpha-1, rover->master->frontsector, PF_Translucent,
-					                       false, gr_frontsector->lightlist[light].extra_colormap);
+					                       false, *gr_frontsector->lightlist[light].extra_colormap);
 #endif
 				}
 				else
@@ -3618,7 +3655,7 @@ static void HWR_Subsector(size_t num)
 					HWR_GetFlat(levelflats[*rover->bottompic].lumpnum);
 					light = R_GetPlaneLight(gr_frontsector, centerHeight, dup_viewz < cullHeight ? true : false);
 					HWR_RenderPlane(NULL, &extrasubsectors[num], false, *rover->bottomheight, PF_Occlude, *gr_frontsector->lightlist[light].lightlevel, levelflats[*rover->bottompic].lumpnum,
-					                rover->master->frontsector, 255, false, gr_frontsector->lightlist[light].extra_colormap);
+					                rover->master->frontsector, 255, false, *gr_frontsector->lightlist[light].extra_colormap);
 				}
 			}
 
@@ -3673,7 +3710,7 @@ static void HWR_Subsector(size_t num)
 					                        *rover->topheight,
 					                        *gr_frontsector->lightlist[light].lightlevel,
 					                        rover->alpha-1 > 255 ? 255 : rover->alpha-1, rover->master->frontsector, PF_Translucent,
-					                        false, gr_frontsector->lightlist[light].extra_colormap);
+					                        false, *gr_frontsector->lightlist[light].extra_colormap);
 #endif
 
 				}
@@ -3682,7 +3719,7 @@ static void HWR_Subsector(size_t num)
 					HWR_GetFlat(levelflats[*rover->toppic].lumpnum);
 					light = R_GetPlaneLight(gr_frontsector, centerHeight, dup_viewz < cullHeight ? true : false);
 					HWR_RenderPlane(NULL, &extrasubsectors[num], true, *rover->topheight, PF_Occlude, *gr_frontsector->lightlist[light].lightlevel, levelflats[*rover->toppic].lumpnum,
-					                  rover->master->frontsector, 255, false, gr_frontsector->lightlist[light].extra_colormap);
+					                  rover->master->frontsector, 255, false, *gr_frontsector->lightlist[light].extra_colormap);
 				}
 			}
 		}
@@ -4072,7 +4109,7 @@ static void HWR_DrawSpriteShadow(gr_vissprite_t *spr, GLPatch_t *gpatch, float t
 		angle_t shadowdir;
 
 		// Set direction
-		if (splitscreen && stplyr != &players[displayplayer])
+		if (splitscreen && stplyr == &players[secondarydisplayplayer])
 			shadowdir = localangle2 + FixedAngle(cv_cam2_rotate.value);
 		else
 			shadowdir = localangle + FixedAngle(cv_cam_rotate.value);
@@ -4193,8 +4230,8 @@ static void HWR_DrawSpriteShadow(gr_vissprite_t *spr, GLPatch_t *gpatch, float t
 			if (!(spr->mobj->frame & FF_FULLBRIGHT))
 				lightlevel = *sector->lightlist[light].lightlevel;
 
-			if (sector->lightlist[light].extra_colormap)
-				colormap = sector->lightlist[light].extra_colormap;
+			if (*sector->lightlist[light].extra_colormap)
+				colormap = *sector->lightlist[light].extra_colormap;
 		}
 		else
 		{
@@ -4355,7 +4392,7 @@ static void HWR_SplitSprite(gr_vissprite_t *spr)
 
 	// Start with the lightlevel and colormap from the top of the sprite
 	lightlevel = *list[sector->numlights - 1].lightlevel;
-	colormap = list[sector->numlights - 1].extra_colormap;
+	colormap = *list[sector->numlights - 1].extra_colormap;
 	i = 0;
 	temp = FLOAT_TO_FIXED(realtop);
 
@@ -4371,7 +4408,7 @@ static void HWR_SplitSprite(gr_vissprite_t *spr)
 		{
 			if (!(spr->mobj->frame & FF_FULLBRIGHT))
 				lightlevel = *list[i-1].lightlevel;
-			colormap = list[i-1].extra_colormap;
+			colormap = *list[i-1].extra_colormap;
 			break;
 		}
 	}
@@ -4379,7 +4416,7 @@ static void HWR_SplitSprite(gr_vissprite_t *spr)
 	i = R_GetPlaneLight(sector, temp, false);
 	if (!(spr->mobj->frame & FF_FULLBRIGHT))
 		lightlevel = *list[i].lightlevel;
-	colormap = list[i].extra_colormap;
+	colormap = *list[i].extra_colormap;
 #endif
 
 	for (i = 0; i < sector->numlights; i++)
@@ -4395,7 +4432,7 @@ static void HWR_SplitSprite(gr_vissprite_t *spr)
 		{
 			if (!(spr->mobj->frame & FF_FULLBRIGHT))
 				lightlevel = *list[i].lightlevel;
-			colormap = list[i].extra_colormap;
+			colormap = *list[i].extra_colormap;
 		}
 
 #ifdef ESLOPE
@@ -4727,8 +4764,8 @@ static inline void HWR_DrawPrecipitationSprite(gr_vissprite_t *spr)
 			if (!(spr->mobj->frame & FF_FULLBRIGHT))
 				lightlevel = *sector->lightlist[light].lightlevel;
 
-			if (sector->lightlist[light].extra_colormap)
-				colormap = sector->lightlist[light].extra_colormap;
+			if (*sector->lightlist[light].extra_colormap)
+				colormap = *sector->lightlist[light].extra_colormap;
 		}
 		else
 		{
@@ -5263,8 +5300,10 @@ static void HWR_AddSprites(sector_t *sec)
 
 			approx_dist = P_AproxDistance(viewx-thing->x, viewy-thing->y);
 
-			if (approx_dist <= limit_dist)
-				HWR_ProjectSprite(thing);
+			if (approx_dist > limit_dist)
+				continue;
+
+			HWR_ProjectSprite(thing);
 		}
 	}
 	else
@@ -5286,8 +5325,10 @@ static void HWR_AddSprites(sector_t *sec)
 
 			approx_dist = P_AproxDistance(viewx-precipthing->x, viewy-precipthing->y);
 
-			if (approx_dist <= limit_dist)
-				HWR_ProjectPrecipitationSprite(precipthing);
+			if (approx_dist > limit_dist)
+				continue;
+
+			HWR_ProjectPrecipitationSprite(precipthing);
 		}
 	}
 	else
@@ -5480,7 +5521,10 @@ static void HWR_ProjectSprite(mobj_t *thing)
 	}
 
 	heightsec = thing->subsector->sector->heightsec;
-	phs = players[displayplayer].mo->subsector->sector->heightsec;
+	if (viewplayer->mo && viewplayer->mo->subsector)
+		phs = viewplayer->mo->subsector->sector->heightsec;
+	else
+		phs = -1;
 
 	if (heightsec != -1 && phs != -1) // only clip things which are in special sectors
 	{
@@ -5614,6 +5658,16 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing)
 	x1 = tr_x + x1 * rightcos;
 	x2 = tr_x - x2 * rightcos;
 
+	// okay, we can't return now... this is a hack, but weather isn't networked, so it should be ok
+	if (!(thing->precipflags & PCF_THUNK))
+	{
+		if (thing->precipflags & PCF_RAIN)
+			P_RainThinker(thing);
+		else
+			P_SnowThinker(thing);
+		thing->precipflags |= PCF_THUNK;
+	}
+
 	//
 	// store information in a vissprite
 	//
diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index bfb2638eeeea7a5c78078c64b72ed37bbc202fe1..d69233a9b8e8aab91c1fcf252d71ee6a195600f6 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -1194,8 +1194,8 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
 			if (!(spr->mobj->frame & FF_FULLBRIGHT))
 				lightlevel = *sector->lightlist[light].lightlevel;
 
-			if (sector->lightlist[light].extra_colormap)
-				colormap = sector->lightlist[light].extra_colormap;
+			if (*sector->lightlist[light].extra_colormap)
+				colormap = *sector->lightlist[light].extra_colormap;
 		}
 		else
 		{
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index a13801388c12196977e172094ae6b15c74609ae6..6697eb09bdd9ea8966ab721b7b37a81c8c3c4ff2 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -69,6 +69,7 @@ patch_t *nightsnum[10]; // 0-9
 // Level title and credits fonts
 patch_t *lt_font[LT_FONTSIZE];
 patch_t *cred_font[CRED_FONTSIZE];
+patch_t *ttlnum[20]; // act numbers (0-19)
 
 static player_t *plr;
 boolean chat_on; // entering a chat message?
@@ -89,7 +90,7 @@ patch_t *tallinfin;
 //              coop hud
 //-------------------------------------------
 
-patch_t *emeraldpics[3][7]; // 0 = normal, 1 = tiny, 2 = coinbox
+patch_t *emeraldpics[3][8]; // 0 = normal, 1 = tiny, 2 = coinbox
 static patch_t *emblemicon;
 patch_t *tokenicon;
 static patch_t *exiticon;
@@ -237,6 +238,13 @@ void HU_LoadGraphics(void)
 	tallminus = (patch_t *)W_CachePatchName("STTMINUS", PU_HUDGFX);
 	tallinfin = (patch_t *)W_CachePatchName("STTINFIN", PU_HUDGFX);
 
+	// cache act numbers for level titles
+	for (i = 0; i < 20; i++)
+	{
+		sprintf(buffer, "TTL%.2d", i);
+		ttlnum[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX);
+	}
+
 	// cache the crosshairs, don't bother to know which one is being used,
 	// just cache all 3, they're so small anyway.
 	for (i = 0; i < HU_CROSSHAIRS; i++)
@@ -256,6 +264,7 @@ void HU_LoadGraphics(void)
 	emeraldpics[0][4] = W_CachePatchName("CHAOS5", PU_HUDGFX);
 	emeraldpics[0][5] = W_CachePatchName("CHAOS6", PU_HUDGFX);
 	emeraldpics[0][6] = W_CachePatchName("CHAOS7", PU_HUDGFX);
+	emeraldpics[0][7] = W_CachePatchName("CHAOS8", PU_HUDGFX);
 
 	emeraldpics[1][0] = W_CachePatchName("TEMER1", PU_HUDGFX);
 	emeraldpics[1][1] = W_CachePatchName("TEMER2", PU_HUDGFX);
@@ -264,6 +273,7 @@ void HU_LoadGraphics(void)
 	emeraldpics[1][4] = W_CachePatchName("TEMER5", PU_HUDGFX);
 	emeraldpics[1][5] = W_CachePatchName("TEMER6", PU_HUDGFX);
 	emeraldpics[1][6] = W_CachePatchName("TEMER7", PU_HUDGFX);
+	//emeraldpics[1][7] = W_CachePatchName("TEMER8", PU_HUDGFX); -- unused
 
 	emeraldpics[2][0] = W_CachePatchName("EMBOX1", PU_HUDGFX);
 	emeraldpics[2][1] = W_CachePatchName("EMBOX2", PU_HUDGFX);
@@ -272,6 +282,7 @@ void HU_LoadGraphics(void)
 	emeraldpics[2][4] = W_CachePatchName("EMBOX5", PU_HUDGFX);
 	emeraldpics[2][5] = W_CachePatchName("EMBOX6", PU_HUDGFX);
 	emeraldpics[2][6] = W_CachePatchName("EMBOX7", PU_HUDGFX);
+	//emeraldpics[2][7] = W_CachePatchName("EMBOX8", PU_HUDGFX); -- unused
 }
 
 // Initialise Heads up
@@ -1264,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)
 		{
@@ -1402,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/hu_stuff.h b/src/hu_stuff.h
index fb1fa181763118d40d8d4fb09b8628cb10dae5a2..9e67e49b59106090b7b3e7c4e4a596169c59c280 100644
--- a/src/hu_stuff.h
+++ b/src/hu_stuff.h
@@ -63,7 +63,8 @@ extern patch_t *tallnum[10];
 extern patch_t *nightsnum[10];
 extern patch_t *lt_font[LT_FONTSIZE];
 extern patch_t *cred_font[CRED_FONTSIZE];
-extern patch_t *emeraldpics[3][7];
+extern patch_t *ttlnum[20];
+extern patch_t *emeraldpics[3][8];
 extern patch_t *rflagico;
 extern patch_t *bflagico;
 extern patch_t *rmatcico;
diff --git a/src/i_sound.h b/src/i_sound.h
index 084479ee1860485b23537983653b067e7b2d0094..2f73017ddd94fdb93e08f69fe9b11cee9977060f 100644
--- a/src/i_sound.h
+++ b/src/i_sound.h
@@ -18,6 +18,21 @@
 #include "sounds.h"
 #include "command.h"
 
+// copied from SDL mixer, plus GME
+typedef enum {
+	MU_NONE,
+	MU_CMD,
+	MU_WAV,
+	MU_MOD,
+	MU_MID,
+	MU_OGG,
+	MU_MP3,
+	MU_MP3_MAD_UNUSED, // use MU_MP3 instead
+	MU_FLAC,
+	MU_MODPLUG_UNUSED, // use MU_MOD instead
+	MU_GME
+} musictype_t;
+
 /**	\brief Sound subsystem runing and waiting
 */
 extern UINT8 sound_started;
@@ -51,9 +66,9 @@ void I_StartupSound(void);
 */
 void I_ShutdownSound(void);
 
-//
-//  SFX I/O
-//
+/// ------------------------
+///  SFX I/O
+/// ------------------------
 
 /**	\brief	Starts a sound in a particular sound channel.
 	\param	id	sfxid
@@ -64,7 +79,7 @@ void I_ShutdownSound(void);
 
 	\return	sfx handle
 */
-INT32 I_StartSound(sfxenum_t id, UINT8 vol, UINT8 sep, UINT8 pitch, UINT8 priority);
+INT32 I_StartSound(sfxenum_t id, UINT8 vol, UINT8 sep, UINT8 pitch, UINT8 priority, INT32 channel);
 
 /**	\brief	Stops a sound channel.
 
@@ -105,9 +120,10 @@ void I_UpdateSoundParams(INT32 handle, UINT8 vol, UINT8 sep, UINT8 pitch);
 */
 void I_SetSfxVolume(UINT8 volume);
 
-//
-//  MUSIC I/O
-//
+/// ------------------------
+//  MUSIC SYSTEM
+/// ------------------------
+
 /** \brief Init the music systems
 */
 void I_InitMusic(void);
@@ -116,41 +132,23 @@ void I_InitMusic(void);
 */
 void I_ShutdownMusic(void);
 
-/**	\brief	PAUSE game handling.
+/// ------------------------
+//  MUSIC PROPERTIES
+/// ------------------------
 
-	\param	handle	song handle
+musictype_t I_SongType(void);
+boolean I_SongPlaying(void);
+boolean I_SongPaused(void);
 
-	\return	void
-*/
-void I_PauseSong(INT32 handle);
-
-/**	\brief	RESUME game handling
+/// ------------------------
+//  MUSIC EFFECTS
+/// ------------------------
 
-	\param	handle	song handle
-
-	\return	void
-*/
-void I_ResumeSong(INT32 handle);
-
-//
-//  MIDI I/O
-//
-
-/**	\brief Startup the MIDI music system
-*/
-void I_InitMIDIMusic(void);
-
-/**	\brief Shutdown the MIDI music system
-*/
-void I_ShutdownMIDIMusic(void);
-
-/**	\brief	The I_SetMIDIMusicVolume function
-
-	\param	volume	volume to set at
+boolean I_SetSongSpeed(float speed);
 
-	\return	void
-*/
-void I_SetMIDIMusicVolume(UINT8 volume);
+/// ------------------------
+//  MUSIC PLAYBACK
+/// ------------------------
 
 /**	\brief	Registers a song handle to song data.
 
@@ -161,7 +159,16 @@ void I_SetMIDIMusicVolume(UINT8 volume);
 
 	\todo Remove this
 */
-INT32 I_RegisterSong(void *data, size_t len);
+boolean I_LoadSong(char *data, size_t len);
+
+/**	\brief	See ::I_LoadSong, then think backwards
+
+	\param	handle	song handle
+
+	\sa I_LoadSong
+	\todo remove midi handle
+*/
+void I_UnloadSong(void);
 
 /**	\brief	Called by anything that wishes to start music
 
@@ -172,7 +179,7 @@ INT32 I_RegisterSong(void *data, size_t len);
 
 	\todo pass music name, not handle
 */
-boolean I_PlaySong(INT32 handle, boolean looping);
+boolean I_PlaySong(boolean looping);
 
 /**	\brief	Stops a song over 3 seconds
 
@@ -181,58 +188,37 @@ boolean I_PlaySong(INT32 handle, boolean looping);
 
 	/todo drop handle
 */
-void I_StopSong(INT32 handle);
+void I_StopSong(void);
 
-/**	\brief	See ::I_RegisterSong, then think backwards
+/**	\brief	PAUSE game handling.
 
 	\param	handle	song handle
 
-	\sa I_RegisterSong
-	\todo remove midi handle
-*/
-void I_UnRegisterSong(INT32 handle);
-
-//
-//  DIGMUSIC I/O
-//
-
-/**	\brief Startup the music system
-*/
-void I_InitDigMusic(void);
-
-/**	\brief Shutdown the music system
+	\return	void
 */
-void I_ShutdownDigMusic(void);
-
-boolean I_SetSongSpeed(float speed);
-
-boolean I_SetSongTrack(INT32 track);
-
-/**	\brief The I_StartDigSong function
+void I_PauseSong(void);
 
-	\param	musicname	music lump name
-	\param	looping	if true, loop the song
+/**	\brief	RESUME game handling
 
-	\return	if true, song playing
-*/
-boolean I_StartDigSong(const char *musicname, boolean looping);
+	\param	handle	song handle
 
-/**	\brief stop non-MIDI song
+	\return	void
 */
-void I_StopDigSong(void);
+void I_ResumeSong(void);
 
-/**	\brief The I_SetDigMusicVolume function
+/**	\brief	The I_SetMusicVolume function
 
 	\param	volume	volume to set at
 
 	\return	void
 */
-void I_SetDigMusicVolume(UINT8 volume);
+void I_SetMusicVolume(UINT8 volume);
 
-//
-// CD MUSIC I/O
-//
+boolean I_SetSongTrack(INT32 track);
 
+/// ------------------------
+//  CD MUSIC I/O
+/// ------------------------
 
 /**	\brief  cd music interface
 */
@@ -279,4 +265,4 @@ void I_PlayCD(UINT8 track, UINT8 looping);
 */
 boolean I_SetVolumeCD(INT32 volume);
 
-#endif
+#endif
\ No newline at end of file
diff --git a/src/i_tcp.c b/src/i_tcp.c
index 9bb4ec630320fb7129bf39f9ad7dd71ce4fc9200..044bf4e4cfcde253c5474a1550d1e0e16b6b70f4 100644
--- a/src/i_tcp.c
+++ b/src/i_tcp.c
@@ -649,14 +649,29 @@ static boolean SOCK_CanGet(void)
 #endif
 
 #ifndef NONET
-static void SOCK_Send(void)
+static inline ssize_t SOCK_SendToAddr(SOCKET_TYPE socket, mysockaddr_t *sockaddr)
 {
-	ssize_t c = ERRSOCKET;
 	socklen_t d4 = (socklen_t)sizeof(struct sockaddr_in);
 #ifdef HAVE_IPV6
 	socklen_t d6 = (socklen_t)sizeof(struct sockaddr_in6);
 #endif
 	socklen_t d, da = (socklen_t)sizeof(mysockaddr_t);
+
+	switch (sockaddr->any.sa_family)
+	{
+		case AF_INET:  d = d4; break;
+#ifdef HAVE_IPV6
+		case AF_INET6: d = d6; break;
+#endif
+		default:       d = da; break;
+	}
+
+	return sendto(socket, (char *)&doomcom->data, doomcom->datalength, 0, &sockaddr->any, d);
+}
+
+static void SOCK_Send(void)
+{
+	ssize_t c = ERRSOCKET;
 	size_t i, j;
 
 	if (!nodeconnected[doomcom->remotenode])
@@ -669,19 +684,7 @@ static void SOCK_Send(void)
 			for (j = 0; j < broadcastaddresses; j++)
 			{
 				if (myfamily[i] == broadcastaddress[j].any.sa_family)
-				{
-					if (broadcastaddress[i].any.sa_family == AF_INET)
-						d = d4;
-#ifdef HAVE_IPV6
-					else if (broadcastaddress[i].any.sa_family == AF_INET6)
-						d = d6;
-#endif
-					else
-						d = da;
-
-					c = sendto(mysockets[i], (char *)&doomcom->data, doomcom->datalength, 0,
-						&broadcastaddress[j].any, d);
-				}
+					SOCK_SendToAddr(mysockets[i], &broadcastaddress[j]);
 			}
 		}
 		return;
@@ -691,35 +694,13 @@ static void SOCK_Send(void)
 		for (i = 0; i < mysocketses; i++)
 		{
 			if (myfamily[i] == clientaddress[doomcom->remotenode].any.sa_family)
-			{
-				if (clientaddress[doomcom->remotenode].any.sa_family == AF_INET)
-					d = d4;
-#ifdef HAVE_IPV6
-				else if (clientaddress[doomcom->remotenode].any.sa_family == AF_INET6)
-					d = d6;
-#endif
-				else
-					d = da;
-
-				sendto(mysockets[i], (char *)&doomcom->data, doomcom->datalength, 0,
-					&clientaddress[doomcom->remotenode].any, d);
-			}
+				SOCK_SendToAddr(mysockets[i], &clientaddress[doomcom->remotenode]);
 		}
 		return;
 	}
 	else
 	{
-		if (clientaddress[doomcom->remotenode].any.sa_family == AF_INET)
-			d = d4;
-#ifdef HAVE_IPV6
-		else if (clientaddress[doomcom->remotenode].any.sa_family == AF_INET6)
-			d = d6;
-#endif
-		else
-			d = da;
-
-		c = sendto(nodesocket[doomcom->remotenode], (char *)&doomcom->data, doomcom->datalength, 0,
-			&clientaddress[doomcom->remotenode].any, d);
+		c = SOCK_SendToAddr(nodesocket[doomcom->remotenode], &clientaddress[doomcom->remotenode]);
 	}
 
 	if (c == ERRSOCKET && errno != ECONNREFUSED && errno != EWOULDBLOCK)
@@ -1007,7 +988,7 @@ static boolean UDP_Socket(void)
 	if (gaie == 0)
 	{
 		runp = ai;
-		while (runp != NULL)
+		while (runp != NULL && s < MAXNETNODES+1)
 		{
 			memcpy(&clientaddress[s], runp->ai_addr, runp->ai_addrlen);
 			s++;
@@ -1022,12 +1003,15 @@ static boolean UDP_Socket(void)
 		clientaddress[s].ip4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); //GetLocalAddress(); // my own ip
 		s++;
 	}
+
+	s = 0;
+
 	// setup broadcast adress to BROADCASTADDR entry
 	gaie = I_getaddrinfo("255.255.255.255", "0", &hints, &ai);
 	if (gaie == 0)
 	{
 		runp = ai;
-		while (runp != NULL)
+		while (runp != NULL && s < MAXNETNODES+1)
 		{
 			memcpy(&broadcastaddress[s], runp->ai_addr, runp->ai_addrlen);
 			s++;
@@ -1050,7 +1034,7 @@ static boolean UDP_Socket(void)
 		if (gaie == 0)
 		{
 			runp = ai;
-			while (runp != NULL)
+			while (runp != NULL && s < MAXNETNODES+1)
 			{
 				memcpy(&broadcastaddress[s], runp->ai_addr, runp->ai_addrlen);
 				s++;
diff --git a/src/info.c b/src/info.c
index 5bca057902bf1d7f11de410459ea58719738693b..3140bd7de121ed761f824d508cbf92c26eb9b642 100644
--- a/src/info.c
+++ b/src/info.c
@@ -16,6 +16,7 @@
 #include "doomstat.h"
 #include "sounds.h"
 #include "p_mobj.h"
+#include "p_local.h" // DMG_ constants
 #include "m_misc.h"
 #include "z_zone.h"
 #include "d_player.h"
@@ -35,33 +36,33 @@ char sprnames[NUMSPRITES + 1][5] =
 	"PLAY",
 
 	// Enemies
-	"POSS",
-	"SPOS",
-	"FISH", // Greenflower Fish
+	"POSS", // Crawla (Blue)
+	"SPOS", // Crawla (Red)
+	"FISH", // SDURF
 	"BUZZ", // Buzz (Gold)
 	"RBUZ", // Buzz (Red)
 	"JETB", // Jetty-Syn Bomber
-	"JETW", // Jetty-Syn Water Bomber
 	"JETG", // Jetty-Syn Gunner
 	"CCOM", // Crawla Commander
 	"DETN", // Deton
 	"SKIM", // Skim mine dropper
-	"TRET",
+	"TRET", // Industrial Turret
 	"TURR", // Pop-Up Turret
 	"SHRP", // Sharp
+	"CRAB", // Crushstacean
 	"JJAW", // Jet Jaw
 	"SNLR", // Snailer
-	"VLTR", // Vulture
+	"VLTR", // BASH
 	"PNTY", // Pointy
 	"ARCH", // Robo-Hood
-	"CBFS", // CastleBot FaceStabber (Egg Knight?)
+	"CBFS", // Castlebot Facestabber
+	"STAB", // Castlebot Facestabber spear aura
 	"SPSH", // Egg Guard
-	"ESHI", // Egg Shield for Egg Guard
+	"ESHI", // Egg Guard's shield
 	"GSNP", // Green Snapper
 	"MNUS", // Minus
 	"SSHL", // Spring Shell
 	"UNID", // Unidus
-	"BBUZ", // AquaBuzz, for Azure Temple
 
 	// Generic Boss Items
 	"JETF", // Boss jet fumes
@@ -117,16 +118,16 @@ char sprnames[NUMSPRITES + 1][5] =
 	"TOKE", // Special Stage Token
 	"RFLG", // Red CTF Flag
 	"BFLG", // Blue CTF Flag
-	"NWNG", // NiGHTS Wing collectable item.
+	"SPHR", // Sphere
+	"NCHP", // NiGHTS chip
+	"NSTR", // NiGHTS star
 	"EMBM", // Emblem
 	"CEMG", // Chaos Emeralds
-	"EMER", // Emerald Hunt
+	"SHRD", // Emerald hunt shards
 
 	// Interactive Objects
-	"FANS",
 	"BBLS", // water bubble source
 	"SIGN", // Level end sign
-	"STEM", // Steam riser
 	"SPIK", // Spike Ball
 	"SFLM", // Spin fire
 	"USPK", // Floor spike
@@ -182,16 +183,20 @@ char sprnames[NUMSPRITES + 1][5] =
 	"FWR4",
 	"BUS1", // GFZ Bush w/ berries
 	"BUS2", // GFZ Bush w/o berries
+	"BUS3", // GFZ Bush w/ BLUE berries
 	// Trees (both GFZ and misc)
 	"TRE1", // GFZ
 	"TRE2", // Checker
 	"TRE3", // Frozen Hillside
 	"TRE4", // Polygon
 	"TRE5", // Bush tree
+	"TRE6", // Spring tree
 
 	// Techno Hill Scenery
-	"THZP", // Techno Hill Zone Plant
-	"FWR5", // Another one
+	"THZP", // THZ1 Steam Flower
+	"FWR5", // THZ1 Spin flower (red)
+	"FWR6", // THZ1 Spin flower (yellow)
+	"THZT", // Steam Whistle tree/bush
 	"ALRM", // THZ2 Alarm
 
 	// Deep Sea Scenery
@@ -202,6 +207,9 @@ char sprnames[NUMSPRITES + 1][5] =
 	"CRL2", // Coral 2
 	"CRL3", // Coral 3
 	"BCRY", // Blue Crystal
+	"KELP", // Kelp
+	"DSTG", // DSZ Stalagmites
+	"LIBE", // DSZ Light beam
 
 	// Castle Eggman Scenery
 	"CHAN", // CEZ Chain
@@ -215,6 +223,16 @@ char sprnames[NUMSPRITES + 1][5] =
 	"RSPB", // Red spring on a ball
 	"SFBR", // Small Firebar
 	"BFBR", // Big Firebar
+	"BANR", // Banner
+	"PINE", // Pine Tree
+	"CEZB", // Bush
+	"CNDL", // Candle/pricket
+	"FLMH", // Flame holder
+	"CTRC", // Fire torch
+	"CFLG", // Waving flag/segment
+	"CSTA", // Crawla statue
+	"CBBS", // Facestabber statue
+	"CABR", // Brambles
 
 	// Arid Canyon Scenery
 	"BTBL", // Big tumbleweed
@@ -235,12 +253,25 @@ char sprnames[NUMSPRITES + 1][5] =
 	"XMS3", // Snowman
 	"XMS4", // Lamppost
 	"XMS5", // Hanging Star
+	"FHZI", // FHZ ice
+
+	// Halloween Scenery
+	"PUMK", // Pumpkins
+	"HHPL", // Dr Seuss Trees
+	"SHRM", // Mushroom
+	"HHZM", // Misc
 
 	// Botanic Serenity Scenery
 	"BSZ1", // Tall flowers
 	"BSZ2", // Medium flowers
 	"BSZ3", // Small flowers
-	"BSZ4", // Tulip
+	//"BSZ4", -- Tulips
+	"BST1", // Red tulip
+	"BST2", // Purple tulip
+	"BST3", // Blue tulip
+	"BST4", // Cyan tulip
+	"BST5", // Yellow tulip
+	"BST6", // Orange tulip
 	"BSZ5", // Cluster of Tulips
 	"BSZ6", // Bush
 	"BSZ7", // Vine
@@ -286,13 +317,20 @@ char sprnames[NUMSPRITES + 1][5] =
 	"FL14", // Dove
 	"FL15", // Cat
 	"FL16", // Canary
+	"FS01", // Spider
+	"FS02", // Bat
 
 	// Springs
-	"SPRY", // yellow spring
-	"SPRR", // red spring
-	"SPRB", // Blue springs
+	"FANS", // Fan
+	"STEM", // Steam riser
+	"BUMP", // Bumpers
+	"BLON", // Balloons
+	"SPRY", // Yellow spring
+	"SPRR", // Red spring
+	"SPRB", // Blue spring
 	"YSPR", // Yellow Diagonal Spring
 	"RSPR", // Red Diagonal Spring
+	"BSPR", // Blue Diagonal Spring
 	"SSWY", // Yellow Side Spring
 	"SSWR", // Red Side Spring
 	"SSWB", // Blue Side Spring
@@ -366,13 +404,32 @@ char sprnames[NUMSPRITES + 1][5] =
 	"NSCR", // NiGHTS score sprite
 	"NPRU", // Nights Powerups
 	"CAPS", // Capsule thingy for NiGHTS
+	"IDYA", // Ideya
+	"NTPN", // Nightopian
+	"SHLP", // Shleep
+
+	// Secret badniks and hazards, shhhh
+	"PENG",
+	"POPH",
+	"HIVE",
+	"BUMB",
+	"BBUZ",
+	"FMCE",
+	"HMCE",
+	"CACO",
+	"BAL2",
+	"SBOB",
+	"SBFL",
+	"SBSK",
+	"HBAT",
 
 	// Debris
-	"SPRK", // spark
+	"SPRK", // Sparkle
 	"BOM1", // Robot Explosion
 	"BOM2", // Boss Explosion 1
 	"BOM3", // Boss Explosion 2
 	"BOM4", // Underwater Explosion
+	"BMNB", // Mine Explosion
 
 	// Crumbly rocks
 	"ROIA",
@@ -392,9 +449,6 @@ char sprnames[NUMSPRITES + 1][5] =
 	"ROIO",
 	"ROIP",
 
-	// Blue Spheres
-	"BBAL",
-
 	// Gravity Well Objects
 	"GWLG",
 	"GWLR",
@@ -670,12 +724,12 @@ state_t states[NUMSTATES] =
 	{SPR_PLAY, SPR2_MLEL,                35, {NULL},                   0, 0, S_PLAY_WALK},  // S_PLAY_MELEE_LANDING
 
 	// SF_SUPER
-	{SPR_PLAY, SPR2_TRNS|FF_SPR2SUPER,                3, {NULL},          0, 0, S_PLAY_SUPER_TRANS2}, // S_PLAY_SUPER_TRANS1
+	{SPR_PLAY, SPR2_TRNS|FF_SPR2SUPER|FF_ANIMATE,     7, {NULL},          0, 4, S_PLAY_SUPER_TRANS2}, // S_PLAY_SUPER_TRANS1
 	{SPR_PLAY, SPR2_TRNS|FF_SPR2SUPER,                3, {NULL},          0, 0, S_PLAY_SUPER_TRANS3}, // S_PLAY_SUPER_TRANS2
 	{SPR_PLAY, SPR2_TRNS|FF_SPR2SUPER|FF_FULLBRIGHT,  2, {NULL},          0, 0, S_PLAY_SUPER_TRANS4}, // S_PLAY_SUPER_TRANS3
 	{SPR_PLAY, SPR2_TRNS|FF_SPR2SUPER|FF_FULLBRIGHT,  2, {NULL},          0, 0, S_PLAY_SUPER_TRANS5}, // S_PLAY_SUPER_TRANS4
 	{SPR_PLAY, SPR2_TRNS|FF_SPR2SUPER|FF_FULLBRIGHT,  2, {NULL},          0, 0, S_PLAY_SUPER_TRANS6}, // S_PLAY_SUPER_TRANS5
-	{SPR_PLAY, SPR2_TRNS|FF_SPR2SUPER|FF_FULLBRIGHT, 20, {A_FadeOverlay}, 0, 0, S_PLAY_FALL},         // S_PLAY_SUPER_TRANS6
+	{SPR_PLAY, SPR2_TRNS|FF_SPR2SUPER|FF_FULLBRIGHT, 19, {A_FadeOverlay}, 0, 0, S_PLAY_FALL},         // S_PLAY_SUPER_TRANS6
 
 	{SPR_NULL, 0, -1, {NULL}, 0, 0, S_OBJPLACE_DUMMY}, //S_OBJPLACE_DUMMY
 
@@ -690,12 +744,12 @@ state_t states[NUMSTATES] =
 	{SPR_PLAY, SPR2_SIGN, 1, {NULL}, 0, 24, S_PLAY_SIGN},         // S_PLAY_SIGN
 
 	// NiGHTS Player, transforming
-	{SPR_PLAY, SPR2_TRNS,                3, {NULL},          0, 0, S_PLAY_NIGHTS_TRANS2}, // S_PLAY_NIGHTS_TRANS1
+	{SPR_PLAY, SPR2_TRNS|FF_ANIMATE,     7, {NULL},          0, 4, S_PLAY_NIGHTS_TRANS2}, // S_PLAY_NIGHTS_TRANS1
 	{SPR_PLAY, SPR2_TRNS,                3, {NULL},          0, 0, S_PLAY_NIGHTS_TRANS3}, // S_PLAY_NIGHTS_TRANS2
 	{SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT,  2, {NULL},          0, 0, S_PLAY_NIGHTS_TRANS4}, // S_PLAY_NIGHTS_TRANS3
 	{SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT,  2, {NULL},          0, 0, S_PLAY_NIGHTS_TRANS5}, // S_PLAY_NIGHTS_TRANS4
 	{SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT,  2, {NULL},          0, 0, S_PLAY_NIGHTS_TRANS6}, // S_PLAY_NIGHTS_TRANS5
-	{SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 25, {A_FadeOverlay}, 4, 0, S_PLAY_NIGHTS_FLOAT},  // S_PLAY_NIGHTS_TRANS5
+	{SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 21, {A_FadeOverlay}, 2, 0, S_PLAY_NIGHTS_FLOAT},  // S_PLAY_NIGHTS_TRANS5
 
 	// NiGHTS Player, stand, float, pain, pull and attack
 	{SPR_PLAY, SPR2_NSTD, 7, {NULL}, 0, 0, S_PLAY_NIGHTS_STAND},  // S_PLAY_NIGHTS_STAND
@@ -782,10 +836,6 @@ state_t states[NUMSTATES] =
 	{SPR_RBUZ, 0, 2, {A_BuzzFly}, sfx_buzz4, 0, S_RBUZZFLY2}, // S_RBUZZFLY1
 	{SPR_RBUZ, 1, 2, {A_BuzzFly}, 0, 0, S_RBUZZFLY1}, // S_RBUZZFLY2
 
-	// AquaBuzz
-	{SPR_BBUZ, 0, 2, {NULL}, 0, 0, S_BBUZZFLY2}, // S_BBUZZFLY1
-	{SPR_BBUZ, 1, 2, {NULL}, 0, 0, S_BBUZZFLY1}, // S_BBUZZFLY2
-
 	// Jetty-Syn Bomber
 	{SPR_JETB, 0, 4, {A_Look}, 0, 0, S_JETBLOOK2},      // S_JETBLOOK1
 	{SPR_JETB, 1, 4, {A_Look}, 0, 0, S_JETBLOOK1},      // S_JETBLOOK2
@@ -822,7 +872,6 @@ state_t states[NUMSTATES] =
 	{SPR_DETN, 3,  1, {A_DetonChase}, 0, 0, S_DETON14}, // S_DETON13
 	{SPR_DETN, 2,  1, {A_DetonChase}, 0, 0, S_DETON15}, // S_DETON14
 	{SPR_DETN, 1,  1, {A_DetonChase}, 0, 0, S_DETON2},  // S_DETON15
-	{SPR_DETN, 0, -1, {NULL},         0, 0, S_DETON16}, // S_DETON16
 
 	// Skim Mine Dropper
 	{SPR_SKIM, 0,  1, {A_SkimChase}, 0, 0, S_SKIM2},    // S_SKIM1
@@ -863,14 +912,40 @@ state_t states[NUMSTATES] =
 	{SPR_TURR, 1, 2, {NULL}, 0, 0, S_TURRETPOPDOWN8},        // S_TURRETPOPDOWN7
 	{SPR_TURR, 0, 69,{A_SetTics}, 0, 1, S_TURRETLOOK},       // S_TURRETPOPDOWN8
 
-	// Sharp
-	{SPR_SHRP, 0, 1, {A_Look},       0, 0, S_SHARP_ROAM1}, // S_SHARP_ROAM1,
-	{SPR_SHRP, 0, 1, {A_SharpChase}, 0, 0, S_SHARP_ROAM2}, // S_SHARP_ROAM2,
-	{SPR_SHRP, 1, 2, {A_FaceTarget}, 0, 0, S_SHARP_AIM2}, // S_SHARP_AIM1,
-	{SPR_SHRP, 2, 2, {A_FaceTarget}, 0, 0, S_SHARP_AIM3}, // S_SHARP_AIM2,
-	{SPR_SHRP, 3, 2, {A_FaceTarget}, 0, 0, S_SHARP_AIM4}, // S_SHARP_AIM3,
-	{SPR_SHRP, 4, 7, {A_FaceTarget}, 0, 0, S_SHARP_SPIN}, // S_SHARP_AIM4,
-	{SPR_SHRP, 4, 1, {A_SharpSpin},  0, 0, S_SHARP_SPIN}, // S_SHARP_SPIN,
+	// Spincushion
+	{SPR_SHRP, 0,  2, {A_Look},                 0, 0, S_SPINCUSHION_LOOK},   // S_SPINCUSHION_LOOK
+	{SPR_SHRP, 1,  2, {A_SharpChase},           0, 0, S_SPINCUSHION_CHASE2}, // S_SPINCUSHION_CHASE1
+	{SPR_SHRP, 2,  2, {A_SharpChase},           0, 0, S_SPINCUSHION_CHASE3}, // S_SPINCUSHION_CHASE2
+	{SPR_SHRP, 3,  2, {A_SharpChase},           0, 0, S_SPINCUSHION_CHASE4}, // S_SPINCUSHION_CHASE3
+	{SPR_SHRP, 0,  2, {A_SharpChase},           0, 0, S_SPINCUSHION_CHASE1}, // S_SPINCUSHION_CHASE4
+	{SPR_SHRP, 0,  2, {NULL},                   0, 0, S_SPINCUSHION_AIM2},   // S_SPINCUSHION_AIM1
+	{SPR_SHRP, 4,  2, {NULL},                   0, 0, S_SPINCUSHION_AIM3},   // S_SPINCUSHION_AIM2
+	{SPR_SHRP, 5,  2, {A_SetObjectFlags}, MF_PAIN, 2, S_SPINCUSHION_AIM4},   // S_SPINCUSHION_AIM3
+	{SPR_SHRP, 6, 16, {A_MultiShotDist}, (MT_DUST<<16)|6, -32, S_SPINCUSHION_AIM5}, // S_SPINCUSHION_AIM4
+	{SPR_SHRP, 6,  0, {A_PlaySound},   sfx_shrpgo, 1, S_SPINCUSHION_SPIN1},  // S_SPINCUSHION_AIM5
+	{SPR_SHRP, 6,  1, {A_SharpSpin},            0, 0, S_SPINCUSHION_SPIN2},  // S_SPINCUSHION_SPIN1
+	{SPR_SHRP, 8,  1, {A_SharpSpin},            0, 0, S_SPINCUSHION_SPIN3},  // S_SPINCUSHION_SPIN2
+	{SPR_SHRP, 7,  1, {A_SharpSpin},            0, 0, S_SPINCUSHION_SPIN4},  // S_SPINCUSHION_SPIN3
+	{SPR_SHRP, 8,  1, {A_SharpSpin},  MT_SPINDUST, 0, S_SPINCUSHION_SPIN1},  // S_SPINCUSHION_SPIN4
+	{SPR_SHRP, 6,  1, {A_PlaySound},    sfx_s3k69, 1, S_SPINCUSHION_STOP2},  // S_SPINCUSHION_STOP1
+	{SPR_SHRP, 6,  4, {A_SharpDecel},           0, 0, S_SPINCUSHION_STOP2},  // S_SPINCUSHION_STOP2
+	{SPR_SHRP, 5,  4, {A_FaceTarget},           0, 0, S_SPINCUSHION_STOP4},  // S_SPINCUSHION_STOP3
+	{SPR_SHRP, 4,  4, {A_SetObjectFlags}, MF_PAIN, 1, S_SPINCUSHION_LOOK},   // S_SPINCUSHION_STOP4
+
+	// Crushstacean
+	{SPR_CRAB, 0,  3, {A_CrushstaceanWalk},  0, S_CRUSHSTACEAN_ROAMPAUSE, S_CRUSHSTACEAN_ROAM2},     // S_CRUSHSTACEAN_ROAM1
+	{SPR_CRAB, 1,  3, {A_CrushstaceanWalk},  0, S_CRUSHSTACEAN_ROAMPAUSE, S_CRUSHSTACEAN_ROAM3},     // S_CRUSHSTACEAN_ROAM2
+	{SPR_CRAB, 0,  3, {A_CrushstaceanWalk},  0, S_CRUSHSTACEAN_ROAMPAUSE, S_CRUSHSTACEAN_ROAM4},     // S_CRUSHSTACEAN_ROAM3
+	{SPR_CRAB, 2,  3, {A_CrushstaceanWalk},  0, S_CRUSHSTACEAN_ROAMPAUSE, S_CRUSHSTACEAN_ROAM1},     // S_CRUSHSTACEAN_ROAM4
+	{SPR_CRAB, 0, 40, {NULL},                0,                        0, S_CRUSHSTACEAN_ROAM1},     // S_CRUSHSTACEAN_ROAMPAUSE
+	{SPR_CRAB, 0, 10, {NULL},                0,                        0, S_CRUSHSTACEAN_PUNCH2},    // S_CRUSHSTACEAN_PUNCH1
+	{SPR_CRAB, 0, -1, {A_CrushstaceanPunch}, 0,                        0, S_CRUSHSTACEAN_ROAMPAUSE}, // S_CRUSHSTACEAN_PUNCH2
+	{SPR_CRAB, 3,  1, {A_CrushclawAim},   40,               20, S_CRUSHCLAW_AIM}, // S_CRUSHCLAW_AIM
+	{SPR_CRAB, 3,  1, {A_CrushclawLaunch}, 0, S_CRUSHCLAW_STAY, S_CRUSHCLAW_OUT}, // S_CRUSHCLAW_OUT
+	{SPR_CRAB, 3, 10, {NULL},              0,                0, S_CRUSHCLAW_IN},  // S_CRUSHCLAW_STAY
+	{SPR_CRAB, 3,  1, {A_CrushclawLaunch}, 1, S_CRUSHCLAW_WAIT, S_CRUSHCLAW_IN},  // S_CRUSHCLAW_IN
+	{SPR_CRAB, 3, 37, {NULL},              0,                0, S_CRUSHCLAW_AIM}, // S_CRUSHCLAW_WAIT
+	{SPR_CRAB, 4, -1, {NULL}, 0, 0, S_NULL}, // S_CRUSHCHAIN
 
 	// Jet Jaw
 	{SPR_JJAW, 0, 1, {A_JetJawRoam},  0, 0, S_JETJAW_ROAM2},   // S_JETJAW_ROAM1
@@ -920,24 +995,31 @@ state_t states[NUMSTATES] =
 	{SPR_PNTY, 1,  1, {A_CheckBuddy}, 0, 0, S_POINTYBALL1}, // S_POINTYBALL1
 
 	// Robo-Hood
-	{SPR_ARCH, 0, 14,       {A_Look}, (512<<16),   0, S_ROBOHOOD_LOOK},  // S_ROBOHOOD_LOOK
-	{SPR_ARCH, 2,  1,  {A_HoodThink},         0,   0, S_ROBOHOOD_STND},  // S_ROBOHOOD_STND
-	{SPR_ARCH, 0, 35,   {A_FireShot},  MT_ARROW, -24, S_ROBOHOOD_STND},  // S_ROBOHOOD_SHOOT
-	{SPR_ARCH, 1,  1,   {A_BunnyHop},         8,   5, S_ROBOHOOD_JUMP2}, // S_ROBOHOOD_JUMP
-	{SPR_ARCH, 1,  1,  {A_HoodThink},         0,   0, S_ROBOHOOD_JUMP2}, // S_ROBOHOOD_JUMP2
-	{SPR_ARCH, 0,  1,  {A_HoodThink},         0,   0, S_ROBOHOOD_FALL},  // S_ROBOHOOD_FALL
-
-	// CastleBot FaceStabber
-	{SPR_CBFS, 0,  1,   {A_FaceStabChase},  0, 0, S_FACESTABBER_STND2},   // S_FACESTABBER_STND1
-	{SPR_CBFS, 1,  1,   {A_FaceStabChase},  0, 0, S_FACESTABBER_STND3},   // S_FACESTABBER_STND2
-	{SPR_CBFS, 2,  1,   {A_FaceStabChase},  0, 0, S_FACESTABBER_STND4},   // S_FACESTABBER_STND3
-	{SPR_CBFS, 3,  1,   {A_FaceStabChase},  0, 0, S_FACESTABBER_STND5},   // S_FACESTABBER_STND4
-	{SPR_CBFS, 4,  1,   {A_FaceStabChase},  0, 0, S_FACESTABBER_STND6},   // S_FACESTABBER_STND5
-	{SPR_CBFS, 5,  1,   {A_FaceStabChase},  0, 0, S_FACESTABBER_STND1},   // S_FACESTABBER_STND6
-	{SPR_CBFS, 6, 14, {A_PlayActiveSound},  0, 0, S_FACESTABBER_CHARGE2}, // S_FACESTABBER_CHARGE1
-	{SPR_CBFS, 6,  0, {A_PlayAttackSound},  0, 0, S_FACESTABBER_CHARGE3}, // S_FACESTABBER_CHARGE2
-	{SPR_CBFS, 6,  0,      {A_FaceTarget},  0, 0, S_FACESTABBER_CHARGE4}, // S_FACESTABBER_CHARGE3
-	{SPR_CBFS, 7, 35,          {A_Thrust}, 20, 1, S_FACESTABBER_STND1},   // S_FACESTABBER_CHARGE4
+	{SPR_ARCH, 0,       4,            {A_Look}, 2048<<FRACBITS,   0, S_ROBOHOOD_LOOK},  // S_ROBOHOOD_LOOK
+	{SPR_ARCH, 0,       1,       {A_HoodThink},              0,   0, S_ROBOHOOD_STAND}, // S_ROBOHOOD_STAND
+	{SPR_ARCH, 2, TICRATE, {A_PlayActiveSound},              0,   0, S_ROBOHOOD_FIRE2}, // S_ROBOHOOD_FIRE1
+	{SPR_ARCH, 2,      20,        {A_HoodFire},       MT_ARROW,   0, S_ROBOHOOD_STAND}, // S_ROBOHOOD_FIRE2
+	{SPR_ARCH, 1,       1,      {A_FaceTarget},              0,   0, S_ROBOHOOD_JUMP2}, // S_ROBOHOOD_JUMP1
+	{SPR_ARCH, 1,       1,        {A_BunnyHop},              4, -10, S_ROBOHOOD_JUMP3}, // S_ROBOHOOD_JUMP2
+	{SPR_ARCH, 1,       1,        {A_HoodFall},              0,   0, S_ROBOHOOD_JUMP3}, // S_ROBOHOOD_JUMP3
+
+	// Castlebot Facestabber
+	{SPR_CBFS, 0,  1,        {A_Chase},  0, 0, S_FACESTABBER_STND2},   // S_FACESTABBER_STND1
+	{SPR_CBFS, 1,  1,        {A_Chase},  0, 0, S_FACESTABBER_STND3},   // S_FACESTABBER_STND2
+	{SPR_CBFS, 2,  1,        {A_Chase},  0, 0, S_FACESTABBER_STND4},   // S_FACESTABBER_STND3
+	{SPR_CBFS, 3,  1,        {A_Chase},  0, 0, S_FACESTABBER_STND5},   // S_FACESTABBER_STND4
+	{SPR_CBFS, 4,  1,        {A_Chase},  0, 0, S_FACESTABBER_STND6},   // S_FACESTABBER_STND5
+	{SPR_CBFS, 5,  1,        {A_Chase},  0, 0, S_FACESTABBER_STND1},   // S_FACESTABBER_STND6
+	{SPR_CBFS, 0,  1,  {A_FaceStabRev},                  20, S_FACESTABBER_CHARGE2, S_FACESTABBER_CHARGE1}, // S_FACESTABBER_CHARGE1
+	{SPR_CBFS, 0,  0,   {A_FaceTarget},                   0,                     0, S_FACESTABBER_CHARGE3}, // S_FACESTABBER_CHARGE2
+	{SPR_CBFS, 7,  1, {A_FaceStabHurl},                   6, S_FACESTABBER_CHARGE4, S_FACESTABBER_CHARGE3}, // S_FACESTABBER_CHARGE3
+	{SPR_CBFS, 7,  1, {A_FaceStabMiss}, 0,   S_FACESTABBER_STND1, S_FACESTABBER_CHARGE4}, // S_FACESTABBER_CHARGE4
+	{SPR_CBFS, 0, 35,         {A_Pain}, 0,                     0, S_FACESTABBER_STND1}, // S_FACESTABBER_PAIN
+	{SPR_CBFS, 0,  2,   {A_BossScream}, 1, MT_SONIC3KBOSSEXPLODE, S_FACESTABBER_DIE2},  // S_FACESTABBER_DIE1
+	{SPR_NULL, 0,  2,   {A_BossScream}, 1, MT_SONIC3KBOSSEXPLODE, S_FACESTABBER_DIE3},  // S_FACESTABBER_DIE2
+	{SPR_NULL, 0,  0,       {A_Repeat}, 7, S_FACESTABBER_DIE1,    S_XPLD_FLICKY},       // S_FACESTABBER_DIE3
+
+	{SPR_STAB, FF_PAPERSPRITE|FF_TRANS50|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_FACESTABBERSPEAR
 
 	// Egg Guard
 	{SPR_SPSH,  0,  1,       {A_Look}, 0, 0, S_EGGGUARD_STND},  // S_EGGGUARD_STND
@@ -953,8 +1035,8 @@ state_t states[NUMSTATES] =
 	{SPR_SPSH, 10,  1, {A_GuardChase}, 0, 0, S_EGGGUARD_RUN4},  // S_EGGGUARD_RUN3
 	{SPR_SPSH, 11,  1, {A_GuardChase}, 0, 0, S_EGGGUARD_RUN1},  // S_EGGGUARD_RUN4
 
-	// Egg Shield for Egg Guard
-	{SPR_ESHI, 0, 8, {A_EggShield}, 0, 0, S_EGGSHIELD}, // S_EGGSHIELD
+	{SPR_ESHI, 0, 8, {A_EggShield}, 0, 0, S_EGGSHIELD},  // S_EGGSHIELD
+	{SPR_ESHI, 0, TICRATE/2, {NULL}, 0, 0, S_NULL}, // S_EGGSHIELDBREAK
 
 	// Green Snapper
 	{SPR_GSNP, 0, 1,  {A_Look}, 0, 0, S_GSNAPPER_STND}, // S_GSNAPPER_STND
@@ -1012,13 +1094,7 @@ state_t states[NUMSTATES] =
 	{SPR_UNID, 1, 1, {A_UnidusBall}, 1, 0, S_UNIDUS_BALL}, // S_UNIDUS_BALL
 
 	// Boss Explosion
-	{SPR_BOM2, FF_FULLBRIGHT,   5, {NULL}, 0, 0, S_BPLD2}, // S_BPLD1
-	{SPR_BOM2, FF_FULLBRIGHT|1, 5, {NULL}, 0, 0, S_BPLD3}, // S_BPLD2
-	{SPR_BOM2, FF_FULLBRIGHT|2, 5, {NULL}, 0, 0, S_BPLD4}, // S_BPLD3
-	{SPR_BOM2, FF_FULLBRIGHT|3, 5, {NULL}, 0, 0, S_BPLD5}, // S_BPLD4
-	{SPR_BOM2, FF_FULLBRIGHT|4, 5, {NULL}, 0, 0, S_BPLD6}, // S_BPLD5
-	{SPR_BOM2, FF_FULLBRIGHT|5, 5, {NULL}, 0, 0, S_BPLD7}, // S_BPLD6
-	{SPR_BOM2, FF_FULLBRIGHT|6, 5, {NULL}, 0, 0, S_NULL},  // S_BPLD7
+	{SPR_BOM2, FF_FULLBRIGHT|FF_ANIMATE, (5*7), {NULL}, 6, 5, S_NULL}, // S_BOSSEXPLODE
 
 	// S3&K Boss Explosion
 	{SPR_BOM3, FF_FULLBRIGHT,   1, {NULL}, 0, 0, S_SONIC3KBOSSEXPLOSION2}, // S_SONIC3KBOSSEXPLOSION1
@@ -1372,12 +1448,12 @@ state_t states[NUMSTATES] =
 	{SPR_RCKT, 2 + FF_FULLBRIGHT, 6, {A_NapalmScatter}, MT_CYBRAKDEMON_NAPALM_FLAMES + (6<<16), 32 + (16<<16), S_CYBRAKDEMONMISSILE_EXPLODE3}, // S_CYBRAKDEMONMISSILE_EXPLODE2
 	{SPR_RCKT, 3 + FF_FULLBRIGHT, 4, {NULL}, 0, 0, S_NULL}, // S_CYBRAKDEMONMISSILE_EXPLODE3
 
-	{SPR_FLME, FF_TRANS20|FF_FULLBRIGHT  , 15, {NULL}, 0, 0, S_CYBRAKDEMONFLAMESHOT_FLY2}, // S_CYBRAKDEMONFLAMESHOT_FLY1
-	{SPR_FLME, FF_TRANS20|FF_FULLBRIGHT|1, 15, {NULL}, 0, 0, S_CYBRAKDEMONFLAMESHOT_FLY3}, // S_CYBRAKDEMONFLAMESHOT_FLY2
-	{SPR_FLME, FF_TRANS20|FF_FULLBRIGHT|2, -1, {NULL}, 0, 0, S_CYBRAKDEMONFLAMESHOT_FLY3}, // S_CYBRAKDEMONFLAMESHOT_FLY3
-	{SPR_FLME, FF_TRANS20|FF_FULLBRIGHT|2, 0, {A_SpawnObjectRelative}, 0, MT_CYBRAKDEMON_FLAMEREST, S_NULL}, // S_CYBRAKDEMONFLAMESHOT_DIE
+	{SPR_FLME, FF_FULLBRIGHT  , 15, {NULL}, 0, 0, S_CYBRAKDEMONFLAMESHOT_FLY2}, // S_CYBRAKDEMONFLAMESHOT_FLY1
+	{SPR_FLME, FF_FULLBRIGHT|1, 15, {NULL}, 0, 0, S_CYBRAKDEMONFLAMESHOT_FLY3}, // S_CYBRAKDEMONFLAMESHOT_FLY2
+	{SPR_FLME, FF_FULLBRIGHT|2, -1, {NULL}, 0, 0, S_CYBRAKDEMONFLAMESHOT_FLY3}, // S_CYBRAKDEMONFLAMESHOT_FLY3
+	{SPR_FLME, FF_FULLBRIGHT|2, 0, {A_SpawnObjectRelative}, 0, MT_CYBRAKDEMON_FLAMEREST, S_NULL}, // S_CYBRAKDEMONFLAMESHOT_DIE
 
-	{SPR_FLAM, FF_TRANS20|FF_FULLBRIGHT|5, 3, {A_SetFuse}, 10*TICRATE, 0, S_FLAMEREST}, // S_CYBRAKDEMONFLAMEREST
+	{SPR_FLAM, FF_FULLBRIGHT, 0, {A_SetFuse}, 10*TICRATE, 0, S_FLAMEREST}, // S_CYBRAKDEMONFLAMEREST
 
 	{SPR_ELEC, 0 + FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_CYBRAKDEMONELECTRICBARRIER_INIT2}, // S_CYBRAKDEMONELECTRICBARRIER_INIT1
 	{SPR_ELEC, 0 + FF_FULLBRIGHT, 0, {A_RemoteAction}, -1, S_CYBRAKDEMON_INVINCIBLERIZE, S_CYBRAKDEMONELECTRICBARRIER_PLAYSOUND}, // S_CYBRAKDEMONELECTRICBARRIER_INIT2
@@ -1524,18 +1600,28 @@ state_t states[NUMSTATES] =
 	// Ring
 	{SPR_RING, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 23, 1, S_RING}, // S_RING
 
-	// Blue Sphere Replacement for special stages
-	{SPR_BBAL, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BLUEBALL
-	{SPR_BBAL, 0, 20, {NULL}, 0, 0, S_NULL}, // S_BLUEBALLSPARK
+	// Blue Sphere for special stages
+	{SPR_SPHR, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_BLUESPHERE
+	{SPR_SPHR, FF_FULLBRIGHT|FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 1, 4, S_NULL}, // S_BLUESPHEREBONUS
+	{SPR_SPHR, 0, 20, {NULL}, 0, 0, S_NULL}, // S_BLUESPHERESPARK
 
-	// Gravity Well sprites for Egg Rock's Special Stage
-	{SPR_GWLG, 0, 1, {NULL}, 0, 0, S_GRAVWELLGREEN2}, // S_GRAVWELLGREEN
-	{SPR_GWLG, 1, 1, {NULL}, 0, 0, S_GRAVWELLGREEN3}, // S_GRAVWELLGREEN2
-	{SPR_GWLG, 2, 1, {NULL}, 0, 0, S_GRAVWELLGREEN},  // S_GRAVWELLGREEN3
+	// Bomb Sphere
+	{SPR_SPHR, FF_FULLBRIGHT|3, 2, {NULL}, 0, 0, S_BOMBSPHERE2}, // S_BOMBSPHERE1
+	{SPR_SPHR, FF_FULLBRIGHT|4, 1, {NULL}, 0, 0, S_BOMBSPHERE3}, // S_BOMBSPHERE2
+	{SPR_SPHR, FF_FULLBRIGHT|5, 2, {NULL}, 0, 0, S_BOMBSPHERE4}, // S_BOMBSPHERE3
+	{SPR_SPHR, FF_FULLBRIGHT|4, 1, {NULL}, 0, 0, S_BOMBSPHERE1}, // S_BOMBSPHERE4
 
-	{SPR_GWLR, 0, 1, {NULL}, 0, 0, S_GRAVWELLRED2},   // S_GRAVWELLRED
-	{SPR_GWLR, 1, 1, {NULL}, 0, 0, S_GRAVWELLRED3},   // S_GRAVWELLRED2
-	{SPR_GWLR, 2, 1, {NULL}, 0, 0, S_GRAVWELLRED},    // S_GRAVWELLRED3
+	// NiGHTS Chip
+	{SPR_NCHP, FF_FULLBRIGHT|FF_ANIMATE,    -1, {NULL}, 15, 2, S_NULL}, // S_NIGHTSCHIP
+	{SPR_NCHP, FF_FULLBRIGHT|FF_ANIMATE|16, -1, {NULL}, 15, 2, S_NULL}, // S_NIGHTSCHIPBONUS
+
+	// NiGHTS Star
+	{SPR_NSTR, FF_ANIMATE, -1, {NULL}, 14, 2, S_NULL}, // S_NIGHTSSTAR
+	{SPR_NSTR, 15, -1, {NULL}, 0, 0, S_NULL}, // S_NIGHTSSTARXMAS
+
+	// Gravity Well sprites for Egg Rock's Special Stage
+	{SPR_GWLG, FF_ANIMATE, -1, {NULL}, 2, 1, S_NULL}, // S_GRAVWELLGREEN
+	{SPR_GWLR, FF_ANIMATE, -1, {NULL}, 2, 1, S_NULL}, // S_GRAVWELLRED
 
 	// Individual Team Rings (now with shield attracting action! =P)
 	{SPR_TRNG, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 23, 1, S_TEAMRING},  // S_TEAMRING
@@ -1584,15 +1670,10 @@ state_t states[NUMSTATES] =
 	{SPR_CEMG, FF_FULLBRIGHT|5, -1, {NULL}, 0, 0, S_NULL}, // S_CEMG6
 	{SPR_CEMG, FF_FULLBRIGHT|6, -1, {NULL}, 0, 0, S_NULL}, // S_CEMG7
 
-	// Emeralds (for hunt)
-	{SPR_EMER, 0, -1, {NULL}, 0, 0, S_NULL}, // S_EMER1
-
-	// Fan
-	{SPR_FANS, 0, 1, {A_FanBubbleSpawn}, 2048, 0, S_FAN2}, // S_FAN
-	{SPR_FANS, 1, 1, {A_FanBubbleSpawn}, 1024, 0, S_FAN3}, // S_FAN2
-	{SPR_FANS, 2, 1, {A_FanBubbleSpawn},  512, 0, S_FAN4}, // S_FAN3
-	{SPR_FANS, 3, 1, {A_FanBubbleSpawn}, 1024, 0, S_FAN5}, // S_FAN4
-	{SPR_FANS, 4, 1, {A_FanBubbleSpawn},  512, 0, S_FAN},  // S_FAN5
+	// Emerald hunt shards
+	{SPR_SHRD, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SHRD1
+	{SPR_SHRD, 1, -1, {NULL}, 0, 0, S_NULL}, // S_SHRD2
+	{SPR_SHRD, 2, -1, {NULL}, 0, 0, S_NULL}, // S_SHRD3
 
 	// Bubble Source
 	{SPR_BBLS, 0, 8, {A_BubbleSpawn}, 2048, 0, S_BUBBLES2}, // S_BUBBLES1
@@ -1655,25 +1736,15 @@ state_t states[NUMSTATES] =
 	{SPR_SIGN, 3, -1, {NULL}, 0, 0, S_NULL},         // S_SIGN52 Eggman
 	{SPR_SIGN, 7, -1, {A_SignPlayer}, 0, 0, S_NULL}, // S_SIGN53 Blank
 
-	// Steam Riser
-	{SPR_STEM, 0, 2, {A_SetSolidSteam}, 0, 0, S_STEAM2},   // S_STEAM1
-	{SPR_STEM, 1, 2, {A_UnsetSolidSteam}, 0, 0, S_STEAM3}, // S_STEAM2
-	{SPR_STEM, 2, 2, {NULL}, 0, 0, S_STEAM4},              // S_STEAM3
-	{SPR_STEM, 3, 2, {NULL}, 0, 0, S_STEAM5},              // S_STEAM4
-	{SPR_STEM, 4, 2, {NULL}, 0, 0, S_STEAM6},              // S_STEAM5
-	{SPR_STEM, 5, 2, {NULL}, 0, 0, S_STEAM7},              // S_STEAM6
-	{SPR_STEM, 6, 2, {NULL}, 0, 0, S_STEAM8},              // S_STEAM7
-	{SPR_STEM, 7, 18, {NULL}, 0, 0, S_STEAM1},             // S_STEAM8
-
 	// Spike Ball
-	{SPR_SPIK, 0, 1, {A_RotateSpikeBall}, 0, 0, S_SPIKEBALL2}, // S_SPIKEBALL1
-	{SPR_SPIK, 1, 1, {A_RotateSpikeBall}, 0, 0, S_SPIKEBALL3}, // S_SPIKEBALL2
-	{SPR_SPIK, 2, 1, {A_RotateSpikeBall}, 0, 0, S_SPIKEBALL4}, // S_SPIKEBALL3
-	{SPR_SPIK, 3, 1, {A_RotateSpikeBall}, 0, 0, S_SPIKEBALL5}, // S_SPIKEBALL4
-	{SPR_SPIK, 4, 1, {A_RotateSpikeBall}, 0, 0, S_SPIKEBALL6}, // S_SPIKEBALL5
-	{SPR_SPIK, 5, 1, {A_RotateSpikeBall}, 0, 0, S_SPIKEBALL7}, // S_SPIKEBALL6
-	{SPR_SPIK, 6, 1, {A_RotateSpikeBall}, 0, 0, S_SPIKEBALL8}, // S_SPIKEBALL7
-	{SPR_SPIK, 7, 1, {A_RotateSpikeBall}, 0, 0, S_SPIKEBALL1}, // S_SPIKEBALL8
+	{SPR_SPIK, 0, 1, {NULL}, 0, 0, S_SPIKEBALL2}, // S_SPIKEBALL1
+	{SPR_SPIK, 1, 1, {NULL}, 0, 0, S_SPIKEBALL3}, // S_SPIKEBALL2
+	{SPR_SPIK, 2, 1, {NULL}, 0, 0, S_SPIKEBALL4}, // S_SPIKEBALL3
+	{SPR_SPIK, 3, 1, {NULL}, 0, 0, S_SPIKEBALL5}, // S_SPIKEBALL4
+	{SPR_SPIK, 4, 1, {NULL}, 0, 0, S_SPIKEBALL6}, // S_SPIKEBALL5
+	{SPR_SPIK, 5, 1, {NULL}, 0, 0, S_SPIKEBALL7}, // S_SPIKEBALL6
+	{SPR_SPIK, 6, 1, {NULL}, 0, 0, S_SPIKEBALL8}, // S_SPIKEBALL7
+	{SPR_SPIK, 7, 1, {NULL}, 0, 0, S_SPIKEBALL1}, // S_SPIKEBALL8
 
 	// Elemental Shield's Spawn
 	{SPR_SFLM, FF_FULLBRIGHT,   2, {NULL}, 0, 0, S_SPINFIRE2}, // S_SPINFIRE1
@@ -1712,14 +1783,18 @@ state_t states[NUMSTATES] =
 	{SPR_STPT, FF_ANIMATE|15,  2, {NULL},  1, 1, S_STARPOST_FLASH}, // S_STARPOST_ENDSPIN
 
 	// Big floating mine
-	{SPR_BMNE, 0, 5, {NULL}, 0, 0, S_BIGMINE2},    // S_BIGMINE1
-	{SPR_BMNE, 1, 5, {NULL}, 0, 0, S_BIGMINE3},    // S_BIGMINE2
-	{SPR_BMNE, 2, 5, {NULL}, 0, 0, S_BIGMINE4},    // S_BIGMINE3
-	{SPR_BMNE, 3, 5, {NULL}, 0, 0, S_BIGMINE5},    // S_BIGMINE4
-	{SPR_BMNE, 4, 5, {NULL}, 0, 0, S_BIGMINE6},    // S_BIGMINE5
-	{SPR_BMNE, 5, 5, {NULL}, 0, 0, S_BIGMINE7},    // S_BIGMINE6
-	{SPR_BMNE, 6, 5, {NULL}, 0, 0, S_BIGMINE8},    // S_BIGMINE7
-	{SPR_BMNE, 7, 5, {NULL}, 0, 0, S_BIGMINE1},    // S_BIGMINE8
+	{SPR_BMNE, 0,  2, {A_Look},      ((224<<FRACBITS)|1), 0, S_BIGMINE_IDLE},   // S_BIGMINE_IDLE
+	{SPR_BMNE, 1,  2, {A_MineRange}, 112,                 0, S_BIGMINE_ALERT2}, // S_BIGMINE_ALERT1
+	{SPR_BMNE, 2,  2, {A_MineRange}, 112,                 0, S_BIGMINE_ALERT3}, // S_BIGMINE_ALERT2
+	{SPR_BMNE, 0,  1, {A_Look},      ((224<<FRACBITS)|1), 1, S_BIGMINE_IDLE},   // S_BIGMINE_ALERT3
+	{SPR_BMNE, 3, 25, {A_Pain},           0,            0, S_BIGMINE_SET2},   // S_BIGMINE_SET1
+	{SPR_BMNE, 3, 10, {A_SetObjectFlags}, MF_SHOOTABLE, 1, S_BIGMINE_SET3},   // S_BIGMINE_SET1
+	{SPR_BMNE, 3,  1, {A_MineExplode},    0,            0, S_BIGMINE_BLAST1}, // S_BIGMINE_SET3
+	{SPR_BMNB,   FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_BIGMINE_BLAST2}, // S_BIGMINE_BLAST1
+	{SPR_BMNB, 1|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_BIGMINE_BLAST3}, // S_BIGMINE_BLAST2
+	{SPR_BMNB, 2|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_BIGMINE_BLAST4}, // S_BIGMINE_BLAST3
+	{SPR_BMNB, 3|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_BIGMINE_BLAST5}, // S_BIGMINE_BLAST4
+	{SPR_NULL, 0, 35, {NULL}, 0, 0, S_NULL}, // S_BIGMINE_BLAST5
 
 	// Cannon launcher
 	{SPR_NULL, 0, 1,    {A_FindTarget},     MT_PLAYER,         0, S_CANNONLAUNCHER2}, // S_CANNONLAUNCHER1
@@ -1879,24 +1954,19 @@ state_t states[NUMSTATES] =
 
 	{SPR_CBLL, 0, -1, {NULL}, 0, 0, S_NULL}, // S_CANNONBALL1
 
-	{SPR_AROW, 0, 1, {A_ArrowCheck}, 0, 0, S_ARROW},     // S_ARROW
-	{SPR_AROW, 1, 1, {A_ArrowCheck}, 0, 0, S_ARROWUP},   // S_ARROWUP
-	{SPR_AROW, 2, 1, {A_ArrowCheck}, 0, 0, S_ARROWDOWN}, // S_ARROWDOWN
+	{SPR_AROW, 0, -1, {NULL}, 0, 0, S_NULL}, // S_ARROW
+	{SPR_AROW, FF_ANIMATE, TICRATE, {A_ArrowBonks}, 7, 2, S_NULL}, // S_ARROWBONK
 
-	{SPR_CFIR, FF_FULLBRIGHT,   2, {NULL}, 0, 0, S_DEMONFIRE2}, // S_DEMONFIRE1
-	{SPR_CFIR, FF_FULLBRIGHT|1, 2, {NULL}, 0, 0, S_DEMONFIRE3}, // S_DEMONFIRE2
-	{SPR_CFIR, FF_FULLBRIGHT|2, 2, {NULL}, 0, 0, S_DEMONFIRE4}, // S_DEMONFIRE3
-	{SPR_CFIR, FF_FULLBRIGHT|3, 2, {NULL}, 0, 0, S_DEMONFIRE5}, // S_DEMONFIRE4
-	{SPR_CFIR, FF_FULLBRIGHT|4, 2, {NULL}, 0, 0, S_DEMONFIRE6}, // S_DEMONFIRE5
-	{SPR_CFIR, FF_FULLBRIGHT|5, 2, {NULL}, 0, 0, S_DEMONFIRE1}, // S_DEMONFIRE6
+	{SPR_CFIR, FF_FULLBRIGHT|FF_ANIMATE, -1, {NULL}, 5, 2, S_NULL}, // S_DEMONFIRE
 
-	// GFZ Flower
+	// GFZ flowers
 	{SPR_FWR1, FF_ANIMATE, -1, {NULL},  7, 3, S_NULL}, // S_GFZFLOWERA
 	{SPR_FWR2, FF_ANIMATE, -1, {NULL}, 19, 3, S_NULL}, // S_GFZFLOWERB
 	{SPR_FWR3, FF_ANIMATE, -1, {NULL}, 11, 4, S_NULL}, // S_GFZFLOWERC
 
-	{SPR_BUS1, 0, -1, {NULL}, 0, 0, S_NULL},       // S_BERRYBUSH
-	{SPR_BUS2, 0, -1, {NULL}, 0, 0, S_NULL},       // S_BUSH
+	{SPR_BUS3, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BLUEBERRYBUSH
+	{SPR_BUS1, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BERRYBUSH
+	{SPR_BUS2, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BUSH
 
 	// Trees
 	{SPR_TRE1, 0, -1, {NULL}, 0, 0, S_NULL}, // S_GFZTREE
@@ -1909,9 +1979,28 @@ state_t states[NUMSTATES] =
 	{SPR_TRE4, 0, -1, {NULL}, 0, 0, S_NULL}, // S_POLYGONTREE
 	{SPR_TRE5, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BUSHTREE
 	{SPR_TRE5, 1, -1, {NULL}, 0, 0, S_NULL}, // S_BUSHREDTREE
+	{SPR_TRE6, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SPRINGTREE
 
+	// THZ flowers
 	{SPR_THZP, FF_ANIMATE, -1, {NULL},  7, 4, S_NULL}, // S_THZFLOWERA
 	{SPR_FWR5, FF_ANIMATE, -1, {NULL}, 19, 2, S_NULL}, // S_THZFLOWERB
+	{SPR_FWR6, FF_ANIMATE, -1, {NULL}, 19, 2, S_NULL}, // S_THZFLOWERC
+
+	// THZ Steam Whistle tree/bush
+	{SPR_THZT, 0, -1, {NULL}, 0, 0, S_NULL}, // S_THZTREE
+	{SPR_THZT,  1|FF_PAPERSPRITE, 40, {NULL}, 0, 0, S_THZTREEBRANCH2}, // S_THZTREEBRANCH1
+	{SPR_THZT,  2|FF_PAPERSPRITE,  4, {NULL}, 0, 0, S_THZTREEBRANCH3}, // S_THZTREEBRANCH2
+	{SPR_THZT,  3|FF_PAPERSPRITE,  4, {NULL}, 0, 0, S_THZTREEBRANCH4}, // S_THZTREEBRANCH3
+	{SPR_THZT,  4|FF_PAPERSPRITE,  4, {NULL}, 0, 0, S_THZTREEBRANCH5}, // S_THZTREEBRANCH4
+	{SPR_THZT,  5|FF_PAPERSPRITE,  4, {NULL}, 0, 0, S_THZTREEBRANCH6}, // S_THZTREEBRANCH5
+	{SPR_THZT,  6|FF_PAPERSPRITE,  4, {NULL}, 0, 0, S_THZTREEBRANCH7}, // S_THZTREEBRANCH6
+	{SPR_THZT,  7|FF_PAPERSPRITE,  4, {NULL}, 0, 0, S_THZTREEBRANCH8}, // S_THZTREEBRANCH7
+	{SPR_THZT,  8|FF_PAPERSPRITE,  4, {NULL}, 0, 0, S_THZTREEBRANCH9}, // S_THZTREEBRANCH8
+	{SPR_THZT,  9|FF_PAPERSPRITE,  4, {NULL}, 0, 0, S_THZTREEBRANCH10}, // S_THZTREEBRANCH9
+	{SPR_THZT, 10|FF_PAPERSPRITE,  4, {NULL}, 0, 0, S_THZTREEBRANCH11}, // S_THZTREEBRANCH10
+	{SPR_THZT, 11|FF_PAPERSPRITE,  4, {NULL}, 0, 0, S_THZTREEBRANCH12}, // S_THZTREEBRANCH11
+	{SPR_THZT, 12|FF_PAPERSPRITE,  4, {NULL}, 0, 0, S_THZTREEBRANCH13}, // S_THZTREEBRANCH12
+	{SPR_THZT, 13|FF_PAPERSPRITE,  4, {NULL}, 0, 0, S_THZTREEBRANCH1}, // S_THZTREEBRANCH13
 
 	// THZ Alarm
 	{SPR_ALRM, FF_FULLBRIGHT, 35, {A_Scream}, 0, 0, S_ALARM1}, // S_ALARM1
@@ -1949,19 +2038,34 @@ state_t states[NUMSTATES] =
 	// Blue Crystal
 	{SPR_BCRY, FF_TRANS30, -1, {NULL}, 0, 0, S_NULL}, // S_BLUECRYSTAL1
 
+	// Kelp
+	{SPR_KELP, 0, -1, {NULL}, 0, 0, S_NULL}, // S_KELP
+
+	// DSZ Stalagmites
+	{SPR_DSTG, 0, -1, {NULL}, 0, 0, S_NULL}, // S_DSZSTALAGMITE
+	{SPR_DSTG, 1, -1, {NULL}, 0, 0, S_NULL}, // S_DSZ2STALAGMITE
+
+	// DSZ Light beam
+	{SPR_LIBE, 0|FF_TRANS80|FF_FULLBRIGHT|FF_PAPERSPRITE, 4, {A_LightBeamReset}, 0, 0, S_LIGHTBEAM2}, // S_LIGHTBEAM1
+	{SPR_LIBE, 0|FF_TRANS70|FF_FULLBRIGHT|FF_PAPERSPRITE, 4, {NULL}, 0, 0, S_LIGHTBEAM3},  // S_LIGHTBEAM2
+	{SPR_LIBE, 0|FF_TRANS60|FF_FULLBRIGHT|FF_PAPERSPRITE, 4, {NULL}, 0, 0, S_LIGHTBEAM4},  // S_LIGHTBEAM3
+	{SPR_LIBE, 0|FF_TRANS50|FF_FULLBRIGHT|FF_PAPERSPRITE, 2, {NULL}, 0, 0, S_LIGHTBEAM5},  // S_LIGHTBEAM4
+	{SPR_LIBE, 0|FF_TRANS40|FF_FULLBRIGHT|FF_PAPERSPRITE, 2, {NULL}, 0, 0, S_LIGHTBEAM6},  // S_LIGHTBEAM5
+	{SPR_LIBE, 0|FF_TRANS30|FF_FULLBRIGHT|FF_PAPERSPRITE, 9, {NULL}, 0, 0, S_LIGHTBEAM7},  // S_LIGHTBEAM6
+	{SPR_LIBE, 0|FF_TRANS40|FF_FULLBRIGHT|FF_PAPERSPRITE, 2, {NULL}, 0, 0, S_LIGHTBEAM8},  // S_LIGHTBEAM7
+	{SPR_LIBE, 0|FF_TRANS50|FF_FULLBRIGHT|FF_PAPERSPRITE, 2, {NULL}, 0, 0, S_LIGHTBEAM9},  // S_LIGHTBEAM8
+	{SPR_LIBE, 0|FF_TRANS60|FF_FULLBRIGHT|FF_PAPERSPRITE, 4, {NULL}, 0, 0, S_LIGHTBEAM10}, // S_LIGHTBEAM9
+	{SPR_LIBE, 0|FF_TRANS70|FF_FULLBRIGHT|FF_PAPERSPRITE, 4, {NULL}, 0, 0, S_LIGHTBEAM11}, // S_LIGHTBEAM10
+	{SPR_LIBE, 0|FF_TRANS80|FF_FULLBRIGHT|FF_PAPERSPRITE, 4, {NULL}, 0, 0, S_LIGHTBEAM12}, // S_LIGHTBEAM11
+	{SPR_NULL, 0, 2, {A_SetRandomTics}, 4, 35, S_LIGHTBEAM1}, // S_LIGHTBEAM12
+
 	// CEZ Chain
 	{SPR_CHAN, 0, -1, {NULL}, 0, 0, S_NULL}, // S_CEZCHAIN
 
 	// Flame
-	{SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20,    3, {A_FlameParticle}, 3, FRACUNIT/2, S_FLAME2}, // S_FLAME1
-	{SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|1,  3,            {NULL}, 0, 0         , S_FLAME3}, // S_FLAME2
-	{SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|2,  3, {A_FlameParticle}, 3, FRACUNIT/2, S_FLAME4}, // S_FLAME3
-	{SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|3,  3,            {NULL}, 0, 0         , S_FLAME5}, // S_FLAME4
-	{SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|4,  3, {A_FlameParticle}, 3, FRACUNIT/2, S_FLAME6}, // S_FLAME5
-	{SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|5,  3,            {NULL}, 0, 0         , S_FLAME1}, // S_FLAME6
-	{SPR_FLAM, FF_FULLBRIGHT|FF_TRANS10|6, 24,            {NULL}, 0, 0         , S_NULL},   // S_FLAMEPARTICLE
-
-	{SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|FF_ANIMATE, -1, {NULL}, 5, 3, S_FLAME2}, // S_FLAMEREST
+	{SPR_FLAM, FF_FULLBRIGHT|FF_ANIMATE,       3*8, {A_FlameParticle}, 7, 3, S_FLAME}, // S_FLAME
+	{SPR_FLAM, FF_FULLBRIGHT|FF_ANIMATE|8, TICRATE,            {NULL}, 3, 3, S_NULL},  // S_FLAMEPARTICLE
+	{SPR_FLAM, FF_FULLBRIGHT|FF_ANIMATE,        -1,            {NULL}, 7, 3, S_NULL},  // S_FLAMEREST
 
 	// Eggman statue
 	{SPR_ESTA, 0, -1, {NULL}, 0, 0, S_NULL}, // S_EGGSTATUE1
@@ -1971,10 +2075,12 @@ state_t states[NUMSTATES] =
 	{SPR_NULL, 0, -1, {A_SlingAppear}, 0, 0, S_NULL},   // S_SLING2
 
 	// CEZ maces and chains
-	{SPR_SMCH, 0, -1, {NULL}, 0, 0, S_SMALLMACECHAIN}, // S_SMALLMACECHAIN
-	{SPR_BMCH, 0, -1, {NULL}, 0, 0, S_BIGMACECHAIN},   // S_BIGMACECHAIN
-	{SPR_SMCE, 0, -1, {NULL}, 0, 0, S_SMALLMACE},      // S_SMALLMACE
-	{SPR_BMCE, 0, -1, {NULL}, 0, 0, S_BIGMACE},        // S_BIGMACE
+	{SPR_SMCH, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SMALLMACECHAIN
+	{SPR_BMCH, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BIGMACECHAIN
+	{SPR_SMCE, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SMALLMACE
+	{SPR_BMCE, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BIGMACE
+	{SPR_SMCH, 1, -1, {NULL}, 0, 0, S_NULL}, // S_SMALLGRABCHAIN
+	{SPR_BMCH, 1, -1, {NULL}, 0, 0, S_NULL}, // S_BIGGRABCHAIN
 
 	// Yellow spring on a ball
 	{SPR_YSPB, 0, -1, {NULL},   0, 0, S_NULL},              // S_YELLOWSPRINGBALL
@@ -1991,43 +2097,69 @@ state_t states[NUMSTATES] =
 	{SPR_RSPB, 1,  1, {NULL},   0, 0, S_REDSPRINGBALL},  // S_REDSPRINGBALL5
 
 	// Small Firebar
-	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20,     1, {A_FlameParticle}, 3, FRACUNIT/3, S_SMALLFIREBAR2},  // S_SMALLFIREBAR1
-	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20| 1,  1, {NULL},            0, 0,          S_SMALLFIREBAR3},  // S_SMALLFIREBAR2
-	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20| 2,  1, {A_FlameParticle}, 3, FRACUNIT/3, S_SMALLFIREBAR4},  // S_SMALLFIREBAR3
-	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20| 3,  1, {NULL},            0, 0,          S_SMALLFIREBAR5},  // S_SMALLFIREBAR4
-	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20| 4,  1, {A_FlameParticle}, 3, FRACUNIT/3, S_SMALLFIREBAR6},  // S_SMALLFIREBAR5
-	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20| 5,  1, {NULL},            0, 0,          S_SMALLFIREBAR7},  // S_SMALLFIREBAR6
-	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20| 6,  1, {A_FlameParticle}, 3, FRACUNIT/3, S_SMALLFIREBAR8},  // S_SMALLFIREBAR7
-	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20| 7,  1, {NULL},            0, 0,          S_SMALLFIREBAR9},  // S_SMALLFIREBAR8
-	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20| 8,  1, {A_FlameParticle}, 3, FRACUNIT/3, S_SMALLFIREBAR10}, // S_SMALLFIREBAR9
-	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20| 9,  1, {NULL},            0, 0,          S_SMALLFIREBAR11}, // S_SMALLFIREBAR10
-	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20|10,  1, {A_FlameParticle}, 3, FRACUNIT/3, S_SMALLFIREBAR12}, // S_SMALLFIREBAR11
-	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20|11,  1, {NULL},            0, 0,          S_SMALLFIREBAR13}, // S_SMALLFIREBAR12
-	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20|12,  1, {A_FlameParticle}, 3, FRACUNIT/3, S_SMALLFIREBAR14}, // S_SMALLFIREBAR13
-	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20|13,  1, {NULL},            0, 0,          S_SMALLFIREBAR15}, // S_SMALLFIREBAR14
-	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20|14,  1, {A_FlameParticle}, 3, FRACUNIT/3, S_SMALLFIREBAR16}, // S_SMALLFIREBAR15
-	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20|15,  1, {NULL},            0, 0,          S_SMALLFIREBAR1},  // S_SMALLFIREBAR16
+	{SPR_SFBR, FF_FULLBRIGHT,     1, {NULL},            0, 0, S_SMALLFIREBAR2},  // S_SMALLFIREBAR1
+	{SPR_SFBR, FF_FULLBRIGHT| 1,  1, {NULL},            0, 0, S_SMALLFIREBAR3},  // S_SMALLFIREBAR2
+	{SPR_SFBR, FF_FULLBRIGHT| 2,  1, {A_FlameParticle}, 0, 0, S_SMALLFIREBAR4},  // S_SMALLFIREBAR3
+	{SPR_SFBR, FF_FULLBRIGHT| 3,  1, {NULL},            0, 0, S_SMALLFIREBAR5},  // S_SMALLFIREBAR4
+	{SPR_SFBR, FF_FULLBRIGHT| 4,  1, {NULL},            0, 0, S_SMALLFIREBAR6},  // S_SMALLFIREBAR5
+	{SPR_SFBR, FF_FULLBRIGHT| 5,  1, {NULL},            0, 0, S_SMALLFIREBAR7},  // S_SMALLFIREBAR6
+	{SPR_SFBR, FF_FULLBRIGHT| 6,  1, {A_FlameParticle}, 0, 0, S_SMALLFIREBAR8},  // S_SMALLFIREBAR7
+	{SPR_SFBR, FF_FULLBRIGHT| 7,  1, {NULL},            0, 0, S_SMALLFIREBAR9},  // S_SMALLFIREBAR8
+	{SPR_SFBR, FF_FULLBRIGHT| 8,  1, {NULL},            0, 0, S_SMALLFIREBAR10}, // S_SMALLFIREBAR9
+	{SPR_SFBR, FF_FULLBRIGHT| 9,  1, {NULL},            0, 0, S_SMALLFIREBAR11}, // S_SMALLFIREBAR10
+	{SPR_SFBR, FF_FULLBRIGHT|10,  1, {A_FlameParticle}, 0, 0, S_SMALLFIREBAR12}, // S_SMALLFIREBAR11
+	{SPR_SFBR, FF_FULLBRIGHT|11,  1, {NULL},            0, 0, S_SMALLFIREBAR13}, // S_SMALLFIREBAR12
+	{SPR_SFBR, FF_FULLBRIGHT|12,  1, {NULL},            0, 0, S_SMALLFIREBAR14}, // S_SMALLFIREBAR13
+	{SPR_SFBR, FF_FULLBRIGHT|13,  1, {NULL},            0, 0, S_SMALLFIREBAR15}, // S_SMALLFIREBAR14
+	{SPR_SFBR, FF_FULLBRIGHT|14,  1, {A_FlameParticle}, 0, 0, S_SMALLFIREBAR16}, // S_SMALLFIREBAR15
+	{SPR_SFBR, FF_FULLBRIGHT|15,  1, {NULL},            0, 0, S_SMALLFIREBAR1},  // S_SMALLFIREBAR16
 
 	// Big Firebar
-	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20,     1, {A_FlameParticle}, 3, FRACUNIT/2, S_BIGFIREBAR2},  // S_BIGFIREBAR1
-	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20| 1,  1, {NULL},            0, 0,          S_BIGFIREBAR3},  // S_BIGFIREBAR2
-	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20| 2,  1, {A_FlameParticle}, 3, FRACUNIT/2, S_BIGFIREBAR4},  // S_BIGFIREBAR3
-	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20| 3,  1, {NULL},            0, 0,          S_BIGFIREBAR5},  // S_BIGFIREBAR4
-	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20| 4,  1, {A_FlameParticle}, 3, FRACUNIT/2, S_BIGFIREBAR6},  // S_BIGFIREBAR5
-	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20| 5,  1, {NULL},            0, 0,          S_BIGFIREBAR7},  // S_BIGFIREBAR6
-	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20| 6,  1, {A_FlameParticle}, 3, FRACUNIT/2, S_BIGFIREBAR8},  // S_BIGFIREBAR7
-	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20| 7,  1, {NULL},            0, 0,          S_BIGFIREBAR9},  // S_BIGFIREBAR8
-	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20| 8,  1, {A_FlameParticle}, 3, FRACUNIT/2, S_BIGFIREBAR10}, // S_BIGFIREBAR9
-	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20| 9,  1, {NULL},            0, 0,          S_BIGFIREBAR11}, // S_BIGFIREBAR10
-	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20|10,  1, {A_FlameParticle}, 3, FRACUNIT/2, S_BIGFIREBAR12}, // S_BIGFIREBAR11
-	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20|11,  1, {NULL},            0, 0,          S_BIGFIREBAR13}, // S_BIGFIREBAR12
-	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20|12,  1, {A_FlameParticle}, 3, FRACUNIT/2, S_BIGFIREBAR14}, // S_BIGFIREBAR13
-	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20|13,  1, {NULL},            0, 0,          S_BIGFIREBAR15}, // S_BIGFIREBAR14
-	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20|14,  1, {A_FlameParticle}, 3, FRACUNIT/2, S_BIGFIREBAR16}, // S_BIGFIREBAR15
-	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20|15,  1, {NULL},            0, 0,          S_BIGFIREBAR1},  // S_BIGFIREBAR16
-
-	// CEZ Flower
-	{SPR_FWR4, 0, -1, {NULL}, 0, 0, S_NULL}, // S_CEZFLOWER1
+	{SPR_BFBR, FF_FULLBRIGHT,     1, {NULL},            0, 0, S_BIGFIREBAR2},  // S_BIGFIREBAR1
+	{SPR_BFBR, FF_FULLBRIGHT| 1,  1, {NULL},            0, 0, S_BIGFIREBAR3},  // S_BIGFIREBAR2
+	{SPR_BFBR, FF_FULLBRIGHT| 2,  1, {A_FlameParticle}, 0, 0, S_BIGFIREBAR4},  // S_BIGFIREBAR3
+	{SPR_BFBR, FF_FULLBRIGHT| 3,  1, {NULL},            0, 0, S_BIGFIREBAR5},  // S_BIGFIREBAR4
+	{SPR_BFBR, FF_FULLBRIGHT| 4,  1, {NULL},            0, 0, S_BIGFIREBAR6},  // S_BIGFIREBAR5
+	{SPR_BFBR, FF_FULLBRIGHT| 5,  1, {NULL},            0, 0, S_BIGFIREBAR7},  // S_BIGFIREBAR6
+	{SPR_BFBR, FF_FULLBRIGHT| 6,  1, {A_FlameParticle}, 0, 0, S_BIGFIREBAR8},  // S_BIGFIREBAR7
+	{SPR_BFBR, FF_FULLBRIGHT| 7,  1, {NULL},            0, 0, S_BIGFIREBAR9},  // S_BIGFIREBAR8
+	{SPR_BFBR, FF_FULLBRIGHT| 8,  1, {NULL},            0, 0, S_BIGFIREBAR10}, // S_BIGFIREBAR9
+	{SPR_BFBR, FF_FULLBRIGHT| 9,  1, {NULL},            0, 0, S_BIGFIREBAR11}, // S_BIGFIREBAR10
+	{SPR_BFBR, FF_FULLBRIGHT|10,  1, {A_FlameParticle}, 0, 0, S_BIGFIREBAR12}, // S_BIGFIREBAR11
+	{SPR_BFBR, FF_FULLBRIGHT|11,  1, {NULL},            0, 0, S_BIGFIREBAR13}, // S_BIGFIREBAR12
+	{SPR_BFBR, FF_FULLBRIGHT|12,  1, {NULL},            0, 0, S_BIGFIREBAR14}, // S_BIGFIREBAR13
+	{SPR_BFBR, FF_FULLBRIGHT|13,  1, {NULL},            0, 0, S_BIGFIREBAR15}, // S_BIGFIREBAR14
+	{SPR_BFBR, FF_FULLBRIGHT|14,  1, {A_FlameParticle}, 0, 0, S_BIGFIREBAR16}, // S_BIGFIREBAR15
+	{SPR_BFBR, FF_FULLBRIGHT|15,  1, {NULL},            0, 0, S_BIGFIREBAR1},  // S_BIGFIREBAR16
+
+	{SPR_FWR4, 0, -1, {NULL}, 0, 0, S_NULL}, // S_CEZFLOWER
+	{SPR_BANR, 1, -1, {NULL}, 0, 0, S_NULL}, // S_CEZPOLE
+
+	{SPR_BANR, FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_CEZBANNER
+
+	{SPR_PINE, 0, -1, {NULL}, 0, 0, S_NULL}, // S_PINETREE
+	{SPR_CEZB, 0, -1, {NULL}, 0, 0, S_NULL}, // S_CEZBUSH1
+	{SPR_CEZB, 1, -1, {NULL}, 0, 0, S_NULL}, // S_CEZBUSH2
+
+	{SPR_CNDL, FF_FULLBRIGHT,   -1, {NULL}, 0, 0, S_NULL}, // S_CANDLE
+	{SPR_CNDL, FF_FULLBRIGHT|1, -1, {NULL}, 0, 0, S_NULL}, // S_CANDLEPRICKET
+
+	{SPR_FLMH, 0, -1, {NULL}, 0, 0, S_NULL}, // S_FLAMEHOLDER
+
+	{SPR_CTRC, FF_FULLBRIGHT|FF_ANIMATE, 8*3, {A_FlameParticle}, 3, 3, S_FIRETORCH}, // S_FIRETORCH
+
+	{SPR_CFLG,                0, -1, {NULL}, 0, 0, S_NULL}, // S_WAVINGFLAG
+	{SPR_CFLG, FF_PAPERSPRITE|1, -1, {NULL}, 0, 0, S_NULL}, // S_WAVINGFLAGSEG
+
+	{SPR_CSTA, 0, -1, {NULL}, 0, 0, S_NULL}, // S_CRAWLASTATUE
+
+	{SPR_CBBS, 0, -1, {NULL}, 0, 0, S_NULL}, // S_FACESTABBERSTATUE
+
+	{SPR_CBBS, 0, 5, {A_Look}, 768*FRACUNIT, 0, S_SUSPICIOUSFACESTABBERSTATUE_WAIT},   // S_SUSPICIOUSFACESTABBERSTATUE_WAIT
+	{SPR_CBBS, FF_ANIMATE, 23, {NULL},    6, 1, S_SUSPICIOUSFACESTABBERSTATUE_BURST2}, // S_SUSPICIOUSFACESTABBERSTATUE_BURST1
+	{SPR_NULL, 0, 40, {A_StatueBurst}, MT_FACESTABBER, S_FACESTABBER_CHARGE2, S_NULL}, // S_SUSPICIOUSFACESTABBERSTATUE_BURST2
+
+	{SPR_CABR, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BRAMBLES
 
 	// Big Tumbleweed
 	{SPR_BTBL, 0, -1, {NULL}, 0, 0, S_NULL},                // S_BIGTUMBLEWEED
@@ -2061,9 +2193,9 @@ state_t states[NUMSTATES] =
 	{SPR_NULL, 0, 2*TICRATE, {NULL},             0, 0, S_FLAMEJETSTART}, // S_FLAMEJETSTND
 	{SPR_NULL, 0, 3*TICRATE, {A_ToggleFlameJet}, 0, 0,  S_FLAMEJETSTOP}, // S_FLAMEJETSTART
 	{SPR_NULL, 0,         1, {A_ToggleFlameJet}, 0, 0,  S_FLAMEJETSTND}, // S_FLAMEJETSTOP
-	{SPR_FLME, FF_FULLBRIGHT|FF_TRANS50  ,  4, {NULL}, 0, 0, S_FLAMEJETFLAME2}, // S_FLAMEJETFLAME1
-	{SPR_FLME, FF_FULLBRIGHT|FF_TRANS60|1,  5, {NULL}, 0, 0, S_FLAMEJETFLAME3}, // S_FLAMEJETFLAME2
-	{SPR_FLME, FF_FULLBRIGHT|FF_TRANS70|2, 11, {NULL}, 0, 0,           S_NULL}, // S_FLAMEJETFLAME3
+	{SPR_FLME, FF_FULLBRIGHT  ,  4, {NULL}, 0, 0, S_FLAMEJETFLAME2}, // S_FLAMEJETFLAME1
+	{SPR_FLME, FF_FULLBRIGHT|1,  5, {NULL}, 0, 0, S_FLAMEJETFLAME3}, // S_FLAMEJETFLAME2
+	{SPR_FLME, FF_FULLBRIGHT|2, 11, {NULL}, 0, 0,           S_NULL}, // S_FLAMEJETFLAME3
 
 	// Spinning flame jets
 	// A: Counter-clockwise
@@ -2134,8 +2266,57 @@ state_t states[NUMSTATES] =
 	{SPR_XMS4, 1, -1, {NULL}, 0, 0, S_NULL}, // S_LAMPPOST2
 	{SPR_XMS5, 0, -1, {NULL}, 0, 0, S_NULL}, // S_HANGSTAR
 	// Xmas GFZ bushes
-	{SPR_BUS1, 1, -1, {NULL}, 0, 0, S_NULL}, // S_BERRYBUSH
-	{SPR_BUS2, 1, -1, {NULL}, 0, 0, S_NULL}, // S_BUSH
+	{SPR_BUS3, 1, -1, {NULL}, 0, 0, S_NULL}, // S_XMASBLUEBERRYBUSH
+	{SPR_BUS1, 1, -1, {NULL}, 0, 0, S_NULL}, // S_XMASBERRYBUSH
+	{SPR_BUS2, 1, -1, {NULL}, 0, 0, S_NULL}, // S_XMASBUSH
+	// FHZ
+	{SPR_FHZI, 0, -1, {NULL}, 0, 0, S_NULL}, // S_FHZICE1
+	{SPR_FHZI, 1, -1, {NULL}, 0, 0, S_NULL}, // S_FHZICE2
+
+	// Halloween Scenery
+	// Pumpkins
+	{SPR_PUMK,  0, -1, {NULL}, 0, 0, S_NULL}, // S_JACKO1
+	{SPR_PUMK,  3|FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_JACKO1OVERLAY_2}, // S_JACKO1OVERLAY_1
+	{SPR_PUMK,  4|FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_JACKO1OVERLAY_3}, // S_JACKO1OVERLAY_2
+	{SPR_PUMK,  5|FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_JACKO1OVERLAY_4}, // S_JACKO1OVERLAY_3
+	{SPR_PUMK,  4|FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_JACKO1OVERLAY_1}, // S_JACKO1OVERLAY_4
+	{SPR_PUMK,  1, -1, {NULL}, 0, 0, S_NULL}, // S_JACKO2
+	{SPR_PUMK,  6|FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_JACKO2OVERLAY_2}, // S_JACKO2OVERLAY_1
+	{SPR_PUMK,  7|FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_JACKO2OVERLAY_3}, // S_JACKO2OVERLAY_2
+	{SPR_PUMK,  8|FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_JACKO2OVERLAY_4}, // S_JACKO2OVERLAY_3
+	{SPR_PUMK,  7|FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_JACKO2OVERLAY_1}, // S_JACKO2OVERLAY_4
+	{SPR_PUMK,  2, -1, {NULL}, 0, 0, S_NULL}, // S_JACKO3
+	{SPR_PUMK,  9|FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_JACKO3OVERLAY_2}, // S_JACKO3OVERLAY_1
+	{SPR_PUMK, 10|FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_JACKO3OVERLAY_3}, // S_JACKO3OVERLAY_2
+	{SPR_PUMK, 11|FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_JACKO3OVERLAY_4}, // S_JACKO3OVERLAY_3
+	{SPR_PUMK, 10|FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_JACKO3OVERLAY_1}, // S_JACKO3OVERLAY_4
+	// Dr Seuss Trees
+	{SPR_HHPL, 2, -1, {A_ConnectToGround}, MT_HHZTREE_PART, 0, S_NULL}, // S_HHZTREE_TOP,
+	{SPR_HHPL, 1, -1, {NULL}, 0, 0, S_NULL}, // S_HHZTREE_TRUNK,
+	{SPR_HHPL, FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_HHZTREE_LEAF,
+	// Mushroom
+	{SPR_SHRM, 4,  3, {NULL}, 0, 0, S_HHZSHROOM_2},  // S_HHZSHROOM_1,
+	{SPR_SHRM, 3,  3, {NULL}, 0, 0, S_HHZSHROOM_3},  // S_HHZSHROOM_2,
+	{SPR_SHRM, 2,  2, {NULL}, 0, 0, S_HHZSHROOM_4},  // S_HHZSHROOM_3,
+	{SPR_SHRM, 1,  1, {NULL}, 0, 0, S_HHZSHROOM_5},  // S_HHZSHROOM_4,
+	{SPR_SHRM, 0,  1, {NULL}, 0, 0, S_HHZSHROOM_6},  // S_HHZSHROOM_5,
+	{SPR_SHRM, 1,  4, {NULL}, 0, 0, S_HHZSHROOM_7},  // S_HHZSHROOM_6,
+	{SPR_SHRM, 2,  2, {NULL}, 0, 0, S_HHZSHROOM_8},  // S_HHZSHROOM_7,
+	{SPR_SHRM, 3,  3, {NULL}, 0, 0, S_HHZSHROOM_9},  // S_HHZSHROOM_8,
+	{SPR_SHRM, 4,  3, {NULL}, 0, 0, S_HHZSHROOM_10}, // S_HHZSHROOM_9,
+	{SPR_SHRM, 3,  3, {NULL}, 0, 0, S_HHZSHROOM_11}, // S_HHZSHROOM_10,
+	{SPR_SHRM, 5,  2, {NULL}, 0, 0, S_HHZSHROOM_12}, // S_HHZSHROOM_11,
+	{SPR_SHRM, 6,  1, {NULL}, 0, 0, S_HHZSHROOM_13}, // S_HHZSHROOM_12,
+	{SPR_SHRM, 7,  1, {NULL}, 0, 0, S_HHZSHROOM_14}, // S_HHZSHROOM_13,
+	{SPR_SHRM, 6,  4, {NULL}, 0, 0, S_HHZSHROOM_15}, // S_HHZSHROOM_14,
+	{SPR_SHRM, 5,  2, {NULL}, 0, 0, S_HHZSHROOM_16}, // S_HHZSHROOM_15,
+	{SPR_SHRM, 3,  3, {NULL}, 0, 0, S_HHZSHROOM_1},  // S_HHZSHROOM_16,
+	// Misc
+	{SPR_HHZM, 0, -1, {NULL}, 0, 0, S_NULL}, // S_HHZGRASS,
+	{SPR_HHZM, 1, -1, {NULL}, 0, 0, S_NULL}, // S_HHZTENT1,
+	{SPR_HHZM, 2, -1, {NULL}, 0, 0, S_NULL}, // S_HHZTENT2,
+	{SPR_HHZM, 4, -1, {NULL}, 0, 0, S_NULL}, // S_HHZSTALAGMITE_TALL,
+	{SPR_HHZM, 5, -1, {NULL}, 0, 0, S_NULL}, // S_HHZSTALAGMITE_SHORT,
 
 	// Loads of Botanic Serenity bullshit
 	{SPR_BSZ1, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BSZTALLFLOWER_RED
@@ -2156,12 +2337,12 @@ state_t states[NUMSTATES] =
 	{SPR_BSZ3, 3, -1, {NULL}, 0, 0, S_NULL}, // S_BSZSHORTFLOWER_CYAN
 	{SPR_BSZ3, 4, -1, {NULL}, 0, 0, S_NULL}, // S_BSZSHORTFLOWER_YELLOW
 	{SPR_BSZ3, 5, -1, {NULL}, 0, 0, S_NULL}, // S_BSZSHORTFLOWER_ORANGE
-	{SPR_BSZ4, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BSZTULIP_RED
-	{SPR_BSZ4, 1, -1, {NULL}, 0, 0, S_NULL}, // S_BSZTULIP_PURPLE
-	{SPR_BSZ4, 2, -1, {NULL}, 0, 0, S_NULL}, // S_BSZTULIP_BLUE
-	{SPR_BSZ4, 3, -1, {NULL}, 0, 0, S_NULL}, // S_BSZTULIP_CYAN
-	{SPR_BSZ4, 4, -1, {NULL}, 0, 0, S_NULL}, // S_BSZTULIP_YELLOW
-	{SPR_BSZ4, 5, -1, {NULL}, 0, 0, S_NULL}, // S_BSZTULIP_ORANGE
+	{SPR_BST1, FF_ANIMATE, -1, {NULL}, 11, 4, S_NULL}, // S_BSZTULIP_RED
+	{SPR_BST2, FF_ANIMATE, -1, {NULL}, 11, 4, S_NULL}, // S_BSZTULIP_PURPLE
+	{SPR_BST3, FF_ANIMATE, -1, {NULL}, 11, 4, S_NULL}, // S_BSZTULIP_BLUE
+	{SPR_BST4, FF_ANIMATE, -1, {NULL}, 11, 4, S_NULL}, // S_BSZTULIP_CYAN
+	{SPR_BST5, FF_ANIMATE, -1, {NULL}, 11, 4, S_NULL}, // S_BSZTULIP_YELLOW
+	{SPR_BST6, FF_ANIMATE, -1, {NULL}, 11, 4, S_NULL}, // S_BSZTULIP_ORANGE
 	{SPR_BSZ5, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BSZCLUSTER_RED
 	{SPR_BSZ5, 1, -1, {NULL}, 0, 0, S_NULL}, // S_BSZCLUSTER_PURPLE
 	{SPR_BSZ5, 2, -1, {NULL}, 0, 0, S_NULL}, // S_BSZCLUSTER_BLUE
@@ -2183,9 +2364,9 @@ state_t states[NUMSTATES] =
 	{SPR_BSZ8, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BSZSHRUB
 	{SPR_BSZ8, 1, -1, {NULL}, 0, 0, S_NULL}, // S_BSZCLOVER
 	{SPR_BSZ8, 2, -1, {NULL}, 0, 0, S_NULL}, // S_BIG_PALMTREE_TRUNK
-	{SPR_BSZ8, 3, -1, {NULL}, 0, 0, S_NULL}, // S_BIG_PALMTREE_TOP
+	{SPR_BSZ8, 3, -1, {A_ConnectToGround}, MT_BIG_PALMTREE_TRUNK, 0, S_NULL}, // S_BIG_PALMTREE_TOP
 	{SPR_BSZ8, 4, -1, {NULL}, 0, 0, S_NULL}, // S_PALMTREE_TRUNK
-	{SPR_BSZ8, 5, -1, {NULL}, 0, 0, S_NULL}, // S_PALMTREE_TOP
+	{SPR_BSZ8, 5, -1, {A_ConnectToGround},     MT_PALMTREE_TRUNK, 0, S_NULL}, // S_PALMTREE_TOP
 
 	// Disco ball
 	{SPR_DBAL, FF_FULLBRIGHT,   5, {NULL}, 0, 0, S_DBALL2}, // S_DBALL1
@@ -2460,6 +2641,8 @@ state_t states[NUMSTATES] =
 	{SPR_FL01, 1, 3, {A_FlickyFly},          4*FRACUNIT,       16*FRACUNIT, S_FLICKY_01_FLAP2}, // S_FLICKY_01_FLAP1
 	{SPR_FL01, 2, 3, {A_FlickyFly},          4*FRACUNIT,       16*FRACUNIT, S_FLICKY_01_FLAP3}, // S_FLICKY_01_FLAP2
 	{SPR_FL01, 3, 3, {A_FlickyFly},          4*FRACUNIT,       16*FRACUNIT, S_FLICKY_01_FLAP1}, // S_FLICKY_01_FLAP3
+	{SPR_FL01, FF_ANIMATE|1, -1, {NULL}, 2, 3, S_NULL},                                         // S_FLICKY_01_STAND
+	{SPR_NULL, 0, 15, {A_FlickyCenter}, MT_FLICKY_01, 384*FRACUNIT, S_FLICKY_01_CENTER},        // S_FLICKY_01_CENTER
 
 	// Rabbit
 	{SPR_FL02, 0, 2, {A_FlickyCheck}, S_FLICKY_02_AIM,                0, S_FLICKY_02_OUT},  // S_FLICKY_02_OUT
@@ -2467,6 +2650,8 @@ state_t states[NUMSTATES] =
 	{SPR_FL02, 1, 1, {A_FlickyHop},        6*FRACUNIT,       4*FRACUNIT, S_FLICKY_02_UP},   // S_FLICKY_02_HOP
 	{SPR_FL02, 2, 2, {A_FlickyCheck}, S_FLICKY_02_AIM, S_FLICKY_02_DOWN, S_FLICKY_02_UP},   // S_FLICKY_02_UP
 	{SPR_FL02, 3, 2, {A_FlickyCheck}, S_FLICKY_02_AIM,                0, S_FLICKY_02_DOWN}, // S_FLICKY_02_DOWN
+	{SPR_FL02, FF_ANIMATE|1, -1, {NULL}, 2, 4, S_NULL}, // S_FLICKY_02_STAND
+	{SPR_NULL, 0, 15, {A_FlickyCenter}, MT_FLICKY_02, 384*FRACUNIT, S_FLICKY_02_CENTER},        // S_FLICKY_02_CENTER
 
 	// Chicken
 	{SPR_FL03, 0, 2, {A_FlickyCheck},   S_FLICKY_03_AIM, S_FLICKY_03_FLAP1, S_FLICKY_03_OUT},   // S_FLICKY_03_OUT
@@ -2475,6 +2660,8 @@ state_t states[NUMSTATES] =
 	{SPR_FL03, 2, 2, {A_FlickyFlutter}, S_FLICKY_03_HOP, S_FLICKY_03_FLAP1, S_FLICKY_03_UP},    // S_FLICKY_03_UP
 	{SPR_FL03, 3, 2, {A_FlickyFlutter}, S_FLICKY_03_HOP,                 0, S_FLICKY_03_FLAP2}, // S_FLICKY_03_FLAP1
 	{SPR_FL03, 4, 2, {A_FlickyFlutter}, S_FLICKY_03_HOP,                 0, S_FLICKY_03_FLAP1}, // S_FLICKY_03_FLAP2
+	{SPR_FL03, FF_ANIMATE|1, -1, {NULL}, 2, 4, S_NULL}, // S_FLICKY_03_STAND
+	{SPR_NULL, 0, 15, {A_FlickyCenter}, MT_FLICKY_03, 384*FRACUNIT, S_FLICKY_03_CENTER},        // S_FLICKY_03_CENTER
 
 	// Seal
 	{SPR_FL04, 0, 2, {A_FlickyCheck}, S_FLICKY_04_AIM,                 0, S_FLICKY_04_OUT},   // S_FLICKY_04_OUT
@@ -2486,6 +2673,8 @@ state_t states[NUMSTATES] =
 	{SPR_FL04, 4, 4, {A_FlickyCoast},        FRACUNIT, S_FLICKY_04_SWIM1, S_FLICKY_04_SWIM3}, // S_FLICKY_04_SWIM2
 	{SPR_FL04, 3, 4, {A_FlickyCoast},        FRACUNIT, S_FLICKY_04_SWIM1, S_FLICKY_04_SWIM4}, // S_FLICKY_04_SWIM3
 	{SPR_FL04, 5, 4, {A_FlickyCoast},        FRACUNIT, S_FLICKY_04_SWIM1, S_FLICKY_04_SWIM1}, // S_FLICKY_04_SWIM4
+	{SPR_FL04, FF_ANIMATE|1, -1, {NULL}, 2, 4, S_NULL}, // S_FLICKY_04_STAND
+	{SPR_NULL, 0, 15, {A_FlickyCenter}, MT_FLICKY_04, 384*FRACUNIT, S_FLICKY_04_CENTER},        // S_FLICKY_04_CENTER
 
 	// Pig
 	{SPR_FL05, 0, 2, {A_FlickyCheck}, S_FLICKY_05_AIM,                0, S_FLICKY_05_OUT},  // S_FLICKY_05_OUT
@@ -2493,6 +2682,8 @@ state_t states[NUMSTATES] =
 	{SPR_FL05, 1, 1, {A_FlickyHop},        4*FRACUNIT,       3*FRACUNIT, S_FLICKY_05_UP},   // S_FLICKY_05_HOP
 	{SPR_FL05, 2, 2, {A_FlickyCheck}, S_FLICKY_05_AIM, S_FLICKY_05_DOWN, S_FLICKY_05_UP},   // S_FLICKY_05_UP
 	{SPR_FL05, 3, 2, {A_FlickyCheck}, S_FLICKY_05_AIM,                0, S_FLICKY_05_DOWN}, // S_FLICKY_05_DOWN
+	{SPR_FL05, FF_ANIMATE|1, -1, {NULL}, 2, 4, S_NULL}, // S_FLICKY_05_STAND
+	{SPR_NULL, 0, 15, {A_FlickyCenter}, MT_FLICKY_05, 384*FRACUNIT, S_FLICKY_05_CENTER},        // S_FLICKY_05_CENTER
 
 	// Chipmunk
 	{SPR_FL06, 0, 2, {A_FlickyCheck}, S_FLICKY_06_AIM,                0, S_FLICKY_06_OUT},  // S_FLICKY_06_OUT
@@ -2500,6 +2691,8 @@ state_t states[NUMSTATES] =
 	{SPR_FL06, 1, 1, {A_FlickyHop},        5*FRACUNIT,       6*FRACUNIT, S_FLICKY_06_UP},   // S_FLICKY_06_HOP
 	{SPR_FL06, 2, 2, {A_FlickyCheck}, S_FLICKY_06_AIM, S_FLICKY_06_DOWN, S_FLICKY_06_UP},   // S_FLICKY_06_UP
 	{SPR_FL06, 3, 2, {A_FlickyCheck}, S_FLICKY_06_AIM,                0, S_FLICKY_06_DOWN}, // S_FLICKY_06_DOWN
+	{SPR_FL06, FF_ANIMATE|1, -1, {NULL}, 2, 4, S_NULL}, // S_FLICKY_06_STAND
+	{SPR_NULL, 0, 15, {A_FlickyCenter}, MT_FLICKY_06, 384*FRACUNIT, S_FLICKY_06_CENTER},        // S_FLICKY_06_CENTER
 
 	// Penguin
 	{SPR_FL07, 0, 2, {A_FlickyCheck}, S_FLICKY_07_AIML,                 0, S_FLICKY_07_OUT},   // S_FLICKY_07_OUT
@@ -2514,6 +2707,8 @@ state_t states[NUMSTATES] =
 	{SPR_FL07, 4, 4, {A_FlickyFly},         3*FRACUNIT,       72*FRACUNIT, S_FLICKY_07_SWIM2}, // S_FLICKY_07_SWIM1
 	{SPR_FL07, 5, 4, {A_FlickyCoast},         FRACUNIT, S_FLICKY_07_SWIM1, S_FLICKY_07_SWIM3}, // S_FLICKY_07_SWIM2
 	{SPR_FL07, 6, 4, {A_FlickyCoast},       2*FRACUNIT, S_FLICKY_07_SWIM1, S_FLICKY_07_SWIM3}, // S_FLICKY_07_SWIM3
+	{SPR_FL07, FF_ANIMATE|1, -1, {NULL}, 2, 4, S_NULL}, // S_FLICKY_07_STAND
+	{SPR_NULL, 0, 15, {A_FlickyCenter}, MT_FLICKY_07, 384*FRACUNIT, S_FLICKY_07_CENTER},        // S_FLICKY_07_CENTER
 
 	// Fish
 	{SPR_FL08, 0, 2, {A_FlickyCheck}, S_FLICKY_08_AIM,                 0, S_FLICKY_08_OUT},   // S_FLICKY_08_OUT
@@ -2527,6 +2722,8 @@ state_t states[NUMSTATES] =
 	{SPR_FL08, 1, 4, {A_FlickyCoast},        FRACUNIT, S_FLICKY_08_SWIM1, S_FLICKY_08_SWIM3}, // S_FLICKY_08_SWIM2
 	{SPR_FL08, 0, 4, {A_FlickyCoast},        FRACUNIT, S_FLICKY_08_SWIM1, S_FLICKY_08_SWIM4}, // S_FLICKY_08_SWIM3
 	{SPR_FL08, 2, 4, {A_FlickyCoast},        FRACUNIT, S_FLICKY_08_SWIM1, S_FLICKY_08_SWIM4}, // S_FLICKY_08_SWIM4
+	{SPR_FL08, FF_ANIMATE, -1, {NULL}, 2, 4, S_NULL}, // S_FLICKY_08_STAND
+	{SPR_NULL, 0, 15, {A_FlickyCenter}, MT_FLICKY_08, 384*FRACUNIT, S_FLICKY_08_CENTER},        // S_FLICKY_08_CENTER
 
 	// Ram
 	{SPR_FL09, 0, 2, {A_FlickyCheck}, S_FLICKY_09_AIM,                0, S_FLICKY_09_OUT},  // S_FLICKY_09_OUT
@@ -2534,11 +2731,15 @@ state_t states[NUMSTATES] =
 	{SPR_FL09, 1, 1, {A_FlickyHop},        7*FRACUNIT,       2*FRACUNIT, S_FLICKY_09_UP},   // S_FLICKY_09_HOP
 	{SPR_FL09, 2, 2, {A_FlickyCheck}, S_FLICKY_09_AIM, S_FLICKY_09_DOWN, S_FLICKY_09_UP},   // S_FLICKY_09_UP
 	{SPR_FL09, 3, 2, {A_FlickyCheck}, S_FLICKY_09_AIM,                0, S_FLICKY_09_DOWN}, // S_FLICKY_09_DOWN
+	{SPR_FL09, FF_ANIMATE|1, -1, {NULL}, 2, 4, S_NULL}, // S_FLICKY_09_STAND
+	{SPR_NULL, 0, 15, {A_FlickyCenter}, MT_FLICKY_09, 384*FRACUNIT, S_FLICKY_09_CENTER},        // S_FLICKY_09_CENTER
 
 	// Puffin
 	{SPR_FL10, 0, 2, {A_FlickyCheck}, S_FLICKY_10_FLAP1, S_FLICKY_10_FLAP1, S_FLICKY_10_OUT},   // S_FLICKY_10_OUT
 	{SPR_FL10, 1, 3, {A_FlickySoar},         4*FRACUNIT,       16*FRACUNIT, S_FLICKY_10_FLAP2}, // S_FLICKY_10_FLAP1
 	{SPR_FL10, 2, 3, {A_FlickySoar},         4*FRACUNIT,       16*FRACUNIT, S_FLICKY_10_FLAP1}, // S_FLICKY_10_FLAP2
+	{SPR_FL10, FF_ANIMATE|1, -1, {NULL}, 1, 3, S_NULL}, // S_FLICKY_10_STAND
+	{SPR_NULL, 0, 15, {A_FlickyCenter}, MT_FLICKY_10, 384*FRACUNIT, S_FLICKY_10_CENTER},        // S_FLICKY_10_CENTER
 
 	// Cow
 	{SPR_FL11, 0, 2, {A_FlickyCheck}, S_FLICKY_11_AIM,           0, S_FLICKY_11_OUT},  // S_FLICKY_11_OUT
@@ -2546,6 +2747,8 @@ state_t states[NUMSTATES] =
 	{SPR_FL11, 1, 3, {A_FlickyHop},        FRACUNIT/2,  2*FRACUNIT, S_FLICKY_11_RUN2}, // S_FLICKY_11_RUN1
 	{SPR_FL11, 2, 4, {A_FlickyHop},        FRACUNIT/2,  2*FRACUNIT, S_FLICKY_11_RUN3}, // S_FLICKY_11_RUN2
 	{SPR_FL11, 3, 4, {A_FlickyHop},        FRACUNIT/2,  2*FRACUNIT, S_FLICKY_11_AIM},  // S_FLICKY_11_RUN3
+	{SPR_FL11, FF_ANIMATE|1, -1, {NULL}, 2, 4, S_NULL}, // S_FLICKY_11_STAND
+	{SPR_NULL, 0, 15, {A_FlickyCenter}, MT_FLICKY_11, 384*FRACUNIT, S_FLICKY_11_CENTER},        // S_FLICKY_11_CENTER
 
 	// Rat
 	{SPR_FL12, 0, 2, {A_FlickyCheck}, S_FLICKY_12_AIM,           0, S_FLICKY_12_OUT},  // S_FLICKY_12_OUT
@@ -2553,19 +2756,25 @@ state_t states[NUMSTATES] =
 	{SPR_FL12, 1, 2, {A_FlickyHop},                 1, 12*FRACUNIT, S_FLICKY_12_RUN2}, // S_FLICKY_12_RUN1
 	{SPR_FL12, 2, 3, {A_FlickyHop},                 1, 12*FRACUNIT, S_FLICKY_12_RUN3}, // S_FLICKY_12_RUN2
 	{SPR_FL12, 3, 3, {A_FlickyHop},                 1, 12*FRACUNIT, S_FLICKY_12_AIM},  // S_FLICKY_12_RUN3
+	{SPR_FL12, FF_ANIMATE|1, -1, {NULL}, 2, 4, S_NULL}, // S_FLICKY_12_STAND
+	{SPR_NULL, 0, 15, {A_FlickyCenter}, MT_FLICKY_12, 384*FRACUNIT, S_FLICKY_12_CENTER},        // S_FLICKY_12_CENTER
 
 	// Bear
-	{SPR_FL13, 0, 2, {A_FlickyCheck}, S_FLICKY_13_AIM,                0, S_FLICKY_13_OUT}, // S_FLICKY_13_OUT
-	{SPR_FL13, 1, 1, {A_FlickyAim},             ANG30,      32*FRACUNIT, S_FLICKY_13_HOP}, // S_FLICKY_13_AIM
-	{SPR_FL13, 1, 1, {A_FlickyHop},        5*FRACUNIT,       3*FRACUNIT, S_FLICKY_13_UP}, // S_FLICKY_13_HOP
-	{SPR_FL13, 2, 2, {A_FlickyCheck}, S_FLICKY_13_AIM, S_FLICKY_13_DOWN, S_FLICKY_13_UP}, // S_FLICKY_13_UP
+	{SPR_FL13, 0, 2, {A_FlickyCheck}, S_FLICKY_13_AIM,                0, S_FLICKY_13_OUT},  // S_FLICKY_13_OUT
+	{SPR_FL13, 1, 1, {A_FlickyAim},             ANG30,      32*FRACUNIT, S_FLICKY_13_HOP},  // S_FLICKY_13_AIM
+	{SPR_FL13, 1, 1, {A_FlickyHop},        5*FRACUNIT,       3*FRACUNIT, S_FLICKY_13_UP},   // S_FLICKY_13_HOP
+	{SPR_FL13, 2, 2, {A_FlickyCheck}, S_FLICKY_13_AIM, S_FLICKY_13_DOWN, S_FLICKY_13_UP},   // S_FLICKY_13_UP
 	{SPR_FL13, 3, 2, {A_FlickyCheck}, S_FLICKY_13_AIM,                0, S_FLICKY_13_DOWN}, // S_FLICKY_13_DOWN
+	{SPR_FL13, FF_ANIMATE|1, -1, {NULL}, 2, 4, S_NULL}, // S_FLICKY_13_STAND
+	{SPR_NULL, 0, 15, {A_FlickyCenter}, MT_FLICKY_13, 384*FRACUNIT, S_FLICKY_13_CENTER},        // S_FLICKY_13_CENTER
 
 	// Dove
 	{SPR_FL14, 0, 2, {A_FlickyCheck}, S_FLICKY_14_FLAP1, S_FLICKY_14_FLAP1, S_FLICKY_14_OUT},   // S_FLICKY_14_OUT
 	{SPR_FL14, 1, 3, {A_FlickySoar},         4*FRACUNIT,       32*FRACUNIT, S_FLICKY_14_FLAP2}, // S_FLICKY_14_FLAP1
 	{SPR_FL14, 2, 3, {A_FlickySoar},         4*FRACUNIT,       32*FRACUNIT, S_FLICKY_14_FLAP3}, // S_FLICKY_14_FLAP2
 	{SPR_FL14, 3, 3, {A_FlickySoar},         4*FRACUNIT,       32*FRACUNIT, S_FLICKY_14_FLAP1}, // S_FLICKY_14_FLAP3
+	{SPR_FL14, FF_ANIMATE|1, -1, {NULL}, 2, 3, S_NULL}, // S_FLICKY_14_STAND
+	{SPR_NULL, 0, 15, {A_FlickyCenter}, MT_FLICKY_14, 384*FRACUNIT, S_FLICKY_14_CENTER},        // S_FLICKY_14_CENTER
 
 	// Cat
 	{SPR_FL15, 0, 2, {A_FlickyCheck}, S_FLICKY_15_AIM,                0, S_FLICKY_15_OUT},  // S_FLICKY_15_OUT
@@ -2573,12 +2782,63 @@ state_t states[NUMSTATES] =
 	{SPR_FL15, 1, 1, {A_FlickyFlounder},   2*FRACUNIT,       6*FRACUNIT, S_FLICKY_15_UP},   // S_FLICKY_15_HOP
 	{SPR_FL15, 2, 2, {A_FlickyCheck}, S_FLICKY_15_AIM, S_FLICKY_15_DOWN, S_FLICKY_15_UP},   // S_FLICKY_15_UP
 	{SPR_FL15, 3, 2, {A_FlickyCheck}, S_FLICKY_15_AIM,                0, S_FLICKY_15_DOWN}, // S_FLICKY_15_DOWN
+	{SPR_FL15, FF_ANIMATE|1, -1, {NULL}, 2, 4, S_NULL}, // S_FLICKY_15_STAND
+	{SPR_NULL, 0, 15, {A_FlickyCenter}, MT_FLICKY_15, 384*FRACUNIT, S_FLICKY_15_CENTER},        // S_FLICKY_15_CENTER
 
 	// Canary
 	{SPR_FL16, 0, 2, {A_FlickyHeightCheck}, S_FLICKY_16_FLAP1,          0, S_FLICKY_16_OUT},   // S_FLICKY_16_OUT
 	{SPR_FL16, 1, 3, {A_FlickyFly},                4*FRACUNIT, 8*FRACUNIT, S_FLICKY_16_FLAP2}, // S_FLICKY_16_FLAP1
 	{SPR_FL16, 2, 3, {A_SetObjectFlags},         MF_NOGRAVITY,          1, S_FLICKY_16_FLAP3}, // S_FLICKY_16_FLAP2
 	{SPR_FL16, 3, 3, {A_FlickyHeightCheck}, S_FLICKY_16_FLAP1,          0, S_FLICKY_16_FLAP3}, // S_FLICKY_16_FLAP3
+	{SPR_FL16, FF_ANIMATE|1, -1, {NULL}, 2, 3, S_NULL}, // S_FLICKY_16_STAND
+	{SPR_NULL, 0, 15, {A_FlickyCenter}, MT_FLICKY_16, 384*FRACUNIT, S_FLICKY_16_CENTER},        // S_FLICKY_16_CENTER
+
+	// Spider
+	{SPR_FS01, 0, 2, {A_FlickyCheck}, S_SECRETFLICKY_01_AIM,                      0, S_SECRETFLICKY_01_OUT},  // S_SECRETFLICKY_01_OUT
+	{SPR_FS01, 1, 1, {A_FlickyAim},                   ANG30,            32*FRACUNIT, S_SECRETFLICKY_01_HOP},  // S_SECRETFLICKY_01_AIM
+	{SPR_FS01, 1, 1, {A_FlickyFlounder},         2*FRACUNIT,             6*FRACUNIT, S_SECRETFLICKY_01_UP},   // S_SECRETFLICKY_01_HOP
+	{SPR_FS01, 2, 2, {A_FlickyCheck}, S_SECRETFLICKY_01_AIM, S_SECRETFLICKY_01_DOWN, S_SECRETFLICKY_01_UP},   // S_SECRETFLICKY_01_UP
+	{SPR_FS01, 3, 2, {A_FlickyCheck}, S_SECRETFLICKY_01_AIM,                      0, S_SECRETFLICKY_01_DOWN}, // S_SECRETFLICKY_01_DOWN
+	{SPR_FS01, FF_ANIMATE|1, -1, {NULL}, 2, 4, S_NULL}, // S_SECRETFLICKY_01_STAND
+	{SPR_NULL, 0, 15, {A_FlickyCenter}, MT_SECRETFLICKY_01, 384*FRACUNIT, S_SECRETFLICKY_01_CENTER},        // S_SECRETFLICKY_01_CENTER
+
+	// Bat
+	{SPR_FS02, 0, 2, {A_FlickyHeightCheck}, S_SECRETFLICKY_02_FLAP1, S_SECRETFLICKY_02_FLAP1, S_SECRETFLICKY_02_OUT},   // S_SECRETFLICKY_02_OUT
+	{SPR_FS02, 1, 3, {A_FlickyFly},                      4*FRACUNIT,             16*FRACUNIT, S_SECRETFLICKY_02_FLAP2}, // S_SECRETFLICKY_02_FLAP1
+	{SPR_FS02, 2, 3, {A_FlickyFly},                      4*FRACUNIT,             16*FRACUNIT, S_SECRETFLICKY_02_FLAP3}, // S_SECRETFLICKY_02_FLAP2
+	{SPR_FS02, 3, 3, {A_FlickyFly},                      4*FRACUNIT,             16*FRACUNIT, S_SECRETFLICKY_02_FLAP1}, // S_SECRETFLICKY_02_FLAP3
+	{SPR_FS02, FF_ANIMATE|1, -1, {NULL}, 2, 2, S_NULL}, // S_SECRETFLICKY_02_STAND
+	{SPR_NULL, 0, 15, {A_FlickyCenter}, MT_SECRETFLICKY_02, 384*FRACUNIT, S_SECRETFLICKY_02_CENTER},        // S_SECRETFLICKY_02_CENTER
+
+	// Fan
+	{SPR_FANS, 0, 1, {A_FanBubbleSpawn}, 2048, 0, S_FAN2}, // S_FAN
+	{SPR_FANS, 1, 1, {A_FanBubbleSpawn}, 1024, 0, S_FAN3}, // S_FAN2
+	{SPR_FANS, 2, 1, {A_FanBubbleSpawn},  512, 0, S_FAN4}, // S_FAN3
+	{SPR_FANS, 3, 1, {A_FanBubbleSpawn}, 1024, 0, S_FAN5}, // S_FAN4
+	{SPR_FANS, 4, 1, {A_FanBubbleSpawn},  512, 0, S_FAN},  // S_FAN5
+
+	// Steam Riser
+	{SPR_STEM, 0, 2, {A_SetSolidSteam}, 0, 0, S_STEAM2},   // S_STEAM1
+	{SPR_STEM, 1, 2, {A_UnsetSolidSteam}, 0, 0, S_STEAM3}, // S_STEAM2
+	{SPR_STEM, 2, 2, {NULL}, 0, 0, S_STEAM4},              // S_STEAM3
+	{SPR_STEM, 3, 2, {NULL}, 0, 0, S_STEAM5},              // S_STEAM4
+	{SPR_STEM, 4, 2, {NULL}, 0, 0, S_STEAM6},              // S_STEAM5
+	{SPR_STEM, 5, 2, {NULL}, 0, 0, S_STEAM7},              // S_STEAM6
+	{SPR_STEM, 6, 2, {NULL}, 0, 0, S_STEAM8},              // S_STEAM7
+	{SPR_NULL, 0, 18, {NULL}, 0, 0, S_STEAM1},             // S_STEAM8
+
+	// Bumpers
+	{SPR_BUMP, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL},   3, 4, S_NULL},   // S_BUMPER
+	{SPR_BUMP, FF_ANIMATE|4,             12, {A_Pain}, 1, 3, S_BUMPER}, //S_BUMPERHIT
+
+	// Balloons
+	{SPR_BLON, FF_ANIMATE, -1, {NULL}, 2, 5, S_NULL}, // S_BALLOON
+	{SPR_BLON, 3, 0, {A_RemoteDamage},   0, 1, S_BALLOONPOP2}, // S_BALLOONPOP1
+	{SPR_BLON, 3, 1, {A_Pain},           0, 0, S_BALLOONPOP3}, // S_BALLOONPOP2
+	{SPR_BLON, 4, 1, {NULL},             0, 0, S_BALLOONPOP4}, // S_BALLOONPOP3
+	{SPR_NULL, 0, TICRATE, {A_CheckFlags2}, MF2_AMBUSH, S_BALLOONPOP5, S_NULL}, // S_BALLOONPOP4
+	{SPR_NULL, 0, 15*TICRATE, {NULL},    0, 0, S_BALLOONPOP6}, // S_BALLOONPOP5
+	{SPR_NULL, 0, 0, {A_SpawnFreshCopy}, 0, 0, S_NULL},        // S_BALLOONPOP6
 
 	// Yellow Spring
 	{SPR_SPRY, 0, -1, {NULL}, 0, 0, S_NULL},           // S_YELLOWSPRING
@@ -2621,6 +2881,16 @@ state_t states[NUMSTATES] =
 	{SPR_RSPR, 2, 1, {NULL}, 0, 0, S_RDIAG8},   // S_RDIAG7
 	{SPR_RSPR, 1, 1, {NULL}, 0, 0, S_RDIAG1},   // S_RDIAG8
 
+	// Blue Diagonal Spring
+	{SPR_BSPR, 0, -1, {NULL}, 0, 0, S_NULL},    // S_BDIAG1
+	{SPR_BSPR, 1, 1, {A_Pain}, 0, 0, S_BDIAG3}, // S_BDIAG2
+	{SPR_BSPR, 2, 1, {NULL}, 0, 0, S_BDIAG4},   // S_BDIAG3
+	{SPR_BSPR, 3, 1, {NULL}, 0, 0, S_BDIAG5},   // S_BDIAG4
+	{SPR_BSPR, 4, 1, {NULL}, 0, 0, S_BDIAG6},   // S_BDIAG5
+	{SPR_BSPR, 3, 1, {NULL}, 0, 0, S_BDIAG7},   // S_BDIAG6
+	{SPR_BSPR, 2, 1, {NULL}, 0, 0, S_BDIAG8},   // S_BDIAG7
+	{SPR_BSPR, 1, 1, {NULL}, 0, 0, S_BDIAG1},   // S_BDIAG8
+
 	// Yellow Side Spring
 	{SPR_SSWY, 0, -1, {NULL}, 0, 0, S_NULL},    // S_YHORIZ1
 	{SPR_SSWY, 1, 1, {A_Pain}, 0, 0, S_YHORIZ3}, // S_YHORIZ2
@@ -2731,8 +3001,7 @@ state_t states[NUMSTATES] =
 	{SPR_SEED, FF_FULLBRIGHT|FF_ANIMATE, -1, {NULL}, 2, 2, S_NULL}, // S_SEED
 
 	// Particle sprite
-	{SPR_PRTL, FF_FULLBRIGHT|FF_TRANS70, 2*TICRATE, {NULL}, 0, 0, S_NULL}, // S_PARTICLE
-	{SPR_NULL,     0,          3, {A_ParticleSpawn}, 0, 0, S_PARTICLEGEN}, // S_PARTICLEGEN
+	{SPR_PRTL, 0, 2*TICRATE, {NULL}, 0, 0, S_NULL}, // S_PARTICLE
 
 	{SPR_SCOR, 0, 32, {A_ScoreRise}, 0, 0, S_NULL}, // S_SCRA  - 100
 	{SPR_SCOR, 1, 32, {A_ScoreRise}, 0, 0, S_NULL}, // S_SCRB  - 200
@@ -2745,6 +3014,7 @@ state_t states[NUMSTATES] =
 	{SPR_SCOR, 8, 32, {A_ScoreRise}, 0, 0, S_NULL}, // S_SCRI  - 4000 (mario mode)
 	{SPR_SCOR, 9, 32, {A_ScoreRise}, 0, 0, S_NULL}, // S_SCRJ  - 8000 (mario mode)
 	{SPR_SCOR, 10, 32, {A_ScoreRise}, 0, 0, S_NULL}, // S_SCRK - 1UP (mario mode)
+	{SPR_SCOR, 11, 32, {A_ScoreRise}, 0, 0, S_NULL}, // S_SCRL - 10
 
 	// Drowning Timer Numbers
 	{SPR_DRWN, 0, 40, {NULL}, 0, 0, S_NULL}, // S_ZERO1
@@ -3089,9 +3359,6 @@ state_t states[NUMSTATES] =
 	{SPR_NSCR, FF_FULLBRIGHT|18, -1, {NULL}, 0, 0, S_NULL}, // S_NIGHTSCORE90_2
 	{SPR_NSCR, FF_FULLBRIGHT|19, -1, {NULL}, 0, 0, S_NULL}, // S_NIGHTSCORE100_2
 
-	{SPR_NWNG, 0, -1, {NULL}, 0, 0, S_NULL}, // S_NIGHTSWING
-	{SPR_NWNG, 1, -1, {NULL}, 0, 0, S_NULL}, // S_NIGHTSWING_XMAS
-
 	// NiGHTS Paraloop Powerups
 	{SPR_NPRU, 0, -1, {NULL}, 0, 0, S_NULL}, // S_NIGHTSSUPERLOOP
 	{SPR_NPRU, 1, -1, {NULL}, 0, 0, S_NULL}, // S_NIGHTSDRILLREFILL
@@ -3101,7 +3368,7 @@ state_t states[NUMSTATES] =
 
 	{SPR_CAPS, 0, -1, {NULL}, 0, 0, S_NULL}, // S_EGGCAPSULE
 
-	// Orbiting Chaos Emeralds for NiGHTS
+	// Orbiting Chaos Emeralds/Ideya for NiGHTS
 	{SPR_CEMG, FF_FULLBRIGHT,   1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM1}, // S_ORBITEM1
 	{SPR_CEMG, FF_FULLBRIGHT|1, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM2}, // S_ORBITEM2
 	{SPR_CEMG, FF_FULLBRIGHT|2, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM3}, // S_ORBITEM3
@@ -3110,14 +3377,11 @@ state_t states[NUMSTATES] =
 	{SPR_CEMG, FF_FULLBRIGHT|5, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM6}, // S_ORBITEM6
 	{SPR_CEMG, FF_FULLBRIGHT|6, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM7}, // S_ORBITEM7
 	{SPR_CEMG, FF_FULLBRIGHT|7, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM8}, // S_ORBITEM8
-	{SPR_CEMG, FF_FULLBRIGHT|8, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM8}, // S_ORBITEM9
-	{SPR_CEMG, FF_FULLBRIGHT|9, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM8}, // S_ORBITEM10
-	{SPR_CEMG, FF_FULLBRIGHT|10, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM8}, // S_ORBITEM11
-	{SPR_CEMG, FF_FULLBRIGHT|11, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM8}, // S_ORBITEM12
-	{SPR_CEMG, FF_FULLBRIGHT|12, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM8}, // S_ORBITEM13
-	{SPR_CEMG, FF_FULLBRIGHT|13, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM8}, // S_ORBITEM14
-	{SPR_CEMG, FF_FULLBRIGHT|14, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM8}, // S_ORBITEM15
-	{SPR_CEMG, FF_FULLBRIGHT|15, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM8}, // S_ORBITEM16
+	{SPR_IDYA, FF_TRANS20|FF_FULLBRIGHT,   1, {A_OrbitNights}, ANG2*2, 0, S_ORBIDYA1}, // S_ORBIDYA1
+	{SPR_IDYA, FF_TRANS20|FF_FULLBRIGHT|1, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBIDYA2}, // S_ORBIDYA2
+	{SPR_IDYA, FF_TRANS20|FF_FULLBRIGHT|2, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBIDYA3}, // S_ORBIDYA3
+	{SPR_IDYA, FF_TRANS20|FF_FULLBRIGHT|3, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBIDYA4}, // S_ORBIDYA4
+	{SPR_IDYA, FF_TRANS20|FF_FULLBRIGHT|4, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBIDYA5}, // S_ORBIDYA5
 
 	// Flicky helper for NiGHTS
 	{SPR_FL01, 1, 1, {A_OrbitNights}, ANG2*2, 180 | 0x10000, S_NIGHTOPIANHELPER2}, // S_NIGHTOPIANHELPER1
@@ -3130,7 +3394,143 @@ state_t states[NUMSTATES] =
 	{SPR_FL01, 3, 1, {A_OrbitNights}, ANG2*2, 180 | 0x10000, S_NIGHTOPIANHELPER9}, // S_NIGHTOPIANHELPER8
 	{SPR_FL01, 3, 1, {A_OrbitNights}, ANG2*2, 180 | 0x10000, S_NIGHTOPIANHELPER1}, // S_NIGHTOPIANHELPER9
 
-	{SPR_NULL, 0, 35, {NULL}, 0, 0, S_CRUMBLE2},  // S_CRUMBLE1
+	// Nightopian
+	{SPR_NTPN, 0, 4, {A_Look}, 0, 0, S_PIAN0}, // S_PIAN0
+	{SPR_NTPN, 0, 4, {A_JetgThink}, 0, 0, S_PIAN2}, // S_PIAN1
+	{SPR_NTPN, 1, 4, {NULL}, 0, 0, S_PIAN3}, // S_PIAN2
+	{SPR_NTPN, 2, 4, {NULL}, 0, 0, S_PIAN4}, // S_PIAN3
+	{SPR_NTPN, 3, 4, {NULL}, 0, 0, S_PIAN5}, // S_PIAN4
+	{SPR_NTPN, 2, 4, {NULL}, 0, 0, S_PIAN6}, // S_PIAN5
+	{SPR_NTPN, 1, 4, {NULL}, 0, 0, S_PIAN1}, // S_PIAN6
+	{SPR_NTPN, 5|FF_ANIMATE, 4, {NULL}, 1, 4, S_PIAN1}, // S_PIANSING
+
+	// Shleep
+	{SPR_SHLP, 0, 15, {NULL}, 0, 0, S_SHLEEP2}, // S_SHLEEP1
+	{SPR_SHLP, 1, 15, {NULL}, 0, 0, S_SHLEEP3}, // S_SHLEEP2
+	{SPR_SHLP, 2, 15, {NULL}, 0, 0, S_SHLEEP4}, // S_SHLEEP3
+	{SPR_SHLP, 1, 15, {NULL}, 0, 0, S_SHLEEP1}, // S_SHLEEP4
+	{SPR_SHLP, 3, 1, {A_Scream},  0, 0, S_SHLEEPBOUNCE2}, // S_SHLEEPBOUNCE1
+	{SPR_SHLP, 3, 1, {A_ZThrust}, 9, 0, S_SHLEEPBOUNCE3}, // S_SHLEEPBOUNCE2
+	{SPR_SHLP, 3, 400, {A_SetObjectFlags}, MF_SLIDEME|MF_ENEMY|MF_BOUNCE|MF_NOCLIP|MF_NOCLIPHEIGHT, 0, S_NULL}, // S_SHLEEPBOUNCE3
+
+	// Secret badniks and hazards, shhhh
+	{SPR_PENG, 0, 2, {A_Look},  0, 0, S_PENGUINATOR_LOOK},    // S_PENGUINATOR_LOOK
+	{SPR_PENG, 0, 2, {A_Chase}, 0, 0, S_PENGUINATOR_WADDLE2}, // S_PENGUINATOR_WADDLE1
+	{SPR_PENG, 1, 2, {A_Chase}, 0, 0, S_PENGUINATOR_WADDLE3}, // S_PENGUINATOR_WADDLE2
+	{SPR_PENG, 0, 2, {A_Chase}, 0, 0, S_PENGUINATOR_WADDLE4}, // S_PENGUINATOR_WADDLE3
+	{SPR_PENG, 2, 2, {A_Chase}, 0, 0, S_PENGUINATOR_WADDLE1}, // S_PENGUINATOR_WADDLE4
+	{SPR_PENG, 0,  0, {A_FaceTarget},      0,  0, S_PENGUINATOR_SLIDE2}, // S_PENGUINATOR_SLIDE1
+	{SPR_PENG, 3,  5, {A_BunnyHop},        4, 10, S_PENGUINATOR_SLIDE3}, // S_PENGUINATOR_SLIDE2
+	{SPR_PENG, 4, 90, {A_PlayAttackSound}, 0,  0, S_PENGUINATOR_SLIDE4}, // S_PENGUINATOR_SLIDE3
+	{SPR_PENG, 3,  5, {A_Thrust},          0,  1, S_PENGUINATOR_SLIDE5}, // S_PENGUINATOR_SLIDE4
+	{SPR_PENG, 0,  5, {A_FaceTarget},      0,  0, S_PENGUINATOR_LOOK},   // S_PENGUINATOR_SLIDE5
+
+	{SPR_POPH, 0,  2, {A_Look},  (2048<<16)|1,           0, S_POPHAT_LOOK},   // S_POPHAT_LOOK
+	{SPR_POPH, 1,  2, {A_LobShot}, MT_POPSHOT, (70<<16)|60, S_POPHAT_SHOOT2}, // S_POPHAT_SHOOT1
+	{SPR_POPH, 2,  1, {NULL},               0,           0, S_POPHAT_SHOOT3}, // S_POPHAT_SHOOT2
+	{SPR_POPH, 0, 57, {NULL},               0,           0, S_POPHAT_LOOK},   // S_POPHAT_SHOOT3
+
+	{SPR_HIVE, 0,  5, {A_Look}, 1, 1, S_HIVEELEMENTAL_LOOK}, // S_HIVEELEMENTAL_LOOK
+	{SPR_HIVE, 0, 14, {A_PlaySound}, sfx_s3k76, 1, S_HIVEELEMENTAL_PREPARE2}, // S_HIVEELEMENTAL_PREPARE1
+	{SPR_HIVE, 0,  6, {A_PlaySound}, sfx_s3k8c, 1, S_HIVEELEMENTAL_SHOOT1}, // S_HIVEELEMENTAL_PREPARE2
+	{SPR_HIVE, 1,  4, {A_WhoCaresIfYourSonIsABee}, (MT_BUMBLEBORE<<16)|4, (1<<16)|32, S_HIVEELEMENTAL_SHOOT2}, // S_HIVEELEMENTAL_SHOOT1
+	{SPR_HIVE, 2,  2, {NULL}, 0, 0, S_HIVEELEMENTAL_DORMANT}, // S_HIVEELEMENTAL_SHOOT2
+	{SPR_HIVE, 0,  5, {A_ParentTriesToSleep}, S_HIVEELEMENTAL_PREPARE1, 0, S_HIVEELEMENTAL_DORMANT}, // S_HIVEELEMENTAL_DORMANT
+	{SPR_HIVE, 3, 35, {A_Pain}, 0, 0, S_HIVEELEMENTAL_LOOK}, // S_HIVEELEMENTAL_PAIN
+	{SPR_HIVE, 3,  2, {A_BossScream}, 1, MT_SONIC3KBOSSEXPLODE, S_HIVEELEMENTAL_DIE2}, // S_HIVEELEMENTAL_DIE1
+	{SPR_NULL, 0,  2, {A_BossScream}, 1, MT_SONIC3KBOSSEXPLODE, S_HIVEELEMENTAL_DIE3}, // S_HIVEELEMENTAL_DIE2
+	{SPR_NULL, 0,  0, {A_Repeat}, 7, S_HIVEELEMENTAL_DIE1, S_XPLD_FLICKY}, // S_HIVEELEMENTAL_DIE3
+
+	{SPR_BUMB, 1, 10, {NULL}, 0, 0, S_BUMBLEBORE_LOOK1}, // S_BUMBLEBORE_SPAWN
+	{SPR_BUMB, 0,  4, {A_Look}, 1, 1, S_BUMBLEBORE_LOOK2}, // S_BUMBLEBORE_LOOK1
+	{SPR_BUMB, 1,  4, {A_Look}, 1, 1, S_BUMBLEBORE_LOOK1}, // S_BUMBLEBORE_LOOK2
+	{SPR_BUMB, 0,  4, {A_JetbThink}, 0, 0, S_BUMBLEBORE_FLY2}, // S_BUMBLEBORE_FLY1
+	{SPR_BUMB, 1,  4, {A_JetbThink}, 0, 0, S_BUMBLEBORE_FLY1}, // S_BUMBLEBORE_FLY2
+	{SPR_BUMB, 2|FF_FULLBRIGHT,  12, {A_ZThrust},  4, (1<<16)|1, S_BUMBLEBORE_FALL1},  // S_BUMBLEBORE_RAISE
+	{SPR_BUMB, 2|FF_FULLBRIGHT,   0, {A_ZThrust}, -8, (1<<16)|1, S_BUMBLEBORE_FALL2},  // S_BUMBLEBORE_FALL1
+	{SPR_BUMB, 2|FF_FULLBRIGHT, 300, {NULL},       0,         0, S_BUMBLEBORE_DIE},    // S_BUMBLEBORE_FALL2
+	{SPR_BUMB, 4, 3, {A_MultiShotDist}, (MT_DUST<<16)|6, -40, S_BUMBLEBORE_STUCK2},    // S_BUMBLEBORE_STUCK1
+	{SPR_BUMB, 5, 120, {NULL}, 0, 0, S_BUMBLEBORE_DIE}, // S_BUMBLEBORE_STUCK2
+	{SPR_BUMB, 5, 0, {A_CryingToMomma}, 0, 0, S_XPLD1}, // S_BUMBLEBORE_DIE
+
+	{SPR_BBUZ, 0, 2, {NULL}, 0, 0, S_BBUZZFLY2}, // S_BBUZZFLY1
+	{SPR_BBUZ, 1, 2, {NULL}, 0, 0, S_BBUZZFLY1}, // S_BBUZZFLY2
+
+	{SPR_FMCE, 0, 20, {NULL}, 0, 0, S_SMASHSPIKE_EASE1}, // S_SMASHSPIKE_FLOAT
+	{SPR_FMCE, 0,  4, {A_ZThrust},  4, (1<<16)|1, S_SMASHSPIKE_EASE2}, // S_SMASHSPIKE_EASE1
+	{SPR_FMCE, 0,  4, {A_ZThrust},  0, (1<<16)|1, S_SMASHSPIKE_FALL},  // S_SMASHSPIKE_EASE2
+	{SPR_FMCE, 0,  2, {A_ZThrust}, -6,         1, S_SMASHSPIKE_FALL},  // S_SMASHSPIKE_FALL
+	{SPR_FMCE, 1,  2, {A_MultiShotDist}, (MT_DUST<<16)|10, -48, S_SMASHSPIKE_STOMP2}, // S_SMASHSPIKE_STOMP1
+	{SPR_FMCE, 2, 14, {NULL}, 0, 0, S_SMASHSPIKE_RISE1}, // S_SMASHSPIKE_STOMP2
+	{SPR_FMCE, 1,  2, {NULL}, 0, 0, S_SMASHSPIKE_RISE2}, // S_SMASHSPIKE_RISE1
+	{SPR_FMCE, 0,  2, {A_ZThrust}, 6, (1<<16)|1, S_SMASHSPIKE_RISE2}, // S_SMASHSPIKE_RISE2
+
+	{SPR_CACO, 0,  5, {A_Look}, (1100<<16)|1, 0, S_CACO_LOOK}, // S_CACO_LOOK
+	{SPR_CACO, 1,  0, {A_MultiShotDist}, (MT_DUST<<16)|7, -48, S_CACO_WAKE2}, // S_CACO_WAKE1
+	{SPR_CACO, 1, 10, {A_ZThrust}, 4, (1<<16)|1, S_CACO_WAKE3}, // S_CACO_WAKE2
+	{SPR_CACO, 2,  8, {A_ZThrust}, 2, (1<<16)|1, S_CACO_WAKE4}, // S_CACO_WAKE3
+	{SPR_CACO, 2,  4, {A_ZThrust}, 0, (1<<16)|1, S_CACO_ROAR},  // S_CACO_WAKE4
+	{SPR_CACO, 2, 10, {A_PlayActiveSound}, 0, 0, S_CACO_CHASE}, // S_CACO_ROAR
+	{SPR_CACO, 2,  5, {A_JetChase}, 0, 0, S_CACO_CHASE_REPEAT}, // S_CACO_CHASE
+	{SPR_CACO, 2,  0, {A_Repeat}, 5, S_CACO_CHASE, S_CACO_RANDOM}, // S_CACO_CHASE_REPEAT
+	{SPR_CACO, 2,  0, {A_RandomState}, S_CACO_PREPARE_SOUND, S_CACO_CHASE, S_CACO_RANDOM}, // S_CACO_RANDOM
+	{SPR_CACO, 2,  8, {A_PlaySound}, sfx_s3k95, 1, S_CACO_PREPARE1},  // S_CACO_PREPARE_SOUND
+	{SPR_CACO, 3,               8, {NULL}, 0, 0, S_CACO_PREPARE2},    // S_CACO_PREPARE1
+	{SPR_CACO, 4|FF_FULLBRIGHT, 8, {NULL}, 0, 0, S_CACO_PREPARE3},    // S_CACO_PREPARE2
+	{SPR_CACO, 5|FF_FULLBRIGHT, 8, {NULL}, 0, 0, S_CACO_SHOOT_SOUND}, // S_CACO_PREPARE3
+	{SPR_CACO, 4|FF_FULLBRIGHT, 0, {A_PlaySound}, sfx_s3k4e, 1, S_CACO_SHOOT1}, // S_CACO_SHOOT_SOUND
+	{SPR_CACO, 4|FF_FULLBRIGHT, 0, {A_SpawnParticleRelative}, 0, S_CACOFIRE_EXPLODE1, S_CACO_SHOOT2}, // S_CACO_SHOOT1
+	{SPR_CACO, 4|FF_FULLBRIGHT, 6, {A_FireShot}, MT_CACOFIRE, -24, S_CACO_CLOSE}, // S_CACO_SHOOT2
+	{SPR_CACO, 3,              15, {NULL}, 0, 0, S_CACO_CHASE}, // S_CACO_CLOSE
+	{SPR_CACO, 10, 0, {A_SetObjectFlags}, MF_NOBLOCKMAP, 0, S_CACO_DIE_GIB1}, // S_CACO_DIE_FLAGS
+	{SPR_CACO, 10, 0, {A_NapalmScatter}, (7<<16)|MT_CACOSHARD, (30<<16)|20, S_CACO_DIE_GIB2}, // S_CACO_DIE_GIB1
+	{SPR_CACO, 10, 0, {A_NapalmScatter}, (10<<16)|MT_CACOSHARD, (24<<16)|32, S_CACO_DIE_SCREAM}, // S_CACO_DIE_GIB2
+	{SPR_CACO, 10, 0, {A_Scream}, 0, 0, S_CACO_DIE_SHATTER}, // S_CACO_DIE_SCREAM
+	{SPR_CACO, 10, 0, {A_PlaySound}, sfx_pumpkn, 1, S_CACO_DIE_FALL}, // S_CACO_DIE_SHATTER
+	{SPR_CACO, 10, 250, {A_FlickySpawn}, (1<<16), 0, S_NULL}, // S_CACO_DIE_FALL
+
+	{SPR_CACO, 6, 0, {A_RandomState}, S_CACOSHARD1_1, S_CACOSHARD2_1, S_NULL}, // S_CACOSHARD_RANDOMIZE
+	{SPR_CACO, 6, 3, {NULL}, 0, 0, S_CACOSHARD1_2}, // S_CACOSHARD1_1
+	{SPR_CACO, 7, 3, {NULL}, 0, 0, S_CACOSHARD1_1}, // S_CACOSHARD1_2
+	{SPR_CACO, 8, 3, {NULL}, 0, 0, S_CACOSHARD2_2}, // S_CACOSHARD2_1
+	{SPR_CACO, 9, 3, {NULL}, 0, 0, S_CACOSHARD2_1}, // S_CACOSHARD2_2
+	{SPR_BAL2,   FF_FULLBRIGHT, 2, {A_GhostMe}, 0, 0, S_CACOFIRE2}, // S_CACOFIRE1
+	{SPR_BAL2, 1|FF_FULLBRIGHT, 2, {A_GhostMe}, 0, 0, S_CACOFIRE3}, // S_CACOFIRE2
+	{SPR_BAL2,   FF_FULLBRIGHT, 0, {A_PlayActiveSound}, 0, 0, S_CACOFIRE1}, // S_CACOFIRE3
+	{SPR_BAL2, 2|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_CACOFIRE_EXPLODE2}, // S_CACOFIRE_EXPLODE1
+	{SPR_BAL2, 3|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_CACOFIRE_EXPLODE3}, // S_CACOFIRE_EXPLODE2
+	{SPR_BAL2, 4|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_CACOFIRE_EXPLODE4}, // S_CACOFIRE_EXPLODE3
+	{SPR_BAL2, 5|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL},      // S_CACOFIRE_EXPLODE4
+
+	{SPR_SBOB, 1, 10, {A_ZThrust}, -2, (1<<16)|1, S_SPINBOBERT_MOVE_UP},       // S_SPINBOBERT_MOVE_FLIPUP
+	{SPR_SBOB, 0, 45, {A_ZThrust},  4, (1<<16)|1, S_SPINBOBERT_MOVE_FLIPDOWN}, // S_SPINBOBERT_MOVE_UP
+	{SPR_SBOB, 1, 10, {A_ZThrust},  2, (1<<16)|1, S_SPINBOBERT_MOVE_DOWN},     // S_SPINBOBERT_MOVE_FLIPDOWN
+	{SPR_SBOB, 2, 45, {A_ZThrust}, -4, (1<<16)|1, S_SPINBOBERT_MOVE_FLIPUP},   // S_SPINBOBERT_MOVE_DOWN
+	{SPR_SBSK, FF_FULLBRIGHT, 1, {A_RotateSpikeBall},       0,                        0, S_SPINBOBERT_FIRE_GHOST}, // S_SPINBOBERT_FIRE_MOVE
+	{SPR_SBSK, FF_FULLBRIGHT, 0, {A_SpawnParticleRelative}, 0, S_SPINBOBERT_FIRE_TRAIL1, S_SPINBOBERT_FIRE_MOVE},  // S_SPINBOBERT_FIRE_GHOST
+	{SPR_SBFL, 2|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_SPINBOBERT_FIRE_TRAIL2}, // S_SPINBOBERT_FIRE_TRAIL1
+	{SPR_SBFL, 1|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_SPINBOBERT_FIRE_TRAIL3}, // S_SPINBOBERT_FIRE_TRAIL2
+	{SPR_SBFL,   FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL},                   // S_SPINBOBERT_FIRE_TRAIL3
+
+	{SPR_HBAT, 0,  5, {A_Look}, (900<<16)|1, 0, S_HANGSTER_LOOK}, // S_HANGSTER_LOOK
+	{SPR_HBAT, 1,  0, {A_MultiShotDist}, (MT_DUST<<16)|10, -34, S_HANGSTER_SWOOP2}, // S_HANGSTER_SWOOP1
+	{SPR_HBAT, 1,  2, {A_ZThrust}, -8, (1<<16)|1, S_HANGSTER_SWOOP2}, // S_HANGSTER_SWOOP2
+	{SPR_HBAT, 1,  6, {A_ZThrust}, -5, (1<<16), S_HANGSTER_ARC2}, // S_HANGSTER_ARC1
+	{SPR_HBAT, 1,  5, {A_ZThrust}, -2, (1<<16), S_HANGSTER_ARC3}, // S_HANGSTER_ARC2
+	{SPR_HBAT, 1,  1, {A_ZThrust},  0, (1<<16), S_HANGSTER_FLY1}, // S_HANGSTER_ARC3
+	{SPR_HBAT, 1,  4, {A_Thrust}, 6, 1, S_HANGSTER_FLY2}, // S_HANGSTER_FLY1
+	{SPR_HBAT, 2,  1, {A_PlaySound}, sfx_s3k52, 1, S_HANGSTER_FLY3}, // S_HANGSTER_FLY2
+	{SPR_HBAT, 3,  4, {A_Thrust}, 6, 1, S_HANGSTER_FLY4}, // S_HANGSTER_FLY3
+	{SPR_HBAT, 2,  1, {A_Thrust}, 6, 1, S_HANGSTER_FLYREPEAT}, // S_HANGSTER_FLY4
+	{SPR_HBAT, 2,  0, {A_Repeat}, 12, S_HANGSTER_FLY1, S_HANGSTER_ARCUP1}, // S_HANGSTER_FLYREPEAT
+	{SPR_HBAT, 1,  5, {A_ZThrust},  2, (1<<16), S_HANGSTER_ARCUP2}, // S_HANGSTER_ARCUP1
+	{SPR_HBAT, 1,  6, {A_ZThrust},  5, (1<<16), S_HANGSTER_ARCUP3}, // S_HANGSTER_ARCUP2
+	{SPR_HBAT, 1,  1, {A_ZThrust},  0, (1<<16), S_HANGSTER_RETURN1}, // S_HANGSTER_ARCUP3
+	{SPR_HBAT, 1,  1, {A_ZThrust},  8, (1<<16), S_HANGSTER_RETURN2}, // S_HANGSTER_RETURN1
+	{SPR_HBAT, 3,  1, {NULL}, 0, 0, S_HANGSTER_RETURN1}, // S_HANGSTER_RETURN2
+	{SPR_HBAT, 0, 15, {NULL}, 0, 0, S_HANGSTER_LOOK}, // S_HANGSTER_RETURN3
+
+	{SPR_NULL, 0,  35, {NULL}, 0, 0, S_CRUMBLE2}, // S_CRUMBLE1
 	{SPR_NULL, 0, 105, {A_Scream}, 0, 0, S_NULL}, // S_CRUMBLE2
 
 	// Spark
@@ -3170,24 +3570,29 @@ state_t states[NUMSTATES] =
 	{SPR_BOM4, 4, 3, {NULL},     0, 0, S_WPLD6}, // S_WPLD5
 	{SPR_BOM4, 5, 3, {NULL},     0, 0, S_NULL},  // S_WPLD6
 
+	{SPR_DUST,   FF_TRANS40, 4, {NULL}, 0, 0, S_DUST2}, // S_DUST1
+	{SPR_DUST, 1|FF_TRANS50, 5, {NULL}, 0, 0, S_DUST3}, // S_DUST2
+	{SPR_DUST, 2|FF_TRANS60, 3, {NULL}, 0, 0, S_DUST4}, // S_DUST3
+	{SPR_DUST, 3|FF_TRANS70, 2, {NULL}, 0, 0, S_NULL},  // S_DUST4
+
 	{SPR_NULL, 0, 1, {A_RockSpawn}, 0, 0, S_ROCKSPAWN}, // S_ROCKSPAWN
 
-	{SPR_ROIA, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEA}, // S_ROCKCRUMBLEA
-	{SPR_ROIB, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEB}, // S_ROCKCRUMBLEB
-	{SPR_ROIC, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEC}, // S_ROCKCRUMBLEC
-	{SPR_ROID, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLED}, // S_ROCKCRUMBLED
-	{SPR_ROIE, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEE}, // S_ROCKCRUMBLEE
-	{SPR_ROIF, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEF}, // S_ROCKCRUMBLEF
-	{SPR_ROIG, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEG}, // S_ROCKCRUMBLEG
-	{SPR_ROIH, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEH}, // S_ROCKCRUMBLEH
-	{SPR_ROII, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEI}, // S_ROCKCRUMBLEI
-	{SPR_ROIJ, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEJ}, // S_ROCKCRUMBLEJ
-	{SPR_ROIK, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEK}, // S_ROCKCRUMBLEK
-	{SPR_ROIL, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEL}, // S_ROCKCRUMBLEL
-	{SPR_ROIM, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEM}, // S_ROCKCRUMBLEM
-	{SPR_ROIN, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEN}, // S_ROCKCRUMBLEN
-	{SPR_ROIO, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEO}, // S_ROCKCRUMBLEO
-	{SPR_ROIP, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEP}, // S_ROCKCRUMBLEP
+	{SPR_ROIA, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEA
+	{SPR_ROIB, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEB
+	{SPR_ROIC, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEC
+	{SPR_ROID, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLED
+	{SPR_ROIE, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEE
+	{SPR_ROIF, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEF
+	{SPR_ROIG, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEG
+	{SPR_ROIH, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEH
+	{SPR_ROII, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEI
+	{SPR_ROIJ, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEJ
+	{SPR_ROIK, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEK
+	{SPR_ROIL, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEL
+	{SPR_ROIM, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEM
+	{SPR_ROIN, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEN
+	{SPR_ROIO, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEO
+	{SPR_ROIP, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEP
 
 #ifdef SEENAMES
 	{SPR_NULL, 0, 1, {NULL}, 0, 0, S_NULL}, // S_NAMECHECK
@@ -3212,7 +3617,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		8,              // speed
+		0,              // speed
 		0,              // radius
 		0,              // height
 		0,              // display offset
@@ -3301,7 +3706,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		MT_THOK,        // damage
 		sfx_None,       // activesound
 		MF_SOLID|MF_SHOOTABLE, // flags
-		(statenum_t)MT_NULL // raisestate
+		MT_NULL         // raisestate
 	},
 
 	{           // MT_TAILSOVERLAY
@@ -3466,33 +3871,6 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_AQUABUZZ
-		124,            // doomednum
-		S_BBUZZFLY1,    // spawnstate
-		1,              // spawnhealth
-		S_BBUZZFLY1,    // seestate
-		sfx_None,       // seesound
-		2,              // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		TICRATE,        // painchance
-		sfx_None,       // painsound
-		S_NULL,         // meleestate
-		S_NULL,         // missilestate
-		S_XPLD_FLICKY,  // deathstate
-		S_NULL,         // xdeathstate
-		sfx_pop,        // deathsound
-		6*FRACUNIT,     // speed
-		20*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
-		0,              // display offset
-		100,            // mass
-		0,              // damage
-		sfx_gbeep,      // activesound
-		MF_SLIDEME|MF_ENEMY|MF_SPECIAL|MF_SHOOTABLE|MF_NOGRAVITY, // flags
-		S_NULL          // raisestate
-	},
-
 	{           // MT_JETTBOMBER
 		105,            // doomednum
 		S_JETBLOOK1,    // spawnstate
@@ -3554,7 +3932,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_CCOMMAND3,    // seestate
 		sfx_None,       // seesound
 		2*TICRATE,      // reactiontime
-		sfx_None,       // attacksound
+		sfx_s3k60,      // attacksound
 		S_CCOMMAND3,    // painstate
 		200,            // painchance
 		sfx_dmpain,     // painsound
@@ -3569,7 +3947,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		0,              // display offset
 		100,            // mass
 		0,              // damage
-		sfx_None,       // activesound
+		sfx_s3k5d,      // activesound
 		MF_SLIDEME|MF_ENEMY|MF_SPECIAL|MF_SHOOTABLE, // flags
 		S_NULL          // raisestate
 	},
@@ -3588,13 +3966,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_XPLD_FLICKY,  // deathstate
-		S_DETON16,      // xdeathstate
+		S_NULL,         // xdeathstate
 		sfx_pop,        // deathsound
 		1*FRACUNIT,     // speed
 		20*FRACUNIT,    // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		0,              // mass
 		1,              // damage
 		sfx_None,       // activesound
 		MF_ENEMY|MF_SHOOTABLE|MF_NOGRAVITY|MF_MISSILE, // flags
@@ -3682,33 +4060,114 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		(statenum_t)MT_JETTBULLET// raisestate
 	},
 
-	{           // MT_SHARP
+	{           // MT_SPINCUSHION
 		112,            // doomednum
-		S_SHARP_ROAM1,  // spawnstate
+		S_SPINCUSHION_LOOK, // spawnstate
 		1,              // spawnhealth
-		S_SHARP_ROAM2,  // seestate
+		S_SPINCUSHION_CHASE1, // seestate
 		sfx_None,       // seesound
 		3*TICRATE,      // reactiontime
 		sfx_s3kd8s,     // attacksound
 		S_NULL,         // painstate
 		5*TICRATE,      // painchance
-		sfx_None,       // painsound
-		S_NULL,         // meleestate
-		S_SHARP_AIM1,   // missilestate
+		sfx_shrpsp,     // painsound
+		S_SPINCUSHION_STOP1, // meleestate
+		S_SPINCUSHION_AIM1, // missilestate
 		S_XPLD_FLICKY,  // deathstate
-		S_SHARP_SPIN,   // xdeathstate
+		S_SPINCUSHION_STOP3, // xdeathstate
 		sfx_pop,        // deathsound
 		2,              // speed
 		16*FRACUNIT,    // radius
 		24*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		DMG_SPIKE,      // mass
 		0,              // damage
-		sfx_None,       // activesound
+		sfx_s3kaa,      // activesound
 		MF_ENEMY|MF_SPECIAL|MF_SHOOTABLE|MF_BOUNCE, // flags
 		S_NULL          // raisestate
 	},
 
+	{           // MT_CRUSHSTACEAN
+		126,            // doomednum
+		S_CRUSHSTACEAN_ROAM1, // spawnstate
+		1,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		32,             // reactiontime
+		sfx_s3k6b,      // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_CRUSHSTACEAN_PUNCH1, // missilestate
+		S_XPLD_FLICKY,  // deathstate
+		S_NULL,         // xdeathstate
+		sfx_pop,        // deathsound
+		8,              // speed
+		24*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		0,              // display offset
+		0,              // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_ENEMY|MF_SPECIAL|MF_SHOOTABLE, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_CRUSHCLAW
+		-1,             // doomednum
+		S_CRUSHCLAW_AIM, // spawnstate
+		1,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		16,             // reactiontime
+		sfx_s3k6b,      // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_CRUSHCLAW_OUT,// missilestate
+		S_XPLD1,        // deathstate
+		S_NULL,         // xdeathstate
+		sfx_pop,        // deathsound
+		1,              // speed
+		22*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
+		0,              // display offset
+		(sfx_s3k49<<8), // mass
+		0,              // damage
+		sfx_s3kd2l,     // activesound
+		MF_PAIN|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		MT_CRUSHCHAIN   // raisestate
+	},
+
+	{           // MT_CRUSHCHAIN
+		-1,             // doomednum
+		S_CRUSHCHAIN,   // spawnstate
+		0,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		0,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		0,              // radius
+		0,              // height
+		0,              // display offset
+		0,              // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY, // flags
+		S_NULL          // raisestate
+	},
+
 	{           // MT_JETJAW
 		113,            // doomednum
 		S_JETJAW_ROAM1, // spawnstate
@@ -3837,7 +4296,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		4*FRACUNIT,     // radius
 		8*FRACUNIT,     // height
 		0,              // display offset
-		100,            // mass
+		DMG_SPIKE,      // mass
 		1,              // damage
 		sfx_None,       // activesound
 		MF_PAIN|MF_NOGRAVITY, // flags
@@ -3848,56 +4307,83 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		117,              // doomednum
 		S_ROBOHOOD_LOOK,  // spawnstate
 		1,                // spawnhealth
-		S_ROBOHOOD_STND,  // seestate
+		S_ROBOHOOD_STAND, // seestate
 		sfx_None,         // seesound
 		TICRATE,          // reactiontime
 		sfx_None,         // attacksound
-		S_ROBOHOOD_JUMP,  // painstate
+		S_NULL,           // painstate
 		0,                // painchance
 		sfx_None,         // painsound
-		S_NULL,           // meleestate
-		S_ROBOHOOD_SHOOT, // missilestate
+		S_ROBOHOOD_JUMP3, // meleestate
+		S_ROBOHOOD_FIRE1, // missilestate
 		S_XPLD_FLICKY,    // deathstate
-		S_ROBOHOOD_JUMP2, // xdeathstate
+		S_NULL,           // xdeathstate
 		sfx_pop,          // deathsound
-		0,                // speed
+		3,                // speed
 		24*FRACUNIT,      // radius
 		32*FRACUNIT,      // height
 		0,                // display offset
 		100,              // mass
 		0,                // damage
-		sfx_None,         // activesound
+		sfx_s3k4a,        // activesound
 		MF_ENEMY|MF_SPECIAL|MF_SHOOTABLE, // flags
-		S_ROBOHOOD_FALL   // raisestate
+		S_ROBOHOOD_JUMP1  // raisestate
 	},
 
 	{           // MT_FACESTABBER
 		118,            // doomednum
 		S_FACESTABBER_STND1, // spawnstate
-		1,              // spawnhealth
+		2,              // spawnhealth
 		S_FACESTABBER_STND1, // seestate
 		sfx_None,       // seesound
-		35,             // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
+		70,             // reactiontime
+		sfx_zoom,       // attacksound
+		S_FACESTABBER_PAIN, // painstate
 		0,              // painchance
-		sfx_None,       // painsound
+		sfx_dmpain,     // painsound
 		S_FACESTABBER_CHARGE1, // meleestate
-		S_NULL,         // missilestate
-		S_XPLD_FLICKY,  // deathstate
+		S_FACESTABBER_CHARGE1, // missilestate
+		S_FACESTABBER_DIE1, // deathstate
 		S_NULL,         // xdeathstate
-		sfx_pop,        // deathsound
+		sfx_cybdth,     // deathsound
 		3,              // speed
 		32*FRACUNIT,    // radius
-		64*FRACUNIT,    // height
+		72*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
-		sfx_None,       // activesound
-		MF_ENEMY|MF_SPECIAL|MF_SHOOTABLE, // flags
+		sfx_s3kc5s,      // activesound
+		MF_ENEMY|MF_SPECIAL|MF_SHOOTABLE|MF_SLIDEME, // flags
 		S_NULL          // raisestate
 	},
 
+	{           // MT_FACESTABBERSPEAR
+		-1,              // doomednum
+		S_FACESTABBERSPEAR, // spawnstate
+		1,               // spawnhealth
+		S_NULL,          // seestate
+		sfx_None,        // seesound
+		35,              // reactiontime
+		sfx_None,        // attacksound
+		S_NULL,          // painstate
+		0,               // painchance
+		sfx_None,        // painsound
+		S_NULL,          // meleestate
+		S_NULL,          // missilestate
+		S_NULL,          // deathstate
+		S_NULL,          // xdeathstate
+		sfx_None,        // deathsound
+		0,               // speed
+		32*FRACUNIT,     // radius
+		72*FRACUNIT,     // height
+		0,               // display offset
+		DMG_SPIKE,       // mass
+		0,               // damage
+		sfx_None,        // activesound
+		MF_PAIN|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		S_NULL           // raisestate
+	},
+
 	{           // MT_EGGGUARD
 		119,             // doomednum
 		S_EGGGUARD_STND, // spawnstate
@@ -3938,7 +4424,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_s3k7b,       // painsound
 		S_NULL,          // meleestate
 		S_NULL,          // missilestate
-		S_XPLD1,         // deathstate
+		S_EGGSHIELDBREAK,// deathstate
 		S_NULL,          // xdeathstate
 		sfx_wbreak,      // deathsound
 		3,               // speed
@@ -4107,7 +4593,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		13*FRACUNIT,    // radius
 		26*FRACUNIT,    // height
 		0,              // display offset
-		0,              // mass
+		DMG_SPIKE,      // mass
 		8*FRACUNIT,     // damage
 		sfx_None,       // activesound
 		MF_PAIN|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING, // flags
@@ -4116,7 +4602,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_BOSSEXPLODE
 		-1,             // doomednum
-		S_BPLD1,        // spawnstate
+		S_BOSSEXPLODE,  // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -4404,7 +4890,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		8*FRACUNIT,     // radius
 		14*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		DMG_FIRE,       // mass
 		1,              // damage
 		sfx_None,       // activesound
 		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY|MF_FIRE, // flags
@@ -4566,7 +5052,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		4*FRACUNIT,     // radius
 		4*FRACUNIT,     // height
 		0,              // display offset
-		4,              // mass
+		DMG_WATER,      // mass
 		0,              // damage
 		sfx_None,       // activesound
 		MF_PAIN,        // flags
@@ -4721,14 +5207,14 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_BPLD1,        // deathstate
+		S_BOSSEXPLODE,  // deathstate
 		S_NULL,         // xdeathstate
 		sfx_cybdth,     // deathsound
 		48*FRACUNIT,    // speed
 		34*FRACUNIT,    // radius
 		68*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		DMG_SPIKE,      // mass
 		1,              // damage
 		sfx_mswing,     // activesound
 		MF_PAIN|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY, // flags
@@ -4755,7 +5241,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		20*FRACUNIT,    // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
-		4,              // mass
+		DMG_FIRE,       // mass
 		0,              // damage
 		sfx_None,       // activesound
 		MF_NOGRAVITY|MF_PAIN|MF_FIRE, // flags
@@ -4836,7 +5322,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		11*FRACUNIT,    // radius
 		8*FRACUNIT,     // height
 		100,            // display offset
-		100,            // mass
+		0,              // mass
 		0,              // damage
 		sfx_None,       // activesound
 		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags
@@ -4856,14 +5342,14 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_BPLD1,        // deathstate
+		S_BOSSEXPLODE,  // deathstate
 		S_NULL,         // xdeathstate
 		sfx_bexpld,     // deathsound
 		10*FRACUNIT,    // speed
 		24*FRACUNIT,    // radius
 		24*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		0,              // mass
 		1,              // damage
 		sfx_None,       // activesound
 		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags
@@ -4917,7 +5403,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		48*FRACUNIT,    // radius
 		160*FRACUNIT,   // height
 		0,              // display offset
-		100,            // mass
+		DMG_ELECTRIC,   // mass
 		1,              // damage
 		sfx_beelec,     // activesound
 		MF_PAIN|MF_FIRE|MF_NOGRAVITY|MF_PUSHABLE, // flags
@@ -4944,7 +5430,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		11*FRACUNIT,    // radius
 		8*FRACUNIT,     // height
 		0,              // display offset
-		100,            // mass
+		0,              // mass
 		32*FRACUNIT,    // damage
 		sfx_None,       // activesound
 		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags
@@ -4971,7 +5457,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		24*FRACUNIT,    // radius
 		24*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		0,              // mass
 		1,              // damage
 		sfx_None,       // activesound
 		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags
@@ -4998,7 +5484,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		24*FRACUNIT,    // radius
 		24*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		DMG_FIRE,       // mass
 		1,              // damage
 		sfx_None,       // activesound
 		MF_PAIN|MF_FIRE|MF_RUNSPAWNFUNC, // flags
@@ -5018,7 +5504,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_BPLD1,        // deathstate
+		S_BOSSEXPLODE,  // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		10*FRACUNIT,    // speed
@@ -5045,7 +5531,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_BPLD1,        // deathstate
+		S_BOSSEXPLODE,  // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		10*FRACUNIT,    // speed
@@ -5068,7 +5554,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		0,              // painchance
+		20*TICRATE,     // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
@@ -5079,7 +5565,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		24*FRACUNIT,    // radius
 		24*FRACUNIT,    // height
 		0,              // display offset
-		20*TICRATE,     // mass
+		0,              // mass
 		48*FRACUNIT,    // damage
 		sfx_s3k5d,      // activesound
 		MF_NOBLOCKMAP|MF_MISSILE|MF_BOUNCE|MF_GRENADEBOUNCE, // flags
@@ -5106,7 +5592,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		24*FRACUNIT,    // radius
 		24*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		0,              // mass
 		32*FRACUNIT,    // damage
 		sfx_s3k99,       // activesound
 		MF_NOBLOCKMAP|MF_MISSILE|MF_BOUNCE, // flags
@@ -5133,7 +5619,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		24*FRACUNIT,    // radius
 		24*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		0,              // mass
 		1,              // damage
 		sfx_None,       // activesound
 		MF_NOBLOCKMAP|MF_MISSILE, // flags
@@ -5215,7 +5701,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		16*FRACUNIT,        // radius
 		48*FRACUNIT,        // height
 		0,                  // display offset
-		sfx_s3k5a,          // mass
+		0,                  // mass
 		3,                  // damage
 		sfx_mswarp,         // activesound
 		MF_NOGRAVITY|MF_BOSS|MF_SLIDEME, // flags
@@ -5330,20 +5816,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BLUEBALL
-		-1,             // doomednum
-		S_BLUEBALL,    // spawnstate
+	{           // MT_BLUESPHERE
+		1706,           // doomednum
+		S_BLUESPHERE,   // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
-		MT_NULL,        // reactiontime
+		MT_FLINGBLUESPHERE,        // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_BLUEBALLSPARK, // deathstate
+		S_BLUESPHERESPARK, // deathstate
 		S_NULL,         // xdeathstate
 		sfx_s3k65,      // deathsound
 		38*FRACUNIT,    // speed
@@ -5354,25 +5840,25 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		0,              // damage
 		sfx_None,       // activesound
 		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
-		S_NULL          // raisestate
+		S_BLUESPHEREBONUS // raisestate
 	},
 
-	{           // MT_REDTEAMRING
-		308,            // doomednum
-		S_TEAMRING,     // spawnstate
+	{           // MT_FLINGBLUESPHERE
+		-1,             // doomednum
+		S_BLUESPHERE,         // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
-		MT_FLINGRING,   // reactiontime
+		MT_FLINGBLUESPHERE,   // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		0,              // painchance
+		MT_BLUESPHERE,        // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SPRK1,        // deathstate
+		S_BLUESPHERESPARK, // deathstate
 		S_NULL,         // xdeathstate
-		sfx_itemup,     // deathsound
+		sfx_s3k65,     // deathsound
 		38*FRACUNIT,    // speed
 		16*FRACUNIT,    // radius
 		24*FRACUNIT,    // height
@@ -5380,26 +5866,26 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
-		S_NULL          // raisestate
+		MF_SLIDEME|MF_SPECIAL, // flags
+		S_BLUESPHEREBONUS // raisestate
 	},
 
-	{           // MT_BLUETEAMRING
-		309,            // doomednum
-		S_TEAMRING,     // spawnstate
+	{           // MT_BOMBSPHERE
+		520,            // doomednum
+		S_BOMBSPHERE1,  // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
-		MT_FLINGRING,   // reactiontime
+		MT_NULL,        // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SPRK1,        // deathstate
+		S_BOSSEXPLODE,  // deathstate
 		S_NULL,         // xdeathstate
-		sfx_itemup,     // deathsound
+		sfx_cybdth,     // deathsound
 		38*FRACUNIT,    // speed
 		16*FRACUNIT,    // radius
 		24*FRACUNIT,    // height
@@ -5411,11 +5897,65 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_TOKEN
-		312,            // doomednum
-		S_TOKEN,         // spawnstate
-		1000,           // spawnhealth
-		S_NULL,         // seestate
+	{           // MT_REDTEAMRING
+		308,            // doomednum
+		S_TEAMRING,     // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		MT_FLINGRING,   // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_SPRK1,        // deathstate
+		S_NULL,         // xdeathstate
+		sfx_itemup,     // deathsound
+		38*FRACUNIT,    // speed
+		16*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_BLUETEAMRING
+		309,            // doomednum
+		S_TEAMRING,     // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		MT_FLINGRING,   // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_SPRK1,        // deathstate
+		S_NULL,         // xdeathstate
+		sfx_itemup,     // deathsound
+		38*FRACUNIT,    // speed
+		16*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_TOKEN
+		312,            // doomednum
+		S_TOKEN,         // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
@@ -5426,7 +5966,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // missilestate
 		S_SPRK1,        // deathstate
 		S_NULL,         // xdeathstate
-		sfx_token,      // deathsound
+		sfx_None,       // deathsound
 		0,              // speed
 		8*FRACUNIT,     // radius
 		16*FRACUNIT,    // height
@@ -5704,7 +6244,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_EMERHUNT
 		320,            // doomednum
-		S_EMER1,        // spawnstate
+		S_SHRD1,        // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -5719,8 +6259,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_cgot,       // deathsound
 		8,              // speed
-		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		12*FRACUNIT,    // radius
+		42*FRACUNIT,    // height
 		0,              // display offset
 		4,              // mass
 		0,              // damage
@@ -5837,40 +6377,67 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BLUESPRING
-		552,            // doomednum
-		S_BLUESPRING,   // spawnstate
+	{           // MT_BUMPER
+		542,            // doomednum
+		S_BUMPER,       // spawnstate
 		1000,           // spawnhealth
-		S_BLUESPRING2,  // seestate
+		S_NULL,         // seestate
 		sfx_None,       // seesound
-		8,              // reactiontime
+		5,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		0,              // painchance
-		sfx_spring,     // painsound
+		-1,             // painchance
+		sfx_s3kaa,      // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		20*FRACUNIT,    // radius
-		16*FRACUNIT,    // height
+		32*FRACUNIT,    // radius
+		64*FRACUNIT,    // height
 		0,              // display offset
-		11*FRACUNIT,    // mass
+		16*FRACUNIT,    // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_SOLID|MF_SPRING, // flags
-		S_BLUESPRING2   // raisestate
+		MF_SPRING|MF_NOGRAVITY, // flags
+		S_BUMPERHIT     // raisestate
+	},
+
+	{           // MT_BALLOON
+		543,            // doomednum
+		S_BALLOON,      // spawnstate
+		1,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		0,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		2,              // painchance
+		sfx_s3k77,      // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_BALLOONPOP2,  // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		32*FRACUNIT,    // radius
+		64*FRACUNIT,    // height
+		0,              // display offset
+		20*FRACUNIT,    // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_SPRING|MF_NOGRAVITY, // flags
+		S_BALLOONPOP1   // raisestate
 	},
 
 	{           // MT_YELLOWSPRING
 		550,            // doomednum
 		S_YELLOWSPRING, // spawnstate
 		1000,           // spawnhealth
-		S_YELLOWSPRING2,// seestate
+		S_NULL,         // seestate
 		sfx_None,       // seesound
-		8,              // reactiontime
+		0,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
@@ -5887,7 +6454,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		20*FRACUNIT,    // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_SOLID|MF_SPRING, // flags
+		MF_SPRING, // flags
 		S_YELLOWSPRING2 // raisestate
 	},
 
@@ -5895,9 +6462,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		551,            // doomednum
 		S_REDSPRING,    // spawnstate
 		1000,           // spawnhealth
-		S_REDSPRING2,   // seestate
+		S_NULL,         // seestate
 		sfx_None,       // seesound
-		8,              // reactiontime
+		0,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
@@ -5914,17 +6481,44 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		32*FRACUNIT,    // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_SOLID|MF_SPRING, // flags
+		MF_SPRING, // flags
 		S_REDSPRING2    // raisestate
 	},
 
+	{           // MT_BLUESPRING
+		552,            // doomednum
+		S_BLUESPRING,   // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		0,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_spring,     // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		20*FRACUNIT,    // radius
+		16*FRACUNIT,    // height
+		0,              // display offset
+		11*FRACUNIT,    // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_SPRING, // flags
+		S_BLUESPRING2   // raisestate
+	},
+
 	{           // MT_YELLOWDIAG
 		555,            // doomednum
 		S_YDIAG1,       // spawnstate
 		1,              // spawnhealth
-		S_YDIAG2,       // seestate
+		S_NULL,         // seestate
 		sfx_None,       // seesound
-		8,              // reactiontime
+		0,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
@@ -5941,7 +6535,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		20*FRACUNIT,    // mass
 		20*FRACUNIT,    // damage
 		sfx_None,       // activesound
-		MF_SOLID|MF_SPRING, // flags
+		MF_SPRING, // flags
 		S_YDIAG2        // raisestate
 	},
 
@@ -5949,9 +6543,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		556,            // doomednum
 		S_RDIAG1,       // spawnstate
 		1,              // spawnhealth
-		S_RDIAG2,       // seestate
+		S_NULL,         // seestate
 		sfx_None,       // seesound
-		8,              // reactiontime
+		0,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
@@ -5968,17 +6562,44 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		32*FRACUNIT,    // mass
 		32*FRACUNIT,    // damage
 		sfx_None,       // activesound
-		MF_SOLID|MF_SPRING, // flags
+		MF_SPRING, // flags
 		S_RDIAG2        // raisestate
 	},
 
+	{           // MT_BLUEDIAG
+		557,            // doomednum
+		S_BDIAG1,       // spawnstate
+		1,              // spawnhealth
+		S_BDIAG2,       // seestate
+		sfx_None,       // seesound
+		0,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_spring,     // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		16*FRACUNIT,    // radius
+		16*FRACUNIT,    // height
+		0,              // display offset
+		11*FRACUNIT,    // mass
+		11*FRACUNIT,    // damage
+		sfx_None,       // activesound
+		MF_SPRING, // flags
+		S_BDIAG2        // raisestate
+	},
+
 	{           // MT_YELLOWHORIZ
 		558,            // doomednum
 		S_YHORIZ1,      // spawnstate
 		1,              // spawnhealth
-		S_YHORIZ2,      // seestate
+		S_NULL,         // seestate
 		sfx_None,       // seesound
-		8,              // reactiontime
+		0,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
@@ -5995,7 +6616,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		0,              // mass
 		36*FRACUNIT,    // damage
 		sfx_None,       // activesound
-		MF_SOLID|MF_SPRING|MF_NOGRAVITY, // flags
+		MF_SPRING|MF_NOGRAVITY, // flags
 		S_YHORIZ2       // raisestate
 	},
 
@@ -6003,9 +6624,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		559,            // doomednum
 		S_RHORIZ1,      // spawnstate
 		1,              // spawnhealth
-		S_RHORIZ2,      // seestate
+		S_NULL,         // seestate
 		sfx_None,       // seesound
-		8,              // reactiontime
+		0,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
@@ -6022,7 +6643,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		0,              // mass
 		72*FRACUNIT,    // damage
 		sfx_None,       // activesound
-		MF_SOLID|MF_SPRING|MF_NOGRAVITY, // flags
+		MF_SPRING|MF_NOGRAVITY, // flags
 		S_RHORIZ2       // raisestate
 	},
 
@@ -6030,9 +6651,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		560,            // doomednum
 		S_BHORIZ1,      // spawnstate
 		1,              // spawnhealth
-		S_BHORIZ2,      // seestate
+		S_NULL,         // seestate
 		sfx_None,       // seesound
-		8,              // reactiontime
+		0,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
@@ -6047,9 +6668,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		32*FRACUNIT,    // height
 		0,              // display offset
 		0,              // mass
-		4*FRACUNIT,    // damage
+		1*FRACUNIT,     // damage
 		sfx_None,       // activesound
-		MF_SOLID|MF_SPRING|MF_NOGRAVITY, // flags
+		MF_SPRING|MF_NOGRAVITY, // flags
 		S_BHORIZ2       // raisestate
 	},
 
@@ -6108,33 +6729,6 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 	},
 
 	{           // MT_SPIKEBALL
-		-1,            // doomednum
-		S_SPIKEBALL1,   // spawnstate
-		1000,           // spawnhealth
-		S_NULL,         // seestate
-		sfx_None,       // seesound
-		8,              // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		0,              // painchance
-		sfx_None,       // painsound
-		S_NULL,         // meleestate
-		S_NULL,         // missilestate
-		S_NULL,         // deathstate
-		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
-		10*FRACUNIT,    // speed
-		4*FRACUNIT,     // radius
-		8*FRACUNIT,     // height
-		0,              // display offset
-		100,            // mass
-		1,              // damage
-		sfx_None,       // activesound
-		MF_PAIN|MF_NOGRAVITY, // flags
-		S_NULL          // raisestate
-	},
-
-	{           // MT_SPECIALSPIKEBALL
 		521,            // doomednum
 		S_SPIKEBALL1,   // spawnstate
 		1000,           // spawnhealth
@@ -6152,12 +6746,12 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // deathsound
 		10*FRACUNIT,    // speed
 		12*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		8*FRACUNIT,     // height
 		0,              // display offset
-		100,            // mass
+		DMG_SPIKE,      // mass
 		1,              // damage
 		sfx_None,       // activesound
-		MF_SPECIAL|MF_NOGRAVITY, // flags
+		MF_PAIN|MF_NOGRAVITY, // flags
 		S_NULL          // raisestate
 	},
 
@@ -6181,7 +6775,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		8*FRACUNIT,     // radius
 		14*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		DMG_FIRE,       // mass
 		1,              // damage
 		sfx_None,       // activesound
 		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY|MF_FIRE, // flags
@@ -6199,7 +6793,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // painstate
 		0,              // painchance
 		sfx_s3k64,      // painsound
-		S_NULL,         // meleestate
+		S_SPIKE4,       // meleestate
 		S_NULL,         // missilestate
 		S_SPIKED1,      // deathstate
 		S_SPIKED2,      // xdeathstate
@@ -6211,7 +6805,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		4,              // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SCENERY|MF_NOCLIPHEIGHT,  // flags
+		MF_NOBLOCKMAP|MF_SCENERY|MF_NOCLIPHEIGHT,  // flags
 		S_NULL          // raisestate
 	},
 
@@ -6297,61 +6891,61 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 	},
 
 	{           // MT_BIGMINE
-		524,            // doomednum
-		S_BIGMINE1,     // spawnstate
+		1012,           // doomednum
+		S_BIGMINE_IDLE, // spawnstate
 		1,              // spawnhealth
-		S_NULL,         // seestate
-		sfx_None,       // seesound
+		S_BIGMINE_ALERT1, // seestate
+		sfx_s3k5c,      // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		TICRATE,        // painchance
-		sfx_None,       // painsound
-		S_NULL,         // meleestate
+		0,              // painchance
+		sfx_s3k86,      // painsound
+		S_BIGMINE_SET1, // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
+		S_BIGMINE_SET2, // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		2*FRACUNIT,     // speed
-		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		28*FRACUNIT,    // radius
+		56*FRACUNIT,    // height
 		0,              // display offset
 		MT_UWEXPLODE,   // mass
-		64*FRACUNIT,    // damage
-		sfx_gbeep,      // activesound
-		MF_SPECIAL|MF_NOGRAVITY,     // flags
+		0,              // damage
+		sfx_s3k9e,      // activesound
+		MF_SPECIAL|MF_NOGRAVITY|MF_SHOOTABLE|MF_ENEMY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BIGAIRMINE
-		527,            // doomednum
-		S_BIGMINE1,     // spawnstate
+	{           // MT_BLASTEXECUTOR
+		756,            // doomednum
+		S_INVISIBLE,    // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		TICRATE,        // painchance
+		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_bexpld,     // deathsound
-		2*FRACUNIT,     // speed
-		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		sfx_None,       // deathsound
+		8,              // speed
+		32*FRACUNIT,    // radius
+		16*FRACUNIT,    // height
 		0,              // display offset
-		MT_BOSSEXPLODE, // mass
-		64*FRACUNIT,    // damage
-		sfx_gbeep,      // activesound
-		MF_SPECIAL|MF_NOGRAVITY,     // flags
+		4,              // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_SHOOTABLE|MF_NOGRAVITY|MF_NOCLIPTHING, // flags
 		S_NULL          // raisestate
 	},
 
 	{           // MT_CANNONLAUNCHER
-		525,            // doomednum
+		1123,           // doomednum
 		S_CANNONLAUNCHER1, // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
@@ -7882,7 +8476,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		11*FRACUNIT,    // radius
 		8*FRACUNIT,     // height
 		0,              // display offset
-		100,            // mass
+		0,              // mass
 		20,             // damage
 		sfx_None,       // activesound
 		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags
@@ -7909,7 +8503,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		11*FRACUNIT,    // radius
 		8*FRACUNIT,     // height
 		0,              // display offset
-		100,            // mass
+		0,              // mass
 		20,             // damage
 		sfx_None,       // activesound
 		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags
@@ -7936,7 +8530,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		11*FRACUNIT,    // radius
 		8*FRACUNIT,     // height
 		0,              // display offset
-		100,            // mass
+		0,              // mass
 		20,             // damage
 		sfx_None,       // activesound
 		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags
@@ -7963,7 +8557,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		11*FRACUNIT,    // radius
 		8*FRACUNIT,     // height
 		0,              // display offset
-		100,            // mass
+		0,              // mass
 		20,             // damage
 		sfx_None,       // activesound
 		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags
@@ -7990,7 +8584,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		60*FRACUNIT,    // radius
 		120*FRACUNIT,   // height
 		0,              // display offset
-		100,            // mass
+		0,              // mass
 		20,             // damage
 		sfx_None,       // activesound
 		MF_PAIN|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
@@ -8017,7 +8611,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		8*FRACUNIT,     // radius
 		10*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		0,              // mass
 		64*FRACUNIT,    // damage
 		sfx_None,       // activesound
 		MF_NOBLOCKMAP|MF_MISSILE, // flags
@@ -8044,7 +8638,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		4*FRACUNIT,     // radius
 		8*FRACUNIT,     // height
 		0,              // display offset
-		100,            // mass
+		0,              // mass
 		1,              // damage
 		sfx_None,       // activesound
 		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags
@@ -8071,7 +8665,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		12*FRACUNIT,    // radius
 		24*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		0,              // mass
 		1,              // damage
 		sfx_None,       // activesound
 		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags
@@ -8098,7 +8692,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		16*FRACUNIT,    // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		0,              // mass
 		1,              // damage
 		sfx_None,       // activesound
 		MF_NOBLOCKMAP|MF_MISSILE, // flags
@@ -8106,7 +8700,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 	},
 
 	{           // MT_CANNONBALLDECOR
-		526,            // doomednum
+		1124,           // doomednum
 		S_CANNONBALL1,  // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
@@ -8137,7 +8731,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_ARROW,        // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
-		sfx_None,       // seesound
+		sfx_s3ka0,      // seesound
 		32,             // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
@@ -8145,23 +8739,23 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
-		S_ARROWDOWN,    // xdeathstate
-		sfx_None,       // deathsound
+		S_ARROWBONK,    // deathstate
+		S_NULL,         // xdeathstate
+		sfx_s3k52,      // deathsound
 		16*FRACUNIT,    // speed
 		4*FRACUNIT,     // radius
 		8*FRACUNIT,     // height
 		0,              // display offset
-		100,            // mass
+		DMG_SPIKE,      // mass
 		1,              // damage
-		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags
-		S_ARROWUP       // raisestate
+		sfx_s3k51,      // activesound
+		MF_NOBLOCKMAP|MF_MISSILE, // flags
+		S_NULL          // raisestate
 	},
 
 	{           // MT_DEMONFIRE
 		-1,             // doomednum
-		S_DEMONFIRE1,   // spawnstate
+		S_DEMONFIRE,    // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -8179,7 +8773,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		8*FRACUNIT,     // radius
 		8*FRACUNIT,     // height
 		0,              // display offset
-		100,            // mass
+		0,              // mass
 		1,              // damage
 		sfx_None,       // activesound
 		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags
@@ -8267,6 +8861,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
+	{           // MT_BLUEBERRYBUSH
+		803,            // doomednum
+		S_BLUEBERRYBUSH, // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		S_NULL          // raisestate
+	},
+
 	{           // MT_BERRYBUSH
 		804,            // doomednum
 		S_BERRYBUSH,    // spawnstate
@@ -8323,7 +8944,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_GFZTREE
 		806,            // doomednum
-		S_GFZTREE,         // spawnstate
+		S_GFZTREE,      // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -8350,7 +8971,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_GFZBERRYTREE
 		807,            // doomednum
-		S_GFZBERRYTREE,         // spawnstate
+		S_GFZBERRYTREE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -8377,7 +8998,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_GFZCHERRYTREE
 		808,            // doomednum
-		S_GFZCHERRYTREE,         // spawnstate
+		S_GFZCHERRYTREE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -8403,8 +9024,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 	},
 
 	{           // MT_CHECKERTREE
-		810,            // doomednum
-		S_CHECKERTREE,         // spawnstate
+		809,            // doomednum
+		S_CHECKERTREE,  // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -8430,8 +9051,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 	},
 
 	{           // MT_CHECKERSUNSETTREE
-		811,            // doomednum
-		S_CHECKERSUNSETTREE,         // spawnstate
+		810,            // doomednum
+		S_CHECKERSUNSETTREE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -8457,8 +9078,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 	},
 
 	{           // MT_FHZTREE
-		812,            // doomednum
-		S_FHZTREE,         // spawnstate
+		2102,           // doomednum
+		S_FHZTREE,      // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -8484,8 +9105,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 	},
 
 	{           // MT_FHZPINKTREE
-		813,            // doomednum
-		S_FHZPINKTREE,         // spawnstate
+		2103,           // doomednum
+		S_FHZPINKTREE,  // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -8511,8 +9132,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 	},
 
 	{           // MT_POLYGONTREE
-		814,            // doomednum
-		S_POLYGONTREE,         // spawnstate
+		811,            // doomednum
+		S_POLYGONTREE,  // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -8538,8 +9159,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 	},
 
 	{           // MT_BUSHTREE
-		815,            // doomednum
-		S_BUSHTREE,         // spawnstate
+		812,            // doomednum
+		S_BUSHTREE,     // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -8565,8 +9186,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 	},
 
 	{           // MT_BUSHREDTREE
-		816,            // doomednum
-		S_BUSHREDTREE,         // spawnstate
+		813,            // doomednum
+		S_BUSHREDTREE,  // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -8591,6 +9212,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
+	{           // MT_SPRINGTREE
+		1600,           // doomednum
+		S_SPRINGTREE,   // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		S_NULL          // raisestate
+	},
+
 	{           // MT_THZFLOWER1
 		900,            // doomednum
 		S_THZFLOWERA,    // spawnstate
@@ -8635,8 +9283,89 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		8,              // speed
-		8*FRACUNIT,     // radius
-		32*FRACUNIT,    // height
+		16*FRACUNIT,    // radius
+		64*FRACUNIT,    // height
+		0,              // display offset
+		16,             // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_THZFLOWER3
+		903,            // doomednum
+		S_THZFLOWERC,    // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		8,              // speed
+		16*FRACUNIT,    // radius
+		64*FRACUNIT,    // height
+		0,              // display offset
+		16,             // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_THZTREE
+		904,            // doomednum
+		S_THZTREE,      // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		8,              // speed
+		16*FRACUNIT,    // radius
+		64*FRACUNIT,    // height
+		0,              // display offset
+		16,             // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_THZTREEBRANCH
+		-1,             // doomednum
+		S_THZTREEBRANCH1, // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		8,              // speed
+		1*FRACUNIT,     // radius
+		1*FRACUNIT,     // height
 		0,              // display offset
 		16,             // mass
 		0,              // damage
@@ -8915,6 +9644,114 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
+	{           // MT_KELP
+		1007,           // doomednum
+		S_KELP, // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		16*FRACUNIT,     // radius
+		292*FRACUNIT,    // height
+		0,              // display offset
+		4,              // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_SCENERY|MF_NOBLOCKMAP,     // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_DSZSTALAGMITE
+		1008,           // doomednum
+		S_DSZSTALAGMITE, // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		8*FRACUNIT,     // radius
+		116*FRACUNIT,    // height
+		0,              // display offset
+		4,              // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_SCENERY|MF_SOLID,     // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_DSZ2STALAGMITE
+		1011,           // doomednum
+		S_DSZ2STALAGMITE, // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		8*FRACUNIT,     // radius
+		116*FRACUNIT,    // height
+		0,              // display offset
+		4,              // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_SCENERY|MF_SOLID,     // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_LIGHTBEAM
+		1010,           // doomednum
+		S_LIGHTBEAM1, // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		16*FRACUNIT,     // radius
+		16*FRACUNIT,    // height
+		0,              // display offset
+		4,              // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_SCENERY|MF_NOBLOCKMAP|MF_NOGRAVITY, // flags
+		S_NULL          // raisestate
+	},
+
 	{           // MT_CHAIN
 		1100,           // doomednum
 		S_CEZCHAIN,     // spawnstate
@@ -8944,7 +9781,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_FLAME
 		1101,           // doomednum
-		S_FLAME1,       // spawnstate
+		S_FLAME,        // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -8962,7 +9799,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		8*FRACUNIT,     // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		DMG_FIRE,       // mass
 		0,              // damage
 		sfx_None,       // activesound
 		MF_NOGRAVITY|MF_PAIN|MF_FIRE, // flags
@@ -9186,7 +10023,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 	},
 
 	{           // MT_CUSTOMMACEPOINT
-		1111,           // doomednum
+		1110,           // doomednum
 		S_INVISIBLE,    // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
@@ -9229,13 +10066,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,           // xdeathstate
 		sfx_None,         // deathsound
 		24*FRACUNIT,      // speed
-		24*FRACUNIT,      // radius
-		32*FRACUNIT,      // height
+		17*FRACUNIT,      // radius
+		34*FRACUNIT,      // height
 		0,                // display offset
 		100,              // mass
 		1,                // damage
 		sfx_None,         // activesound
-		MF_SCENERY|MF_SPECIAL|MF_NOGRAVITY, // flags
+		MF_SCENERY|MF_NOGRAVITY, // flags
 		S_NULL            // raisestate
 	},
 
@@ -9256,13 +10093,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		48*FRACUNIT,    // speed
-		48*FRACUNIT,    // radius
-		96*FRACUNIT,    // height
+		34*FRACUNIT,    // radius
+		68*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		1,              // damage
 		sfx_None,       // activesound
-		MF_SCENERY|MF_SPECIAL|MF_NOGRAVITY, // flags
+		MF_SCENERY|MF_NOGRAVITY, // flags
 		S_NULL          // raisestate
 	},
 
@@ -9286,9 +10123,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		17*FRACUNIT,    // radius
 		34*FRACUNIT,    // height
 		1,              // display offset
-		100,            // mass
+		0,              // mass
 		1,              // damage
-		sfx_mswing,     // activesound
+		sfx_s3kc9s, //sfx_mswing, -- activesound
 		MF_SCENERY|MF_PAIN|MF_NOGRAVITY, // flags
 		S_NULL          // raisestate
 	},
@@ -9313,37 +10150,91 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		34*FRACUNIT,    // radius
 		68*FRACUNIT,    // height
 		1,              // display offset
-		100,            // mass
+		0,              // mass
 		1,              // damage
-		sfx_mswing,     // activesound
+		sfx_s3kc9s, //sfx_mswing, -- activesound
 		MF_SCENERY|MF_PAIN|MF_NOGRAVITY, // flags
 		S_NULL          // raisestate
 	},
 
-	{            // MT_YELLOWSPRINGBALL
-		-1,             // doomednum
-		S_YELLOWSPRINGBALL, // spawnstate
-		1000,           // spawnhealth
-		S_YELLOWSPRINGBALL2, // seestate
-		sfx_None,       // seesound
-		8,              // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		0,              // painchance
-		sfx_spring,     // painsound
-		S_NULL,         // meleestate
-		S_NULL,         // missilestate
-		S_NULL,         // deathstate
-		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
-		24*FRACUNIT,    // speed
-		17*FRACUNIT,    // radius
+	{            // MT_SMALLGRABCHAIN
+		-1,               // doomednum
+		S_SMALLGRABCHAIN, // spawnstate
+		1000,             // spawnhealth
+		S_NULL,           // seestate
+		sfx_None,         // seesound
+		8,                // reactiontime
+		sfx_None,         // attacksound
+		S_NULL,           // painstate
+		0,                // painchance
+		sfx_None,         // painsound
+		S_NULL,           // meleestate
+		S_NULL,           // missilestate
+		S_NULL,           // deathstate
+		S_NULL,           // xdeathstate
+		sfx_None,         // deathsound
+		24*FRACUNIT,      // speed
+		17*FRACUNIT,      // radius
+		34*FRACUNIT,      // height
+		0,                // display offset
+		100,              // mass
+		1,                // damage
+		sfx_None,         // activesound
+		MF_SCENERY|MF_SPECIAL|MF_NOGRAVITY, // flags
+		S_NULL            // raisestate
+	},
+
+	{            // MT_BIGGRABCHAIN
+		-1,             // doomednum
+		S_BIGGRABCHAIN,	// spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		48*FRACUNIT,    // speed
+		34*FRACUNIT,    // radius
+		68*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		1,              // damage
+		sfx_None,       // activesound
+		MF_SCENERY|MF_SPECIAL|MF_NOGRAVITY, // flags
+		S_NULL          // raisestate
+	},
+
+	{            // MT_YELLOWSPRINGBALL
+		-1,             // doomednum
+		S_YELLOWSPRINGBALL, // spawnstate
+		1000,           // spawnhealth
+		S_YELLOWSPRINGBALL2, // seestate
+		sfx_None,       // seesound
+		0,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_spring,     // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		24*FRACUNIT,    // speed
+		17*FRACUNIT,    // radius
 		34*FRACUNIT,    // height
 		1,              // display offset
 		20*FRACUNIT,    // mass
 		0,              // damage
 		sfx_mswing,     // activesound
-		MF_SCENERY|MF_SOLID|MF_SPRING|MF_NOGRAVITY, // flags
+		MF_SCENERY|MF_SPRING|MF_NOGRAVITY, // flags
 		S_YELLOWSPRINGBALL2 // raisestate
 	},
 
@@ -9353,7 +10244,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		1000,           // spawnhealth
 		S_REDSPRINGBALL2, // seestate
 		sfx_None,       // seesound
-		8,              // reactiontime
+		0,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
@@ -9370,7 +10261,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		32*FRACUNIT,    // mass
 		0,              // damage
 		sfx_mswing,     // activesound
-		MF_SCENERY|MF_SOLID|MF_SPRING|MF_NOGRAVITY, // flags
+		MF_SCENERY|MF_SPRING|MF_NOGRAVITY, // flags
 		S_REDSPRINGBALL2 // raisestate
 	},
 
@@ -9394,7 +10285,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		17*FRACUNIT,    // radius
 		34*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		DMG_FIRE,       // mass
 		1,              // damage
 		sfx_None,       // activesound
 		MF_SCENERY|MF_PAIN|MF_FIRE|MF_NOGRAVITY, // flags
@@ -9421,7 +10312,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		34*FRACUNIT,    // radius
 		68*FRACUNIT,    // height
 		1,              // display offset
-		100,            // mass
+		DMG_FIRE,       // mass
 		1,              // damage
 		sfx_None,       // activesound
 		MF_SCENERY|MF_PAIN|MF_FIRE|MF_NOGRAVITY, // flags
@@ -9430,7 +10321,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_CEZFLOWER
 		1103,           // doomednum
-		S_CEZFLOWER1,   // spawnstate
+		S_CEZFLOWER,    // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -9455,11 +10346,11 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BIGTUMBLEWEED
-		1200,           // doomednum
-		S_BIGTUMBLEWEED,// spawnstate
+	{           // MT_CEZPOLE
+		1117,           // doomednum
+		S_CEZPOLE,      // spawnstate
 		1000,           // spawnhealth
-		S_BIGTUMBLEWEED_ROLL1, // seestate
+		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
@@ -9472,21 +10363,21 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		24*FRACUNIT,    // radius
-		48*FRACUNIT,    // height
+		40*FRACUNIT,    // radius
+		224*FRACUNIT,   // height
 		0,              // display offset
 		100,            // mass
-		1,              // damage
-		sfx_s3k64,      // activesound
-		MF_SPECIAL|MF_BOUNCE,      // flags
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_LITTLETUMBLEWEED
-		1201,           // doomednum
-		S_LITTLETUMBLEWEED,// spawnstate
+	{           // MT_CEZBANNER
+		-1,             // doomednum
+		S_CEZBANNER,    // spawnstate
 		1000,           // spawnhealth
-		S_LITTLETUMBLEWEED_ROLL1, // seestate
+		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
@@ -9499,19 +10390,19 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		12*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		40*FRACUNIT,    // radius
+		224*FRACUNIT,   // height
 		0,              // display offset
 		100,            // mass
-		1,              // damage
-		sfx_s3k64,      // activesound
-		MF_SPECIAL|MF_BOUNCE,      // flags
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_CACTI1
-		1203,           // doomednum
-		S_CACTI1,       // spawnstate
+	{           // MT_PINETREE
+		1114,           // doomednum
+		S_PINETREE,     // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -9527,18 +10418,18 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // deathsound
 		0,              // speed
 		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		628*FRACUNIT,   // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		MF_NOGRAVITY|MF_SOLID|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_CACTI2
-		1204,           // doomednum
-		S_CACTI2,       // spawnstate
+	{           // MT_CEZBUSH1
+		1115,           // doomednum
+		S_CEZBUSH1,     // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -9554,7 +10445,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // deathsound
 		0,              // speed
 		16*FRACUNIT,    // radius
-		64*FRACUNIT,    // height
+		24*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
@@ -9563,9 +10454,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_CACTI3
-		1205,           // doomednum
-		S_CACTI3,       // spawnstate
+	{           // MT_CEZBUSH2
+		1116,           // doomednum
+		S_CEZBUSH2,     // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -9580,8 +10471,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		3*FRACUNIT,    // radius
+		48*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
@@ -9590,9 +10481,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_CACTI4
-		1206,           // doomednum
-		S_CACTI4,       // spawnstate
+	{           // MT_CANDLE
+		1119,           // doomednum
+		S_CANDLE,       // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -9607,19 +10498,19 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		16*FRACUNIT,    // radius
-		80*FRACUNIT,    // height
+		8*FRACUNIT,     // radius
+		48*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_FLAMEJET
-		1300,           // doomednum
-		S_FLAMEJETSTND, // spawnstate
+	{           // MT_CANDLEPRICKET
+		1120,           // doomednum
+		S_CANDLEPRICKET, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -9634,19 +10525,19 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		16*FRACUNIT,    // radius
-		40*FRACUNIT,    // height
+		8*FRACUNIT,     // radius
+		176*FRACUNIT,   // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_NOBLOCKMAP|MF_NOSECTOR, // flags
+		MF_SOLID,       // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_VERTICALFLAMEJET
-		1301,           // doomednum
-		S_FLAMEJETSTND, // spawnstate
+	{           // MT_FLAMEHOLDER
+		1121,           // doomednum
+		S_FLAMEHOLDER,  // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -9661,46 +10552,46 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		16*FRACUNIT,    // radius
-		40*FRACUNIT,    // height
+		24*FRACUNIT,    // radius
+		80*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_NOBLOCKMAP|MF_NOSECTOR, // flags
+		MF_SOLID,       // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_FLAMEJETFLAME
-		-1,             // doomednum
-		S_FLAMEJETFLAME1, // spawnstate
+	{           // MT_FIRETORCH
+		1122,           // doomednum
+		S_FIRETORCH,    // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		0,              // painchance
+		MT_FLAMEPARTICLE, // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		5*FRACUNIT,     // speed
-		8*FRACUNIT,     // radius
-		8*FRACUNIT,     // height
+		0,              // speed
+		16*FRACUNIT,    // radius
+		80*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOGRAVITY|MF_MISSILE|MF_FIRE, // flags
+		MF_NOBLOCKMAP|MF_NOGRAVITY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_FJSPINAXISA
-		3575,           // doomednum
-		S_FJSPINAXISA1, // spawnstate
+	{           // MT_WAVINGFLAG
+		1118,           // doomednum
+		S_WAVINGFLAG,   // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -9714,20 +10605,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		10*FRACUNIT,    // speed
-		16*FRACUNIT,    // radius
-		1*FRACUNIT,     // height
+		0,              // speed
+		4*FRACUNIT,     // radius
+		104*FRACUNIT,   // height
 		0,              // display offset
 		100,            // mass
-		1,              // damage
+		0,              // damage
 		sfx_None,       // activesound
-		MF_NOCLIP|MF_NOCLIPTHING|MF_NOGRAVITY|MF_NOSECTOR, // flags
+		MF_SOLID,       // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_FJSPINAXISB
-		3576,           // doomednum
-		S_FJSPINAXISB1, // spawnstate
+	{           // MT_WAVINGFLAGSEG
+		-1,             // doomednum
+		S_WAVINGFLAGSEG, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -9741,23 +10632,23 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		10*FRACUNIT,    // speed
-		16*FRACUNIT,    // radius
-		1*FRACUNIT,     // height
+		0,              // speed
+		4*FRACUNIT,     // radius
+		1,              // height -- this is not a typo
 		0,              // display offset
 		100,            // mass
-		1,              // damage
+		0,              // damage
 		sfx_None,       // activesound
-		MF_NOCLIP|MF_NOCLIPTHING|MF_NOGRAVITY|MF_NOSECTOR, // flags
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_FLAMEJETFLAMEB
-		-1,             // doomednum
-		S_FLAMEJETFLAMEB1, // spawnstate
+	{           // MT_CRAWLASTATUE
+		1111,           // doomednum
+		S_CRAWLASTATUE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
-		sfx_fire,       // seesound
+		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
@@ -9768,20 +10659,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		18,             // speed
+		0,              // speed
 		16*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		40*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		1,              // damage
+		0,              // damage
 		sfx_None,       // activesound
-		MF_NOGRAVITY|MF_MISSILE|MF_FIRE|MF_NOBLOCKMAP|MF_RUNSPAWNFUNC, // flags
+		MF_SLIDEME|MF_SOLID|MF_PUSHABLE, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_TRAPGOYLE
-		1500,           // doomednum
-		S_TRAPGOYLE,    // spawnstate
+	{           // MT_FACESTABBERSTATUE
+		1112,           // doomednum
+		S_FACESTABBERSTATUE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -9795,23 +10686,23 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		21*FRACUNIT,    // speed
-		16*FRACUNIT,    // radius
-		40*FRACUNIT,    // height
+		0,              // speed
+		32*FRACUNIT,    // radius
+		72*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		1,              // damage
-		sfx_statu2,     // activesound
+		0,              // damage
+		sfx_None,       // activesound
 		MF_SLIDEME|MF_SOLID|MF_PUSHABLE, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_TRAPGOYLEUP
-		1501,           // doomednum
-		S_TRAPGOYLEUP,  // spawnstate
+	{           // MT_SUSPICIOUSFACESTABBERSTATUE
+		1113,           // doomednum
+		S_SUSPICIOUSFACESTABBERSTATUE_WAIT, // spawnstate
 		1000,           // spawnhealth
-		S_NULL,         // seestate
-		sfx_None,       // seesound
+		S_SUSPICIOUSFACESTABBERSTATUE_BURST1, // seestate
+		sfx_s3k6f,      // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
@@ -9822,20 +10713,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		21*FRACUNIT,    // speed
-		16*FRACUNIT,    // radius
-		40*FRACUNIT,    // height
+		0,              // speed
+		32*FRACUNIT,    // radius
+		72*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		1,              // damage
-		sfx_statu2,     // activesound
+		0,              // damage
+		sfx_None,       // activesound
 		MF_SLIDEME|MF_SOLID|MF_PUSHABLE, // flags
-		S_NULL          // raisestate
+		MT_ROCKCRUMBLE3 // raisestate
 	},
 
-	{           // MT_TRAPGOYLEDOWN
-		1502,           // doomednum
-		S_TRAPGOYLEDOWN,// spawnstate
+	{           // MT_BRAMBLES
+		1125,           // doomednum
+		S_BRAMBLES,     // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -9849,22 +10740,22 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		21*FRACUNIT,    // speed
-		16*FRACUNIT,    // radius
-		40*FRACUNIT,    // height
+		0,              // speed
+		48*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		1,              // damage
-		sfx_statu2,     // activesound
-		MF_SLIDEME|MF_SOLID|MF_PUSHABLE, // flags
+		0,              // damage
+		sfx_None,       // activesound
+		MF_SCENERY|MF_NOBLOCKMAP, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_TRAPGOYLELONG
-		1503,           // doomednum
-		S_TRAPGOYLELONG,// spawnstate
+	{           // MT_BIGTUMBLEWEED
+		1200,           // doomednum
+		S_BIGTUMBLEWEED,// spawnstate
 		1000,           // spawnhealth
-		S_NULL,         // seestate
+		S_BIGTUMBLEWEED_ROLL1, // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
@@ -9876,47 +10767,47 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		21*FRACUNIT,    // speed
-		16*FRACUNIT,    // radius
-		40*FRACUNIT,    // height
+		0,              // speed
+		24*FRACUNIT,    // radius
+		48*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		1,              // damage
-		sfx_statu2,     // activesound
-		MF_SLIDEME|MF_SOLID|MF_PUSHABLE, // flags
+		sfx_s3k64,      // activesound
+		MF_SPECIAL|MF_BOUNCE,      // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_TARGET
-		1504,           // doomednum
-		S_TARGET_IDLE,  // spawnstate
-		1,              // spawnhealth
-		S_NULL,         // seestate
+	{           // MT_LITTLETUMBLEWEED
+		1201,           // doomednum
+		S_LITTLETUMBLEWEED,// spawnstate
+		1000,           // spawnhealth
+		S_LITTLETUMBLEWEED_ROLL1, // seestate
 		sfx_None,       // seesound
-		32,             // reactiontime
+		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		200,            // painchance
+		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_TARGET_HIT1,  // deathstate
-		S_TARGET_ALLDONE, // xdeathstate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		24*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		12*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		0,              // damage
-		sfx_None,       // activesound
-		MF_SPECIAL|MF_SHOOTABLE, // flags
+		1,              // damage
+		sfx_s3k64,      // activesound
+		MF_SPECIAL|MF_BOUNCE,      // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_STALAGMITE0
-		1900,           // doomednum
-		S_STG0,         // spawnstate
+	{           // MT_CACTI1
+		1203,           // doomednum
+		S_CACTI1,       // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -9932,18 +10823,18 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // deathsound
 		0,              // speed
 		16*FRACUNIT,    // radius
-		40*FRACUNIT,    // height
+		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOCLIP|MF_SCENERY, // flags
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_STALAGMITE1
-		1901,           // doomednum
-		S_STG1,         // spawnstate
+	{           // MT_CACTI2
+		1204,           // doomednum
+		S_CACTI2,       // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -9959,7 +10850,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // deathsound
 		0,              // speed
 		16*FRACUNIT,    // radius
-		40*FRACUNIT,    // height
+		64*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
@@ -9968,9 +10859,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_STALAGMITE2
-		1902,           // doomednum
-		S_STG2,         // spawnstate
+	{           // MT_CACTI3
+		1205,           // doomednum
+		S_CACTI3,       // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -9986,7 +10877,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // deathsound
 		0,              // speed
 		16*FRACUNIT,    // radius
-		40*FRACUNIT,    // height
+		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
@@ -9995,9 +10886,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_STALAGMITE3
-		1903,           // doomednum
-		S_STG3,         // spawnstate
+	{           // MT_CACTI4
+		1206,           // doomednum
+		S_CACTI4,       // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10013,7 +10904,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // deathsound
 		0,              // speed
 		16*FRACUNIT,    // radius
-		40*FRACUNIT,    // height
+		80*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
@@ -10022,9 +10913,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_STALAGMITE4
-		1904,           // doomednum
-		S_STG4,         // spawnstate
+	{           // MT_FLAMEJET
+		1300,           // doomednum
+		S_FLAMEJETSTND, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10045,13 +10936,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_NOBLOCKMAP|MF_NOSECTOR, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_STALAGMITE5
-		1905,           // doomednum
-		S_STG5,         // spawnstate
+	{           // MT_VERTICALFLAMEJET
+		1301,           // doomednum
+		S_FLAMEJETSTND, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10072,13 +10963,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_NOBLOCKMAP|MF_NOSECTOR, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_STALAGMITE6
-		1906,           // doomednum
-		S_STG6,         // spawnstate
+	{           // MT_FLAMEJETFLAME
+		-1,             // doomednum
+		S_FLAMEJETFLAME1, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10092,20 +10983,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
-		16*FRACUNIT,    // radius
-		40*FRACUNIT,    // height
+		5*FRACUNIT,     // speed
+		8*FRACUNIT,     // radius
+		8*FRACUNIT,     // height
 		0,              // display offset
-		100,            // mass
+		DMG_FIRE,       // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		MF_NOGRAVITY|MF_MISSILE|MF_FIRE, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_STALAGMITE7
-		1907,           // doomednum
-		S_STG7,         // spawnstate
+	{           // MT_FJSPINAXISA
+		1302,           // doomednum
+		S_FJSPINAXISA1, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10119,20 +11010,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
+		10*FRACUNIT,    // speed
 		16*FRACUNIT,    // radius
-		40*FRACUNIT,    // height
+		1*FRACUNIT,     // height
 		0,              // display offset
 		100,            // mass
-		0,              // damage
+		1,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		MF_NOCLIP|MF_NOCLIPTHING|MF_NOGRAVITY|MF_NOSECTOR, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_STALAGMITE8
-		1908,           // doomednum
-		S_STG8,         // spawnstate
+	{           // MT_FJSPINAXISB
+		1303,           // doomednum
+		S_FJSPINAXISB1, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10146,23 +11037,23 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
+		10*FRACUNIT,    // speed
 		16*FRACUNIT,    // radius
-		40*FRACUNIT,    // height
+		1*FRACUNIT,     // height
 		0,              // display offset
 		100,            // mass
-		0,              // damage
+		1,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		MF_NOCLIP|MF_NOCLIPTHING|MF_NOGRAVITY|MF_NOSECTOR, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_STALAGMITE9
-		1909,           // doomednum
-		S_STG9,         // spawnstate
+	{           // MT_FLAMEJETFLAMEB
+		-1,             // doomednum
+		S_FLAMEJETFLAMEB1, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
-		sfx_None,       // seesound
+		sfx_fire,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
@@ -10173,20 +11064,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
+		18,             // speed
 		16*FRACUNIT,    // radius
-		40*FRACUNIT,    // height
+		24*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
-		0,              // damage
+		DMG_FIRE,       // mass
+		1,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		MF_NOGRAVITY|MF_MISSILE|MF_FIRE|MF_NOBLOCKMAP|MF_RUNSPAWNFUNC, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_XMASPOLE
-		1850,           // doomednum
-		S_XMASPOLE,     // spawnstate
+	{           // MT_TRAPGOYLE
+		1500,           // doomednum
+		S_TRAPGOYLE,    // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10200,20 +11091,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
+		21*FRACUNIT,    // speed
 		16*FRACUNIT,    // radius
 		40*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		0,              // damage
-		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		1,              // damage
+		sfx_statu2,     // activesound
+		MF_SLIDEME|MF_SOLID|MF_PUSHABLE, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_CANDYCANE
-		1851,           // doomednum
-		S_CANDYCANE,    // spawnstate
+	{           // MT_TRAPGOYLEUP
+		1501,           // doomednum
+		S_TRAPGOYLEUP,  // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10227,20 +11118,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		8,              // speed
-		8*FRACUNIT,     // radius
-		32*FRACUNIT,    // height
+		21*FRACUNIT,    // speed
+		16*FRACUNIT,    // radius
+		40*FRACUNIT,    // height
 		0,              // display offset
-		16,             // mass
-		0,              // damage
-		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		100,            // mass
+		1,              // damage
+		sfx_statu2,     // activesound
+		MF_SLIDEME|MF_SOLID|MF_PUSHABLE, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_SNOWMAN
-		1852,           // doomednum
-		S_SNOWMAN,      // spawnstate
+	{           // MT_TRAPGOYLEDOWN
+		1502,           // doomednum
+		S_TRAPGOYLEDOWN,// spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10254,20 +11145,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		25*FRACUNIT,    // speed
+		21*FRACUNIT,    // speed
 		16*FRACUNIT,    // radius
-		64*FRACUNIT,    // height
+		40*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		1,              // damage
-		sfx_None,       // activesound
+		sfx_statu2,     // activesound
 		MF_SLIDEME|MF_SOLID|MF_PUSHABLE, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_SNOWMANHAT
-		1853,           // doomednum
-		S_SNOWMANHAT,   // spawnstate
+	{           // MT_TRAPGOYLELONG
+		1503,           // doomednum
+		S_TRAPGOYLELONG,// spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10281,47 +11172,47 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		25*FRACUNIT,    // speed
+		21*FRACUNIT,    // speed
 		16*FRACUNIT,    // radius
-		80*FRACUNIT,    // height
+		40*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		1,              // damage
-		sfx_None,       // activesound
+		sfx_statu2,     // activesound
 		MF_SLIDEME|MF_SOLID|MF_PUSHABLE, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_LAMPPOST1
-		1854,           // doomednum
-		S_LAMPPOST1,    // spawnstate
-		1000,           // spawnhealth
+	{           // MT_TARGET
+		1504,           // doomednum
+		S_TARGET_IDLE,  // spawnstate
+		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
-		8,              // reactiontime
+		32,             // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		0,              // painchance
+		200,            // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
-		S_NULL,         // xdeathstate
+		S_TARGET_HIT1,  // deathstate
+		S_TARGET_ALLDONE, // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		8*FRACUNIT,     // radius
-		120*FRACUNIT,   // height
+		24*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		1,              // damage
+		0,              // damage
 		sfx_None,       // activesound
-		MF_SOLID,       // flags
+		MF_SPECIAL|MF_SHOOTABLE, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_LAMPPOST2
-		1855,           // doomednum
-		S_LAMPPOST2,    // spawnstate
+	{           // MT_STALAGMITE0
+		1900,           // doomednum
+		S_STG0,         // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10336,19 +11227,19 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		8*FRACUNIT,     // radius
-		120*FRACUNIT,   // height
+		16*FRACUNIT,    // radius
+		40*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		1,              // damage
+		0,              // damage
 		sfx_None,       // activesound
-		MF_SOLID,       // flags
+		MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_HANGSTAR
-		1856,           // doomednum
-		S_HANGSTAR,     // spawnstate
+	{           // MT_STALAGMITE1
+		1901,           // doomednum
+		S_STG1,         // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10363,19 +11254,19 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		4*FRACUNIT,     // radius
-		80*FRACUNIT,    // height
+		16*FRACUNIT,    // radius
+		40*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		1,              // damage
+		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_SPAWNCEILING|MF_NOGRAVITY|MF_SCENERY, // flags
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_XMASBERRYBUSH
-		1857,           // doomednum
-		S_XMASBERRYBUSH, // spawnstate
+	{           // MT_STALAGMITE2
+		1902,           // doomednum
+		S_STG2,         // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10391,7 +11282,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // deathsound
 		0,              // speed
 		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		40*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
@@ -10400,9 +11291,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_XMASBUSH
-		1858,           // doomednum
-		S_XMASBUSH,     // spawnstate
+	{           // MT_STALAGMITE3
+		1903,           // doomednum
+		S_STG3,         // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10418,7 +11309,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // deathsound
 		0,              // speed
 		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		40*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
@@ -10427,12 +11318,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	// No, I did not do all of this by hand.
-	// I made a script to make all of these for me.
-	// Ha HA. ~Inuyasha
-	{           // MT_BSZTALLFLOWER_RED
-		1400,           // doomednum
-		S_BSZTALLFLOWER_RED, // spawnstate
+	{           // MT_STALAGMITE4
+		1904,           // doomednum
+		S_STG4,         // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10448,7 +11336,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // deathsound
 		0,              // speed
 		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		40*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
@@ -10457,9 +11345,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZTALLFLOWER_PURPLE
-		1401,           // doomednum
-		S_BSZTALLFLOWER_PURPLE, // spawnstate
+	{           // MT_STALAGMITE5
+		1905,           // doomednum
+		S_STG5,         // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10475,7 +11363,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // deathsound
 		0,              // speed
 		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		40*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
@@ -10484,9 +11372,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZTALLFLOWER_BLUE
-		1402,           // doomednum
-		S_BSZTALLFLOWER_BLUE, // spawnstate
+	{           // MT_STALAGMITE6
+		1906,           // doomednum
+		S_STG6,         // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10502,7 +11390,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // deathsound
 		0,              // speed
 		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		40*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
@@ -10511,9 +11399,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZTALLFLOWER_CYAN
-		1403,           // doomednum
-		S_BSZTALLFLOWER_CYAN, // spawnstate
+	{           // MT_STALAGMITE7
+		1907,           // doomednum
+		S_STG7,         // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10529,7 +11417,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // deathsound
 		0,              // speed
 		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		40*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
@@ -10538,9 +11426,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZTALLFLOWER_YELLOW
-		1404,           // doomednum
-		S_BSZTALLFLOWER_YELLOW, // spawnstate
+	{           // MT_STALAGMITE8
+		1908,           // doomednum
+		S_STG8,         // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10556,7 +11444,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // deathsound
 		0,              // speed
 		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		40*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
@@ -10565,9 +11453,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZTALLFLOWER_ORANGE
-		1405,           // doomednum
-		S_BSZTALLFLOWER_ORANGE, // spawnstate
+	{           // MT_STALAGMITE9
+		1909,           // doomednum
+		S_STG9,         // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10583,7 +11471,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // deathsound
 		0,              // speed
 		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		40*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
@@ -10592,9 +11480,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZFLOWER_RED
-		1410,           // doomednum
-		S_BSZFLOWER_RED, // spawnstate
+	{           // MT_XMASPOLE
+		1850,           // doomednum
+		S_XMASPOLE,     // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10610,7 +11498,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // deathsound
 		0,              // speed
 		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		40*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
@@ -10619,9 +11507,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZFLOWER_PURPLE
-		1411,           // doomednum
-		S_BSZFLOWER_PURPLE, // spawnstate
+	{           // MT_CANDYCANE
+		1851,           // doomednum
+		S_CANDYCANE,    // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10635,20 +11523,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
-		16*FRACUNIT,    // radius
+		8,              // speed
+		8*FRACUNIT,     // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
 		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZFLOWER_BLUE
-		1412,           // doomednum
-		S_BSZFLOWER_BLUE, // spawnstate
+	{           // MT_SNOWMAN
+		1852,           // doomednum
+		S_SNOWMAN,      // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10662,20 +11550,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
+		25*FRACUNIT,    // speed
 		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		64*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		0,              // damage
+		1,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		MF_SLIDEME|MF_SOLID|MF_PUSHABLE, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZFLOWER_CYAN
-		1413,           // doomednum
-		S_BSZFLOWER_CYAN, // spawnstate
+	{           // MT_SNOWMANHAT
+		1853,           // doomednum
+		S_SNOWMANHAT,   // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10689,20 +11577,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
+		25*FRACUNIT,    // speed
 		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		80*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		0,              // damage
+		1,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		MF_SLIDEME|MF_SOLID|MF_PUSHABLE, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZFLOWER_YELLOW
-		1414,           // doomednum
-		S_BSZFLOWER_YELLOW, // spawnstate
+	{           // MT_LAMPPOST1
+		1854,           // doomednum
+		S_LAMPPOST1,    // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10717,19 +11605,19 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		8*FRACUNIT,     // radius
+		120*FRACUNIT,   // height
 		0,              // display offset
 		100,            // mass
-		0,              // damage
+		1,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		MF_SOLID,       // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZFLOWER_ORANGE
-		1415,           // doomednum
-		S_BSZFLOWER_ORANGE, // spawnstate
+	{           // MT_LAMPPOST2
+		1855,           // doomednum
+		S_LAMPPOST2,    // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10744,19 +11632,19 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		8*FRACUNIT,     // radius
+		120*FRACUNIT,   // height
 		0,              // display offset
 		100,            // mass
-		0,              // damage
+		1,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		MF_SOLID,       // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZSHORTFLOWER_RED
-		1420,           // doomednum
-		S_BSZSHORTFLOWER_RED, // spawnstate
+	{           // MT_HANGSTAR
+		1856,           // doomednum
+		S_HANGSTAR,     // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10771,19 +11659,19 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		4*FRACUNIT,     // radius
+		80*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		0,              // damage
+		1,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_SPAWNCEILING|MF_NOGRAVITY|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZSHORTFLOWER_PURPLE
-		1421,           // doomednum
-		S_BSZSHORTFLOWER_PURPLE, // spawnstate
+	{           // MT_XMASBLUEBERRYBUSH
+		1859,           // doomednum
+		S_XMASBLUEBERRYBUSH, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10808,9 +11696,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZSHORTFLOWER_BLUE
-		1422,           // doomednum
-		S_BSZSHORTFLOWER_BLUE, // spawnstate
+	{           // MT_XMASBERRYBUSH
+		1857,           // doomednum
+		S_XMASBERRYBUSH, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10835,9 +11723,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZSHORTFLOWER_CYAN
-		1423,           // doomednum
-		S_BSZSHORTFLOWER_CYAN, // spawnstate
+	{           // MT_XMASBUSH
+		1858,           // doomednum
+		S_XMASBUSH,     // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10862,9 +11750,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZSHORTFLOWER_YELLOW
-		1424,           // doomednum
-		S_BSZSHORTFLOWER_YELLOW, // spawnstate
+	{           // MT_FHZICE1
+		2100,           // doomednum
+		S_FHZICE1,      // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10879,7 +11767,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		16*FRACUNIT,    // radius
+		8*FRACUNIT,     // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
@@ -10889,9 +11777,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZSHORTFLOWER_ORANGE
-		1425,           // doomednum
-		S_BSZSHORTFLOWER_ORANGE, // spawnstate
+	{           // MT_FHZICE2
+		2101,           // doomednum
+		S_FHZICE2,      // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10906,7 +11794,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		16*FRACUNIT,    // radius
+		8*FRACUNIT,     // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
@@ -10916,9 +11804,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZTULIP_RED
-		1430,           // doomednum
-		S_BSZTULIP_RED, // spawnstate
+	{           // MT_JACKO1
+		2006,           // doomednum
+		S_JACKO1,       // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10933,19 +11821,19 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		FRACUNIT,       // radius
+		FRACUNIT,       // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
-		S_NULL          // raisestate
+		MF_NOBLOCKMAP|MF_SCENERY, // flags
+		S_JACKO1OVERLAY_1 // raisestate
 	},
 
-	{           // MT_BSZTULIP_PURPLE
-		1431,           // doomednum
-		S_BSZTULIP_PURPLE, // spawnstate
+	{           // MT_JACKO2
+		2007,           // doomednum
+		S_JACKO2,       // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10960,19 +11848,19 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		FRACUNIT,       // radius
+		FRACUNIT,       // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
-		S_NULL          // raisestate
+		MF_NOBLOCKMAP|MF_SCENERY, // flags
+		S_JACKO2OVERLAY_1 // raisestate
 	},
 
-	{           // MT_BSZTULIP_BLUE
-		1432,           // doomednum
-		S_BSZTULIP_BLUE, // spawnstate
+	{           // MT_JACKO3
+		2008,           // doomednum
+		S_JACKO3,       // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -10987,19 +11875,19 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		FRACUNIT,       // radius
+		FRACUNIT,       // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
-		S_NULL          // raisestate
+		MF_NOBLOCKMAP|MF_SCENERY, // flags
+		S_JACKO3OVERLAY_1 // raisestate
 	},
 
-	{           // MT_BSZTULIP_CYAN
-		1433,           // doomednum
-		S_BSZTULIP_CYAN, // spawnstate
+	{           // MT_HHZTREE_TOP
+		2010,           // doomednum
+		S_HHZTREE_TOP,  // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11014,21 +11902,21 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		FRACUNIT,       // radius
+		40*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		MF_NOTHINK|MF_SCENERY|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_RUNSPAWNFUNC, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZTULIP_YELLOW
-		1434,           // doomednum
-		S_BSZTULIP_YELLOW, // spawnstate
+	{           // MT_HHZTREE_PART
+		-1,             // doomednum
+		S_HHZTREE_TRUNK,// spawnstate
 		1000,           // spawnhealth
-		S_NULL,         // seestate
+		S_HHZTREE_LEAF, // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
@@ -11041,19 +11929,19 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		FRACUNIT,       // radius
+		40*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		MF_NOTHINK|MF_SCENERY|MF_NOBLOCKMAP|MF_NOGRAVITY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZTULIP_ORANGE
-		1435,           // doomednum
-		S_BSZTULIP_ORANGE, // spawnstate
+	{           // MT_HHZSHROOM
+		2009,           // doomednum
+		S_HHZSHROOM_1,    // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11068,19 +11956,19 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		FRACUNIT,       // radius
+		FRACUNIT,       // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		MF_SCENERY|MF_NOBLOCKMAP, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZCLUSTER_RED
-		1440,           // doomednum
-		S_BSZCLUSTER_RED, // spawnstate
+	{           // MT_HHZGRASS
+		2001,           // doomednum
+		S_HHZGRASS,     // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11095,19 +11983,19 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		FRACUNIT,       // radius
+		FRACUNIT,       // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		MF_NOTHINK|MF_SCENERY|MF_NOBLOCKMAP, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZCLUSTER_PURPLE
-		1441,           // doomednum
-		S_BSZCLUSTER_PURPLE, // spawnstate
+	{           // MT_HHZTENTACLE1
+		2002,           // doomednum
+		S_HHZTENT1,     // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11122,19 +12010,19 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		FRACUNIT,       // radius
+		FRACUNIT,       // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		MF_NOTHINK|MF_SCENERY|MF_NOBLOCKMAP, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZCLUSTER_BLUE
-		1442,           // doomednum
-		S_BSZCLUSTER_BLUE, // spawnstate
+	{           // MT_HHZTENTACLE2
+		2003,           // doomednum
+		S_HHZTENT2,     // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11149,19 +12037,19 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		FRACUNIT,       // radius
+		FRACUNIT,       // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		MF_NOTHINK|MF_SCENERY|MF_NOBLOCKMAP, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZCLUSTER_CYAN
-		1443,           // doomednum
-		S_BSZCLUSTER_CYAN, // spawnstate
+	{           // MT_HHZSTALAGMITE_TALL
+		2004,           // doomednum
+		S_HHZSTALAGMITE_TALL, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11176,19 +12064,19 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		FRACUNIT,       // radius
+		FRACUNIT,       // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		MF_NOTHINK|MF_SCENERY|MF_NOBLOCKMAP, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZCLUSTER_YELLOW
-		1444,           // doomednum
-		S_BSZCLUSTER_YELLOW, // spawnstate
+	{           // MT_HHZSTALAGMITE_SHORT
+		2005,           // doomednum
+		S_HHZSTALAGMITE_SHORT, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11203,19 +12091,22 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
-		0,              // display offset
+		FRACUNIT,       // radius
+		FRACUNIT,       // height
+		0,              // display offset
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		MF_NOTHINK|MF_SCENERY|MF_NOBLOCKMAP, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZCLUSTER_ORANGE
-		1445,           // doomednum
-		S_BSZCLUSTER_ORANGE, // spawnstate
+	// No, I did not do all of this by hand.
+	// I made a script to make all of these for me.
+	// Ha HA. ~Inuyasha
+	{           // MT_BSZTALLFLOWER_RED
+		1400,           // doomednum
+		S_BSZTALLFLOWER_RED, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11240,9 +12131,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZBUSH_RED
-		1450,           // doomednum
-		S_BSZBUSH_RED,  // spawnstate
+	{           // MT_BSZTALLFLOWER_PURPLE
+		1401,           // doomednum
+		S_BSZTALLFLOWER_PURPLE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11267,9 +12158,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZBUSH_PURPLE
-		1451,           // doomednum
-		S_BSZBUSH_PURPLE, // spawnstate
+	{           // MT_BSZTALLFLOWER_BLUE
+		1402,           // doomednum
+		S_BSZTALLFLOWER_BLUE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11294,9 +12185,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZBUSH_BLUE
-		1452,           // doomednum
-		S_BSZBUSH_BLUE, // spawnstate
+	{           // MT_BSZTALLFLOWER_CYAN
+		1403,           // doomednum
+		S_BSZTALLFLOWER_CYAN, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11321,9 +12212,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZBUSH_CYAN
-		1453,           // doomednum
-		S_BSZBUSH_CYAN, // spawnstate
+	{           // MT_BSZTALLFLOWER_YELLOW
+		1404,           // doomednum
+		S_BSZTALLFLOWER_YELLOW, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11348,9 +12239,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZBUSH_YELLOW
-		1454,           // doomednum
-		S_BSZBUSH_YELLOW, // spawnstate
+	{           // MT_BSZTALLFLOWER_ORANGE
+		1405,           // doomednum
+		S_BSZTALLFLOWER_ORANGE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11375,9 +12266,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZBUSH_ORANGE
-		1455,           // doomednum
-		S_BSZBUSH_ORANGE, // spawnstate
+	{           // MT_BSZFLOWER_RED
+		1410,           // doomednum
+		S_BSZFLOWER_RED, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11402,9 +12293,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZVINE_RED
-		1460,           // doomednum
-		S_BSZVINE_RED,  // spawnstate
+	{           // MT_BSZFLOWER_PURPLE
+		1411,           // doomednum
+		S_BSZFLOWER_PURPLE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11429,9 +12320,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZVINE_PURPLE
-		1461,           // doomednum
-		S_BSZVINE_PURPLE, // spawnstate
+	{           // MT_BSZFLOWER_BLUE
+		1412,           // doomednum
+		S_BSZFLOWER_BLUE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11456,9 +12347,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZVINE_BLUE
-		1462,           // doomednum
-		S_BSZVINE_BLUE, // spawnstate
+	{           // MT_BSZFLOWER_CYAN
+		1413,           // doomednum
+		S_BSZFLOWER_CYAN, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11483,9 +12374,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZVINE_CYAN
-		1463,           // doomednum
-		S_BSZVINE_CYAN, // spawnstate
+	{           // MT_BSZFLOWER_YELLOW
+		1414,           // doomednum
+		S_BSZFLOWER_YELLOW, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11510,9 +12401,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZVINE_YELLOW
-		1464,           // doomednum
-		S_BSZVINE_YELLOW, // spawnstate
+	{           // MT_BSZFLOWER_ORANGE
+		1415,           // doomednum
+		S_BSZFLOWER_ORANGE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11537,9 +12428,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZVINE_ORANGE
-		1465,           // doomednum
-		S_BSZVINE_ORANGE, // spawnstate
+	{           // MT_BSZSHORTFLOWER_RED
+		1420,           // doomednum
+		S_BSZSHORTFLOWER_RED, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11564,9 +12455,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZSHRUB
-		1470,           // doomednum
-		S_BSZSHRUB,     // spawnstate
+	{           // MT_BSZSHORTFLOWER_PURPLE
+		1421,           // doomednum
+		S_BSZSHORTFLOWER_PURPLE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11591,9 +12482,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSZCLOVER
-		1471,           // doomednum
-		S_BSZCLOVER,    // spawnstate
+	{           // MT_BSZSHORTFLOWER_BLUE
+		1422,           // doomednum
+		S_BSZSHORTFLOWER_BLUE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11618,9 +12509,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BIG_PALMTREE_TRUNK
-		1472,           // doomednum
-		S_BIG_PALMTREE_TRUNK, // spawnstate
+	{           // MT_BSZSHORTFLOWER_CYAN
+		1423,           // doomednum
+		S_BSZSHORTFLOWER_CYAN, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11645,9 +12536,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BIG_PALMTREE_TOP
-		1473,           // doomednum
-		S_BIG_PALMTREE_TOP, // spawnstate
+	{           // MT_BSZSHORTFLOWER_YELLOW
+		1424,           // doomednum
+		S_BSZSHORTFLOWER_YELLOW, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11672,9 +12563,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_PALMTREE_TRUNK
-		1474,           // doomednum
-		S_PALMTREE_TRUNK, // spawnstate
+	{           // MT_BSZSHORTFLOWER_ORANGE
+		1425,           // doomednum
+		S_BSZSHORTFLOWER_ORANGE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11699,9 +12590,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_PALMTREE_TOP
-		1475,           // doomednum
-		S_PALMTREE_TOP, // spawnstate
+	{           // MT_BSZTULIP_RED
+		1430,           // doomednum
+		S_BSZTULIP_RED, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11722,13 +12613,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_DBALL
-		1875,           // doomednum
-		S_DBALL1,       // spawnstate
+	{           // MT_BSZTULIP_PURPLE
+		1431,           // doomednum
+		S_BSZTULIP_PURPLE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11742,20 +12633,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		8,              // speed
+		0,              // speed
 		16*FRACUNIT,    // radius
-		54*FRACUNIT,    // height
+		32*FRACUNIT,    // height
 		0,              // display offset
-		16,             // mass
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_SPAWNCEILING|MF_NOGRAVITY|MF_SCENERY, // flags
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_EGGSTATUE2
-		1876,           // doomednum
-		S_EGGSTATUE2,   // spawnstate
+	{           // MT_BSZTULIP_BLUE
+		1432,           // doomednum
+		S_BSZTULIP_BLUE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11770,263 +12661,263 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		20*FRACUNIT,    // radius
-		96*FRACUNIT,    // height
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		1,              // damage
+		0,              // damage
 		sfx_None,       // activesound
-		MF_SOLID|MF_PUSHABLE|MF_SCENERY, // flags
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_ELEMENTAL_ORB
-		-1,             // doomednum
-		S_ELEM1,        // spawnstate
+	{           // MT_BSZTULIP_CYAN
+		1433,           // doomednum
+		S_BSZTULIP_CYAN, // spawnstate
 		1000,           // spawnhealth
-		S_ELEMF1,       // seestate
+		S_NULL,         // seestate
 		sfx_None,       // seesound
-		0,              // reactiontime
+		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_ELEM13,       // painstate
-		SKINCOLOR_NONE, // painchance
+		S_NULL,         // painstate
+		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		SH_ELEMENTAL,   // speed
-		64*FRACUNIT,    // radius
-		64*FRACUNIT,    // height
-		4,              // display offset
-		16,             // mass
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
-		S_ELEMF9        // raisestate
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		S_NULL          // raisestate
 	},
 
-	{           // MT_ATTRACT_ORB
-		-1,             // doomednum
-		S_MAGN1,        // spawnstate
+	{           // MT_BSZTULIP_YELLOW
+		1434,           // doomednum
+		S_BSZTULIP_YELLOW, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_MAGN13,       // painstate
-		SKINCOLOR_NONE, // painchance
+		S_NULL,         // painstate
+		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		SH_ATTRACT,     // speed
-		64*FRACUNIT,    // radius
-		64*FRACUNIT,    // height
-		4,              // display offset
-		16,             // mass
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_FORCE_ORB
-		-1,             // doomednum
-		S_FORC1,        // spawnstate
+	{           // MT_BSZTULIP_ORANGE
+		1435,           // doomednum
+		S_BSZTULIP_ORANGE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_FORC11,       // painstate
-		SKINCOLOR_NONE, // painchance
+		S_NULL,         // painstate
+		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		SH_FORCE,       // speed
-		64*FRACUNIT,    // radius
-		64*FRACUNIT,    // height
-		4,              // display offset
-		16,             // mass
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
-		S_FORC21        // raisestate
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		S_NULL          // raisestate
 	},
 
-	{           // MT_ARMAGEDDON_ORB
-		-1,             // doomednum
-		S_ARMA1,        // spawnstate
+	{           // MT_BSZCLUSTER_RED
+		1440,           // doomednum
+		S_BSZCLUSTER_RED, // spawnstate
 		1000,           // spawnhealth
-		S_ARMF1,        // seestate
+		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		SKINCOLOR_NONE, // painchance
+		0,              // painchance
 		sfx_None,       // painsound
-		S_ARMB1,        // meleestate
+		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		SH_ARMAGEDDON,  // speed
-		64*FRACUNIT,    // radius
-		64*FRACUNIT,    // height
-		4,              // display offset
-		16,             // mass
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_WHIRLWIND_ORB
-		-1,             // doomednum
-		S_WIND1,        // spawnstate
+	{           // MT_BSZCLUSTER_PURPLE
+		1441,           // doomednum
+		S_BSZCLUSTER_PURPLE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		SKINCOLOR_NONE, // painchance
+		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		SH_WHIRLWIND,        // speed
-		64*FRACUNIT,    // radius
-		64*FRACUNIT,    // height
-		4,              // display offset
-		16,             // mass
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_PITY_ORB
-		-1,             // doomednum
-		S_PITY1,        // spawnstate
+	{           // MT_BSZCLUSTER_BLUE
+		1442,           // doomednum
+		S_BSZCLUSTER_BLUE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		SKINCOLOR_NONE, // painchance
+		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		SH_PITY,        // speed
-		64*FRACUNIT,    // radius
-		64*FRACUNIT,    // height
-		4,              // display offset
-		16,             // mass
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_FLAMEAURA_ORB
-		-1,             // doomednum
-		S_FIRSB1,       // spawnstate
+	{           // MT_BSZCLUSTER_CYAN
+		1443,           // doomednum
+		S_BSZCLUSTER_CYAN, // spawnstate
 		1000,           // spawnhealth
-		S_FIRS1,        // seestate
+		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_FIRSB10,      // painstate
-		SKINCOLOR_NONE, // painchance
+		S_NULL,         // painstate
+		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		SH_FLAMEAURA,   // speed
-		64*FRACUNIT,    // radius
-		64*FRACUNIT,    // height
-		-4,             // display offset
-		16,             // mass
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
-		S_FIRS10        // raisestate
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		S_NULL          // raisestate
 	},
 
-	{           // MT_BUBBLEWRAP_ORB
-		-1,             // doomednum
-		S_BUBSB1,       // spawnstate
+	{           // MT_BSZCLUSTER_YELLOW
+		1444,           // doomednum
+		S_BSZCLUSTER_YELLOW, // spawnstate
 		1000,           // spawnhealth
-		S_BUBS1,        // seestate
+		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_BUBSB5,       // painstate
-		SKINCOLOR_NONE, // painchance
+		S_NULL,         // painstate
+		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		SH_BUBBLEWRAP,  // speed
-		64*FRACUNIT,    // radius
-		64*FRACUNIT,    // height
-		4,              // display offset
-		16,             // mass
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
-		S_BUBS10        // raisestate
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		S_NULL          // raisestate
 	},
 
-	{           // MT_THUNDERCOIN_ORB
-		-1,             // doomednum
-		S_ZAPSB1,       // spawnstate
+	{           // MT_BSZCLUSTER_ORANGE
+		1445,           // doomednum
+		S_BSZCLUSTER_ORANGE, // spawnstate
 		1000,           // spawnhealth
-		S_ZAPS1,        // seestate
+		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_ZAPSB11,      // painstate
-		SKINCOLOR_NONE, // painchance
+		S_NULL,         // painstate
+		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		SH_THUNDERCOIN, // speed
-		64*FRACUNIT,    // radius
-		64*FRACUNIT,    // height
-		-4,             // display offset
-		16,             // mass
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
-		S_ZAPS14        // raisestate
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		S_NULL          // raisestate
 	},
 
-	{           // MT_THUNDERCOIN_SPARK
-		-1,             // doomednum
-		S_THUNDERCOIN_SPARK, // spawnstate
-		1,              // spawnhealth
+	{           // MT_BSZBUSH_RED
+		1450,           // doomednum
+		S_BSZBUSH_RED,  // spawnstate
+		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
@@ -12040,19 +12931,19 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		4*FRACUNIT,     // radius
-		4*FRACUNIT,     // height
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_IVSP
-		-1,             // doomednum
-		S_IVSP,         // spawnstate
+	{           // MT_BSZBUSH_PURPLE
+		1451,           // doomednum
+		S_BSZBUSH_PURPLE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -12066,21 +12957,21 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		8,              // speed
-		64*FRACUNIT,    // radius
-		64*FRACUNIT,    // height
-		3,              // display offset
-		16,             // mass
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY, // flags
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_SUPERSPARK
-		-1,             // doomednum
-		S_SSPK1,        // spawnstate
-		1,              // spawnhealth
+	{           // MT_BSZBUSH_BLUE
+		1452,           // doomednum
+		S_BSZBUSH_BLUE, // spawnstate
+		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
@@ -12094,20 +12985,19 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		8*FRACUNIT,     // radius
-		8*FRACUNIT,     // height
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	// Bluebird
-	{           // MT_FLICKY_01
-		-1,             // doomednum
-		S_FLICKY_01_OUT, // spawnstate
+	{           // MT_BSZBUSH_CYAN
+		1453,           // doomednum
+		S_BSZBUSH_CYAN, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -12121,20 +13011,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		8,              // speed
-		8*FRACUNIT,     // radius
-		20*FRACUNIT,    // height
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
 		0,              // display offset
-		16,             // mass
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOCLIPTHING, // flags
-		S_FLICKY_BUBBLE // raisestate
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		S_NULL          // raisestate
 	},
 
-	{           // MT_FLICKY_02
-		-1,             // doomednum
-		S_FLICKY_02_OUT, // spawnstate
+	{           // MT_BSZBUSH_YELLOW
+		1454,           // doomednum
+		S_BSZBUSH_YELLOW, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -12148,20 +13038,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		8,              // speed
-		8*FRACUNIT,     // radius
-		20*FRACUNIT,    // height
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
 		0,              // display offset
-		16,             // mass
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOCLIPTHING, // flags
-		S_FLICKY_BUBBLE // raisestate
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		S_NULL          // raisestate
 	},
 
-	{           // MT_FLICKY_03
-		-1,             // doomednum
-		S_FLICKY_03_OUT, // spawnstate
+	{           // MT_BSZBUSH_ORANGE
+		1455,           // doomednum
+		S_BSZBUSH_ORANGE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -12175,20 +13065,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		8,              // speed
-		8*FRACUNIT,     // radius
-		20*FRACUNIT,    // height
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
 		0,              // display offset
-		16,             // mass
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOCLIPTHING, // flags
-		S_FLICKY_BUBBLE // raisestate
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		S_NULL          // raisestate
 	},
 
-	{           // MT_FLICKY_04
-		-1,             // doomednum
-		S_FLICKY_04_OUT, // spawnstate
+	{           // MT_BSZVINE_RED
+		1460,           // doomednum
+		S_BSZVINE_RED,  // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -12197,25 +13087,25 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // painstate
 		0,              // painchance
 		sfx_None,       // painsound
-		S_FLICKY_04_SWIM1, // meleestate
+		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		8,              // speed
-		8*FRACUNIT,     // radius
-		20*FRACUNIT,    // height
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
 		0,              // display offset
-		16,             // mass
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOCLIPTHING, // flags
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_FLICKY_05
-		-1,             // doomednum
-		S_FLICKY_05_OUT, // spawnstate
+	{           // MT_BSZVINE_PURPLE
+		1461,           // doomednum
+		S_BSZVINE_PURPLE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -12229,20 +13119,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		8,              // speed
-		8*FRACUNIT,     // radius
-		20*FRACUNIT,    // height
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
 		0,              // display offset
-		16,             // mass
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOCLIPTHING, // flags
-		S_FLICKY_BUBBLE // raisestate
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		S_NULL          // raisestate
 	},
 
-	{           // MT_FLICKY_06
-		-1,             // doomednum
-		S_FLICKY_06_OUT, // spawnstate
+	{           // MT_BSZVINE_BLUE
+		1462,           // doomednum
+		S_BSZVINE_BLUE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -12256,20 +13146,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		8,              // speed
-		8*FRACUNIT,     // radius
-		20*FRACUNIT,    // height
-		0,              // display offset
-		16,             // mass
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOCLIPTHING, // flags
-		S_FLICKY_BUBBLE // raisestate
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		S_NULL          // raisestate
 	},
 
-	{           // MT_FLICKY_07
-		-1,             // doomednum
-		S_FLICKY_07_OUT, // spawnstate
+	{           // MT_BSZVINE_CYAN
+		1463,           // doomednum
+		S_BSZVINE_CYAN, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -12278,25 +13168,25 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // painstate
 		0,              // painchance
 		sfx_None,       // painsound
-		S_FLICKY_07_SWIM1, // meleestate
+		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		8,              // speed
-		8*FRACUNIT,     // radius
-		20*FRACUNIT,    // height
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
 		0,              // display offset
-		16,             // mass
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOCLIPTHING, // flags
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_FLICKY_08
-		-1,             // doomednum
-		S_FLICKY_08_OUT, // spawnstate
+	{           // MT_BSZVINE_YELLOW
+		1464,           // doomednum
+		S_BSZVINE_YELLOW, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -12305,25 +13195,25 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // painstate
 		0,              // painchance
 		sfx_None,       // painsound
-		S_FLICKY_08_SWIM1, // meleestate
+		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		8,              // speed
-		8*FRACUNIT,     // radius
-		20*FRACUNIT,    // height
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
 		0,              // display offset
-		16,             // mass
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOCLIPTHING, // flags
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_FLICKY_09
-		-1,             // doomednum
-		S_FLICKY_09_OUT, // spawnstate
+	{           // MT_BSZVINE_ORANGE
+		1465,           // doomednum
+		S_BSZVINE_ORANGE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -12337,20 +13227,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		8,              // speed
-		8*FRACUNIT,     // radius
-		20*FRACUNIT,    // height
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
 		0,              // display offset
-		16,             // mass
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOCLIPTHING, // flags
-		S_FLICKY_BUBBLE // raisestate
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		S_NULL          // raisestate
 	},
 
-	{           // MT_FLICKY_10
-		-1,             // doomednum
-		S_FLICKY_10_OUT, // spawnstate
+	{           // MT_BSZSHRUB
+		1470,           // doomednum
+		S_BSZSHRUB,     // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -12364,20 +13254,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		8,              // speed
-		8*FRACUNIT,     // radius
-		20*FRACUNIT,    // height
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
 		0,              // display offset
-		16,             // mass
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOCLIPTHING, // flags
-		S_FLICKY_BUBBLE // raisestate
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		S_NULL          // raisestate
 	},
 
-	{           // MT_FLICKY_11
-		-1,             // doomednum
-		S_FLICKY_11_OUT, // spawnstate
+	{           // MT_BSZCLOVER
+		1471,           // doomednum
+		S_BSZCLOVER,    // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -12391,20 +13281,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		8,              // speed
-		8*FRACUNIT,     // radius
-		20*FRACUNIT,    // height
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
 		0,              // display offset
-		16,             // mass
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOCLIPTHING, // flags
-		S_FLICKY_BUBBLE // raisestate
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		S_NULL          // raisestate
 	},
 
-	{           // MT_FLICKY_12
+	{           // MT_BIG_PALMTREE_TRUNK
 		-1,             // doomednum
-		S_FLICKY_12_OUT, // spawnstate
+		S_BIG_PALMTREE_TRUNK, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -12418,20 +13308,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		8,              // speed
-		8*FRACUNIT,     // radius
-		20*FRACUNIT,    // height
+		0,              // speed
+		16*FRACUNIT,    // radius
+		160*FRACUNIT,   // height
 		0,              // display offset
-		16,             // mass
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOCLIPTHING, // flags
-		S_FLICKY_BUBBLE // raisestate
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		S_NULL          // raisestate
 	},
 
-	{           // MT_FLICKY_13
-		-1,             // doomednum
-		S_FLICKY_13_OUT, // spawnstate
+	{           // MT_BIG_PALMTREE_TOP
+		1473,           // doomednum
+		S_BIG_PALMTREE_TOP, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -12445,20 +13335,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		8,              // speed
-		8*FRACUNIT,     // radius
-		20*FRACUNIT,    // height
+		0,              // speed
+		16*FRACUNIT,    // radius
+		160*FRACUNIT,   // height
 		0,              // display offset
-		16,             // mass
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOCLIPTHING, // flags
-		S_FLICKY_BUBBLE // raisestate
+		MF_RUNSPAWNFUNC|MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		S_NULL          // raisestate
 	},
 
-	{           // MT_FLICKY_14
+	{           // MT_PALMTREE_TRUNK
 		-1,             // doomednum
-		S_FLICKY_14_OUT, // spawnstate
+		S_PALMTREE_TRUNK, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -12472,20 +13362,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		8,              // speed
-		8*FRACUNIT,     // radius
-		20*FRACUNIT,    // height
+		0,              // speed
+		16*FRACUNIT,    // radius
+		80*FRACUNIT,    // height
 		0,              // display offset
-		16,             // mass
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOCLIPTHING, // flags
-		S_FLICKY_BUBBLE // raisestate
+		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		S_NULL          // raisestate
 	},
 
-	{           // MT_FLICKY_15
-		-1,             // doomednum
-		S_FLICKY_15_OUT, // spawnstate
+	{           // MT_PALMTREE_TOP
+		1475,           // doomednum
+		S_PALMTREE_TOP, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -12499,20 +13389,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		8,              // speed
-		8*FRACUNIT,     // radius
-		20*FRACUNIT,    // height
+		0,              // speed
+		16*FRACUNIT,    // radius
+		80*FRACUNIT,    // height
 		0,              // display offset
-		16,             // mass
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOCLIPTHING, // flags
-		S_FLICKY_BUBBLE // raisestate
+		MF_RUNSPAWNFUNC|MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		S_NULL          // raisestate
 	},
 
-	{           // MT_FLICKY_16
-		-1,             // doomednum
-		S_FLICKY_16_OUT, // spawnstate
+	{           // MT_DBALL
+		1875,           // doomednum
+		S_DBALL1,       // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -12527,19 +13417,19 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		8,              // speed
-		8*FRACUNIT,     // radius
-		20*FRACUNIT,    // height
+		16*FRACUNIT,    // radius
+		54*FRACUNIT,    // height
 		0,              // display offset
 		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOCLIPTHING, // flags
-		S_FLICKY_BUBBLE // raisestate
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_SPAWNCEILING|MF_NOGRAVITY|MF_SCENERY, // flags
+		S_NULL          // raisestate
 	},
 
-	{           // MT_RAIN
-		-1,             // doomednum
-		S_RAIN1,        // spawnstate
+	{           // MT_EGGSTATUE2
+		1876,           // doomednum
+		S_EGGSTATUE2,   // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -12553,291 +13443,264 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		-24*FRACUNIT,   // speed
-		1*FRACUNIT,     // radius
-		8*FRACUNIT,     // height
+		0,              // speed
+		20*FRACUNIT,    // radius
+		96*FRACUNIT,    // height
 		0,              // display offset
-		4,              // mass
-		0,              // damage
+		100,            // mass
+		1,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP,  // flags
+		MF_SOLID|MF_PUSHABLE|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_SNOWFLAKE
+	{           // MT_ELEMENTAL_ORB
 		-1,             // doomednum
-		S_SNOW1,        // spawnstate
+		S_ELEM1,        // spawnstate
 		1000,           // spawnhealth
-		S_NULL,         // seestate
+		S_ELEMF1,       // seestate
 		sfx_None,       // seesound
-		8,              // reactiontime
+		0,              // reactiontime
 		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		0,              // painchance
+		S_ELEM13,       // painstate
+		SKINCOLOR_NONE, // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		-2*FRACUNIT,    // speed
-		4*FRACUNIT,     // radius
-		4*FRACUNIT,     // height
-		0,              // display offset
-		4,              // mass
+		SH_ELEMENTAL,   // speed
+		64*FRACUNIT,    // radius
+		64*FRACUNIT,    // height
+		4,              // display offset
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP,  // flags
-		S_NULL          // raisestate
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
+		S_ELEMF9        // raisestate
 	},
 
-	{           // MT_SPLISH
+	{           // MT_ATTRACT_ORB
 		-1,             // doomednum
-		S_SPLISH1,      // spawnstate
+		S_MAGN1,        // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		0,              // painchance
+		S_MAGN13,       // painstate
+		SKINCOLOR_NONE, // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
-		6*FRACUNIT,     // radius
-		1*FRACUNIT,     // height
-		0,              // display offset
-		100,            // mass
-		1,              // damage
+		SH_ATTRACT,     // speed
+		64*FRACUNIT,    // radius
+		64*FRACUNIT,    // height
+		4,              // display offset
+		16,             // mass
+		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_SMOKE
+	{           // MT_FORCE_ORB
 		-1,             // doomednum
-		S_SMOKE1,       // spawnstate
+		S_FORC1,        // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		0,              // painchance
+		S_FORC11,       // painstate
+		SKINCOLOR_NONE, // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
-		20*FRACUNIT,    // radius
-		16*FRACUNIT,    // height
-		0,              // display offset
-		100,            // mass
+		SH_FORCE,       // speed
+		64*FRACUNIT,    // radius
+		64*FRACUNIT,    // height
+		4,              // display offset
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_SCENERY, // flags
-		S_NULL          // raisestate
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
+		S_FORC21        // raisestate
 	},
 
-	{           // MT_SMALLBUBBLE
+	{           // MT_ARMAGEDDON_ORB
 		-1,             // doomednum
-		S_SMALLBUBBLE,  // spawnstate
+		S_ARMA1,        // spawnstate
 		1000,           // spawnhealth
-		S_NULL,         // seestate
+		S_ARMF1,        // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		0,              // painchance
+		SKINCOLOR_NONE, // painchance
 		sfx_None,       // painsound
-		S_NULL,         // meleestate
+		S_ARMB1,        // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		8,              // speed
-		4*FRACUNIT,     // radius
-		4*FRACUNIT,     // height
-		0,              // display offset
+		SH_ARMAGEDDON,  // speed
+		64*FRACUNIT,    // radius
+		64*FRACUNIT,    // height
+		4,              // display offset
 		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_MEDIUMBUBBLE
+	{           // MT_WHIRLWIND_ORB
 		-1,             // doomednum
-		S_MEDIUMBUBBLE, // spawnstate
+		S_WIND1,        // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		0,              // painchance
+		SKINCOLOR_NONE, // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		8,              // speed
-		8*FRACUNIT,     // radius
-		8*FRACUNIT,     // height
-		0,              // display offset
+		SH_WHIRLWIND,        // speed
+		64*FRACUNIT,    // radius
+		64*FRACUNIT,    // height
+		4,              // display offset
 		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_EXTRALARGEBUBBLE
+	{           // MT_PITY_ORB
 		-1,             // doomednum
-		S_LARGEBUBBLE1, // spawnstate
+		S_PITY1,        // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		0,              // painchance
+		SKINCOLOR_NONE, // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_POP1,         // deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_gasp,       // deathsound
-		8,              // speed
-		23*FRACUNIT,    // radius
-		43*FRACUNIT,    // height
-		0,              // display offset
+		sfx_None,       // deathsound
+		SH_PITY,        // speed
+		64*FRACUNIT,    // radius
+		64*FRACUNIT,    // height
+		4,              // display offset
 		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_SPECIAL|MF_NOGRAVITY|MF_SCENERY, // flags
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_WATERZAP
+	{           // MT_FLAMEAURA_ORB
 		-1,             // doomednum
-		S_WATERZAP,     // spawnstate
+		S_FIRSB1,       // spawnstate
 		1000,           // spawnhealth
-		S_NULL,         // seestate
+		S_FIRS1,        // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		0,              // painchance
+		S_FIRSB10,      // painstate
+		SKINCOLOR_NONE, // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		8,              // speed
-		4*FRACUNIT,     // radius
-		4*FRACUNIT,     // height
-		0,              // display offset
+		SH_FLAMEAURA,   // speed
+		64*FRACUNIT,    // radius
+		64*FRACUNIT,    // height
+		-4,             // display offset
 		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags
-		S_NULL          // raisestate
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
+		S_FIRS10        // raisestate
 	},
 
-	{           // MT_SPINDUST
+	{           // MT_BUBBLEWRAP_ORB
 		-1,             // doomednum
-		S_SPINDUST1,     // spawnstate
+		S_BUBSB1,       // spawnstate
 		1000,           // spawnhealth
-		S_NULL,         // seestate
+		S_BUBS1,        // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		0,              // painchance
+		S_BUBSB5,       // painstate
+		SKINCOLOR_NONE, // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		4*FRACUNIT,     // speed
-		4*FRACUNIT,     // radius
-		4*FRACUNIT,     // height
-		0,              // display offset
-		4,              // mass
+		SH_BUBBLEWRAP,  // speed
+		64*FRACUNIT,    // radius
+		64*FRACUNIT,    // height
+		4,              // display offset
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIP, // flags
-		S_NULL          // raisestate
-	},
-
-	{           // MT_TFOG
-		-1,             // doomednum
-		S_FOG1,         // spawnstate
-		1,              // spawnhealth
-		S_NULL,         // seestate
-		sfx_None,       // seesound
-		8,              // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		0,              // painchance
-		sfx_None,       // painsound
-		S_NULL,         // meleestate
-		S_NULL,         // missilestate
-		S_NULL,         // deathstate
-		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
-		0,              // speed
-		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
-		0,              // display offset
-		100,            // mass
-		1,              // damage
-		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP, // flags
-		S_NULL          // raisestate
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
+		S_BUBS10        // raisestate
 	},
 
-	{           // MT_SEED
+	{           // MT_THUNDERCOIN_ORB
 		-1,             // doomednum
-		S_SEED,         // spawnstate
+		S_ZAPSB1,       // spawnstate
 		1000,           // spawnhealth
-		S_NULL,         // seestate
+		S_ZAPS1,        // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		0,              // painchance
+		S_ZAPSB11,      // painstate
+		SKINCOLOR_NONE, // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		-2*FRACUNIT,    // speed
-		4*FRACUNIT,     // radius
-		4*FRACUNIT,     // height
-		0,              // display offset
-		4,              // mass
+		SH_THUNDERCOIN, // speed
+		64*FRACUNIT,    // radius
+		64*FRACUNIT,    // height
+		-4,             // display offset
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_SCENERY, // flags
-		S_NULL          // raisestate
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
+		S_ZAPS14        // raisestate
 	},
 
-	{           // MT_PARTICLE
+	{           // MT_THUNDERCOIN_SPARK
 		-1,             // doomednum
-		S_PARTICLE,     // spawnstate
-		1000,           // spawnhealth
+		S_THUNDERCOIN_SPARK, // spawnstate
+		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
@@ -12850,20 +13713,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		4*FRACUNIT,     // speed
+		0,              // speed
 		4*FRACUNIT,     // radius
 		4*FRACUNIT,     // height
 		0,              // display offset
-		4,              // mass
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIP, // flags
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_PARTICLEGEN
-		757,            // doomednum
-		S_PARTICLEGEN,  // spawnstate
+	{           // MT_IVSP
+		-1,             // doomednum
+		S_IVSP,         // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -12878,19 +13741,19 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		8,              // speed
-		1*FRACUNIT,     // radius
-		1*FRACUNIT,     // height
-		0,              // display offset
+		64*FRACUNIT,    // radius
+		64*FRACUNIT,    // height
+		3,              // display offset
 		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOCLIP|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_SCORE
+	{           // MT_SUPERSPARK
 		-1,             // doomednum
-		S_SCRA,         // spawnstate
+		S_SSPK1,        // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -12904,22 +13767,23 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		3*FRACUNIT,     // speed
+		0,              // speed
 		8*FRACUNIT,     // radius
 		8*FRACUNIT,     // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_DROWNNUMBERS
+	// Bluebird
+	{           // MT_FLICKY_01
 		-1,             // doomednum
-		S_ZERO1,        // spawnstate
+		S_FLICKY_01_OUT, // spawnstate
 		1000,           // spawnhealth
-		S_NULL,         // seestate
+		S_FLICKY_01_STAND, // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
@@ -12933,18 +13797,18 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // deathsound
 		8,              // speed
 		8*FRACUNIT,     // radius
-		8*FRACUNIT,     // height
-		113,            // display offset
+		20*FRACUNIT,    // height
+		0,              // display offset
 		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags
-		S_NULL          // raisestate
+		MF_NOCLIPTHING, // flags
+		S_FLICKY_BUBBLE // raisestate
 	},
 
-	{           // MT_GOTEMERALD
-		-1,             // doomednum
-		S_CEMG1,        // spawnstate
+	{           // MT_FLICKY_01_CENTER
+		2200,             // doomednum
+		S_FLICKY_01_CENTER, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -12953,27 +13817,27 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // painstate
 		0,              // painchance
 		sfx_None,       // painsound
-		S_ORBITEM1,     // meleestate
+		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		8,              // speed
 		8*FRACUNIT,     // radius
-		16*FRACUNIT,    // height
-		112,            // display offset
+		20*FRACUNIT,    // height
+		0,              // display offset
 		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_SCENERY, // flags
-		S_NULL          // raisestate
+		MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_SCENERY|MF_RUNSPAWNFUNC, // flags
+		S_NULL // raisestate
 	},
 
-	{           // MT_LOCKON
+	{           // MT_FLICKY_02
 		-1,             // doomednum
-		S_LOCKON1,       // spawnstate
+		S_FLICKY_02_OUT, // spawnstate
 		1000,           // spawnhealth
-		S_NULL,         // seestate
+		S_FLICKY_02_STAND, // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
@@ -12986,19 +13850,19 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		8,              // speed
-		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
-		111,            // display offset
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
+		0,              // display offset
 		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
-		S_NULL          // raisestate
+		MF_NOCLIPTHING, // flags
+		S_FLICKY_BUBBLE // raisestate
 	},
 
-	{           // MT_TAG
-		-1,             // doomednum
-		S_TTAG,         // spawnstate
+	{           // MT_FLICKY_02_CENTER
+		2201,             // doomednum
+		S_FLICKY_02_CENTER, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -13013,21 +13877,21 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		8,              // speed
-		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
-		111,            // display offset
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
+		0,              // display offset
 		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags
-		S_NULL          // raisestate
+		MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_SCENERY|MF_RUNSPAWNFUNC, // flags
+		S_NULL // raisestate
 	},
 
-	{           // MT_GOTFLAG
+	{           // MT_FLICKY_03
 		-1,             // doomednum
-		S_GOTFLAG,      // spawnstate
+		S_FLICKY_03_OUT, // spawnstate
 		1000,           // spawnhealth
-		S_NULL,         // seestate
+		S_FLICKY_03_STAND, // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
@@ -13040,23 +13904,22 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		8,              // speed
-		64*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
-		111,            // display offset
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
+		0,              // display offset
 		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags
-		S_NULL          // raisestate
+		MF_NOCLIPTHING, // flags
+		S_FLICKY_BUBBLE // raisestate
 	},
 
-	// ambient water 1a (large)
-	{           // MT_AWATERA
-		700,            // doomednum
-		S_INVISIBLE,    // spawnstate
-		35,             // spawnhealth
+	{           // MT_FLICKY_03_CENTER
+		2202,             // doomednum
+		S_FLICKY_03_CENTER, // spawnstate
+		1000,           // spawnhealth
 		S_NULL,         // seestate
-		sfx_amwtr1,     // seesound
+		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
@@ -13067,52 +13930,50 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
-		16*FRACUNIT,    // radius
-		16*FRACUNIT,    // height
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOGRAVITY|MF_AMBIENT, // flags
-		S_NULL          // raisestate
+		MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_SCENERY|MF_RUNSPAWNFUNC, // flags
+		S_NULL // raisestate
 	},
 
-	// ambient water 1b (large)
-	{           // MT_AWATERB
-		701,            // doomednum
-		S_INVISIBLE,    // spawnstate
-		35,             // spawnhealth
-		S_NULL,         // seestate
-		sfx_amwtr2,     // seesound
+	{           // MT_FLICKY_04
+		-1,             // doomednum
+		S_FLICKY_04_OUT, // spawnstate
+		1000,           // spawnhealth
+		S_FLICKY_04_STAND, // seestate
+		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
 		sfx_None,       // painsound
-		S_NULL,         // meleestate
+		S_FLICKY_04_SWIM1, // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
-		16*FRACUNIT,    // radius
-		16*FRACUNIT,    // height
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOGRAVITY|MF_AMBIENT, // flags
+		MF_NOCLIPTHING, // flags
 		S_NULL          // raisestate
 	},
 
-	// ambient water 2a (medium)
-	{           // MT_AWATERC
-		702,            // doomednum
-		S_INVISIBLE,    // spawnstate
-		35,             // spawnhealth
+	{           // MT_FLICKY_04_CENTER
+		2203,             // doomednum
+		S_FLICKY_04_CENTER, // spawnstate
+		1000,           // spawnhealth
 		S_NULL,         // seestate
-		sfx_amwtr3,     // seesound
+		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
@@ -13123,24 +13984,23 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
-		16*FRACUNIT,    // radius
-		16*FRACUNIT,    // height
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOGRAVITY|MF_AMBIENT, // flags
-		S_NULL          // raisestate
+		MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_SCENERY|MF_RUNSPAWNFUNC, // flags
+		S_NULL // raisestate
 	},
 
-	// ambient water 2b (medium)
-	{           // MT_AWATERD
-		703,            // doomednum
-		S_INVISIBLE,    // spawnstate
-		35,             // spawnhealth
-		S_NULL,         // seestate
-		sfx_amwtr4,     // seesound
+	{           // MT_FLICKY_05
+		-1,             // doomednum
+		S_FLICKY_05_OUT, // spawnstate
+		1000,           // spawnhealth
+		S_FLICKY_05_STAND, // seestate
+		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
@@ -13151,24 +14011,23 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
-		16*FRACUNIT,    // radius
-		16*FRACUNIT,    // height
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOGRAVITY|MF_AMBIENT, // flags
-		S_NULL          // raisestate
+		MF_NOCLIPTHING, // flags
+		S_FLICKY_BUBBLE // raisestate
 	},
 
-	// ambient water 3a (small)
-	{           // MT_AWATERE
-		704,            // doomednum
-		S_INVISIBLE,    // spawnstate
-		35,             // spawnhealth
+	{           // MT_FLICKY_05_CENTER
+		2204,             // doomednum
+		S_FLICKY_05_CENTER, // spawnstate
+		1000,           // spawnhealth
 		S_NULL,         // seestate
-		sfx_amwtr5,     // seesound
+		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
@@ -13179,24 +14038,23 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
-		16*FRACUNIT,    // radius
-		16*FRACUNIT,    // height
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOGRAVITY|MF_AMBIENT, // flags
-		S_NULL          // raisestate
+		MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_SCENERY|MF_RUNSPAWNFUNC, // flags
+		S_NULL // raisestate
 	},
 
-	// ambient water 3b (small)
-	{           // MT_AWATERF
-		705,            // doomednum
-		S_INVISIBLE,    // spawnstate
-		35,             // spawnhealth
-		S_NULL,         // seestate
-		sfx_amwtr6,     // seesound
+	{           // MT_FLICKY_06
+		-1,             // doomednum
+		S_FLICKY_06_OUT, // spawnstate
+		1000,           // spawnhealth
+		S_FLICKY_06_STAND, // seestate
+		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
@@ -13207,24 +14065,23 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
-		16*FRACUNIT,    // radius
-		16*FRACUNIT,    // height
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOGRAVITY|MF_AMBIENT, // flags
-		S_NULL          // raisestate
+		MF_NOCLIPTHING, // flags
+		S_FLICKY_BUBBLE // raisestate
 	},
 
-	// ambient water 4a (extra large)
-	{           // MT_AWATERG
-		706,            // doomednum
-		S_INVISIBLE,    // spawnstate
-		35,             // spawnhealth
+	{           // MT_FLICKY_06_CENTER
+		2205,             // doomednum
+		S_FLICKY_06_CENTER, // spawnstate
+		1000,           // spawnhealth
 		S_NULL,         // seestate
-		sfx_amwtr7,     // seesound
+		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
@@ -13235,512 +14092,2354 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
-		16*FRACUNIT,    // radius
-		16*FRACUNIT,    // height
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOGRAVITY|MF_AMBIENT, // flags
-		S_NULL          // raisestate
+		MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_SCENERY|MF_RUNSPAWNFUNC, // flags
+		S_NULL // raisestate
 	},
 
-	// ambient water 4b (extra large)
-	{           // MT_AWATERH
-		707,            // doomednum
-		S_INVISIBLE,    // spawnstate
-		35,             // spawnhealth
-		S_NULL,         // seestate
-		sfx_amwtr8,     // seesound
+	{           // MT_FLICKY_07
+		-1,             // doomednum
+		S_FLICKY_07_OUT, // spawnstate
+		1000,           // spawnhealth
+		S_FLICKY_07_STAND, // seestate
+		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
 		sfx_None,       // painsound
-		S_NULL,         // meleestate
+		S_FLICKY_07_SWIM1, // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
-		16*FRACUNIT,    // radius
-		16*FRACUNIT,    // height
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOGRAVITY|MF_AMBIENT, // flags
+		MF_NOCLIPTHING, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_RANDOMAMBIENT
-		708,            // doomednum
-		S_INVISIBLE,    // spawnstate
-		512,            // spawnhealth: repeat speed
+	{           // MT_FLICKY_07_CENTER
+		2206,             // doomednum
+		S_FLICKY_07_CENTER, // spawnstate
+		1000,           // spawnhealth
 		S_NULL,         // seestate
-		sfx_ambint,     // seesound
-		0,              // reactiontime
+		sfx_None,       // seesound
+		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		255,            // painchance
+		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
+		8,              // speed
 		8*FRACUNIT,     // radius
-		16*FRACUNIT,    // height
+		20*FRACUNIT,    // height
 		0,              // display offset
-		1000,           // mass
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_AMBIENT, // flags
-		S_NULL          // raisestate
+		MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_SCENERY|MF_RUNSPAWNFUNC, // flags
+		S_NULL // raisestate
 	},
 
-	{           // MT_RANDOMAMBIENT2
-		709,            // doomednum
-		S_INVISIBLE,    // spawnstate
-		220,            // spawnhealth: repeat speed
-		S_NULL,         // seestate
-		sfx_ambin2,     // seesound
-		0,              // reactiontime
+	{           // MT_FLICKY_08
+		-1,             // doomednum
+		S_FLICKY_08_OUT, // spawnstate
+		1000,           // spawnhealth
+		S_FLICKY_08_STAND, // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		255,            // painchance
+		0,              // painchance
 		sfx_None,       // painsound
-		S_NULL,         // meleestate
+		S_FLICKY_08_SWIM1, // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
+		8,              // speed
 		8*FRACUNIT,     // radius
-		16*FRACUNIT,    // height
+		20*FRACUNIT,    // height
 		0,              // display offset
-		1000,           // mass
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_AMBIENT, // flags
+		MF_NOCLIPTHING, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_CORK
-		-1,             // doomednum
-		S_CORK,         // spawnstate
+	{           // MT_FLICKY_08_CENTER
+		2207,             // doomednum
+		S_FLICKY_08_CENTER, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
-		sfx_corkp,      // seesound
-		0,              // reactiontime
+		sfx_None,       // seesound
+		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SMOKE1,       // deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_corkh,      // deathsound
-		60*FRACUNIT,    // speed
-		16*FRACUNIT,    // radius
-		16*FRACUNIT,    // height
+		sfx_None,       // deathsound
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
-		1,              // damage
+		16,             // mass
+		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags
-		S_NULL          // raisestate
+		MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_SCENERY|MF_RUNSPAWNFUNC, // flags
+		S_NULL // raisestate
 	},
 
-	{           // MT_REDRING
+	{           // MT_FLICKY_09
 		-1,             // doomednum
-		S_RRNG1,        // spawnstate
+		S_FLICKY_09_OUT, // spawnstate
 		1000,           // spawnhealth
-		S_NULL,         // seestate
-		sfx_wepfir,     // seesound
-		0,              // reactiontime
+		S_FLICKY_09_STAND, // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SPRK1,        // deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_itemup,     // deathsound
-		60*FRACUNIT,    // speed
-		16*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		sfx_None,       // deathsound
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
-		1,              // damage
+		16,             // mass
+		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags
-		S_NULL          // raisestate
+		MF_NOCLIPTHING, // flags
+		S_FLICKY_BUBBLE // raisestate
 	},
 
-// Ring ammo: Health = amount given
-	{           // MT_BOUNCERING
-		301,            // doomednum
-		S_BOUNCERINGAMMO, // spawnstate
-		10,             // spawnhealth
+	{           // MT_FLICKY_09_CENTER
+		2208,             // doomednum
+		S_FLICKY_09_CENTER, // spawnstate
+		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
-		0,              // reactiontime
+		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SPRK1,        // deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_itemup,     // deathsound
-		60*FRACUNIT,    // speed
-		24*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		sfx_None,       // deathsound
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
 		0,              // display offset
-		pw_bouncering,  // mass
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
-		S_NULL          // raisestate
+		MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_SCENERY|MF_RUNSPAWNFUNC, // flags
+		S_NULL // raisestate
 	},
 
-	{           // MT_RAILRING
-		302,            // doomednum
-		S_RAILRINGAMMO, // spawnstate
-		5,              // spawnhealth
-		S_NULL,         // seestate
+	{           // MT_FLICKY_10
+		-1,             // doomednum
+		S_FLICKY_10_OUT, // spawnstate
+		1000,           // spawnhealth
+		S_FLICKY_10_STAND, // seestate
 		sfx_None,       // seesound
-		0,              // reactiontime
+		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SPRK1,        // deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_itemup,     // deathsound
-		60*FRACUNIT,    // speed
-		24*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		sfx_None,       // deathsound
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
 		0,              // display offset
-		pw_railring,    // mass
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
-		S_NULL          // raisestate
+		MF_NOCLIPTHING, // flags
+		S_FLICKY_BUBBLE // raisestate
 	},
 
-	{           // MT_INFINITYRING
-		303,            // doomednum
-		S_INFINITYRINGAMMO,// spawnstate
-		80,             // spawnhealth
+	{           // MT_FLICKY_10_CENTER
+		2209,             // doomednum
+		S_FLICKY_10_CENTER, // spawnstate
+		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
-		0,              // reactiontime
+		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SPRK1,        // deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_itemup,     // deathsound
-		60*FRACUNIT,    // speed
-		24*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		sfx_None,       // deathsound
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
 		0,              // display offset
-		pw_infinityring,// mass
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
-		S_NULL          // raisestate
+		MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_SCENERY|MF_RUNSPAWNFUNC, // flags
+		S_NULL // raisestate
 	},
 
-	{           // MT_AUTOMATICRING
-		304,            // doomednum
-		S_AUTOMATICRINGAMMO, // spawnstate
-		40,             // spawnhealth
-		S_NULL,         // seestate
+	{           // MT_FLICKY_11
+		-1,             // doomednum
+		S_FLICKY_11_OUT, // spawnstate
+		1000,           // spawnhealth
+		S_FLICKY_11_STAND, // seestate
 		sfx_None,       // seesound
-		0,              // reactiontime
+		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SPRK1,        // deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_itemup,     // deathsound
-		60*FRACUNIT,    // speed
-		24*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		sfx_None,       // deathsound
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
 		0,              // display offset
-		pw_automaticring, // mass
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
-		S_NULL          // raisestate
+		MF_NOCLIPTHING, // flags
+		S_FLICKY_BUBBLE // raisestate
 	},
 
-	{           // MT_EXPLOSIONRING
-		305,            // doomednum
-		S_EXPLOSIONRINGAMMO, // spawnstate
-		5,              // spawnhealth
+	{           // MT_FLICKY_11_CENTER
+		2210,             // doomednum
+		S_FLICKY_11_CENTER, // spawnstate
+		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
-		0,              // reactiontime
+		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SPRK1,        // deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_itemup,     // deathsound
-		60*FRACUNIT,    // speed
-		24*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		sfx_None,       // deathsound
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
 		0,              // display offset
-		pw_explosionring, // mass
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
-		S_NULL          // raisestate
+		MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_SCENERY|MF_RUNSPAWNFUNC, // flags
+		S_NULL // raisestate
 	},
 
-	{           // MT_SCATTERRING
-		306,            // doomednum
-		S_SCATTERRINGAMMO, // spawnstate
-		5,              // spawnhealth
-		S_NULL,         // seestate
+	{           // MT_FLICKY_12
+		-1,             // doomednum
+		S_FLICKY_12_OUT, // spawnstate
+		1000,           // spawnhealth
+		S_FLICKY_12_STAND, // seestate
 		sfx_None,       // seesound
-		0,              // reactiontime
+		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SPRK1,        // deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_itemup,     // deathsound
-		60*FRACUNIT,    // speed
-		24*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		sfx_None,       // deathsound
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
 		0,              // display offset
-		pw_scatterring, // mass
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
-		S_NULL          // raisestate
+		MF_NOCLIPTHING, // flags
+		S_FLICKY_BUBBLE // raisestate
 	},
 
-	{           // MT_GRENADERING
-		307,            // doomednum
-		S_GRENADERINGAMMO, // spawnstate
-		10,             // spawnhealth
+	{           // MT_FLICKY_12_CENTER
+		2211,             // doomednum
+		S_FLICKY_12_CENTER, // spawnstate
+		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
-		0,              // reactiontime
+		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SPRK1,        // deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_itemup,     // deathsound
-		60*FRACUNIT,    // speed
-		24*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		sfx_None,       // deathsound
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
 		0,              // display offset
-		pw_grenadering, // mass
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
-		S_NULL          // raisestate
+		MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_SCENERY|MF_RUNSPAWNFUNC, // flags
+		S_NULL // raisestate
 	},
 
-// Ring panels: Reactiontime = amount given
-	{           // MT_BOUNCEPICKUP
-		330,            // doomednum
-		S_BOUNCEPICKUP, // spawnstate
-		1,              // spawnhealth
-		S_NULL,         // seestate
+	{           // MT_FLICKY_13
+		-1,             // doomednum
+		S_FLICKY_13_OUT, // spawnstate
+		1000,           // spawnhealth
+		S_FLICKY_13_STAND, // seestate
 		sfx_None,       // seesound
-		10,             // reactiontime
+		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		1,              // painchance
+		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_BOUNCEPICKUPFADE1, // deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_ncitem,     // deathsound
-		60*FRACUNIT,    // speed
-		24*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		sfx_None,       // deathsound
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
 		0,              // display offset
-		pw_bouncering,  // mass
-		2*TICRATE,      // damage
+		16,             // mass
+		0,              // damage
 		sfx_None,       // activesound
-		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
-		S_NULL          // raisestate
+		MF_NOCLIPTHING, // flags
+		S_FLICKY_BUBBLE // raisestate
 	},
 
-	{           // MT_RAILPICKUP
-		331,            // doomednum
-		S_RAILPICKUP,   // spawnstate
-		1,              // spawnhealth
+	{           // MT_FLICKY_13_CENTER
+		2212,             // doomednum
+		S_FLICKY_13_CENTER, // spawnstate
+		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
-		5,              // reactiontime
+		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		2,              // painchance
+		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_RAILPICKUPFADE1, // deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_ncitem,     // deathsound
-		60*FRACUNIT,    // speed
-		24*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		sfx_None,       // deathsound
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
 		0,              // display offset
-		pw_railring,    // mass
-		2*TICRATE,      // damage
+		16,             // mass
+		0,              // damage
 		sfx_None,       // activesound
-		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
-		S_NULL          // raisestate
+		MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_SCENERY|MF_RUNSPAWNFUNC, // flags
+		S_NULL // raisestate
 	},
 
-	{           // MT_AUTOPICKUP
-		332,            // doomednum
-		S_AUTOPICKUP,   // spawnstate
-		1,              // spawnhealth
-		S_NULL,         // seestate
+	{           // MT_FLICKY_14
+		-1,             // doomednum
+		S_FLICKY_14_OUT, // spawnstate
+		1000,           // spawnhealth
+		S_FLICKY_14_STAND, // seestate
 		sfx_None,       // seesound
-		40,             // reactiontime
+		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		4,              // painchance
+		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_AUTOPICKUPFADE1, // deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_ncitem,     // deathsound
-		60*FRACUNIT,    // speed
-		24*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
-		0,              // display offset
+		sfx_None,       // deathsound
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
+		0,              // display offset
+		16,             // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOCLIPTHING, // flags
+		S_FLICKY_BUBBLE // raisestate
+	},
+
+	{           // MT_FLICKY_14_CENTER
+		2213,             // doomednum
+		S_FLICKY_14_CENTER, // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
+		0,              // display offset
+		16,             // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_SCENERY|MF_RUNSPAWNFUNC, // flags
+		S_NULL // raisestate
+	},
+
+	{           // MT_FLICKY_15
+		-1,             // doomednum
+		S_FLICKY_15_OUT, // spawnstate
+		1000,           // spawnhealth
+		S_FLICKY_15_STAND, // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
+		0,              // display offset
+		16,             // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOCLIPTHING, // flags
+		S_FLICKY_BUBBLE // raisestate
+	},
+
+	{           // MT_FLICKY_15_CENTER
+		2214,             // doomednum
+		S_FLICKY_15_CENTER, // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
+		0,              // display offset
+		16,             // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_SCENERY|MF_RUNSPAWNFUNC, // flags
+		S_NULL // raisestate
+	},
+
+	{           // MT_FLICKY_16
+		-1,             // doomednum
+		S_FLICKY_16_OUT, // spawnstate
+		1000,           // spawnhealth
+		S_FLICKY_16_STAND, // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
+		0,              // display offset
+		16,             // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOCLIPTHING, // flags
+		S_FLICKY_BUBBLE // raisestate
+	},
+
+	{           // MT_FLICKY_16_CENTER
+		2215,             // doomednum
+		S_FLICKY_16_CENTER, // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
+		0,              // display offset
+		16,             // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_SCENERY|MF_RUNSPAWNFUNC, // flags
+		S_NULL // raisestate
+	},
+
+	{           // MT_SECRETFLICKY_01
+		-1,             // doomednum
+		S_SECRETFLICKY_01_OUT, // spawnstate
+		1000,           // spawnhealth
+		S_SECRETFLICKY_01_STAND, // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
+		0,              // display offset
+		16,             // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOCLIPTHING, // flags
+		S_FLICKY_BUBBLE // raisestate
+	},
+
+	{           // MT_SECRETFLICKY_01_CENTER
+		2216,             // doomednum
+		S_SECRETFLICKY_01_CENTER, // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
+		0,              // display offset
+		16,             // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_SCENERY|MF_RUNSPAWNFUNC, // flags
+		S_NULL // raisestate
+	},
+
+	{           // MT_SECRETFLICKY_02
+		-1,             // doomednum
+		S_SECRETFLICKY_02_OUT, // spawnstate
+		1000,           // spawnhealth
+		S_SECRETFLICKY_02_STAND, // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
+		0,              // display offset
+		16,             // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOCLIPTHING, // flags
+		S_FLICKY_BUBBLE // raisestate
+	},
+
+	{           // MT_SECRETFLICKY_02_CENTER
+		2217,             // doomednum
+		S_SECRETFLICKY_02_CENTER, // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		8,              // speed
+		8*FRACUNIT,     // radius
+		20*FRACUNIT,    // height
+		0,              // display offset
+		16,             // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_SCENERY|MF_RUNSPAWNFUNC, // flags
+		S_NULL // raisestate
+	},
+
+	{           // MT_SEED
+		-1,             // doomednum
+		S_SEED,         // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		-2*FRACUNIT,    // speed
+		4*FRACUNIT,     // radius
+		4*FRACUNIT,     // height
+		0,              // display offset
+		4,              // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_SCENERY, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_RAIN
+		-1,             // doomednum
+		S_RAIN1,        // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		-24*FRACUNIT,   // speed
+		1*FRACUNIT,     // radius
+		8*FRACUNIT,     // height
+		0,              // display offset
+		4,              // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP,  // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_SNOWFLAKE
+		-1,             // doomednum
+		S_SNOW1,        // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		-2*FRACUNIT,    // speed
+		4*FRACUNIT,     // radius
+		4*FRACUNIT,     // height
+		0,              // display offset
+		4,              // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP,  // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_SPLISH
+		-1,             // doomednum
+		S_SPLISH1,      // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		6*FRACUNIT,     // radius
+		1*FRACUNIT,     // height
+		0,              // display offset
+		100,            // mass
+		1,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_SMOKE
+		-1,             // doomednum
+		S_SMOKE1,       // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		20*FRACUNIT,    // radius
+		16*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_SCENERY, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_SMALLBUBBLE
+		-1,             // doomednum
+		S_SMALLBUBBLE,  // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		8,              // speed
+		4*FRACUNIT,     // radius
+		4*FRACUNIT,     // height
+		0,              // display offset
+		16,             // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_MEDIUMBUBBLE
+		-1,             // doomednum
+		S_MEDIUMBUBBLE, // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		8,              // speed
+		8*FRACUNIT,     // radius
+		8*FRACUNIT,     // height
+		0,              // display offset
+		16,             // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_EXTRALARGEBUBBLE
+		-1,             // doomednum
+		S_LARGEBUBBLE1, // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_POP1,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_gasp,       // deathsound
+		8,              // speed
+		23*FRACUNIT,    // radius
+		43*FRACUNIT,    // height
+		0,              // display offset
+		16,             // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_SPECIAL|MF_NOGRAVITY|MF_SCENERY, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_WATERZAP
+		-1,             // doomednum
+		S_WATERZAP,     // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		8,              // speed
+		4*FRACUNIT,     // radius
+		4*FRACUNIT,     // height
+		0,              // display offset
+		16,             // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_SPINDUST
+		-1,             // doomednum
+		S_SPINDUST1,     // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		4*FRACUNIT,     // speed
+		4*FRACUNIT,     // radius
+		4*FRACUNIT,     // height
+		0,              // display offset
+		4,              // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIP, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_TFOG
+		-1,             // doomednum
+		S_FOG1,         // spawnstate
+		1,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		1,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_PARTICLE
+		-1,             // doomednum
+		S_PARTICLE,     // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		4*FRACUNIT,     // speed
+		4*FRACUNIT,     // radius
+		4*FRACUNIT,     // height
+		1,              // display offset
+		4,              // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIP, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_PARTICLEGEN
+		757,            // doomednum
+		S_INVISIBLE,    // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		8,              // speed
+		1*FRACUNIT,     // radius
+		1*FRACUNIT,     // height
+		0,              // display offset
+		16,             // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOCLIP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_SCENERY, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_SCORE
+		-1,             // doomednum
+		S_SCRA,         // spawnstate
+		1,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		3*FRACUNIT,     // speed
+		8*FRACUNIT,     // radius
+		8*FRACUNIT,     // height
+		1,              // display offset
+		100,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_DROWNNUMBERS
+		-1,             // doomednum
+		S_ZERO1,        // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		8,              // speed
+		8*FRACUNIT,     // radius
+		8*FRACUNIT,     // height
+		113,            // display offset
+		16,             // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_GOTEMERALD
+		-1,             // doomednum
+		S_CEMG1,        // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_ORBITEM1,     // meleestate
+		S_ORBIDYA1,     // missilestate
+		S_XPLD1,        // deathstate
+		S_NULL,         // xdeathstate
+		sfx_s3k8a,      // deathsound
+		8,              // speed
+		8*FRACUNIT,     // radius
+		16*FRACUNIT,    // height
+		112,            // display offset
+		16,             // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_SCENERY, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_LOCKON
+		-1,             // doomednum
+		S_LOCKON1,       // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		8,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		111,            // display offset
+		16,             // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_TAG
+		-1,             // doomednum
+		S_TTAG,         // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		8,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		111,            // display offset
+		16,             // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_GOTFLAG
+		-1,             // doomednum
+		S_GOTFLAG,      // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		8,              // speed
+		64*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		111,            // display offset
+		16,             // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags
+		S_NULL          // raisestate
+	},
+
+	// ambient water 1a (large)
+	{           // MT_AWATERA
+		700,            // doomednum
+		S_INVISIBLE,    // spawnstate
+		35,             // spawnhealth
+		S_NULL,         // seestate
+		sfx_amwtr1,     // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		16*FRACUNIT,    // radius
+		16*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOGRAVITY|MF_AMBIENT, // flags
+		S_NULL          // raisestate
+	},
+
+	// ambient water 1b (large)
+	{           // MT_AWATERB
+		701,            // doomednum
+		S_INVISIBLE,    // spawnstate
+		35,             // spawnhealth
+		S_NULL,         // seestate
+		sfx_amwtr2,     // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		16*FRACUNIT,    // radius
+		16*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOGRAVITY|MF_AMBIENT, // flags
+		S_NULL          // raisestate
+	},
+
+	// ambient water 2a (medium)
+	{           // MT_AWATERC
+		702,            // doomednum
+		S_INVISIBLE,    // spawnstate
+		35,             // spawnhealth
+		S_NULL,         // seestate
+		sfx_amwtr3,     // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		16*FRACUNIT,    // radius
+		16*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOGRAVITY|MF_AMBIENT, // flags
+		S_NULL          // raisestate
+	},
+
+	// ambient water 2b (medium)
+	{           // MT_AWATERD
+		703,            // doomednum
+		S_INVISIBLE,    // spawnstate
+		35,             // spawnhealth
+		S_NULL,         // seestate
+		sfx_amwtr4,     // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		16*FRACUNIT,    // radius
+		16*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOGRAVITY|MF_AMBIENT, // flags
+		S_NULL          // raisestate
+	},
+
+	// ambient water 3a (small)
+	{           // MT_AWATERE
+		704,            // doomednum
+		S_INVISIBLE,    // spawnstate
+		35,             // spawnhealth
+		S_NULL,         // seestate
+		sfx_amwtr5,     // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		16*FRACUNIT,    // radius
+		16*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOGRAVITY|MF_AMBIENT, // flags
+		S_NULL          // raisestate
+	},
+
+	// ambient water 3b (small)
+	{           // MT_AWATERF
+		705,            // doomednum
+		S_INVISIBLE,    // spawnstate
+		35,             // spawnhealth
+		S_NULL,         // seestate
+		sfx_amwtr6,     // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		16*FRACUNIT,    // radius
+		16*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOGRAVITY|MF_AMBIENT, // flags
+		S_NULL          // raisestate
+	},
+
+	// ambient water 4a (extra large)
+	{           // MT_AWATERG
+		706,            // doomednum
+		S_INVISIBLE,    // spawnstate
+		35,             // spawnhealth
+		S_NULL,         // seestate
+		sfx_amwtr7,     // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		16*FRACUNIT,    // radius
+		16*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOGRAVITY|MF_AMBIENT, // flags
+		S_NULL          // raisestate
+	},
+
+	// ambient water 4b (extra large)
+	{           // MT_AWATERH
+		707,            // doomednum
+		S_INVISIBLE,    // spawnstate
+		35,             // spawnhealth
+		S_NULL,         // seestate
+		sfx_amwtr8,     // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		16*FRACUNIT,    // radius
+		16*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOGRAVITY|MF_AMBIENT, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_RANDOMAMBIENT
+		708,            // doomednum
+		S_INVISIBLE,    // spawnstate
+		512,            // spawnhealth: repeat speed
+		S_NULL,         // seestate
+		sfx_ambint,     // seesound
+		0,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		255,            // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		8*FRACUNIT,     // radius
+		16*FRACUNIT,    // height
+		0,              // display offset
+		1000,           // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_AMBIENT, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_RANDOMAMBIENT2
+		709,            // doomednum
+		S_INVISIBLE,    // spawnstate
+		220,            // spawnhealth: repeat speed
+		S_NULL,         // seestate
+		sfx_ambin2,     // seesound
+		0,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		255,            // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		8*FRACUNIT,     // radius
+		16*FRACUNIT,    // height
+		0,              // display offset
+		1000,           // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_AMBIENT, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_MACHINEAMBIENCE
+		710,            // doomednum
+		S_INVISIBLE,    // spawnstate
+		24,             // spawnhealth: repeat speed
+		S_NULL,         // seestate
+		sfx_ambmac,     // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		200,            // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		1*FRACUNIT,     // speed
+		16*FRACUNIT,    // radius
+		16*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		20,             // damage
+		sfx_None,       // activesound
+		MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_AMBIENT, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_CORK
+		-1,             // doomednum
+		S_CORK,         // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_corkp,      // seesound
+		0,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_SMOKE1,       // deathstate
+		S_NULL,         // xdeathstate
+		sfx_corkh,      // deathsound
+		60*FRACUNIT,    // speed
+		16*FRACUNIT,    // radius
+		16*FRACUNIT,    // height
+		0,              // display offset
+		0,              // mass
+		1,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_REDRING
+		-1,             // doomednum
+		S_RRNG1,        // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_wepfir,     // seesound
+		0,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_SPRK1,        // deathstate
+		S_NULL,         // xdeathstate
+		sfx_itemup,     // deathsound
+		60*FRACUNIT,    // speed
+		16*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
+		0,              // display offset
+		0,              // mass
+		1,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags
+		S_NULL          // raisestate
+	},
+
+// Ring ammo: Health = amount given
+	{           // MT_BOUNCERING
+		301,            // doomednum
+		S_BOUNCERINGAMMO, // spawnstate
+		10,             // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		0,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_SPRK1,        // deathstate
+		S_NULL,         // xdeathstate
+		sfx_itemup,     // deathsound
+		60*FRACUNIT,    // speed
+		24*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
+		0,              // display offset
+		pw_bouncering,  // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_RAILRING
+		302,            // doomednum
+		S_RAILRINGAMMO, // spawnstate
+		5,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		0,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_SPRK1,        // deathstate
+		S_NULL,         // xdeathstate
+		sfx_itemup,     // deathsound
+		60*FRACUNIT,    // speed
+		24*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
+		0,              // display offset
+		pw_railring,    // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_INFINITYRING
+		303,            // doomednum
+		S_INFINITYRINGAMMO,// spawnstate
+		80,             // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		0,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_SPRK1,        // deathstate
+		S_NULL,         // xdeathstate
+		sfx_itemup,     // deathsound
+		60*FRACUNIT,    // speed
+		24*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
+		0,              // display offset
+		pw_infinityring,// mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_AUTOMATICRING
+		304,            // doomednum
+		S_AUTOMATICRINGAMMO, // spawnstate
+		40,             // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		0,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_SPRK1,        // deathstate
+		S_NULL,         // xdeathstate
+		sfx_itemup,     // deathsound
+		60*FRACUNIT,    // speed
+		24*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
+		0,              // display offset
+		pw_automaticring, // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_EXPLOSIONRING
+		305,            // doomednum
+		S_EXPLOSIONRINGAMMO, // spawnstate
+		5,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		0,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_SPRK1,        // deathstate
+		S_NULL,         // xdeathstate
+		sfx_itemup,     // deathsound
+		60*FRACUNIT,    // speed
+		24*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
+		0,              // display offset
+		pw_explosionring, // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_SCATTERRING
+		306,            // doomednum
+		S_SCATTERRINGAMMO, // spawnstate
+		5,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		0,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_SPRK1,        // deathstate
+		S_NULL,         // xdeathstate
+		sfx_itemup,     // deathsound
+		60*FRACUNIT,    // speed
+		24*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
+		0,              // display offset
+		pw_scatterring, // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_GRENADERING
+		307,            // doomednum
+		S_GRENADERINGAMMO, // spawnstate
+		10,             // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		0,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_SPRK1,        // deathstate
+		S_NULL,         // xdeathstate
+		sfx_itemup,     // deathsound
+		60*FRACUNIT,    // speed
+		24*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
+		0,              // display offset
+		pw_grenadering, // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		S_NULL          // raisestate
+	},
+
+// Ring panels: Reactiontime = amount given
+	{           // MT_BOUNCEPICKUP
+		330,            // doomednum
+		S_BOUNCEPICKUP, // spawnstate
+		1,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		10,             // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		1,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_BOUNCEPICKUPFADE1, // deathstate
+		S_NULL,         // xdeathstate
+		sfx_ncitem,     // deathsound
+		60*FRACUNIT,    // speed
+		24*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
+		0,              // display offset
+		pw_bouncering,  // mass
+		2*TICRATE,      // damage
+		sfx_None,       // activesound
+		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_RAILPICKUP
+		331,            // doomednum
+		S_RAILPICKUP,   // spawnstate
+		1,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		5,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		2,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_RAILPICKUPFADE1, // deathstate
+		S_NULL,         // xdeathstate
+		sfx_ncitem,     // deathsound
+		60*FRACUNIT,    // speed
+		24*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
+		0,              // display offset
+		pw_railring,    // mass
+		2*TICRATE,      // damage
+		sfx_None,       // activesound
+		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_AUTOPICKUP
+		332,            // doomednum
+		S_AUTOPICKUP,   // spawnstate
+		1,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		40,             // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		4,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_AUTOPICKUPFADE1, // deathstate
+		S_NULL,         // xdeathstate
+		sfx_ncitem,     // deathsound
+		60*FRACUNIT,    // speed
+		24*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
+		0,              // display offset
 		pw_automaticring, // mass
 		2*TICRATE,      // damage
 		sfx_None,       // activesound
-		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_EXPLODEPICKUP
+		333,            // doomednum
+		S_EXPLODEPICKUP,// spawnstate
+		1,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		5,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		8,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_EXPLODEPICKUPFADE1, // deathstate
+		S_NULL,         // xdeathstate
+		sfx_ncitem,     // deathsound
+		60*FRACUNIT,    // speed
+		24*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
+		0,              // display offset
+		pw_explosionring, // mass
+		2*TICRATE,      // damage
+		sfx_None,       // activesound
+		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_SCATTERPICKUP
+		334,            // doomednum
+		S_SCATTERPICKUP,// spawnstate
+		1,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		5,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		8,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_SCATTERPICKUPFADE1, // deathstate
+		S_NULL,         // xdeathstate
+		sfx_ncitem,     // deathsound
+		60*FRACUNIT,    // speed
+		24*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
+		0,              // display offset
+		pw_scatterring, // mass
+		2*TICRATE,      // damage
+		sfx_None,       // activesound
+		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_GRENADEPICKUP
+		335,            // doomednum
+		S_GRENADEPICKUP,// spawnstate
+		1,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		10,             // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		8,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_GRENADEPICKUPFADE1, // deathstate
+		S_NULL,         // xdeathstate
+		sfx_ncitem,     // deathsound
+		60*FRACUNIT,    // speed
+		24*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
+		0,              // display offset
+		pw_grenadering, // mass
+		2*TICRATE,      // damage
+		sfx_None,       // activesound
+		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_THROWNBOUNCE
+		-1,             // doomednum
+		S_THROWNBOUNCE1,// spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_bnce1,      // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_SPRK1,        // deathstate
+		S_NULL,         // xdeathstate
+		sfx_itemup,     // deathsound
+		60*FRACUNIT,    // speed
+		16*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
+		0,              // display offset
+		0,              // mass
+		1,              // damage
+		sfx_bnce1,      // activesound
+		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY|MF_BOUNCE, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_THROWNINFINITY
+		-1,             // doomednum
+		S_THROWNINFINITY1, // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_wepfir,     // seesound
+		0,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_SPRK1,        // deathstate
+		S_NULL,         // xdeathstate
+		sfx_itemup,     // deathsound
+		60*FRACUNIT,    // speed
+		16*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
+		0,              // display offset
+		0,              // mass
+		1,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_THROWNAUTOMATIC
+		-1,             // doomednum
+		S_THROWNAUTOMATIC1, // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_wepfir,     // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_SPRK1,        // deathstate
+		S_NULL,         // xdeathstate
+		sfx_itemup,     // deathsound
+		60*FRACUNIT,    // speed
+		16*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
+		0,              // display offset
+		0,              // mass
+		1,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_THROWNSCATTER
+		-1,             // doomednum
+		S_THROWNSCATTER,// spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_bnce2,      // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_SPRK1,        // deathstate
+		S_NULL,         // xdeathstate
+		sfx_itemup,     // deathsound
+		60*FRACUNIT,    // speed
+		16*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
+		0,              // display offset
+		0,              // mass
+		1,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_THROWNEXPLOSION
+		-1,             // doomednum
+		S_THROWNEXPLOSION1, // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_cannon,     // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		192*FRACUNIT,   // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_RINGEXPLODE,  // deathstate
+		S_NULL,         // xdeathstate
+		sfx_pop,        // deathsound
+		60*FRACUNIT,    // speed
+		16*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
+		0,              // display offset
+		0,              // mass
+		1,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_THROWNGRENADE
+		-1,             // doomednum
+		S_THROWNGRENADE1, // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_wepfir,     // seesound
+		6*TICRATE,      // reactiontime (<-- Looking for the Grenade Ring's fuse? It's right here! Again!)
+		sfx_gbeep,      // attacksound
+		S_NULL,         // painstate
+		192*FRACUNIT,   // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_RINGEXPLODE,  // deathstate
+		S_NULL,         // xdeathstate
+		sfx_pop,        // deathsound
+		30*FRACUNIT,    // speed
+		16*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
+		0,              // display offset
+		0,              // mass
+		1,              // damage
+		sfx_s3k5d,      // activesound
+		MF_NOBLOCKMAP|MF_MISSILE|MF_BOUNCE|MF_GRENADEBOUNCE, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_COIN
+		1800,           // doomednum
+		S_COIN1,        // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		MT_FLINGCOIN,   // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_COINSPARKLE1, // deathstate
+		S_NULL,         // xdeathstate
+		sfx_mario4,     // deathsound
+		60*FRACUNIT,    // speed
+		16*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_EXPLODEPICKUP
-		333,            // doomednum
-		S_EXPLODEPICKUP,// spawnstate
-		1,              // spawnhealth
+	{           // MT_FLINGCOIN
+		-1,             // doomednum
+		S_COIN1,        // spawnstate
+		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
-		5,              // reactiontime
+		MT_FLINGCOIN,   // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		8,              // painchance
+		MT_COIN,        // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_EXPLODEPICKUPFADE1, // deathstate
+		S_COINSPARKLE1, // deathstate
 		S_NULL,         // xdeathstate
-		sfx_ncitem,     // deathsound
+		sfx_mario4,     // deathsound
 		60*FRACUNIT,    // speed
-		24*FRACUNIT,    // radius
+		15*FRACUNIT,    // radius
 		24*FRACUNIT,    // height
 		0,              // display offset
-		pw_explosionring, // mass
-		2*TICRATE,      // damage
+		100,            // mass
+		0,              // damage
 		sfx_None,       // activesound
-		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		MF_SLIDEME|MF_SPECIAL, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_SCATTERPICKUP
-		334,            // doomednum
-		S_SCATTERPICKUP,// spawnstate
+	{           // MT_GOOMBA
+		1801,           // doomednum
+		S_GOOMBA1,      // spawnstate
 		1,              // spawnhealth
+		S_GOOMBA2,      // seestate
+		sfx_None,       // seesound
+		32,             // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		200,            // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_GOOMBA_DEAD,  // deathstate
+		S_NULL,         // xdeathstate
+		sfx_mario5,     // deathsound
+		6,              // speed
+		24*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_ENEMY|MF_SPECIAL|MF_SHOOTABLE, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_BLUEGOOMBA
+		1802,              // doomednum
+		S_BLUEGOOMBA1,     // spawnstate
+		1,                 // spawnhealth
+		S_BLUEGOOMBA2,     // seestate
+		sfx_None,          // seesound
+		32,                // reactiontime
+		sfx_None,          // attacksound
+		S_NULL,            // painstate
+		170,               // painchance
+		sfx_None,          // painsound
+		S_NULL,            // meleestate
+		S_NULL,            // missilestate
+		S_BLUEGOOMBA_DEAD, // deathstate
+		S_NULL,            // xdeathstate
+		sfx_mario5,        // deathsound
+		6,                 // speed
+		24*FRACUNIT,       // radius
+		32*FRACUNIT,       // height
+		0,                 // display offset
+		100,               // mass
+		0,                 // damage
+		sfx_None,          // activesound
+		MF_ENEMY|MF_SPECIAL|MF_SHOOTABLE, // flags
+		S_NULL             // raisestate
+	},
+
+	{           // MT_FIREFLOWER
+		1803,           // doomednum
+		S_FIREFLOWER1,  // spawnstate
+		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
-		5,              // reactiontime
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_SPECIAL,     // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_FIREBALL
+		-1,             // doomednum
+		S_FIREBALL1,    // spawnstate
+		1000,           // spawnhealth
+		S_FIREBALLEXP1, // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_FIREBALLEXP1, // meleestate
+		S_FIREBALLEXP1, // missilestate
+		S_FIREBALLEXP1, // deathstate
+		S_FIREBALLEXP1, // xdeathstate
+		sfx_mario1,     // deathsound
+		10*FRACUNIT,    // speed
+		4*FRACUNIT,     // radius
+		8*FRACUNIT,     // height
+		0,              // display offset
+		DMG_FIRE,       // mass
+		1,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_FIRE|MF_MISSILE, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_SHELL
+		1804,           // doomednum
+		S_SHELL,        // spawnstate
+		1,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		16,             // speed
+		16*FRACUNIT,    // radius
+		20*FRACUNIT,    // height
+		0,              // display offset
+		0,              // mass
+		1,              // damage
+		sfx_mario1,     // activesound
+		MF_SPECIAL|MF_SHOOTABLE|MF_BOUNCE, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_PUMA
+		1805,           // doomednum
+		S_PUMA_START1,  // spawnstate
+		1000,           // spawnhealth
+		S_PUMA_START1,  // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_PUMA_DOWN1,   // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_PUMA_DOWN3,   // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		8*FRACUNIT,     // radius
+		16*FRACUNIT,    // height
+		0,              // display offset
+		DMG_FIRE,       // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_PAIN|MF_FIRE, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_PUMATRAIL
+		-1,             // doomednum
+		S_PUMATRAIL1,   // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		2*FRACUNIT,     // radius
+		4*FRACUNIT,     // height
+		0,              // display offset
+		100,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_HAMMER
+		-1,             // doomednum
+		S_HAMMER,      // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		8,              // painchance
+		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SCATTERPICKUPFADE1, // deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_ncitem,     // deathsound
-		60*FRACUNIT,    // speed
-		24*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		sfx_None,       // deathsound
+		0,              // speed
+		4*FRACUNIT,     // radius
+		8*FRACUNIT,     // height
 		0,              // display offset
-		pw_scatterring, // mass
-		2*TICRATE,      // damage
+		0,              // mass
+		0,              // damage
 		sfx_None,       // activesound
-		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		MF_PAIN,        // flags
 		S_NULL          // raisestate
 	},
-
-	{           // MT_GRENADEPICKUP
-		335,            // doomednum
-		S_GRENADEPICKUP,// spawnstate
-		1,              // spawnhealth
+	{           // MT_KOOPA
+		1806,           // doomednum
+		S_KOOPA1,       // spawnstate
+		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
-		10,             // reactiontime
+		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		8,              // painchance
+		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_GRENADEPICKUPFADE1, // deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_ncitem,     // deathsound
-		60*FRACUNIT,    // speed
-		24*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		sfx_None,       // deathsound
+		0,              // speed
+		16*FRACUNIT,    // radius
+		48*FRACUNIT,    // height
 		0,              // display offset
-		pw_grenadering, // mass
-		2*TICRATE,      // damage
+		0,              // mass
+		0,              // damage
 		sfx_None,       // activesound
-		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		MF_PAIN,        // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_THROWNBOUNCE
+	{           // MT_KOOPAFLAME
 		-1,             // doomednum
-		S_THROWNBOUNCE1,// spawnstate
+		S_KOOPAFLAME1,  // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
-		sfx_bnce1,      // seesound
+		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
@@ -13748,53 +16447,53 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SPRK1,        // deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_itemup,     // deathsound
-		60*FRACUNIT,    // speed
-		16*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		sfx_None,       // deathsound
+		5*FRACUNIT,     // speed
+		8*FRACUNIT,     // radius
+		8*FRACUNIT,     // height
 		0,              // display offset
-		100,            // mass
-		1,              // damage
-		sfx_bnce1,      // activesound
-		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY|MF_BOUNCE, // flags
+		DMG_FIRE,       // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOGRAVITY|MF_MISSILE|MF_FIRE, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_THROWNINFINITY
-		-1,             // doomednum
-		S_THROWNINFINITY1, // spawnstate
+	{           // MT_AXE
+		1807,           // doomednum
+		S_AXE1,         // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
-		sfx_wepfir,     // seesound
-		0,              // reactiontime
+		sfx_None,       // seesound
+		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SPRK1,        // deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_itemup,     // deathsound
-		60*FRACUNIT,    // speed
-		16*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		sfx_None,       // deathsound
+		0,              // speed
+		8*FRACUNIT,     // radius
+		16*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		1,              // damage
+		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags
+		MF_SPECIAL,     // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_THROWNAUTOMATIC
-		-1,             // doomednum
-		S_THROWNAUTOMATIC1, // spawnstate
+	{           // MT_MARIOBUSH1
+		1808,           // doomednum
+		S_MARIOBUSH1,   // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
-		sfx_wepfir,     // seesound
+		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
@@ -13802,26 +16501,26 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SPRK1,        // deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_itemup,     // deathsound
-		60*FRACUNIT,    // speed
+		sfx_None,       // deathsound
+		0,              // speed
 		16*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		1,              // damage
+		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_THROWNSCATTER
-		-1,             // doomednum
-		S_THROWNSCATTER,// spawnstate
+	{           // MT_MARIOBUSH2
+		1809,           // doomednum
+		S_MARIOBUSH2,   // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
-		sfx_bnce2,      // seesound
+		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
@@ -13829,187 +16528,187 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SPRK1,        // deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_itemup,     // deathsound
-		60*FRACUNIT,    // speed
+		sfx_None,       // deathsound
+		0,              // speed
 		16*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		1,              // damage
+		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_THROWNEXPLOSION
-		-1,             // doomednum
-		S_THROWNEXPLOSION1, // spawnstate
+	{           // MT_TOAD
+		1810,           // doomednum
+		S_TOAD,         // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
-		sfx_cannon,     // seesound
+		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		192*FRACUNIT,   // painchance
+		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_RINGEXPLODE,  // deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_pop,        // deathsound
-		60*FRACUNIT,    // speed
-		16*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		sfx_None,       // deathsound
+		0,              // speed
+		8*FRACUNIT,     // radius
+		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		1,              // damage
+		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_THROWNGRENADE
-		-1,             // doomednum
-		S_THROWNGRENADE1, // spawnstate
+	{           // MT_AXIS
+		1700,           // doomednum
+		S_INVISIBLE,    // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
-		sfx_wepfir,     // seesound
+		sfx_None,       // seesound
 		8,              // reactiontime
-		sfx_gbeep,      // attacksound
+		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		192*FRACUNIT,   // painchance
+		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_RINGEXPLODE,  // deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_pop,        // deathsound
-		30*FRACUNIT,    // speed
-		16*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		sfx_None,       // deathsound
+		10*FRACUNIT,    // speed
+		256*FRACUNIT,   // radius
+		1*FRACUNIT,     // height
 		0,              // display offset
-		6*TICRATE,      // mass (<-- Looking for the Grenade Ring's fuse? It's right here!)
-		1,              // damage
-		sfx_s3k5d,      // activesound
-		MF_NOBLOCKMAP|MF_MISSILE|MF_BOUNCE|MF_GRENADEBOUNCE, // flags
+		100,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOGRAVITY|MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOCLIP, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_COIN
-		1800,           // doomednum
-		S_COIN1,        // spawnstate
+	{           // MT_AXISTRANSFER
+		1701,           // doomednum
+		S_INVISIBLE,    // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
-		MT_FLINGCOIN,   // reactiontime
+		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_COINSPARKLE1, // deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_mario4,     // deathsound
-		60*FRACUNIT,    // speed
+		sfx_None,       // deathsound
+		10,             // speed
 		16*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		1,              // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY, // flags
+		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOCLIP,    // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_FLINGCOIN
-		-1,             // doomednum
-		S_COIN1,        // spawnstate
+	{           // MT_AXISTRANSFERLINE
+		1702,           // doomednum
+		S_INVISIBLE,    // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
-		MT_FLINGCOIN,   // reactiontime
+		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		MT_COIN,        // painchance
+		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_COINSPARKLE1, // deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_mario4,     // deathsound
-		60*FRACUNIT,    // speed
-		15*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		sfx_None,       // deathsound
+		10,             // speed
+		32*FRACUNIT,    // radius
+		1,              // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_SLIDEME|MF_SPECIAL, // flags
+		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOCLIP,    // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_GOOMBA
-		1801,           // doomednum
-		S_GOOMBA1,      // spawnstate
-		1,              // spawnhealth
-		S_GOOMBA2,      // seestate
+	{           // MT_NIGHTSDRONE
+		1703,           // doomednum
+		S_NIGHTSDRONE1, // spawnstate
+		120,            // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		0,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		255,            // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		16*FRACUNIT,    // radius
+		56*FRACUNIT,    // height
+		1,              // display offset
+		1000,           // mass
+		0,              // damage
+		sfx_ideya,      // activesound
+		MF_SPECIAL,     // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_NIGHTSGOAL
+		-1,             // doomednum
+		S_NIGHTSGOAL1,  // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
 		sfx_None,       // seesound
-		32,             // reactiontime
+		0,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		200,            // painchance
+		255,            // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_GOOMBA_DEAD,  // deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_mario5,     // deathsound
-		6,              // speed
-		24*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
-		0,              // display offset
-		100,            // mass
+		sfx_None,       // deathsound
+		0,              // speed
+		16*FRACUNIT,    // radius
+		56*FRACUNIT,    // height
+		-1,             // display offset
+		1000,           // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_ENEMY|MF_SPECIAL|MF_SHOOTABLE, // flags
+		MF_NOGRAVITY|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BLUEGOOMBA
-		1802,              // doomednum
-		S_BLUEGOOMBA1,     // spawnstate
-		1,                 // spawnhealth
-		S_BLUEGOOMBA2,     // seestate
-		sfx_None,          // seesound
-		32,                // reactiontime
-		sfx_None,          // attacksound
-		S_NULL,            // painstate
-		170,               // painchance
-		sfx_None,          // painsound
-		S_NULL,            // meleestate
-		S_NULL,            // missilestate
-		S_BLUEGOOMBA_DEAD, // deathstate
-		S_NULL,            // xdeathstate
-		sfx_mario5,        // deathsound
-		6,                 // speed
-		24*FRACUNIT,       // radius
-		32*FRACUNIT,       // height
-		0,                 // display offset
-		100,               // mass
-		0,                 // damage
-		sfx_None,          // activesound
-		MF_ENEMY|MF_SPECIAL|MF_SHOOTABLE, // flags
-		S_NULL             // raisestate
-	},
-
-	{           // MT_FIREFLOWER
-		1803,           // doomednum
-		S_FIREFLOWER1,  // spawnstate
+	{           // MT_NIGHTSPARKLE
+		-1,             // doomednum
+		S_NIGHTSPARKLE1,// spawnstate
 		1000,           // spawnhealth
-		S_NULL,         // seestate
+		S_NIGHTSPARKLESUPER1, // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
@@ -14022,49 +16721,49 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		2*FRACUNIT,     // radius
+		4*FRACUNIT,     // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_SPECIAL,     // flags
+		MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_FIREBALL
+	{           // MT_NIGHTSLOOPHELPER
 		-1,             // doomednum
-		S_FIREBALL1,    // spawnstate
+		S_NIGHTSLOOPHELPER,// spawnstate
 		1000,           // spawnhealth
-		S_FIREBALLEXP1, // seestate
+		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
 		sfx_None,       // painsound
-		S_FIREBALLEXP1, // meleestate
-		S_FIREBALLEXP1, // missilestate
-		S_FIREBALLEXP1, // deathstate
-		S_FIREBALLEXP1, // xdeathstate
-		sfx_mario1,     // deathsound
-		10*FRACUNIT,    // speed
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
 		4*FRACUNIT,     // radius
-		8*FRACUNIT,     // height
+		4*FRACUNIT,     // height
 		0,              // display offset
 		100,            // mass
-		1,              // damage
+		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_FIRE|MF_MISSILE, // flags
+		MF_SPECIAL|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_SHELL
-		1804,           // doomednum
-		S_SHELL,        // spawnstate
+	{           // MT_NIGHTSBUMPER
+		1704,           // doomednum
+		S_NIGHTSBUMPER1,// spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
-		sfx_None,       // seesound
+		sfx_nbmper,     // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
@@ -14075,47 +16774,47 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		16,             // speed
-		16*FRACUNIT,    // radius
-		20*FRACUNIT,    // height
+		21000,          // speed
+		32*FRACUNIT,    // radius
+		64*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		1,              // damage
-		sfx_mario1,     // activesound
-		MF_SPECIAL|MF_SHOOTABLE|MF_BOUNCE, // flags
+		0,              // damage
+		sfx_None,       // activesound
+		MF_SCENERY|MF_SPECIAL|MF_NOGRAVITY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_PUMA
-		1805,           // doomednum
-		S_PUMA_START1,  // spawnstate
+	{           // MT_HOOP
+		-1,             // doomednum
+		S_HOOP,         // spawnstate
 		1000,           // spawnhealth
-		S_PUMA_START1,  // seestate
+		S_HOOP_XMASA,   // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
 		sfx_None,       // painsound
-		S_PUMA_DOWN1,   // meleestate
+		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
-		S_PUMA_DOWN3,   // xdeathstate
+		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
+		1,              // speed
 		8*FRACUNIT,     // radius
 		16*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		4,              // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_PAIN|MF_FIRE, // flags
+		MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_PUMATRAIL
+	{           // MT_HOOPCOLLIDE
 		-1,             // doomednum
-		S_PUMATRAIL1,   // spawnstate
+		S_INVISIBLE,    // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -14129,20 +16828,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
-		2*FRACUNIT,     // radius
-		4*FRACUNIT,     // height
+		1,              // speed
+		8*FRACUNIT,     // radius
+		16*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		4,              // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY, // flags
+		MF_NOSECTOR|MF_NOCLIP|MF_NOGRAVITY|MF_SPECIAL|MF_NOTHINK, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_HAMMER
+	{           // MT_HOOPCENTER
 		-1,             // doomednum
-		S_HAMMER,      // spawnstate
+		S_INVISIBLE,    // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -14156,19 +16855,20 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
-		4*FRACUNIT,     // radius
-		8*FRACUNIT,     // height
+		1,              // speed
+		2*FRACUNIT,     // radius
+		4*FRACUNIT,     // height
 		0,              // display offset
-		100,            // mass
+		4,              // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_PAIN,        // flags
+		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
 		S_NULL          // raisestate
 	},
-	{           // MT_KOOPA
-		1806,           // doomednum
-		S_KOOPA1,       // spawnstate
+
+	{           // MT_NIGHTSCORE
+		-1,             // doomednum
+		S_NIGHTSCORE10, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -14180,245 +16880,245 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
-		S_NULL,         // xdeathstate
+		S_NIGHTSCORE10_2, // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
-		16*FRACUNIT,    // radius
-		48*FRACUNIT,    // height
+		1,              // speed
+		8*FRACUNIT,     // radius
+		8*FRACUNIT,     // height
 		0,              // display offset
-		100,            // mass
+		4,              // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_PAIN,        // flags
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_KOOPAFLAME
+	{           // MT_NIGHTSCHIP
 		-1,             // doomednum
-		S_KOOPAFLAME1,  // spawnstate
+		S_NIGHTSCHIP,   // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
-		8,              // reactiontime
+		MT_FLINGNIGHTSCHIP, // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
-		sfx_None,       // painsound
-		S_NULL,         // meleestate
+		sfx_s3k33,      // painsound
+		S_RING,         // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
+		S_SPRK1,        // deathstate
 		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
-		5*FRACUNIT,     // speed
-		8*FRACUNIT,     // radius
-		8*FRACUNIT,     // height
+		sfx_ncchip,     // deathsound
+		1,              // speed
+		16*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		4,              // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOGRAVITY|MF_MISSILE|MF_FIRE, // flags
-		S_NULL          // raisestate
+		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		S_NIGHTSCHIPBONUS // raisestate
 	},
 
-	{           // MT_AXE
-		1807,           // doomednum
-		S_AXE1,         // spawnstate
+	{           // MT_FLINGNIGHTSCHIP
+		-1,             // doomednum
+		S_NIGHTSCHIP,         // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
-		8,              // reactiontime
+		MT_FLINGNIGHTSCHIP,   // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		0,              // painchance
+		MT_NIGHTSCHIP,        // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
+		S_SPRK1,        // deathstate
 		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
-		0,              // speed
-		8*FRACUNIT,     // radius
-		16*FRACUNIT,    // height
+		sfx_ncchip,     // deathsound
+		38*FRACUNIT,    // speed
+		16*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_SPECIAL,     // flags
-		S_NULL          // raisestate
+		MF_SLIDEME|MF_SPECIAL, // flags
+		S_NIGHTSCHIPBONUS // raisestate
 	},
 
-	{           // MT_MARIOBUSH1
-		1808,           // doomednum
-		S_MARIOBUSH1,   // spawnstate
+	{           // MT_NIGHTSSTAR
+		-1,             // doomednum
+		S_NIGHTSSTAR,   // spawnstate
 		1000,           // spawnhealth
-		S_NULL,         // seestate
+		S_NIGHTSSTARXMAS, // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		0,              // painchance
-		sfx_None,       // painsound
-		S_NULL,         // meleestate
+		sfx_s3k33,      // painsound
+		S_RING,         // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
+		S_SPRK1,        // deathstate
 		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
-		0,              // speed
+		sfx_ncitem,     // deathsound
+		1,              // speed
 		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		24*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		4,              // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_MARIOBUSH2
-		1809,           // doomednum
-		S_MARIOBUSH2,   // spawnstate
+	{           // MT_NIGHTSSUPERLOOP
+		1707,           // doomednum
+		S_NIGHTSSUPERLOOP, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
-		8,              // reactiontime
+		0,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		0,              // painchance
+		255,            // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
+		S_SPRK1,        // deathstate
 		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
-		0,              // speed
+		sfx_ncspec,     // deathsound
+		20*TICRATE,     // speed
 		16*FRACUNIT,    // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		1000,           // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_NIGHTSITEM,   // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_TOAD
-		1810,           // doomednum
-		S_TOAD,         // spawnstate
+	{           // MT_NIGHTSDRILLREFILL
+		1708,           // doomednum
+		S_NIGHTSDRILLREFILL, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
-		8,              // reactiontime
+		0,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		0,              // painchance
+		255,            // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
+		S_SPRK1,        // deathstate
 		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
-		0,              // speed
-		8*FRACUNIT,     // radius
+		sfx_ncspec,     // deathsound
+		96*20,          // speed
+		16*FRACUNIT,    // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		1000,           // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_NIGHTSITEM,   // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_AXIS
-		1700,           // doomednum
-		S_INVISIBLE,    // spawnstate
+	{           // MT_NIGHTSHELPER
+		1709,           // doomednum
+		S_NIGHTSHELPER, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
-		8,              // reactiontime
+		0,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		0,              // painchance
+		255,            // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
+		S_SPRK1,        // deathstate
 		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
-		10*FRACUNIT,    // speed
-		256*FRACUNIT,   // radius
-		1*FRACUNIT,     // height
+		sfx_ncspec,     // deathsound
+		20*TICRATE,     // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		1000,           // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOGRAVITY|MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOCLIP, // flags
+		MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_NIGHTSITEM,   // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_AXISTRANSFER
-		1701,           // doomednum
-		S_INVISIBLE,    // spawnstate
+	{           // MT_NIGHTSEXTRATIME
+		1711,           // doomednum
+		S_NIGHTSEXTRATIME, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
-		8,              // reactiontime
+		0,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		0,              // painchance
+		255,            // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
+		S_SPRK1,        // deathstate
 		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
-		10,             // speed
+		sfx_ncspec,     // deathsound
+		30*TICRATE,     // speed
 		16*FRACUNIT,    // radius
-		1,              // height
+		32*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		1000,           // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOCLIP,    // flags
+		MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_NIGHTSITEM,   // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_AXISTRANSFERLINE
-		1702,           // doomednum
-		S_INVISIBLE,    // spawnstate
+	{           // MT_NIGHTSLINKFREEZE
+		1712,           // doomednum
+		S_NIGHTSLINKFREEZE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
-		8,              // reactiontime
+		0,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		0,              // painchance
+		255,            // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
+		S_SPRK1,        // deathstate
 		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
-		10,             // speed
-		32*FRACUNIT,    // radius
-		1,              // height
+		sfx_ncspec,     // deathsound
+		15*TICRATE,     // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		1000,           // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOCLIP,    // flags
+		MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_NIGHTSITEM,   // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_NIGHTSDRONE
-		1703,           // doomednum
-		S_NIGHTSDRONE1, // spawnstate
-		120,            // spawnhealth
+	{           // MT_EGGCAPSULE
+		1710,           // doomednum
+		S_EGGCAPSULE,   // spawnstate
+		20,             // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		0,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		255,            // painchance
+		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
@@ -14426,19 +17126,19 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		0,              // speed
-		16*FRACUNIT,    // radius
-		56*FRACUNIT,    // height
-		1,              // display offset
-		1000,           // mass
-		0,              // damage
-		sfx_ideya,      // activesound
-		MF_SPECIAL,     // flags
+		72*FRACUNIT,    // radius
+		144*FRACUNIT,   // height
+		0,              // display offset
+		100,            // mass
+		1,              // damage
+		sfx_None,       // activesound
+		MF_NOGRAVITY|MF_SPECIAL, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_NIGHTSGOAL
+	{           // MT_NIGHTOPIANHELPER
 		-1,             // doomednum
-		S_NIGHTSGOAL1,  // spawnstate
+		S_NIGHTOPIANHELPER1, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -14454,234 +17154,234 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // deathsound
 		0,              // speed
 		16*FRACUNIT,    // radius
-		56*FRACUNIT,    // height
-		-1,             // display offset
-		1000,           // mass
+		16*FRACUNIT,    // height
+		0,              // display offset
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOGRAVITY|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+		MF_NOGRAVITY,   // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_NIGHTSPARKLE
-		-1,             // doomednum
-		S_NIGHTSPARKLE1,// spawnstate
+	{           // MT_PIAN
+		1602,           // doomednum
+		S_PIAN0,        // spawnstate
 		1000,           // spawnhealth
-		S_NIGHTSPARKLESUPER1, // seestate
+		S_PIAN1,        // seestate
 		sfx_None,       // seesound
-		8,              // reactiontime
+		0,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		0,              // painchance
+		200,            // painchance
 		sfx_None,       // painsound
-		S_NULL,         // meleestate
+		S_PIANSING,     // meleestate
 		S_NULL,         // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
-		2*FRACUNIT,     // radius
-		4*FRACUNIT,     // height
+		FRACUNIT,       // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
+		MF_SLIDEME|MF_ENEMY|MF_SPECIAL|MF_SHOOTABLE|MF_NOGRAVITY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_NIGHTSLOOPHELPER
-		-1,             // doomednum
-		S_NIGHTSLOOPHELPER,// spawnstate
-		1000,           // spawnhealth
+	{           // MT_SHLEEP
+		1601,           // doomednum
+		S_SHLEEP1,      // spawnstate
+		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
-		8,              // reactiontime
+		0,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		0,              // painchance
+		200,            // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
+		S_SHLEEPBOUNCE1, // deathstate
 		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
+		sfx_peww,       // deathsound
 		0,              // speed
-		4*FRACUNIT,     // radius
-		4*FRACUNIT,     // height
+		24*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_SPECIAL|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
+		MF_SLIDEME|MF_ENEMY|MF_SPECIAL|MF_SHOOTABLE|MF_NOGRAVITY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_NIGHTSBUMPER
-		1704,           // doomednum
-		S_NIGHTSBUMPER1,// spawnstate
+	{           // MT_PENGUINATOR
+		129,            // doomednum
+		S_PENGUINATOR_LOOK, // spawnstate
 		1,              // spawnhealth
-		S_NULL,         // seestate
-		sfx_nbmper,     // seesound
-		8,              // reactiontime
-		sfx_None,       // attacksound
+		S_PENGUINATOR_WADDLE1, // seestate
+		sfx_None,       // seesound
+		0,              // reactiontime
+		sfx_ngjump,     // attacksound
 		S_NULL,         // painstate
-		0,              // painchance
+		200,            // painchance
 		sfx_None,       // painsound
-		S_NULL,         // meleestate
-		S_NULL,         // missilestate
-		S_NULL,         // deathstate
+		S_PENGUINATOR_SLIDE1, // meleestate
+		S_PENGUINATOR_SLIDE1, // missilestate
+		S_XPLD_FLICKY,  // deathstate
 		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
-		21000,          // speed
-		32*FRACUNIT,    // radius
-		64*FRACUNIT,    // height
+		sfx_pop,        // deathsound
+		5,              // speed
+		24*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_SCENERY|MF_SPECIAL|MF_NOGRAVITY, // flags
+		MF_SPECIAL|MF_SHOOTABLE|MF_ENEMY|MF_SLIDEME, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_HOOP
-		-1,             // doomednum
-		S_HOOP,         // spawnstate
-		1000,           // spawnhealth
-		S_HOOP_XMASA,   // seestate
+	{           // MT_POPHAT
+		130,            // doomednum -- happy anniversary!
+		S_POPHAT_LOOK,  // spawnstate
+		1,              // spawnhealth
+		S_POPHAT_SHOOT1, // seestate
 		sfx_None,       // seesound
-		8,              // reactiontime
+		1,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		0,              // painchance
+		200,            // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
+		S_XPLD_FLICKY,  // deathstate
 		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
-		1,              // speed
-		8*FRACUNIT,     // radius
-		16*FRACUNIT,    // height
+		sfx_pop,        // deathsound
+		0,              // speed
+		24*FRACUNIT,    // radius
+		64*FRACUNIT,    // height
 		0,              // display offset
-		4,              // mass
+		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_SCENERY, // flags
+		MF_SPECIAL|MF_SHOOTABLE|MF_ENEMY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_HOOPCOLLIDE
+	{           // MT_POPSHOT
 		-1,             // doomednum
-		S_INVISIBLE,    // spawnstate
-		1000,           // spawnhealth
+		S_ROCKCRUMBLEI, // spawnstate
+		1,              // spawnhealth
 		S_NULL,         // seestate
-		sfx_None,       // seesound
-		8,              // reactiontime
+		sfx_cannon,     // seesound
+		0,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		0,              // painchance
+		200,            // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
+		S_XPLD1,        // deathstate
 		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
-		1,              // speed
-		8*FRACUNIT,     // radius
-		16*FRACUNIT,    // height
+		sfx_pop,        // deathsound
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
 		0,              // display offset
-		4,              // mass
+		0,              // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOSECTOR|MF_NOCLIP|MF_NOGRAVITY|MF_SPECIAL|MF_NOTHINK, // flags
+		MF_NOBLOCKMAP|MF_MISSILE, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_HOOPCENTER
-		-1,             // doomednum
-		S_INVISIBLE,    // spawnstate
-		1000,           // spawnhealth
-		S_NULL,         // seestate
-		sfx_None,       // seesound
-		8,              // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
+	{           // MT_HIVEELEMENTAL
+		127,            // doomednum
+		S_HIVEELEMENTAL_LOOK, // spawnstate
+		2,              // spawnhealth
+		S_HIVEELEMENTAL_PREPARE1, // seestate
+		sfx_s3k74,      // seesound
+		0,              // reactiontime
+		sfx_s3k91,      // attacksound
+		S_HIVEELEMENTAL_PAIN, // painstate
 		0,              // painchance
-		sfx_None,       // painsound
+		sfx_dmpain,     // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
+		S_HIVEELEMENTAL_DIE1, // deathstate
 		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
-		1,              // speed
-		2*FRACUNIT,     // radius
-		4*FRACUNIT,     // height
+		sfx_cybdth,     // deathsound
+		6*FRACUNIT,     // speed
+		30*FRACUNIT,    // radius
+		80*FRACUNIT,    // height
 		0,              // display offset
-		4,              // mass
+		100,            // mass
 		0,              // damage
-		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		sfx_s3k72,      // activesound
+		MF_SPECIAL|MF_SHOOTABLE|MF_ENEMY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_NIGHTSCORE
-		-1,             // doomednum
-		S_NIGHTSCORE10, // spawnstate
-		1000,           // spawnhealth
-		S_NULL,         // seestate
-		sfx_None,       // seesound
-		8,              // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
+	{           // MT_BUMBLEBORE
+		128,			// doomednum
+		S_BUMBLEBORE_SPAWN, // spawnstate
+		0,              // spawnhealth -- this is how you do drones...
+		S_BUMBLEBORE_FLY1, // seestate
+		sfx_s3k8e,      // seesound
+		2,              // reactiontime
+		sfx_s3k9e,      // attacksound
+		S_BUMBLEBORE_STUCK1, // painstate
 		0,              // painchance
 		sfx_None,       // painsound
-		S_NULL,         // meleestate
+		S_BUMBLEBORE_RAISE, // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
-		S_NIGHTSCORE10_2, // xdeathstate
-		sfx_None,       // deathsound
-		1,              // speed
-		8*FRACUNIT,     // radius
-		8*FRACUNIT,     // height
+		S_BUMBLEBORE_DIE, // deathstate
+		S_NULL,         // xdeathstate
+		sfx_pop,        // deathsound
+		4*FRACUNIT,     // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
 		0,              // display offset
-		4,              // mass
+		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY, // flags
+		MF_SPECIAL|MF_SHOOTABLE|MF_ENEMY|MF_NOGRAVITY|MF_SLIDEME, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_NIGHTSWING
-		1706,           // doomednum
-		S_NIGHTSWING,   // spawnstate
-		1000,           // spawnhealth
-		S_NIGHTSWING_XMAS, // seestate
+	{           // MT_BUBBLEBUZZ
+		124,            // doomednum
+		S_BBUZZFLY1,    // spawnstate
+		1,              // spawnhealth
+		S_BBUZZFLY1,    // seestate
 		sfx_None,       // seesound
-		8,              // reactiontime
+		2,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		0,              // painchance
-		sfx_s3k33,      // painsound
-		S_RING,         // meleestate
+		TICRATE,        // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SPRK1,        // deathstate
+		S_XPLD_FLICKY,  // deathstate
 		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
-		1,              // speed
-		12*FRACUNIT,    // radius
+		sfx_pop,        // deathsound
+		6*FRACUNIT,     // speed
+		20*FRACUNIT,    // radius
 		24*FRACUNIT,    // height
 		0,              // display offset
-		4,              // mass
+		100,            // mass
 		0,              // damage
-		sfx_ncitem,     // activesound
-		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		sfx_gbeep,      // activesound
+		MF_SLIDEME|MF_ENEMY|MF_SPECIAL|MF_SHOOTABLE|MF_NOGRAVITY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_NIGHTSSUPERLOOP
-		1707,           // doomednum
-		S_NIGHTSSUPERLOOP, // spawnstate
+	{           // MT_SMASHINGSPIKEBALL
+		2000,           // doomednum
+		S_SMASHSPIKE_FLOAT, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -14692,180 +17392,206 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SPRK1,        // deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_ncspec,     // deathsound
-		20*TICRATE,     // speed
-		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		sfx_None,       // deathsound
+		0,              // speed
+		18*FRACUNIT,    // radius
+		28*FRACUNIT,    // height
 		0,              // display offset
-		1000,           // mass
+		0,              // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_NIGHTSITEM,   // flags
+		MF_NOGRAVITY|MF_PAIN, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_NIGHTSDRILLREFILL
-		1708,           // doomednum
-		S_NIGHTSDRILLREFILL, // spawnstate
-		1000,           // spawnhealth
-		S_NULL,         // seestate
-		sfx_None,       // seesound
-		0,              // reactiontime
+	{           // MT_CACOLANTERN
+		132,            // doomednum
+		S_CACO_LOOK,    // spawnstate
+		1,              // spawnhealth
+		S_CACO_WAKE1,   // seestate
+		sfx_s3k8a,      // seesound
+		32,             // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		255,            // painchance
+		200,            // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SPRK1,        // deathstate
+		S_CACO_DIE_FLAGS, // deathstate
 		S_NULL,         // xdeathstate
-		sfx_ncspec,     // deathsound
-		96*20,          // speed
-		16*FRACUNIT,    // radius
+		sfx_lntdie,     // deathsound
+		FRACUNIT,       // speed
+		32*FRACUNIT,    // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
-		1000,           // mass
+		100,            // mass
 		0,              // damage
-		sfx_None,       // activesound
-		MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_NIGHTSITEM,   // flags
+		sfx_lntsit,       // activesound
+		MF_SPECIAL|MF_SHOOTABLE|MF_ENEMY|MF_NOGRAVITY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_NIGHTSHELPER
-		1709,           // doomednum
-		S_NIGHTSHELPER, // spawnstate
+	{           // MT_CACOSHARD
+		-1,             // doomednum
+		S_CACOSHARD_RANDOMIZE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
-		0,              // reactiontime
+		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		255,            // painchance
+		200,            // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SPRK1,        // deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
-		sfx_ncspec,     // deathsound
-		20*TICRATE,     // speed
-		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		sfx_lntdie,     // deathsound
+		FRACUNIT,       // speed
+		FRACUNIT,       // radius
+		FRACUNIT,       // height
 		0,              // display offset
-		1000,           // mass
+		0,              // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_NIGHTSITEM,   // flags
+		MF_MISSILE|MF_NOBLOCKMAP|MF_RUNSPAWNFUNC, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_NIGHTSEXTRATIME
-		1711,           // doomednum
-		S_NIGHTSEXTRATIME, // spawnstate
+	{           // MT_CACOFIRE
+		-1,             // doomednum
+		S_CACOFIRE1,    // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
-		sfx_None,       // seesound
-		0,              // reactiontime
+		sfx_s3k70,      // seesound
+		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		255,            // painchance
+		200,            // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SPRK1,        // deathstate
+		S_CACOFIRE_EXPLODE1, // deathstate
 		S_NULL,         // xdeathstate
-		sfx_ncspec,     // deathsound
-		30*TICRATE,     // speed
+		sfx_s3k81,      // deathsound
+		20*FRACUNIT,    // speed
 		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		16*FRACUNIT,    // height
 		0,              // display offset
-		1000,           // mass
-		0,              // damage
-		sfx_None,       // activesound
-		MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_NIGHTSITEM,   // flags
+		0,              // mass
+		20,             // damage
+		sfx_s3k48,      // activesound
+		MF_MISSILE|MF_NOBLOCKMAP|MF_NOGRAVITY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_NIGHTSLINKFREEZE
-		1712,           // doomednum
-		S_NIGHTSLINKFREEZE, // spawnstate
-		1000,           // spawnhealth
+	{           // MT_SPINBOBERT
+		131,            // doomednum
+		S_SPINBOBERT_MOVE_FLIPUP, // spawnstate
+		1,              // spawnhealth
 		S_NULL,         // seestate
-		sfx_None,       // seesound
-		0,              // reactiontime
+		sfx_s3ka0,      // seesound
+		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		255,            // painchance
+		200,            // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SPRK1,        // deathstate
+		S_XPLD1,        // deathstate
 		S_NULL,         // xdeathstate
-		sfx_ncspec,     // deathsound
-		15*TICRATE,     // speed
-		16*FRACUNIT,    // radius
+		sfx_s3k92,      // deathsound
+		20*FRACUNIT,    // speed
+		32*FRACUNIT,    // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
-		1000,           // mass
-		0,              // damage
-		sfx_None,       // activesound
-		MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_NIGHTSITEM,   // flags
+		100,            // mass
+		20,             // damage
+		sfx_s3k48,      // activesound
+		MF_SPECIAL|MF_SHOOTABLE|MF_ENEMY|MF_NOGRAVITY, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_EGGCAPSULE
-		1710,           // doomednum
-		S_EGGCAPSULE,   // spawnstate
-		20,             // spawnhealth
+	{           // MT_SPINBOBERT_FIRE1
+		-1,             // doomednum
+		S_SPINBOBERT_FIRE_MOVE, // spawnstate
+		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
-		0,              // reactiontime
+		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		0,              // painchance
+		200,            // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
+		S_XPLD1,        // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
-		72*FRACUNIT,    // radius
-		144*FRACUNIT,   // height
+		10*FRACUNIT,    // speed
+		16*FRACUNIT,    // radius
+		16*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
-		1,              // damage
+		(sfx_ghosty<<8),// mass
+		20,             // damage
 		sfx_None,       // activesound
-		MF_NOGRAVITY|MF_SPECIAL, // flags
+		MF_PAIN|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
 		S_NULL          // raisestate
 	},
 
-	// the flicky that orbits the player when they have a Nightopian helper
-	{           // MT_NIGHTOPIANHELPER
+	{           // MT_SPINBOBERT_FIRE2
 		-1,             // doomednum
-		S_NIGHTOPIANHELPER1, // spawnstate
-		1000,           // spawnhealth
+		S_SPINBOBERT_FIRE_MOVE, // spawnstate
+		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
-		0,              // reactiontime
+		8,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		255,            // painchance
+		200,            // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
+		S_XPLD1,        // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		0,              // speed
+		-10*FRACUNIT,   // speed - only difference from above
 		16*FRACUNIT,    // radius
 		16*FRACUNIT,    // height
 		0,              // display offset
-		16,             // mass
-		0,              // damage
+		(sfx_ghosty<<8),// mass
+		20,             // damage
 		sfx_None,       // activesound
-		MF_NOGRAVITY,   // flags
+		MF_PAIN|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_HANGSTER
+		133,            // doomednum
+		S_HANGSTER_LOOK, // spawnstate
+		1,              // spawnhealth
+		S_HANGSTER_SWOOP1, // seestate
+		sfx_s3ka0,      // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		200,            // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_XPLD_FLICKY,  // deathstate
+		S_NULL,         // xdeathstate
+		sfx_pop,        // deathsound
+		20*FRACUNIT,    // speed
+		24*FRACUNIT,    // radius
+		24*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		20,             // damage
+		sfx_s3k48,      // activesound
+		MF_SPECIAL|MF_SHOOTABLE|MF_ENEMY|MF_NOGRAVITY|MF_SPAWNCEILING, // flags
 		S_NULL          // raisestate
 	},
 
@@ -15273,7 +17999,34 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_DUST
+		-1,             // doomednum
+		S_DUST1,     // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		3*FRACUNIT,     // speed
+		FRACUNIT,       // radius
+		FRACUNIT,       // height
+		0,              // display offset
+		4,              // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIP, // flags
 		S_NULL          // raisestate
 	},
 
@@ -15310,7 +18063,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_ambint,     // seesound
-		0,              // reactiontime
+		4,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		255,            // painchance
@@ -15324,7 +18077,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		8*FRACUNIT,     // radius
 		16*FRACUNIT,    // height
 		0,              // display offset
-		4,              // mass
+		0,              // mass
 		0,              // damage
 		sfx_rocks1,     // activesound
 		MF_PAIN|MF_BOUNCE,  // flags
@@ -15784,7 +18537,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		30*FRACUNIT,    // radius
 		40*FRACUNIT,    // height
 		0,              // display offset
-		100,            // mass
+		0,              // mass
 		0,              // damage
 		sfx_None,       // activesound
 		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY|MF_NOSECTOR, // flags
diff --git a/src/info.h b/src/info.h
index d0e75e2e836c4c599098423b8539b3c8556be90c..b757deec0353319de13d62ff7fef8104ad74c1f4 100644
--- a/src/info.h
+++ b/src/info.h
@@ -34,6 +34,10 @@ void A_GoldMonitorSparkle();
 void A_Look();
 void A_Chase();
 void A_FaceStabChase();
+void A_FaceStabRev();
+void A_FaceStabHurl();
+void A_FaceStabMiss();
+void A_StatueBurst();
 void A_FaceTarget();
 void A_FaceTracer();
 void A_Scream();
@@ -53,7 +57,6 @@ void A_ExtraLife(); // Extra Life
 void A_GiveShield(); // Obtained Shield
 void A_GravityBox();
 void A_ScoreRise(); // Rise the score logo
-void A_ParticleSpawn();
 void A_AttractChase(); // Ring Chase
 void A_DropMine(); // Drop Mine from Skim or Jetty-Syn Bomber
 void A_FishJump(); // Fish Jump
@@ -98,11 +101,18 @@ void A_JetJawRoam();
 void A_JetJawChomp();
 void A_PointyThink();
 void A_CheckBuddy();
+void A_HoodFire();
 void A_HoodThink();
-void A_ArrowCheck();
+void A_HoodFall();
+void A_ArrowBonks();
 void A_SnailerThink();
 void A_SharpChase();
 void A_SharpSpin();
+void A_SharpDecel();
+void A_CrushstaceanWalk();
+void A_CrushstaceanPunch();
+void A_CrushclawAim();
+void A_CrushclawLaunch();
 void A_VultureVtol();
 void A_VultureCheck();
 void A_SkimChase();
@@ -206,6 +216,7 @@ void A_BrakLobShot();
 void A_NapalmScatter();
 void A_SpawnFreshCopy();
 void A_FlickySpawn();
+void A_FlickyCenter();
 void A_FlickyAim();
 void A_FlickyFly();
 void A_FlickySoar();
@@ -218,6 +229,16 @@ void A_FlickyFlutter();
 void A_FlameParticle();
 void A_FadeOverlay();
 void A_Boss5Jump();
+void A_LightBeamReset();
+void A_MineExplode();
+void A_MineRange();
+void A_ConnectToGround();
+void A_SpawnParticleRelative();
+void A_MultiShotDist();
+void A_WhoCaresIfYourSonIsABee();
+void A_ParentTriesToSleep();
+void A_CryingToMomma();
+void A_CheckFlags2();
 
 // ratio of states to sprites to mobj types is roughly 6 : 1 : 1
 #define NUMMOBJFREESLOTS 256
@@ -234,33 +255,33 @@ typedef enum sprite
 	SPR_PLAY,
 
 	// Enemies
-	SPR_POSS,
-	SPR_SPOS,
-	SPR_FISH, // Greenflower Fish
+	SPR_POSS, // Crawla (Blue)
+	SPR_SPOS, // Crawla (Red)
+	SPR_FISH, // SDURF
 	SPR_BUZZ, // Buzz (Gold)
 	SPR_RBUZ, // Buzz (Red)
 	SPR_JETB, // Jetty-Syn Bomber
-	SPR_JETW, // Jetty-Syn Water Bomber
 	SPR_JETG, // Jetty-Syn Gunner
 	SPR_CCOM, // Crawla Commander
 	SPR_DETN, // Deton
 	SPR_SKIM, // Skim mine dropper
-	SPR_TRET,
+	SPR_TRET, // Industrial Turret
 	SPR_TURR, // Pop-Up Turret
 	SPR_SHRP, // Sharp
+	SPR_CRAB, // Crushstacean
 	SPR_JJAW, // Jet Jaw
 	SPR_SNLR, // Snailer
-	SPR_VLTR, // Vulture
+	SPR_VLTR, // BASH
 	SPR_PNTY, // Pointy
 	SPR_ARCH, // Robo-Hood
-	SPR_CBFS, // CastleBot FaceStabber (Egg Knight?)
+	SPR_CBFS, // Castlebot Facestabber
+	SPR_STAB, // Castlebot Facestabber spear aura
 	SPR_SPSH, // Egg Guard
-	SPR_ESHI, // Egg Shield for Egg Guard
+	SPR_ESHI, // Egg Guard's shield
 	SPR_GSNP, // Green Snapper
 	SPR_MNUS, // Minus
 	SPR_SSHL, // Spring Shell
 	SPR_UNID, // Unidus
-	SPR_BBUZ, // AquaBuzz, for Azure Temple
 
 	// Generic Boss Items
 	SPR_JETF, // Boss jet fumes
@@ -316,16 +337,16 @@ typedef enum sprite
 	SPR_TOKE, // Special Stage Token
 	SPR_RFLG, // Red CTF Flag
 	SPR_BFLG, // Blue CTF Flag
-	SPR_NWNG, // NiGHTS Wing collectable item.
+	SPR_SPHR, // Sphere
+	SPR_NCHP, // NiGHTS chip
+	SPR_NSTR, // NiGHTS star
 	SPR_EMBM, // Emblem
 	SPR_CEMG, // Chaos Emeralds
-	SPR_EMER, // Emerald Hunt
+	SPR_SHRD, // Emerald Hunt
 
 	// Interactive Objects
-	SPR_FANS,
 	SPR_BBLS, // water bubble source
 	SPR_SIGN, // Level end sign
-	SPR_STEM, // Steam riser
 	SPR_SPIK, // Spike Ball
 	SPR_SFLM, // Spin fire
 	SPR_USPK, // Floor spike
@@ -381,16 +402,20 @@ typedef enum sprite
 	SPR_FWR4,
 	SPR_BUS1, // GFZ Bush w/ berries
 	SPR_BUS2, // GFZ Bush w/o berries
+	SPR_BUS3, // GFZ Bush w/ BLUE berries
 	// Trees (both GFZ and misc)
 	SPR_TRE1, // GFZ
 	SPR_TRE2, // Checker
 	SPR_TRE3, // Frozen Hillside
 	SPR_TRE4, // Polygon
 	SPR_TRE5, // Bush tree
+	SPR_TRE6, // Spring tree
 
 	// Techno Hill Scenery
-	SPR_THZP, // THZ1 Flower
-	SPR_FWR5, // Another flower
+	SPR_THZP, // THZ1 Steam Flower
+	SPR_FWR5, // THZ1 Spin flower (red)
+	SPR_FWR6, // THZ1 Spin flower (yellow)
+	SPR_THZT, // Steam Whistle tree/bush
 	SPR_ALRM, // THZ2 Alarm
 
 	// Deep Sea Scenery
@@ -401,6 +426,9 @@ typedef enum sprite
 	SPR_CRL2, // Coral 2
 	SPR_CRL3, // Coral 3
 	SPR_BCRY, // Blue Crystal
+	SPR_KELP, // Kelp
+	SPR_DSTG, // DSZ Stalagmites
+	SPR_LIBE, // DSZ Light beam
 
 	// Castle Eggman Scenery
 	SPR_CHAN, // CEZ Chain
@@ -414,6 +442,16 @@ typedef enum sprite
 	SPR_RSPB, // Red spring on a ball
 	SPR_SFBR, // Small Firebar
 	SPR_BFBR, // Big Firebar
+	SPR_BANR, // Banner/pole
+	SPR_PINE, // Pine Tree
+	SPR_CEZB, // Bush
+	SPR_CNDL, // Candle/pricket
+	SPR_FLMH, // Flame holder
+	SPR_CTRC, // Fire torch
+	SPR_CFLG, // Waving flag/segment
+	SPR_CSTA, // Crawla statue
+	SPR_CBBS, // Facestabber statue
+	SPR_CABR, // Brambles
 
 	// Arid Canyon Scenery
 	SPR_BTBL, // Big tumbleweed
@@ -434,12 +472,25 @@ typedef enum sprite
 	SPR_XMS3, // Snowman
 	SPR_XMS4, // Lamppost
 	SPR_XMS5, // Hanging Star
+	SPR_FHZI, // FHZ Ice
+
+	// Halloween Scenery
+	SPR_PUMK, // Pumpkins
+	SPR_HHPL, // Dr Seuss Trees
+	SPR_SHRM, // Mushroom
+	SPR_HHZM, // Misc
 
 	// Botanic Serenity Scenery
 	SPR_BSZ1, // Tall flowers
 	SPR_BSZ2, // Medium flowers
 	SPR_BSZ3, // Small flowers
-	SPR_BSZ4, // Tulip
+	//SPR_BSZ4, -- Tulips
+	SPR_BST1, // Red tulip
+	SPR_BST2, // Purple tulip
+	SPR_BST3, // Blue tulip
+	SPR_BST4, // Cyan tulip
+	SPR_BST5, // Yellow tulip
+	SPR_BST6, // Orange tulip
 	SPR_BSZ5, // Cluster of Tulips
 	SPR_BSZ6, // Bush
 	SPR_BSZ7, // Vine
@@ -485,13 +536,20 @@ typedef enum sprite
 	SPR_FL14, // Dove
 	SPR_FL15, // Cat
 	SPR_FL16, // Canary
+	SPR_FS01, // Spider
+	SPR_FS02, // Bat
 
 	// Springs
-	SPR_SPRY, // yellow spring
-	SPR_SPRR, // red spring
-	SPR_SPRB, // Blue springs
+	SPR_FANS, // Fan
+	SPR_STEM, // Steam riser
+	SPR_BUMP, // Bumpers
+	SPR_BLON, // Balloons
+	SPR_SPRY, // Yellow spring
+	SPR_SPRR, // Red spring
+	SPR_SPRB, // Blue spring
 	SPR_YSPR, // Yellow Diagonal Spring
 	SPR_RSPR, // Red Diagonal Spring
+	SPR_BSPR, // Blue Diagonal Spring
 	SPR_SSWY, // Yellow Side Spring
 	SPR_SSWR, // Red Side Spring
 	SPR_SSWB, // Blue Side Spring
@@ -565,13 +623,32 @@ typedef enum sprite
 	SPR_NSCR, // NiGHTS score sprite
 	SPR_NPRU, // Nights Powerups
 	SPR_CAPS, // Capsule thingy for NiGHTS
+	SPR_IDYA, // Ideya
+	SPR_NTPN, // Nightopian
+	SPR_SHLP, // Shleep
+
+	// Secret badniks and hazards, shhhh
+	SPR_PENG,
+	SPR_POPH,
+	SPR_HIVE,
+	SPR_BUMB,
+	SPR_BBUZ,
+	SPR_FMCE,
+	SPR_HMCE,
+	SPR_CACO,
+	SPR_BAL2,
+	SPR_SBOB,
+	SPR_SBFL,
+	SPR_SBSK,
+	SPR_HBAT,
 
 	// Debris
-	SPR_SPRK, // spark
+	SPR_SPRK, // Sparkle
 	SPR_BOM1, // Robot Explosion
 	SPR_BOM2, // Boss Explosion 1
 	SPR_BOM3, // Boss Explosion 2
 	SPR_BOM4, // Underwater Explosion
+	SPR_BMNB, // Mine Explosion
 
 	// Crumbly rocks
 	SPR_ROIA,
@@ -591,9 +668,6 @@ typedef enum sprite
 	SPR_ROIO,
 	SPR_ROIP,
 
-	// Blue Spheres
-	SPR_BBAL,
-
 	// Gravity Well Objects
 	SPR_GWLG,
 	SPR_GWLR,
@@ -890,10 +964,6 @@ typedef enum state
 	S_RBUZZFLY1,
 	S_RBUZZFLY2,
 
-	// AquaBuzz
-	S_BBUZZFLY1,
-	S_BBUZZFLY2,
-
 	// Jetty-Syn Bomber
 	S_JETBLOOK1,
 	S_JETBLOOK2,
@@ -930,7 +1000,6 @@ typedef enum state
 	S_DETON13,
 	S_DETON14,
 	S_DETON15,
-	S_DETON16,
 
 	// Skim Mine Dropper
 	S_SKIM1,
@@ -972,14 +1041,40 @@ typedef enum state
 	S_TURRETPOPDOWN7,
 	S_TURRETPOPDOWN8,
 
-	// Sharp
-	S_SHARP_ROAM1,
-	S_SHARP_ROAM2,
-	S_SHARP_AIM1,
-	S_SHARP_AIM2,
-	S_SHARP_AIM3,
-	S_SHARP_AIM4,
-	S_SHARP_SPIN,
+	// Spincushion
+	S_SPINCUSHION_LOOK,
+	S_SPINCUSHION_CHASE1,
+	S_SPINCUSHION_CHASE2,
+	S_SPINCUSHION_CHASE3,
+	S_SPINCUSHION_CHASE4,
+	S_SPINCUSHION_AIM1,
+	S_SPINCUSHION_AIM2,
+	S_SPINCUSHION_AIM3,
+	S_SPINCUSHION_AIM4,
+	S_SPINCUSHION_AIM5,
+	S_SPINCUSHION_SPIN1,
+	S_SPINCUSHION_SPIN2,
+	S_SPINCUSHION_SPIN3,
+	S_SPINCUSHION_SPIN4,
+	S_SPINCUSHION_STOP1,
+	S_SPINCUSHION_STOP2,
+	S_SPINCUSHION_STOP3,
+	S_SPINCUSHION_STOP4,
+
+	// Crushstacean
+	S_CRUSHSTACEAN_ROAM1,
+	S_CRUSHSTACEAN_ROAM2,
+	S_CRUSHSTACEAN_ROAM3,
+	S_CRUSHSTACEAN_ROAM4,
+	S_CRUSHSTACEAN_ROAMPAUSE,
+	S_CRUSHSTACEAN_PUNCH1,
+	S_CRUSHSTACEAN_PUNCH2,
+	S_CRUSHCLAW_AIM,
+	S_CRUSHCLAW_OUT,
+	S_CRUSHCLAW_STAY,
+	S_CRUSHCLAW_IN,
+	S_CRUSHCLAW_WAIT,
+	S_CRUSHCHAIN,
 
 	// Jet Jaw
 	S_JETJAW_ROAM1,
@@ -1028,13 +1123,14 @@ typedef enum state
 
 	// Robo-Hood
 	S_ROBOHOOD_LOOK,
-	S_ROBOHOOD_STND,
-	S_ROBOHOOD_SHOOT,
-	S_ROBOHOOD_JUMP,
+	S_ROBOHOOD_STAND,
+	S_ROBOHOOD_FIRE1,
+	S_ROBOHOOD_FIRE2,
+	S_ROBOHOOD_JUMP1,
 	S_ROBOHOOD_JUMP2,
-	S_ROBOHOOD_FALL,
+	S_ROBOHOOD_JUMP3,
 
-	// CastleBot FaceStabber
+	// Castlebot Facestabber
 	S_FACESTABBER_STND1,
 	S_FACESTABBER_STND2,
 	S_FACESTABBER_STND3,
@@ -1045,6 +1141,11 @@ typedef enum state
 	S_FACESTABBER_CHARGE2,
 	S_FACESTABBER_CHARGE3,
 	S_FACESTABBER_CHARGE4,
+	S_FACESTABBER_PAIN,
+	S_FACESTABBER_DIE1,
+	S_FACESTABBER_DIE2,
+	S_FACESTABBER_DIE3,
+	S_FACESTABBERSPEAR,
 
 	// Egg Guard
 	S_EGGGUARD_STND,
@@ -1062,6 +1163,7 @@ typedef enum state
 
 	// Egg Shield for Egg Guard
 	S_EGGSHIELD,
+	S_EGGSHIELDBREAK,
 
 	// Green Snapper
 	S_GSNAPPER_STND,
@@ -1119,13 +1221,7 @@ typedef enum state
 	S_UNIDUS_BALL,
 
 	// Boss Explosion
-	S_BPLD1,
-	S_BPLD2,
-	S_BPLD3,
-	S_BPLD4,
-	S_BPLD5,
-	S_BPLD6,
-	S_BPLD7,
+	S_BOSSEXPLODE,
 
 	// S3&K Boss Explosion
 	S_SONIC3KBOSSEXPLOSION1,
@@ -1630,17 +1726,27 @@ typedef enum state
 	S_RING,
 
 	// Blue Sphere for special stages
-	S_BLUEBALL,
-	S_BLUEBALLSPARK,
+	S_BLUESPHERE,
+	S_BLUESPHEREBONUS,
+	S_BLUESPHERESPARK,
+
+	// Bomb Sphere
+	S_BOMBSPHERE1,
+	S_BOMBSPHERE2,
+	S_BOMBSPHERE3,
+	S_BOMBSPHERE4,
+
+	// NiGHTS Chip
+	S_NIGHTSCHIP,
+	S_NIGHTSCHIPBONUS,
+
+	// NiGHTS Star
+	S_NIGHTSSTAR,
+	S_NIGHTSSTARXMAS,
 
 	// Gravity Wells for special stages
 	S_GRAVWELLGREEN,
-	S_GRAVWELLGREEN2,
-	S_GRAVWELLGREEN3,
-
 	S_GRAVWELLRED,
-	S_GRAVWELLRED2,
-	S_GRAVWELLRED3,
 
 	// Individual Team Rings
 	S_TEAMRING,
@@ -1689,14 +1795,10 @@ typedef enum state
 	S_CEMG6,
 	S_CEMG7,
 
-	// Emeralds (for hunt)
-	S_EMER1,
-
-	S_FAN,
-	S_FAN2,
-	S_FAN3,
-	S_FAN4,
-	S_FAN5,
+	// Emerald hunt shards
+	S_SHRD1,
+	S_SHRD2,
+	S_SHRD3,
 
 	// Bubble Source
 	S_BUBBLES1,
@@ -1759,16 +1861,6 @@ typedef enum state
 	S_SIGN52, // Eggman
 	S_SIGN53,
 
-	// Steam Riser
-	S_STEAM1,
-	S_STEAM2,
-	S_STEAM3,
-	S_STEAM4,
-	S_STEAM5,
-	S_STEAM6,
-	S_STEAM7,
-	S_STEAM8,
-
 	// Spike Ball
 	S_SPIKEBALL1,
 	S_SPIKEBALL2,
@@ -1816,14 +1908,18 @@ typedef enum state
 	S_STARPOST_ENDSPIN,
 
 	// Big floating mine
-	S_BIGMINE1,
-	S_BIGMINE2,
-	S_BIGMINE3,
-	S_BIGMINE4,
-	S_BIGMINE5,
-	S_BIGMINE6,
-	S_BIGMINE7,
-	S_BIGMINE8,
+	S_BIGMINE_IDLE,
+	S_BIGMINE_ALERT1,
+	S_BIGMINE_ALERT2,
+	S_BIGMINE_ALERT3,
+	S_BIGMINE_SET1,
+	S_BIGMINE_SET2,
+	S_BIGMINE_SET3,
+	S_BIGMINE_BLAST1,
+	S_BIGMINE_BLAST2,
+	S_BIGMINE_BLAST3,
+	S_BIGMINE_BLAST4,
+	S_BIGMINE_BLAST5,
 
 	// Cannon Launcher
 	S_CANNONLAUNCHER1,
@@ -1986,22 +2082,17 @@ typedef enum state
 
 	// Arrow
 	S_ARROW,
-	S_ARROWUP,
-	S_ARROWDOWN,
+	S_ARROWBONK,
 
 	// Trapgoyle Demon fire
-	S_DEMONFIRE1,
-	S_DEMONFIRE2,
-	S_DEMONFIRE3,
-	S_DEMONFIRE4,
-	S_DEMONFIRE5,
-	S_DEMONFIRE6,
+	S_DEMONFIRE,
 
 	// GFZ flowers
 	S_GFZFLOWERA,
 	S_GFZFLOWERB,
 	S_GFZFLOWERC,
 
+	S_BLUEBERRYBUSH,
 	S_BERRYBUSH,
 	S_BUSH,
 
@@ -2016,10 +2107,28 @@ typedef enum state
 	S_POLYGONTREE,
 	S_BUSHTREE,
 	S_BUSHREDTREE,
-
-	// THZ Plant
-	S_THZFLOWERA,
-	S_THZFLOWERB,
+	S_SPRINGTREE,
+
+	// THZ flowers
+	S_THZFLOWERA, // THZ1 Steam flower
+	S_THZFLOWERB, // THZ1 Spin flower (red)
+	S_THZFLOWERC, // THZ1 Spin flower (yellow)
+
+	// THZ Steam Whistle tree/bush
+	S_THZTREE,
+	S_THZTREEBRANCH1,
+	S_THZTREEBRANCH2,
+	S_THZTREEBRANCH3,
+	S_THZTREEBRANCH4,
+	S_THZTREEBRANCH5,
+	S_THZTREEBRANCH6,
+	S_THZTREEBRANCH7,
+	S_THZTREEBRANCH8,
+	S_THZTREEBRANCH9,
+	S_THZTREEBRANCH10,
+	S_THZTREEBRANCH11,
+	S_THZTREEBRANCH12,
+	S_THZTREEBRANCH13,
 
 	// THZ Alarm
 	S_ALARM1,
@@ -2057,18 +2166,33 @@ typedef enum state
 	// Blue Crystal
 	S_BLUECRYSTAL1,
 
+	// Kelp,
+	S_KELP,
+
+	// DSZ Stalagmites
+	S_DSZSTALAGMITE,
+	S_DSZ2STALAGMITE,
+
+	// DSZ Light beam
+	S_LIGHTBEAM1,
+	S_LIGHTBEAM2,
+	S_LIGHTBEAM3,
+	S_LIGHTBEAM4,
+	S_LIGHTBEAM5,
+	S_LIGHTBEAM6,
+	S_LIGHTBEAM7,
+	S_LIGHTBEAM8,
+	S_LIGHTBEAM9,
+	S_LIGHTBEAM10,
+	S_LIGHTBEAM11,
+	S_LIGHTBEAM12,
+
 	// CEZ Chain
 	S_CEZCHAIN,
 
 	// Flame
-	S_FLAME1,
-	S_FLAME2,
-	S_FLAME3,
-	S_FLAME4,
-	S_FLAME5,
-	S_FLAME6,
+	S_FLAME,
 	S_FLAMEPARTICLE,
-
 	S_FLAMEREST,
 
 	// Eggman Statue
@@ -2083,6 +2207,8 @@ typedef enum state
 	S_BIGMACECHAIN,
 	S_SMALLMACE,
 	S_BIGMACE,
+	S_SMALLGRABCHAIN,
+	S_BIGGRABCHAIN,
 
 	// Yellow spring on a ball
 	S_YELLOWSPRINGBALL,
@@ -2134,7 +2260,24 @@ typedef enum state
 	S_BIGFIREBAR15,
 	S_BIGFIREBAR16,
 
-	S_CEZFLOWER1,
+	S_CEZFLOWER,
+	S_CEZPOLE,
+	S_CEZBANNER,
+	S_PINETREE,
+	S_CEZBUSH1,
+	S_CEZBUSH2,
+	S_CANDLE,
+	S_CANDLEPRICKET,
+	S_FLAMEHOLDER,
+	S_FIRETORCH,
+	S_WAVINGFLAG,
+	S_WAVINGFLAGSEG,
+	S_CRAWLASTATUE,
+	S_FACESTABBERSTATUE,
+	S_SUSPICIOUSFACESTABBERSTATUE_WAIT,
+	S_SUSPICIOUSFACESTABBERSTATUE_BURST1,
+	S_SUSPICIOUSFACESTABBERSTATUE_BURST2,
+	S_BRAMBLES,
 
 	// Big Tumbleweed
 	S_BIGTUMBLEWEED,
@@ -2235,8 +2378,57 @@ typedef enum state
 	S_LAMPPOST2,  // with snow
 	S_HANGSTAR,
 	// Xmas GFZ bushes
+	S_XMASBLUEBERRYBUSH,
 	S_XMASBERRYBUSH,
 	S_XMASBUSH,
+	// FHZ
+	S_FHZICE1,
+	S_FHZICE2,
+
+	// Halloween Scenery
+	// Pumpkins
+	S_JACKO1,
+	S_JACKO1OVERLAY_1,
+	S_JACKO1OVERLAY_2,
+	S_JACKO1OVERLAY_3,
+	S_JACKO1OVERLAY_4,
+	S_JACKO2,
+	S_JACKO2OVERLAY_1,
+	S_JACKO2OVERLAY_2,
+	S_JACKO2OVERLAY_3,
+	S_JACKO2OVERLAY_4,
+	S_JACKO3,
+	S_JACKO3OVERLAY_1,
+	S_JACKO3OVERLAY_2,
+	S_JACKO3OVERLAY_3,
+	S_JACKO3OVERLAY_4,
+	// Dr Seuss Trees
+	S_HHZTREE_TOP,
+	S_HHZTREE_TRUNK,
+	S_HHZTREE_LEAF,
+	// Mushroom
+	S_HHZSHROOM_1,
+	S_HHZSHROOM_2,
+	S_HHZSHROOM_3,
+	S_HHZSHROOM_4,
+	S_HHZSHROOM_5,
+	S_HHZSHROOM_6,
+	S_HHZSHROOM_7,
+	S_HHZSHROOM_8,
+	S_HHZSHROOM_9,
+	S_HHZSHROOM_10,
+	S_HHZSHROOM_11,
+	S_HHZSHROOM_12,
+	S_HHZSHROOM_13,
+	S_HHZSHROOM_14,
+	S_HHZSHROOM_15,
+	S_HHZSHROOM_16,
+	// Misc
+	S_HHZGRASS,
+	S_HHZTENT1,
+	S_HHZTENT2,
+	S_HHZSTALAGMITE_TALL,
+	S_HHZSTALAGMITE_SHORT,
 
 	// Botanic Serenity's loads of scenery states
 	S_BSZTALLFLOWER_RED,
@@ -2557,6 +2749,8 @@ typedef enum state
 	S_FLICKY_01_FLAP1,
 	S_FLICKY_01_FLAP2,
 	S_FLICKY_01_FLAP3,
+	S_FLICKY_01_STAND,
+	S_FLICKY_01_CENTER,
 
 	// Rabbit
 	S_FLICKY_02_OUT,
@@ -2564,6 +2758,8 @@ typedef enum state
 	S_FLICKY_02_HOP,
 	S_FLICKY_02_UP,
 	S_FLICKY_02_DOWN,
+	S_FLICKY_02_STAND,
+	S_FLICKY_02_CENTER,
 
 	// Chicken
 	S_FLICKY_03_OUT,
@@ -2572,6 +2768,8 @@ typedef enum state
 	S_FLICKY_03_UP,
 	S_FLICKY_03_FLAP1,
 	S_FLICKY_03_FLAP2,
+	S_FLICKY_03_STAND,
+	S_FLICKY_03_CENTER,
 
 	// Seal
 	S_FLICKY_04_OUT,
@@ -2583,6 +2781,8 @@ typedef enum state
 	S_FLICKY_04_SWIM2,
 	S_FLICKY_04_SWIM3,
 	S_FLICKY_04_SWIM4,
+	S_FLICKY_04_STAND,
+	S_FLICKY_04_CENTER,
 
 	// Pig
 	S_FLICKY_05_OUT,
@@ -2590,6 +2790,8 @@ typedef enum state
 	S_FLICKY_05_HOP,
 	S_FLICKY_05_UP,
 	S_FLICKY_05_DOWN,
+	S_FLICKY_05_STAND,
+	S_FLICKY_05_CENTER,
 
 	// Chipmunk
 	S_FLICKY_06_OUT,
@@ -2597,6 +2799,8 @@ typedef enum state
 	S_FLICKY_06_HOP,
 	S_FLICKY_06_UP,
 	S_FLICKY_06_DOWN,
+	S_FLICKY_06_STAND,
+	S_FLICKY_06_CENTER,
 
 	// Penguin
 	S_FLICKY_07_OUT,
@@ -2611,6 +2815,8 @@ typedef enum state
 	S_FLICKY_07_SWIM1,
 	S_FLICKY_07_SWIM2,
 	S_FLICKY_07_SWIM3,
+	S_FLICKY_07_STAND,
+	S_FLICKY_07_CENTER,
 
 	// Fish
 	S_FLICKY_08_OUT,
@@ -2624,6 +2830,8 @@ typedef enum state
 	S_FLICKY_08_SWIM2,
 	S_FLICKY_08_SWIM3,
 	S_FLICKY_08_SWIM4,
+	S_FLICKY_08_STAND,
+	S_FLICKY_08_CENTER,
 
 	// Ram
 	S_FLICKY_09_OUT,
@@ -2631,11 +2839,15 @@ typedef enum state
 	S_FLICKY_09_HOP,
 	S_FLICKY_09_UP,
 	S_FLICKY_09_DOWN,
+	S_FLICKY_09_STAND,
+	S_FLICKY_09_CENTER,
 
 	// Puffin
 	S_FLICKY_10_OUT,
 	S_FLICKY_10_FLAP1,
 	S_FLICKY_10_FLAP2,
+	S_FLICKY_10_STAND,
+	S_FLICKY_10_CENTER,
 
 	// Cow
 	S_FLICKY_11_OUT,
@@ -2643,6 +2855,8 @@ typedef enum state
 	S_FLICKY_11_RUN1,
 	S_FLICKY_11_RUN2,
 	S_FLICKY_11_RUN3,
+	S_FLICKY_11_STAND,
+	S_FLICKY_11_CENTER,
 
 	// Rat
 	S_FLICKY_12_OUT,
@@ -2650,6 +2864,8 @@ typedef enum state
 	S_FLICKY_12_RUN1,
 	S_FLICKY_12_RUN2,
 	S_FLICKY_12_RUN3,
+	S_FLICKY_12_STAND,
+	S_FLICKY_12_CENTER,
 
 	// Bear
 	S_FLICKY_13_OUT,
@@ -2657,12 +2873,16 @@ typedef enum state
 	S_FLICKY_13_HOP,
 	S_FLICKY_13_UP,
 	S_FLICKY_13_DOWN,
+	S_FLICKY_13_STAND,
+	S_FLICKY_13_CENTER,
 
 	// Dove
 	S_FLICKY_14_OUT,
 	S_FLICKY_14_FLAP1,
 	S_FLICKY_14_FLAP2,
 	S_FLICKY_14_FLAP3,
+	S_FLICKY_14_STAND,
+	S_FLICKY_14_CENTER,
 
 	// Cat
 	S_FLICKY_15_OUT,
@@ -2670,26 +2890,79 @@ typedef enum state
 	S_FLICKY_15_HOP,
 	S_FLICKY_15_UP,
 	S_FLICKY_15_DOWN,
+	S_FLICKY_15_STAND,
+	S_FLICKY_15_CENTER,
 
 	// Canary
 	S_FLICKY_16_OUT,
 	S_FLICKY_16_FLAP1,
 	S_FLICKY_16_FLAP2,
 	S_FLICKY_16_FLAP3,
+	S_FLICKY_16_STAND,
+	S_FLICKY_16_CENTER,
+
+	// Spider
+	S_SECRETFLICKY_01_OUT,
+	S_SECRETFLICKY_01_AIM,
+	S_SECRETFLICKY_01_HOP,
+	S_SECRETFLICKY_01_UP,
+	S_SECRETFLICKY_01_DOWN,
+	S_SECRETFLICKY_01_STAND,
+	S_SECRETFLICKY_01_CENTER,
+
+	// Bat
+	S_SECRETFLICKY_02_OUT,
+	S_SECRETFLICKY_02_FLAP1,
+	S_SECRETFLICKY_02_FLAP2,
+	S_SECRETFLICKY_02_FLAP3,
+	S_SECRETFLICKY_02_STAND,
+	S_SECRETFLICKY_02_CENTER,
+
+	// Fan
+	S_FAN,
+	S_FAN2,
+	S_FAN3,
+	S_FAN4,
+	S_FAN5,
 
+	// Steam Riser
+	S_STEAM1,
+	S_STEAM2,
+	S_STEAM3,
+	S_STEAM4,
+	S_STEAM5,
+	S_STEAM6,
+	S_STEAM7,
+	S_STEAM8,
+
+	// Bumpers
+	S_BUMPER,
+	S_BUMPERHIT,
+
+	// Balloons
+	S_BALLOON,
+	S_BALLOONPOP1,
+	S_BALLOONPOP2,
+	S_BALLOONPOP3,
+	S_BALLOONPOP4,
+	S_BALLOONPOP5,
+	S_BALLOONPOP6,
+
+	// Yellow Spring
 	S_YELLOWSPRING,
 	S_YELLOWSPRING2,
 	S_YELLOWSPRING3,
 	S_YELLOWSPRING4,
 	S_YELLOWSPRING5,
 
+	// Red Spring
 	S_REDSPRING,
 	S_REDSPRING2,
 	S_REDSPRING3,
 	S_REDSPRING4,
 	S_REDSPRING5,
 
-	// Blue Springs
+	// Blue Spring
 	S_BLUESPRING,
 	S_BLUESPRING2,
 	S_BLUESPRING3,
@@ -2716,6 +2989,16 @@ typedef enum state
 	S_RDIAG7,
 	S_RDIAG8,
 
+	// Blue Diagonal Spring
+	S_BDIAG1,
+	S_BDIAG2,
+	S_BDIAG3,
+	S_BDIAG4,
+	S_BDIAG5,
+	S_BDIAG6,
+	S_BDIAG7,
+	S_BDIAG8,
+
 	// Yellow Side Spring
 	S_YHORIZ1,
 	S_YHORIZ2,
@@ -2821,7 +3104,6 @@ typedef enum state
 	S_SEED,
 
 	S_PARTICLE,
-	S_PARTICLEGEN,
 
 	// Score Logos
 	S_SCRA, // 100
@@ -2835,6 +3117,7 @@ typedef enum state
 	S_SCRI, // 4000 (mario)
 	S_SCRJ, // 8000 (mario)
 	S_SCRK, // 1UP (mario)
+	S_SCRL, // 10
 
 	// Drowning Timer Numbers
 	S_ZERO1,
@@ -3133,9 +3416,6 @@ typedef enum state
 	S_NIGHTSCORE90_2,
 	S_NIGHTSCORE100_2,
 
-	S_NIGHTSWING,
-	S_NIGHTSWING_XMAS,
-
 	// NiGHTS Paraloop Powerups
 	S_NIGHTSSUPERLOOP,
 	S_NIGHTSDRILLREFILL,
@@ -3153,14 +3433,11 @@ typedef enum state
 	S_ORBITEM6,
 	S_ORBITEM7,
 	S_ORBITEM8,
-	S_ORBITEM9,
-	S_ORBITEM10,
-	S_ORBITEM11,
-	S_ORBITEM12,
-	S_ORBITEM13,
-	S_ORBITEM14,
-	S_ORBITEM15,
-	S_ORBITEM16,
+	S_ORBIDYA1,
+	S_ORBIDYA2,
+	S_ORBIDYA3,
+	S_ORBIDYA4,
+	S_ORBIDYA5,
 
 	// "Flicky" helper
 	S_NIGHTOPIANHELPER1,
@@ -3173,6 +3450,141 @@ typedef enum state
 	S_NIGHTOPIANHELPER8,
 	S_NIGHTOPIANHELPER9,
 
+	// Nightopian
+	S_PIAN0,
+	S_PIAN1,
+	S_PIAN2,
+	S_PIAN3,
+	S_PIAN4,
+	S_PIAN5,
+	S_PIAN6,
+	S_PIANSING,
+
+	// Shleep
+	S_SHLEEP1,
+	S_SHLEEP2,
+	S_SHLEEP3,
+	S_SHLEEP4,
+	S_SHLEEPBOUNCE1,
+	S_SHLEEPBOUNCE2,
+	S_SHLEEPBOUNCE3,
+
+	// Secret badniks and hazards, shhhh
+	S_PENGUINATOR_LOOK,
+	S_PENGUINATOR_WADDLE1,
+	S_PENGUINATOR_WADDLE2,
+	S_PENGUINATOR_WADDLE3,
+	S_PENGUINATOR_WADDLE4,
+	S_PENGUINATOR_SLIDE1,
+	S_PENGUINATOR_SLIDE2,
+	S_PENGUINATOR_SLIDE3,
+	S_PENGUINATOR_SLIDE4,
+	S_PENGUINATOR_SLIDE5,
+
+	S_POPHAT_LOOK,
+	S_POPHAT_SHOOT1,
+	S_POPHAT_SHOOT2,
+	S_POPHAT_SHOOT3,
+
+	S_HIVEELEMENTAL_LOOK,
+	S_HIVEELEMENTAL_PREPARE1,
+	S_HIVEELEMENTAL_PREPARE2,
+	S_HIVEELEMENTAL_SHOOT1,
+	S_HIVEELEMENTAL_SHOOT2,
+	S_HIVEELEMENTAL_DORMANT,
+	S_HIVEELEMENTAL_PAIN,
+	S_HIVEELEMENTAL_DIE1,
+	S_HIVEELEMENTAL_DIE2,
+	S_HIVEELEMENTAL_DIE3,
+
+	S_BUMBLEBORE_SPAWN,
+	S_BUMBLEBORE_LOOK1,
+	S_BUMBLEBORE_LOOK2,
+	S_BUMBLEBORE_FLY1,
+	S_BUMBLEBORE_FLY2,
+	S_BUMBLEBORE_RAISE,
+	S_BUMBLEBORE_FALL1,
+	S_BUMBLEBORE_FALL2,
+	S_BUMBLEBORE_STUCK1,
+	S_BUMBLEBORE_STUCK2,
+	S_BUMBLEBORE_DIE,
+
+	S_BBUZZFLY1,
+	S_BBUZZFLY2,
+
+	S_SMASHSPIKE_FLOAT,
+	S_SMASHSPIKE_EASE1,
+	S_SMASHSPIKE_EASE2,
+	S_SMASHSPIKE_FALL,
+	S_SMASHSPIKE_STOMP1,
+	S_SMASHSPIKE_STOMP2,
+	S_SMASHSPIKE_RISE1,
+	S_SMASHSPIKE_RISE2,
+
+	S_CACO_LOOK,
+	S_CACO_WAKE1,
+	S_CACO_WAKE2,
+	S_CACO_WAKE3,
+	S_CACO_WAKE4,
+	S_CACO_ROAR,
+	S_CACO_CHASE,
+	S_CACO_CHASE_REPEAT,
+	S_CACO_RANDOM,
+	S_CACO_PREPARE_SOUND,
+	S_CACO_PREPARE1,
+	S_CACO_PREPARE2,
+	S_CACO_PREPARE3,
+	S_CACO_SHOOT_SOUND,
+	S_CACO_SHOOT1,
+	S_CACO_SHOOT2,
+	S_CACO_CLOSE,
+	S_CACO_DIE_FLAGS,
+	S_CACO_DIE_GIB1,
+	S_CACO_DIE_GIB2,
+	S_CACO_DIE_SCREAM,
+	S_CACO_DIE_SHATTER,
+	S_CACO_DIE_FALL,
+	S_CACOSHARD_RANDOMIZE,
+	S_CACOSHARD1_1,
+	S_CACOSHARD1_2,
+	S_CACOSHARD2_1,
+	S_CACOSHARD2_2,
+	S_CACOFIRE1,
+	S_CACOFIRE2,
+	S_CACOFIRE3,
+	S_CACOFIRE_EXPLODE1,
+	S_CACOFIRE_EXPLODE2,
+	S_CACOFIRE_EXPLODE3,
+	S_CACOFIRE_EXPLODE4,
+
+	S_SPINBOBERT_MOVE_FLIPUP,
+	S_SPINBOBERT_MOVE_UP,
+	S_SPINBOBERT_MOVE_FLIPDOWN,
+	S_SPINBOBERT_MOVE_DOWN,
+	S_SPINBOBERT_FIRE_MOVE,
+	S_SPINBOBERT_FIRE_GHOST,
+	S_SPINBOBERT_FIRE_TRAIL1,
+	S_SPINBOBERT_FIRE_TRAIL2,
+	S_SPINBOBERT_FIRE_TRAIL3,
+
+	S_HANGSTER_LOOK,
+	S_HANGSTER_SWOOP1,
+	S_HANGSTER_SWOOP2,
+	S_HANGSTER_ARC1,
+	S_HANGSTER_ARC2,
+	S_HANGSTER_ARC3,
+	S_HANGSTER_FLY1,
+	S_HANGSTER_FLY2,
+	S_HANGSTER_FLY3,
+	S_HANGSTER_FLY4,
+	S_HANGSTER_FLYREPEAT,
+	S_HANGSTER_ARCUP1,
+	S_HANGSTER_ARCUP2,
+	S_HANGSTER_ARCUP3,
+	S_HANGSTER_RETURN1,
+	S_HANGSTER_RETURN2,
+	S_HANGSTER_RETURN3,
+
 	S_CRUMBLE1,
 	S_CRUMBLE2,
 
@@ -3212,6 +3624,11 @@ typedef enum state
 	S_WPLD5,
 	S_WPLD6,
 
+	S_DUST1,
+	S_DUST2,
+	S_DUST3,
+	S_DUST4,
+
 	S_ROCKSPAWN,
 
 	S_ROCKCRUMBLEA,
@@ -3268,29 +3685,32 @@ typedef enum mobj_type
 	MT_TAILSOVERLAY, // c:
 
 	// Enemies
-	MT_BLUECRAWLA,
-	MT_REDCRAWLA,
-	MT_GFZFISH, // Greenflower Fish
-	MT_GOLDBUZZ,
-	MT_REDBUZZ,
-	MT_AQUABUZZ, // AquaBuzz for ATZ
+	MT_BLUECRAWLA, // Crawla (Blue)
+	MT_REDCRAWLA, // Crawla (Red)
+	MT_GFZFISH, // SDURF
+	MT_GOLDBUZZ, // Buzz (Gold)
+	MT_REDBUZZ, // Buzz (Red)
 	MT_JETTBOMBER, // Jetty-Syn Bomber
 	MT_JETTGUNNER, // Jetty-Syn Gunner
 	MT_CRAWLACOMMANDER, // Crawla Commander
 	MT_DETON, // Deton
 	MT_SKIM, // Skim mine dropper
-	MT_TURRET,
-	MT_POPUPTURRET,
-	MT_SHARP, // Sharp
+	MT_TURRET, // Industrial Turret
+	MT_POPUPTURRET, // Pop-Up Turret
+	MT_SPINCUSHION, // Spincushion
+	MT_CRUSHSTACEAN, // Crushstacean
+	MT_CRUSHCLAW, // Big meaty claw
+	MT_CRUSHCHAIN, // Chain
 	MT_JETJAW, // Jet Jaw
 	MT_SNAILER, // Snailer
-	MT_VULTURE, // Vulture
+	MT_VULTURE, // BASH
 	MT_POINTY, // Pointy
 	MT_POINTYBALL, // Pointy Ball
 	MT_ROBOHOOD, // Robo-Hood
-	MT_FACESTABBER, // CastleBot FaceStabber
+	MT_FACESTABBER, // Castlebot Facestabber
+	MT_FACESTABBERSPEAR, // Castlebot Facestabber spear aura
 	MT_EGGGUARD, // Egg Guard
-	MT_EGGSHIELD, // Egg Shield for Egg Guard
+	MT_EGGSHIELD, // Egg Guard's shield
 	MT_GSNAPPER, // Green Snapper
 	MT_MINUS, // Minus
 	MT_SPRINGSHELL, // Spring Shell
@@ -3360,7 +3780,9 @@ typedef enum mobj_type
 	// Collectible Items
 	MT_RING,
 	MT_FLINGRING, // Lost ring
-	MT_BLUEBALL,  // Blue sphere replacement for special stages
+	MT_BLUESPHERE,  // Blue sphere for special stages
+	MT_FLINGBLUESPHERE, // Lost blue sphere
+	MT_BOMBSPHERE,
 	MT_REDTEAMRING,  //Rings collectable by red team.
 	MT_BLUETEAMRING, //Rings collectable by blue team.
 	MT_TOKEN, // Special Stage token for special stage
@@ -3380,28 +3802,31 @@ typedef enum mobj_type
 
 	// Springs and others
 	MT_FAN,
-	MT_STEAM, // Steam riser
-	MT_BLUESPRING,
+	MT_STEAM,
+	MT_BUMPER,
+	MT_BALLOON,
+
 	MT_YELLOWSPRING,
 	MT_REDSPRING,
-	MT_YELLOWDIAG, // Yellow Diagonal Spring
-	MT_REDDIAG, // Red Diagonal Spring
-	MT_YELLOWHORIZ, // Yellow Side Spring
-	MT_REDHORIZ, // Red Side Spring
-	MT_BLUEHORIZ, // Blue Side Spring
+	MT_BLUESPRING,
+	MT_YELLOWDIAG,
+	MT_REDDIAG,
+	MT_BLUEDIAG,
+	MT_YELLOWHORIZ,
+	MT_REDHORIZ,
+	MT_BLUEHORIZ,
 
 	// Interactive Objects
 	MT_BUBBLES, // Bubble source
 	MT_SIGN, // Level end sign
 	MT_SPIKEBALL, // Spike Ball
-	MT_SPECIALSPIKEBALL,
 	MT_SPINFIRE,
 	MT_SPIKE,
 	MT_WALLSPIKE,
 	MT_WALLSPIKEBASE,
 	MT_STARPOST,
 	MT_BIGMINE,
-	MT_BIGAIRMINE,
+	MT_BLASTEXECUTOR,
 	MT_CANNONLAUNCHER,
 
 	// Monitor miscellany
@@ -3487,8 +3912,11 @@ typedef enum mobj_type
 	MT_GFZFLOWER1,
 	MT_GFZFLOWER2,
 	MT_GFZFLOWER3,
+
+	MT_BLUEBERRYBUSH,
 	MT_BERRYBUSH,
 	MT_BUSH,
+
 	// Trees (both GFZ and misc)
 	MT_GFZTREE,
 	MT_GFZBERRYTREE,
@@ -3500,10 +3928,14 @@ typedef enum mobj_type
 	MT_POLYGONTREE,
 	MT_BUSHTREE,
 	MT_BUSHREDTREE,
+	MT_SPRINGTREE,
 
 	// Techno Hill Scenery
 	MT_THZFLOWER1,
 	MT_THZFLOWER2,
+	MT_THZFLOWER3,
+	MT_THZTREE, // Steam whistle tree/bush
+	MT_THZTREEBRANCH, // branch of said tree
 	MT_ALARM,
 
 	// Deep Sea Scenery
@@ -3516,6 +3948,10 @@ typedef enum mobj_type
 	MT_CORAL2, // Coral 2
 	MT_CORAL3, // Coral 3
 	MT_BLUECRYSTAL, // Blue Crystal
+	MT_KELP, // Kelp
+	MT_DSZSTALAGMITE, // Deep Sea 1 Stalagmite
+	MT_DSZ2STALAGMITE, // Deep Sea 2 Stalagmite
+	MT_LIGHTBEAM, // DSZ Light beam
 
 	// Castle Eggman Scenery
 	MT_CHAIN, // CEZ Chain
@@ -3533,11 +3969,28 @@ typedef enum mobj_type
 	MT_BIGMACECHAIN, // Big Mace Chain
 	MT_SMALLMACE, // Small Mace
 	MT_BIGMACE, // Big Mace
+	MT_SMALLGRABCHAIN, // Small Grab Chain
+	MT_BIGGRABCHAIN, // Big Grab Chain
 	MT_YELLOWSPRINGBALL, // Yellow spring on a ball
 	MT_REDSPRINGBALL, // Red spring on a ball
 	MT_SMALLFIREBAR, // Small Firebar
 	MT_BIGFIREBAR, // Big Firebar
-	MT_CEZFLOWER,
+	MT_CEZFLOWER, // Flower
+	MT_CEZPOLE, // Pole
+	MT_CEZBANNER, // Banner
+	MT_PINETREE, // Pine Tree
+	MT_CEZBUSH1, // Bush 1
+	MT_CEZBUSH2, // Bush 2
+	MT_CANDLE, // Candle
+	MT_CANDLEPRICKET, // Candle pricket
+	MT_FLAMEHOLDER, // Flame holder
+	MT_FIRETORCH, // Fire torch
+	MT_WAVINGFLAG, // Waving flag
+	MT_WAVINGFLAGSEG, // Waving flag segment
+	MT_CRAWLASTATUE, // Crawla statue
+	MT_FACESTABBERSTATUE, // Facestabber statue
+	MT_SUSPICIOUSFACESTABBERSTATUE, // :eggthinking:
+	MT_BRAMBLES, // Brambles
 
 	// Arid Canyon Scenery
 	MT_BIGTUMBLEWEED,
@@ -3589,8 +4042,28 @@ typedef enum mobj_type
 	MT_LAMPPOST2,  // with snow
 	MT_HANGSTAR,
 	// Xmas GFZ bushes
+	MT_XMASBLUEBERRYBUSH,
 	MT_XMASBERRYBUSH,
 	MT_XMASBUSH,
+	// FHZ
+	MT_FHZICE1,
+	MT_FHZICE2,
+
+	// Halloween Scenery
+	// Pumpkins
+	MT_JACKO1,
+	MT_JACKO2,
+	MT_JACKO3,
+	// Dr Seuss Trees
+	MT_HHZTREE_TOP,
+	MT_HHZTREE_PART,
+	// Misc
+	MT_HHZSHROOM,
+	MT_HHZGRASS,
+	MT_HHZTENTACLE1,
+	MT_HHZTENTACLE2,
+	MT_HHZSTALAGMITE_TALL,
+	MT_HHZSTALAGMITE_SHORT,
 
 	// Botanic Serenity scenery
 	MT_BSZTALLFLOWER_RED,
@@ -3662,21 +4135,42 @@ typedef enum mobj_type
 
 	// Flickies
 	MT_FLICKY_01, // Bluebird
+	MT_FLICKY_01_CENTER,
 	MT_FLICKY_02, // Rabbit
+	MT_FLICKY_02_CENTER,
 	MT_FLICKY_03, // Chicken
+	MT_FLICKY_03_CENTER,
 	MT_FLICKY_04, // Seal
+	MT_FLICKY_04_CENTER,
 	MT_FLICKY_05, // Pig
+	MT_FLICKY_05_CENTER,
 	MT_FLICKY_06, // Chipmunk
+	MT_FLICKY_06_CENTER,
 	MT_FLICKY_07, // Penguin
+	MT_FLICKY_07_CENTER,
 	MT_FLICKY_08, // Fish
+	MT_FLICKY_08_CENTER,
 	MT_FLICKY_09, // Ram
+	MT_FLICKY_09_CENTER,
 	MT_FLICKY_10, // Puffin
+	MT_FLICKY_10_CENTER,
 	MT_FLICKY_11, // Cow
+	MT_FLICKY_11_CENTER,
 	MT_FLICKY_12, // Rat
+	MT_FLICKY_12_CENTER,
 	MT_FLICKY_13, // Bear
+	MT_FLICKY_13_CENTER,
 	MT_FLICKY_14, // Dove
+	MT_FLICKY_14_CENTER,
 	MT_FLICKY_15, // Cat
+	MT_FLICKY_15_CENTER,
 	MT_FLICKY_16, // Canary
+	MT_FLICKY_16_CENTER,
+	MT_SECRETFLICKY_01, // Spider
+	MT_SECRETFLICKY_01_CENTER,
+	MT_SECRETFLICKY_02, // Bat
+	MT_SECRETFLICKY_02_CENTER,
+	MT_SEED,
 
 	// Environmental Effects
 	MT_RAIN, // Rain
@@ -3689,7 +4183,6 @@ typedef enum mobj_type
 	MT_WATERZAP,
 	MT_SPINDUST, // Spindash dust
 	MT_TFOG,
-	MT_SEED,
 	MT_PARTICLE,
 	MT_PARTICLEGEN, // For fans, etc.
 
@@ -3712,6 +4205,7 @@ typedef enum mobj_type
 	MT_AWATERH, // Ambient Water Sound 8
 	MT_RANDOMAMBIENT,
 	MT_RANDOMAMBIENT2,
+	MT_MACHINEAMBIENCE,
 
 	MT_CORK,
 
@@ -3770,7 +4264,9 @@ typedef enum mobj_type
 	MT_HOOPCOLLIDE, // Collision detection for NiGHTS hoops
 	MT_HOOPCENTER, // Center of a hoop
 	MT_NIGHTSCORE,
-	MT_NIGHTSWING,
+	MT_NIGHTSCHIP, // NiGHTS Chip
+	MT_FLINGNIGHTSCHIP, // Lost NiGHTS Chip
+	MT_NIGHTSSTAR, // NiGHTS Star
 	MT_NIGHTSSUPERLOOP,
 	MT_NIGHTSDRILLREFILL,
 	MT_NIGHTSHELPER,
@@ -3778,6 +4274,27 @@ typedef enum mobj_type
 	MT_NIGHTSLINKFREEZE,
 	MT_EGGCAPSULE,
 	MT_NIGHTOPIANHELPER, // the actual helper object that orbits you
+	MT_PIAN, // decorative singing friend
+	MT_SHLEEP, // almost-decorative sleeping enemy
+
+	// Secret badniks and hazards, shhhh
+	MT_PENGUINATOR,
+	MT_POPHAT,
+	MT_POPSHOT,
+
+	MT_HIVEELEMENTAL,
+	MT_BUMBLEBORE,
+
+	MT_BUBBLEBUZZ,
+
+	MT_SMASHINGSPIKEBALL,
+	MT_CACOLANTERN,
+	MT_CACOSHARD,
+	MT_CACOFIRE,
+	MT_SPINBOBERT,
+	MT_SPINBOBERT_FIRE1,
+	MT_SPINBOBERT_FIRE2,
+	MT_HANGSTER,
 
 	// Utility Objects
 	MT_TELEPORTMAN,
@@ -3799,6 +4316,7 @@ typedef enum mobj_type
 	MT_SPARK, //spark
 	MT_EXPLODE, // Robot Explosion
 	MT_UWEXPLODE, // Underwater Explosion
+	MT_DUST,
 	MT_ROCKSPAWNER,
 	MT_FALLINGROCK,
 	MT_ROCKCRUMBLE1,
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index 0cb530b53dc486fc7fb92e7e69477a5665f99d61..ce017620c5e712fd2eeb1cfaa777433e1954b139 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -1356,11 +1356,12 @@ static int lib_pRadiusAttack(lua_State *L)
 	mobj_t *spot = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
 	mobj_t *source = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ));
 	fixed_t damagedist = luaL_checkfixed(L, 3);
+	UINT8 damagetype = luaL_optinteger(L, 4, 0);
 	NOHUD
 	INLEVEL
 	if (!spot || !source)
 		return LUA_ErrInvalid(L, "mobj_t");
-	P_RadiusAttack(spot, source, damagedist);
+	P_RadiusAttack(spot, source, damagedist, damagetype);
 	return 0;
 }
 
@@ -1808,9 +1809,11 @@ static int lib_pFadeLight(lua_State *L)
 	INT16 tag = (INT16)luaL_checkinteger(L, 1);
 	INT32 destvalue = (INT32)luaL_checkinteger(L, 2);
 	INT32 speed = (INT32)luaL_checkinteger(L, 3);
+	boolean ticbased = lua_optboolean(L, 4);
+	boolean force = lua_optboolean(L, 5);
 	NOHUD
 	INLEVEL
-	P_FadeLight(tag, destvalue, speed);
+	P_FadeLight(tag, destvalue, speed, ticbased, force);
 	return 0;
 }
 
diff --git a/src/lua_consolelib.c b/src/lua_consolelib.c
index c82c39b4606c4425e2b425d7deece121c568ed7c..d576efb9ab9bc23c7976bfebf3b6dae4b5041f5e 100644
--- a/src/lua_consolelib.c
+++ b/src/lua_consolelib.c
@@ -85,7 +85,9 @@ void Got_Luacmd(UINT8 **cp, INT32 playernum)
 
 deny:
 	//must be hacked/buggy client
-	lua_settop(gL, 0); // clear stack
+	if (gL) // check if Lua is actually turned on first, you dummmy -- Monster Iestyn 04/07/18
+		lua_settop(gL, 0); // clear stack
+
 	CONS_Alert(CONS_WARNING, M_GetText("Illegal lua command received from %s\n"), player_names[playernum]);
 	if (server)
 	{
diff --git a/src/lua_hud.h b/src/lua_hud.h
index beaca7883e4425818539cbd9d0866e4e6cf3b8cc..c1479d5efe889f85b6fae657aa445a2fee86d7e7 100644
--- a/src/lua_hud.h
+++ b/src/lua_hud.h
@@ -24,7 +24,7 @@ enum hud {
 	// NiGHTS mode
 	hud_nightslink,
 	hud_nightsdrill,
-	hud_nightsrings,
+	hud_nightsspheres,
 	hud_nightsscore,
 	hud_nightstime,
 	hud_nightsrecords,
diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index f1bfcb8f1b845e0b3161df7084415badaa01d5d4..28fe8c75f7c0f955d51f18e02a60e92109a83072 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -421,6 +421,7 @@ static int sector_get(lua_State *L)
 {
 	sector_t *sector = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR));
 	enum sector_e field = luaL_checkoption(L, 2, sector_opt[0], sector_opt);
+	INT16 i;
 
 	if (!sector)
 	{
@@ -443,11 +444,23 @@ static int sector_get(lua_State *L)
 		lua_pushfixed(L, sector->ceilingheight);
 		return 1;
 	case sector_floorpic: // floorpic
-		lua_pushlstring(L, levelflats[sector->floorpic].name, 8);
+	{
+		levelflat_t *levelflat = &levelflats[sector->floorpic];
+		for (i = 0; i < 8; i++)
+			if (!levelflat->name[i])
+				break;
+		lua_pushlstring(L, levelflat->name, i);
 		return 1;
+	}
 	case sector_ceilingpic: // ceilingpic
-		lua_pushlstring(L, levelflats[sector->ceilingpic].name, 8);
+	{
+		levelflat_t *levelflat = &levelflats[sector->ceilingpic];
+		for (i = 0; i < 8; i++)
+			if (!levelflat->name[i])
+				break;
+		lua_pushlstring(L, levelflat->name, i);
 		return 1;
+	}
 	case sector_lightlevel:
 		lua_pushinteger(L, sector->lightlevel);
 		return 1;
@@ -1784,6 +1797,8 @@ static int mapheaderinfo_get(lua_State *L)
 		lua_pushinteger(L, header->levelselect);
 	else if (fastcmp(field,"bonustype"))
 		lua_pushinteger(L, header->bonustype);
+	else if (fastcmp(field,"maxbonuslives"))
+		lua_pushinteger(L, header->maxbonuslives);
 	else if (fastcmp(field,"levelflags"))
 		lua_pushinteger(L, header->levelflags);
 	else if (fastcmp(field,"menuflags"))
diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c
index 1583bd3c4df9f335338f5d56d7e04dc5997a6f1e..da0e99ab216510b10d7661a2bc6fc8e03ba62a60 100644
--- a/src/lua_mobjlib.c
+++ b/src/lua_mobjlib.c
@@ -40,6 +40,8 @@ enum mobj_e {
 	mobj_subsector,
 	mobj_floorz,
 	mobj_ceilingz,
+	mobj_floorrover,
+	mobj_ceilingrover,
 	mobj_radius,
 	mobj_height,
 	mobj_momx,
@@ -100,6 +102,8 @@ static const char *const mobj_opt[] = {
 	"subsector",
 	"floorz",
 	"ceilingz",
+	"floorrover",
+	"ceilingrover",
 	"radius",
 	"height",
 	"momx",
@@ -208,6 +212,12 @@ static int mobj_get(lua_State *L)
 	case mobj_ceilingz:
 		lua_pushfixed(L, mo->ceilingz);
 		break;
+	case mobj_floorrover:
+		LUA_PushUserdata(L, mo->floorrover, META_FFLOOR);
+		break;
+	case mobj_ceilingrover:
+		LUA_PushUserdata(L, mo->ceilingrover, META_FFLOOR);
+		break;
 	case mobj_radius:
 		lua_pushfixed(L, mo->radius);
 		break;
@@ -396,6 +406,8 @@ static int mobj_set(lua_State *L)
 		P_CheckPosition(mo, mo->x, mo->y);
 		mo->floorz = tmfloorz;
 		mo->ceilingz = tmceilingz;
+		mo->floorrover = tmfloorrover;
+		mo->ceilingrover = tmceilingrover;
 		P_SetTarget(&tmthing, ptmthing);
 		break;
 	}
@@ -430,6 +442,10 @@ static int mobj_set(lua_State *L)
 		return NOSETPOS;
 	case mobj_ceilingz:
 		return NOSETPOS;
+	case mobj_floorrover:
+		return NOSET;
+	case mobj_ceilingrover:
+		return NOSET;
 	case mobj_radius:
 	{
 		mobj_t *ptmthing = tmthing;
@@ -439,6 +455,8 @@ static int mobj_set(lua_State *L)
 		P_CheckPosition(mo, mo->x, mo->y);
 		mo->floorz = tmfloorz;
 		mo->ceilingz = tmceilingz;
+		mo->floorrover = tmfloorrover;
+		mo->ceilingrover = tmceilingrover;
 		P_SetTarget(&tmthing, ptmthing);
 		break;
 	}
@@ -451,6 +469,8 @@ static int mobj_set(lua_State *L)
 		P_CheckPosition(mo, mo->x, mo->y);
 		mo->floorz = tmfloorz;
 		mo->ceilingz = tmceilingz;
+		mo->floorrover = tmfloorrover;
+		mo->ceilingrover = tmceilingrover;
 		P_SetTarget(&tmthing, ptmthing);
 		break;
 	}
diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c
index 12b2646d04df53fa69f3b0526435da84ca4df1ff..f973061f1d6ca7c928b303129208ad279fb9048d 100644
--- a/src/lua_playerlib.c
+++ b/src/lua_playerlib.c
@@ -130,6 +130,8 @@ static int player_get(lua_State *L)
 		lua_pushangle(L, plr->drawangle);
 	else if (fastcmp(field,"rings"))
 		lua_pushinteger(L, plr->rings);
+	else if (fastcmp(field,"spheres"))
+		lua_pushinteger(L, plr->spheres);
 	else if (fastcmp(field,"pity"))
 		lua_pushinteger(L, plr->pity);
 	else if (fastcmp(field,"currentweapon"))
@@ -288,20 +290,40 @@ static int player_get(lua_State *L)
 		LUA_PushUserdata(L, plr->capsule, META_MOBJ);
 	else if (fastcmp(field,"mare"))
 		lua_pushinteger(L, plr->mare);
+	else if (fastcmp(field,"marelap"))
+		lua_pushinteger(L, plr->marelap);
+	else if (fastcmp(field,"marebonuslap"))
+		lua_pushinteger(L, plr->marebonuslap);
 	else if (fastcmp(field,"marebegunat"))
 		lua_pushinteger(L, plr->marebegunat);
 	else if (fastcmp(field,"startedtime"))
 		lua_pushinteger(L, plr->startedtime);
 	else if (fastcmp(field,"finishedtime"))
 		lua_pushinteger(L, plr->finishedtime);
+	else if (fastcmp(field,"lapbegunat"))
+		lua_pushinteger(L, plr->lapbegunat);
+	else if (fastcmp(field,"lapstartedtime"))
+		lua_pushinteger(L, plr->lapstartedtime);
+	else if (fastcmp(field,"finishedspheres"))
+		lua_pushinteger(L, plr->finishedspheres);
 	else if (fastcmp(field,"finishedrings"))
 		lua_pushinteger(L, plr->finishedrings);
 	else if (fastcmp(field,"marescore"))
 		lua_pushinteger(L, plr->marescore);
 	else if (fastcmp(field,"lastmarescore"))
 		lua_pushinteger(L, plr->lastmarescore);
+	else if (fastcmp(field,"totalmarescore"))
+		lua_pushinteger(L, plr->totalmarescore);
 	else if (fastcmp(field,"lastmare"))
 		lua_pushinteger(L, plr->lastmare);
+	else if (fastcmp(field,"lastmarelap"))
+		lua_pushinteger(L, plr->lastmarelap);
+	else if (fastcmp(field,"lastmarebonuslap"))
+		lua_pushinteger(L, plr->lastmarebonuslap);
+	else if (fastcmp(field,"totalmarelap"))
+		lua_pushinteger(L, plr->totalmarelap);
+	else if (fastcmp(field,"totalmarebonuslap"))
+		lua_pushinteger(L, plr->totalmarebonuslap);
 	else if (fastcmp(field,"maxlink"))
 		lua_pushinteger(L, plr->maxlink);
 	else if (fastcmp(field,"texttimer"))
@@ -396,6 +418,8 @@ static int player_set(lua_State *L)
 		plr->drawangle = luaL_checkangle(L, 3);
 	else if (fastcmp(field,"rings"))
 		plr->rings = (INT32)luaL_checkinteger(L, 3);
+	else if (fastcmp(field,"spheres"))
+		plr->spheres = (INT32)luaL_checkinteger(L, 3);
 	else if (fastcmp(field,"pity"))
 		plr->pity = (SINT8)luaL_checkinteger(L, 3);
 	else if (fastcmp(field,"currentweapon"))
@@ -448,7 +472,7 @@ static int player_set(lua_State *L)
 	else if (fastcmp(field,"followitem"))
 		plr->followitem = luaL_checkinteger(L, 3);
 	else if (fastcmp(field,"followmobj"))
-		plr->followmobj = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
+		P_SetTarget(&plr->followmobj, *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ)));
 	else if (fastcmp(field,"actionspd"))
 		plr->actionspd = (INT32)luaL_checkinteger(L, 3);
 	else if (fastcmp(field,"mindash"))
@@ -564,20 +588,40 @@ static int player_set(lua_State *L)
 	}
 	else if (fastcmp(field,"mare"))
 		plr->mare = (UINT8)luaL_checkinteger(L, 3);
+	else if (fastcmp(field,"marelap"))
+		plr->marelap = (UINT8)luaL_checkinteger(L, 3);
+	else if (fastcmp(field,"marebonuslap"))
+		plr->marebonuslap = (UINT8)luaL_checkinteger(L, 3);
 	else if (fastcmp(field,"marebegunat"))
 		plr->marebegunat = (tic_t)luaL_checkinteger(L, 3);
 	else if (fastcmp(field,"startedtime"))
 		plr->startedtime = (tic_t)luaL_checkinteger(L, 3);
 	else if (fastcmp(field,"finishedtime"))
 		plr->finishedtime = (tic_t)luaL_checkinteger(L, 3);
+	else if (fastcmp(field,"lapbegunat"))
+		plr->lapbegunat = (tic_t)luaL_checkinteger(L, 3);
+	else if (fastcmp(field,"lapstartedtime"))
+		plr->lapstartedtime = (tic_t)luaL_checkinteger(L, 3);
+	else if (fastcmp(field,"finishedspheres"))
+		plr->finishedspheres = (INT16)luaL_checkinteger(L, 3);
 	else if (fastcmp(field,"finishedrings"))
 		plr->finishedrings = (INT16)luaL_checkinteger(L, 3);
 	else if (fastcmp(field,"marescore"))
 		plr->marescore = (UINT32)luaL_checkinteger(L, 3);
 	else if (fastcmp(field,"lastmarescore"))
 		plr->lastmarescore = (UINT32)luaL_checkinteger(L, 3);
+	else if (fastcmp(field,"totalmarescore"))
+		plr->totalmarescore = (UINT32)luaL_checkinteger(L, 3);
 	else if (fastcmp(field,"lastmare"))
 		plr->lastmare = (UINT8)luaL_checkinteger(L, 3);
+	else if (fastcmp(field,"lastmarelap"))
+		plr->lastmarelap = (UINT8)luaL_checkinteger(L, 3);
+	else if (fastcmp(field,"lastmarebonuslap"))
+		plr->lastmarebonuslap = (UINT8)luaL_checkinteger(L, 3);
+	else if (fastcmp(field,"totalmarelap"))
+		plr->totalmarelap = (UINT8)luaL_checkinteger(L, 3);
+	else if (fastcmp(field,"totalmarebonuslap"))
+		plr->totalmarebonuslap = (UINT8)luaL_checkinteger(L, 3);
 	else if (fastcmp(field,"maxlink"))
 		plr->maxlink = (INT32)luaL_checkinteger(L, 3);
 	else if (fastcmp(field,"texttimer"))
diff --git a/src/lua_script.c b/src/lua_script.c
index 79927613c1010acd50d87cb88c46bf0b6f340db6..3225e332a8c7d5ef47fb4d1c95bd64d5f35166a1 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -193,25 +193,27 @@ void LUA_LoadLump(UINT16 wad, UINT16 lump)
 {
 	MYFILE f;
 	char *name;
-
+	size_t len;
 	f.wad = wad;
 	f.size = W_LumpLengthPwad(wad, lump);
 	f.data = Z_Malloc(f.size, PU_LUA, NULL);
 	W_ReadLumpPwad(wad, lump, f.data);
 	f.curpos = f.data;
 
+	len = strlen(wadfiles[wad]->filename); // length of file name
+
 	if (wadfiles[wad]->type == RET_LUA)
 	{
-		name = malloc(strlen(wadfiles[wad]->filename)+1);
+		name = malloc(len+1);
 		strcpy(name, wadfiles[wad]->filename);
 	}
 	else // If it's not a .lua file, copy the lump name in too.
 	{
 		lumpinfo_t *lump_p = &wadfiles[wad]->lumpinfo[lump];
-		size_t length = strlen(wadfiles[wad]->filename) + 1 + strlen(lump_p->name2); // length of file name, '|', and lump name
-		name = malloc(length + 1);
+		len += 1 + strlen(lump_p->name2); // length of file name, '|', and lump name
+		name = malloc(len+1);
 		sprintf(name, "%s|%s", wadfiles[wad]->filename, lump_p->name2);
-		name[length] = '\0';
+		name[len] = '\0';
 	}
 
 	LUA_LoadFile(&f, name); // actually load file!
@@ -572,9 +574,23 @@ static UINT8 ArchiveValue(int TABLESINDEX, int myindex)
 		break;
 	}
 	case LUA_TSTRING:
+	{
+		UINT16 len = (UINT16)lua_objlen(gL, myindex); // get length of string, including embedded zeros
+		const char *s = lua_tostring(gL, myindex);
+		UINT16 i = 0;
 		WRITEUINT8(save_p, ARCH_STRING);
-		WRITESTRING(save_p, lua_tostring(gL, myindex));
+		// if you're wondering why we're writing a string to save_p this way,
+		// it turns out that Lua can have embedded zeros ('\0') in the strings,
+		// so we can't use WRITESTRING as that cuts off when it finds a '\0'.
+		// Saving the size of the string also allows us to get the size of the string on the other end,
+		// fixing the awful crashes previously encountered for reading strings longer than 1024
+		// (yes I know that's kind of a stupid thing to care about, but it'd be evil to trim or ignore them?)
+		// -- Monster Iestyn 05/08/18
+		WRITEUINT16(save_p, len); // save size of string
+		while (i < len)
+			WRITECHAR(save_p, s[i++]); // write chars individually, including the embedded zeros
 		break;
+	}
 	case LUA_TTABLE:
 	{
 		boolean found = false;
@@ -905,9 +921,19 @@ static UINT8 UnArchiveValue(int TABLESINDEX)
 		break;
 	case ARCH_STRING:
 	{
-		char value[1024];
-		READSTRING(save_p, value);
-		lua_pushstring(gL, value);
+		UINT16 len = READUINT16(save_p); // length of string, including embedded zeros
+		char *value;
+		UINT16 i = 0;
+		// See my comments in the ArchiveValue function;
+		// it's much the same for reading strings as writing them!
+		// (i.e. we can't use READSTRING either)
+		// -- Monster Iestyn 05/08/18
+		value = malloc(len); // make temp buffer of size len
+		// now read the actual string
+		while (i < len)
+			value[i++] = READCHAR(save_p); // read chars individually, including the embedded zeros
+		lua_pushlstring(gL, value, len); // push the string (note: this function supports embedded zeros)
+		free(value); // free the buffer
 		break;
 	}
 	case ARCH_TABLE:
diff --git a/src/m_anigif.c b/src/m_anigif.c
index 6ae112ea8ee86af4dcf57574f0988ecb9abc97d1..4ebf2aff3b307e79172d717f7dc7d6f8a9759b0d 100644
--- a/src/m_anigif.c
+++ b/src/m_anigif.c
@@ -497,7 +497,9 @@ static void GIF_framewrite(void)
 
 	// screen regions are handled in GIF_lzw
 	{
-		UINT16 delay = 3; // todo
+		int d1 = (int)((100.0/NEWTICRATE)*(gif_frames+1));
+		int d2 = (int)((100.0/NEWTICRATE)*(gif_frames));
+		UINT16 delay = d1-d2;
 		INT32 startline;
 
 		WRITEMEM(p, gifframe_gchead, 4);
diff --git a/src/m_cheat.c b/src/m_cheat.c
index 174e2780dde499460ac7ce1da91cf434f8ff7bd2..473209350899e4a5390c5ba73adb2d4dc43a3599 100644
--- a/src/m_cheat.c
+++ b/src/m_cheat.c
@@ -499,56 +499,211 @@ void Command_Teleport_f(void)
 	REQUIRE_INLEVEL;
 	REQUIRE_SINGLEPLAYER;
 
-	if (COM_Argc() < 3 || COM_Argc() > 7)
+	if (COM_Argc() < 3 || COM_Argc() > 11)
 	{
-		CONS_Printf(M_GetText("teleport -x <value> -y <value> -z <value>: teleport to a location\n"));
+		CONS_Printf(M_GetText("teleport -x <value> -y <value> -z <value> -ang <value> -aim <value>: teleport to a location\nteleport -sp <sequence> <placement>: teleport to specified checkpoint\n"));
 		return;
 	}
 
 	if (!p->mo)
 		return;
 
-	i = COM_CheckParm("-x");
+	i = COM_CheckParm("-sp");
 	if (i)
-		intx = atoi(COM_Argv(i + 1));
-	else
 	{
-		CONS_Alert(CONS_NOTICE, M_GetText("%s value not specified\n"), "X");
-		return;
-	}
+		INT32 starpostnum = atoi(COM_Argv(i + 1)); // starpost number
+		INT32 starpostpath = atoi(COM_Argv(i + 2)); // quick, dirty way to distinguish between paths
 
-	i = COM_CheckParm("-y");
-	if (i)
-		inty = atoi(COM_Argv(i + 1));
-	else
-	{
-		CONS_Alert(CONS_NOTICE, M_GetText("%s value not specified\n"), "Y");
-		return;
-	}
+		if (starpostnum < 0 || starpostpath < 0)
+		{
+			CONS_Alert(CONS_NOTICE, M_GetText("Negative starpost indexing is not valid.\n"));
+			return;
+		}
 
-	ss = R_PointInSubsector(intx*FRACUNIT, inty*FRACUNIT);
-	if (!ss || ss->sector->ceilingheight - ss->sector->floorheight < p->mo->height)
-	{
-		CONS_Alert(CONS_NOTICE, M_GetText("Not a valid location.\n"));
-		return;
-	}
-	i = COM_CheckParm("-z");
-	if (i)
-	{
-		intz = atoi(COM_Argv(i + 1));
-		intz <<= FRACBITS;
-		if (intz < ss->sector->floorheight)
-			intz = ss->sector->floorheight;
-		if (intz > ss->sector->ceilingheight - p->mo->height)
-			intz = ss->sector->ceilingheight - p->mo->height;
+		if (!starpostnum) // spawnpoints...
+		{
+			mapthing_t *mt;
+
+			if (starpostpath >= numcoopstarts)
+			{
+				CONS_Alert(CONS_NOTICE, M_GetText("Player %d spawnpoint not found (%d max).\n"), starpostpath+1, numcoopstarts-1);
+				return;
+			}
+
+			mt = playerstarts[starpostpath]; // Given above check, should never be NULL.
+			intx = mt->x<<FRACBITS;
+			inty = mt->y<<FRACBITS;
+
+			ss = R_IsPointInSubsector(intx, inty);
+			if (!ss || ss->sector->ceilingheight - ss->sector->floorheight < p->mo->height)
+			{
+				CONS_Alert(CONS_NOTICE, M_GetText("Spawnpoint not in a valid location.\n"));
+				return;
+			}
+
+			// Flagging a player's ambush will make them start on the ceiling
+			// Objectflip inverts
+			if (!!(mt->options & MTF_AMBUSH) ^ !!(mt->options & MTF_OBJECTFLIP))
+			{
+				intz = ss->sector->ceilingheight - p->mo->height;
+				if (mt->options >> ZSHIFT)
+					intz -= ((mt->options >> ZSHIFT) << FRACBITS);
+			}
+			else
+			{
+				intz = ss->sector->floorheight;
+				if (mt->options >> ZSHIFT)
+					intz += ((mt->options >> ZSHIFT) << FRACBITS);
+			}
+
+			if (mt->options & MTF_OBJECTFLIP) // flip the player!
+			{
+				p->mo->eflags |= MFE_VERTICALFLIP;
+				p->mo->flags2 |= MF2_OBJECTFLIP;
+			}
+			else
+			{
+				p->mo->eflags &= ~MFE_VERTICALFLIP;
+				p->mo->flags2 &= ~MF2_OBJECTFLIP;
+			}
+
+			localangle = p->mo->angle = p->drawangle = FixedAngle(mt->angle<<FRACBITS);
+		}
+		else // scan the thinkers to find starposts...
+		{
+			mobj_t *mo2;
+			thinker_t *th;
+
+			INT32 starpostmax = 0;
+			intz = starpostpath; // variable reuse - counting down for selection purposes
+
+			for (th = thinkercap.next; th != &thinkercap; th = th->next)
+			{
+				if (th->function.acp1 != (actionf_p1)P_MobjThinker)
+					continue;
+
+				mo2 = (mobj_t *)th;
+
+				if (mo2->type != MT_STARPOST)
+					continue;
+
+				if (mo2->health != starpostnum)
+				{
+					if (mo2->health > starpostmax)
+						starpostmax = mo2->health;
+					continue;
+				}
+
+				if (intz--)
+					continue;
+
+				break;
+			}
+
+			if (th == &thinkercap)
+			{
+				if (intz == starpostpath)
+					CONS_Alert(CONS_NOTICE, M_GetText("No starpost of position %d found (%d max).\n"), starpostnum, starpostmax);
+				else
+					CONS_Alert(CONS_NOTICE, M_GetText("Starpost of position %d, %d not found (%d, %d max).\n"), starpostnum, starpostpath, starpostmax, (starpostpath-intz)-1);
+				return;
+			}
+
+			ss = R_IsPointInSubsector(mo2->x, mo2->y);
+			if (!ss || ss->sector->ceilingheight - ss->sector->floorheight < p->mo->height)
+			{
+				CONS_Alert(CONS_NOTICE, M_GetText("Starpost not in a valid location.\n"));
+				return;
+			}
+
+			intx = mo2->x;
+			inty = mo2->y;
+			intz = mo2->z;
+
+			if (mo2->flags2 & MF2_OBJECTFLIP) // flip the player!
+			{
+				p->mo->eflags |= MFE_VERTICALFLIP;
+				p->mo->flags2 |= MF2_OBJECTFLIP;
+			}
+			else
+			{
+				p->mo->eflags &= ~MFE_VERTICALFLIP;
+				p->mo->flags2 &= ~MF2_OBJECTFLIP;
+			}
+
+			localangle = p->mo->angle = p->drawangle = mo2->angle;
+		}
+
+		CONS_Printf(M_GetText("Teleporting to checkpoint %d, %d...\n"), starpostnum, starpostpath);
 	}
 	else
-		intz = ss->sector->floorheight;
+	{
+		i = COM_CheckParm("-nop"); // undocumented stupid addition to allow pivoting on the spot with -ang and -aim
+		if (i)
+		{
+			intx = p->mo->x;
+			inty = p->mo->y;
+		}
+		else
+		{
+			i = COM_CheckParm("-x");
+			if (i)
+				intx = atoi(COM_Argv(i + 1))<<FRACBITS;
+			else
+			{
+				CONS_Alert(CONS_NOTICE, M_GetText("%s value not specified.\n"), "X");
+				return;
+			}
+
+			i = COM_CheckParm("-y");
+			if (i)
+				inty = atoi(COM_Argv(i + 1))<<FRACBITS;
+			else
+			{
+				CONS_Alert(CONS_NOTICE, M_GetText("%s value not specified.\n"), "Y");
+				return;
+			}
+		}
 
-	CONS_Printf(M_GetText("Teleporting to %d, %d, %d...\n"), intx, inty, FixedInt(intz));
+		ss = R_IsPointInSubsector(intx, inty);
+		if (!ss || ss->sector->ceilingheight - ss->sector->floorheight < p->mo->height)
+		{
+			CONS_Alert(CONS_NOTICE, M_GetText("Not a valid location.\n"));
+			return;
+		}
+		i = COM_CheckParm("-z");
+		if (i)
+		{
+			intz = atoi(COM_Argv(i + 1))<<FRACBITS;
+			if (intz < ss->sector->floorheight)
+				intz = ss->sector->floorheight;
+			if (intz > ss->sector->ceilingheight - p->mo->height)
+				intz = ss->sector->ceilingheight - p->mo->height;
+		}
+		else
+			intz = ((p->mo->eflags & MFE_VERTICALFLIP) ? ss->sector->ceilingheight : ss->sector->floorheight);
+
+		i = COM_CheckParm("-ang");
+		if (i)
+			localangle = p->drawangle = p->mo->angle = FixedAngle(atoi(COM_Argv(i + 1))<<FRACBITS);
+
+		i = COM_CheckParm("-aim");
+		if (i)
+		{
+			angle_t aim = FixedAngle(atoi(COM_Argv(i + 1))<<FRACBITS);
+			if (aim >= ANGLE_90 && aim <= ANGLE_270)
+			{
+				CONS_Alert(CONS_NOTICE, M_GetText("Not a valid aiming angle (between +/-90).\n"));
+				return;
+			}
+			localaiming = p->aiming = aim;
+		}
+
+		CONS_Printf(M_GetText("Teleporting to %d, %d, %d...\n"), FixedInt(intx), FixedInt(inty), FixedInt(intz));
+	}
 
 	P_MapStart();
-	if (!P_TeleportMove(p->mo, intx*FRACUNIT, inty*FRACUNIT, intz))
+	if (!P_TeleportMove(p->mo, intx, inty, intz))
 		CONS_Alert(CONS_WARNING, M_GetText("Unable to teleport to that spot!\n"));
 	else
 		S_StartSound(p->mo, sfx_mixup);
@@ -725,11 +880,19 @@ void Command_Setrings_f(void)
 
 	if (COM_Argc() > 1)
 	{
-		// P_GivePlayerRings does value clamping
-		players[consoleplayer].rings = 0;
-		P_GivePlayerRings(&players[consoleplayer], atoi(COM_Argv(1)));
-		if (!G_IsSpecialStage(gamemap) || !useNightsSS)
+		if (!(maptol & TOL_NIGHTS))
+		{
+			// P_GivePlayerRings does value clamping
+			players[consoleplayer].rings = 0;
+			P_GivePlayerRings(&players[consoleplayer], atoi(COM_Argv(1)));
 			players[consoleplayer].totalring -= atoi(COM_Argv(1)); //undo totalring addition done in P_GivePlayerRings
+		}
+		else
+		{
+			players[consoleplayer].spheres = 0;
+			P_GivePlayerSpheres(&players[consoleplayer], atoi(COM_Argv(1)));
+			// no totalsphere addition to revert
+		}
 
 		G_SetGameModified(multiplayer);
 	}
@@ -744,9 +907,15 @@ void Command_Setlives_f(void)
 
 	if (COM_Argc() > 1)
 	{
-		// P_GivePlayerLives does value clamping
-		players[consoleplayer].lives = 0;
-		P_GivePlayerLives(&players[consoleplayer], atoi(COM_Argv(1)));
+		SINT8 lives = atoi(COM_Argv(1));
+		if (lives == -1)
+			players[consoleplayer].lives = INFLIVES; // infinity!
+		else
+		{
+			// P_GivePlayerLives does value clamping
+			players[consoleplayer].lives = 0;
+			P_GivePlayerLives(&players[consoleplayer], atoi(COM_Argv(1)));
+		}
 
 		G_SetGameModified(multiplayer);
 	}
@@ -779,10 +948,12 @@ void Command_Setcontinues_f(void)
 static CV_PossibleValue_t op_mapthing_t[] = {{0, "MIN"}, {4095, "MAX"}, {0, NULL}};
 static CV_PossibleValue_t op_speed_t[] = {{1, "MIN"}, {128, "MAX"}, {0, NULL}};
 static CV_PossibleValue_t op_flags_t[] = {{0, "MIN"}, {15, "MAX"}, {0, NULL}};
+static CV_PossibleValue_t op_hoopflags_t[] = {{0, "MIN"}, {15, "MAX"}, {0, NULL}};
 
 consvar_t cv_mapthingnum = {"op_mapthingnum", "0", CV_NOTINNET, op_mapthing_t, NULL, 0, NULL, NULL, 0, 0, NULL};
 consvar_t cv_speed = {"op_speed", "16", CV_NOTINNET, op_speed_t, NULL, 0, NULL, NULL, 0, 0, NULL};
 consvar_t cv_opflags = {"op_flags", "0", CV_NOTINNET, op_flags_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_ophoopflags = {"op_hoopflags", "4", CV_NOTINNET, op_hoopflags_t, NULL, 0, NULL, NULL, 0, 0, NULL};
 
 boolean objectplacing = false;
 mobjtype_t op_currentthing = 0; // For the object placement mode
@@ -986,17 +1157,10 @@ void OP_NightsObjectplace(player_t *player)
 	{
 		UINT16 angle = (UINT16)(player->anotherflyangle % 360);
 		INT16 temp = (INT16)FixedInt(AngleFixed(player->mo->angle)); // Traditional 2D Angle
-		sector_t *sec = player->mo->subsector->sector;
-#ifdef ESLOPE
-		fixed_t fheight = sec->f_slope ? P_GetZAt(sec->f_slope, player->mo->x & 0xFFFF0000, player->mo->y & 0xFFFF0000) : sec->floorheight;
-#else
-		fixed_t fheight = sec->floorheight;
-#endif
-
 
 		player->pflags |= PF_ATTACKDOWN;
 
-		mt = OP_CreateNewMapThing(player, 1705, false);
+		mt = OP_CreateNewMapThing(player, 1713, false);
 
 		// Tilt
 		mt->angle = (INT16)FixedInt(FixedDiv(angle*FRACUNIT, 360*(FRACUNIT/256)));
@@ -1007,43 +1171,84 @@ void OP_NightsObjectplace(player_t *player)
 			temp += 90;
 		temp %= 360;
 
-		mt->options = (UINT16)((player->mo->z - fheight)>>FRACBITS);
+		mt->options = (mt->options & ~(UINT16)cv_opflags.value) | (UINT16)cv_ophoopflags.value;
 		mt->angle = (INT16)(mt->angle+(INT16)((FixedInt(FixedDiv(temp*FRACUNIT, 360*(FRACUNIT/256))))<<8));
 
-		P_SpawnHoopsAndRings(mt);
+		P_SpawnHoopsAndRings(mt, false);
 	}
 
 	// This places a bumper!
 	if (cmd->buttons & BT_TOSSFLAG)
 	{
+		UINT16 vertangle = (UINT16)(player->anotherflyangle % 360);
+		UINT16 newflags, newz;
+
 		player->pflags |= PF_ATTACKDOWN;
 		if (!OP_HeightOkay(player, false))
 			return;
 
 		mt = OP_CreateNewMapThing(player, (UINT16)mobjinfo[MT_NIGHTSBUMPER].doomednum, false);
+		newz = min((mt->options >> ZSHIFT) - (mobjinfo[MT_NIGHTSBUMPER].height/4), 0);
+			// height offset: from P_TouchSpecialThing case MT_NIGHTSBUMPER
+
+		// clockwise
+		if (vertangle >= 75 && vertangle < 105) // up
+			newflags = 3;
+		else if (vertangle >= 105 && vertangle < 135) // 60 upward tilt
+			newflags = 2;
+		else if (vertangle >= 135 && vertangle < 165) // 30 upward tilt
+			newflags = 1;
+		//else if (vertangle >= 165 && vertangle < 195) // forward, see else case
+		//	newflags = 0;
+		else if (vertangle >= 195 && vertangle < 225) // 30 downward tilt
+			newflags = 11;
+		else if (vertangle >= 225 && vertangle < 255) // 60 downward tilt
+			newflags = 10;
+		else if (vertangle >= 255 && vertangle < 285) // down
+			newflags = 9;
+		else if (vertangle >= 285 && vertangle < 315) // 60 downward tilt backwards
+			newflags = 8;
+		else if (vertangle >= 315 && vertangle < 345) // 30 downward tilt backwards
+			newflags = 7;
+		else if (vertangle >= 345 || vertangle < 15) // backwards
+			newflags = 6;
+		else if (vertangle >= 15 && vertangle < 45) // 30 upward tilt backwards
+			newflags = 5;
+		else if (vertangle >= 45 && vertangle < 75) // 60 upward tilt backwards
+			newflags = 4;
+		else // forward
+			newflags = 0;
+
+		mt->options = (newz << ZSHIFT) | newflags;
+
+		// if NiGHTS is facing backwards, orient the Thing angle forwards so that the sprite angle
+		// displays correctly. Backwards movement via the Thing flags is unaffected.
+		if (vertangle < 90 || vertangle > 270)
+			mt->angle = (mt->angle + 180) % 360;
+
 		P_SpawnMapThing(mt);
 	}
 
-	// This places a ring!
+	// This places a sphere!
 	if (cmd->buttons & BT_WEAPONNEXT)
 	{
 		player->pflags |= PF_ATTACKDOWN;
 		if (!OP_HeightOkay(player, false))
 			return;
 
-		mt = OP_CreateNewMapThing(player, (UINT16)mobjinfo[MT_RING].doomednum, false);
-		P_SpawnHoopsAndRings(mt);
+		mt = OP_CreateNewMapThing(player, (UINT16)mobjinfo[MT_BLUESPHERE].doomednum, false);
+		P_SpawnHoopsAndRings(mt, false);
 	}
 
-	// This places a wing item!
+	// This places a ring!
 	if (cmd->buttons & BT_WEAPONPREV)
 	{
 		player->pflags |= PF_ATTACKDOWN;
 		if (!OP_HeightOkay(player, false))
 			return;
 
-		mt = OP_CreateNewMapThing(player, (UINT16)mobjinfo[MT_NIGHTSWING].doomednum, false);
-		P_SpawnHoopsAndRings(mt);
+		mt = OP_CreateNewMapThing(player, (UINT16)mobjinfo[MT_RING].doomednum, false);
+		P_SpawnHoopsAndRings(mt, false);
 	}
 
 	// This places a custom object as defined in the console cv_mapthingnum.
@@ -1077,12 +1282,12 @@ void OP_NightsObjectplace(player_t *player)
 
 		if (mt->type == 300 // Ring
 		|| mt->type == 308 || mt->type == 309 // Team Rings
-		|| mt->type == 1706 // Nights Wing
+		|| mt->type == 1706 // Sphere
 		|| (mt->type >= 600 && mt->type <= 609) // Placement patterns
 		|| mt->type == 1705 || mt->type == 1713 // NiGHTS Hoops
 		|| mt->type == 1800) // Mario Coin
 		{
-			P_SpawnHoopsAndRings(mt);
+			P_SpawnHoopsAndRings(mt, false);
 		}
 		else
 			P_SpawnMapThing(mt);
@@ -1227,7 +1432,7 @@ void OP_ObjectplaceMovement(player_t *player)
 		|| mt->type == 1705 || mt->type == 1713 // NiGHTS Hoops
 		|| mt->type == 1800) // Mario Coin
 		{
-			P_SpawnHoopsAndRings(mt);
+			P_SpawnHoopsAndRings(mt, false);
 		}
 		else
 			P_SpawnMapThing(mt);
diff --git a/src/m_cheat.h b/src/m_cheat.h
index 951c7a16a261f435fc464f37dd93585c0c156b2e..d50ddc1196fe335a68b72894d0522cfde68e4267 100644
--- a/src/m_cheat.h
+++ b/src/m_cheat.h
@@ -28,7 +28,7 @@ void cht_Init(void);
 void Command_ObjectPlace_f(void);
 void Command_Writethings_f(void);
 
-extern consvar_t cv_opflags, cv_mapthingnum, cv_speed;
+extern consvar_t cv_opflags, cv_ophoopflags, cv_mapthingnum, cv_speed;
 //extern consvar_t cv_snapto, cv_grid;
 
 extern boolean objectplacing;
diff --git a/src/m_menu.c b/src/m_menu.c
index a866dac1b95a8ac5fff4b45ba9d324fc5ea02cac..5e2cc48d51fe8ac688639284f2e14c95463d6ee6 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -458,7 +458,7 @@ consvar_t cv_ghost_guest     = {"ghost_guest",     "Show", CV_SAVE, ghost2_cons_
 static CV_PossibleValue_t dummyteam_cons_t[] = {{0, "Spectator"}, {1, "Red"}, {2, "Blue"}, {0, NULL}};
 static CV_PossibleValue_t dummyscramble_cons_t[] = {{0, "Random"}, {1, "Points"}, {0, NULL}};
 static CV_PossibleValue_t ringlimit_cons_t[] = {{0, "MIN"}, {9999, "MAX"}, {0, NULL}};
-static CV_PossibleValue_t liveslimit_cons_t[] = {{0, "MIN"}, {99, "MAX"}, {0, NULL}};
+static CV_PossibleValue_t liveslimit_cons_t[] = {{-1, "MIN"}, {99, "MAX"}, {0, NULL}};
 static CV_PossibleValue_t dummymares_cons_t[] = {
 	{-1, "END"}, {0,"Overall"}, {1,"Mare 1"}, {2,"Mare 2"}, {3,"Mare 3"}, {4,"Mare 4"}, {5,"Mare 5"}, {6,"Mare 6"}, {7,"Mare 7"}, {8,"Mare 8"}, {0,NULL}
 };
@@ -4938,6 +4938,7 @@ static void M_DrawAddons(void)
 {
 	INT32 x, y;
 	ssize_t i, max;
+	const char* topstr;
 
 	// hack - need to refresh at end of frame to handle addfile...
 	if (refreshdirmenu & M_AddonsRefresh())
@@ -4949,9 +4950,16 @@ static void M_DrawAddons(void)
 	if (addonsresponselimit)
 		addonsresponselimit--;
 
-	V_DrawCenteredString(BASEVIDWIDTH/2, 4+offs, 0, (Playing()
-	? "\x85""Adding files mid-game may cause problems."
-	: LOCATIONSTRING));
+	if (Playing())
+		topstr = "\x85""Adding files mid-game may cause problems.";
+	else if (savemoddata)
+		topstr = "\x83""Add-on has its own data, saving enabled.";
+	else if (modifiedgame)
+		topstr = "\x87""Game is modified, saving is disabled.";
+	else
+		topstr = LOCATIONSTRING;
+
+	V_DrawCenteredString(BASEVIDWIDTH/2, 4+offs, 0, topstr);
 
 	if (numwadfiles <= mainwads+1)
 		y = 0;
@@ -5222,7 +5230,7 @@ static void M_HandleAddons(INT32 choice)
 						case EXT_SOC:
 						case EXT_WAD:
 						case EXT_PK3:
-							COM_BufAddText(va("addfile %s%s", menupath, dirmenu[dir_on[menudepthleft]]+DIR_STRING));
+							COM_BufAddText(va("addfile \"%s%s\"", menupath, dirmenu[dir_on[menudepthleft]]+DIR_STRING));
 							addonsresponselimit = 5;
 							break;
 						default:
@@ -5265,8 +5273,14 @@ static void M_HandleAddons(INT32 choice)
 static void M_PandorasBox(INT32 choice)
 {
 	(void)choice;
-	CV_StealthSetValue(&cv_dummyrings, max(players[consoleplayer].rings, 0));
-	CV_StealthSetValue(&cv_dummylives, players[consoleplayer].lives);
+	if (maptol & TOL_NIGHTS)
+		CV_StealthSetValue(&cv_dummyrings, max(players[consoleplayer].spheres, 0));
+	else
+		CV_StealthSetValue(&cv_dummyrings, max(players[consoleplayer].rings, 0));
+	if (players[consoleplayer].lives == INFLIVES)
+		CV_StealthSetValue(&cv_dummylives, -1);
+	else
+		CV_StealthSetValue(&cv_dummylives, players[consoleplayer].lives);
 	CV_StealthSetValue(&cv_dummycontinues, players[consoleplayer].continues);
 	SR_PandorasBox[6].status = ((players[consoleplayer].charflags & SF_SUPER)
 #ifndef DEVELOP
@@ -5280,7 +5294,12 @@ static void M_PandorasBox(INT32 choice)
 static boolean M_ExitPandorasBox(void)
 {
 	if (cv_dummyrings.value != max(players[consoleplayer].rings, 0))
-		COM_ImmedExecute(va("setrings %d", cv_dummyrings.value));
+	{
+		if (maptol & TOL_NIGHTS)
+			COM_ImmedExecute(va("setspheres %d", cv_dummyrings.value));
+		else
+			COM_ImmedExecute(va("setrings %d", cv_dummyrings.value));
+	}
 	if (cv_dummylives.value != players[consoleplayer].lives)
 		COM_ImmedExecute(va("setlives %d", cv_dummylives.value));
 	if (cv_dummycontinues.value != players[consoleplayer].continues)
@@ -6315,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;
 
@@ -6342,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));
@@ -8829,7 +8848,7 @@ static void M_HandleSetupMultiPlayer(INT32 choice)
 				break;
 			S_StartSound(NULL,sfx_menu1); // Tails
 			l = strlen(setupm_name);
-			if (l < MAXPLAYERNAME-1)
+			if (l < MAXPLAYERNAME)
 			{
 				setupm_name[l] = (char)choice;
 				setupm_name[l+1] = 0;
@@ -9331,7 +9350,7 @@ static void M_SoundMenu(INT32 choice)
 {
 	(void)choice;
 
-	OP_SoundOptionsMenu[6].status = ((nosound || sound_disabled) ? IT_GRAYEDOUT : (IT_STRING | IT_CVAR));
+	OP_SoundOptionsMenu[6].status = (sound_disabled ? IT_GRAYEDOUT : (IT_STRING | IT_CVAR));
 	M_SetupNextMenu(&OP_SoundOptionsDef);
 }
 
@@ -9344,25 +9363,25 @@ void M_DrawSoundMenu(void)
 
 	V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x,
 		currentMenu->y+currentMenu->menuitems[0].alphaKey,
-		(nosound ? V_REDMAP : V_YELLOWMAP),
-		((nosound || sound_disabled) ? offstring : onstring));
+		(sound_disabled ? V_REDMAP : V_YELLOWMAP),
+		(sound_disabled ? offstring : onstring));
 
 	V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x,
 		currentMenu->y+currentMenu->menuitems[2].alphaKey,
-		(nodigimusic ? V_REDMAP : V_YELLOWMAP),
-		((nodigimusic || digital_disabled) ? offstring : onstring));
+		(digital_disabled ? V_REDMAP : V_YELLOWMAP),
+		(digital_disabled ? offstring : onstring));
 
 	V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x,
 		currentMenu->y+currentMenu->menuitems[4].alphaKey,
-		(nomidimusic ? V_REDMAP : V_YELLOWMAP),
-		((nomidimusic || music_disabled) ? offstring : onstring));
+		(midi_disabled ? V_REDMAP : V_YELLOWMAP),
+		(midi_disabled ? offstring : onstring));
 
 	if (itemOn == 0)
-		lengthstring = ((nosound || sound_disabled) ? 3 : 2);
+		lengthstring = (sound_disabled ? 3 : 2);
 	else if (itemOn == 2)
-		lengthstring = ((nodigimusic || digital_disabled) ? 3 : 2);
+		lengthstring = (digital_disabled ? 3 : 2);
 	else if (itemOn == 4)
-		lengthstring = ((nomidimusic || music_disabled) ? 3 : 2);
+		lengthstring = (midi_disabled ? 3 : 2);
 	else
 		return;
 
@@ -9397,32 +9416,20 @@ static void M_ToggleSFX(INT32 choice)
 			break;
 	}
 
-	if (nosound)
+	if (sound_disabled)
 	{
-		nosound = false;
-		I_StartupSound();
-		if (nosound) return;
-		S_Init(cv_soundvolume.value, cv_digmusicvolume.value, cv_midimusicvolume.value);
+		sound_disabled = false;
+		S_InitSfxChannels(cv_soundvolume.value);
 		S_StartSound(NULL, sfx_strpst);
 		OP_SoundOptionsMenu[6].status = IT_STRING | IT_CVAR;
 		//M_StartMessage(M_GetText("SFX Enabled\n"), NULL, MM_NOTHING);
 	}
 	else
 	{
-		if (sound_disabled)
-		{
-			sound_disabled = false;
-			S_StartSound(NULL, sfx_strpst);
-			OP_SoundOptionsMenu[6].status = IT_STRING | IT_CVAR;
-			//M_StartMessage(M_GetText("SFX Enabled\n"), NULL, MM_NOTHING);
-		}
-		else
-		{
-			sound_disabled = true;
-			S_StopSounds();
-			OP_SoundOptionsMenu[6].status = IT_GRAYEDOUT;
-			//M_StartMessage(M_GetText("SFX Disabled\n"), NULL, MM_NOTHING);
-		}
+		sound_disabled = true;
+		S_StopSounds();
+		OP_SoundOptionsMenu[6].status = IT_GRAYEDOUT;
+		//M_StartMessage(M_GetText("SFX Disabled\n"), NULL, MM_NOTHING);
 	}
 }
 
@@ -9450,12 +9457,10 @@ static void M_ToggleDigital(INT32 choice)
 			break;
 	}
 
-	if (nodigimusic)
+	if (digital_disabled)
 	{
-		nodigimusic = false;
-		I_InitDigMusic();
-		if (nodigimusic) return;
-		S_Init(cv_soundvolume.value, cv_digmusicvolume.value, cv_midimusicvolume.value);
+		digital_disabled = false;
+		I_InitMusic();
 		S_StopMusic();
 		if (Playing())
 			P_RestoreMusic(&players[consoleplayer]);
@@ -9465,21 +9470,27 @@ static void M_ToggleDigital(INT32 choice)
 	}
 	else
 	{
-		if (digital_disabled)
+		digital_disabled = true;
+		if (S_MusicType() != MU_MID)
 		{
-			digital_disabled = false;
-			if (Playing())
-				P_RestoreMusic(&players[consoleplayer]);
+			if (midi_disabled)
+				S_StopMusic();
 			else
-				S_ChangeMusicInternal("_clear", false);
-			//M_StartMessage(M_GetText("Digital Music Enabled\n"), NULL, MM_NOTHING);
-		}
-		else
-		{
-			digital_disabled = true;
-			S_StopMusic();
-			//M_StartMessage(M_GetText("Digital Music Disabled\n"), NULL, MM_NOTHING);
+			{
+				char mmusic[7];
+				UINT16 mflags;
+				boolean looping;
+
+				if (S_MusicInfo(mmusic, &mflags, &looping) && S_MIDIExists(mmusic))
+				{
+					S_StopMusic();
+					S_ChangeMusic(mmusic, mflags, looping);
+				}
+				else
+					S_StopMusic();
+			}
 		}
+		//M_StartMessage(M_GetText("Digital Music Disabled\n"), NULL, MM_NOTHING);
 	}
 }
 
@@ -9497,6 +9508,12 @@ static void M_ToggleMIDI(INT32 choice)
 			itemOn--;
 			return;
 
+		case KEY_LEFTARROW:
+		case KEY_RIGHTARROW:
+			if (S_MusicType() != MU_MID && S_MusicType() != MU_NONE)
+				S_StartSound(NULL, sfx_menu1);
+			break;
+
 		case KEY_ESCAPE:
 			if (currentMenu->prevMenu)
 				M_SetupNextMenu(currentMenu->prevMenu);
@@ -9506,13 +9523,10 @@ static void M_ToggleMIDI(INT32 choice)
 		default:
 			break;
 	}
-
-	if (nomidimusic)
+	if (midi_disabled)
 	{
-		nomidimusic = false;
-		I_InitMIDIMusic();
-		if (nomidimusic) return;
-		S_Init(cv_soundvolume.value, cv_digmusicvolume.value, cv_midimusicvolume.value);
+		midi_disabled = false;
+		I_InitMusic();
 		if (Playing())
 			P_RestoreMusic(&players[consoleplayer]);
 		else
@@ -9521,21 +9535,27 @@ static void M_ToggleMIDI(INT32 choice)
 	}
 	else
 	{
-		if (music_disabled)
+		midi_disabled = true;
+		if (S_MusicType() == MU_MID)
 		{
-			music_disabled = false;
-			if (Playing())
-				P_RestoreMusic(&players[consoleplayer]);
+			if (digital_disabled)
+				S_StopMusic();
 			else
-				S_ChangeMusicInternal("_clear", false);
-			//M_StartMessage(M_GetText("MIDI Music Enabled\n"), NULL, MM_NOTHING);
-		}
-		else
-		{
-			music_disabled = true;
-			S_StopMusic();
-			//M_StartMessage(M_GetText("MIDI Music Disabled\n"), NULL, MM_NOTHING);
+			{
+				char mmusic[7];
+				UINT16 mflags;
+				boolean looping;
+
+				if (S_MusicInfo(mmusic, &mflags, &looping) && S_DigExists(mmusic))
+				{
+					S_StopMusic();
+					S_ChangeMusic(mmusic, mflags, looping);
+				}
+				else
+					S_StopMusic();
+			}
 		}
+		//M_StartMessage(M_GetText("MIDI Music Disabled\n"), NULL, MM_NOTHING);
 	}
 }
 
diff --git a/src/m_misc.c b/src/m_misc.c
index bf637f7c3fcb65ea8b1879872d2f1bdd6724d893..50b6d7a05cb93434d5b0f679c35ce679889b461b 100644
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -56,7 +56,9 @@ typedef off_t off64_t;
 #endif
 #endif
 
-#if defined (_WIN32)
+#if defined(__MINGW32__) && ((__GNUC__ > 7) || (__GNUC__ == 6 && __GNUC_MINOR__ >= 3))
+#define PRIdS "u"
+#elif defined (_WIN32) 
 #define PRIdS "Iu"
 #elif defined (DJGPP)
 #define PRIdS "u"
diff --git a/src/p_enemy.c b/src/p_enemy.c
index d142e2886f32cde777d17c8d1b0433ba55920b97..8088c20a8a66f1ad2f3b1611c6d8ed7ebd355d6e 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -1,10622 +1,11815 @@
-// SONIC ROBO BLAST 2
-//-----------------------------------------------------------------------------
-// Copyright (C) 1993-1996 by id Software, Inc.
-// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
-//
-// This program is free software distributed under the
-// terms of the GNU General Public License, version 2.
-// See the 'LICENSE' file for more details.
-//-----------------------------------------------------------------------------
-/// \file  p_enemy.c
-/// \brief Enemy thinking, AI
-///        Action Pointer Functions that are associated with states/frames
-
-#include "doomdef.h"
-#include "g_game.h"
-#include "p_local.h"
-#include "r_main.h"
-#include "r_state.h"
-#include "s_sound.h"
-#include "m_random.h"
-#include "m_misc.h"
-#include "r_things.h"
-#include "i_video.h"
-#include "lua_hook.h"
-
-#ifdef HW3SOUND
-#include "hardware/hw3sound.h"
-#endif
-
-#ifdef HAVE_BLUA
-boolean LUA_CallAction(const char *action, mobj_t *actor);
-#endif
-
-player_t *stplyr;
-INT32 var1;
-INT32 var2;
-
-//
-// P_NewChaseDir related LUT.
-//
-static dirtype_t opposite[] =
-{
-	DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST,
-	DI_EAST, DI_NORTHEAST, DI_NORTH, DI_NORTHWEST, DI_NODIR
-};
-
-static dirtype_t diags[] =
-{
-	DI_NORTHWEST, DI_NORTHEAST, DI_SOUTHWEST, DI_SOUTHEAST
-};
-
-//Real Prototypes to A_*
-void A_Fall(mobj_t *actor);
-void A_Look(mobj_t *actor);
-void A_Chase(mobj_t *actor);
-void A_FaceStabChase(mobj_t *actor);
-void A_JetJawRoam(mobj_t *actor);
-void A_JetJawChomp(mobj_t *actor);
-void A_PointyThink(mobj_t *actor);
-void A_CheckBuddy(mobj_t *actor);
-void A_HoodThink(mobj_t *actor);
-void A_ArrowCheck(mobj_t *actor);
-void A_SnailerThink(mobj_t *actor);
-void A_SharpChase(mobj_t *actor);
-void A_SharpSpin(mobj_t *actor);
-void A_VultureVtol(mobj_t *actor);
-void A_VultureCheck(mobj_t *actor);
-void A_SkimChase(mobj_t *actor);
-void A_FaceTarget(mobj_t *actor);
-void A_FaceTracer(mobj_t *actor);
-void A_LobShot(mobj_t *actor);
-void A_FireShot(mobj_t *actor);
-void A_SuperFireShot(mobj_t *actor);
-void A_BossFireShot(mobj_t *actor);
-void A_Boss7FireMissiles(mobj_t *actor);
-void A_Boss1Laser(mobj_t *actor);
-void A_FocusTarget(mobj_t *actor);
-void A_Boss4Reverse(mobj_t *actor);
-void A_Boss4SpeedUp(mobj_t *actor);
-void A_Boss4Raise(mobj_t *actor);
-void A_SkullAttack(mobj_t *actor);
-void A_BossZoom(mobj_t *actor);
-void A_BossScream(mobj_t *actor);
-void A_Scream(mobj_t *actor);
-void A_Pain(mobj_t *actor);
-void A_1upThinker(mobj_t *actor);
-void A_MonitorPop(mobj_t *actor);
-void A_GoldMonitorPop(mobj_t *actor);
-void A_GoldMonitorRestore(mobj_t *actor);
-void A_GoldMonitorSparkle(mobj_t *actor);
-void A_Explode(mobj_t *actor);
-void A_BossDeath(mobj_t *actor);
-void A_CustomPower(mobj_t *actor);
-void A_GiveWeapon(mobj_t *actor);
-void A_RingBox(mobj_t *actor);
-void A_Invincibility(mobj_t *actor);
-void A_SuperSneakers(mobj_t *actor);
-void A_AwardScore(mobj_t *actor);
-void A_ExtraLife(mobj_t *actor);
-void A_GiveShield(mobj_t *actor);
-void A_GravityBox(mobj_t *actor);
-void A_ScoreRise(mobj_t *actor);
-void A_ParticleSpawn(mobj_t *actor);
-void A_BunnyHop(mobj_t *actor);
-void A_BubbleSpawn(mobj_t *actor);
-void A_FanBubbleSpawn(mobj_t *actor);
-void A_BubbleRise(mobj_t *actor);
-void A_BubbleCheck(mobj_t *actor);
-void A_AttractChase(mobj_t *actor);
-void A_DropMine(mobj_t *actor);
-void A_FishJump(mobj_t *actor);
-void A_ThrownRing(mobj_t *actor);
-void A_SetSolidSteam(mobj_t *actor);
-void A_UnsetSolidSteam(mobj_t *actor);
-void A_SignPlayer(mobj_t *actor);
-void A_OverlayThink(mobj_t *actor);
-void A_JetChase(mobj_t *actor);
-void A_JetbThink(mobj_t *actor);
-void A_JetgShoot(mobj_t *actor);
-void A_JetgThink(mobj_t *actor);
-void A_ShootBullet(mobj_t *actor);
-void A_MinusDigging(mobj_t *actor);
-void A_MinusPopup(mobj_t *actor);
-void A_MinusCheck(mobj_t *actor);
-void A_ChickenCheck(mobj_t *actor);
-void A_MouseThink(mobj_t *actor);
-void A_DetonChase(mobj_t *actor);
-void A_CapeChase(mobj_t *actor);
-void A_RotateSpikeBall(mobj_t *actor);
-void A_SlingAppear(mobj_t *actor);
-void A_UnidusBall(mobj_t *actor);
-void A_RockSpawn(mobj_t *actor);
-void A_SetFuse(mobj_t *actor);
-void A_CrawlaCommanderThink(mobj_t *actor);
-void A_RingExplode(mobj_t *actor);
-void A_OldRingExplode(mobj_t *actor);
-void A_MixUp(mobj_t *actor);
-void A_RecyclePowers(mobj_t *actor);
-void A_Boss2TakeDamage(mobj_t *actor);
-void A_Boss7Chase(mobj_t *actor);
-void A_GoopSplat(mobj_t *actor);
-void A_Boss2PogoSFX(mobj_t *actor);
-void A_Boss2PogoTarget(mobj_t *actor);
-void A_EggmanBox(mobj_t *actor);
-void A_TurretFire(mobj_t *actor);
-void A_SuperTurretFire(mobj_t *actor);
-void A_TurretStop(mobj_t *actor);
-void A_SparkFollow(mobj_t *actor);
-void A_BuzzFly(mobj_t *actor);
-void A_GuardChase(mobj_t *actor);
-void A_EggShield(mobj_t *actor);
-void A_SetReactionTime(mobj_t *actor);
-void A_Boss1Spikeballs(mobj_t *actor);
-void A_Boss3TakeDamage(mobj_t *actor);
-void A_Boss3Path(mobj_t *actor);
-void A_LinedefExecute(mobj_t *actor);
-void A_PlaySeeSound(mobj_t *actor);
-void A_PlayAttackSound(mobj_t *actor);
-void A_PlayActiveSound(mobj_t *actor);
-void A_SmokeTrailer(mobj_t *actor);
-void A_SpawnObjectAbsolute(mobj_t *actor);
-void A_SpawnObjectRelative(mobj_t *actor);
-void A_ChangeAngleRelative(mobj_t *actor);
-void A_ChangeAngleAbsolute(mobj_t *actor);
-void A_PlaySound(mobj_t *actor);
-void A_FindTarget(mobj_t *actor);
-void A_FindTracer(mobj_t *actor);
-void A_SetTics(mobj_t *actor);
-void A_SetRandomTics(mobj_t *actor);
-void A_ChangeColorRelative(mobj_t *actor);
-void A_ChangeColorAbsolute(mobj_t *actor);
-void A_MoveRelative(mobj_t *actor);
-void A_MoveAbsolute(mobj_t *actor);
-void A_Thrust(mobj_t *actor);
-void A_ZThrust(mobj_t *actor);
-void A_SetTargetsTarget(mobj_t *actor);
-void A_SetObjectFlags(mobj_t *actor);
-void A_SetObjectFlags2(mobj_t *actor);
-void A_RandomState(mobj_t *actor);
-void A_RandomStateRange(mobj_t *actor);
-void A_DualAction(mobj_t *actor);
-void A_RemoteAction(mobj_t *actor);
-void A_ToggleFlameJet(mobj_t *actor);
-void A_OrbitNights(mobj_t *actor);
-void A_GhostMe(mobj_t *actor);
-void A_SetObjectState(mobj_t *actor);
-void A_SetObjectTypeState(mobj_t *actor);
-void A_KnockBack(mobj_t *actor);
-void A_PushAway(mobj_t *actor);
-void A_RingDrain(mobj_t *actor);
-void A_SplitShot(mobj_t *actor);
-void A_MissileSplit(mobj_t *actor);
-void A_MultiShot(mobj_t *actor);
-void A_InstaLoop(mobj_t *actor);
-void A_Custom3DRotate(mobj_t *actor);
-void A_SearchForPlayers(mobj_t *actor);
-void A_CheckRandom(mobj_t *actor);
-void A_CheckTargetRings(mobj_t *actor);
-void A_CheckRings(mobj_t *actor);
-void A_CheckTotalRings(mobj_t *actor);
-void A_CheckHealth(mobj_t *actor);
-void A_CheckRange(mobj_t *actor);
-void A_CheckHeight(mobj_t *actor);
-void A_CheckTrueRange(mobj_t *actor);
-void A_CheckThingCount(mobj_t *actor);
-void A_CheckAmbush(mobj_t *actor);
-void A_CheckCustomValue(mobj_t *actor);
-void A_CheckCusValMemo(mobj_t *actor);
-void A_SetCustomValue(mobj_t *actor);
-void A_UseCusValMemo(mobj_t *actor);
-void A_RelayCustomValue(mobj_t *actor);
-void A_CusValAction(mobj_t *actor);
-void A_ForceStop(mobj_t *actor);
-void A_ForceWin(mobj_t *actor);
-void A_SpikeRetract(mobj_t *actor);
-void A_InfoState(mobj_t *actor);
-void A_Repeat(mobj_t *actor);
-void A_SetScale(mobj_t *actor);
-void A_RemoteDamage(mobj_t *actor);
-void A_HomingChase(mobj_t *actor);
-void A_TrapShot(mobj_t *actor);
-//for p_enemy.c
-void A_Boss1Chase(mobj_t *actor);
-void A_Boss2Chase(mobj_t *actor);
-void A_Boss2Pogo(mobj_t *actor);
-void A_BossJetFume(mobj_t *actor);
-void A_VileTarget(mobj_t *actor);
-void A_VileAttack(mobj_t *actor);
-void A_VileFire(mobj_t *actor);
-void A_BrakChase(mobj_t *actor);
-void A_BrakFireShot(mobj_t *actor);
-void A_BrakLobShot(mobj_t *actor);
-void A_NapalmScatter(mobj_t *actor);
-void A_SpawnFreshCopy(mobj_t *actor);
-void A_FlickySpawn(mobj_t *actor);
-void A_FlickyAim(mobj_t *actor);
-void A_FlickyFly(mobj_t *actor);
-void A_FlickySoar(mobj_t *actor);
-void A_FlickyCoast(mobj_t *actor);
-void A_FlickyHop(mobj_t *actor);
-void A_FlickyFlounder(mobj_t *actor);
-void A_FlickyCheck(mobj_t *actor);
-void A_FlickyHeightCheck(mobj_t *actor);
-void A_FlickyFlutter(mobj_t *actor);
-void A_FlameParticle(mobj_t *actor);
-void A_FadeOverlay(mobj_t *actor);
-void A_Boss5Jump(mobj_t *actor);
-
-//
-// ENEMY THINKING
-// Enemies are always spawned with targetplayer = -1, threshold = 0
-// Most monsters are spawned unaware of all players, but some can be made preaware.
-//
-
-//
-// P_CheckMeleeRange
-//
-boolean P_CheckMeleeRange(mobj_t *actor)
-{
-	mobj_t *pl;
-	fixed_t dist;
-
-	if (!actor->target)
-		return false;
-
-	pl = actor->target;
-	dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y);
-
-	if (dist >= FixedMul(MELEERANGE - 20*FRACUNIT, actor->scale) + pl->radius)
-		return false;
-
-	// check height now, so that damn crawlas cant attack
-	// you if you stand on a higher ledge.
-	if ((pl->z > actor->z + actor->height) || (actor->z > pl->z + pl->height))
-		return false;
-
-	if (!P_CheckSight(actor, actor->target))
-		return false;
-
-	return true;
-}
-
-// P_CheckMeleeRange for Jettysyn Bomber.
-boolean P_JetbCheckMeleeRange(mobj_t *actor)
-{
-	mobj_t *pl;
-	fixed_t dist;
-
-	if (!actor->target)
-		return false;
-
-	pl = actor->target;
-	dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y);
-
-	if (dist >= (actor->radius + pl->radius)*2)
-		return false;
-
-	if (actor->eflags & MFE_VERTICALFLIP)
-	{
-		if (pl->z < actor->z + actor->height + FixedMul(40<<FRACBITS, actor->scale))
-			return false;
-	}
-	else
-	{
-		if (pl->z + pl->height > actor->z - FixedMul(40<<FRACBITS, actor->scale))
-			return false;
-	}
-
-	return true;
-}
-
-// P_CheckMeleeRange for CastleBot FaceStabber.
-boolean P_FaceStabCheckMeleeRange(mobj_t *actor)
-{
-	mobj_t *pl;
-	fixed_t dist;
-
-	if (!actor->target)
-		return false;
-
-	pl = actor->target;
-	dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y);
-
-	if (dist >= (actor->radius + pl->radius)*4)
-		return false;
-
-	if ((pl->z > actor->z + actor->height) || (actor->z > pl->z + pl->height))
-		return false;
-
-	if (!P_CheckSight(actor, actor->target))
-		return false;
-
-	return true;
-}
-
-// P_CheckMeleeRange for Skim.
-boolean P_SkimCheckMeleeRange(mobj_t *actor)
-{
-	mobj_t *pl;
-	fixed_t dist;
-
-	if (!actor->target)
-		return false;
-
-	pl = actor->target;
-	dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y);
-
-	if (dist >= FixedMul(MELEERANGE - 20*FRACUNIT, actor->scale) + pl->radius)
-		return false;
-
-	if (actor->eflags & MFE_VERTICALFLIP)
-	{
-		if (pl->z < actor->z + actor->height + FixedMul(24<<FRACBITS, actor->scale))
-			return false;
-	}
-	else
-	{
-		if (pl->z + pl->height > actor->z - FixedMul(24<<FRACBITS, actor->scale))
-			return false;
-	}
-
-	return true;
-}
-
-//
-// P_CheckMissileRange
-//
-boolean P_CheckMissileRange(mobj_t *actor)
-{
-	fixed_t dist;
-
-	if (!actor->target)
-		return false;
-
-	if (actor->reactiontime)
-		return false; // do not attack yet
-
-	if (!P_CheckSight(actor, actor->target))
-		return false;
-
-	// OPTIMIZE: get this from a global checksight
-	dist = P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) - FixedMul(64*FRACUNIT, actor->scale);
-
-	if (!actor->info->meleestate)
-		dist -= FixedMul(128*FRACUNIT, actor->scale); // no melee attack, so fire more
-
-	dist >>= FRACBITS;
-
-	if (actor->type == MT_EGGMOBILE)
-		dist >>= 1;
-
-	if (dist > 200)
-		dist = 200;
-
-	if (actor->type == MT_EGGMOBILE && dist > 160)
-		dist = 160;
-
-	if (P_RandomByte() < dist)
-		return false;
-
-	return true;
-}
-
-/** Checks for water in a sector.
-  * Used by Skim movements.
-  *
-  * \param x X coordinate on the map.
-  * \param y Y coordinate on the map.
-  * \return True if there's water at this location, false if not.
-  * \sa ::MT_SKIM
-  */
-static boolean P_WaterInSector(mobj_t *mobj, fixed_t x, fixed_t y)
-{
-	sector_t *sector;
-
-	sector = R_PointInSubsector(x, y)->sector;
-
-	if (sector->ffloors)
-	{
-		ffloor_t *rover;
-
-		for (rover = sector->ffloors; rover; rover = rover->next)
-		{
-			if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_SWIMMABLE))
-				continue;
-
-			if (*rover->topheight >= mobj->floorz && *rover->topheight <= mobj->z)
-				return true; // we found water!!
-		}
-	}
-
-	return false;
-}
-
-static const fixed_t xspeed[NUMDIRS] = {FRACUNIT, 46341>>(16-FRACBITS), 0, -(46341>>(16-FRACBITS)), -FRACUNIT, -(46341>>(16-FRACBITS)), 0, 46341>>(16-FRACBITS)};
-static const fixed_t yspeed[NUMDIRS] = {0, 46341>>(16-FRACBITS), FRACUNIT, 46341>>(16-FRACBITS), 0, -(46341>>(16-FRACBITS)), -FRACUNIT, -(46341>>(16-FRACBITS))};
-
-/** Moves an actor in its current direction.
-  *
-  * \param actor Actor object to move.
-  * \return False if the move is blocked, otherwise true.
-  */
-boolean P_Move(mobj_t *actor, fixed_t speed)
-{
-	fixed_t tryx, tryy;
-	dirtype_t movedir = actor->movedir;
-
-	if (movedir == DI_NODIR || !actor->health)
-		return false;
-
-	I_Assert(movedir < NUMDIRS);
-
-	tryx = actor->x + FixedMul(speed*xspeed[movedir], actor->scale);
-	if (twodlevel || actor->flags2 & MF2_TWOD)
-		tryy = actor->y;
-	else
-		tryy = actor->y + FixedMul(speed*yspeed[movedir], actor->scale);
-
-	if (actor->type == MT_SKIM && !P_WaterInSector(actor, tryx, tryy)) // bail out if sector lacks water
-		return false;
-
-	if (!P_TryMove(actor, tryx, tryy, false))
-	{
-		if (actor->flags & MF_FLOAT && floatok)
-		{
-			// must adjust height
-			if (actor->z < tmfloorz)
-				actor->z += FixedMul(FLOATSPEED, actor->scale);
-			else
-				actor->z -= FixedMul(FLOATSPEED, actor->scale);
-
-			if (actor->type == MT_JETJAW && actor->z + actor->height > actor->watertop)
-				actor->z = actor->watertop - actor->height;
-
-			actor->flags2 |= MF2_INFLOAT;
-			return true;
-		}
-
-		return false;
-	}
-	else
-		actor->flags2 &= ~MF2_INFLOAT;
-
-	return true;
-}
-
-/** Attempts to move an actor on in its current direction.
-  * If the move succeeds, the actor's move count is reset
-  * randomly to a value from 0 to 15.
-  *
-  * \param actor Actor to move.
-  * \return True if the move succeeds, false if the move is blocked.
-  */
-static boolean P_TryWalk(mobj_t *actor)
-{
-	if (!P_Move(actor, actor->info->speed))
-		return false;
-	actor->movecount = P_RandomByte() & 15;
-	return true;
-}
-
-void P_NewChaseDir(mobj_t *actor)
-{
-	fixed_t deltax, deltay;
-	dirtype_t d[3];
-	dirtype_t tdir = DI_NODIR, olddir, turnaround;
-
-	I_Assert(actor->target != NULL);
-	I_Assert(!P_MobjWasRemoved(actor->target));
-
-	olddir = actor->movedir;
-
-	if (olddir >= NUMDIRS)
-		olddir = DI_NODIR;
-
-	if (olddir != DI_NODIR)
-		turnaround = opposite[olddir];
-	else
-		turnaround = olddir;
-
-	deltax = actor->target->x - actor->x;
-	deltay = actor->target->y - actor->y;
-
-	if (deltax > FixedMul(10*FRACUNIT, actor->scale))
-		d[1] = DI_EAST;
-	else if (deltax < -FixedMul(10*FRACUNIT, actor->scale))
-		d[1] = DI_WEST;
-	else
-		d[1] = DI_NODIR;
-
-	if (twodlevel || actor->flags2 & MF2_TWOD)
-		d[2] = DI_NODIR;
-	if (deltay < -FixedMul(10*FRACUNIT, actor->scale))
-		d[2] = DI_SOUTH;
-	else if (deltay > FixedMul(10*FRACUNIT, actor->scale))
-		d[2] = DI_NORTH;
-	else
-		d[2] = DI_NODIR;
-
-	// try direct route
-	if (d[1] != DI_NODIR && d[2] != DI_NODIR)
-	{
-		dirtype_t newdir = diags[((deltay < 0)<<1) + (deltax > 0)];
-
-		actor->movedir = newdir;
-		if ((newdir != turnaround) && P_TryWalk(actor))
-			return;
-	}
-
-	// try other directions
-	if (P_RandomChance(25*FRACUNIT/32) || abs(deltay) > abs(deltax))
-	{
-		tdir = d[1];
-		d[1] = d[2];
-		d[2] = tdir;
-	}
-
-	if (d[1] == turnaround)
-		d[1] = DI_NODIR;
-	if (d[2] == turnaround)
-		d[2] = DI_NODIR;
-
-	if (d[1] != DI_NODIR)
-	{
-		actor->movedir = d[1];
-
-		if (P_TryWalk(actor))
-			return; // either moved forward or attacked
-	}
-
-	if (d[2] != DI_NODIR)
-	{
-		actor->movedir = d[2];
-
-		if (P_TryWalk(actor))
-			return;
-	}
-
-	// there is no direct path to the player, so pick another direction.
-	if (olddir != DI_NODIR)
-	{
-		actor->movedir =olddir;
-
-		if (P_TryWalk(actor))
-			return;
-	}
-
-	// randomly determine direction of search
-	if (P_RandomChance(FRACUNIT/2))
-	{
-		for (tdir = DI_EAST; tdir <= DI_SOUTHEAST; tdir++)
-		{
-			if (tdir != turnaround)
-			{
-				actor->movedir = tdir;
-
-				if (P_TryWalk(actor))
-					return;
-			}
-		}
-	}
-	else
-	{
-		for (tdir = DI_SOUTHEAST; tdir >= DI_EAST; tdir--)
-		{
-			if (tdir != turnaround)
-			{
-				actor->movedir = tdir;
-
-				if (P_TryWalk(actor))
-					return;
-			}
-		}
-	}
-
-	if (turnaround != DI_NODIR)
-	{
-		actor->movedir = turnaround;
-
-		if (P_TryWalk(actor))
-			return;
-	}
-
-	actor->movedir = (angle_t)DI_NODIR; // cannot move
-}
-
-/** Looks for players to chase after, aim at, or whatever.
-  *
-  * \param actor     The object looking for flesh.
-  * \param allaround Look all around? If false, only players in a 180-degree
-  *                  range in front will be spotted.
-  * \param dist      If > 0, checks distance
-  * \return True if a player is found, otherwise false.
-  * \sa P_SupermanLook4Players
-  */
-boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed_t dist)
-{
-	INT32 c = 0, stop;
-	player_t *player;
-	angle_t an;
-
-	// BP: first time init, this allow minimum lastlook changes
-	if (actor->lastlook < 0)
-		actor->lastlook = P_RandomByte();
-
-	actor->lastlook %= MAXPLAYERS;
-
-	stop = (actor->lastlook - 1) & PLAYERSMASK;
-
-	for (; ; actor->lastlook = (actor->lastlook + 1) & PLAYERSMASK)
-	{
-		// done looking
-		if (actor->lastlook == stop)
-			return false;
-
-		if (!playeringame[actor->lastlook])
-			continue;
-
-		if (c++ == 2)
-			return false;
-
-		player = &players[actor->lastlook];
-
-		if ((netgame || multiplayer) && player->spectator)
-			continue;
-
-		if (player->pflags & PF_INVIS)
-			continue; // ignore notarget
-
-		if (!player->mo || P_MobjWasRemoved(player->mo))
-			continue;
-
-		if (player->mo->health <= 0)
-			continue; // dead
-
-		if (dist > 0
-			&& P_AproxDistance(P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y), player->mo->z - actor->z) > dist)
-			continue; // Too far away
-
-		if (!allaround)
-		{
-			an = R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y) - actor->angle;
-			if (an > ANGLE_90 && an < ANGLE_270)
-			{
-				dist = P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y);
-				// if real close, react anyway
-				if (dist > FixedMul(MELEERANGE, actor->scale))
-					continue; // behind back
-			}
-		}
-
-		if (!P_CheckSight(actor, player->mo))
-			continue; // out of sight
-
-		if (tracer)
-			P_SetTarget(&actor->tracer, player->mo);
-		else
-			P_SetTarget(&actor->target, player->mo);
-		return true;
-	}
-
-	//return false;
-}
-
-/** Looks for a player with a ring shield.
-  * Used by rings.
-  *
-  * \param actor Ring looking for a shield to be attracted to.
-  * \return True if a player with ring shield is found, otherwise false.
-  * \sa A_AttractChase
-  */
-static boolean P_LookForShield(mobj_t *actor)
-{
-	INT32 c = 0, stop;
-	player_t *player;
-
-	// BP: first time init, this allow minimum lastlook changes
-	if (actor->lastlook < 0)
-		actor->lastlook = P_RandomByte();
-
-	actor->lastlook %= MAXPLAYERS;
-
-	stop = (actor->lastlook - 1) & PLAYERSMASK;
-
-	for (; ; actor->lastlook = ((actor->lastlook + 1) & PLAYERSMASK))
-	{
-		// done looking
-		if (actor->lastlook == stop)
-			return false;
-
-		if (!playeringame[actor->lastlook])
-			continue;
-
-		if (c++ == 2)
-			return false;
-
-		player = &players[actor->lastlook];
-
-		if (!player->mo || player->mo->health <= 0)
-			continue; // dead
-
-		//When in CTF, don't pull rings that you cannot pick up.
-		if ((actor->type == MT_REDTEAMRING && player->ctfteam != 1) ||
-			(actor->type == MT_BLUETEAMRING && player->ctfteam != 2))
-			continue;
-
-		if ((player->powers[pw_shield] & SH_PROTECTELECTRIC)
-			&& (P_AproxDistance(P_AproxDistance(actor->x-player->mo->x, actor->y-player->mo->y), actor->z-player->mo->z) < FixedMul(RING_DIST, player->mo->scale)))
-		{
-			P_SetTarget(&actor->tracer, player->mo);
-			return true;
-		}
-	}
-
-	//return false;
-}
-
-#ifdef WEIGHTEDRECYCLER
-// Compares players to see who currently has the "best" items, etc.
-static int P_RecycleCompare(const void *p1, const void *p2)
-{
-	player_t *player1 = &players[*(const UINT8 *)p1];
-	player_t *player2 = &players[*(const UINT8 *)p2];
-
-	// Non-shooting gametypes
-	if (!G_PlatformGametype())
-	{
-		// Invincibility.
-		if (player1->powers[pw_invulnerability] > player2->powers[pw_invulnerability]) return -1;
-		else if (player2->powers[pw_invulnerability] > player1->powers[pw_invulnerability]) return 1;
-
-		// One has a shield, the other doesn't.
-		if (player1->powers[pw_shield] && !player2->powers[pw_shield]) return -1;
-		else if (player2->powers[pw_shield] && !player1->powers[pw_shield]) return 1;
-
-		// Sneakers.
-		if (player1->powers[pw_sneakers] > player2->powers[pw_sneakers]) return -1;
-		else if (player2->powers[pw_sneakers] > player1->powers[pw_sneakers]) return 1;
-	}
-	else // Match, Team Match, CTF, Tag, Etc.
-	{
-		UINT8 player1_em = M_CountBits((UINT32)player1->powers[pw_emeralds], 7);
-		UINT8 player2_em = M_CountBits((UINT32)player2->powers[pw_emeralds], 7);
-
-		UINT8 player1_rw = M_CountBits((UINT32)player1->ringweapons, NUM_WEAPONS-1);
-		UINT8 player2_rw = M_CountBits((UINT32)player2->ringweapons, NUM_WEAPONS-1);
-
-		UINT16 player1_am = player1->powers[pw_infinityring]          // max 800
-		                  + player1->powers[pw_automaticring]         // max 300
-		                  + (player1->powers[pw_bouncering]    * 3)   // max 100
-		                  + (player1->powers[pw_explosionring] * 6)   // max 50
-		                  + (player1->powers[pw_scatterring]   * 3)   // max 100
-		                  + (player1->powers[pw_grenadering]   * 6)   // max 50
-		                  + (player1->powers[pw_railring]      * 6);  // max 50
-		UINT16 player2_am = player2->powers[pw_infinityring]          // max 800
-		                  + player2->powers[pw_automaticring]         // max 300
-		                  + (player2->powers[pw_bouncering]    * 3)   // max 100
-		                  + (player2->powers[pw_explosionring] * 6)   // max 50
-		                  + (player2->powers[pw_scatterring]   * 3)   // max 100
-		                  + (player2->powers[pw_grenadering]   * 6)   // max 50
-		                  + (player2->powers[pw_railring]      * 6);  // max 50
-
-		// Super trumps everything.
-		if (player1->powers[pw_super] && !player2->powers[pw_super]) return -1;
-		else if (player2->powers[pw_super] && !player1->powers[pw_super]) return 1;
-
-		// Emerald count if neither player is Super.
-		if (player1_em > player2_em) return -1;
-		else if (player1_em < player2_em) return 1;
-
-		// One has a shield, the other doesn't.
-		// (the likelihood of a shielded player being worse off than one without one is low.)
-		if (player1->powers[pw_shield] && !player2->powers[pw_shield]) return -1;
-		else if (player2->powers[pw_shield] && !player1->powers[pw_shield]) return 1;
-
-		// Ring weapons count
-		if (player1_rw > player2_rw) return -1;
-		else if (player1_rw < player2_rw) return 1;
-
-		// Ring ammo if they have the same number of weapons
-		if (player1_am > player2_am) return -1;
-		else if (player1_am < player2_am) return 1;
-	}
-
-	// Identical for our purposes
-	return 0;
-}
-#endif
-
-// Handles random monitor weights via console.
-static mobjtype_t P_DoRandomBoxChances(void)
-{
-	mobjtype_t spawnchance[256];
-	INT32 numchoices = 0, i = 0;
-
-	if (!(netgame || multiplayer))
-	{
-		switch (P_RandomKey(10))
-		{
-			case 0:
-				return MT_RING_ICON;
-			case 1:
-				return MT_SNEAKERS_ICON;
-			case 2:
-				return MT_INVULN_ICON;
-			case 3:
-				return MT_WHIRLWIND_ICON;
-			case 4:
-				return MT_ELEMENTAL_ICON;
-			case 5:
-				return MT_ATTRACT_ICON;
-			case 6:
-				return MT_FORCE_ICON;
-			case 7:
-				return MT_ARMAGEDDON_ICON;
-			case 8:
-				return MT_1UP_ICON;
-			case 9:
-				return MT_EGGMAN_ICON;
-		}
-		return MT_NULL;
-	}
-
-#define QUESTIONBOXCHANCES(type, cvar) \
-for (i = cvar.value; i; --i) spawnchance[numchoices++] = type
-	QUESTIONBOXCHANCES(MT_RING_ICON,       cv_superring);
-	QUESTIONBOXCHANCES(MT_SNEAKERS_ICON,   cv_supersneakers);
-	QUESTIONBOXCHANCES(MT_INVULN_ICON,     cv_invincibility);
-	QUESTIONBOXCHANCES(MT_WHIRLWIND_ICON,  cv_jumpshield);
-	QUESTIONBOXCHANCES(MT_ELEMENTAL_ICON,  cv_watershield);
-	QUESTIONBOXCHANCES(MT_ATTRACT_ICON,    cv_ringshield);
-	QUESTIONBOXCHANCES(MT_FORCE_ICON,      cv_forceshield);
-	QUESTIONBOXCHANCES(MT_ARMAGEDDON_ICON, cv_bombshield);
-	QUESTIONBOXCHANCES(MT_1UP_ICON,        cv_1up);
-	QUESTIONBOXCHANCES(MT_EGGMAN_ICON,     cv_eggmanbox);
-	QUESTIONBOXCHANCES(MT_MIXUP_ICON,      cv_teleporters);
-	QUESTIONBOXCHANCES(MT_RECYCLER_ICON,   cv_recycler);
-#undef QUESTIONBOXCHANCES
-
-	if (numchoices == 0) return MT_NULL;
-	return spawnchance[P_RandomKey(numchoices)];
-}
-
-//
-// ACTION ROUTINES
-//
-
-// Function: A_Look
-//
-// Description: Look for a player and set your target to them.
-//
-// var1:
-//		lower 16 bits = look all around
-//		upper 16 bits = distance limit
-// var2 = If 1, only change to seestate. If 2, only play seesound. If 0, do both.
-//
-void A_Look(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_Look", actor))
-		return;
-#endif
-
-	if (!P_LookForPlayers(actor, locvar1 & 65535, false , FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale)))
-		return;
-
-	// go into chase state
-	if (!locvar2)
-	{
-		P_SetMobjState(actor, actor->info->seestate);
-		A_PlaySeeSound(actor);
-	}
-	else if (locvar2 == 1) // Only go into seestate
-		P_SetMobjState(actor, actor->info->seestate);
-	else if (locvar2 == 2) // Only play seesound
-		A_PlaySeeSound(actor);
-}
-
-// Function: A_Chase
-//
-// Description: Chase after your target.
-//
-// var1:
-//		1 = don't check meleestate
-//		2 = don't check missilestate
-//		3 = don't check meleestate and missilestate
-// var2 = unused
-//
-void A_Chase(mobj_t *actor)
-{
-	INT32 delta;
-	INT32 locvar1 = var1;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_Chase", actor))
-		return;
-#endif
-
-	I_Assert(actor != NULL);
-	I_Assert(!P_MobjWasRemoved(actor));
-
-	if (actor->reactiontime)
-		actor->reactiontime--;
-
-	// modify target threshold
-	if (actor->threshold)
-	{
-		if (!actor->target || actor->target->health <= 0)
-			actor->threshold = 0;
-		else
-			actor->threshold--;
-	}
-
-	// turn towards movement direction if not there yet
-	if (actor->movedir < NUMDIRS)
-	{
-		actor->angle &= (7<<29);
-		delta = actor->angle - (actor->movedir << 29);
-
-		if (delta > 0)
-			actor->angle -= ANGLE_45;
-		else if (delta < 0)
-			actor->angle += ANGLE_45;
-	}
-
-	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
-	{
-		// look for a new target
-		if (P_LookForPlayers(actor, true, false, 0))
-			return; // got a new target
-
-		P_SetMobjStateNF(actor, actor->info->spawnstate);
-		return;
-	}
-
-	// do not attack twice in a row
-	if (actor->flags2 & MF2_JUSTATTACKED)
-	{
-		actor->flags2 &= ~MF2_JUSTATTACKED;
-		P_NewChaseDir(actor);
-		return;
-	}
-
-	// check for melee attack
-	if (!(locvar1 & 1) && actor->info->meleestate && P_CheckMeleeRange(actor))
-	{
-		if (actor->info->attacksound)
-			S_StartAttackSound(actor, actor->info->attacksound);
-
-		P_SetMobjState(actor, actor->info->meleestate);
-		return;
-	}
-
-	// check for missile attack
-	if (!(locvar1 & 2) && actor->info->missilestate)
-	{
-		if (actor->movecount || !P_CheckMissileRange(actor))
-			goto nomissile;
-
-		P_SetMobjState(actor, actor->info->missilestate);
-		actor->flags2 |= MF2_JUSTATTACKED;
-		return;
-	}
-
-nomissile:
-	// possibly choose another target
-	if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
-		&& P_LookForPlayers(actor, true, false, 0))
-		return; // got a new target
-
-	// chase towards player
-	if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
-		P_NewChaseDir(actor);
-}
-
-// Function: A_FaceStabChase
-//
-// Description: A_Chase for CastleBot FaceStabber.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_FaceStabChase(mobj_t *actor)
-{
-	INT32 delta;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_FaceStabChase", actor))
-		return;
-#endif
-
-	if (actor->reactiontime)
-		actor->reactiontime--;
-
-	// modify target threshold
-	if (actor->threshold)
-	{
-		if (!actor->target || actor->target->health <= 0)
-			actor->threshold = 0;
-		else
-			actor->threshold--;
-	}
-
-	// turn towards movement direction if not there yet
-	if (actor->movedir < NUMDIRS)
-	{
-		actor->angle &= (7<<29);
-		delta = actor->angle - (actor->movedir << 29);
-
-		if (delta > 0)
-			actor->angle -= ANGLE_45;
-		else if (delta < 0)
-			actor->angle += ANGLE_45;
-	}
-
-	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
-	{
-		// look for a new target
-		if (P_LookForPlayers(actor, true, false, 0))
-			return; // got a new target
-
-		P_SetMobjStateNF(actor, actor->info->spawnstate);
-		return;
-	}
-
-	// do not attack twice in a row
-	if (actor->flags2 & MF2_JUSTATTACKED)
-	{
-		actor->flags2 &= ~MF2_JUSTATTACKED;
-		P_NewChaseDir(actor);
-		return;
-	}
-
-	// check for melee attack
-	if (actor->info->meleestate && P_FaceStabCheckMeleeRange(actor))
-	{
-		if (actor->info->attacksound)
-			S_StartAttackSound(actor, actor->info->attacksound);
-
-		P_SetMobjState(actor, actor->info->meleestate);
-		return;
-	}
-
-	// check for missile attack
-	if (actor->info->missilestate)
-	{
-		if (actor->movecount || !P_CheckMissileRange(actor))
-			goto nomissile;
-
-		P_SetMobjState(actor, actor->info->missilestate);
-		actor->flags2 |= MF2_JUSTATTACKED;
-		return;
-	}
-
-nomissile:
-	// possibly choose another target
-	if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
-		&& P_LookForPlayers(actor, true, false, 0))
-		return; // got a new target
-
-	// chase towards player
-	if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
-		P_NewChaseDir(actor);
-}
-
-// Function: A_JetJawRoam
-//
-// Description: Roaming routine for JetJaw
-//
-// var1 = unused
-// var2 = unused
-//
-void A_JetJawRoam(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_JetJawRoam", actor))
-		return;
-#endif
-	if (actor->reactiontime)
-	{
-		actor->reactiontime--;
-		P_InstaThrust(actor, actor->angle, FixedMul(actor->info->speed*FRACUNIT/4, actor->scale));
-	}
-	else
-	{
-		actor->reactiontime = actor->info->reactiontime;
-		actor->angle += ANGLE_180;
-	}
-
-	if (P_LookForPlayers(actor, false, false, actor->radius * 16))
-		P_SetMobjState(actor, actor->info->seestate);
-}
-
-// Function: A_JetJawChomp
-//
-// Description: Chase and chomp at the target, as long as it is in view
-//
-// var1 = unused
-// var2 = unused
-//
-void A_JetJawChomp(mobj_t *actor)
-{
-	INT32 delta;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_JetJawChomp", actor))
-		return;
-#endif
-
-	// turn towards movement direction if not there yet
-	if (actor->movedir < NUMDIRS)
-	{
-		actor->angle &= (7<<29);
-		delta = actor->angle - (actor->movedir << 29);
-
-		if (delta > 0)
-			actor->angle -= ANGLE_45;
-		else if (delta < 0)
-			actor->angle += ANGLE_45;
-	}
-
-	// Stop chomping if target's dead or you can't see it
-	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)
-		|| actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
-	{
-		P_SetMobjStateNF(actor, actor->info->spawnstate);
-		return;
-	}
-
-	// chase towards player
-	if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
-		P_NewChaseDir(actor);
-}
-
-// Function: A_PointyThink
-//
-// Description: Thinker function for Pointy
-//
-// var1 = unused
-// var2 = unused
-//
-void A_PointyThink(mobj_t *actor)
-{
-	INT32 i;
-	player_t *player = NULL;
-	mobj_t *ball;
-	TVector v;
-	TVector *res;
-	angle_t fa;
-	fixed_t radius = FixedMul(actor->info->radius*actor->info->reactiontime, actor->scale);
-	boolean firsttime = true;
-	INT32 sign;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_PointyThink", actor))
-		return;
-#endif
-	actor->momx = actor->momy = actor->momz = 0;
-
-	// Find nearest player
-	for (i = 0; i < MAXPLAYERS; i++)
-	{
-		if (!playeringame[i] || players[i].spectator)
-			continue;
-
-		if (!players[i].mo)
-			continue;
-
-		if (!players[i].mo->health)
-			continue;
-
-		if (!P_CheckSight(actor, players[i].mo))
-			continue;
-
-		if (firsttime)
-		{
-			firsttime = false;
-			player = &players[i];
-		}
-		else
-		{
-			if (P_AproxDistance(players[i].mo->x - actor->x, players[i].mo->y - actor->y) <
-				P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y))
-				player = &players[i];
-		}
-	}
-
-	if (!player)
-		return;
-
-	// Okay, we found the closest player. Let's move based on his movement.
-	P_SetTarget(&actor->target, player->mo);
-	A_FaceTarget(actor);
-
-	if (P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y) < P_AproxDistance(player->mo->x + player->mo->momx - actor->x, player->mo->y + player->mo->momy - actor->y))
-		sign = -1; // Player is moving away
-	else
-		sign = 1; // Player is moving closer
-
-	if (player->mo->momx || player->mo->momy)
-	{
-		P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y), FixedMul(actor->info->speed*sign, actor->scale));
-
-		// Rotate our spike balls
-		actor->lastlook += actor->info->damage;
-		actor->lastlook %= FINEANGLES/4;
-	}
-
-	if (!actor->tracer) // For some reason we do not have spike balls...
-		return;
-
-	// Position spike balls relative to the value of 'lastlook'.
-	ball = actor->tracer;
-
-	i = 0;
-	while (ball)
-	{
-		fa = actor->lastlook+i;
-		v[0] = FixedMul(FINECOSINE(fa),radius);
-		v[1] = 0;
-		v[2] = FixedMul(FINESINE(fa),radius);
-		v[3] = FRACUNIT;
-
-		res = VectorMatrixMultiply(v, *RotateXMatrix(FixedAngle(actor->lastlook+i)));
-		M_Memcpy(&v, res, sizeof (v));
-		res = VectorMatrixMultiply(v, *RotateZMatrix(actor->angle+ANGLE_180));
-		M_Memcpy(&v, res, sizeof (v));
-
-		P_UnsetThingPosition(ball);
-		ball->x = actor->x + v[0];
-		ball->y = actor->y + v[1];
-		ball->z = actor->z + (actor->height>>1) + v[2];
-		P_SetThingPosition(ball);
-
-		ball = ball->tracer;
-		i += ANGLE_90 >> ANGLETOFINESHIFT;
-	}
-}
-
-// Function: A_CheckBuddy
-//
-// Description: Checks if target/tracer exists/has health. If not, the object removes itself.
-//
-// var1:
-//		0 = target
-//		1 = tracer
-// var2 = unused
-//
-void A_CheckBuddy(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_CheckBuddy", actor))
-		return;
-#endif
-	if (locvar1 && (!actor->tracer || actor->tracer->health <= 0))
-		P_RemoveMobj(actor);
-	else if (!locvar1 && (!actor->target || actor->target->health <= 0))
-		P_RemoveMobj(actor);
-}
-
-// Function: A_HoodThink
-//
-// Description: Thinker for Robo-Hood
-//
-// var1 = unused
-// var2 = unused
-//
-void A_HoodThink(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_HoodThink", actor))
-		return;
-#endif
-	// Currently in the air...
-	if (!(actor->eflags & MFE_VERTICALFLIP) && actor->z > actor->floorz)
-	{
-		if (actor->momz > 0)
-			P_SetMobjStateNF(actor, actor->info->xdeathstate); // Rising
-		else
-			P_SetMobjStateNF(actor, actor->info->raisestate); // Falling
-
-		return;
-	}
-	else if ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height < actor->ceilingz)
-	{
-		if (actor->momz < 0)
-			P_SetMobjStateNF(actor, actor->info->xdeathstate); // Rising
-		else
-			P_SetMobjStateNF(actor, actor->info->raisestate); // Falling
-
-		return;
-	}
-
-	if (actor->state == &states[actor->info->xdeathstate]
-		|| actor->state == &states[actor->info->raisestate])
-		P_SetMobjStateNF(actor, actor->info->seestate);
-
-	if (!actor->target)
-	{
-		P_SetMobjState(actor, actor->info->spawnstate);
-		return;
-	}
-
-	A_FaceTarget(actor); // Aiming... aiming...
-
-	if (--actor->reactiontime > 0)
-		return;
-
-	// Shoot, if not too close (cheap shots are lame)
-	if ((P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) > FixedMul(192*FRACUNIT, actor->scale))
-	|| (actor->spawnpoint && (actor->spawnpoint->options & MTF_AMBUSH))) // If you can't jump, might as well shoot regardless of distance!
-		P_SetMobjState(actor, actor->info->missilestate);
-	else if (!(actor->spawnpoint && (actor->spawnpoint->options & MTF_AMBUSH)))// But we WILL jump!
-		P_SetMobjState(actor, actor->info->painstate);
-
-	actor->reactiontime = actor->info->reactiontime;
-}
-
-// Function: A_ArrowCheck
-//
-// Description: Checks arrow direction and adjusts sprite accordingly
-//
-// var1 = unused
-// var2 = unused
-//
-void A_ArrowCheck(mobj_t *actor)
-{
-	fixed_t x,y,z;
-	angle_t angle;
-	fixed_t dist;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_ArrowCheck", actor))
-		return;
-#endif
-
-	// Movement vector
-	x = actor->momx;
-	y = actor->momy;
-	z = actor->momz;
-
-	// Calculate the angle of movement.
-	/*
-	       Z
-	     / |
-	   /   |
-	 /     |
-	0------dist(X,Y)
-	*/
-
-	dist = P_AproxDistance(x, y);
-
-	angle = R_PointToAngle2(0, 0, dist, z);
-
-	if (angle > ANG20 && angle <= ANGLE_180)
-		P_SetMobjStateNF(actor, actor->info->raisestate);
-	else if (angle < ANG340 && angle > ANGLE_180)
-		P_SetMobjStateNF(actor, actor->info->xdeathstate);
-	else
-		P_SetMobjStateNF(actor, actor->info->spawnstate);
-}
-
-// Function: A_SnailerThink
-//
-// Description: Thinker function for Snailer
-//
-// var1 = unused
-// var2 = unused
-//
-void A_SnailerThink(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SnailerThink", actor))
-		return;
-#endif
-
-	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
-	{
-		// look for a new target
-		if (!P_LookForPlayers(actor, true, false, 0))
-			return;
-	}
-
-	// We now have a target. Oh bliss, rapture, and contentment!
-
-	if (actor->target->z + actor->target->height > actor->z - FixedMul(32*FRACUNIT, actor->scale)
-		&& actor->target->z < actor->z + actor->height + FixedMul(32*FRACUNIT, actor->scale)
-		&& !(leveltime % (TICRATE*2)))
-	{
-		angle_t an;
-		fixed_t z;
-
-		// Actor shouldn't face target, so we'll do things a bit differently here
-
-		an = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) - actor->angle;
-
-		z = actor->z + actor->height/2;
-
-		if (an > ANGLE_45 && an < ANGLE_315) // fire as close as you can to the target, even if too sharp an angle from your front
-		{
-			fixed_t dist;
-			fixed_t dx, dy;
-
-			dist = P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y);
-
-			if (an > ANGLE_45 && an <= ANGLE_90) // fire at 45 degrees to the left
-			{
-				dx = actor->x + P_ReturnThrustX(actor, actor->angle + ANGLE_45, dist);
-				dy = actor->y + P_ReturnThrustY(actor, actor->angle + ANGLE_45, dist);
-			}
-			else if (an >= ANGLE_270 && an < ANGLE_315) // fire at 45 degrees to the right
-			{
-				dx = actor->x + P_ReturnThrustX(actor, actor->angle - ANGLE_45, dist);
-				dy = actor->y + P_ReturnThrustY(actor, actor->angle - ANGLE_45, dist);
-			}
-			else // fire straight ahead
-			{
-				dx = actor->x + P_ReturnThrustX(actor, actor->angle, dist);
-				dy = actor->y + P_ReturnThrustY(actor, actor->angle, dist);
-			}
-
-			P_SpawnPointMissile(actor, dx, dy, actor->target->z, MT_ROCKET, actor->x, actor->y, z);
-		}
-		else
-			P_SpawnXYZMissile(actor, actor->target, MT_ROCKET, actor->x, actor->y, z);
-	}
-
-	if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->target->z > actor->z)
-	|| (actor->eflags & MFE_VERTICALFLIP && (actor->target->z + actor->target->height) > (actor->z + actor->height)))
-		actor->momz += FixedMul(actor->info->speed, actor->scale);
-	else if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->target->z < actor->z)
-	|| (actor->eflags & MFE_VERTICALFLIP && (actor->target->z + actor->target->height) < (actor->z + actor->height)))
-		actor->momz -= FixedMul(actor->info->speed, actor->scale);
-
-	actor->momz /= 2;
-}
-
-// Function: A_SharpChase
-//
-// Description: Thinker/Chase routine for Sharps
-//
-// var1 = unused
-// var2 = unused
-//
-void A_SharpChase(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SharpChase", actor))
-		return;
-#endif
-
-	if (!actor->health)
-	{
-		P_SetMobjState(actor, actor->info->deathstate);
-		return;
-	}
-
-	if (actor->reactiontime)
-	{
-		INT32 delta;
-
-		actor->reactiontime--;
-
-		// turn towards movement direction if not there yet
-		if (actor->movedir < NUMDIRS)
-		{
-			actor->angle &= (7<<29);
-			delta = actor->angle - (actor->movedir << 29);
-
-			if (delta > 0)
-				actor->angle -= ANGLE_45;
-			else if (delta < 0)
-				actor->angle += ANGLE_45;
-		}
-
-		if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
-		{
-			// look for a new target
-			if (P_LookForPlayers(actor, true, false, 0))
-				return; // got a new target
-
-			P_SetMobjState(actor, actor->info->spawnstate);
-			return;
-		}
-
-		// chase towards player
-		if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
-			P_NewChaseDir(actor);
-	}
-	else
-	{
-		actor->threshold = actor->info->painchance;
-		P_SetMobjState(actor, actor->info->missilestate);
-		S_StartSound(actor, actor->info->attacksound);
-	}
-}
-
-// Function: A_SharpSpin
-//
-// Description: Spin chase routine for Sharps
-//
-// var1 = unused
-// var2 = unused
-//
-void A_SharpSpin(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SharpSpin", actor))
-		return;
-#endif
-
-	if (!actor->health)
-	{
-		P_SetMobjState(actor, actor->info->deathstate);
-		return;
-	}
-
-	if (actor->threshold && actor->target)
-	{
-		actor->angle += ANGLE_22h;
-		P_Thrust(actor, R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y), FixedMul(actor->info->speed*FRACUNIT, actor->scale));
-		actor->threshold--;
-	}
-	else
-	{
-		actor->reactiontime = actor->info->reactiontime;
-		P_SetMobjState(actor, actor->info->spawnstate);
-
-		var1 = 1;
-		A_Look(actor);
-		if (actor->target)
-			actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
-	}
-}
-
-// Function: A_VultureVtol
-//
-// Description: Vulture rising up to match target's height
-//
-// var1 = unused
-// var2 = unused
-//
-void A_VultureVtol(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_VultureVtol", actor))
-		return;
-#endif
-
-	if (!actor->target)
-		return;
-
-	actor->flags |= MF_NOGRAVITY;
-	actor->flags |= MF_FLOAT;
-
-	A_FaceTarget(actor);
-
-	S_StopSound(actor);
-
-	if (actor->z < actor->target->z+(actor->target->height/4) && actor->z + actor->height < actor->ceilingz)
-		actor->momz = FixedMul(2*FRACUNIT, actor->scale);
-	else if (actor->z > (actor->target->z+(actor->target->height/4)*3) && actor->z > actor->floorz)
-		actor->momz = FixedMul(-2*FRACUNIT, actor->scale);
-	else
-	{
-		// Attack!
-		actor->momz = 0;
-		P_SetMobjState(actor, actor->info->missilestate);
-		S_StartSound(actor, actor->info->activesound);
-	}
-}
-
-// Function: A_VultureCheck
-//
-// Description: If the vulture is stopped, look for a new target
-//
-// var1 = unused
-// var2 = unused
-//
-void A_VultureCheck(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_VultureCheck", actor))
-		return;
-#endif
-
-	if (actor->momx || actor->momy)
-		return;
-
-	actor->flags &= ~MF_NOGRAVITY; // Fall down
-
-	if (actor->z <= actor->floorz)
-	{
-		actor->angle -= ANGLE_180; // turn around
-		P_SetMobjState(actor, actor->info->spawnstate);
-	}
-}
-
-// Function: A_SkimChase
-//
-// Description: Thinker/Chase routine for Skims
-//
-// var1 = unused
-// var2 = unused
-//
-void A_SkimChase(mobj_t *actor)
-{
-	INT32 delta;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SkimChase", actor))
-		return;
-#endif
-	if (actor->reactiontime)
-		actor->reactiontime--;
-
-	// modify target threshold
-	if (actor->threshold)
-	{
-		if (!actor->target || actor->target->health <= 0)
-			actor->threshold = 0;
-		else
-			actor->threshold--;
-	}
-
-	// turn towards movement direction if not there yet
-	if (actor->movedir < NUMDIRS)
-	{
-		actor->angle &= (7<<29);
-		delta = actor->angle - (actor->movedir << 29);
-
-		if (delta > 0)
-			actor->angle -= ANGLE_45;
-		else if (delta < 0)
-			actor->angle += ANGLE_45;
-	}
-
-	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
-	{
-		// look for a new target
-		P_LookForPlayers(actor, true, false, 0);
-
-		// the spawnstate for skims already calls this function so just return either way
-		// without changing state
-		return;
-	}
-
-	// do not attack twice in a row
-	if (actor->flags2 & MF2_JUSTATTACKED)
-	{
-		actor->flags2 &= ~MF2_JUSTATTACKED;
-		P_NewChaseDir(actor);
-		return;
-	}
-
-	// check for melee attack
-	if (actor->info->meleestate && P_SkimCheckMeleeRange(actor))
-	{
-		if (actor->info->attacksound)
-			S_StartAttackSound(actor, actor->info->attacksound);
-
-		P_SetMobjState(actor, actor->info->meleestate);
-		return;
-	}
-
-	// check for missile attack
-	if (actor->info->missilestate)
-	{
-		if (actor->movecount || !P_CheckMissileRange(actor))
-			goto nomissile;
-
-		P_SetMobjState(actor, actor->info->missilestate);
-		actor->flags2 |= MF2_JUSTATTACKED;
-		return;
-	}
-
-nomissile:
-	// possibly choose another target
-	if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
-		&& P_LookForPlayers(actor, true, false, 0))
-		return; // got a new target
-
-	// chase towards player
-	if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
-		P_NewChaseDir(actor);
-}
-
-// Function: A_FaceTarget
-//
-// Description: Immediately turn to face towards your target.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_FaceTarget(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_FaceTarget", actor))
-		return;
-#endif
-	if (!actor->target)
-		return;
-
-	actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
-}
-
-// Function: A_FaceTracer
-//
-// Description: Immediately turn to face towards your tracer.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_FaceTracer(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_FaceTracer", actor))
-		return;
-#endif
-	if (!actor->tracer)
-		return;
-
-	actor->angle = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y);
-}
-
-// Function: A_LobShot
-//
-// Description: Lob an object at your target.
-//
-// var1 = object # to lob
-// var2:
-//		var2 >> 16 = height offset
-//		var2 & 65535 = airtime
-//
-void A_LobShot(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2 >> 16;
-	mobj_t *shot, *hitspot;
-	angle_t an;
-	fixed_t z;
-	fixed_t dist;
-	fixed_t vertical, horizontal;
-	fixed_t airtime = var2 & 65535;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_LobShot", actor))
-		return;
-#endif
-	if (!actor->target)
-		return;
-
-	A_FaceTarget(actor);
-
-	if (actor->eflags & MFE_VERTICALFLIP)
-	{
-		z = actor->z + actor->height - FixedMul(locvar2*FRACUNIT, actor->scale);
-		if (actor->type == MT_BLACKEGGMAN)
-			z -= FixedMul(mobjinfo[locvar1].height, actor->scale/2);
-		else
-			z -= FixedMul(mobjinfo[locvar1].height, actor->scale);
-	}
-	else
-		z = actor->z + FixedMul(locvar2*FRACUNIT, actor->scale);
-
-	shot = P_SpawnMobj(actor->x, actor->y, z, locvar1);
-
-	if (actor->type == MT_BLACKEGGMAN)
-	{
-		shot->destscale = actor->scale/2;
-		P_SetScale(shot, actor->scale/2);
-	}
-	else
-	{
-		shot->destscale = actor->scale;
-		P_SetScale(shot, actor->scale);
-	}
-
-	// Keep track of where it's going to land
-	hitspot = P_SpawnMobj(actor->target->x&(64*FRACUNIT-1), actor->target->y&(64*FRACUNIT-1), actor->target->subsector->sector->floorheight, MT_NULL);
-	hitspot->tics = airtime;
-	P_SetTarget(&shot->tracer, hitspot);
-
-	P_SetTarget(&shot->target, actor); // where it came from
-
-	shot->angle = an = actor->angle;
-	an >>= ANGLETOFINESHIFT;
-
-	dist = P_AproxDistance(actor->target->x - shot->x, actor->target->y - shot->y);
-
-	horizontal = dist / airtime;
-	vertical = FixedMul((gravity*airtime)/2, shot->scale);
-
-	shot->momx = FixedMul(horizontal, FINECOSINE(an));
-	shot->momy = FixedMul(horizontal, FINESINE(an));
-	shot->momz = vertical;
-
-/* Try to adjust when destination is not the same height
-	if (actor->z != actor->target->z)
-	{
-		fixed_t launchhyp;
-		fixed_t diff;
-		fixed_t orig;
-
-		diff = actor->z - actor->target->z;
-		{
-			launchhyp = P_AproxDistance(horizontal, vertical);
-
-			orig = FixedMul(FixedDiv(vertical, horizontal), diff);
-
-			CONS_Debug(DBG_GAMELOGIC, "orig: %d\n", (orig)>>FRACBITS);
-
-			horizontal = dist / airtime;
-			vertical = (gravity*airtime)/2;
-		}
-		dist -= orig;
-		shot->momx = FixedMul(horizontal, FINECOSINE(an));
-		shot->momy = FixedMul(horizontal, FINESINE(an));
-		shot->momz = vertical;
-*/
-
-	if (shot->info->seesound)
-		S_StartSound(shot, shot->info->seesound);
-
-	if (!(actor->flags & MF_BOSS))
-	{
-		if (ultimatemode)
-			actor->reactiontime = actor->info->reactiontime*TICRATE;
-		else
-			actor->reactiontime = actor->info->reactiontime*TICRATE*2;
-	}
-}
-
-// Function: A_FireShot
-//
-// Description: Shoot an object at your target.
-//
-// var1 = object # to shoot
-// var2 = height offset
-//
-void A_FireShot(mobj_t *actor)
-{
-	fixed_t z;
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_FireShot", actor))
-		return;
-#endif
-	if (!actor->target)
-		return;
-
-	A_FaceTarget(actor);
-
-	if (actor->eflags & MFE_VERTICALFLIP)
-		z = actor->z + actor->height - FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
-	else
-		z = actor->z + FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
-
-	P_SpawnXYZMissile(actor, actor->target, locvar1, actor->x, actor->y, z);
-
-	if (!(actor->flags & MF_BOSS))
-	{
-		if (ultimatemode)
-			actor->reactiontime = actor->info->reactiontime*TICRATE;
-		else
-			actor->reactiontime = actor->info->reactiontime*TICRATE*2;
-	}
-}
-
-// Function: A_SuperFireShot
-//
-// Description: Shoot an object at your target that will even stall Super Sonic.
-//
-// var1 = object # to shoot
-// var2 = height offset
-//
-void A_SuperFireShot(mobj_t *actor)
-{
-	fixed_t z;
-	mobj_t *mo;
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SuperFireShot", actor))
-		return;
-#endif
-	if (!actor->target)
-		return;
-
-	A_FaceTarget(actor);
-
-	if (actor->eflags & MFE_VERTICALFLIP)
-		z = actor->z + actor->height - FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
-	else
-		z = actor->z + FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
-
-	mo = P_SpawnXYZMissile(actor, actor->target, locvar1, actor->x, actor->y, z);
-
-	if (mo)
-		mo->flags2 |= MF2_SUPERFIRE;
-
-	if (!(actor->flags & MF_BOSS))
-	{
-		if (ultimatemode)
-			actor->reactiontime = actor->info->reactiontime*TICRATE;
-		else
-			actor->reactiontime = actor->info->reactiontime*TICRATE*2;
-	}
-}
-
-// Function: A_BossFireShot
-//
-// Description: Shoot an object at your target ala Bosses:
-//
-// var1 = object # to shoot
-// var2:
-//		0 - Boss 1 Left side
-//		1 - Boss 1 Right side
-//		2 - Boss 3 Left side upper
-//		3 - Boss 3 Left side lower
-//		4 - Boss 3 Right side upper
-//		5 - Boss 3 Right side lower
-//
-void A_BossFireShot(mobj_t *actor)
-{
-	fixed_t x, y, z;
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_BossFireShot", actor))
-		return;
-#endif
-	if (!actor->target)
-		return;
-
-	A_FaceTarget(actor);
-
-	switch (locvar2)
-	{
-		case 0:
-			x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
-			y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
-			if (actor->eflags & MFE_VERTICALFLIP)
-				z = actor->z + actor->height - FixedMul(48*FRACUNIT, actor->scale);
-			else
-				z = actor->z + FixedMul(48*FRACUNIT, actor->scale);
-			break;
-		case 1:
-			x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
-			y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
-			if (actor->eflags & MFE_VERTICALFLIP)
-				z = actor->z + actor->height - FixedMul(48*FRACUNIT, actor->scale);
-			else
-				z = actor->z + FixedMul(48*FRACUNIT, actor->scale);
-			break;
-		case 2:
-			x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(56*FRACUNIT, actor->scale));
-			y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(56*FRACUNIT, actor->scale));
-			if (actor->eflags & MFE_VERTICALFLIP)
-				z = actor->z + actor->height - FixedMul(42*FRACUNIT, actor->scale);
-			else
-				z = actor->z + FixedMul(42*FRACUNIT, actor->scale);
-			break;
-		case 3:
-			x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(58*FRACUNIT, actor->scale));
-			y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(58*FRACUNIT, actor->scale));
-			if (actor->eflags & MFE_VERTICALFLIP)
-				z = actor->z + actor->height - FixedMul(30*FRACUNIT, actor->scale);
-			else
-				z = actor->z + FixedMul(30*FRACUNIT, actor->scale);
-			break;
-		case 4:
-			x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(56*FRACUNIT, actor->scale));
-			y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(56*FRACUNIT, actor->scale));
-			if (actor->eflags & MFE_VERTICALFLIP)
-				z = actor->z + actor->height - FixedMul(42*FRACUNIT, actor->scale);
-			else
-				z = actor->z + FixedMul(42*FRACUNIT, actor->scale);
-			break;
-		case 5:
-			x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(58*FRACUNIT, actor->scale));
-			y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(58*FRACUNIT, actor->scale));
-			if (actor->eflags & MFE_VERTICALFLIP)
-				z = actor->z + actor->height - FixedMul(30*FRACUNIT, actor->scale);
-			else
-				z = actor->z + FixedMul(30*FRACUNIT, actor->scale);
-			break;
-		default:
-			x = actor->x;
-			y = actor->y;
-			z = actor->z + actor->height/2;
-			break;
-	}
-
-	P_SpawnXYZMissile(actor, actor->target, locvar1, x, y, z);
-}
-
-// Function: A_Boss7FireMissiles
-//
-// Description: Shoot 4 missiles of a specific object type at your target ala Black Eggman
-//
-// var1 = object # to shoot
-// var2 = firing sound
-//
-void A_Boss7FireMissiles(mobj_t *actor)
-{
-	mobj_t dummymo;
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_Boss7FireMissiles", actor))
-		return;
-#endif
-
-	if (!actor->target)
-	{
-		P_SetMobjState(actor, actor->info->spawnstate);
-		return;
-	}
-
-	A_FaceTarget(actor);
-
-	S_StartSound(NULL, locvar2);
-
-	// set dummymo's coordinates
-	dummymo.x = actor->target->x;
-	dummymo.y = actor->target->y;
-	dummymo.z = actor->target->z + FixedMul(16*FRACUNIT, actor->scale); // raised height
-
-	P_SpawnXYZMissile(actor, &dummymo, locvar1,
-		actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
-		actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
-		actor->z + FixedDiv(actor->height, 3*FRACUNIT/2));
-
-	P_SpawnXYZMissile(actor, &dummymo, locvar1,
-		actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
-		actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
-		actor->z + FixedDiv(actor->height, 3*FRACUNIT/2));
-
-	P_SpawnXYZMissile(actor, &dummymo, locvar1,
-		actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
-		actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
-		actor->z + actor->height/2);
-
-	P_SpawnXYZMissile(actor, &dummymo, locvar1,
-		actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
-		actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
-		actor->z + actor->height/2);
-}
-
-// Function: A_Boss1Laser
-//
-// Description: Shoot an object at your target ala Bosses:
-//
-// var1 = object # to shoot
-// var2:
-//		0 - Boss 1 Left side
-//		1 - Boss 1 Right side
-//
-void A_Boss1Laser(mobj_t *actor)
-{
-	fixed_t x, y, z, floorz, speed;
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	INT32 i;
-	angle_t angle;
-	mobj_t *point;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_Boss1Laser", actor))
-		return;
-#endif
-	if (!actor->target)
-		return;
-
-	switch (locvar2)
-	{
-		case 0:
-			x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
-			y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
-			if (actor->eflags & MFE_VERTICALFLIP)
-				z = actor->z + actor->height - FixedMul(56*FRACUNIT, actor->scale) - mobjinfo[locvar1].height;
-			else
-				z = actor->z + FixedMul(56*FRACUNIT, actor->scale);
-			break;
-		case 1:
-			x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
-			y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
-			if (actor->eflags & MFE_VERTICALFLIP)
-				z = actor->z + actor->height - FixedMul(56*FRACUNIT, actor->scale) - mobjinfo[locvar1].height;
-			else
-				z = actor->z + FixedMul(56*FRACUNIT, actor->scale);
-			break;
-		default:
-			x = actor->x;
-			y = actor->y;
-			z = actor->z + actor->height/2;
-			break;
-	}
-
-	if (!(actor->flags2 & MF2_FIRING))
-	{
-		actor->angle = R_PointToAngle2(x, y, actor->target->x, actor->target->y);
-		if (mobjinfo[locvar1].seesound)
-			S_StartSound(actor, mobjinfo[locvar1].seesound);
-		if (!(actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH))
-		{
-			point = P_SpawnMobj(x + P_ReturnThrustX(actor, actor->angle, actor->radius), y + P_ReturnThrustY(actor, actor->angle, actor->radius), actor->z - actor->height / 2, MT_EGGMOBILE_TARGET);
-			point->angle = actor->angle;
-			point->fuse = actor->tics+1;
-			P_SetTarget(&point->target, actor->target);
-			P_SetTarget(&actor->target, point);
-		}
-	}
-	/* -- the following was relevant when the MT_EGGMOBILE_TARGET was allowed to move left and right from its path
-	else if (actor->target && !(actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH))
-		actor->angle = R_PointToAngle2(x, y, actor->target->x, actor->target->y);*/
-
-	if (actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH)
-		angle = FixedAngle(FixedDiv(actor->tics*160*FRACUNIT, actor->state->tics*FRACUNIT) + 10*FRACUNIT);
-	else
-		angle = R_PointToAngle2(z + (mobjinfo[locvar1].height>>1), 0, actor->target->z, R_PointToDist2(x, y, actor->target->x, actor->target->y));
-	point = P_SpawnMobj(x, y, z, locvar1);
-	P_SetTarget(&point->target, actor);
-	point->angle = actor->angle;
-	speed = point->radius*2;
-	point->momz = FixedMul(FINECOSINE(angle>>ANGLETOFINESHIFT), speed);
-	point->momx = FixedMul(FINESINE(angle>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(point->angle>>ANGLETOFINESHIFT), speed));
-	point->momy = FixedMul(FINESINE(angle>>ANGLETOFINESHIFT), FixedMul(FINESINE(point->angle>>ANGLETOFINESHIFT), speed));
-
-	for (i = 0; i < 256; i++)
-	{
-		mobj_t *mo = P_SpawnMobj(point->x, point->y, point->z, point->type);
-		mo->angle = point->angle;
-		P_UnsetThingPosition(mo);
-		mo->flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY;
-		P_SetThingPosition(mo);
-
-		x = point->x, y = point->y, z = point->z;
-		if (P_RailThinker(point))
-			break;
-	}
-
-	floorz = P_FloorzAtPos(x, y, z, mobjinfo[MT_EGGMOBILE_FIRE].height);
-	if (z - floorz < mobjinfo[MT_EGGMOBILE_FIRE].height>>1)
-	{
-		point = P_SpawnMobj(x, y, floorz+1, MT_EGGMOBILE_FIRE);
-		point->target = actor;
-		point->destscale = 3*FRACUNIT;
-		point->scalespeed = FRACUNIT>>2;
-		point->fuse = TICRATE;
-	}
-
-	if (actor->tics > 1)
-		actor->flags2 |= MF2_FIRING;
-	else
-		actor->flags2 &= ~MF2_FIRING;
-}
-
-// Function: A_FocusTarget
-//
-// Description: Home in on your target.
-//
-// var1:
-//		0 - accelerative focus with friction
-//		1 - steady focus with fixed movement speed
-//      anything else - don't move
-// var2:
-//		0 - don't trace target, just move forwards
-//      & 1 - change horizontal angle
-//      & 2 - change vertical angle
-//
-void A_FocusTarget(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_FocusTarget", actor))
-		return;
-#endif
-
-	if (actor->target)
-	{
-		fixed_t speed = FixedMul(actor->info->speed, actor->scale);
-		fixed_t dist = (locvar2 ? R_PointToDist2(actor->x, actor->y, actor->target->x, actor->target->y) : speed+1);
-		angle_t hangle = ((locvar2 & 1) ? R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) : actor->angle);
-		angle_t vangle = ((locvar2 & 2) ? R_PointToAngle2(actor->z , 0, actor->target->z + (actor->target->height>>1), dist) : ANGLE_90);
-		switch(locvar1)
-		{
-		case 0:
-			{
-				actor->momx -= actor->momx>>4, actor->momy -= actor->momy>>4, actor->momz -= actor->momz>>4;
-				actor->momz += FixedMul(FINECOSINE(vangle>>ANGLETOFINESHIFT), speed);
-				actor->momx += FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(hangle>>ANGLETOFINESHIFT), speed));
-				actor->momy += FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINESINE(hangle>>ANGLETOFINESHIFT), speed));
-			}
-			break;
-		case 1:
-			if (dist > speed)
-			{
-				actor->momz = FixedMul(FINECOSINE(vangle>>ANGLETOFINESHIFT), speed);
-				actor->momx = FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(hangle>>ANGLETOFINESHIFT), speed));
-				actor->momy = FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINESINE(hangle>>ANGLETOFINESHIFT), speed));
-			}
-			else
-			{
-				actor->momx = 0, actor->momy = 0, actor->momz = 0;
-				actor->z = actor->target->z + (actor->target->height>>1);
-				P_TryMove(actor, actor->target->x, actor->target->y, true);
-			}
-			break;
-		default:
-			break;
-		}
-	}
-}
-
-// Function: A_Boss4Reverse
-//
-// Description: Reverse arms direction.
-//
-// var1 = sfx to play
-// var2 = unused
-//
-void A_Boss4Reverse(mobj_t *actor)
-{
-	sfxenum_t locvar1 = (sfxenum_t)var1;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_Boss4Reverse", actor))
-		return;
-#endif
-	S_StartSound(NULL, locvar1);
-	actor->reactiontime = 0;
-	if (actor->movedir == 1)
-		actor->movedir = 2;
-	else
-		actor->movedir = 1;
-}
-
-// Function: A_Boss4SpeedUp
-//
-// Description: Speed up arms
-//
-// var1 = sfx to play
-// var2 = unused
-//
-void A_Boss4SpeedUp(mobj_t *actor)
-{
-	sfxenum_t locvar1 = (sfxenum_t)var1;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_Boss4SpeedUp", actor))
-		return;
-#endif
-	S_StartSound(NULL, locvar1);
-	actor->reactiontime = 2;
-}
-
-// Function: A_Boss4Raise
-//
-// Description: Raise helmet
-//
-// var1 = sfx to play
-// var2 = unused
-//
-void A_Boss4Raise(mobj_t *actor)
-{
-	sfxenum_t locvar1 = (sfxenum_t)var1;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_Boss4Raise", actor))
-		return;
-#endif
-	S_StartSound(NULL, locvar1);
-	actor->reactiontime = 1;
-}
-
-// Function: A_SkullAttack
-//
-// Description: Fly at the player like a missile.
-//
-// var1:
-//		0 - Fly at the player
-//		1 - Fly away from the player
-//		2 - Strafe in relation to the player
-// var2:
-//		0 - Fly horizontally and vertically
-//		1 - Fly horizontal-only (momz = 0)
-//
-#define SKULLSPEED (20*FRACUNIT)
-
-void A_SkullAttack(mobj_t *actor)
-{
-	mobj_t *dest;
-	angle_t an;
-	INT32 dist;
-	INT32 speed;
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SkullAttack", actor))
-		return;
-#endif
-	if (!actor->target)
-		return;
-
-	speed = FixedMul(SKULLSPEED, actor->scale);
-
-	dest = actor->target;
-	actor->flags2 |= MF2_SKULLFLY;
-	if (actor->info->activesound)
-		S_StartSound(actor, actor->info->activesound);
-	A_FaceTarget(actor);
-
-	if (locvar1 == 1)
-		actor->angle += ANGLE_180;
-	else if (locvar1 == 2)
-		actor->angle += (P_RandomChance(FRACUNIT/2)) ? ANGLE_90 : -ANGLE_90;
-
-	an = actor->angle >> ANGLETOFINESHIFT;
-
-	actor->momx = FixedMul(speed, FINECOSINE(an));
-	actor->momy = FixedMul(speed, FINESINE(an));
-	dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y);
-	dist = dist / speed;
-
-	if (dist < 1)
-		dist = 1;
-
-	actor->momz = (dest->z + (dest->height>>1) - actor->z) / dist;
-
-	if (locvar1 == 1)
-		actor->momz = -actor->momz;
-	if (locvar2 == 1)
-		actor->momz = 0;
-}
-
-// Function: A_BossZoom
-//
-// Description: Like A_SkullAttack, but used by Boss 1.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_BossZoom(mobj_t *actor)
-{
-	mobj_t *dest;
-	angle_t an;
-	INT32 dist;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_BossZoom", actor))
-		return;
-#endif
-	if (!actor->target)
-		return;
-
-	dest = actor->target;
-	actor->flags2 |= MF2_SKULLFLY;
-	if (actor->info->attacksound)
-		S_StartAttackSound(actor, actor->info->attacksound);
-	A_FaceTarget(actor);
-	an = actor->angle >> ANGLETOFINESHIFT;
-	actor->momx = FixedMul(FixedMul(actor->info->speed*5*FRACUNIT, actor->scale), FINECOSINE(an));
-	actor->momy = FixedMul(FixedMul(actor->info->speed*5*FRACUNIT, actor->scale), FINESINE(an));
-	dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y);
-	dist = dist / FixedMul(actor->info->speed*5*FRACUNIT, actor->scale);
-
-	if (dist < 1)
-		dist = 1;
-	actor->momz = (dest->z + (dest->height>>1) - actor->z) / dist;
-}
-
-// Function: A_BossScream
-//
-// Description: Spawns explosions and plays appropriate sounds around the defeated boss.
-//
-// var1:
-//		0 - Use movecount to spawn explosions evenly
-//		1 - Use P_Random to spawn explosions at complete random
-// var2 = Object to spawn. Default is MT_BOSSEXPLODE.
-//
-void A_BossScream(mobj_t *actor)
-{
-	mobj_t *mo;
-	fixed_t x, y, z;
-	angle_t fa;
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	mobjtype_t explodetype;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_BossScream", actor))
-		return;
-#endif
-	switch (locvar1)
-	{
-	default:
-	case 0:
-		actor->movecount += 4*16;
-		actor->movecount %= 360;
-		fa = (FixedAngle(actor->movecount*FRACUNIT)>>ANGLETOFINESHIFT) & FINEMASK;
-		break;
-	case 1:
-		fa = (FixedAngle(P_RandomKey(360)*FRACUNIT)>>ANGLETOFINESHIFT) & FINEMASK;
-		break;
-	}
-	x = actor->x + FixedMul(FINECOSINE(fa),actor->radius);
-	y = actor->y + FixedMul(FINESINE(fa),actor->radius);
-
-	// Determine what mobj to spawn. If undefined or invalid, use MT_BOSSEXPLODE as default.
-	if (locvar2 <= 0 || locvar2 >= NUMMOBJTYPES)
-		explodetype = MT_BOSSEXPLODE;
-	else
-		explodetype = (mobjtype_t)locvar2;
-
-	if (actor->eflags & MFE_VERTICALFLIP)
-		z = actor->z + actor->height - mobjinfo[explodetype].height - FixedMul((P_RandomByte()<<(FRACBITS-2)) - 8*FRACUNIT, actor->scale);
-	else
-		z = actor->z + FixedMul((P_RandomByte()<<(FRACBITS-2)) - 8*FRACUNIT, actor->scale);
-
-	mo = P_SpawnMobj(x, y, z, explodetype);
-	if (actor->eflags & MFE_VERTICALFLIP)
-		mo->flags2 |= MF2_OBJECTFLIP;
-	mo->destscale = actor->scale;
-	P_SetScale(mo, mo->destscale);
-	if (actor->info->deathsound)
-		S_StartSound(mo, actor->info->deathsound);
-}
-
-// Function: A_Scream
-//
-// Description: Starts the death sound of the object.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_Scream(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_Scream", actor))
-		return;
-#endif
-	if (actor->tracer && (actor->tracer->type == MT_SHELL || actor->tracer->type == MT_FIREBALL))
-		S_StartScreamSound(actor, sfx_mario2);
-	else if (actor->info->deathsound)
-		S_StartScreamSound(actor, actor->info->deathsound);
-}
-
-// Function: A_Pain
-//
-// Description: Starts the pain sound of the object.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_Pain(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_Pain", actor))
-		return;
-#endif
-	if (actor->info->painsound)
-		S_StartSound(actor, actor->info->painsound);
-
-	actor->flags2 &= ~MF2_FIRING;
-	actor->flags2 &= ~MF2_SUPERFIRE;
-}
-
-// Function: A_Fall
-//
-// Description: Changes a dying object's flags to reflect its having fallen to the ground.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_Fall(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_Fall", actor))
-		return;
-#endif
-	// actor is on ground, it can be walked over
-	actor->flags &= ~MF_SOLID;
-
-	// fall through the floor
-	actor->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY;
-
-	// So change this if corpse objects
-	// are meant to be obstacles.
-}
-
-#define LIVESBOXDISPLAYPLAYER // Use displayplayer instead of closest player
-
-// Function: A_1upThinker
-//
-// Description: Used by the 1up box to show the player's face.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_1upThinker(mobj_t *actor)
-{
-	INT32 i;
-	fixed_t dist = INT32_MAX;
-	fixed_t temp;
-	INT32 closestplayer = -1;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_1upThinker", actor))
-		return;
-#endif
-	for (i = 0; i < MAXPLAYERS; i++)
-	{
-		if (!playeringame[i] || players[i].bot || players[i].spectator)
-			continue;
-
-		if (!players[i].mo)
-			continue;
-
-		if ((netgame || multiplayer) && players[i].playerstate != PST_LIVE)
-			continue;
-
-		temp = P_AproxDistance(players[i].mo->x-actor->x, players[i].mo->y-actor->y);
-
-		if (temp < dist)
-		{
-			closestplayer = i;
-			dist = temp;
-		}
-	}
-
-	if (closestplayer == -1 || skins[players[closestplayer].skin].sprites[SPR2_LIFE].numframes == 0)
-	{ // Closest player not found (no players in game?? may be empty dedicated server!), or does not have correct sprite.
-		if (actor->tracer) {
-			P_RemoveMobj(actor->tracer);
-			actor->tracer = NULL;
-		}
-		return;
-	}
-
-	// We're using the overlay, so use the overlay 1up box (no text)
-	actor->sprite = SPR_TV1P;
-
-	if (!actor->tracer)
-	{
-		P_SetTarget(&actor->tracer, P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY));
-		P_SetTarget(&actor->tracer->target, actor);
-		actor->tracer->skin = &skins[players[closestplayer].skin]; // required here to prevent spr2 default showing stand for a single frame
-		P_SetMobjState(actor->tracer, actor->info->seestate);
-
-		// The overlay is going to be one tic early turning off and on
-		// because it's going to get its thinker run the frame we spawned it.
-		// So make it take one tic longer if it just spawned.
-		++actor->tracer->tics;
-	}
-
-	actor->tracer->color = players[closestplayer].mo->color;
-	actor->tracer->skin = &skins[players[closestplayer].skin];
-}
-
-// Function: A_MonitorPop
-//
-// Description: Used by monitors when they explode.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_MonitorPop(mobj_t *actor)
-{
-	mobjtype_t item = 0;
-	mobj_t *newmobj;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_MonitorPop", actor))
-		return;
-#endif
-
-	// Spawn the "pop" explosion.
-	if (actor->info->deathsound)
-		S_StartSound(actor, actor->info->deathsound);
-	P_SpawnMobjFromMobj(actor, 0, 0, actor->height/4, MT_EXPLODE);
-
-	// We're dead now. De-solidify.
-	actor->health = 0;
-	P_UnsetThingPosition(actor);
-	actor->flags &= ~MF_SOLID;
-	actor->flags |= MF_NOCLIP;
-	P_SetThingPosition(actor);
-
-	if (actor->info->damage == MT_UNKNOWN)
-	{
-		// MT_UNKNOWN is random. Because it's unknown to us... get it?
-		item = P_DoRandomBoxChances();
-
-		if (item == MT_NULL)
-		{
-			CONS_Alert(CONS_WARNING, M_GetText("All monitors turned off.\n"));
-			return;
-		}
-	}
-	else
-		item = actor->info->damage;
-
-	if (item == 0)
-	{
-		CONS_Debug(DBG_GAMELOGIC, "Powerup item not defined in 'damage' field for A_MonitorPop\n");
-		return;
-	}
-
-	newmobj = P_SpawnMobjFromMobj(actor, 0, 0, 13*FRACUNIT, item);
-	P_SetTarget(&newmobj->target, actor->target); // Transfer target
-
-	if (item == MT_1UP_ICON)
-	{
-		if (actor->tracer) // Remove the old lives icon.
-			P_RemoveMobj(actor->tracer);
-
-		if (!newmobj->target
-		 || !newmobj->target->player
-		 || !newmobj->target->skin
-		 || ((skin_t *)newmobj->target->skin)->sprites[SPR2_LIFE].numframes == 0)
-			{} // No lives icon for this player, use the default.
-		else
-		{ // Spawn the lives icon.
-			mobj_t *livesico = P_SpawnMobjFromMobj(newmobj, 0, 0, 0, MT_OVERLAY);
-			P_SetTarget(&livesico->target, newmobj);
-			P_SetTarget(&newmobj->tracer, livesico);
-
-			livesico->color = newmobj->target->player->mo->color;
-			livesico->skin = &skins[newmobj->target->player->skin];
-			P_SetMobjState(livesico, newmobj->info->seestate);
-
-			// We're using the overlay, so use the overlay 1up sprite (no text)
-			newmobj->sprite = SPR_TV1P;
-		}
-	}
-}
-
-// Function: A_GoldMonitorPop
-//
-// Description: Used by repeating monitors when they turn off. They don't really pop, but, you know...
-//
-// var1 = unused
-// var2 = unused
-//
-void A_GoldMonitorPop(mobj_t *actor)
-{
-	mobjtype_t item = 0;
-	mobj_t *newmobj;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_GoldMonitorPop", actor))
-		return;
-#endif
-
-	// Don't spawn the "pop" explosion, because the monitor isn't broken.
-	if (actor->info->deathsound)
-		S_StartSound(actor, actor->info->deathsound);
-	//P_SpawnMobjFromMobj(actor, 0, 0, actor.height/4, MT_EXPLODE);
-
-	// Remove our flags for a bit.
-	// Players can now stand on top of us.
-	P_UnsetThingPosition(actor);
-	actor->flags  &= ~(MF_MONITOR|MF_SHOOTABLE);
-	actor->flags2 |= MF2_STANDONME;
-	P_SetThingPosition(actor);
-
-	// Don't count this box in statistics. Sorry.
-	if (actor->target && actor->target->player)
-		--actor->target->player->numboxes;
-	actor->fuse = 0; // Don't let the monitor code screw us up.
-
-	if (actor->info->damage == MT_UNKNOWN)
-	{
-		// MT_UNKNOWN is random. Because it's unknown to us... get it?
-		item = P_DoRandomBoxChances();
-
-		if (item == MT_NULL)
-		{
-			CONS_Alert(CONS_WARNING, M_GetText("All monitors turned off.\n"));
-			return;
-		}
-	}
-	else
-		item = actor->info->damage;
-
-	if (item == 0)
-	{
-		CONS_Debug(DBG_GAMELOGIC, "Powerup item not defined in 'damage' field for A_GoldMonitorPop\n");
-		return;
-	}
-
-	// Note: the icon spawns 1 fracunit higher
-	newmobj = P_SpawnMobjFromMobj(actor, 0, 0, 14*FRACUNIT, item);
-	P_SetTarget(&newmobj->target, actor->target); // Transfer target
-
-	if (item == MT_1UP_ICON)
-	{
-		if (actor->tracer) // Remove the old lives icon.
-			P_RemoveMobj(actor->tracer);
-
-		if (!newmobj->target
-		 || !newmobj->target->player
-		 || !newmobj->target->skin
-		 || ((skin_t *)newmobj->target->skin)->sprites[SPR2_LIFE].numframes == 0)
-			{} // No lives icon for this player, use the default.
-		else
-		{ // Spawn the lives icon.
-			mobj_t *livesico = P_SpawnMobjFromMobj(newmobj, 0, 0, 0, MT_OVERLAY);
-			P_SetTarget(&livesico->target, newmobj);
-			P_SetTarget(&newmobj->tracer, livesico);
-
-			livesico->color = newmobj->target->player->mo->color;
-			livesico->skin = &skins[newmobj->target->player->skin];
-			P_SetMobjState(livesico, newmobj->info->seestate);
-
-			// We're using the overlay, so use the overlay 1up sprite (no text)
-			newmobj->sprite = SPR_TV1P;
-		}
-	}
-}
-
-// Function: A_GoldMonitorRestore
-//
-// Description: A repeating monitor is coming back to life. Reset monitor flags, etc.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_GoldMonitorRestore(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_GoldMonitorRestore", actor))
-		return;
-#endif
-
-	actor->flags |= MF_MONITOR|MF_SHOOTABLE;
-	actor->flags2 &= ~MF2_STANDONME;
-	actor->health = 1; // Just in case.
-}
-
-// Function: A_GoldMonitorSparkle
-//
-// Description: Spawns the little sparkly effect around big monitors. Looks pretty, doesn't it?
-//
-// var1 = unused
-// var2 = unused
-//
-void A_GoldMonitorSparkle(mobj_t *actor)
-{
-	fixed_t i, ngangle, xofs, yofs;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_GoldMonitorSparkle", actor))
-		return;
-#endif
-
-	ngangle = FixedAngle(((leveltime * 21) % 360) << FRACBITS);
-	xofs = FINESINE((ngangle>>ANGLETOFINESHIFT) & FINEMASK)   * (actor->radius>>FRACBITS);
-	yofs = FINECOSINE((ngangle>>ANGLETOFINESHIFT) & FINEMASK) * (actor->radius>>FRACBITS);
-
-	for (i = FRACUNIT*2; i <= FRACUNIT*3; i += FRACUNIT/2)
-		P_SetObjectMomZ(P_SpawnMobjFromMobj(actor, xofs, yofs, 0, MT_BOXSPARKLE), i, false);
-}
-
-// Function: A_Explode
-//
-// Description: Explodes an object, doing damage to any objects nearby. The target is used as the cause of the explosion. Damage value is used as explosion range.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_Explode(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_Explode", actor))
-		return;
-#endif
-	P_RadiusAttack(actor, actor->target, actor->info->damage);
-}
-
-// Function: A_BossDeath
-//
-// Description: Possibly trigger special effects when boss dies.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_BossDeath(mobj_t *mo)
-{
-	thinker_t *th;
-	mobj_t *mo2;
-	line_t junk;
-	INT32 i;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_BossDeath", mo))
-		return;
-#endif
-
-	P_LinedefExecute(LE_BOSSDEAD, mo, NULL);
-	mo->health = 0;
-
-	// Boss is dead (but not necessarily fleeing...)
-	// Lua may use this to ignore bosses after they start fleeing
-	mo->flags2 |= MF2_BOSSDEAD;
-
-	// make sure there is a player alive for victory
-	for (i = 0; i < MAXPLAYERS; i++)
-		if (playeringame[i] && ((players[i].mo && players[i].mo->health)
-			|| ((netgame || multiplayer) && (players[i].lives || players[i].continues))))
-			break;
-
-	if (i == MAXPLAYERS)
-		return; // no one left alive, so do not end game
-
-	// scan the remaining thinkers to see
-	// if all bosses are dead
-	for (th = thinkercap.next; th != &thinkercap; th = th->next)
-	{
-		if (th->function.acp1 != (actionf_p1)P_MobjThinker)
-			continue;
-
-		mo2 = (mobj_t *)th;
-		if (mo2 != mo && (mo2->flags & MF_BOSS) && mo2->health > 0)
-			goto bossjustdie; // other boss not dead - just go straight to dying!
-	}
-
-	// victory!
-	P_LinedefExecute(LE_ALLBOSSESDEAD, mo, NULL);
-	if (mo->flags2 & MF2_BOSSNOTRAP)
-	{
-		for (i = 0; i < MAXPLAYERS; i++)
-			P_DoPlayerExit(&players[i]);
-	}
-	else
-	{
-		// Bring the egg trap up to the surface
-		junk.tag = 680;
-		EV_DoElevator(&junk, elevateHighest, false);
-		junk.tag = 681;
-		EV_DoElevator(&junk, elevateUp, false);
-		junk.tag = 682;
-		EV_DoElevator(&junk, elevateHighest, false);
-	}
-
-bossjustdie:
-#ifdef HAVE_BLUA
-	if (LUAh_BossDeath(mo))
-		return;
-	else if (P_MobjWasRemoved(mo))
-		return;
-#endif
-	if (mo->type == MT_BLACKEGGMAN || mo->type == MT_CYBRAKDEMON)
-	{
-		mo->flags |= MF_NOCLIP;
-		mo->flags &= ~MF_SPECIAL;
-
-		S_StartSound(NULL, sfx_befall);
-	}
-	else if (mo->type == MT_KOOPA)
-	{
-		junk.tag = 650;
-		EV_DoCeiling(&junk, raiseToHighest);
-		return;
-	}
-	else // eggmobiles
-	{
-		// Stop exploding and prepare to run.
-		P_SetMobjState(mo, mo->info->xdeathstate);
-		if (P_MobjWasRemoved(mo))
-			return;
-
-		P_SetTarget(&mo->target, NULL);
-
-		// Flee! Flee! Find a point to escape to! If none, just shoot upward!
-		// scan the thinkers to find the runaway point
-		for (th = thinkercap.next; th != &thinkercap; th = th->next)
-		{
-			if (th->function.acp1 != (actionf_p1)P_MobjThinker)
-				continue;
-
-			mo2 = (mobj_t *)th;
-
-			if (mo2->type == MT_BOSSFLYPOINT)
-			{
-				// If this one's closer then the last one, go for it.
-				if (!mo->target ||
-					P_AproxDistance(P_AproxDistance(mo->x - mo2->x, mo->y - mo2->y), mo->z - mo2->z) <
-					P_AproxDistance(P_AproxDistance(mo->x - mo->target->x, mo->y - mo->target->y), mo->z - mo->target->z))
-						P_SetTarget(&mo->target, mo2);
-				// Otherwise... Don't!
-			}
-		}
-
-		mo->flags |= MF_NOGRAVITY|MF_NOCLIP;
-		mo->flags |= MF_NOCLIPHEIGHT;
-
-		if (mo->target)
-		{
-			mo->angle = R_PointToAngle2(mo->x, mo->y, mo->target->x, mo->target->y);
-			mo->flags2 |= MF2_BOSSFLEE;
-			mo->momz = FixedMul(FixedDiv(mo->target->z - mo->z, P_AproxDistance(mo->x-mo->target->x,mo->y-mo->target->y)), FixedMul(2*FRACUNIT, mo->scale));
-		}
-		else
-			mo->momz = FixedMul(2*FRACUNIT, mo->scale);
-	}
-
-	if (mo->type == MT_EGGMOBILE2)
-	{
-		mo2 = P_SpawnMobj(mo->x + P_ReturnThrustX(mo, mo->angle - ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)),
-			mo->y + P_ReturnThrustY(mo, mo->angle - ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)),
-			mo->z + mo->height/2 + ((mo->eflags & MFE_VERTICALFLIP)? FixedMul(8*FRACUNIT, mo->scale)-mobjinfo[MT_BOSSTANK1].height : -FixedMul(8*FRACUNIT, mo->scale)), MT_BOSSTANK1); // Right tank
-		mo2->angle = mo->angle;
-		mo2->destscale = mo->scale;
-		P_SetScale(mo2, mo2->destscale);
-		if (mo->eflags & MFE_VERTICALFLIP)
-		{
-			mo2->eflags |= MFE_VERTICALFLIP;
-			mo2->flags2 |= MF2_OBJECTFLIP;
-		}
-		P_InstaThrust(mo2, mo2->angle - ANGLE_90, FixedMul(4*FRACUNIT, mo2->scale));
-		P_SetObjectMomZ(mo2, 4*FRACUNIT, false);
-
-		mo2 = P_SpawnMobj(mo->x + P_ReturnThrustX(mo, mo->angle + ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)),
-			mo->y + P_ReturnThrustY(mo, mo->angle + ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)),
-			mo->z + mo->height/2 + ((mo->eflags & MFE_VERTICALFLIP)? FixedMul(8*FRACUNIT, mo->scale)-mobjinfo[MT_BOSSTANK2].height : -FixedMul(8*FRACUNIT, mo->scale)), MT_BOSSTANK2); // Left tank
-		mo2->angle = mo->angle;
-		mo2->destscale = mo->scale;
-		P_SetScale(mo2, mo2->destscale);
-		if (mo->eflags & MFE_VERTICALFLIP)
-		{
-			mo2->eflags |= MFE_VERTICALFLIP;
-			mo2->flags2 |= MF2_OBJECTFLIP;
-		}
-		P_InstaThrust(mo2, mo2->angle + ANGLE_90, FixedMul(4*FRACUNIT, mo2->scale));
-		P_SetObjectMomZ(mo2, 4*FRACUNIT, false);
-
-		mo2 = P_SpawnMobj(mo->x, mo->y,
-			mo->z + ((mo->eflags & MFE_VERTICALFLIP)? mobjinfo[MT_BOSSSPIGOT].height-FixedMul(32*FRACUNIT,mo->scale): mo->height + FixedMul(32*FRACUNIT, mo->scale)), MT_BOSSSPIGOT);
-		mo2->angle = mo->angle;
-		mo2->destscale = mo->scale;
-		P_SetScale(mo2, mo2->destscale);
-		if (mo->eflags & MFE_VERTICALFLIP)
-		{
-			mo2->eflags |= MFE_VERTICALFLIP;
-			mo2->flags2 |= MF2_OBJECTFLIP;
-		}
-		P_SetObjectMomZ(mo2, 4*FRACUNIT, false);
-		return;
-	}
-}
-
-// Function: A_CustomPower
-//
-// Description: Provides a custom powerup. Target (must be a player) is awarded the powerup. Reactiontime of the object is used as an index to the powers array.
-//
-// var1 = Power index #
-// var2 = Power duration in tics
-//
-void A_CustomPower(mobj_t *actor)
-{
-	player_t *player;
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	boolean spawnshield = false;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_CustomPower", actor))
-		return;
-#endif
-	if (!actor->target || !actor->target->player)
-	{
-		CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
-		return;
-	}
-
-	if (locvar1 >= NUMPOWERS)
-	{
-		CONS_Debug(DBG_GAMELOGIC, "Power #%d out of range!\n", locvar1);
-		return;
-	}
-
-	player = actor->target->player;
-
-	if (locvar1 == pw_shield && player->powers[pw_shield] != locvar2)
-		spawnshield = true;
-
-	player->powers[locvar1] = (UINT16)locvar2;
-	if (actor->info->seesound)
-		S_StartSound(player->mo, actor->info->seesound);
-
-	if (spawnshield) //workaround for a bug
-		P_SpawnShieldOrb(player);
-}
-
-// Function: A_GiveWeapon
-//
-// Description: Gives the player the specified weapon panels.
-//
-// var1 = Weapon index #
-// var2 = unused
-//
-void A_GiveWeapon(mobj_t *actor)
-{
-	player_t *player;
-	INT32 locvar1 = var1;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_GiveWeapon", actor))
-		return;
-#endif
-	if (!actor->target || !actor->target->player)
-	{
-		CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
-		return;
-	}
-
-	if (locvar1 >= 1<<(NUM_WEAPONS-1))
-	{
-		CONS_Debug(DBG_GAMELOGIC, "Weapon #%d out of range!\n", locvar1);
-		return;
-	}
-
-	player = actor->target->player;
-
-	player->ringweapons |= locvar1;
-	if (actor->info->seesound)
-		S_StartSound(player->mo, actor->info->seesound);
-}
-
-// Function: A_RingBox
-//
-// Description: Awards the player 10 rings.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_RingBox(mobj_t *actor)
-{
-	player_t *player;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_RingBox", actor))
-		return;
-#endif
-	if (!actor->target || !actor->target->player)
-	{
-		CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
-		return;
-	}
-
-	player = actor->target->player;
-
-	P_GivePlayerRings(player, actor->info->reactiontime);
-	if (actor->info->seesound)
-		S_StartSound(player->mo, actor->info->seesound);
-}
-
-// Function: A_Invincibility
-//
-// Description: Awards the player invincibility.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_Invincibility(mobj_t *actor)
-{
-	player_t *player;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_Invincibility", actor))
-		return;
-#endif
-	if (!actor->target || !actor->target->player)
-	{
-		CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
-		return;
-	}
-
-	player = actor->target->player;
-	player->powers[pw_invulnerability] = invulntics + 1;
-
-	if (P_IsLocalPlayer(player) && !player->powers[pw_super])
-	{
-		S_StopMusic();
-		if (mariomode)
-			G_GhostAddColor(GHC_INVINCIBLE);
-		strlcpy(S_sfx[sfx_None].caption, "Invincibility", 14);
-		S_StartCaption(sfx_None, -1, player->powers[pw_invulnerability]);
-		S_ChangeMusicInternal((mariomode) ? "_minv" : "_inv", false);
-	}
-}
-
-// Function: A_SuperSneakers
-//
-// Description: Awards the player super sneakers.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_SuperSneakers(mobj_t *actor)
-{
-	player_t *player;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SuperSneakers", actor))
-		return;
-#endif
-	if (!actor->target || !actor->target->player)
-	{
-		CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
-		return;
-	}
-
-	player = actor->target->player;
-
-	actor->target->player->powers[pw_sneakers] = sneakertics + 1;
-
-	if (P_IsLocalPlayer(player) && !player->powers[pw_super])
-	{
-		if (S_SpeedMusic(0.0f) && (mapheaderinfo[gamemap-1]->levelflags & LF_SPEEDMUSIC))
-			S_SpeedMusic(1.4f);
-		else
-		{
-			S_StopMusic();
-			S_ChangeMusicInternal("_shoes", false);
-		}
-		strlcpy(S_sfx[sfx_None].caption, "Speed shoes", 12);
-		S_StartCaption(sfx_None, -1, player->powers[pw_sneakers]);
-	}
-}
-
-// Function: A_AwardScore
-//
-// Description: Adds a set amount of points to the player's score.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_AwardScore(mobj_t *actor)
-{
-	player_t *player;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_AwardScore", actor))
-		return;
-#endif
-	if (!actor->target || !actor->target->player)
-	{
-		CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
-		return;
-	}
-
-	player = actor->target->player;
-
-	P_AddPlayerScore(player, actor->info->reactiontime);
-	if (actor->info->seesound)
-		S_StartSound(player->mo, actor->info->seesound);
-}
-
-// Function: A_ExtraLife
-//
-// Description: Awards the player an extra life.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_ExtraLife(mobj_t *actor)
-{
-	player_t *player;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_ExtraLife", actor))
-		return;
-#endif
-	if (!actor->target || !actor->target->player)
-	{
-		CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
-		return;
-	}
-
-	player = actor->target->player;
-
-	if (actor->type == MT_1UP_ICON && actor->tracer)
-	{
-		// We're using the overlay, so use the overlay 1up sprite (no text)
-		actor->sprite = SPR_TV1P;
-	}
-
-	if (ultimatemode) //I don't THINK so!
-	{
-		S_StartSound(player->mo, sfx_lose);
-		return;
-	}
-
-	P_GiveCoopLives(player, 1, true);
-}
-
-// Function: A_GiveShield
-//
-// Description: Awards the player a specified shield.
-//
-// var1 = Shield type (make with SH_ constants)
-// var2 = unused
-//
-void A_GiveShield(mobj_t *actor)
-{
-	player_t *player;
-	UINT16 locvar1 = var1;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_GiveShield", actor))
-		return;
-#endif
-	if (!actor->target || !actor->target->player)
-	{
-		CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
-		return;
-	}
-
-	player = actor->target->player;
-
-	P_SwitchShield(player, locvar1);
-	S_StartSound(player->mo, actor->info->seesound);
-}
-
-// Function: A_GravityBox
-//
-// Description: Awards the player gravity boots.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_GravityBox(mobj_t *actor)
-{
-	player_t *player;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_GravityBox", actor))
-		return;
-#endif
-	if (!actor->target || !actor->target->player)
-	{
-		CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
-		return;
-	}
-
-	player = actor->target->player;
-
-	S_StartSound(player, actor->info->activesound);
-
-	player->powers[pw_gravityboots] = (UINT16)(actor->info->reactiontime + 1);
-}
-
-// Function: A_ScoreRise
-//
-// Description: Makes the little score logos rise. Speed value sets speed.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_ScoreRise(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_ScoreRise", actor))
-		return;
-#endif
-	// make logo rise!
-	P_SetObjectMomZ(actor, actor->info->speed, false);
-}
-
-// Function: A_ParticleSpawn
-//
-// Description: Hyper-specialised function for spawning a particle for MT_PARTICLEGEN.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_ParticleSpawn(mobj_t *actor)
-{
-	INT32 i = 0;
-	mobj_t *spawn;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_ParticleSpawn", actor))
-		return;
-#endif
-	if (!actor->health)
-		return;
-
-	if (!actor->lastlook)
-		return;
-
-	if (!actor->threshold)
-		return;
-
-	for (i = 0; i < actor->lastlook; i++)
-	{
-		spawn = P_SpawnMobj(
-			actor->x + FixedMul(FixedMul(actor->friction, actor->scale), FINECOSINE(actor->angle>>ANGLETOFINESHIFT)),
-			actor->y + FixedMul(FixedMul(actor->friction, actor->scale), FINESINE(actor->angle>>ANGLETOFINESHIFT)),
-			actor->z,
-			(mobjtype_t)actor->threshold);
-		P_SetScale(spawn, actor->scale);
-		spawn->momz = FixedMul(actor->movefactor, spawn->scale);
-		spawn->destscale = spawn->scale/100;
-		spawn->scalespeed = spawn->scale/actor->health;
-		spawn->tics = (tic_t)actor->health;
-		spawn->flags2 |= (actor->flags2 & MF2_OBJECTFLIP);
-		spawn->angle += P_RandomKey(36)*ANG10; // irrelevant for default objects but might make sense for some custom ones
-
-		actor->angle += actor->movedir;
-	}
-
-	actor->angle += (angle_t)actor->movecount;
-	actor->tics = (tic_t)actor->reactiontime;
-}
-
-// Function: A_BunnyHop
-//
-// Description: Makes object hop like a bunny.
-//
-// var1 = jump strength
-// var2 = horizontal movement
-//
-void A_BunnyHop(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_BunnyHop", actor))
-		return;
-#endif
-	if (((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)
-		|| (!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz))
-	{
-		P_SetObjectMomZ(actor, locvar1*FRACUNIT, false);
-		P_InstaThrust(actor, actor->angle, FixedMul(locvar2*FRACUNIT, actor->scale)); // Launch the hopping action! PHOOM!!
-	}
-}
-
-// Function: A_BubbleSpawn
-//
-// Description: Spawns a randomly sized bubble from the object's location. Only works underwater.
-//
-// var1 = Distance to look for players.  If no player is in this distance, bubbles aren't spawned. (Ambush overrides)
-// var2 = unused
-//
-void A_BubbleSpawn(mobj_t *actor)
-{
-	INT32 i, locvar1 = var1;
-	UINT8 prandom;
-	mobj_t *bubble = NULL;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_BubbleSpawn", actor))
-		return;
-#endif
-	if (!(actor->eflags & MFE_UNDERWATER))
-	{
-		// Don't draw or spawn bubbles above water
-		actor->flags2 |= MF2_DONTDRAW;
-		return;
-	}
-	actor->flags2 &= ~MF2_DONTDRAW;
-
-	if (!(actor->flags2 & MF2_AMBUSH))
-	{
-		// Quick! Look through players!
-		// Don't spawn bubbles unless a player is relatively close by (var2).
-		for (i = 0; i < MAXPLAYERS; ++i)
-			if (playeringame[i] && players[i].mo
-			 && P_AproxDistance(actor->x - players[i].mo->x, actor->y - players[i].mo->y) < (locvar1<<FRACBITS))
-				break; // Stop looking.
-		if (i == MAXPLAYERS)
-			return; // don't make bubble!
-	}
-
-	prandom = P_RandomByte();
-
-	if (leveltime % (3*TICRATE) < 8)
-		bubble = P_SpawnMobj(actor->x, actor->y, actor->z + (actor->height / 2), MT_EXTRALARGEBUBBLE);
-	else if (prandom > 128)
-		bubble = P_SpawnMobj(actor->x, actor->y, actor->z + (actor->height / 2), MT_SMALLBUBBLE);
-	else if (prandom < 128 && prandom > 96)
-		bubble = P_SpawnMobj(actor->x, actor->y, actor->z + (actor->height / 2), MT_MEDIUMBUBBLE);
-
-	if (bubble)
-	{
-		bubble->destscale = actor->scale;
-		P_SetScale(bubble, actor->scale);
-	}
-}
-
-// Function: A_FanBubbleSpawn
-//
-// Description: Spawns bubbles from fans, if they're underwater.
-//
-// var1 = Distance to look for players.  If no player is in this distance, bubbles aren't spawned. (Ambush overrides)
-// var2 = unused
-//
-void A_FanBubbleSpawn(mobj_t *actor)
-{
-	INT32 i, locvar1 = var1;
-	UINT8 prandom;
-	mobj_t *bubble = NULL;
-	fixed_t hz = actor->z + (4*actor->height)/5;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_FanBubbleSpawn", actor))
-		return;
-#endif
-	if (!(actor->eflags & MFE_UNDERWATER))
-		return;
-
-	if (!(actor->flags2 & MF2_AMBUSH))
-	{
-	// Quick! Look through players!
-	// Don't spawn bubbles unless a player is relatively close by (var2).
-		for (i = 0; i < MAXPLAYERS; ++i)
-			if (playeringame[i] && players[i].mo
-			 && P_AproxDistance(actor->x - players[i].mo->x, actor->y - players[i].mo->y) < (locvar1<<FRACBITS))
-				break; // Stop looking.
-		if (i == MAXPLAYERS)
-			return; // don't make bubble!
-	}
-
-	prandom = P_RandomByte();
-
-	if ((prandom & 0x7) == 0x7)
-		bubble = P_SpawnMobj(actor->x, actor->y, hz, MT_SMALLBUBBLE);
-	else if ((prandom & 0xF0) == 0xF0)
-		bubble = P_SpawnMobj(actor->x, actor->y, hz, MT_MEDIUMBUBBLE);
-
-	if (bubble)
-	{
-		bubble->destscale = actor->scale;
-		P_SetScale(bubble, actor->scale);
-	}
-}
-
-// Function: A_BubbleRise
-//
-// Description: Raises a bubble
-//
-// var1:
-//		0 = Bend around the water abit, looking more realistic
-//		1 = Rise straight up
-// var2 = rising speed
-//
-void A_BubbleRise(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_BubbleRise", actor))
-		return;
-#endif
-	if (actor->type == MT_EXTRALARGEBUBBLE)
-		P_SetObjectMomZ(actor, FixedDiv(6*FRACUNIT,5*FRACUNIT), false); // make bubbles rise!
-	else
-	{
-		P_SetObjectMomZ(actor, locvar2, true); // make bubbles rise!
-
-		// Move around slightly to make it look like it's bending around the water
-		if (!locvar1)
-		{
-			UINT8 prandom = P_RandomByte();
-			if (!(prandom & 0x7)) // *****000
-			{
-				P_InstaThrust(actor, prandom & 0x70 ? actor->angle + ANGLE_90 : actor->angle,
-					FixedMul(prandom & 0xF0 ? FRACUNIT/2 : -FRACUNIT/2, actor->scale));
-			}
-			else if (!(prandom & 0x38)) // **000***
-			{
-				P_InstaThrust(actor, prandom & 0x70 ? actor->angle - ANGLE_90 : actor->angle - ANGLE_180,
-					FixedMul(prandom & 0xF0 ? FRACUNIT/2 : -FRACUNIT/2, actor->scale));
-			}
-		}
-	}
-}
-
-// Function: A_BubbleCheck
-//
-// Description: Checks if a bubble should be drawn or not. Bubbles are not drawn above water.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_BubbleCheck(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_BubbleCheck", actor))
-		return;
-#endif
-	if (actor->eflags & MFE_UNDERWATER)
-		actor->flags2 &= ~MF2_DONTDRAW; // underwater so draw
-	else
-		actor->flags2 |= MF2_DONTDRAW; // above water so don't draw
-}
-
-// Function: A_AttractChase
-//
-// Description: Makes a ring chase after a player with a ring shield and also causes spilled rings to flicker.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_AttractChase(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_AttractChase", actor))
-		return;
-#endif
-	if (actor->flags2 & MF2_NIGHTSPULL || !actor->health)
-		return;
-
-	// spilled rings flicker before disappearing
-	if (leveltime & 1 && actor->type == (mobjtype_t)actor->info->reactiontime && actor->fuse && actor->fuse < 2*TICRATE)
-		actor->flags2 |= MF2_DONTDRAW;
-	else
-		actor->flags2 &= ~MF2_DONTDRAW;
-
-	// Turn flingrings back into regular rings if attracted.
-	if (actor->tracer && actor->tracer->player
-		&& !(actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC) && actor->info->reactiontime && actor->type != (mobjtype_t)actor->info->reactiontime)
-	{
-		mobj_t *newring;
-		newring = P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->reactiontime);
-		newring->momx = actor->momx;
-		newring->momy = actor->momy;
-		newring->momz = actor->momz;
-		P_RemoveMobj(actor);
-		return;
-	}
-
-	P_LookForShield(actor); // Go find 'em, boy!
-
-	if (!actor->tracer
-		|| !actor->tracer->player
-		|| !actor->tracer->health
-		|| !P_CheckSight(actor, actor->tracer)) // You have to be able to SEE it...sorta
-	{
-		// Lost attracted rings don't through walls anymore.
-		actor->flags &= ~MF_NOCLIP;
-		P_SetTarget(&actor->tracer, NULL);
-		return;
-	}
-
-	// If a FlingRing gets attracted by a shield, change it into a normal ring.
-	if (actor->type == (mobjtype_t)actor->info->reactiontime)
-	{
-		P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->painchance);
-		P_RemoveMobj(actor);
-		return;
-	}
-
-	// Keep stuff from going down inside floors and junk
-	actor->flags &= ~MF_NOCLIPHEIGHT;
-
-	// Let attracted rings move through walls and such.
-	actor->flags |= MF_NOCLIP;
-
-	P_Attract(actor, actor->tracer, false);
-}
-
-// Function: A_DropMine
-//
-// Description: Drops a mine. Raisestate specifies the object # to use for the mine.
-//
-// var1 = height offset
-// var2:
-//		lower 16 bits = proximity check distance (0 disables)
-//		upper 16 bits = 0 to check proximity with target, 1 for tracer
-//
-void A_DropMine(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	fixed_t z;
-	mobj_t *mine;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_DropMine", actor))
-		return;
-#endif
-
-	if (locvar2 & 65535)
-	{
-		fixed_t dist;
-		mobj_t *target;
-
-		if (locvar2 >> 16)
-			target = actor->tracer;
-		else
-			target = actor->target;
-
-		if (!target)
-			return;
-
-		dist = P_AproxDistance(actor->x-target->x, actor->y-target->y)>>FRACBITS;
-
-		if (dist > FixedMul((locvar2 & 65535), actor->scale))
-			return;
-	}
-
-	if (actor->eflags & MFE_VERTICALFLIP)
-		z = actor->z + actor->height - mobjinfo[actor->info->raisestate].height - FixedMul((locvar1*FRACUNIT) - 12*FRACUNIT, actor->scale);
-	else
-		z = actor->z + FixedMul((locvar1*FRACUNIT) - 12*FRACUNIT, actor->scale);
-
-	// Use raisestate instead of MT_MINE
-	mine = P_SpawnMobj(actor->x, actor->y, z, (mobjtype_t)actor->info->raisestate);
-	if (actor->eflags & MFE_VERTICALFLIP)
-		mine->eflags |= MFE_VERTICALFLIP;
-	mine->momz = actor->momz + actor->pmomz;
-
-	S_StartSound(actor, actor->info->attacksound);
-}
-
-// Function: A_FishJump
-//
-// Description: Makes the stupid harmless fish in Greenflower Zone jump.
-//
-// var1 = Jump strength (in FRACBITS), if specified. Otherwise, uses the angle value.
-// var2 = unused
-//
-void A_FishJump(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_FishJump", actor))
-		return;
-#endif
-
-	if (locvar2)
-	{
-		fixed_t rad = actor->radius>>FRACBITS;
-		P_SpawnMobjFromMobj(actor, P_RandomRange(rad, -rad)<<FRACBITS, P_RandomRange(rad, -rad)<<FRACBITS, 0, (mobjtype_t)locvar2);
-	}
-
-	if ((actor->z <= actor->floorz) || (actor->z <= actor->watertop - FixedMul((64 << FRACBITS), actor->scale)))
-	{
-		fixed_t jumpval;
-
-		if (locvar1)
-			jumpval = var1;
-		else
-			jumpval = FixedMul(AngleFixed(actor->angle)/4, actor->scale);
-
-		if (!jumpval) jumpval = FixedMul(44*(FRACUNIT/4), actor->scale);
-		actor->momz = jumpval;
-		P_SetMobjStateNF(actor, actor->info->seestate);
-	}
-
-	if (actor->momz < 0
-		&& (actor->state < &states[actor->info->meleestate] || actor->state > &states[actor->info->xdeathstate]))
-		P_SetMobjStateNF(actor, actor->info->meleestate);
-}
-
-// Function:A_ThrownRing
-//
-// Description: Thinker for thrown rings/sparkle trail
-//
-// var1 = unused
-// var2 = unused
-//
-void A_ThrownRing(mobj_t *actor)
-{
-	INT32 c = 0;
-	INT32 stop;
-	player_t *player;
-	fixed_t dist;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_ThrownRing", actor))
-		return;
-#endif
-
-	if (leveltime % (TICRATE/7) == 0)
-	{
-		mobj_t *ring = NULL;
-
-		if (actor->flags2 & MF2_EXPLOSION)
-		{
-			if (actor->momx != 0 || actor->momy != 0)
-				ring = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMOKE);
-			// Else spawn nothing because it's totally stationary and constantly smoking would be weird -SH
-		}
-		else if (actor->flags2 & MF2_AUTOMATIC)
-			ring = P_SpawnGhostMobj(actor);
-		else if (!(actor->flags2 & MF2_RAILRING))
-			ring = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SPARK);
-
-		if (ring)
-		{
-			/*
-			P_SetTarget(&ring->target, actor);
-			ring->color = actor->color; //copy color
-			*/
-			ring->destscale = actor->scale;
-			P_SetScale(ring, actor->scale);
-		}
-	}
-
-	// A_GrenadeRing beeping lives once moooooore -SH
-	if (actor->type == MT_THROWNGRENADE && actor->fuse % TICRATE == 0)
-		S_StartSound(actor, actor->info->attacksound);
-
-	// decrement bounce ring time
-	if (actor->flags2 & MF2_BOUNCERING)
-	{
-		if (actor->fuse)
-			actor->fuse--;
-		else {
-			P_RemoveMobj(actor);
-			return;
-		}
-	}
-
-	// spilled rings (and thrown bounce) flicker before disappearing
-	if (leveltime & 1 && actor->fuse > 0 && actor->fuse < 2*TICRATE
-		&& actor->type != MT_THROWNGRENADE)
-		actor->flags2 |= MF2_DONTDRAW;
-	else
-		actor->flags2 &= ~MF2_DONTDRAW;
-
-	if (actor->tracer && actor->tracer->health <= 0)
-		P_SetTarget(&actor->tracer, NULL);
-
-	// Updated homing ring special capability
-	// If you have a ring shield, all rings thrown
-	// at you become homing (except rail)!
-	if (actor->tracer)
-	{
-		// A non-homing ring getting attracted by a
-		// magnetic player. If he gets too far away, make
-		// sure to stop the attraction!
-		if ((!actor->tracer->health) || (actor->tracer->player && (actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC)
-		    && P_AproxDistance(P_AproxDistance(actor->tracer->x-actor->x,
-		    actor->tracer->y-actor->y), actor->tracer->z-actor->z) > FixedMul(RING_DIST/4, actor->tracer->scale)))
-		{
-			P_SetTarget(&actor->tracer, NULL);
-		}
-
-		if (actor->tracer && (actor->tracer->health)
-			&& (actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC))// Already found someone to follow.
-		{
-			const INT32 temp = actor->threshold;
-			actor->threshold = 32000;
-			P_HomingAttack(actor, actor->tracer);
-			actor->threshold = temp;
-			return;
-		}
-	}
-
-	// first time init, this allow minimum lastlook changes
-	if (actor->lastlook < 0)
-		actor->lastlook = P_RandomByte();
-
-	actor->lastlook %= MAXPLAYERS;
-
-	stop = (actor->lastlook - 1) & PLAYERSMASK;
-
-	for (; ; actor->lastlook = (actor->lastlook + 1) & PLAYERSMASK)
-	{
-		// done looking
-		if (actor->lastlook == stop)
-			return;
-
-		if (!playeringame[actor->lastlook])
-			continue;
-
-		if (c++ == 2)
-			return;
-
-		player = &players[actor->lastlook];
-
-		if (!player->mo)
-			continue;
-
-		if (player->mo->health <= 0)
-			continue; // dead
-
-		if ((netgame || multiplayer) && player->spectator)
-			continue; // spectator
-
-		if (actor->target && actor->target->player)
-		{
-			if (player->mo == actor->target)
-				continue;
-
-			// Don't home in on teammates.
-			if (gametype == GT_CTF
-				&& actor->target->player->ctfteam == player->ctfteam)
-				continue;
-		}
-
-		dist = P_AproxDistance(P_AproxDistance(player->mo->x-actor->x,
-			player->mo->y-actor->y), player->mo->z-actor->z);
-
-		// check distance
-		if (actor->flags2 & MF2_RAILRING)
-		{
-			if (dist > FixedMul(RING_DIST/2, player->mo->scale))
-				continue;
-		}
-		else if (dist > FixedMul(RING_DIST, player->mo->scale))
-			continue;
-
-		// do this after distance check because it's more computationally expensive
-		if (!P_CheckSight(actor, player->mo))
-			continue; // out of sight
-
-		if ((player->powers[pw_shield] & SH_PROTECTELECTRIC)
-			&& dist < FixedMul(RING_DIST/4, player->mo->scale))
-			P_SetTarget(&actor->tracer, player->mo);
-		return;
-	}
-
-	return;
-}
-
-// Function: A_SetSolidSteam
-//
-// Description: Makes steam solid so it collides with the player to boost them.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_SetSolidSteam(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SetSolidSteam", actor))
-		return;
-#endif
-	actor->flags &= ~MF_NOCLIP;
-	actor->flags |= MF_SOLID;
-	if (!(actor->flags2 & MF2_AMBUSH))
-	{
-		if (P_RandomChance(FRACUNIT/8))
-		{
-			if (actor->info->deathsound)
-				S_StartSound(actor, actor->info->deathsound); // Hiss!
-		}
-		else
-		{
-			if (actor->info->painsound)
-				S_StartSound(actor, actor->info->painsound);
-		}
-	}
-
-	P_SetObjectMomZ (actor, 1, true);
-}
-
-// Function: A_UnsetSolidSteam
-//
-// Description: Makes an object non-solid and also noclip. Used by the steam.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_UnsetSolidSteam(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_UnsetSolidSteam", actor))
-		return;
-#endif
-	actor->flags &= ~MF_SOLID;
-	actor->flags |= MF_NOCLIP;
-}
-
-// Function: A_SignPlayer
-//
-// Description: Changes the state of a level end sign to reflect the player that hit it.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_SignPlayer(mobj_t *actor)
-{
-	mobj_t *ov;
-	skin_t *skin;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SignPlayer", actor))
-		return;
-#endif
-	if (!actor->target)
-		return;
-
-	if (!actor->target->player)
-		return;
-
-	skin = &skins[actor->target->player->skin];
-
-	if ((actor->target->player->skincolor == skin->prefcolor) && (skin->prefoppositecolor)) // Set it as the skin's preferred oppositecolor?
-	{
-		actor->color = skin->prefoppositecolor;
-		/*
-		If you're here from the comment above Color_Opposite,
-		the following line is the one which is dependent on the
-		array being symmetrical. It gets the opposite of the
-		opposite of your desired colour just so it can get the
-		brightness frame for the End Sign. It's not a great
-		design choice, but it's constant time array access and
-		the idea that the colours should be OPPOSITES is kind
-		of in the name. If you have a better idea, feel free
-		to let me know. ~toast 2016/07/20
-		*/
-		actor->frame += (15 - Color_Opposite[(Color_Opposite[(skin->prefoppositecolor - 1)*2] - 1)*2 + 1]);
-	}
-	else if (actor->target->player->skincolor) // Set the sign to be an appropriate background color for this player's skincolor.
-	{
-		actor->color = Color_Opposite[(actor->target->player->skincolor - 1)*2];
-		actor->frame += (15 - Color_Opposite[(actor->target->player->skincolor - 1)*2 + 1]);
-	}
-
-	if (skin->sprites[SPR2_SIGN].numframes)
-	{
-		// spawn an overlay of the player's face.
-		ov = P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY);
-		P_SetTarget(&ov->target, actor);
-		ov->color = actor->target->player->skincolor;
-		ov->skin = skin;
-		P_SetMobjState(ov, actor->info->seestate); // S_PLAY_SIGN
-	}
-}
-
-// Function: A_OverlayThink
-//
-// Description: Moves the overlay to the position of its target.
-//
-// var1 = unused
-// var2 = invert, z offset
-//
-void A_OverlayThink(mobj_t *actor)
-{
-	fixed_t destx, desty;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_OverlayThink", actor))
-		return;
-#endif
-	if (!actor->target)
-		return;
-
-	if (!splitscreen && rendermode != render_soft)
-	{
-		angle_t viewingangle;
-
-		if (players[displayplayer].awayviewtics)
-			viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, players[displayplayer].awayviewmobj->x, players[displayplayer].awayviewmobj->y);
-		else if (!camera.chase && players[displayplayer].mo)
-			viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, players[displayplayer].mo->x, players[displayplayer].mo->y);
-		else
-			viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, camera.x, camera.y);
-
-		destx = actor->target->x + P_ReturnThrustX(actor->target, viewingangle, FixedMul(FRACUNIT, actor->scale));
-		desty = actor->target->y + P_ReturnThrustY(actor->target, viewingangle, FixedMul(FRACUNIT, actor->scale));
-	}
-	else
-	{
-		destx = actor->target->x;
-		desty = actor->target->y;
-	}
-	P_UnsetThingPosition(actor);
-	actor->x = destx;
-	actor->y = desty;
-	P_SetThingPosition(actor);
-	if (actor->eflags & MFE_VERTICALFLIP)
-		actor->z = actor->target->z + actor->target->height - mobjinfo[actor->type].height  - ((var2>>16) ? -1 : 1)*(var2&0xFFFF)*FRACUNIT;
-	else
-		actor->z = actor->target->z + ((var2>>16) ? -1 : 1)*(var2&0xFFFF)*FRACUNIT;
-	actor->angle = actor->target->angle;
-	actor->eflags = actor->target->eflags;
-
-	actor->momx = actor->target->momx;
-	actor->momy = actor->target->momy;
-	actor->momz = actor->target->momz; // assume target has correct momz! Do not use P_SetObjectMomZ!
-}
-
-// Function: A_JetChase
-//
-// Description: A_Chase for Jettysyns
-//
-// var1 = unused
-// var2 = unused
-//
-void A_JetChase(mobj_t *actor)
-{
-	fixed_t thefloor;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_JetChase", actor))
-		return;
-#endif
-
-	if (actor->flags2 & MF2_AMBUSH)
-		return;
-
-	if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz
-		&& actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale))
-		thefloor = actor->watertop;
-	else
-		thefloor = actor->floorz;
-
-	if (actor->reactiontime)
-		actor->reactiontime--;
-
-	if (P_RandomChance(FRACUNIT/32))
-	{
-		actor->momx = actor->momx / 2;
-		actor->momy = actor->momy / 2;
-		actor->momz = actor->momz / 2;
-	}
-
-	// Bounce if too close to floor or ceiling -
-	// ideal for Jetty-Syns above you on 3d floors
-	if (actor->momz && ((actor->z - FixedMul((32<<FRACBITS), actor->scale)) < thefloor) && !((thefloor + FixedMul(32*FRACUNIT, actor->scale) + actor->height) > actor->ceilingz))
-		actor->momz = -actor->momz/2;
-
-	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
-	{
-		// look for a new target
-		if (P_LookForPlayers(actor, true, false, 0))
-			return; // got a new target
-
-		actor->momx = actor->momy = actor->momz = 0;
-		P_SetMobjState(actor, actor->info->spawnstate);
-		return;
-	}
-
-	// modify target threshold
-	if (actor->threshold)
-	{
-		if (!actor->target || actor->target->health <= 0)
-			actor->threshold = 0;
-		else
-			actor->threshold--;
-	}
-
-	// turn towards movement direction if not there yet
-	actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
-
-	if ((multiplayer || netgame) && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target)))
-		if (P_LookForPlayers(actor, true, false, 0))
-			return; // got a new target
-
-	// If the player is over 3072 fracunits away, then look for another player
-	if (P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y),
-		actor->target->z - actor->z) > FixedMul(3072*FRACUNIT, actor->scale) && P_LookForPlayers(actor, true, false, FixedMul(3072*FRACUNIT, actor->scale)))
-	{
-		return; // got a new target
-	}
-
-	// chase towards player
-	if (ultimatemode)
-		P_Thrust(actor, actor->angle, FixedMul(actor->info->speed/2, actor->scale));
-	else
-		P_Thrust(actor, actor->angle, FixedMul(actor->info->speed/4, actor->scale));
-
-	// must adjust height
-	if (ultimatemode)
-	{
-		if (actor->z < (actor->target->z + actor->target->height + FixedMul((64<<FRACBITS), actor->scale)))
-			actor->momz += FixedMul(FRACUNIT/2, actor->scale);
-		else
-			actor->momz -= FixedMul(FRACUNIT/2, actor->scale);
-	}
-	else
-	{
-		if (actor->z < (actor->target->z + actor->target->height + FixedMul((32<<FRACBITS), actor->scale)))
-			actor->momz += FixedMul(FRACUNIT/2, actor->scale);
-		else
-			actor->momz -= FixedMul(FRACUNIT/2, actor->scale);
-	}
-}
-
-// Function: A_JetbThink
-//
-// Description: Thinker for Jetty-Syn bombers
-//
-// var1 = unused
-// var2 = unused
-//
-void A_JetbThink(mobj_t *actor)
-{
-	sector_t *nextsector;
-	fixed_t thefloor;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_JetbThink", actor))
-		return;
-#endif
-
-	if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz
-		&& actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale))
-		thefloor = actor->watertop;
-	else
-		thefloor = actor->floorz;
-
-	if (actor->target)
-	{
-		A_JetChase(actor);
-		// check for melee attack
-		if ((actor->z > (actor->floorz + FixedMul((32<<FRACBITS), actor->scale)))
-			&& P_JetbCheckMeleeRange(actor) && !actor->reactiontime
-			&& (actor->target->z >= actor->floorz))
-		{
-			mobj_t *bomb;
-			if (actor->info->attacksound)
-				S_StartAttackSound(actor, actor->info->attacksound);
-
-			// use raisestate instead of MT_MINE
-			bomb = P_SpawnMobj(actor->x, actor->y, actor->z - FixedMul((32<<FRACBITS), actor->scale), (mobjtype_t)actor->info->raisestate);
-
-			P_SetTarget(&bomb->target, actor);
-			bomb->destscale = actor->scale;
-			P_SetScale(bomb, actor->scale);
-			actor->reactiontime = TICRATE; // one second
-			S_StartSound(actor, actor->info->attacksound);
-		}
-	}
-	else if (((actor->z - FixedMul((32<<FRACBITS), actor->scale)) < thefloor) && !((thefloor + FixedMul((32<<FRACBITS), actor->scale) + actor->height) > actor->ceilingz))
-			actor->z = thefloor+FixedMul((32<<FRACBITS), actor->scale);
-
-	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
-	{
-		// look for a new target
-		if (P_LookForPlayers(actor, true, false, 0))
-			return; // got a new target
-
-		P_SetMobjState(actor, actor->info->spawnstate);
-		return;
-	}
-
-	nextsector = R_PointInSubsector(actor->x + actor->momx, actor->y + actor->momy)->sector;
-
-	// Move downwards or upwards to go through a passageway.
-	if (nextsector->ceilingheight < actor->z + actor->height)
-		actor->momz -= FixedMul(5*FRACUNIT, actor->scale);
-	else if (nextsector->floorheight > actor->z)
-		actor->momz += FixedMul(5*FRACUNIT, actor->scale);
-}
-
-// Function: A_JetgShoot
-//
-// Description: Firing function for Jetty-Syn gunners.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_JetgShoot(mobj_t *actor)
-{
-	fixed_t dist;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_JetgShoot", actor))
-		return;
-#endif
-
-	if (!actor->target)
-		return;
-
-	if (actor->reactiontime)
-		return;
-
-	dist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
-
-	if (dist > FixedMul(actor->info->painchance*FRACUNIT, actor->scale))
-		return;
-
-	if (dist < FixedMul(64*FRACUNIT, actor->scale))
-		return;
-
-	A_FaceTarget(actor);
-	P_SpawnMissile(actor, actor->target, (mobjtype_t)actor->info->raisestate);
-
-	if (ultimatemode)
-		actor->reactiontime = actor->info->reactiontime*TICRATE;
-	else
-		actor->reactiontime = actor->info->reactiontime*TICRATE*2;
-
-	if (actor->info->attacksound)
-		S_StartSound(actor, actor->info->attacksound);
-}
-
-// Function: A_ShootBullet
-//
-// Description: Shoots a bullet. Raisestate defines object # to use as projectile.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_ShootBullet(mobj_t *actor)
-{
-	fixed_t dist;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_ShootBullet", actor))
-		return;
-#endif
-
-	if (!actor->target)
-		return;
-
-	dist = P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y), actor->target->z - actor->z);
-
-	if (dist > FixedMul(actor->info->painchance*FRACUNIT, actor->scale))
-		return;
-
-	A_FaceTarget(actor);
-	P_SpawnMissile(actor, actor->target, (mobjtype_t)actor->info->raisestate);
-
-	if (actor->info->attacksound)
-		S_StartSound(actor, actor->info->attacksound);
-}
-
-// Function: A_MinusDigging
-//
-// Description: Minus digging in the ground.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_MinusDigging(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_MinusDigging", actor))
-		return;
-#endif
-	actor->flags &= ~MF_SPECIAL;
-	actor->flags &= ~MF_SHOOTABLE;
-
-	if (!actor->target)
-	{
-		A_Look(actor);
-		return;
-	}
-
-	if (actor->reactiontime)
-	{
-		actor->reactiontime--;
-		return;
-	}
-
-	// Dirt trail
-	P_SpawnGhostMobj(actor);
-
-	actor->flags |= MF_NOCLIPTHING;
-	var1 = 3;
-	A_Chase(actor);
-	actor->flags &= ~MF_NOCLIPTHING;
-
-	// Play digging sound
-	if (!(leveltime & 15))
-		S_StartSound(actor, actor->info->activesound);
-
-	// If we're close enough to our target, pop out of the ground
-	if (P_AproxDistance(actor->target->x-actor->x, actor->target->y-actor->y) < actor->radius
-		&& abs(actor->target->z - actor->z) < 2*actor->height)
-		P_SetMobjState(actor, actor->info->missilestate);
-
-	// Snap to ground
-	if (actor->eflags & MFE_VERTICALFLIP)
-		actor->z = actor->ceilingz - actor->height;
-	else
-		actor->z = actor->floorz;
-}
-
-// Function: A_MinusPopup
-//
-// Description: Minus popping out of the ground.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_MinusPopup(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_MinusPopup", actor))
-		return;
-#endif
-	P_SetObjectMomZ(actor, 10*FRACUNIT, false);
-
-	actor->flags |= MF_SPECIAL;
-	actor->flags |= MF_SHOOTABLE;
-
-	// Sound for busting out of the ground.
-	S_StartSound(actor, actor->info->attacksound);
-}
-
-// Function: A_MinusCheck
-//
-// Description: If the minus hits the floor, dig back into the ground.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_MinusCheck(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_MinusCheck", actor))
-		return;
-#endif
-	if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)
-	|| ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz))
-	{
-		actor->flags &= ~MF_SPECIAL;
-		actor->flags &= ~MF_SHOOTABLE;
-		actor->reactiontime = TICRATE;
-		P_SetMobjState(actor, actor->info->seestate);
-		return;
-	}
-
-	// 'Falling' animation
-	if (P_MobjFlip(actor)*actor->momz < 0 && actor->state < &states[actor->info->meleestate])
-		P_SetMobjState(actor, actor->info->meleestate);
-}
-
-// Function: A_ChickenCheck
-//
-// Description: Resets the chicken once it hits the floor again.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_ChickenCheck(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_ChickenCheck", actor))
-		return;
-#endif
-	if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)
-	|| (actor->eflags & MFE_VERTICALFLIP && actor->z + actor->height >= actor->ceilingz))
-	{
-		if (!(actor->momx || actor->momy || actor->momz)
-			&& actor->state > &states[actor->info->seestate])
-		{
-			A_Chase(actor);
-			P_SetMobjState(actor, actor->info->seestate);
-		}
-
-		actor->momx >>= 2;
-		actor->momy >>= 2;
-	}
-}
-
-// Function: A_JetgThink
-//
-// Description: Thinker for Jetty-Syn Gunners
-//
-// var1 = unused
-// var2 = unused
-//
-void A_JetgThink(mobj_t *actor)
-{
-	sector_t *nextsector;
-
-	fixed_t thefloor;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_JetgThink", actor))
-		return;
-#endif
-
-	if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz
-		&& actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale))
-		thefloor = actor->watertop;
-	else
-		thefloor = actor->floorz;
-
-	if (actor->target)
-	{
-		if (P_RandomChance(FRACUNIT/8) && !actor->reactiontime)
-			P_SetMobjState(actor, actor->info->missilestate);
-		else
-			A_JetChase (actor);
-	}
-	else if (actor->z - FixedMul((32<<FRACBITS), actor->scale) < thefloor && !(thefloor + FixedMul((32<<FRACBITS), actor->scale)
-		+ actor->height > actor->ceilingz))
-	{
-		actor->z = thefloor + FixedMul((32<<FRACBITS), actor->scale);
-	}
-
-	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
-	{
-		// look for a new target
-		if (P_LookForPlayers(actor, true, false, 0))
-			return; // got a new target
-
-		P_SetMobjState(actor, actor->info->spawnstate);
-		return;
-	}
-
-	nextsector = R_PointInSubsector(actor->x + actor->momx, actor->y + actor->momy)->sector;
-
-	// Move downwards or upwards to go through a passageway.
-	if (nextsector->ceilingheight < actor->z + actor->height)
-		actor->momz -= FixedMul(5*FRACUNIT, actor->scale);
-	else if (nextsector->floorheight > actor->z)
-		actor->momz += FixedMul(5*FRACUNIT, actor->scale);
-}
-
-// Function: A_MouseThink
-//
-// Description: Thinker for scurrying mice.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_MouseThink(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_MouseThink", actor))
-		return;
-#endif
-
-	if (actor->reactiontime)
-		actor->reactiontime--;
-
-	if (((!(actor->eflags & MFE_VERTICALFLIP) && actor->z == actor->floorz)
-		|| (actor->eflags & MFE_VERTICALFLIP && actor->z + actor->height == actor->ceilingz))
-		&& !actor->reactiontime)
-	{
-		if (twodlevel || actor->flags2 & MF2_TWOD)
-		{
-			if (P_RandomChance(FRACUNIT/2))
-				actor->angle += ANGLE_180;
-		}
-		else if (P_RandomChance(FRACUNIT/2))
-			actor->angle += ANGLE_90;
-		else
-			actor->angle -= ANGLE_90;
-
-		P_InstaThrust(actor, actor->angle, FixedMul(actor->info->speed, actor->scale));
-		actor->reactiontime = TICRATE/5;
-	}
-}
-
-// Function: A_DetonChase
-//
-// Description: Chases a Deton after a player.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_DetonChase(mobj_t *actor)
-{
-	angle_t exact;
-	fixed_t xydist, dist;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_DetonChase", actor))
-		return;
-#endif
-
-	// modify tracer threshold
-	if (!actor->tracer || actor->tracer->health <= 0)
-		actor->threshold = 0;
-	else
-		actor->threshold = 1;
-
-	if (!actor->tracer || !(actor->tracer->flags & MF_SHOOTABLE))
-	{
-		// look for a new target
-		if (P_LookForPlayers(actor, true, true, 0))
-			return; // got a new target
-
-		actor->momx = actor->momy = actor->momz = 0;
-		P_SetMobjState(actor, actor->info->spawnstate);
-		return;
-	}
-
-	if (multiplayer && !actor->threshold && P_LookForPlayers(actor, true, true, 0))
-		return; // got a new target
-
-	// Face movement direction if not doing so
-	exact = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y);
-	actor->angle = exact;
-	/*if (exact != actor->angle)
-	{
-		if (exact - actor->angle > ANGLE_180)
-		{
-			actor->angle -= actor->info->raisestate;
-			if (exact - actor->angle < ANGLE_180)
-				actor->angle = exact;
-		}
-		else
-		{
-			actor->angle += actor->info->raisestate;
-			if (exact - actor->angle > ANGLE_180)
-				actor->angle = exact;
-		}
-	}*/
-	// movedir is up/down angle: how much it has to go up as it goes over to the player
-	xydist = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y);
-	exact = R_PointToAngle2(0, 0, xydist, actor->tracer->z - actor->z);
-	actor->movedir = exact;
-	/*if (exact != actor->movedir)
-	{
-		if (exact - actor->movedir > ANGLE_180)
-		{
-			actor->movedir -= actor->info->raisestate;
-			if (exact - actor->movedir < ANGLE_180)
-				actor->movedir = exact;
-		}
-		else
-		{
-			actor->movedir += actor->info->raisestate;
-			if (exact - actor->movedir > ANGLE_180)
-				actor->movedir = exact;
-		}
-	}*/
-
-	// check for melee attack
-	if (actor->tracer)
-	{
-		if (P_AproxDistance(actor->tracer->x-actor->x, actor->tracer->y-actor->y) < actor->radius+actor->tracer->radius)
-		{
-			if (!((actor->tracer->z > actor->z + actor->height) || (actor->z > actor->tracer->z + actor->tracer->height)))
-			{
-				P_ExplodeMissile(actor);
-				return;
-			}
-		}
-	}
-
-	// chase towards player
-	if ((dist = P_AproxDistance(xydist, actor->tracer->z-actor->z))
-		> FixedMul((actor->info->painchance << FRACBITS), actor->scale))
-	{
-		P_SetTarget(&actor->tracer, NULL); // Too far away
-		return;
-	}
-
-	if (actor->reactiontime == 0)
-	{
-		actor->reactiontime = actor->info->reactiontime;
-		return;
-	}
-
-	if (actor->reactiontime > 1)
-	{
-		actor->reactiontime--;
-		return;
-	}
-
-	if (actor->reactiontime > 0)
-	{
-		actor->reactiontime = -42;
-
-		if (actor->info->seesound)
-			S_StartScreamSound(actor, actor->info->seesound);
-	}
-
-	if (actor->reactiontime == -42)
-	{
-		fixed_t xyspeed;
-
-		actor->reactiontime = -42;
-
-		exact = actor->movedir>>ANGLETOFINESHIFT;
-		xyspeed = FixedMul(FixedMul(actor->tracer->player->normalspeed,3*FRACUNIT/4), FINECOSINE(exact));
-		actor->momz = FixedMul(FixedMul(actor->tracer->player->normalspeed,3*FRACUNIT/4), FINESINE(exact));
-
-		exact = actor->angle>>ANGLETOFINESHIFT;
-		actor->momx = FixedMul(xyspeed, FINECOSINE(exact));
-		actor->momy = FixedMul(xyspeed, FINESINE(exact));
-
-		// Variable re-use
-		xyspeed = (P_AproxDistance(actor->tracer->x - actor->x, P_AproxDistance(actor->tracer->y - actor->y, actor->tracer->z - actor->z))>>(FRACBITS+6));
-
-		if (xyspeed < 1)
-			xyspeed = 1;
-
-		if (leveltime % xyspeed == 0)
-			S_StartSound(actor, sfx_deton);
-	}
-}
-
-// Function: A_CapeChase
-//
-// Description: Set an object's location to its target or tracer.
-//
-// var1:
-//		0 = Use target
-//		1 = Use tracer
-//		upper 16 bits = Z offset
-// var2:
-//		upper 16 bits = forward/backward offset
-//		lower 16 bits = sideways offset
-//
-void A_CapeChase(mobj_t *actor)
-{
-	mobj_t *chaser;
-	fixed_t foffsetx, foffsety, boffsetx, boffsety;
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	angle_t angle;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_CapeChase", actor))
-		return;
-#endif
-
-	CONS_Debug(DBG_GAMELOGIC, "A_CapeChase called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
-
-	if (locvar1 & 65535)
-		chaser = actor->tracer;
-	else
-		chaser = actor->target;
-
-	if (!chaser || (chaser->health <= 0))
-	{
-		if (chaser)
-			CONS_Debug(DBG_GAMELOGIC, "Hmm, the guy I'm chasing (object type %d) has no health.. so I'll die too!\n", chaser->type);
-
-		P_RemoveMobj(actor);
-		return;
-	}
-
-	angle = (chaser->player ? chaser->player->drawangle : chaser->angle);
-
-	foffsetx = P_ReturnThrustX(chaser, angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale));
-	foffsety = P_ReturnThrustY(chaser, angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale));
-
-	boffsetx = P_ReturnThrustX(chaser, angle-ANGLE_90, FixedMul((locvar2 & 65535)*FRACUNIT, actor->scale));
-	boffsety = P_ReturnThrustY(chaser, angle-ANGLE_90, FixedMul((locvar2 & 65535)*FRACUNIT, actor->scale));
-
-	P_UnsetThingPosition(actor);
-	actor->x = chaser->x + foffsetx + boffsetx;
-	actor->y = chaser->y + foffsety + boffsety;
-	if (chaser->eflags & MFE_VERTICALFLIP)
-	{
-		actor->eflags |= MFE_VERTICALFLIP;
-		actor->flags2 |= MF2_OBJECTFLIP;
-		actor->z = chaser->z + chaser->height - actor->height - FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale);
-	}
-	else
-	{
-		actor->eflags &= ~MFE_VERTICALFLIP;
-		actor->flags2 &= ~MF2_OBJECTFLIP;
-		actor->z = chaser->z + FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale);
-	}
-	actor->angle = angle;
-	P_SetThingPosition(actor);
-}
-
-// Function: A_RotateSpikeBall
-//
-// Description: Rotates a spike ball around its target/tracer.
-//
-// var1:
-//		0 = Use target
-//		1 = Use tracer
-// var2 = unused
-//
-void A_RotateSpikeBall(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	const fixed_t radius = FixedMul(12*actor->info->speed, actor->scale);
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_RotateSpikeBall", actor))
-		return;
-#endif
-
-	if (actor->type == MT_SPECIALSPIKEBALL) // don't remove this, these spikeballs share the same states as the rotating spikeballs
-		return;
-
-	if (!((!locvar1 && (actor->target)) || (locvar1 && (actor->tracer))))// This should NEVER happen.
-	{
-		CONS_Debug(DBG_GAMELOGIC, "A_RotateSpikeBall: Spikeball has no target\n");
-		P_RemoveMobj(actor);
-		return;
-	}
-
-	if (!actor->info->speed)
-	{
-		CONS_Debug(DBG_GAMELOGIC, "A_RotateSpikeBall: Object has no speed.\n");
-		return;
-	}
-
-	actor->angle += FixedAngle(actor->info->speed);
-	P_UnsetThingPosition(actor);
-	{
-		const angle_t fa = actor->angle>>ANGLETOFINESHIFT;
-		if (!locvar1)
-		{
-			actor->x = actor->target->x + FixedMul(FINECOSINE(fa),radius);
-			actor->y = actor->target->y + FixedMul(FINESINE(fa),radius);
-			actor->z = actor->target->z + actor->target->height/2;
-		}
-		else
-		{
-			actor->x = actor->tracer->x + FixedMul(FINECOSINE(fa),radius);
-			actor->y = actor->tracer->y + FixedMul(FINESINE(fa),radius);
-			actor->z = actor->tracer->z + actor->tracer->height/2;
-		}
-		P_SetThingPosition(actor);
-	}
-}
-
-// Function: A_UnidusBall
-//
-// Description: Rotates a spike ball around its target.
-//
-// var1:
-//		0 = Don't throw
-//		1 = Throw
-//		2 = Throw when target leaves MF2_SKULLFLY.
-// var2 = unused
-//
-void A_UnidusBall(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	boolean canthrow = false;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_UnidusBall", actor))
-		return;
-#endif
-
-	actor->angle += ANGLE_11hh;
-
-	if (actor->movecount)
-	{
-		if (P_AproxDistance(actor->momx, actor->momy) < FixedMul(actor->info->damage/2, actor->scale))
-			P_ExplodeMissile(actor);
-		return;
-	}
-
-	if (!actor->target || !actor->target->health)
-	{
-		CONS_Debug(DBG_GAMELOGIC, "A_UnidusBall: Removing unthrown spikeball from nonexistant Unidus\n");
-		P_RemoveMobj(actor);
-		return;
-	}
-
-	P_UnsetThingPosition(actor);
-	{
-		const angle_t angle = actor->movedir + FixedAngle(actor->info->speed*(leveltime%360));
-		const UINT16 fa = angle>>ANGLETOFINESHIFT;
-
-		actor->x = actor->target->x + FixedMul(FINECOSINE(fa),actor->threshold);
-		actor->y = actor->target->y + FixedMul(  FINESINE(fa),actor->threshold);
-		actor->z = actor->target->z + actor->target->height/2 - actor->height/2;
-
-		if (locvar1 == 1 && actor->target->target)
-		{
-			const angle_t tang = R_PointToAngle2(actor->target->x, actor->target->y, actor->target->target->x, actor->target->target->y);
-			const angle_t mina = tang-ANGLE_11hh;
-			canthrow = (angle-mina < FixedAngle(actor->info->speed*3));
-		}
-	}
-	P_SetThingPosition(actor);
-
-	if (locvar1 == 1 && canthrow)
-	{
-		if (P_AproxDistance(actor->target->target->x - actor->target->x, actor->target->target->y - actor->target->y) > FixedMul(MISSILERANGE>>1, actor->scale)
-		|| !P_CheckSight(actor, actor->target->target))
-			return;
-
-		actor->movecount = actor->info->damage>>FRACBITS;
-		actor->flags &= ~(MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING);
-		P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, actor->target->target->x, actor->target->target->y), FixedMul(actor->info->damage, actor->scale));
-	}
-	else if (locvar1 == 2)
-	{
-		boolean skull = (actor->target->flags2 & MF2_SKULLFLY) == MF2_SKULLFLY;
-		if (actor->target->state == &states[actor->target->info->painstate])
-		{
-			P_KillMobj(actor, NULL, NULL, 0);
-			return;
-		}
-		switch(actor->extravalue2)
-		{
-		case 0: // at least one frame where not dashing
-			if (!skull) ++actor->extravalue2;
-			else break;
-			/* FALLTHRU */
-		case 1: // at least one frame where ARE dashing
-			if (skull) ++actor->extravalue2;
-			else break;
-			/* FALLTHRU */
-		case 2: // not dashing again?
-			if (skull) break;
-			// launch.
-		{
-			mobj_t *target = actor->target;
-			if (actor->target->target)
-				target = actor->target->target;
-			actor->movecount = actor->info->damage>>FRACBITS;
-			actor->flags &= ~(MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING);
-			P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, target->x, target->y), FixedMul(actor->info->damage, actor->scale));
-		}
-		default: // from our compiler appeasement program (CAP).
-			break;
-		}
-	}
-}
-
-// Function: A_RockSpawn
-//
-// Spawns rocks at a specified interval
-//
-// var1 = unused
-// var2 = unused
-void A_RockSpawn(mobj_t *actor)
-{
-	mobj_t *mo;
-	mobjtype_t type;
-	INT32 i = P_FindSpecialLineFromTag(12, (INT16)actor->threshold, -1);
-	line_t *line;
-	fixed_t dist;
-	fixed_t randomoomph;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_RockSpawn", actor))
-		return;
-#endif
-
-	if (i == -1)
-	{
-		CONS_Debug(DBG_GAMELOGIC, "A_RockSpawn: Unable to find parameter line 12 (tag %d)!\n", actor->threshold);
-		return;
-	}
-
-	line = &lines[i];
-
-	if (!(sides[line->sidenum[0]].textureoffset >> FRACBITS))
-	{
-		CONS_Debug(DBG_GAMELOGIC, "A_RockSpawn: No X-offset detected! (tag %d)!\n", actor->threshold);
-		return;
-	}
-
-	dist = P_AproxDistance(line->dx, line->dy)/16;
-
-	if (dist < 1)
-		dist = 1;
-
-	type = MT_ROCKCRUMBLE1 + (sides[line->sidenum[0]].rowoffset >> FRACBITS);
-
-	if (line->flags & ML_NOCLIMB)
-		randomoomph = P_RandomByte() * (FRACUNIT/32);
-	else
-		randomoomph = 0;
-
-	mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_FALLINGROCK);
-	P_SetMobjState(mo, mobjinfo[type].spawnstate);
-	mo->angle = R_PointToAngle2(line->v2->x, line->v2->y, line->v1->x, line->v1->y);
-
-	P_InstaThrust(mo, mo->angle, dist + randomoomph);
-	mo->momz = dist + randomoomph;
-
-	var1 = sides[line->sidenum[0]].textureoffset >> FRACBITS;
-	A_SetTics(actor);
-}
-
-//
-// Function: A_SlingAppear
-//
-// Appears a sling.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_SlingAppear(mobj_t *actor)
-{
-	boolean firsttime = true;
-	UINT8 mlength = 4;
-	mobj_t *spawnee;
-	mobj_t *hprev = actor;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SlingAppear", actor))
-		return;
-#endif
-
-	P_UnsetThingPosition(actor);
-	actor->flags &= ~(MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_NOCLIPHEIGHT);
-	P_SetThingPosition(actor);
-	actor->lastlook = 128;
-	actor->movecount = actor->lastlook;
-	actor->threshold = 0;
-	actor->movefactor = actor->threshold;
-	actor->friction = 128;
-
-	while (mlength > 0)
-	{
-		spawnee = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMALLMACECHAIN);
-
-		P_SetTarget(&spawnee->tracer, actor);
-		P_SetTarget(&spawnee->hprev, hprev);
-		P_SetTarget(&hprev->hnext, spawnee);
-		hprev = spawnee;
-
-		spawnee->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT;
-		spawnee->movecount = mlength;
-
-		if (firsttime)
-		{
-			// This is the outermost link in the chain
-			spawnee->flags2 |= MF2_AMBUSH;
-			firsttime = false;
-		}
-
-		mlength--;
-	}
-}
-
-// Function: A_SetFuse
-//
-// Description: Sets the actor's fuse timer if not set already. May also change state when fuse reaches the last tic, otherwise by default the actor will die or disappear. (Replaces A_SnowBall)
-//
-// var1 = fuse timer duration (in tics).
-// var2:
-//		lower 16 bits = if > 0, state to change to when fuse = 1
-//		upper 16 bits: 0 = (default) don't set fuse unless 0, 1 = force change, 2 = force no change
-//
-void A_SetFuse(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SetFuse", actor))
-		return;
-#endif
-
-	if ((!actor->fuse || (locvar2 >> 16)) && (locvar2 >> 16) != 2) // set the actor's fuse value
-		actor->fuse = locvar1;
-
-	if (actor->fuse == 1 && (locvar2 & 65535)) // change state on the very last tic (fuse is handled before actions in P_MobjThinker)
-	{
-		actor->fuse = 0; // don't die/disappear the next tic!
-		P_SetMobjState(actor, locvar2 & 65535);
-	}
-}
-
-// Function: A_CrawlaCommanderThink
-//
-// Description: Thinker for Crawla Commander.
-//
-// var1 = shoot bullets?
-// var2 = "pogo mode" speed
-//
-void A_CrawlaCommanderThink(mobj_t *actor)
-{
-	fixed_t dist;
-	sector_t *nextsector;
-	fixed_t thefloor;
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_CrawlaCommanderThink", actor))
-		return;
-#endif
-
-	if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz
-		&& actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale))
-		thefloor = actor->watertop;
-	else
-		thefloor = actor->floorz;
-
-	if (actor->fuse & 1)
-		actor->flags2 |= MF2_DONTDRAW;
-	else
-		actor->flags2 &= ~MF2_DONTDRAW;
-
-	if (actor->reactiontime > 0)
-		actor->reactiontime--;
-
-	if (actor->fuse < 2)
-	{
-		actor->fuse = 0;
-		actor->flags2 &= ~MF2_FRET;
-	}
-
-	// Hover mode
-	if (actor->health > 1 || actor->fuse)
-	{
-		if (actor->z < thefloor + FixedMul(16*FRACUNIT, actor->scale))
-			actor->momz += FixedMul(FRACUNIT, actor->scale);
-		else if (actor->z < thefloor + FixedMul(32*FRACUNIT, actor->scale))
-			actor->momz += FixedMul(FRACUNIT/2, actor->scale);
-		else
-			actor->momz += FixedMul(16, actor->scale);
-	}
-
-	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
-	{
-		// look for a new target
-		if (P_LookForPlayers(actor, true, false, 0))
-			return; // got a new target
-
-		if (actor->state != &states[actor->info->spawnstate])
-			P_SetMobjState(actor, actor->info->spawnstate);
-		return;
-	}
-
-	dist = P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y);
-
-	if (actor->target->player && actor->health > 1)
-	{
-		if (dist < FixedMul(128*FRACUNIT, actor->scale)
-			&& ((actor->target->player->pflags & PF_JUMPED) || (actor->target->player->pflags & PF_SPINNING)))
-		{
-			// Auugh! He's trying to kill you! Strafe! STRAAAAFFEEE!!
-			if (actor->target->momx || actor->target->momy)
-				P_InstaThrust(actor, actor->angle - ANGLE_180, FixedMul(20*FRACUNIT, actor->scale));
-			return;
-		}
-	}
-
-	if (locvar1)
-	{
-		if (actor->health < 2 && P_RandomChance(FRACUNIT/128))
-			P_SpawnMissile(actor, actor->target, locvar1);
-	}
-
-	// Face the player
-	actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
-
-	if (actor->threshold && dist > FixedMul(256*FRACUNIT, actor->scale))
-		actor->momx = actor->momy = 0;
-
-	if (actor->reactiontime && actor->reactiontime <= 2*TICRATE && dist > actor->target->radius - FixedMul(FRACUNIT, actor->scale))
-	{
-		actor->threshold = 0;
-
-		// Roam around, somewhat in the player's direction.
-		actor->angle += (P_RandomByte()<<10);
-		actor->angle -= (P_RandomByte()<<10);
-
-		if (actor->health > 1)
-			P_InstaThrust(actor, actor->angle, FixedMul(10*FRACUNIT, actor->scale));
-	}
-	else if (!actor->reactiontime)
-	{
-		if (actor->health > 1) // Hover Mode
-		{
-			if (dist < FixedMul(512*FRACUNIT, actor->scale))
-			{
-				actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
-				P_InstaThrust(actor, actor->angle, FixedMul(60*FRACUNIT, actor->scale));
-				actor->threshold = 1;
-			}
-		}
-		actor->reactiontime = 2*TICRATE + P_RandomByte()/2;
-	}
-
-	if (actor->health == 1)
-		P_Thrust(actor, actor->angle, 1);
-
-	// Pogo Mode
-	if (!actor->fuse && actor->health == 1 && actor->z <= actor->floorz)
-	{
-		if (dist < FixedMul(256*FRACUNIT, actor->scale))
-		{
-			actor->momz = FixedMul(locvar2, actor->scale);
-			actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
-			P_InstaThrust(actor, actor->angle, FixedMul(locvar2/8, actor->scale));
-			// pogo on player
-		}
-		else
-		{
-			UINT8 prandom = P_RandomByte();
-			actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom);
-			P_InstaThrust(actor, actor->angle, FixedDiv(FixedMul(locvar2, actor->scale), 3*FRACUNIT/2));
-			actor->momz = FixedMul(locvar2, actor->scale); // Bounce up in air
-		}
-	}
-
-	nextsector = R_PointInSubsector(actor->x + actor->momx, actor->y + actor->momy)->sector;
-
-	// Move downwards or upwards to go through a passageway.
-	if (nextsector->floorheight > actor->z && nextsector->floorheight - actor->z < FixedMul(128*FRACUNIT, actor->scale))
-		actor->momz += (nextsector->floorheight - actor->z) / 4;
-}
-
-// Function: A_RingExplode
-//
-// Description: An explosion ring exploding
-//
-// var1 = unused
-// var2 = unused
-//
-void A_RingExplode(mobj_t *actor)
-{
-	mobj_t *mo2;
-	thinker_t *th;
-	angle_t d;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_RingExplode", actor))
-		return;
-#endif
-
-	for (d = 0; d < 16; d++)
-		P_SpawnParaloop(actor->x, actor->y, actor->z + actor->height, FixedMul(actor->info->painchance, actor->scale), 16, MT_NIGHTSPARKLE, S_NULL, d*(ANGLE_22h), true);
-
-	S_StartSound(actor, sfx_prloop);
-
-	for (th = thinkercap.next; th != &thinkercap; th = th->next)
-	{
-		if (th->function.acp1 != (actionf_p1)P_MobjThinker)
-			continue;
-
-		mo2 = (mobj_t *)th;
-
-		if (mo2 == actor) // Don't explode yourself! Endless loop!
-			continue;
-
-		if (P_AproxDistance(P_AproxDistance(mo2->x - actor->x, mo2->y - actor->y), mo2->z - actor->z) > FixedMul(actor->info->painchance, actor->scale))
-			continue;
-
-		if (mo2->flags & MF_SHOOTABLE)
-		{
-			actor->flags2 |= MF2_DEBRIS;
-			P_DamageMobj(mo2, actor, actor->target, 1, 0);
-			continue;
-		}
-	}
-	return;
-}
-
-// Function: A_OldRingExplode
-//
-// Description: An explosion ring exploding, 1.09.4 style
-//
-// var1 = object # to explode as debris
-// var2 = unused
-//
-void A_OldRingExplode(mobj_t *actor) {
-	UINT8 i;
-	mobj_t *mo;
-	const fixed_t ns = FixedMul(20 * FRACUNIT, actor->scale);
-	INT32 locvar1 = var1;
-	//INT32 locvar2 = var2;
-	boolean changecolor = (actor->target && actor->target->player);
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_OldRingExplode", actor))
-		return;
-#endif
-
-	for (i = 0; i < 32; i++)
-	{
-		const angle_t fa = (i*FINEANGLES/16) & FINEMASK;
-
-		mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1);
-		P_SetTarget(&mo->target, actor->target); // Transfer target so player gets the points
-
-		mo->momx = FixedMul(FINECOSINE(fa),ns);
-		mo->momy = FixedMul(FINESINE(fa),ns);
-
-		if (i > 15)
-		{
-			if (i & 1)
-				mo->momz = ns;
-			else
-				mo->momz = -ns;
-		}
-
-		mo->flags2 |= MF2_DEBRIS;
-		mo->fuse = TICRATE/5;
-
-		if (changecolor)
-		{
-			if (gametype != GT_CTF)
-				mo->color = actor->target->color; //copy color
-			else if (actor->target->player->ctfteam == 2)
-				mo->color = skincolor_bluering;
-		}
-	}
-
-	mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1);
-
-	P_SetTarget(&mo->target, actor->target);
-	mo->momz = ns;
-	mo->flags2 |= MF2_DEBRIS;
-	mo->fuse = TICRATE/5;
-
-	if (changecolor)
-	{
-		if (gametype != GT_CTF)
-			mo->color = actor->target->color; //copy color
-		else if (actor->target->player->ctfteam == 2)
-			mo->color = skincolor_bluering;
-	}
-
-	mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1);
-
-	P_SetTarget(&mo->target, actor->target);
-	mo->momz = -ns;
-	mo->flags2 |= MF2_DEBRIS;
-	mo->fuse = TICRATE/5;
-
-	if (changecolor)
-	{
-		if (gametype != GT_CTF)
-			mo->color = actor->target->color; //copy color
-		else if (actor->target->player->ctfteam == 2)
-			mo->color = skincolor_bluering;
-	}
-}
-
-// Function: A_MixUp
-//
-// Description: Mix up all of the player positions.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_MixUp(mobj_t *actor)
-{
-	boolean teleported[MAXPLAYERS];
-	INT32 i, numplayers = 0, prandom = 0;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_MixUp", actor))
-		return;
-#else
-	(void)actor;
-#endif
-
-	if (!multiplayer)
-		return;
-
-	// No mix-up monitors in hide and seek or time only race.
-	// The random factor is okay for other game modes, but in these, it is cripplingly unfair.
-	if (gametype == GT_HIDEANDSEEK || gametype == GT_RACE)
-	{
-		S_StartSound(actor, sfx_lose);
-		return;
-	}
-
-	numplayers = 0;
-	memset(teleported, 0, sizeof (teleported));
-
-	// Count the number of players in the game
-	// and grab their xyz coords
-	for (i = 0; i < MAXPLAYERS; i++)
-		if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE
-			&& !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE)
-		{
-			if ((netgame || multiplayer) && players[i].spectator) // Ignore spectators
-				continue;
-
-			numplayers++;
-		}
-
-	if (numplayers <= 1) // Not enough players to mix up.
-	{
-		S_StartSound(actor, sfx_lose);
-		return;
-	}
-	else if (numplayers == 2) // Special case -- simple swap
-	{
-		fixed_t x, y, z;
-		angle_t angle;
-		INT32 one = -1, two = 0; // default value 0 to make the compiler shut up
-
-		// Zoom tube stuff
-		mobj_t *tempthing = NULL; //tracer
-		UINT16 carry1,carry2;     //carry
-		INT32 transspeed;         //player speed
-
-		// Starpost stuff
-		INT16 starpostx, starposty, starpostz;
-		INT32 starpostnum;
-		tic_t starposttime;
-		angle_t starpostangle;
-
-		INT32 mflags2;
-
-		for (i = 0; i < MAXPLAYERS; i++)
-			if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE
-				&& !players[i].exiting && !players[i].powers[pw_super])
-			{
-				if ((netgame || multiplayer) && players[i].spectator) // Ignore spectators
-					continue;
-
-				if (one == -1)
-					one = i;
-				else
-				{
-					two = i;
-					break;
-				}
-			}
-
-		//get this done first!
-		tempthing = players[one].mo->tracer;
-		P_SetTarget(&players[one].mo->tracer, players[two].mo->tracer);
-		P_SetTarget(&players[two].mo->tracer, tempthing);
-
-		//zoom tubes use player->speed to determine direction and speed
-		transspeed = players[one].speed;
-		players[one].speed = players[two].speed;
-		players[two].speed = transspeed;
-
-		//set flags variables now but DON'T set them.
-		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;
-		z = players[one].mo->z;
-		angle = players[one].mo->angle;
-
-		starpostx = players[one].starpostx;
-		starposty = players[one].starposty;
-		starpostz = players[one].starpostz;
-		starpostangle = players[one].starpostangle;
-		starpostnum = players[one].starpostnum;
-		starposttime = players[one].starposttime;
-
-		mflags2 = players[one].mo->flags2;
-
-		P_MixUp(players[one].mo, players[two].mo->x, players[two].mo->y, players[two].mo->z, players[two].mo->angle,
-				players[two].starpostx, players[two].starposty, players[two].starpostz,
-				players[two].starpostnum, players[two].starposttime, players[two].starpostangle,
-				players[two].mo->flags2);
-
-		P_MixUp(players[two].mo, x, y, z, angle, starpostx, starposty, starpostz,
-				starpostnum, starposttime, starpostangle,
-				mflags2);
-
-		//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;
-	}
-	else
-	{
-		fixed_t position[MAXPLAYERS][3];
-		angle_t anglepos[MAXPLAYERS];
-		INT32 pindex[MAXPLAYERS], counter = 0, teleportfrom = 0;
-
-		// Zoom tube stuff
-		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
-		INT16 spposition[MAXPLAYERS][3];
-		INT32 starpostnum[MAXPLAYERS];
-		tic_t starposttime[MAXPLAYERS];
-		angle_t starpostangle[MAXPLAYERS];
-
-		INT32 flags2[MAXPLAYERS];
-
-		for (i = 0; i < MAXPLAYERS; i++)
-		{
-			position[i][0] = position[i][1] = position[i][2] = anglepos[i] = pindex[i] = -1;
-			teleported[i] = false;
-		}
-
-		for (i = 0; i < MAXPLAYERS; i++)
-		{
-			if (playeringame[i] && players[i].playerstate == PST_LIVE
-				&& players[i].mo && players[i].mo->health > 0 && !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE)
-			{
-				if ((netgame || multiplayer) && players[i].spectator)// Ignore spectators
-					continue;
-
-				position[counter][0] = players[i].mo->x;
-				position[counter][1] = players[i].mo->y;
-				position[counter][2] = players[i].mo->z;
-				pindex[counter] = i;
-				anglepos[counter] = players[i].mo->angle;
-				players[i].mo->momx = players[i].mo->momy = players[i].mo->momz =
-					players[i].rmomx = players[i].rmomy = 1;
-				players[i].cmomx = players[i].cmomy = 0;
-
-				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;
-
-				spposition[counter][0] = players[i].starpostx;
-				spposition[counter][1] = players[i].starposty;
-				spposition[counter][2] = players[i].starpostz;
-				starpostnum[counter] = players[i].starpostnum;
-				starposttime[counter] = players[i].starposttime;
-				starpostangle[counter] = players[i].starpostangle;
-
-				flags2[counter] = players[i].mo->flags2;
-
-				counter++;
-			}
-		}
-
-		counter = 0;
-
-		// Mix them up!
-		for (;;)
-		{
-			if (counter > 255) // fail-safe to avoid endless loop
-				break;
-			prandom = P_RandomByte();
-			prandom %= numplayers; // I love modular arithmetic, don't you?
-			if (prandom) // Make sure it's not a useless mix
-				break;
-			counter++;
-		}
-
-		counter = 0;
-
-		for (i = 0; i < MAXPLAYERS; i++)
-		{
-			if (playeringame[i] && players[i].playerstate == PST_LIVE
-				&& players[i].mo && players[i].mo->health > 0 && !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE)
-			{
-				if ((netgame || multiplayer) && players[i].spectator)// Ignore spectators
-					continue;
-
-				teleportfrom = (counter + prandom) % numplayers;
-
-				//speed and tracer come before...
-				players[i].speed = transspeed[teleportfrom];
-				P_SetTarget(&players[i].mo->tracer, transtracer[teleportfrom]);
-
-				P_MixUp(players[i].mo, position[teleportfrom][0], position[teleportfrom][1], position[teleportfrom][2], anglepos[teleportfrom],
-					spposition[teleportfrom][0], spposition[teleportfrom][1], spposition[teleportfrom][2],
-					starpostnum[teleportfrom], starposttime[teleportfrom], starpostangle[teleportfrom],
-					flags2[teleportfrom]);
-
-				//...carry after.  same reasoning.
-				players[i].powers[pw_carry] = transcarry[teleportfrom];
-
-				teleported[i] = true;
-				counter++;
-			}
-		}
-	}
-
-	for (i = 0; i < MAXPLAYERS; i++)
-	{
-		if (teleported[i])
-		{
-			if (playeringame[i] && players[i].playerstate == PST_LIVE
-				&& players[i].mo && players[i].mo->health > 0 && !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE)
-			{
-				if ((netgame || multiplayer) && players[i].spectator)// Ignore spectators
-					continue;
-
-				P_SetThingPosition(players[i].mo);
-
-#ifdef ESLOPE
-				players[i].mo->floorz = P_GetFloorZ(players[i].mo, players[i].mo->subsector->sector, players[i].mo->x, players[i].mo->y, NULL);
-				players[i].mo->ceilingz = P_GetCeilingZ(players[i].mo, players[i].mo->subsector->sector, players[i].mo->x, players[i].mo->y, NULL);
-#else
-				players[i].mo->floorz = players[i].mo->subsector->sector->floorheight;
-				players[i].mo->ceilingz = players[i].mo->subsector->sector->ceilingheight;
-#endif
-
-				P_CheckPosition(players[i].mo, players[i].mo->x, players[i].mo->y);
-			}
-		}
-	}
-
-	// Play the 'bowrwoosh!' sound
-	S_StartSound(NULL, sfx_mixup);
-}
-
-// Function: A_RecyclePowers
-//
-// Description: Take all player's powers, and swap 'em.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_RecyclePowers(mobj_t *actor)
-{
-	INT32 i, j, k, numplayers = 0;
-
-#ifdef WEIGHTEDRECYCLER
-	UINT8 beneficiary = 255;
-#endif
-	UINT8 playerslist[MAXPLAYERS];
-	UINT8 postscramble[MAXPLAYERS];
-
-	UINT16 powers[MAXPLAYERS][NUMPOWERS];
-	INT32 weapons[MAXPLAYERS];
-	INT32 weaponheld[MAXPLAYERS];
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_RecyclePowers", actor))
-		return;
-#endif
-
-#if !defined(WEIGHTEDRECYCLER) && !defined(HAVE_BLUA)
-	// actor is used in all scenarios but this one, funny enough
-	(void)actor;
-#endif
-
-	if (!multiplayer)
-	{
-		S_StartSound(actor, sfx_lose);
-		return;
-	}
-
-	numplayers = 0;
-
-	// Count the number of players in the game
-	for (i = 0, j = 0; i < MAXPLAYERS; i++)
-	{
-		if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE
-			&& !players[i].exiting && !((netgame || multiplayer) && players[i].spectator))
-		{
-#ifndef WEIGHTEDRECYCLER
-			if (players[i].powers[pw_super])
-				continue; // Ignore super players
-#endif
-
-			numplayers++;
-			postscramble[j] = playerslist[j] = (UINT8)i;
-
-#ifdef WEIGHTEDRECYCLER
-			// The guy who started the recycle gets the best result
-			if (actor && actor->target && actor->target->player && &players[i] == actor->target->player)
-				beneficiary = (UINT8)i;
-#endif
-
-			// Save powers
-			for (k = 0; k < NUMPOWERS; k++)
-				powers[i][k] = players[i].powers[k];
-			//1.1: ring weapons too
-			weapons[i] = players[i].ringweapons;
-			weaponheld[i] = players[i].currentweapon;
-
-			j++;
-		}
-	}
-
-	if (numplayers <= 1)
-	{
-		S_StartSound(actor, sfx_lose);
-		return; //nobody to touch!
-	}
-
-	//shuffle the post scramble list, whee!
-	// hardcoded 0-1 to 1-0 for two players
-	if (numplayers == 2)
-	{
-		postscramble[0] = playerslist[1];
-		postscramble[1] = playerslist[0];
-	}
-	else
-	for (j = 0; j < numplayers; j++)
-	{
-		UINT8 tempint;
-
-		i = j + ((P_RandomByte() + leveltime) % (numplayers - j));
-		tempint = postscramble[j];
-		postscramble[j] = postscramble[i];
-		postscramble[i] = tempint;
-	}
-
-#ifdef WEIGHTEDRECYCLER
-	//the joys of qsort...
-	if (beneficiary != 255) {
-		qsort(playerslist, numplayers, sizeof(UINT8), P_RecycleCompare);
-
-		// now, make sure the benificiary is in the best slot
-		// swap out whatever poor sap was going to get the best items
-		for (i = 0; i < numplayers; i++)
-		{
-			if (postscramble[i] == beneficiary)
-			{
-				postscramble[i] = postscramble[0];
-				postscramble[0] = beneficiary;
-				break;
-			}
-		}
-	}
-#endif
-
-	// now assign!
-	for (i = 0; i < numplayers; i++)
-	{
-		UINT8 send_pl = playerslist[i];
-		UINT8 recv_pl = postscramble[i];
-
-		// debugF
-		CONS_Debug(DBG_GAMELOGIC, "sending player %hu's items to %hu\n", (UINT16)send_pl, (UINT16)recv_pl);
-
-		for (j = 0; j < NUMPOWERS; j++)
-		{
-			if (j == pw_flashing || j == pw_underwater || j == pw_spacetime || j == pw_carry
-			    || j == pw_tailsfly || j == pw_extralife || j == pw_nocontrol || j == pw_super)
-				continue;
-			players[recv_pl].powers[j] = powers[send_pl][j];
-		}
-
-		//1.1: weapon rings too
-		players[recv_pl].ringweapons = weapons[send_pl];
-		players[recv_pl].currentweapon = weaponheld[send_pl];
-
-		P_SpawnShieldOrb(&players[recv_pl]);
-		if (P_IsLocalPlayer(&players[recv_pl]))
-			P_RestoreMusic(&players[recv_pl]);
-		P_FlashPal(&players[recv_pl], PAL_RECYCLE, 10);
-	}
-
-	S_StartSound(NULL, sfx_gravch); //heh, the sound effect I used is already in
-}
-
-// Function: A_Boss1Chase
-//
-// Description: Like A_Chase, but for Boss 1.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_Boss1Chase(mobj_t *actor)
-{
-	INT32 delta;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_Boss1Chase", actor))
-		return;
-#endif
-
-	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
-	{
-		// look for a new target
-		if (P_LookForPlayers(actor, true, false, 0))
-			return; // got a new target
-
-		P_SetMobjStateNF(actor, actor->info->spawnstate);
-		return;
-	}
-
-	if (actor->reactiontime)
-		actor->reactiontime--;
-
-	// turn towards movement direction if not there yet
-	if (actor->movedir < NUMDIRS)
-	{
-		actor->angle &= (7<<29);
-		delta = actor->angle - (actor->movedir << 29);
-
-		if (delta > 0)
-			actor->angle -= ANGLE_45;
-		else if (delta < 0)
-			actor->angle += ANGLE_45;
-	}
-
-	// do not attack twice in a row
-	if (actor->flags2 & MF2_JUSTATTACKED)
-	{
-		actor->flags2 &= ~MF2_JUSTATTACKED;
-		P_NewChaseDir(actor);
-		return;
-	}
-
-	if (actor->movecount)
-		goto nomissile;
-
-	if (!P_CheckMissileRange(actor))
-		goto nomissile;
-
-	if (actor->reactiontime <= 0)
-	{
-		if (actor->health > actor->info->damage)
-		{
-			if (P_RandomChance(FRACUNIT/2))
-				P_SetMobjState(actor, actor->info->missilestate);
-			else
-				P_SetMobjState(actor, actor->info->meleestate);
-		}
-		else
-		{
-			P_LinedefExecute(LE_PINCHPHASE, actor, NULL);
-			P_SetMobjState(actor, actor->info->raisestate);
-		}
-
-		actor->flags2 |= MF2_JUSTATTACKED;
-		actor->reactiontime = actor->info->reactiontime;
-		return;
-	}
-
-	// ?
-nomissile:
-	// possibly choose another target
-	if (multiplayer && P_RandomChance(FRACUNIT/128))
-	{
-		if (P_LookForPlayers(actor, true, false, 0))
-			return; // got a new target
-	}
-
-	if (actor->flags & MF_FLOAT && !(actor->flags2 & MF2_SKULLFLY))
-	{ // Float up/down to your target's position. Stay above them, but not out of jump range.
-		fixed_t target_min = actor->target->floorz+FixedMul(64*FRACUNIT, actor->scale);
-		if (target_min < actor->target->z - actor->height)
-			target_min = actor->target->z - actor->height;
-		if (target_min < actor->floorz+FixedMul(33*FRACUNIT, actor->scale))
-			target_min = actor->floorz+FixedMul(33*FRACUNIT, actor->scale);
-		if (actor->z > target_min+FixedMul(16*FRACUNIT, actor->scale))
-			actor->momz = FixedMul((-actor->info->speed<<(FRACBITS-1)), actor->scale);
-		else if (actor->z < target_min)
-			actor->momz = FixedMul(actor->info->speed<<(FRACBITS-1), actor->scale);
-		else
-			actor->momz = FixedMul(actor->momz,7*FRACUNIT/8);
-	}
-
-	// chase towards player
-	if (P_AproxDistance(actor->target->x-actor->x, actor->target->y-actor->y) > actor->radius+actor->target->radius)
-	{
-		if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
-			P_NewChaseDir(actor);
-	}
-	// too close, don't want to chase.
-	else if (--actor->movecount < 0)
-	{
-		// A mini-A_FaceTarget based on P_NewChaseDir.
-		// Yes, it really is this simple when you get down to it.
-		fixed_t deltax, deltay;
-
-		deltax = actor->target->x - actor->x;
-		deltay = actor->target->y - actor->y;
-
-		actor->movedir = diags[((deltay < 0)<<1) + (deltax > 0)];
-		actor->movecount = P_RandomByte() & 15;
-	}
-}
-
-// Function: A_Boss2Chase
-//
-// Description: Really doesn't 'chase', but rather goes in a circle.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_Boss2Chase(mobj_t *actor)
-{
-	fixed_t radius;
-	boolean reverse = false;
-	INT32 speedvar;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_Boss2Chase", actor))
-		return;
-#endif
-
-	if (actor->health <= 0)
-		return;
-
-	// Startup randomness
-	if (actor->reactiontime <= -666)
-		actor->reactiontime = 2*TICRATE + P_RandomByte();
-
-	// When reactiontime hits zero, he will go the other way
-	if (--actor->reactiontime <= 0)
-	{
-		reverse = true;
-		actor->reactiontime = 2*TICRATE + P_RandomByte();
-	}
-
-	P_SetTarget(&actor->target, P_GetClosestAxis(actor));
-
-	if (!actor->target) // This should NEVER happen.
-	{
-		CONS_Debug(DBG_GAMELOGIC, "Boss2 has no target!\n");
-		A_BossDeath(actor);
-		return;
-	}
-
-	radius = actor->target->radius;
-
-	if (reverse)
-	{
-		actor->watertop = -actor->watertop;
-		actor->extravalue1 = 18;
-		if (actor->flags2 & MF2_AMBUSH)
-			actor->extravalue1 -= (actor->info->spawnhealth - actor->health)*2;
-		actor->extravalue2 = actor->extravalue1;
-	}
-
-	// Turnaround
-	if (actor->extravalue1 > 0)
-	{
-		--actor->extravalue1;
-
-		// Set base angle
-		{
-			const angle_t fa = (actor->target->angle + FixedAngle(actor->watertop))>>ANGLETOFINESHIFT;
-			const fixed_t fc = FixedMul(FINECOSINE(fa),radius);
-			const fixed_t fs = FixedMul(FINESINE(fa),radius);
-			actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x + fc, actor->target->y + fs);
-		}
-
-		// Now turn you around!
-		// Note that the start position is the final position, we move it back around
-		// to intermediary positions...
-		actor->angle -= FixedAngle(FixedMul(FixedDiv(180<<FRACBITS, actor->extravalue2<<FRACBITS), actor->extravalue1<<FRACBITS));
-	}
-	else
-	{
-		// Only speed up if you have the 'Deaf' flag.
-		if (actor->flags2 & MF2_AMBUSH)
-			speedvar = actor->health;
-		else
-			speedvar = actor->info->spawnhealth;
-
-		actor->target->angle += // Don't use FixedAngleC!
-			FixedAngle(FixedDiv(FixedMul(actor->watertop, (actor->info->spawnhealth*(FRACUNIT/4)*3)), speedvar*FRACUNIT));
-
-		P_UnsetThingPosition(actor);
-		{
-			const angle_t fa = actor->target->angle>>ANGLETOFINESHIFT;
-			const fixed_t fc = FixedMul(FINECOSINE(fa),radius);
-			const fixed_t fs = FixedMul(FINESINE(fa),radius);
-			actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x + fc, actor->target->y + fs);
-			actor->x = actor->target->x + fc;
-			actor->y = actor->target->y + fs;
-		}
-		P_SetThingPosition(actor);
-
-		// Spray goo once every second
-		if (leveltime % (speedvar*15/10)-1 == 0)
-		{
-			const fixed_t ns = FixedMul(3 * FRACUNIT, actor->scale);
-			mobj_t *goop;
-			fixed_t fz = actor->z+actor->height+FixedMul(24*FRACUNIT, actor->scale);
-			angle_t fa;
-			// actor->movedir is used to determine the last
-			// direction goo was sprayed in. There are 8 possible
-			// directions to spray. (45-degree increments)
-
-			actor->movedir++;
-			actor->movedir %= NUMDIRS;
-			fa = (actor->movedir*FINEANGLES/8) & FINEMASK;
-
-			goop = P_SpawnMobj(actor->x, actor->y, fz, actor->info->painchance);
-			goop->momx = FixedMul(FINECOSINE(fa),ns);
-			goop->momy = FixedMul(FINESINE(fa),ns);
-			goop->momz = FixedMul(4*FRACUNIT, actor->scale);
-			goop->fuse = 10*TICRATE;
-
-			if (actor->info->attacksound)
-				S_StartAttackSound(actor, actor->info->attacksound);
-
-			if (P_RandomChance(FRACUNIT/2))
-			{
-				goop->momx *= 2;
-				goop->momy *= 2;
-			}
-			else if (P_RandomChance(129*FRACUNIT/256))
-			{
-				goop->momx *= 3;
-				goop->momy *= 3;
-			}
-
-			actor->flags2 |= MF2_JUSTATTACKED;
-		}
-	}
-}
-
-// Function: A_Boss2Pogo
-//
-// Description: Pogo part of Boss 2 AI.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_Boss2Pogo(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_Boss2Pogo", actor))
-		return;
-#endif
-	if (actor->z <= actor->floorz + FixedMul(8*FRACUNIT, actor->scale) && actor->momz <= 0)
-	{
-		if (actor->state != &states[actor->info->raisestate])
-			P_SetMobjState(actor, actor->info->raisestate);
-		// Pogo Mode
-	}
-	else if (actor->momz < 0 && actor->reactiontime)
-	{
-		const fixed_t ns = FixedMul(3 * FRACUNIT, actor->scale);
-		mobj_t *goop;
-		fixed_t fz = actor->z+actor->height+FixedMul(24*FRACUNIT, actor->scale);
-		angle_t fa;
-		INT32 i;
-		// spray in all 8 directions!
-		for (i = 0; i < 8; i++)
-		{
-			actor->movedir++;
-			actor->movedir %= NUMDIRS;
-			fa = (actor->movedir*FINEANGLES/8) & FINEMASK;
-
-			goop = P_SpawnMobj(actor->x, actor->y, fz, actor->info->painchance);
-			goop->momx = FixedMul(FINECOSINE(fa),ns);
-			goop->momy = FixedMul(FINESINE(fa),ns);
-			goop->momz = FixedMul(4*FRACUNIT, actor->scale);
-
-			goop->fuse = 10*TICRATE;
-		}
-		actor->reactiontime = 0; // we already shot goop, so don't do it again!
-		if (actor->info->attacksound)
-			S_StartAttackSound(actor, actor->info->attacksound);
-		actor->flags2 |= MF2_JUSTATTACKED;
-	}
-}
-
-// Function: A_Boss2TakeDamage
-//
-// Description: Special function for Boss 2 so you can't just sit and destroy him.
-//
-// var1 = Invincibility duration
-// var2 = unused
-//
-void A_Boss2TakeDamage(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_Boss2TakeDamage", actor))
-		return;
-#endif
-	A_Pain(actor);
-	actor->reactiontime = 1; // turn around
-	if (locvar1 == 0) // old A_Invincibilerize behavior
-		actor->movecount = TICRATE;
-	else
-		actor->movecount = locvar1; // become flashing invulnerable for this long.
-}
-
-// Function: A_Boss7Chase
-//
-// Description: Like A_Chase, but for Black Eggman
-//
-// var1 = unused
-// var2 = unused
-//
-void A_Boss7Chase(mobj_t *actor)
-{
-	INT32 delta;
-	INT32 i;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_Boss7Chase", actor))
-		return;
-#endif
-
-	if (actor->z != actor->floorz)
-		return;
-
-	// Self-adjust if stuck on the edge
-	if (actor->tracer)
-	{
-		if (P_AproxDistance(actor->x - actor->tracer->x, actor->y - actor->tracer->y) > 128*FRACUNIT - actor->radius)
-			P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y), FRACUNIT);
-	}
-
-	if (actor->flags2 & MF2_FRET)
-	{
-		P_SetMobjState(actor, S_BLACKEGG_DESTROYPLAT1);
-		S_StartSound(0, sfx_s3k53);
-		actor->flags2 &= ~MF2_FRET;
-		return;
-	}
-
-	// turn towards movement direction if not there yet
-	if (actor->movedir < NUMDIRS)
-	{
-		actor->angle &= (7<<29);
-		delta = actor->angle - (actor->movedir << 29);
-
-		if (delta > 0)
-			actor->angle -= ANGLE_45;
-		else if (delta < 0)
-			actor->angle += ANGLE_45;
-	}
-
-	// Is a player on top of us?
-	for (i = 0; i < MAXPLAYERS; i++)
-	{
-		if (!playeringame[i] || players[i].spectator)
-			continue;
-
-		if (!players[i].mo)
-			continue;
-
-		if (players[i].mo->health <= 0)
-			continue;
-
-		if (P_AproxDistance(players[i].mo->x - actor->x, players[i].mo->y - actor->y) > actor->radius)
-			continue;
-
-		if (players[i].mo->z > actor->z + actor->height - 2*FRACUNIT
-			&& players[i].mo->z < actor->z + actor->height + 32*FRACUNIT)
-		{
-			// Punch him!
-			P_SetMobjState(actor, actor->info->meleestate);
-			S_StartSound(0, sfx_begrnd); // warning sound
-			return;
-		}
-	}
-
-	if (actor->health <= actor->info->damage
-		&& actor->target
-		&& actor->target->player
-		&& (actor->target->player->powers[pw_carry] == CR_GENERIC))
-	{
-		A_FaceTarget(actor);
-		P_SetMobjState(actor, S_BLACKEGG_SHOOT1);
-		actor->movecount = TICRATE + P_RandomByte()/2;
-		return;
-	}
-
-	if (actor->reactiontime)
-		actor->reactiontime--;
-
-	if (actor->reactiontime <= 0 && actor->z == actor->floorz)
-	{
-		// Here, we'll call P_RandomByte() and decide what kind of attack to do
-		switch(actor->threshold)
-		{
-			case 0: // Lob cannon balls
-				if (actor->z < 1056*FRACUNIT)
-				{
-					A_FaceTarget(actor);
-					P_SetMobjState(actor, actor->info->xdeathstate);
-					actor->movecount = 7*TICRATE + P_RandomByte();
-					break;
-				}
-				actor->threshold++;
-				/* FALLTHRU */
-			case 1: // Chaingun Goop
-				A_FaceTarget(actor);
-				P_SetMobjState(actor, S_BLACKEGG_SHOOT1);
-
-				if (actor->health > actor->info->damage)
-					actor->movecount = TICRATE + P_RandomByte()/3;
-				else
-					actor->movecount = TICRATE + P_RandomByte()/2;
-				break;
-			case 2: // Homing Missile
-				A_FaceTarget(actor);
-				P_SetMobjState(actor, actor->info->missilestate);
-				S_StartSound(0, sfx_beflap);
-				break;
-		}
-
-		actor->threshold++;
-		actor->threshold %= 3;
-		return;
-	}
-
-	// possibly choose another target
-	if (multiplayer && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
-		&& P_BossTargetPlayer(actor, false))
-		return; // got a new target
-
-	if (leveltime & 1)
-	{
-		// chase towards player
-		if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
-			P_NewChaseDir(actor);
-	}
-}
-
-// Function: A_GoopSplat
-//
-// Description: Black Eggman goop hits a target and sticks around for awhile.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_GoopSplat(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_GoopSplat", actor))
-		return;
-#endif
-	P_UnsetThingPosition(actor);
-	if (sector_list)
-	{
-		P_DelSeclist(sector_list);
-		sector_list = NULL;
-	}
-	actor->flags = MF_SPECIAL; // Not a typo
-	P_SetThingPosition(actor);
-}
-
-// Function: A_Boss2PogoSFX
-//
-// Description: Pogoing for Boss 2
-//
-// var1 = pogo jump strength
-// var2 = idle pogo speed
-//
-void A_Boss2PogoSFX(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_Boss2PogoSFX", actor))
-		return;
-#endif
-
-	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
-	{
-		// look for a new target
-		if (P_LookForPlayers(actor, true, false, 0))
-			return; // got a new target
-
-		return;
-	}
-
-	// Boing!
-	if (P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) < FixedMul(256*FRACUNIT, actor->scale))
-	{
-		actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
-		P_InstaThrust(actor, actor->angle, FixedMul(actor->info->speed, actor->scale));
-		// pogo on player
-	}
-	else
-	{
-		UINT8 prandom = P_RandomByte();
-		actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom);
-		P_InstaThrust(actor, actor->angle, FixedMul(FixedMul(actor->info->speed,(locvar2)), actor->scale));
-	}
-	if (actor->info->activesound) S_StartSound(actor, actor->info->activesound);
-	actor->momz = FixedMul(locvar1, actor->scale); // Bounce up in air
-	actor->reactiontime = 1;
-}
-
-// Function: A_Boss2PogoTarget
-//
-// Description: Pogoing for Boss 2, tries to actually land on the player directly.
-//
-// var1 = pogo jump strength
-// var2 = idle pogo speed
-//
-void A_Boss2PogoTarget(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_Boss2PogoTarget", actor))
-		return;
-#endif
-
-	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE) || (actor->target->player && actor->target->player->powers[pw_flashing])
-	|| P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) >= FixedMul(512*FRACUNIT, actor->scale))
-	{
-		// look for a new target
-		if (P_LookForPlayers(actor, true, false, 512*FRACUNIT))
-			; // got a new target
-		else if (P_LookForPlayers(actor, true, false, 0))
-			; // got a new target
-		else
-			return;
-	}
-
-	// Target hit, retreat!
-	if (actor->target->player->powers[pw_flashing] > TICRATE || actor->flags2 & MF2_FRET)
-	{
-		UINT8 prandom = P_RandomByte();
-		actor->z++; // unstick from the floor
-		actor->momz = FixedMul(locvar1, actor->scale); // Bounce up in air
-		actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom); // Pick a direction, and randomize it.
-		P_InstaThrust(actor, actor->angle+ANGLE_180, FixedMul(FixedMul(actor->info->speed,(locvar2)), actor->scale)); // Move at wandering speed
-	}
-	// Try to land on top of the player.
-	else if (P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) < FixedMul(512*FRACUNIT, actor->scale))
-	{
-		fixed_t airtime, gravityadd, zoffs;
-
-		// check gravity in the sector (for later math)
-		P_CheckGravity(actor, true);
-		gravityadd = actor->momz;
-
-		actor->z++; // unstick from the floor
-		actor->momz = FixedMul(locvar1 + (locvar1>>2), actor->scale); // Bounce up in air
-
-		/*badmath = 0;
-		airtime = 0;
-		do {
-			badmath += momz;
-			momz += gravityadd;
-			airtime++;
-		} while(badmath > 0);
-		airtime = 2*airtime<<FRACBITS;
-		*/
-
-		// Remember, kids!
-		// Reduced down Calculus lets you avoid bad 'logic math' loops!
-		//airtime = FixedDiv(-actor->momz<<1, gravityadd)<<1; // going from 0 to 0 is much simpler
-		zoffs = (P_GetPlayerHeight(actor->target->player)>>1) + (actor->target->floorz - actor->floorz); // offset by the difference in floor height plus half the player height,
-		airtime = FixedDiv((-actor->momz - FixedSqrt(FixedMul(actor->momz,actor->momz)+zoffs)), gravityadd)<<1; // to try and land on their head rather than on their feet
-
-		actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
-		P_InstaThrust(actor, actor->angle, FixedDiv(P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y), airtime));
-	}
-	// Wander semi-randomly towards the player to get closer.
-	else
-	{
-		UINT8 prandom = P_RandomByte();
-		actor->z++; // unstick from the floor
-		actor->momz = FixedMul(locvar1, actor->scale); // Bounce up in air
-		actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom); // Pick a direction, and randomize it.
-		P_InstaThrust(actor, actor->angle, FixedMul(FixedMul(actor->info->speed,(locvar2)), actor->scale)); // Move at wandering speed
-	}
-	// Boing!
-	if (actor->info->activesound) S_StartSound(actor, actor->info->activesound);
-
-	if (actor->info->missilestate) // spawn the pogo stick collision box
-	{
-		mobj_t *pogo = P_SpawnMobj(actor->x, actor->y, actor->z - mobjinfo[actor->info->missilestate].height, (mobjtype_t)actor->info->missilestate);
-		pogo->target = actor;
-	}
-
-	actor->reactiontime = 1;
-}
-
-// Function: A_EggmanBox
-//
-// Description: Harms the player
-//
-// var1 = unused
-// var2 = unused
-//
-void A_EggmanBox(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_EggmanBox", actor))
-		return;
-#endif
-	if (!actor->target || !actor->target->player)
-	{
-		CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
-		return;
-	}
-
-	P_DamageMobj(actor->target, actor, actor, 1, 0); // Ow!
-}
-
-// Function: A_TurretFire
-//
-// Description: Initiates turret fire.
-//
-// var1 = object # to repeatedly fire
-// var2 = distance threshold
-//
-void A_TurretFire(mobj_t *actor)
-{
-	INT32 count = 0;
-	fixed_t dist;
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_TurretFire", actor))
-		return;
-#endif
-
-	if (locvar2)
-		dist = FixedMul(locvar2*FRACUNIT, actor->scale);
-	else
-		dist = FixedMul(2048*FRACUNIT, actor->scale);
-
-	if (!locvar1)
-		locvar1 = MT_TURRETLASER;
-
-	while (P_SupermanLook4Players(actor) && count < MAXPLAYERS)
-	{
-		if (P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y) < dist)
-		{
-			actor->flags2 |= MF2_FIRING;
-			actor->extravalue1 = locvar1;
-			break;
-		}
-
-		count++;
-	}
-}
-
-// Function: A_SuperTurretFire
-//
-// Description: Initiates turret fire that even stops Super Sonic.
-//
-// var1 = object # to repeatedly fire
-// var2 = distance threshold
-//
-void A_SuperTurretFire(mobj_t *actor)
-{
-	INT32 count = 0;
-	fixed_t dist;
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SuperTurretFire", actor))
-		return;
-#endif
-
-	if (locvar2)
-		dist = FixedMul(locvar2*FRACUNIT, actor->scale);
-	else
-		dist = FixedMul(2048*FRACUNIT, actor->scale);
-
-	if (!locvar1)
-		locvar1 = MT_TURRETLASER;
-
-	while (P_SupermanLook4Players(actor) && count < MAXPLAYERS)
-	{
-		if (P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y) < dist)
-		{
-			actor->flags2 |= MF2_FIRING;
-			actor->flags2 |= MF2_SUPERFIRE;
-			actor->extravalue1 = locvar1;
-			break;
-		}
-
-		count++;
-	}
-}
-
-// Function: A_TurretStop
-//
-// Description: Stops the turret fire.
-//
-// var1 = Don't play activesound?
-// var2 = unused
-//
-void A_TurretStop(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_TurretStop", actor))
-		return;
-#endif
-
-	actor->flags2 &= ~MF2_FIRING;
-	actor->flags2 &= ~MF2_SUPERFIRE;
-
-	if (actor->target && actor->info->activesound && !locvar1)
-		S_StartSound(actor, actor->info->activesound);
-}
-
-// Function: A_SparkFollow
-//
-// Description: Used by the hyper sparks to rotate around their target.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_SparkFollow(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SparkFollow", actor))
-		return;
-#endif
-
-	if ((!actor->target || (actor->target->health <= 0))
-		|| (actor->target->player && !actor->target->player->powers[pw_super]))
-	{
-		P_RemoveMobj(actor);
-		return;
-	}
-
-	actor->angle += FixedAngle(actor->info->damage*FRACUNIT);
-	P_UnsetThingPosition(actor);
-	{
-		const angle_t fa = actor->angle>>ANGLETOFINESHIFT;
-		actor->x = actor->target->x + FixedMul(FINECOSINE(fa),FixedMul(actor->info->speed, actor->scale));
-		actor->y = actor->target->y + FixedMul(FINESINE(fa),FixedMul(actor->info->speed, actor->scale));
-		if (actor->target->eflags & MFE_VERTICALFLIP)
-			actor->z = actor->target->z + actor->target->height - FixedDiv(actor->target->height,3*FRACUNIT);
-		else
-			actor->z = actor->target->z + FixedDiv(actor->target->height,3*FRACUNIT) - actor->height;
-	}
-	P_SetThingPosition(actor);
-}
-
-// Function: A_BuzzFly
-//
-// Description: Makes an object slowly fly after a player, in the manner of a Buzz.
-//
-// var1 = sfx to play
-// var2 = length of sfx, set to threshold if played
-//
-void A_BuzzFly(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_BuzzFly", actor))
-		return;
-#endif
-	if (actor->flags2 & MF2_AMBUSH)
-		return;
-
-	if (actor->reactiontime)
-		actor->reactiontime--;
-
-	// modify target threshold
-	if (actor->threshold)
-	{
-		if (!actor->target || actor->target->health <= 0)
-			actor->threshold = 0;
-		else
-			actor->threshold--;
-	}
-
-	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
-	{
-		// look for a new target
-		if (P_LookForPlayers(actor, true, false, 0))
-			return; // got a new target
-
-		actor->momz = actor->momy = actor->momx = 0;
-		P_SetMobjState(actor, actor->info->spawnstate);
-		return;
-	}
-
-	// turn towards movement direction if not there yet
-	actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
-
-	if (actor->target->health <= 0 || (!actor->threshold && !P_CheckSight(actor, actor->target)))
-	{
-		if ((multiplayer || netgame) && P_LookForPlayers(actor, true, false, FixedMul(3072*FRACUNIT, actor->scale)))
-			return; // got a new target
-
-		actor->momx = actor->momy = actor->momz = 0;
-		P_SetMobjState(actor, actor->info->spawnstate); // Go back to looking around
-		return;
-	}
-
-	// If the player is over 3072 fracunits away, then look for another player
-	if (P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y),
-		actor->target->z - actor->z) > FixedMul(3072*FRACUNIT, actor->scale))
-	{
-		if (multiplayer || netgame)
-			P_LookForPlayers(actor, true, false, FixedMul(3072*FRACUNIT, actor->scale)); // maybe get a new target
-
-		return;
-	}
-
-	// chase towards player
-	{
-		INT32 dist, realspeed;
-		const fixed_t mf = 5*(FRACUNIT/4);
-
-		if (ultimatemode)
-			realspeed = FixedMul(FixedMul(actor->info->speed,mf), actor->scale);
-		else
-			realspeed = FixedMul(actor->info->speed, actor->scale);
-
-		dist = P_AproxDistance(P_AproxDistance(actor->target->x - actor->x,
-			actor->target->y - actor->y), actor->target->z - actor->z);
-
-		if (dist < 1)
-			dist = 1;
-
-		actor->momx = FixedMul(FixedDiv(actor->target->x - actor->x, dist), realspeed);
-		actor->momy = FixedMul(FixedDiv(actor->target->y - actor->y, dist), realspeed);
-		actor->momz = FixedMul(FixedDiv(actor->target->z - actor->z, dist), realspeed);
-
-		if (actor->z+actor->momz >= actor->waterbottom && actor->watertop > actor->floorz
-			&& actor->z+actor->momz > actor->watertop - FixedMul(256*FRACUNIT, actor->scale)
-			&& actor->z+actor->momz <= actor->watertop)
-		{
-			actor->momz = 0;
-			actor->z = actor->watertop;
-		}
-	}
-
-	if (locvar1 != sfx_None && !actor->threshold)
-	{
-		S_StartSound(actor, locvar1);
-		actor->threshold = locvar2;
-	}
-}
-
-// Function: A_GuardChase
-//
-// Description: Modified A_Chase for Egg Guard
-//
-// var1 = unused
-// var2 = unused
-//
-void A_GuardChase(mobj_t *actor)
-{
-	INT32 delta;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_GuardChase", actor))
-		return;
-#endif
-
-	if (actor->reactiontime)
-		actor->reactiontime--;
-
-	if ((!actor->tracer || !actor->tracer->health) && actor->threshold != 42)
-	{
-		P_SetTarget(&actor->tracer, NULL);
-		actor->threshold = 42;
-		P_SetMobjState(actor, actor->info->painstate);
-		actor->flags |= MF_SPECIAL|MF_SHOOTABLE;
-		return;
-	}
-
-	// turn towards movement direction if not there yet
-	if (actor->movedir < NUMDIRS)
-	{
-		actor->angle &= (7<<29);
-		delta = actor->angle - (actor->movedir << 29);
-
-		if (delta > 0)
-			actor->angle -= ANGLE_45;
-		else if (delta < 0)
-			actor->angle += ANGLE_45;
-	}
-
-	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
-	{
-		// look for a new target
-		if (P_LookForPlayers(actor, true, false, 0))
-			return; // got a new target
-
-		P_SetMobjStateNF(actor, actor->info->spawnstate);
-		return;
-	}
-
-	// possibly choose another target
-	if (multiplayer && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
-		&& P_LookForPlayers(actor, true, false, 0))
-		return; // got a new target
-
-	// chase towards player
-	if (--actor->movecount < 0 || !P_Move(actor, (actor->flags2 & MF2_AMBUSH) ? actor->info->speed * 2 : actor->info->speed))
-	{
-		P_NewChaseDir(actor);
-		actor->movecount += 5; // Increase tics before change in direction allowed.
-	}
-
-	// Now that we've moved, its time for our shield to move!
-	// Otherwise it'll never act as a proper overlay.
-	if (actor->tracer && actor->tracer->state
-	&& actor->tracer->state->action.acp1)
-	{
-		var1 = actor->tracer->state->var1, var2 = actor->tracer->state->var2;
-		actor->tracer->state->action.acp1(actor->tracer);
-	}
-}
-
-// Function: A_EggShield
-//
-// Description: Modified A_Chase for Egg Guard's shield
-//
-// var1 = unused
-// var2 = unused
-//
-void A_EggShield(mobj_t *actor)
-{
-	INT32 i;
-	player_t *player;
-	fixed_t blockdist;
-	fixed_t newx, newy;
-	fixed_t movex, movey;
-	angle_t angle;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_EggShield", actor))
-		return;
-#endif
-
-	if (!actor->target || !actor->target->health)
-	{
-		P_RemoveMobj(actor);
-		return;
-	}
-
-	newx = actor->target->x + P_ReturnThrustX(actor, actor->target->angle, FixedMul(FRACUNIT, actor->scale));
-	newy = actor->target->y + P_ReturnThrustY(actor, actor->target->angle, FixedMul(FRACUNIT, actor->scale));
-
-	movex = newx - actor->x;
-	movey = newy - actor->y;
-
-	actor->angle = actor->target->angle;
-	if (actor->target->eflags & MFE_VERTICALFLIP)
-	{
-		actor->eflags |= MFE_VERTICALFLIP;
-		actor->z = actor->target->z + actor->target->height - actor->height;
-	}
-	else
-		actor->z = actor->target->z;
-
-	actor->destscale = actor->target->destscale;
-	P_SetScale(actor, actor->target->scale);
-
-	actor->floorz = actor->target->floorz;
-	actor->ceilingz = actor->target->ceilingz;
-
-	if (!movex && !movey)
-		return;
-
-	P_UnsetThingPosition(actor);
-	actor->x = newx;
-	actor->y = newy;
-	P_SetThingPosition(actor);
-
-	// Search for players to push
-	for (i = 0; i < MAXPLAYERS; i++)
-	{
-		if (!playeringame[i] || players[i].spectator)
-			continue;
-
-		player = &players[i];
-
-		if (!player->mo)
-			continue;
-
-		if (player->mo->z > actor->z + actor->height)
-			continue;
-
-		if (player->mo->z + player->mo->height < actor->z)
-			continue;
-
-		blockdist = actor->radius + player->mo->radius;
-
-		if (abs(actor->x - player->mo->x) >= blockdist || abs(actor->y - player->mo->y) >= blockdist)
-			continue; // didn't hit it
-
-		angle = R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y) - actor->angle;
-
-		if (angle > ANGLE_90 && angle < ANGLE_270)
-			continue;
-
-		// Blocked by the shield
-		player->mo->momx += movex;
-		player->mo->momy += movey;
-		return;
-	}
-}
-
-
-// Function: A_SetReactionTime
-//
-// Description: Sets the object's reaction time.
-//
-// var1 = 1 (use value in var2); 0 (use info table value)
-// var2 = if var1 = 1, then value to set
-//
-void A_SetReactionTime(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SetReactionTime", actor))
-		return;
-#endif
-	if (var1)
-		actor->reactiontime = var2;
-	else
-		actor->reactiontime = actor->info->reactiontime;
-}
-
-// Function: A_Boss1Spikeballs
-//
-// Description: Boss 1 spikeball spawning loop.
-//
-// var1 = ball number
-// var2 = total balls
-//
-void A_Boss1Spikeballs(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	mobj_t *ball;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_Boss1Spikeballs", actor))
-		return;
-#endif
-
-	ball = P_SpawnMobj(actor->x, actor->y, actor->z, MT_EGGMOBILE_BALL);
-	P_SetTarget(&ball->target, actor);
-	ball->movedir = FixedAngle(FixedMul(FixedDiv(locvar1<<FRACBITS, locvar2<<FRACBITS), 360<<FRACBITS));
-	ball->threshold = ball->radius + actor->radius + ball->info->painchance;
-
-	S_StartSound(ball, ball->info->seesound);
-	var1 = ball->state->var1, var2 = ball->state->var2;
-	ball->state->action.acp1(ball);
-}
-
-// Function: A_Boss3TakeDamage
-//
-// Description: Called when Boss 3 takes damage.
-//
-// var1 = movecount value
-// var2 = unused
-//
-void A_Boss3TakeDamage(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_Boss3TakeDamage", actor))
-		return;
-#endif
-	actor->movecount = var1;
-
-	if (actor->target && actor->target->spawnpoint)
-		actor->threshold = actor->target->spawnpoint->extrainfo;
-}
-
-// Function: A_Boss3Path
-//
-// Description: Does pathfinding along Boss 3's nodes.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_Boss3Path(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_Boss3Path", actor))
-		return;
-#endif
-
-	if (actor->tracer && actor->tracer->health && actor->tracer->movecount)
-		actor->movecount |= 1;
-	else if (actor->movecount & 1)
-		actor->movecount = 0;
-
-	if (actor->movecount & 2) // We've reached a firing point?
-	{
-		// Wait here and pretend to be angry or something.
-		actor->momx = 0;
-		actor->momy = 0;
-		actor->momz = 0;
-		P_SetTarget(&actor->target, actor->tracer->target);
-		var1 = 0, var2 = 0;
-		A_FaceTarget(actor);
-		if (actor->tracer->state == &states[actor->tracer->info->missilestate])
-			P_SetMobjState(actor, actor->info->missilestate);
-		return;
-	}
-	else if (actor->threshold >= 0) // Traveling mode
-	{
-		thinker_t *th;
-		mobj_t *mo2;
-		fixed_t dist, dist2;
-		fixed_t speed;
-
-		P_SetTarget(&actor->target, NULL);
-
-		// scan the thinkers
-		// to find a point that matches
-		// the number
-		for (th = thinkercap.next; th != &thinkercap; th = th->next)
-		{
-			if (th->function.acp1 != (actionf_p1)P_MobjThinker)
-				continue;
-
-			mo2 = (mobj_t *)th;
-			if (mo2->type == MT_BOSS3WAYPOINT && mo2->spawnpoint && mo2->spawnpoint->angle == actor->threshold)
-			{
-				P_SetTarget(&actor->target, mo2);
-				break;
-			}
-		}
-
-		if (!actor->target) // Should NEVER happen
-		{
-			CONS_Debug(DBG_GAMELOGIC, "Error: Boss 3 Dummy was unable to find specified waypoint: %d\n", actor->threshold);
-			return;
-		}
-
-		dist = P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y), actor->target->z - actor->z);
-
-		if (dist < 1)
-			dist = 1;
-
-		if (actor->tracer && ((actor->tracer->movedir)
-		|| (actor->tracer->health <= actor->tracer->info->damage)))
-			speed = actor->info->speed * 2;
-		else
-			speed = actor->info->speed;
-
-		actor->momx = FixedMul(FixedDiv(actor->target->x - actor->x, dist), speed);
-		actor->momy = FixedMul(FixedDiv(actor->target->y - actor->y, dist), speed);
-		actor->momz = FixedMul(FixedDiv(actor->target->z - actor->z, dist), speed);
-
-		if (actor->momx != 0 || actor->momy != 0)
-			actor->angle = R_PointToAngle2(0, 0, actor->momx, actor->momy);
-
-		dist2 = P_AproxDistance(P_AproxDistance(actor->target->x - (actor->x + actor->momx), actor->target->y - (actor->y + actor->momy)), actor->target->z - (actor->z + actor->momz));
-
-		if (dist2 < 1)
-			dist2 = 1;
-
-		if ((dist >> FRACBITS) <= (dist2 >> FRACBITS))
-		{
-			// If further away, set XYZ of mobj to waypoint location
-			P_UnsetThingPosition(actor);
-			actor->x = actor->target->x;
-			actor->y = actor->target->y;
-			actor->z = actor->target->z;
-			actor->momx = actor->momy = actor->momz = 0;
-			P_SetThingPosition(actor);
-
-			if (actor->threshold == 0)
-			{
-				P_RemoveMobj(actor); // Cycle completed. Dummy removed.
-				return;
-			}
-
-			// Set to next waypoint in sequence
-			if (actor->target->spawnpoint)
-			{
-				// From the center point, choose one of the five paths
-				if (actor->target->spawnpoint->angle == 0)
-				{
-					P_RemoveMobj(actor); // Cycle completed. Dummy removed.
-					return;
-				}
-				else
-					actor->threshold = actor->target->spawnpoint->extrainfo;
-
-				// If the deaf flag is set, go into firing mode
-				if (actor->target->spawnpoint->options & MTF_AMBUSH)
-					actor->movecount |= 2;
-			}
-			else // This should never happen, as well
-				CONS_Debug(DBG_GAMELOGIC, "Error: Boss 3 Dummy waypoint has no spawnpoint associated with it.\n");
-		}
-	}
-}
-
-// Function: A_LinedefExecute
-//
-// Description: Object's location is used to set the calling sector. The tag used is var1. Optionally, if var2 is set, the actor's angle (multiplied by var2) is added to the tag number as well.
-//
-// var1 = tag
-// var2 = add angle to tag (optional)
-//
-void A_LinedefExecute(mobj_t *actor)
-{
-	INT32 tagnum;
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_LinedefExecute", actor))
-		return;
-#endif
-
-	tagnum = locvar1;
-	// state numbers option is no more, custom states cannot be guaranteed to stay the same number anymore, now that they can be defined by names instead
-
-	if (locvar2)
-		tagnum += locvar2*(AngleFixed(actor->angle)>>FRACBITS);
-
-	CONS_Debug(DBG_GAMELOGIC, "A_LinedefExecute: Running mobjtype %d's sector with tag %d\n", actor->type, tagnum);
-
-	// tag 32768 displayed in map editors is actually tag -32768, tag 32769 is -32767, 65535 is -1 etc.
-	P_LinedefExecute((INT16)tagnum, actor, actor->subsector->sector);
-}
-
-// Function: A_PlaySeeSound
-//
-// Description: Plays the object's seesound.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_PlaySeeSound(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_PlaySeeSound", actor))
-		return;
-#endif
-	if (actor->info->seesound)
-		S_StartScreamSound(actor, actor->info->seesound);
-}
-
-// Function: A_PlayAttackSound
-//
-// Description: Plays the object's attacksound.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_PlayAttackSound(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_PlayAttackSound", actor))
-		return;
-#endif
-	if (actor->info->attacksound)
-		S_StartAttackSound(actor, actor->info->attacksound);
-}
-
-// Function: A_PlayActiveSound
-//
-// Description: Plays the object's activesound.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_PlayActiveSound(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_PlayActiveSound", actor))
-		return;
-#endif
-	if (actor->info->activesound)
-		S_StartSound(actor, actor->info->activesound);
-}
-
-// Function: A_SmokeTrailer
-//
-// Description: Adds smoke trails to an object.
-//
-// var1 = object # to spawn as smoke
-// var2 = unused
-//
-void A_SmokeTrailer(mobj_t *actor)
-{
-	mobj_t *th;
-	INT32 locvar1 = var1;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SmokeTrailer", actor))
-		return;
-#endif
-
-	if (leveltime % 4)
-		return;
-
-	// add the smoke behind the rocket
-	if (actor->eflags & MFE_VERTICALFLIP)
-	{
-		th = P_SpawnMobj(actor->x-actor->momx, actor->y-actor->momy, actor->z + actor->height - FixedMul(mobjinfo[locvar1].height, actor->scale), locvar1);
-		th->flags2 |= MF2_OBJECTFLIP;
-	}
-	else
-		th = P_SpawnMobj(actor->x-actor->momx, actor->y-actor->momy, actor->z, locvar1);
-	P_SetObjectMomZ(th, FRACUNIT, false);
-	th->destscale = actor->scale;
-	P_SetScale(th, actor->scale);
-	th->tics -= P_RandomByte() & 3;
-	if (th->tics < 1)
-		th->tics = 1;
-}
-
-// Function: A_SpawnObjectAbsolute
-//
-// Description: Spawns an object at an absolute position
-//
-// var1:
-//		var1 >> 16 = x
-//		var1 & 65535 = y
-// var2:
-//		var2 >> 16 = z
-//		var2 & 65535 = type
-//
-void A_SpawnObjectAbsolute(mobj_t *actor)
-{
-	INT16 x, y, z; // Want to be sure we can use negative values
-	mobjtype_t type;
-	mobj_t *mo;
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SpawnObjectAbsolute", actor))
-		return;
-#endif
-
-	x = (INT16)(locvar1>>16);
-	y = (INT16)(locvar1&65535);
-	z = (INT16)(locvar2>>16);
-	type = (mobjtype_t)(locvar2&65535);
-
-	mo = P_SpawnMobj(x<<FRACBITS, y<<FRACBITS, z<<FRACBITS, type);
-
-	// Spawn objects with an angle matching the spawner's, rather than spawning Eastwards - Monster Iestyn
-	mo->angle = actor->angle;
-
-	if (actor->eflags & MFE_VERTICALFLIP)
-		mo->flags2 |= MF2_OBJECTFLIP;
-}
-
-// Function: A_SpawnObjectRelative
-//
-// Description: Spawns an object relative to the location of the actor
-//
-// var1:
-//		var1 >> 16 = x
-//		var1 & 65535 = y
-// var2:
-//		var2 >> 16 = z
-//		var2 & 65535 = type
-//
-void A_SpawnObjectRelative(mobj_t *actor)
-{
-	INT16 x, y, z; // Want to be sure we can use negative values
-	mobjtype_t type;
-	mobj_t *mo;
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SpawnObjectRelative", actor))
-		return;
-#endif
-
-	CONS_Debug(DBG_GAMELOGIC, "A_SpawnObjectRelative called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
-
-	x = (INT16)(locvar1>>16);
-	y = (INT16)(locvar1&65535);
-	z = (INT16)(locvar2>>16);
-	type = (mobjtype_t)(locvar2&65535);
-
-	// Spawn objects correctly in reverse gravity.
-	// NOTE: Doing actor->z + actor->height is the bottom of the object while the object has reverse gravity. - Flame
-	mo = P_SpawnMobj(actor->x + FixedMul(x<<FRACBITS, actor->scale),
-		actor->y + FixedMul(y<<FRACBITS, actor->scale),
-		(actor->eflags & MFE_VERTICALFLIP) ? ((actor->z + actor->height - mobjinfo[type].height) - FixedMul(z<<FRACBITS, actor->scale)) : (actor->z + FixedMul(z<<FRACBITS, actor->scale)), type);
-
-	// Spawn objects with an angle matching the spawner's, rather than spawning Eastwards - Monster Iestyn
-	mo->angle = actor->angle;
-
-	if (actor->eflags & MFE_VERTICALFLIP)
-		mo->flags2 |= MF2_OBJECTFLIP;
-}
-
-// Function: A_ChangeAngleRelative
-//
-// Description: Changes the angle to a random relative value between the min and max. Set min and max to the same value to eliminate randomness
-//
-// var1 = min
-// var2 = max
-//
-void A_ChangeAngleRelative(mobj_t *actor)
-{
-	// Oh god, the old code /sucked/. Changed this and the absolute version to get a random range using amin and amax instead of
-	//  getting a random angle from the _entire_ spectrum and then clipping. While we're at it, do the angle conversion to the result
-	//  rather than the ranges, so <0 and >360 work as possible values. -Red
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	//angle_t angle = (P_RandomByte()+1)<<24;
-	const fixed_t amin = locvar1*FRACUNIT;
-	const fixed_t amax = locvar2*FRACUNIT;
-	//const angle_t amin = FixedAngle(locvar1*FRACUNIT);
-	//const angle_t amax = FixedAngle(locvar2*FRACUNIT);
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_ChangeAngleRelative", actor))
-		return;
-#endif
-
-#ifdef PARANOIA
-	if (amin > amax)
-		I_Error("A_ChangeAngleRelative: var1 is greater then var2");
-#endif
-/*
-	if (angle < amin)
-		angle = amin;
-	if (angle > amax)
-		angle = amax;*/
-
-	actor->angle += FixedAngle(P_RandomRange(amin, amax));
-}
-
-// Function: A_ChangeAngleAbsolute
-//
-// Description: Changes the angle to a random absolute value between the min and max. Set min and max to the same value to eliminate randomness
-//
-// var1 = min
-// var2 = max
-//
-void A_ChangeAngleAbsolute(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	//angle_t angle = (P_RandomByte()+1)<<24;
-	const fixed_t amin = locvar1*FRACUNIT;
-	const fixed_t amax = locvar2*FRACUNIT;
-	//const angle_t amin = FixedAngle(locvar1*FRACUNIT);
-	//const angle_t amax = FixedAngle(locvar2*FRACUNIT);
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_ChangeAngleAbsolute", actor))
-		return;
-#endif
-
-#ifdef PARANOIA
-	if (amin > amax)
-		I_Error("A_ChangeAngleAbsolute: var1 is greater then var2");
-#endif
-/*
-	if (angle < amin)
-		angle = amin;
-	if (angle > amax)
-		angle = amax;*/
-
-	actor->angle = FixedAngle(P_RandomRange(amin, amax));
-}
-
-// Function: A_PlaySound
-//
-// Description: Plays a sound
-//
-// var1 = sound # to play
-// var2:
-//		0 = Play sound without an origin
-//		1 = Play sound using calling object as origin
-//
-void A_PlaySound(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_PlaySound", actor))
-		return;
-#endif
-
-	S_StartSound(locvar2 ? actor : NULL, locvar1);
-}
-
-// Function: A_FindTarget
-//
-// Description: Finds the nearest/furthest mobj of the specified type and sets actor->target to it.
-//
-// var1 = mobj type
-// var2 = if (0) nearest; else furthest;
-//
-void A_FindTarget(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	mobj_t *targetedmobj = NULL;
-	thinker_t *th;
-	mobj_t *mo2;
-	fixed_t dist1 = 0, dist2 = 0;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_FindTarget", actor))
-		return;
-#endif
-
-	CONS_Debug(DBG_GAMELOGIC, "A_FindTarget called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
-
-	// scan the thinkers
-	for (th = thinkercap.next; th != &thinkercap; th = th->next)
-	{
-		if (th->function.acp1 != (actionf_p1)P_MobjThinker)
-			continue;
-
-		mo2 = (mobj_t *)th;
-
-		if (mo2->type == (mobjtype_t)locvar1)
-		{
-			if (mo2->player && (mo2->player->spectator || mo2->player->pflags & PF_INVIS))
-				continue; // Ignore spectators
-			if ((mo2->player || mo2->flags & MF_ENEMY) && mo2->health <= 0)
-				continue; // Ignore dead things
-			if (targetedmobj == NULL)
-			{
-				targetedmobj = mo2;
-				dist2 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
-			}
-			else
-			{
-				dist1 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
-
-				if ((!locvar2 && dist1 < dist2) || (locvar2 && dist1 > dist2))
-				{
-					targetedmobj = mo2;
-					dist2 = dist1;
-				}
-			}
-		}
-	}
-
-	if (!targetedmobj)
-	{
-		CONS_Debug(DBG_GAMELOGIC, "A_FindTarget: Unable to find the specified object to target.\n");
-		return; // Oops, nothing found..
-	}
-
-	CONS_Debug(DBG_GAMELOGIC, "A_FindTarget: Found a target.\n");
-
-	P_SetTarget(&actor->target, targetedmobj);
-}
-
-// Function: A_FindTracer
-//
-// Description: Finds the nearest/furthest mobj of the specified type and sets actor->tracer to it.
-//
-// var1 = mobj type
-// var2 = if (0) nearest; else furthest;
-//
-void A_FindTracer(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	mobj_t *targetedmobj = NULL;
-	thinker_t *th;
-	mobj_t *mo2;
-	fixed_t dist1 = 0, dist2 = 0;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_FindTracer", actor))
-		return;
-#endif
-
-	CONS_Debug(DBG_GAMELOGIC, "A_FindTracer called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
-
-	// scan the thinkers
-	for (th = thinkercap.next; th != &thinkercap; th = th->next)
-	{
-		if (th->function.acp1 != (actionf_p1)P_MobjThinker)
-			continue;
-
-		mo2 = (mobj_t *)th;
-
-		if (mo2->type == (mobjtype_t)locvar1)
-		{
-			if (mo2->player && (mo2->player->spectator || mo2->player->pflags & PF_INVIS))
-				continue; // Ignore spectators
-			if ((mo2->player || mo2->flags & MF_ENEMY) && mo2->health <= 0)
-				continue; // Ignore dead things
-			if (targetedmobj == NULL)
-			{
-				targetedmobj = mo2;
-				dist2 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
-			}
-			else
-			{
-				dist1 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
-
-				if ((!locvar2 && dist1 < dist2) || (locvar2 && dist1 > dist2))
-				{
-					targetedmobj = mo2;
-					dist2 = dist1;
-				}
-			}
-		}
-	}
-
-	if (!targetedmobj)
-	{
-		CONS_Debug(DBG_GAMELOGIC, "A_FindTracer: Unable to find the specified object to target.\n");
-		return; // Oops, nothing found..
-	}
-
-	CONS_Debug(DBG_GAMELOGIC, "A_FindTracer: Found a target.\n");
-
-	P_SetTarget(&actor->tracer, targetedmobj);
-}
-
-// Function: A_SetTics
-//
-// Description: Sets the animation tics of an object
-//
-// var1 = tics to set to
-// var2 = if this is set, and no var1 is supplied, the mobj's threshold value will be used.
-//
-void A_SetTics(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SetTics", actor))
-		return;
-#endif
-
-	if (locvar1)
-		actor->tics = locvar1;
-	else if (locvar2)
-		actor->tics = actor->threshold;
-}
-
-// Function: A_SetRandomTics
-//
-// Description: Sets the animation tics of an object to a random value
-//
-// var1 = lower bound
-// var2 = upper bound
-//
-void A_SetRandomTics(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SetRandomTics", actor))
-		return;
-#endif
-
-	actor->tics = P_RandomRange(locvar1, locvar2);
-}
-
-// Function: A_ChangeColorRelative
-//
-// Description: Changes the color of an object
-//
-// var1 = if (var1 > 0), find target and add its color value to yours
-// var2 = if (var1 = 0), color value to add
-//
-void A_ChangeColorRelative(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_ChangeColorRelative", actor))
-		return;
-#endif
-
-	if (locvar1)
-	{
-		// Have you ever seen anything so hideous?
-		if (actor->target)
-			actor->color = (UINT8)(actor->color + actor->target->color);
-	}
-	else
-		actor->color = (UINT8)(actor->color + locvar2);
-}
-
-// Function: A_ChangeColorAbsolute
-//
-// Description: Changes the color of an object by an absolute value. Note: 0 is default colormap.
-//
-// var1 = if (var1 > 0), set your color to your target's color
-// var2 = if (var1 = 0), color value to set to
-//
-void A_ChangeColorAbsolute(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_ChangeColorAbsolute", actor))
-		return;
-#endif
-
-	if (locvar1)
-	{
-		if (actor->target)
-			actor->color = actor->target->color;
-	}
-	else
-		actor->color = (UINT8)locvar2;
-}
-
-// Function: A_MoveRelative
-//
-// Description: Moves an object (wrapper for P_Thrust)
-//
-// var1 = angle
-// var2 = force
-//
-void A_MoveRelative(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_MoveRelative", actor))
-		return;
-#endif
-
-	P_Thrust(actor, actor->angle+FixedAngle(locvar1*FRACUNIT), FixedMul(locvar2*FRACUNIT, actor->scale));
-}
-
-// Function: A_MoveAbsolute
-//
-// Description: Moves an object (wrapper for P_InstaThrust)
-//
-// var1 = angle
-// var2 = force
-//
-void A_MoveAbsolute(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_MoveAbsolute", actor))
-		return;
-#endif
-
-	P_InstaThrust(actor, FixedAngle(locvar1*FRACUNIT), FixedMul(locvar2*FRACUNIT, actor->scale));
-}
-
-// Function: A_Thrust
-//
-// Description: Pushes the object horizontally at its current angle.
-//
-// var1 = amount of force
-// var2 = If 1, xy momentum is lost. If 0, xy momentum is kept
-//
-void A_Thrust(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_Thrust", actor))
-		return;
-#endif
-
-	if (!locvar1)
-		CONS_Debug(DBG_GAMELOGIC, "A_Thrust: Var1 not specified!\n");
-
-	if (locvar2)
-		P_InstaThrust(actor, actor->angle, FixedMul(locvar1*FRACUNIT, actor->scale));
-	else
-		P_Thrust(actor, actor->angle, FixedMul(locvar1*FRACUNIT, actor->scale));
-}
-
-// Function: A_ZThrust
-//
-// Description: Pushes the object up or down.
-//
-// var1 = amount of force
-// var2:
-//		lower 16 bits = If 1, xy momentum is lost. If 0, xy momentum is kept
-//		upper 16 bits = If 1, z momentum is lost. If 0, z momentum is kept
-//
-void A_ZThrust(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_ZThrust", actor))
-		return;
-#endif
-
-	if (!locvar1)
-		CONS_Debug(DBG_GAMELOGIC, "A_ZThrust: Var1 not specified!\n");
-
-	if (locvar2 & 65535)
-		actor->momx = actor->momy = 0;
-
-	if (actor->eflags & MFE_VERTICALFLIP)
-		actor->z--;
-	else
-		actor->z++;
-
-	P_SetObjectMomZ(actor, locvar1*FRACUNIT, !(locvar2 >> 16));
-}
-
-// Function: A_SetTargetsTarget
-//
-// Description: Sets your target to the object who your target is targeting. Yikes! If it happens to be NULL, you're just out of luck.
-//
-// var1: (Your target)
-//		0 = target
-//		1 = tracer
-// var2: (Your target's target)
-//		0 = target/tracer's target
-//		1 = target/tracer's tracer
-//
-void A_SetTargetsTarget(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	mobj_t *oldtarg = NULL, *newtarg = NULL;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SetTargetsTarget", actor))
-		return;
-#endif
-
-	// actor's target
-	if (locvar1) // or tracer
-		oldtarg = actor->tracer;
-	else
-		oldtarg = actor->target;
-
-	if (P_MobjWasRemoved(oldtarg))
-		return;
-
-	// actor's target's target!
-	if (locvar2) // or tracer
-		newtarg = oldtarg->tracer;
-	else
-		newtarg = oldtarg->target;
-
-	if (P_MobjWasRemoved(newtarg))
-		return;
-
-	// set actor's new target
-	if (locvar1) // or tracer
-		P_SetTarget(&actor->tracer, newtarg);
-	else
-		P_SetTarget(&actor->target, newtarg);
-}
-
-// Function: A_SetObjectFlags
-//
-// Description: Sets the flags of an object
-//
-// var1 = flag value to set
-// var2:
-//		if var2 == 2, add the flag to the current flags
-//		else if var2 == 1, remove the flag from the current flags
-//		else if var2 == 0, set the flags to the exact value
-//
-void A_SetObjectFlags(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	boolean unlinkthings = false;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SetObjectFlags", actor))
-		return;
-#endif
-
-	if (locvar2 == 2)
-		locvar1 = actor->flags | locvar1;
-	else if (locvar2 == 1)
-		locvar1 = actor->flags & ~locvar1;
-
-	if ((UINT32)(locvar1 & (MF_NOBLOCKMAP|MF_NOSECTOR)) != (actor->flags & (MF_NOBLOCKMAP|MF_NOSECTOR))) // Blockmap/sector status has changed, so reset the links
-		unlinkthings = true;
-
-	if (unlinkthings) {
-		P_UnsetThingPosition(actor);
-		if (sector_list)
-		{
-			P_DelSeclist(sector_list);
-			sector_list = NULL;
-		}
-	}
-
-	actor->flags = locvar1;
-
-	if (unlinkthings)
-		P_SetThingPosition(actor);
-}
-
-// Function: A_SetObjectFlags2
-//
-// Description: Sets the flags2 of an object
-//
-// var1 = flag value to set
-// var2:
-//		if var2 == 2, add the flag to the current flags
-//		else if var2 == 1, remove the flag from the current flags
-//		else if var2 == 0, set the flags to the exact value
-//
-void A_SetObjectFlags2(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SetObjectFlags2", actor))
-		return;
-#endif
-
-	if (locvar2 == 2)
-		actor->flags2 |= locvar1;
-	else if (locvar2 == 1)
-		actor->flags2 &= ~locvar1;
-	else
-		actor->flags2 = locvar1;
-}
-
-// Function: A_BossJetFume
-//
-// Description: Spawns jet fumes/other attachment miscellany for the boss. To only be used when he is spawned.
-//
-// var1:
-//		0 - Triple jet fume pattern
-//		1 - Boss 3's propeller
-//		2 - Metal Sonic jet fume
-//		3 - Boss 4 jet flame
-// var2 = unused
-//
-void A_BossJetFume(mobj_t *actor)
-{
-	mobj_t *filler;
-	INT32 locvar1 = var1;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_BossJetFume", actor))
-		return;
-#endif
-
-	if (locvar1 == 0) // Boss1 jet fumes
-	{
-		fixed_t jetx, jety, jetz;
-
-		jetx = actor->x + P_ReturnThrustX(actor, actor->angle, -FixedMul(64*FRACUNIT, actor->scale));
-		jety = actor->y + P_ReturnThrustY(actor, actor->angle, -FixedMul(64*FRACUNIT, actor->scale));
-		if (actor->eflags & MFE_VERTICALFLIP)
-			jetz = actor->z + actor->height - FixedMul(38*FRACUNIT + mobjinfo[MT_JETFUME1].height, actor->scale);
-		else
-			jetz = actor->z + FixedMul(38*FRACUNIT, actor->scale);
-
-		filler = P_SpawnMobj(jetx, jety, jetz, MT_JETFUME1);
-		P_SetTarget(&filler->target, actor);
-		filler->destscale = actor->scale;
-		P_SetScale(filler, filler->destscale);
-		if (actor->eflags & MFE_VERTICALFLIP)
-			filler->flags2 |= MF2_OBJECTFLIP;
-		filler->fuse = 56;
-
-		if (actor->eflags & MFE_VERTICALFLIP)
-			jetz = actor->z + actor->height - FixedMul(12*FRACUNIT + mobjinfo[MT_JETFUME1].height, actor->scale);
-		else
-			jetz = actor->z + FixedMul(12*FRACUNIT, actor->scale);
-
-		filler = P_SpawnMobj(jetx + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)),
-				jety + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)),
-				jetz, MT_JETFUME1);
-		P_SetTarget(&filler->target, actor);
-		filler->destscale = actor->scale;
-		P_SetScale(filler, filler->destscale);
-		if (actor->eflags & MFE_VERTICALFLIP)
-			filler->flags2 |= MF2_OBJECTFLIP;
-		filler->fuse = 57;
-
-		filler = P_SpawnMobj(jetx + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)),
-				jety + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)),
-				jetz, MT_JETFUME1);
-		P_SetTarget(&filler->target, actor);
-		filler->destscale = actor->scale;
-		P_SetScale(filler, filler->destscale);
-		if (actor->eflags & MFE_VERTICALFLIP)
-			filler->flags2 |= MF2_OBJECTFLIP;
-		filler->fuse = 58;
-
-		P_SetTarget(&actor->tracer, filler);
-	}
-	else if (locvar1 == 1) // Boss 3 propeller
-	{
-		fixed_t jetx, jety, jetz;
-
-		jetx = actor->x + P_ReturnThrustX(actor, actor->angle, -FixedMul(60*FRACUNIT, actor->scale));
-		jety = actor->y + P_ReturnThrustY(actor, actor->angle, -FixedMul(60*FRACUNIT, actor->scale));
-		if (actor->eflags & MFE_VERTICALFLIP)
-			jetz = actor->z + actor->height - FixedMul(17*FRACUNIT + mobjinfo[MT_PROPELLER].height, actor->scale);
-		else
-			jetz = actor->z + FixedMul(17*FRACUNIT, actor->scale);
-
-		filler = P_SpawnMobj(jetx, jety, jetz, MT_PROPELLER);
-		P_SetTarget(&filler->target, actor);
-		filler->destscale = actor->scale;
-		P_SetScale(filler, filler->destscale);
-		if (actor->eflags & MFE_VERTICALFLIP)
-			filler->flags2 |= MF2_OBJECTFLIP;
-		filler->angle = actor->angle - ANGLE_180;
-
-		P_SetTarget(&actor->tracer, filler);
-	}
-	else if (locvar1 == 2) // Metal Sonic jet fumes
-	{
-		filler = P_SpawnMobj(actor->x, actor->y, actor->z, MT_JETFUME1);
-		P_SetTarget(&filler->target, actor);
-		filler->fuse = 59;
-		P_SetTarget(&actor->tracer, filler);
-		filler->destscale = actor->scale/2;
-		P_SetScale(filler, filler->destscale);
-		if (actor->eflags & MFE_VERTICALFLIP)
-			filler->flags2 |= MF2_OBJECTFLIP;
-	}
-	else if (locvar1 == 3) // Boss 4 jet flame
-	{
-		fixed_t jetz;
-		if (actor->eflags & MFE_VERTICALFLIP)
-			jetz = actor->z + actor->height + FixedMul(50*FRACUNIT - mobjinfo[MT_JETFLAME].height, actor->scale);
-		else
-			jetz = actor->z - FixedMul(50*FRACUNIT, actor->scale);
-		filler = P_SpawnMobj(actor->x, actor->y, jetz, MT_JETFLAME);
-		P_SetTarget(&filler->target, actor);
-		// Boss 4 already uses its tracer for other things
-		filler->destscale = actor->scale;
-		P_SetScale(filler, filler->destscale);
-		if (actor->eflags & MFE_VERTICALFLIP)
-			filler->flags2 |= MF2_OBJECTFLIP;
-	}
-}
-
-// Function: A_RandomState
-//
-// Description: Chooses one of the two state numbers supplied randomly.
-//
-// var1 = state number 1
-// var2 = state number 2
-//
-void A_RandomState(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_RandomState", actor))
-		return;
-#endif
-
-	P_SetMobjState(actor, P_RandomChance(FRACUNIT/2) ? locvar1 : locvar2);
-}
-
-// Function: A_RandomStateRange
-//
-// Description: Chooses a random state within the range supplied.
-//
-// var1 = Minimum state number to choose.
-// var2 = Maximum state number to use.
-//
-void A_RandomStateRange(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_RandomStateRange", actor))
-		return;
-#endif
-
-	P_SetMobjState(actor, P_RandomRange(locvar1, locvar2));
-}
-
-// Function: A_DualAction
-//
-// Description: Calls two actions. Be careful, if you reference the same state this action is called from, you can create an infinite loop.
-//
-// var1 = state # to use 1st action from
-// var2 = state # to use 2nd action from
-//
-void A_DualAction(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_DualAction", actor))
-		return;
-#endif
-
-	CONS_Debug(DBG_GAMELOGIC, "A_DualAction called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
-
-	var1 = states[locvar1].var1;
-	var2 = states[locvar1].var2;
-#ifdef HAVE_BLUA
-	astate = &states[locvar1];
-#endif
-
-	CONS_Debug(DBG_GAMELOGIC, "A_DualAction: Calling First Action (state %d)...\n", locvar1);
-	states[locvar1].action.acp1(actor);
-
-	var1 = states[locvar2].var1;
-	var2 = states[locvar2].var2;
-#ifdef HAVE_BLUA
-	astate = &states[locvar2];
-#endif
-
-	CONS_Debug(DBG_GAMELOGIC, "A_DualAction: Calling Second Action (state %d)...\n", locvar2);
-	states[locvar2].action.acp1(actor);
-}
-
-// Function: A_RemoteAction
-//
-// Description: var1 is the remote object. var2 is the state reference for calling the action called on var1. var1 becomes the actor's target, the action (var2) is called on var1. actor's target is restored
-//
-// var1 = remote object (-2 uses tracer, -1 uses target)
-// var2 = state reference for calling an action
-//
-void A_RemoteAction(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	mobj_t *originaltarget = actor->target; // Hold on to the target for later.
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_RemoteAction", actor))
-		return;
-#endif
-
-	// If >=0, find the closest target.
-	if (locvar1 >= 0)
-	{
-		///* DO A_FINDTARGET STUFF *///
-		mobj_t *targetedmobj = NULL;
-		thinker_t *th;
-		mobj_t *mo2;
-		fixed_t dist1 = 0, dist2 = 0;
-
-		// scan the thinkers
-		for (th = thinkercap.next; th != &thinkercap; th = th->next)
-		{
-			if (th->function.acp1 != (actionf_p1)P_MobjThinker)
-				continue;
-
-			mo2 = (mobj_t *)th;
-
-			if (mo2->type == (mobjtype_t)locvar1)
-			{
-				if (targetedmobj == NULL)
-				{
-					targetedmobj = mo2;
-					dist2 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
-				}
-				else
-				{
-					dist1 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
-
-					if ((locvar2 && dist1 < dist2) || (!locvar2 && dist1 > dist2))
-					{
-						targetedmobj = mo2;
-						dist2 = dist1;
-					}
-				}
-			}
-		}
-
-		if (!targetedmobj)
-		{
-			CONS_Debug(DBG_GAMELOGIC, "A_RemoteAction: Unable to find the specified object to target.\n");
-			return; // Oops, nothing found..
-		}
-
-		CONS_Debug(DBG_GAMELOGIC, "A_RemoteAction: Found a target.\n");
-
-		P_SetTarget(&actor->target, targetedmobj);
-
-		///* END A_FINDTARGET STUFF *///
-	}
-
-	// If -2, use the tracer as the target
-	else if (locvar1 == -2)
-		P_SetTarget(&actor->target, actor->tracer);
-	// if -1 or anything else, just use the target.
-
-	if (actor->target)
-	{
-		// Steal the var1 and var2 from "locvar2"
-		var1 = states[locvar2].var1;
-		var2 = states[locvar2].var2;
-#ifdef HAVE_BLUA
-		astate = &states[locvar2];
-#endif
-
-		CONS_Debug(DBG_GAMELOGIC, "A_RemoteAction: Calling action on %p\n"
-				"var1 is %d\nvar2 is %d\n", actor->target, var1, var2);
-		states[locvar2].action.acp1(actor->target);
-	}
-
-	P_SetTarget(&actor->target, originaltarget); // Restore the original target.
-}
-
-// Function: A_ToggleFlameJet
-//
-// Description: Turns a flame jet on and off.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_ToggleFlameJet(mobj_t* actor)
-{
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_ToggleFlameJet", actor))
-		return;
-#endif
-	// threshold - off delay
-	// movecount - on timer
-
-	if (actor->flags2 & MF2_FIRING)
-	{
-		actor->flags2 &= ~MF2_FIRING;
-
-		if (actor->threshold)
-			actor->tics = actor->threshold;
-	}
-	else
-	{
-		actor->flags2 |= MF2_FIRING;
-
-		if (actor->movecount)
-			actor->tics = actor->movecount;
-	}
-}
-
-// Function: A_OrbitNights
-//
-// Description: Used by Chaos Emeralds to orbit around Nights (aka Super Sonic.)
-//
-// var1 = Angle adjustment (aka orbit speed)
-// var2 = Lower four bits: height offset, Upper 4 bits = set if object is Nightopian Helper
-//
-void A_OrbitNights(mobj_t* actor)
-{
-	INT32 ofs = (var2 & 0xFFFF);
-	boolean ishelper = (var2 & 0xFFFF0000);
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_OrbitNights", actor))
-		return;
-#endif
-
-	if (!actor->target || !actor->target->player ||
-	    !(actor->target->player->powers[pw_carry] == CR_NIGHTSMODE) || !actor->target->player->nightstime
-	    // Also remove this object if they no longer have a NiGHTS helper
-		|| (ishelper && !actor->target->player->powers[pw_nights_helper]))
-	{
-		P_RemoveMobj(actor);
-		return;
-	}
-	else
-	{
-		actor->extravalue1 += var1;
-		P_UnsetThingPosition(actor);
-		{
-			const angle_t fa  = (angle_t)actor->extravalue1 >> ANGLETOFINESHIFT;
-			const angle_t ofa = ((angle_t)actor->extravalue1 + (ofs*ANG1)) >> ANGLETOFINESHIFT;
-
-			const fixed_t fc = FixedMul(FINECOSINE(fa),FixedMul(32*FRACUNIT, actor->scale));
-			const fixed_t fh = FixedMul(FINECOSINE(ofa),FixedMul(20*FRACUNIT, actor->scale));
-			const fixed_t fs = FixedMul(FINESINE(fa),FixedMul(32*FRACUNIT, actor->scale));
-
-			actor->x = actor->target->x + fc;
-			actor->y = actor->target->y + fs;
-			actor->z = actor->target->z + fh + FixedMul(16*FRACUNIT, actor->scale);
-
-			// Semi-lazy hack
-			actor->angle = (angle_t)actor->extravalue1 + ANGLE_90;
-		}
-		P_SetThingPosition(actor);
-
-		if (ishelper) // Flash a helper that's about to be removed.
-		{
-			if ((actor->target->player->powers[pw_nights_helper] < TICRATE)
-			&& (actor->target->player->powers[pw_nights_helper] & 1))
-				actor->flags2 |= MF2_DONTDRAW;
-			else
-				actor->flags2 &= ~MF2_DONTDRAW;
-		}
-	}
-}
-
-// Function: A_GhostMe
-//
-// Description: Spawns a "ghost" mobj of this actor, ala spindash trails and the minus's digging "trails"
-//
-// var1 = duration in tics
-// var2 = unused
-//
-void A_GhostMe(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	mobj_t *ghost;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_GhostMe", actor))
-		return;
-#endif
-	ghost = P_SpawnGhostMobj(actor);
-	if (ghost && locvar1 > 0)
-		ghost->fuse = locvar1;
-}
-
-// Function: A_SetObjectState
-//
-// Description: Changes the state of an object's target/tracer.
-//
-// var1 = state number
-// var2:
-//		0 = target
-//		1 = tracer
-//
-void A_SetObjectState(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	mobj_t *target;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SetObjectState", actor))
-		return;
-#endif
-
-	if ((!locvar2 && !actor->target) || (locvar2 && !actor->tracer))
-	{
-		if (cv_debug)
-			CONS_Printf("A_SetObjectState: No target to change state!\n");
-		return;
-	}
-
-	if (!locvar2) // target
-		target = actor->target;
-	else // tracer
-		target = actor->tracer;
-
-	if (target->health > 0)
-	{
-		if (!target->player)
-			P_SetMobjState(target, locvar1);
-		else
-			P_SetPlayerMobjState(target, locvar1);
-	}
-}
-
-// Function: A_SetObjectTypeState
-//
-// Description: Changes the state of all active objects of a certain type in a certain range of the actor.
-//
-// var1 = state number
-// var2:
-//		lower 16 bits = type
-//		upper 16 bits = range (if == 0, across whole map)
-//
-void A_SetObjectTypeState(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	const UINT16 loc2lw = (UINT16)(locvar2 & 65535);
-	const UINT16 loc2up = (UINT16)(locvar2 >> 16);
-
-	thinker_t *th;
-	mobj_t *mo2;
-	fixed_t dist = 0;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SetObjectTypeState", actor))
-		return;
-#endif
-
-	for (th = thinkercap.next; th != &thinkercap; th = th->next)
-	{
-		if (th->function.acp1 != (actionf_p1)P_MobjThinker)
-			continue;
-
-		mo2 = (mobj_t *)th;
-
-		if (mo2->type == (mobjtype_t)loc2lw)
-		{
-			dist = P_AproxDistance(mo2->x - actor->x, mo2->y - actor->y);
-
-			if (mo2->health > 0)
-			{
-				if (loc2up == 0)
-					P_SetMobjState(mo2, locvar1);
-				else
-				{
-					if (dist <= FixedMul(loc2up*FRACUNIT, actor->scale))
-						P_SetMobjState(mo2, locvar1);
-				}
-			}
-		}
-	}
-}
-
-// Function: A_KnockBack
-//
-// Description: Knocks back the object's target at its current speed.
-//
-// var1:
-//		0 = target
-//		1 = tracer
-// var2 = unused
-//
-void A_KnockBack(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	mobj_t *target;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_KnockBack", actor))
-		return;
-#endif
-
-	if (!locvar1)
-		target = actor->target;
-	else
-		target = actor->tracer;
-
-	if (!target)
-	{
-		if(cv_debug)
-			CONS_Printf("A_KnockBack: No target!\n");
-		return;
-	}
-
-	target->momx *= -1;
-	target->momy *= -1;
-}
-
-// Function: A_PushAway
-//
-// Description: Pushes an object's target away from the calling object.
-//
-// var1 = amount of force
-// var2:
-//		lower 16 bits = If 1, xy momentum is lost. If 0, xy momentum is kept
-//		upper 16 bits = 0 - target, 1 - tracer
-//
-void A_PushAway(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	mobj_t *target; // target
-	angle_t an; // actor to target angle
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_PushAway", actor))
-		return;
-#endif
-
-	if ((!(locvar2 >> 16) && !actor->target) || ((locvar2 >> 16) && !actor->tracer))
-		return;
-
-	if (!locvar1)
-		CONS_Printf("A_Thrust: Var1 not specified!\n");
-
-	if (!(locvar2 >> 16)) // target
-		target = actor->target;
-	else // tracer
-		target = actor->tracer;
-
-	an = R_PointToAngle2(actor->x, actor->y, target->x, target->y);
-
-	if (locvar2 & 65535)
-		P_InstaThrust(target, an, FixedMul(locvar1*FRACUNIT, actor->scale));
-	else
-		P_Thrust(target, an, FixedMul(locvar1*FRACUNIT, actor->scale));
-}
-
-// Function: A_RingDrain
-//
-// Description: Drain targeted player's rings.
-//
-// var1 = ammount of drained rings
-// var2 = unused
-//
-void A_RingDrain(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	player_t *player;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_RingDrain", actor))
-		return;
-#endif
-
-	if (!actor->target || !actor->target->player)
-	{
-		if(cv_debug)
-			CONS_Printf("A_RingDrain: No player targeted!\n");
-		return;
-	}
-
-	player = actor->target->player;
-	P_GivePlayerRings(player, -min(locvar1, player->rings));
-}
-
-// Function: A_SplitShot
-//
-// Description: Shoots 2 missiles that hit next to the player.
-//
-// var1 = target x-y-offset
-// var2:
-//		lower 16 bits = missile type
-//		upper 16 bits = height offset
-//
-void A_SplitShot(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	const UINT16 loc2lw = (UINT16)(locvar2 & 65535);
-	const UINT16 loc2up = (UINT16)(locvar2 >> 16);
-	const fixed_t offs = (fixed_t)(locvar1*FRACUNIT);
-	const fixed_t hoffs = (fixed_t)(loc2up*FRACUNIT);
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SplitShot", actor))
-		return;
-#endif
-
-	A_FaceTarget(actor);
-	{
-		const angle_t an = (actor->angle + ANGLE_90) >> ANGLETOFINESHIFT;
-		const fixed_t fasin = FINESINE(an);
-		const fixed_t facos = FINECOSINE(an);
-		fixed_t xs = FixedMul(facos,FixedMul(offs, actor->scale));
-		fixed_t ys = FixedMul(fasin,FixedMul(offs, actor->scale));
-		fixed_t z;
-
-		if (actor->eflags & MFE_VERTICALFLIP)
-			z = actor->z + actor->height - FixedMul(hoffs, actor->scale);
-		else
-			z = actor->z + FixedMul(hoffs, actor->scale);
-
-		P_SpawnPointMissile(actor, actor->target->x+xs, actor->target->y+ys, actor->target->z, loc2lw, actor->x, actor->y, z);
-		P_SpawnPointMissile(actor, actor->target->x-xs, actor->target->y-ys, actor->target->z, loc2lw, actor->x, actor->y, z);
-	}
-}
-
-// Function: A_MissileSplit
-//
-// Description: If the object is a missile it will create a new missile with an alternate flight path owned by the one who shot the former missile.
-//
-// var1 = splitting missile type
-// var2 = splitting angle
-//
-void A_MissileSplit(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_MissileSplit", actor))
-		return;
-#endif
-	if (actor->eflags & MFE_VERTICALFLIP)
-		P_SpawnAlteredDirectionMissile(actor, locvar1, actor->x, actor->y, actor->z+actor->height, locvar2);
-	else
-		P_SpawnAlteredDirectionMissile(actor, locvar1, actor->x, actor->y, actor->z, locvar2);
-}
-
-// Function: A_MultiShot
-//
-// Description: Shoots objects horizontally that spread evenly in all directions.
-//
-// var1:
-//		lower 16 bits = number of missiles
-//		upper 16 bits = missile type #
-// var2 = height offset
-//
-void A_MultiShot(mobj_t *actor)
-{
-	fixed_t z, xr, yr;
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	const UINT16 loc1lw = (UINT16)(locvar1 & 65535);
-	const UINT16 loc1up = (UINT16)(locvar1 >> 16);
-	INT32 count = 0;
-	fixed_t ad;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_MultiShot", actor))
-		return;
-#endif
-
-	if (actor->target)
-		A_FaceTarget(actor);
-
-	if(loc1lw > 90)
-		ad = FixedMul(90*FRACUNIT, actor->scale);
-	else
-		ad = FixedMul(loc1lw*FRACUNIT, actor->scale);
-
-	if (actor->eflags & MFE_VERTICALFLIP)
-		z = actor->z + actor->height - FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
-	else
-		z = actor->z + FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
-	xr = FixedMul((P_SignedRandom()/3)<<FRACBITS, actor->scale); // please note p_mobj.c's P_Rand() abuse
-	yr = FixedMul((P_SignedRandom()/3)<<FRACBITS, actor->scale); // of rand(), RAND_MAX and signness mess
-
-	while(count <= loc1lw && loc1lw >= 1)
-	{
-		const angle_t fa = FixedAngleC(count*FRACUNIT*360, ad)>>ANGLETOFINESHIFT;
-		const fixed_t rc = FINECOSINE(fa);
-		const fixed_t rs = FINESINE(fa);
-		const fixed_t xrc = FixedMul(xr, rc);
-		const fixed_t yrs = FixedMul(yr, rs);
-		const fixed_t xrs = FixedMul(xr, rs);
-		const fixed_t yrc = FixedMul(yr, rc);
-
-		P_SpawnPointMissile(actor, xrc-yrs+actor->x, xrs+yrc+actor->y, z, loc1up, actor->x, actor->y, z);
-		count++;
-	}
-
-	if (!(actor->flags & MF_BOSS))
-	{
-		if (ultimatemode)
-			actor->reactiontime = actor->info->reactiontime*TICRATE;
-		else
-			actor->reactiontime = actor->info->reactiontime*TICRATE*2;
-	}
-}
-
-// Function: A_InstaLoop
-//
-// Description: Makes the object move along a 2d (view angle, z) polygon.
-//
-// var1:
-//		lower 16 bits = current step
-//		upper 16 bits = maximum step #
-// var2 = force
-//
-void A_InstaLoop(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	fixed_t force = max(locvar2, 1)*FRACUNIT; // defaults to 1 if var2 < 1
-	const UINT16 loc1lw = (UINT16)(locvar1 & 65535);
-	const UINT16 loc1up = (UINT16)(locvar1 >> 16);
-	const angle_t fa = FixedAngleC(loc1lw*FRACUNIT*360, loc1up*FRACUNIT)>>ANGLETOFINESHIFT;
-	const fixed_t ac = FINECOSINE(fa);
-	const fixed_t as = FINESINE(fa);
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_InstaLoop", actor))
-		return;
-#endif
-
-	P_InstaThrust(actor, actor->angle, FixedMul(ac, FixedMul(force, actor->scale)));
-	P_SetObjectMomZ(actor, FixedMul(as, force), false);
-}
-
-// Function: A_Custom3DRotate
-//
-// Description: Rotates the actor around its target in 3 dimensions.
-//
-// var1:
-//		lower 16 bits = radius in fracunits
-//		upper 16 bits = vertical offset
-// var2:
-//		lower 16 bits = vertical rotation speed in 1/10 fracunits per tic
-//		upper 16 bits = horizontal rotation speed in 1/10 fracunits per tic
-//
-void A_Custom3DRotate(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-
-	const UINT16 loc1lw = (UINT16)(locvar1 & 65535);
-	const UINT16 loc1up = (UINT16)(locvar1 >> 16);
-	const UINT16 loc2lw = (UINT16)(locvar2 & 65535);
-	const UINT16 loc2up = (UINT16)(locvar2 >> 16);
-
-	const fixed_t radius = FixedMul(loc1lw*FRACUNIT, actor->scale);
-	const fixed_t hOff = FixedMul(loc1up*FRACUNIT, actor->scale);
-	const fixed_t hspeed = FixedMul(loc2up*FRACUNIT/10, actor->scale);
-	const fixed_t vspeed = FixedMul(loc2lw*FRACUNIT/10, actor->scale);
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_Custom3DRotate", actor))
-		return;
-#endif
-
-	if (actor->target->health == 0)
-	{
-		P_RemoveMobj(actor);
-		return;
-	}
-
-	if (!actor->target) // This should NEVER happen.
-	{
-		if (cv_debug)
-			CONS_Printf("Error: Object has no target\n");
-		P_RemoveMobj(actor);
-		return;
-	}
-	if (hspeed==0 && vspeed==0)
-	{
-		CONS_Printf("Error: A_Custom3DRotate: Object has no speed.\n");
-		return;
-	}
-
-	actor->angle += FixedAngle(hspeed);
-	actor->movedir += FixedAngle(vspeed);
-	P_UnsetThingPosition(actor);
-	{
-		const angle_t fa = actor->angle>>ANGLETOFINESHIFT;
-
-		if (vspeed == 0 && hspeed != 0)
-		{
-			actor->x = actor->target->x + FixedMul(FINECOSINE(fa),radius);
-			actor->y = actor->target->y + FixedMul(FINESINE(fa),radius);
-			actor->z = actor->target->z + actor->target->height/2 - actor->height/2 + hOff;
-		}
-		else
-		{
-			const angle_t md = actor->movedir>>ANGLETOFINESHIFT;
-			actor->x = actor->target->x + FixedMul(FixedMul(FINESINE(md),FINECOSINE(fa)),radius);
-			actor->y = actor->target->y + FixedMul(FixedMul(FINESINE(md),FINESINE(fa)),radius);
-			actor->z = actor->target->z + FixedMul(FINECOSINE(md),radius) + actor->target->height/2 - actor->height/2 + hOff;
-		}
-	}
-	P_SetThingPosition(actor);
-}
-
-// Function: A_SearchForPlayers
-//
-// Description: Checks if the actor has targeted a vulnerable player. If not a new player will be searched all around. If no players are available the object can call a specific state. (Useful for not moving enemies)
-//
-// var1:
-//		if var1 == 0, if necessary call state with same state number as var2
-//		else, do not call a specific state if no players are available
-// var2 = state number
-//
-void A_SearchForPlayers(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SearchForPlayers", actor))
-		return;
-#endif
-
-	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
-	{
-		// look for a new target
-		if (P_LookForPlayers(actor, true, false, 0))
-			return; // got a new target
-
-		if(locvar1==0)
-		{
-			P_SetMobjStateNF(actor, locvar2);
-			return;
-		}
-	}
-}
-
-// Function: A_CheckRandom
-//
-// Description: Calls a state by chance.
-//
-// var1:
-//		lower 16 bits = denominator
-//		upper 16 bits = numerator (defaults to 1 if zero)
-// var2 = state number
-//
-void A_CheckRandom(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	fixed_t chance = FRACUNIT;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_CheckRandom", actor))
-		return;
-#endif
-	if ((locvar1 & 0xFFFF) == 0)
-		return;
-
-	// The PRNG doesn't suck anymore, OK?
-	if (locvar1 >> 16)
-		chance *= (locvar1 >> 16);
-	chance /= (locvar1 & 0xFFFF);
-
-	if (P_RandomChance(chance))
-		P_SetMobjState(actor, locvar2);
-}
-
-// Function: A_CheckTargetRings
-//
-// Description: Calls a state depending on the ammount of rings currently owned by targeted players.
-//
-// var1 = if player rings >= var1 call state
-// var2 = state number
-//
-void A_CheckTargetRings(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_CheckTargetRings", actor))
-		return;
-#endif
-
-	if (!(actor->target) || !(actor->target->player))
-	   return;
-
-	if (actor->target->player->rings >= locvar1)
-		P_SetMobjState(actor, locvar2);
-}
-
-// Function: A_CheckRings
-//
-// Description: Calls a state depending on the ammount of rings currently owned by all players.
-//
-// var1 = if player rings >= var1 call state
-// var2 = state number
-//
-void A_CheckRings(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	INT32 i, cntr = 0;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_CheckRings", actor))
-		return;
-#endif
-
-	for (i = 0; i < MAXPLAYERS; i++)
-		cntr += players[i].rings;
-
-	if (cntr >= locvar1)
-		P_SetMobjState(actor, locvar2);
-}
-
-// Function: A_CheckTotalRings
-//
-// Description: Calls a state depending on the maximum ammount of rings owned by all players during this try.
-//
-// var1 = if total player rings >= var1 call state
-// var2 = state number
-//
-void A_CheckTotalRings(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-
-	INT32 i, cntr = 0;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_CheckTotalRings", actor))
-		return;
-#endif
-
-	for (i = 0; i < MAXPLAYERS; i++)
-		cntr += players[i].totalring;
-
-	if (cntr >= locvar1)
-		P_SetMobjState(actor, locvar2);
-}
-
-// Function: A_CheckHealth
-//
-// Description: Calls a state depending on the object's current health.
-//
-// var1 = if health <= var1 call state
-// var2 = state number
-//
-void A_CheckHealth(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_CheckHealth", actor))
-		return;
-#endif
-
-	if (actor->health <= locvar1)
-		P_SetMobjState(actor, locvar2);
-}
-
-// Function: A_CheckRange
-//
-// Description: Calls a state if the object's target is in range.
-//
-// var1:
-//		lower 16 bits = range
-//		upper 16 bits = 0 - target, 1 - tracer
-// var2 = state number
-//
-void A_CheckRange(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	fixed_t dist;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_CheckRange", actor))
-		return;
-#endif
-
-	if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer))
-		return;
-
-	if (!(locvar1 >> 16)) //target
-		dist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
-	else //tracer
-		dist = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y);
-
-	if (dist <= FixedMul((locvar1 & 65535)*FRACUNIT, actor->scale))
-		P_SetMobjState(actor, locvar2);
-}
-
-// Function: A_CheckHeight
-//
-// Description: Calls a state if the object and it's target have a height offset <= var1 compared to each other.
-//
-// var1:
-//		lower 16 bits = height offset
-//		upper 16 bits = 0 - target, 1 - tracer
-// var2 = state number
-//
-void A_CheckHeight(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	fixed_t height;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_CheckHeight", actor))
-		return;
-#endif
-
-	if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer))
-		return;
-
-	if (!(locvar1 >> 16)) // target
-		height = abs(actor->target->z - actor->z);
-	else // tracer
-		height = abs(actor->tracer->z - actor->z);
-
-	if (height <= FixedMul((locvar1 & 65535)*FRACUNIT, actor->scale))
-		P_SetMobjState(actor, locvar2);
-}
-
-// Function: A_CheckTrueRange
-//
-// Description: Calls a state if the object's target is in true range. (Checks height, too.)
-//
-// var1:
-//		lower 16 bits = range
-//		upper 16 bits = 0 - target, 1 - tracer
-// var2 = state number
-//
-void A_CheckTrueRange(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	fixed_t height; // vertical range
-	fixed_t dist; // horizontal range
-	fixed_t l; // true range
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_CheckTrueRange", actor))
-		return;
-#endif
-
-	if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer))
-		return;
-
-	if (!(locvar1 >> 16)) // target
-	{
-		height = actor->target->z - actor->z;
-		dist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
-
-	}
-	else // tracer
-	{
-		height = actor->tracer->z - actor->z;
-		dist = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y);
-	}
-
-	l = P_AproxDistance(dist, height);
-
-	if (l <= FixedMul((locvar1 & 65535)*FRACUNIT, actor->scale))
-		P_SetMobjState(actor, locvar2);
-
-}
-
-// Function: A_CheckThingCount
-//
-// Description: Calls a state depending on the number of active things in range.
-//
-// var1:
-//		lower 16 bits = number of things
-//		upper 16 bits = thing type
-// var2:
-//		lower 16 bits = state to call
-//		upper 16 bits = range (if == 0, check whole map)
-//
-void A_CheckThingCount(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-
-	const UINT16 loc1lw = (UINT16)(locvar1 & 65535);
-	const UINT16 loc1up = (UINT16)(locvar1 >> 16);
-	const UINT16 loc2lw = (UINT16)(locvar2 & 65535);
-	const UINT16 loc2up = (UINT16)(locvar2 >> 16);
-
-	INT32 count = 0;
-	thinker_t *th;
-	mobj_t *mo2;
-	fixed_t dist = 0;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_CheckThingCount", actor))
-		return;
-#endif
-
-	for (th = thinkercap.next; th != &thinkercap; th = th->next)
-	{
-		if (th->function.acp1 != (actionf_p1)P_MobjThinker)
-			continue;
-
-		mo2 = (mobj_t *)th;
-
-		if (mo2->type == (mobjtype_t)loc1up)
-		{
-			dist = P_AproxDistance(mo2->x - actor->x, mo2->y - actor->y);
-
-			if (loc2up == 0)
-				count++;
-			else
-			{
-				if (dist <= FixedMul(loc2up*FRACUNIT, actor->scale))
-					count++;
-			}
-		}
-	}
-
-	if(loc1lw <= count)
-		P_SetMobjState(actor, loc2lw);
-}
-
-// Function: A_CheckAmbush
-//
-// Description: Calls a state if the actor is behind its targeted player.
-//
-// var1:
-//		0 = target
-//		1 = tracer
-// var2 = state number
-//
-void A_CheckAmbush(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	angle_t at; // angle target is currently facing
-	angle_t atp; // actor to target angle
-	angle_t an; // angle between at and atp
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_CheckAmbush", actor))
-		return;
-#endif
-
-	if ((!locvar1 && !actor->target) || (locvar1 && !actor->tracer))
-		return;
-
-	if (!locvar1) // target
-	{
-		at = actor->target->angle;
-		atp = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
-	}
-	else // tracer
-	{
-		at = actor->tracer->angle;
-		atp = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y);
-	}
-
-	an = atp - at;
-
-	if (an > ANGLE_180) // flip angle if bigger than 180
-		an = InvAngle(an);
-
-	if (an < ANGLE_90+ANGLE_22h) // within an angle of 112.5 from each other?
-		P_SetMobjState(actor, locvar2);
-}
-
-// Function: A_CheckCustomValue
-//
-// Description: Calls a state depending on the object's custom value.
-//
-// var1 = if custom value >= var1, call state
-// var2 = state number
-//
-void A_CheckCustomValue(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_CheckCustomValue", actor))
-		return;
-#endif
-
-	if (actor->cusval >= locvar1)
-		P_SetMobjState(actor, locvar2);
-}
-
-// Function: A_CheckCusValMemo
-//
-// Description: Calls a state depending on the object's custom memory value.
-//
-// var1 = if memory value >= var1, call state
-// var2 = state number
-//
-void A_CheckCusValMemo(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_CheckCusValMemo", actor))
-		return;
-#endif
-
-	if (actor->cvmem >= locvar1)
-		P_SetMobjState(actor, locvar2);
-}
-
-// Function: A_SetCustomValue
-//
-// Description: Changes the custom value of an object.
-//
-// var1 = manipulating value
-// var2:
-//      if var2 == 5, multiply the custom value by var1
-//      else if var2 == 4, divide the custom value by var1
-//      else if var2 == 3, apply modulo var1 to the custom value
-//      else if var2 == 2, add var1 to the custom value
-//      else if var2 == 1, substract var1 from the custom value
-//      else if var2 == 0, replace the custom value with var1
-//
-void A_SetCustomValue(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SetCustomValue", actor))
-		return;
-#endif
-
-	if (cv_debug)
-		CONS_Printf("Init custom value is %d\n", actor->cusval);
-
-	if (locvar1 == 0 && locvar2 == 4)
-		return; // DON'T DIVIDE BY ZERO
-
-	// no need for a "temp" value here, just modify the cusval directly
-	if (locvar2 == 5) // multiply
-		actor->cusval *= locvar1;
-	else if (locvar2 == 4) // divide
-		actor->cusval /= locvar1;
-	else if (locvar2 == 3) // modulo
-		actor->cusval %= locvar1;
-	else if (locvar2 == 2) // add
-		actor->cusval += locvar1;
-	else if (locvar2 == 1) // subtract
-		actor->cusval -= locvar1;
-	else // replace
-		actor->cusval = locvar1;
-
-	if(cv_debug)
-		CONS_Printf("New custom value is %d\n", actor->cusval);
-}
-
-// Function: A_UseCusValMemo
-//
-// Description: Memorizes or recalls a current custom value.
-//
-// var1:
-//      if var1 == 1, manipulate memory value
-//      else, recall memory value replacing the custom value
-// var2:
-//      if var2 == 5, mem = mem*cv  ||  cv = cv*mem
-//      else if var2 == 4,  mem = mem/cv  ||  cv = cv/mem
-//      else if var2 == 3, mem = mem%cv  ||  cv = cv%mem
-//      else if var2 == 2, mem += cv  ||  cv += mem
-//      else if var2 == 1,  mem -= cv  ||  cv -= mem
-//      else mem = cv  ||  cv = mem
-//
-void A_UseCusValMemo(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-
-	INT32 temp = actor->cusval; // value being manipulated
-	INT32 tempM = actor->cvmem; // value used to manipulate temp with
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_UseCusValMemo", actor))
-		return;
-#endif
-
-	if (locvar1 == 1) // cvmem being changed using cusval
-	{
-		temp = actor->cvmem;
-		tempM = actor->cusval;
-	}
-	else // cusval being changed with cvmem
-	{
-		temp = actor->cusval;
-		tempM = actor->cvmem;
-	}
-
-	if (tempM == 0 && locvar2 == 4)
-		return; // DON'T DIVIDE BY ZERO
-
-	// now get new value for cusval/cvmem using the other
-	if (locvar2 == 5) // multiply
-		temp *= tempM;
-	else if (locvar2 == 4) // divide
-		temp /= tempM;
-	else if (locvar2 == 3) // modulo
-		temp %= tempM;
-	else if (locvar2 == 2) // add
-		temp += tempM;
-	else if (locvar2 == 1) // subtract
-		temp -= tempM;
-	else // replace
-		temp = tempM;
-
-	// finally, give cusval/cvmem the new value!
-	if (locvar1 == 1)
-		actor->cvmem = temp;
-	else
-		actor->cusval = temp;
-}
-
-// Function: A_RelayCustomValue
-//
-// Description: Manipulates the custom value of the object's target/tracer.
-//
-// var1:
-//		lower 16 bits:
-//					if var1 == 0, use own custom value
-//					else, use var1 value
-//		upper 16 bits = 0 - target, 1 - tracer
-// var2:
-//      if var2 == 5, multiply the target's custom value by var1
-//      else if var2 == 4, divide the target's custom value by var1
-//      else if var2 == 3, apply modulo var1 to the target's custom value
-//      else if var2 == 2, add var1 to the target's custom value
-//      else if var2 == 1, substract var1 from the target's custom value
-//      else if var2 == 0, replace the target's custom value with var1
-//
-void A_RelayCustomValue(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-
-	INT32 temp; // reference value - var1 lower 16 bits changes this
-	INT32 tempT; // target's value - changed to tracer if var1 upper 16 bits set, then modified to become final value
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_RelayCustomValue", actor))
-		return;
-#endif
-
-	if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer))
-		return;
-
-	// reference custom value
-	if ((locvar1 & 65535) == 0)
-		temp = actor->cusval; // your own custom value
-	else
-		temp = (locvar1 & 65535); // var1 value
-
-	if (!(locvar1 >> 16)) // target's custom value
-		tempT = actor->target->cusval;
-	else // tracer's custom value
-		tempT = actor->tracer->cusval;
-
-	if (temp == 0 && locvar2 == 4)
-		return; // DON'T DIVIDE BY ZERO
-
-	// now get new cusval using target's and the reference
-	if (locvar2 == 5) // multiply
-		tempT *= temp;
-	else if (locvar2 == 4) // divide
-		tempT /= temp;
-	else if (locvar2 == 3) // modulo
-		tempT %= temp;
-	else if (locvar2 == 2) // add
-		tempT += temp;
-	else if (locvar2 == 1) // subtract
-		tempT -= temp;
-	else // replace
-		tempT = temp;
-
-	// finally, give target/tracer the new cusval!
-	if (!(locvar1 >> 16)) // target
-		actor->target->cusval = tempT;
-	else // tracer
-		actor->tracer->cusval = tempT;
-}
-
-// Function: A_CusValAction
-//
-// Description: Calls an action from a reference state applying custom value parameters.
-//
-// var1 = state # to use action from
-// var2:
-//      if var2 == 5, only replace new action's var2 with memory value
-//      else if var2 == 4, only replace new action's var1 with memory value
-//      else if var2 == 3, replace new action's var2 with custom value and var1 with memory value
-//      else if var2 == 2, replace new action's var1 with custom value and var2 with memory value
-//      else if var2 == 1, only replace new action's var2 with custom value
-//      else if var2 == 0, only replace new action's var1 with custom value
-//
-void A_CusValAction(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_CusValAction", actor))
-		return;
-#endif
-
-	if (locvar2 == 5)
-	{
-		var1 = states[locvar1].var1;
-		var2 = (INT32)actor->cvmem;
-	}
-	else if (locvar2 == 4)
-	{
-		var1 = (INT32)actor->cvmem;
-		var2 = states[locvar1].var2;
-	}
-	else if (locvar2 == 3)
-	{
-		var1 = (INT32)actor->cvmem;
-		var2 = (INT32)actor->cusval;
-	}
-	else if (locvar2 == 2)
-	{
-		var1 = (INT32)actor->cusval;
-		var2 = (INT32)actor->cvmem;
-	}
-	else if (locvar2 == 1)
-	{
-		var1 = states[locvar1].var1;
-		var2 = (INT32)actor->cusval;
-	}
-	else
-	{
-		var1 = (INT32)actor->cusval;
-		var2 = states[locvar1].var2;
-	}
-
-#ifdef HAVE_BLUA
-	astate = &states[locvar1];
-#endif
-	states[locvar1].action.acp1(actor);
-}
-
-// Function: A_ForceStop
-//
-// Description: Actor immediately stops its current movement.
-//
-// var1:
-//      if var1 == 0, stop x-y-z-movement
-//      else, stop x-y-movement only
-// var2 = unused
-//
-void A_ForceStop(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_ForceStop", actor))
-		return;
-#endif
-
-	actor->momx = actor->momy = 0;
-	if (locvar1 == 0)
-		actor->momz = 0;
-}
-
-// Function: A_ForceWin
-//
-// Description: Makes all players win the level.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_ForceWin(mobj_t *actor)
-{
-	INT32 i;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_ForceWin", actor))
-		return;
-#else
-	(void)actor;
-#endif
-
-	for (i = 0; i < MAXPLAYERS; i++)
-	{
-		if (playeringame[i] && ((players[i].mo && players[i].mo->health)
-		    || ((netgame || multiplayer) && (players[i].lives || players[i].continues))))
-			break;
-	}
-
-	if (i == MAXPLAYERS)
-		return;
-
-	for (i = 0; i < MAXPLAYERS; i++)
-		P_DoPlayerExit(&players[i]);
-}
-
-// Function: A_SpikeRetract
-//
-// Description: Toggles actor solid flag.
-//
-// var1:
-//        if var1 == 0, actor no collide
-//        else, actor solid
-// var2 = unused
-//
-void A_SpikeRetract(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SpikeRetract", actor))
-		return;
-#endif
-
-	if (actor->flags & MF_NOBLOCKMAP)
-		return;
-
-	if (locvar1 == 0)
-	{
-		actor->flags &= ~MF_SOLID;
-		actor->flags |= MF_NOCLIPTHING;
-	}
-	else
-	{
-		actor->flags |= MF_SOLID;
-		actor->flags &= ~MF_NOCLIPTHING;
-	}
-	if (actor->flags & MF_SOLID)
-		P_CheckPosition(actor, actor->x, actor->y);
-}
-
-// Function: A_InfoState
-//
-// Description: Set mobj state to one predefined in mobjinfo.
-//
-// var1:
-//        if var1 == 0, set actor to spawnstate
-//        else if var1 == 1, set actor to seestate
-//        else if var1 == 2, set actor to meleestate
-//        else if var1 == 3, set actor to missilestate
-//        else if var1 == 4, set actor to deathstate
-//        else if var1 == 5, set actor to xdeathstate
-//        else if var1 == 6, set actor to raisestate
-// var2 = unused
-//
-void A_InfoState(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	switch (locvar1)
-	{
-	case 0:
-		if (actor->state != &states[actor->info->spawnstate])
-			P_SetMobjState(actor, actor->info->spawnstate);
-		break;
-	case 1:
-		if (actor->state != &states[actor->info->seestate])
-			P_SetMobjState(actor, actor->info->seestate);
-		break;
-	case 2:
-		if (actor->state != &states[actor->info->meleestate])
-			P_SetMobjState(actor, actor->info->meleestate);
-		break;
-	case 3:
-		if (actor->state != &states[actor->info->missilestate])
-			P_SetMobjState(actor, actor->info->missilestate);
-		break;
-	case 4:
-		if (actor->state != &states[actor->info->deathstate])
-			P_SetMobjState(actor, actor->info->deathstate);
-		break;
-	case 5:
-		if (actor->state != &states[actor->info->xdeathstate])
-			P_SetMobjState(actor, actor->info->xdeathstate);
-		break;
-	case 6:
-		if (actor->state != &states[actor->info->raisestate])
-			P_SetMobjState(actor, actor->info->raisestate);
-		break;
-	default:
-		break;
-	}
-}
-
-// Function: A_Repeat
-//
-// Description: Returns to state var2 until animation has been used var1 times, then continues to nextstate.
-//
-// var1 = repeat count
-// var2 = state to return to if extravalue2 > 0
-//
-void A_Repeat(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_Repeat", actor))
-		return;
-#endif
-
-	if (locvar1 && (!actor->extravalue2 || actor->extravalue2 > locvar1))
-		actor->extravalue2 = locvar1;
-
-	if (--actor->extravalue2 > 0)
-		P_SetMobjState(actor, locvar2);
-}
-
-// Function: A_SetScale
-//
-// Description: Changes the scale of the actor or its target/tracer
-//
-// var1 = new scale (1*FRACUNIT = 100%)
-// var2:
-//        upper 16 bits: 0 = actor, 1 = target, 2 = tracer
-//        lower 16 bits: 0 = instant change, 1 = smooth change
-//
-void A_SetScale(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	mobj_t *target;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SetScale", actor))
-		return;
-#endif
-
-	if (locvar1 <= 0)
-	{
-		if(cv_debug)
-			CONS_Printf("A_SetScale: Valid scale not specified!\n");
-		return;
-	}
-
-	if ((locvar2>>16) == 1)
-		target = actor->target;
-	else if ((locvar2>>16) == 2)
-		target = actor->tracer;
-	else // default to yourself!
-		target = actor;
-
-	if (!target)
-	{
-		if(cv_debug)
-			CONS_Printf("A_SetScale: No target!\n");
-		return;
-	}
-
-	target->destscale = locvar1; // destination scale
-	if (!(locvar2 & 65535))
-		P_SetScale(target, locvar1); // this instantly changes current scale to var1 if used, if not destscale will alter scale to var1 anyway
-}
-
-// Function: A_RemoteDamage
-//
-// Description: Damages, kills or even removes either the actor or its target/tracer. Actor acts as the inflictor/source unless harming itself
-//
-// var1 = Mobj affected: 0 - actor, 1 - target, 2 - tracer
-// var2 = Action: 0 - Damage, 1 - Kill, 2 - Remove
-//
-void A_RemoteDamage(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	mobj_t *target; // we MUST have a target
-	mobj_t *source = NULL; // on the other hand we don't necessarily need a source
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_RemoteDamage", actor))
-		return;
-#endif
-	if (locvar1 == 1)
-		target = actor->target;
-	else if (locvar1 == 2)
-		target = actor->tracer;
-	else // default to yourself!
-		target = actor;
-
-	if (locvar1 == 1 || locvar1 == 2)
-		source = actor;
-
-	if (!target)
-	{
-		if(cv_debug)
-			CONS_Printf("A_RemoteDamage: No target!\n");
-		return;
-	}
-
-	if (locvar2 == 1) // Kill mobj!
-	{
-		if (target->player) // players die using P_DamageMobj instead for some reason
-			P_DamageMobj(target, source, source, 1, DMG_INSTAKILL);
-		else
-			P_KillMobj(target, source, source, 0);
-	}
-	else if (locvar2 == 2) // Remove mobj!
-	{
-		if (target->player) //don't remove players!
-			return;
-
-		P_RemoveMobj(target);
-	}
-	else // default: Damage mobj!
-		P_DamageMobj(target, source, source, 1, 0);
-}
-
-// Function: A_HomingChase
-//
-// Description: Actor chases directly towards its destination object
-//
-// var1 = speed multiple
-// var2 = destination: 0 = target, 1 = tracer
-//
-void A_HomingChase(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	mobj_t *dest;
-	fixed_t dist;
-	fixed_t speedmul;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_HomingChase", actor))
-		return;
-#endif
-
-	if (locvar2 == 1)
-		dest = actor->tracer;
-	else //default
-		dest = actor->target;
-
-	if (!dest || !dest->health)
-		return;
-
-	actor->angle = R_PointToAngle2(actor->x, actor->y, dest->x, dest->y);
-
-	dist = P_AproxDistance(P_AproxDistance(dest->x - actor->x, dest->y - actor->y), dest->z - actor->z);
-
-	if (dist < 1)
-		dist = 1;
-
-	speedmul = FixedMul(locvar1, actor->scale);
-
-	actor->momx = FixedMul(FixedDiv(dest->x - actor->x, dist), speedmul);
-	actor->momy = FixedMul(FixedDiv(dest->y - actor->y, dist), speedmul);
-	actor->momz = FixedMul(FixedDiv(dest->z - actor->z, dist), speedmul);
-}
-
-// Function: A_TrapShot
-//
-// Description: Fires a missile in a particular direction and angle rather than AT something, Trapgoyle-style!
-//
-// var1:
-//        lower 16 bits = object # to fire
-//        upper 16 bits = front offset
-// var2:
-//        lower 15 bits = vertical angle variable
-//        16th bit:
-//			- 0: use vertical angle variable as vertical angle in degrees
-//			- 1: mimic P_SpawnXYZMissile
-//				use z of actor minus z of missile as vertical distance to cover during momz calculation
-//				use vertical angle variable as horizontal distance to cover during momz calculation
-//        upper 16 bits = height offset
-//
-void A_TrapShot(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	boolean oldstyle = (locvar2 & 32768) ? true : false;
-	mobjtype_t type = (mobjtype_t)(locvar1 & 65535);
-	mobj_t *missile;
-	INT16 frontoff = (INT16)(locvar1 >> 16);
-	INT16 vertoff = (INT16)(locvar2 >> 16);
-	fixed_t x, y, z;
-	fixed_t speed;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_TrapShot", actor))
-		return;
-#endif
-
-	x = actor->x + P_ReturnThrustX(actor, actor->angle, FixedMul(frontoff*FRACUNIT, actor->scale));
-	y = actor->y + P_ReturnThrustY(actor, actor->angle, FixedMul(frontoff*FRACUNIT, actor->scale));
-
-	if (actor->eflags & MFE_VERTICALFLIP)
-		z = actor->z + actor->height - FixedMul(vertoff*FRACUNIT, actor->scale) - FixedMul(mobjinfo[type].height, actor->scale);
-	else
-		z = actor->z + FixedMul(vertoff*FRACUNIT, actor->scale);
-
-	CONS_Debug(DBG_GAMELOGIC, "A_TrapShot: missile no. = %d, front offset = %d, vertical angle = %d, z offset = %d\n",
-		type, frontoff, (INT16)(locvar2 & 65535), vertoff);
-
-	missile = P_SpawnMobj(x, y, z, type);
-
-	if (actor->eflags & MFE_VERTICALFLIP)
-		missile->flags2 |= MF2_OBJECTFLIP;
-
-	missile->destscale = actor->scale;
-	P_SetScale(missile, actor->scale);
-
-	if (missile->info->seesound)
-		S_StartSound(missile, missile->info->seesound);
-
-	P_SetTarget(&missile->target, actor);
-	missile->angle = actor->angle;
-
-	speed = FixedMul(missile->info->speed, missile->scale);
-
-	if (oldstyle)
-	{
-		missile->momx = FixedMul(FINECOSINE(missile->angle>>ANGLETOFINESHIFT), speed);
-		missile->momy = FixedMul(FINESINE(missile->angle>>ANGLETOFINESHIFT), speed);
-		// The below line basically mimics P_SpawnXYZMissile's momz calculation.
-		missile->momz = (actor->z + ((actor->eflags & MFE_VERTICALFLIP) ? actor->height : 0) - z) / ((fixed_t)(locvar2 & 32767)*FRACUNIT / speed);
-		P_CheckMissileSpawn(missile);
-	}
-	else
-	{
-		angle_t vertang = FixedAngle(((INT16)(locvar2 & 32767))*FRACUNIT);
-		if (actor->eflags & MFE_VERTICALFLIP)
-				vertang = InvAngle(vertang); // flip firing angle
-		missile->momx = FixedMul(FINECOSINE(vertang>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(missile->angle>>ANGLETOFINESHIFT), speed));
-		missile->momy = FixedMul(FINECOSINE(vertang>>ANGLETOFINESHIFT), FixedMul(FINESINE(missile->angle>>ANGLETOFINESHIFT), speed));
-		missile->momz = FixedMul(FINESINE(vertang>>ANGLETOFINESHIFT), speed);
-	}
-}
-
-// Function: A_VileTarget
-//
-// Description: Spawns an object directly on the target, and sets this object as the actor's tracer.
-//              Originally used by Archviles to summon a pillar of hellfire, hence the name.
-//
-// var1 = mobj to spawn
-// var2 = If 0, target only the actor's target. Else, target every player, period.
-//
-void A_VileTarget(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	mobj_t *fog;
-	mobjtype_t fogtype;
-	INT32 i;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_VileTarget", actor))
-		return;
-#endif
-
-	if (!actor->target)
-		return;
-
-	A_FaceTarget(actor);
-
-	// Determine object to spawn
-	if (locvar1 <= 0 || locvar1 >= NUMMOBJTYPES)
-		fogtype = MT_CYBRAKDEMON_TARGET_RETICULE;
-	else
-		fogtype = (mobjtype_t)locvar1;
-
-	if (!locvar2)
-	{
-		fog = P_SpawnMobj(actor->target->x,
-							actor->target->y,
-							actor->target->z + ((actor->target->eflags & MFE_VERTICALFLIP) ? actor->target->height - mobjinfo[fogtype].height : 0),
-							fogtype);
-		if (actor->target->eflags & MFE_VERTICALFLIP)
-		{
-			fog->eflags |= MFE_VERTICALFLIP;
-			fog->flags2 |= MF2_OBJECTFLIP;
-		}
-		fog->destscale = actor->target->scale;
-		P_SetScale(fog, fog->destscale);
-
-		P_SetTarget(&actor->tracer, fog);
-		P_SetTarget(&fog->target, actor);
-		P_SetTarget(&fog->tracer, actor->target);
-		A_VileFire(fog);
-	}
-	else
-	{
-		// Our "Archvile" here is actually Oprah. "YOU GET A TARGET! YOU GET A TARGET! YOU ALL GET A TARGET!"
-		for (i = 0; i < MAXPLAYERS; i++)
-		{
-			if (!playeringame[i] || players[i].spectator)
-				continue;
-
-			if (!players[i].mo)
-				continue;
-
-			if (!players[i].mo->health)
-				continue;
-
-			fog = P_SpawnMobj(players[i].mo->x,
-							players[i].mo->y,
-							players[i].mo->z + ((players[i].mo->eflags & MFE_VERTICALFLIP) ? players[i].mo->height - mobjinfo[fogtype].height : 0),
-							fogtype);
-			if (players[i].mo->eflags & MFE_VERTICALFLIP)
-			{
-				fog->eflags |= MFE_VERTICALFLIP;
-				fog->flags2 |= MF2_OBJECTFLIP;
-			}
-			fog->destscale = players[i].mo->scale;
-			P_SetScale(fog, fog->destscale);
-
-			if (players[i].mo == actor->target) // We only care to track the fog targeting who we REALLY hate right now
-				P_SetTarget(&actor->tracer, fog);
-			P_SetTarget(&fog->target, actor);
-			P_SetTarget(&fog->tracer, players[i].mo);
-			A_VileFire(fog);
-		}
-	}
-}
-
-// Function: A_VileAttack
-//
-// Description: Instantly hurts the actor's target, if it's in the actor's line of sight.
-//              Originally used by Archviles to cause explosions where their hellfire pillars were, hence the name.
-//
-// var1 = sound to play
-// var2:
-//		Lower 16 bits = optional explosion object
-//		Upper 16 bits = If 0, attack only the actor's target. Else, attack all the players. All of them.
-//
-void A_VileAttack(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	sfxenum_t soundtoplay;
-	mobjtype_t explosionType = MT_NULL;
-	mobj_t *fire;
-	INT32 i;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_VileAttack", actor))
-		return;
-#endif
-
-	if (!actor->target)
-		return;
-
-	A_FaceTarget(actor);
-
-	if (locvar1 <= 0 || locvar1 >= NUMSFX)
-		soundtoplay = sfx_brakrx;
-	else
-		soundtoplay = (sfxenum_t)locvar1;
-
-	if ((locvar2 & 0xFFFF) > 0 && (locvar2 & 0xFFFF) <= NUMMOBJTYPES)
-	{
-		explosionType = (mobjtype_t)(locvar2 & 0xFFFF);
-	}
-
-	if (!(locvar2 & 0xFFFF0000)) {
-		if (!P_CheckSight(actor, actor->target))
-			return;
-
-		S_StartSound(actor, soundtoplay);
-		P_DamageMobj(actor->target, actor, actor, 1, 0);
-		//actor->target->momz = 1000*FRACUNIT/actor->target->info->mass; // How id did it
-		actor->target->momz += FixedMul(10*FRACUNIT, actor->scale)*P_MobjFlip(actor->target); // How we're doing it
-		if (explosionType != MT_NULL)
-		{
-			P_SpawnMobj(actor->target->x, actor->target->y, actor->target->z, explosionType);
-		}
-
-		// Extra attack. This was for additional damage in Doom. Doesn't really belong in SRB2, but the heck with it, it's here anyway.
-		fire = actor->tracer;
-
-		if (!fire)
-			return;
-
-		// move the fire between the vile and the player
-		//fire->x = actor->target->x - FixedMul (24*FRACUNIT, finecosine[an]);
-		//fire->y = actor->target->y - FixedMul (24*FRACUNIT, finesine[an]);
-		P_TeleportMove(fire,
-						actor->target->x - P_ReturnThrustX(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)),
-						actor->target->y - P_ReturnThrustY(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)),
-						fire->z);
-		P_RadiusAttack(fire, actor, 70*FRACUNIT);
-	}
-	else
-	{
-		// Oprahvile strikes again, but this time, she brings HOT PAIN
-		for (i = 0; i < MAXPLAYERS; i++)
-		{
-			if (!playeringame[i] || players[i].spectator)
-				continue;
-
-			if (!players[i].mo)
-				continue;
-
-			if (!players[i].mo->health)
-				continue;
-
-			if (!P_CheckSight(actor, players[i].mo))
-				continue;
-
-			S_StartSound(actor, soundtoplay);
-			P_DamageMobj(players[i].mo, actor, actor, 1, 0);
-			//actor->target->momz = 1000*FRACUNIT/actor->target->info->mass; // How id did it
-			players[i].mo->momz += FixedMul(10*FRACUNIT, actor->scale)*P_MobjFlip(players[i].mo); // How we're doing it
-			if (explosionType != MT_NULL)
-			{
-				P_SpawnMobj(players[i].mo->x, players[i].mo->y, players[i].mo->z, explosionType);
-			}
-
-			// Extra attack. This was for additional damage in Doom. Doesn't really belong in SRB2, but the heck with it, it's here anyway.
-			// However, it ONLY applies to the actor's target. Nobody else matters!
-			if (actor->target != players[i].mo)
-				continue;
-
-			fire = actor->tracer;
-
-			if (!fire)
-				continue;
-
-			// move the fire between the vile and the player
-			//fire->x = actor->target->x - FixedMul (24*FRACUNIT, finecosine[an]);
-			//fire->y = actor->target->y - FixedMul (24*FRACUNIT, finesine[an]);
-			P_TeleportMove(fire,
-							actor->target->x - P_ReturnThrustX(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)),
-							actor->target->y - P_ReturnThrustY(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)),
-							fire->z);
-			P_RadiusAttack(fire, actor, 70*FRACUNIT);
-		}
-	}
-
-}
-
-// Function: A_VileFire
-//
-// Description: Kind of like A_CapeChase; keeps this object in front of its tracer, unless its target can't see it.
-//				Originally used by Archviles to keep their hellfire pillars on top of the player, hence the name (although it was just "A_Fire" there; added "Vile" to make it more specific).
-//				Added some functionality to optionally draw a line directly to the enemy doing the targetting. Y'know, to hammer things in a bit.
-//
-// var1 = sound to play
-// var2:
-//		Lower 16 bits = mobj to spawn (0 doesn't spawn a line at all)
-//		Upper 16 bits = # to spawn (default is 8)
-//
-void A_VileFire(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	mobj_t *dest;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_VileFire", actor))
-		return;
-#endif
-
-	dest = actor->tracer;
-	if (!dest)
-		return;
-
-	// don't move it if the vile lost sight
-	if (!P_CheckSight(actor->target, dest))
-		return;
-
-	// keep to same scale and gravity as tracer ALWAYS
-	actor->destscale = dest->scale;
-	P_SetScale(actor, actor->destscale);
-	if (dest->eflags & MFE_VERTICALFLIP)
-	{
-		actor->eflags |= MFE_VERTICALFLIP;
-		actor->flags2 |= MF2_OBJECTFLIP;
-	}
-	else
-	{
-		actor->eflags &= ~MFE_VERTICALFLIP;
-		actor->flags2 &= ~MF2_OBJECTFLIP;
-	}
-
-	P_UnsetThingPosition(actor);
-	actor->x = dest->x + P_ReturnThrustX(actor, dest->angle, FixedMul(24*FRACUNIT, actor->scale));
-	actor->y = dest->y + P_ReturnThrustY(actor, dest->angle, FixedMul(24*FRACUNIT, actor->scale));
-	actor->z = dest->z + ((actor->eflags & MFE_VERTICALFLIP) ? dest->height-actor->height : 0);
-	P_SetThingPosition(actor);
-
-	// Play sound, if one's specified
-	if (locvar1 > 0 && locvar1 < NUMSFX)
-		S_StartSound(actor, (sfxenum_t)locvar1);
-
-	// Now draw the line to the actor's target
-	if (locvar2 & 0xFFFF)
-	{
-		mobjtype_t lineMobj;
-		UINT16 numLineMobjs;
-		fixed_t distX;
-		fixed_t distY;
-		fixed_t distZ;
-		UINT16 i;
-
-		lineMobj = (mobjtype_t)(locvar2 & 0xFFFF);
-		numLineMobjs = (UINT16)(locvar2 >> 16);
-		if (numLineMobjs == 0) {
-			numLineMobjs = 8;
-		}
-
-		// Get distance for each step
-		distX = (actor->target->x - actor->x) / numLineMobjs;
-		distY = (actor->target->y - actor->y) / numLineMobjs;
-		distZ = ((actor->target->z + FixedMul(actor->target->height/2, actor->target->scale)) - (actor->z + FixedMul(actor->height/2, actor->scale))) / numLineMobjs;
-
-		for (i = 1; i <= numLineMobjs; i++)
-		{
-			P_SpawnMobj(actor->x + (distX * i), actor->y + (distY * i), actor->z + (distZ * i) + FixedMul(actor->height/2, actor->scale), lineMobj);
-		}
-	}
-}
-
-// Function: A_BrakChase
-//
-// Description: Chase after your target, but speed and attack are tied to health.
-//
-// Every time this is called, generate a random number from a 1/4 to 3/4 of mobj's spawn health.
-// If health is above that value, use missilestate to attack.
-// If health is at or below that value, use meleestate to attack (default to missile state if not available).
-//
-// Likewise, state will linearly speed up as health goes down.
-// Upper bound will be the frame's normal length.
-// Lower bound defaults to 1 tic (technically 0, but we round up), unless a lower bound is specified in var1.
-//
-// var1 = lower-bound of frame length, in tics
-// var2 = optional sound to play
-//
-void A_BrakChase(mobj_t *actor)
-{
-	INT32 delta;
-	INT32 lowerbound;
-	INT32 newtics;
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_BrakChase", actor))
-		return;
-#endif
-
-	// Set new tics NOW, in case the state changes while we're doing this and we try applying this to the painstate or something silly
-	if (actor->tics > 1 && locvar1 < actor->tics) // Not much point, otherwise
-	{
-		if (locvar1 < 0)
-			lowerbound = 0;
-		else
-			lowerbound = locvar1;
-
-		newtics = (((actor->tics - lowerbound) * actor->health) / actor->info->spawnhealth) + lowerbound;
-		if (newtics < 1)
-			newtics = 1;
-
-		actor->tics = newtics;
-	}
-
-	if (actor->reactiontime)
-	{
-		actor->reactiontime--;
-		if (actor->reactiontime == 0 && actor->type == MT_CYBRAKDEMON)
-			S_StartSound(0, sfx_bewar1 + P_RandomKey(4));
-	}
-
-	// modify target threshold
-	if (actor->threshold)
-	{
-		if (!actor->target || actor->target->health <= 0)
-			actor->threshold = 0;
-		else
-			actor->threshold--;
-	}
-
-	// turn towards movement direction if not there yet
-	if (actor->movedir < NUMDIRS)
-	{
-		actor->angle &= (7<<29);
-		delta = actor->angle - (actor->movedir << 29);
-
-		if (delta > 0)
-			actor->angle -= ANGLE_45;
-		else if (delta < 0)
-			actor->angle += ANGLE_45;
-	}
-
-	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
-	{
-		// look for a new target
-		if (P_LookForPlayers(actor, true, false, 0))
-			return; // got a new target
-
-		P_SetMobjStateNF(actor, actor->info->spawnstate);
-		return;
-	}
-
-	// do not attack twice in a row
-	if (actor->flags2 & MF2_JUSTATTACKED)
-	{
-		actor->flags2 &= ~MF2_JUSTATTACKED;
-		P_NewChaseDir(actor);
-		return;
-	}
-
-	// Check if we can attack
-	if (P_CheckMissileRange(actor) && !actor->movecount)
-	{
-		// Check if we should use "melee" attack first. (Yes, this still runs outside of melee range. Quiet, you.)
-		if (actor->info->meleestate
-			&& actor->health <= P_RandomRange(actor->info->spawnhealth/4, (actor->info->spawnhealth * 3)/4)) // Guaranteed true if <= 1/4 health, guaranteed false if > 3/4 health
-		{
-			if (actor->info->attacksound)
-				S_StartAttackSound(actor, actor->info->attacksound);
-
-			P_SetMobjState(actor, actor->info->meleestate);
-			actor->flags2 |= MF2_JUSTATTACKED;
-			return;
-		}
-		// Else, check for missile attack.
-		else if (actor->info->missilestate)
-		{
-			P_SetMobjState(actor, actor->info->missilestate);
-			actor->flags2 |= MF2_JUSTATTACKED;
-			return;
-		}
-	}
-
-	// possibly choose another target
-	if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
-		&& P_LookForPlayers(actor, true, false, 0))
-		return; // got a new target
-
-	// chase towards player
-	if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
-		P_NewChaseDir(actor);
-
-	// Optionally play a sound effect
-	if (locvar2 > 0 && locvar2 < NUMSFX)
-		S_StartSound(actor, (sfxenum_t)locvar2);
-
-	// make active sound
-	if (actor->type != MT_CYBRAKDEMON && actor->info->activesound && P_RandomChance(3*FRACUNIT/256))
-	{
-		S_StartSound(actor, actor->info->activesound);
-	}
-}
-
-// Function: A_BrakFireShot
-//
-// Description: Shoot an object at your target, offset to match where Brak's gun is.
-// Also, sets Brak's reaction time; behaves normally otherwise.
-//
-// var1 = object # to shoot
-// var2 = unused
-//
-void A_BrakFireShot(mobj_t *actor)
-{
-	fixed_t x, y, z;
-	INT32 locvar1 = var1;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_BrakFireShot", actor))
-		return;
-#endif
-	if (!actor->target)
-		return;
-
-	A_FaceTarget(actor);
-
-	x = actor->x
-		+ P_ReturnThrustX(actor, actor->angle, FixedMul(64*FRACUNIT, actor->scale))
-		+ P_ReturnThrustX(actor, actor->angle+ANGLE_270, FixedMul(32*FRACUNIT, actor->scale));
-	y = actor->y
-		+ P_ReturnThrustY(actor, actor->angle, FixedMul(64*FRACUNIT, actor->scale))
-		+ P_ReturnThrustY(actor, actor->angle+ANGLE_270, FixedMul(32*FRACUNIT, actor->scale));
-	if (actor->eflags & MFE_VERTICALFLIP)
-		z = actor->z + actor->height - FixedMul(144*FRACUNIT, actor->scale);
-	else
-		z = actor->z + FixedMul(144*FRACUNIT, actor->scale);
-
-	P_SpawnXYZMissile(actor, actor->target, locvar1, x, y, z);
-
-	if (!(actor->flags & MF_BOSS))
-	{
-		if (ultimatemode)
-			actor->reactiontime = actor->info->reactiontime*TICRATE;
-		else
-			actor->reactiontime = actor->info->reactiontime*TICRATE*2;
-	}
-}
-
-// Function: A_BrakLobShot
-//
-// Description: Lobs an object at the floor about a third of the way toward your target.
-//				Implication is it'll bounce the rest of the way.
-//				(You can also just aim straight at the target, but whatever)
-//				Formula grabbed from http://en.wikipedia.org/wiki/Trajectory_of_a_projectile#Angle_required_to_hit_coordinate_.28x.2Cy.29
-//
-// var1 = object # to lob
-// var2:
-//		Lower 16 bits: height offset to shoot from, from the actor's bottom (none that "airtime" malarky)
-//		Upper 16 bits: if 0, aim 1/3 of the way. Else, aim directly at target.
-//
-
-void A_BrakLobShot(mobj_t *actor)
-{
-	fixed_t v; // Velocity to shoot object
-	fixed_t a1, a2, aToUse; // Velocity squared
-	fixed_t g; // Gravity
-	fixed_t x; // Horizontal difference
-	INT32 x_int; // x! But in integer form!
-	fixed_t y; // Vertical difference (yes that's normally z in SRB2 shut up)
-	INT32 y_int; // y! But in integer form!
-	INT32 intHypotenuse; // x^2 + y^2. Frequently overflows fixed point, hence why we need integers proper.
-	fixed_t fixedHypotenuse; // However, we can work around that and still get a fixed-point number.
-	angle_t theta; // Angle of attack
-	mobjtype_t typeOfShot;
-	mobj_t *shot; // Object to shoot
-	fixed_t newTargetX; // If not aiming directly
-	fixed_t newTargetY; // If not aiming directly
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2 & 0x0000FFFF;
-	INT32 aimDirect = var2 & 0xFFFF0000;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_BrakLobShot", actor))
-		return;
-#endif
-
-	if (!actor->target)
-		return; // Don't even bother if we've got nothing to aim at.
-
-	// Look up actor's current gravity situation
-	if (actor->subsector->sector->gravity)
-		g = FixedMul(gravity,(FixedDiv(*actor->subsector->sector->gravity>>FRACBITS, 1000)));
-	else
-		g = gravity;
-
-	// Look up distance between actor and its target
-	x = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
-	if (!aimDirect)
-	{
-		// Distance should actually be a third of the way over
-		x = FixedDiv(x, 3<<FRACBITS);
-		newTargetX = actor->x + P_ReturnThrustX(actor, actor->angle, x);
-		newTargetY = actor->y + P_ReturnThrustY(actor, actor->angle, x);
-		x = P_AproxDistance(newTargetX - actor->x, newTargetY - actor->y);
-		// Look up height difference between actor and the ground 1/3 of the way to its target
-		y = P_FloorzAtPos(newTargetX, newTargetY, actor->target->z, actor->target->height) - (actor->z + FixedMul(locvar2*FRACUNIT, actor->scale));
-	}
-	else
-	{
-		// Look up height difference between actor and its target
-		y = actor->target->z - (actor->z + FixedMul(locvar2*FRACUNIT, actor->scale));
-	}
-
-	// Get x^2 + y^2. Have to do it in a roundabout manner, because this overflows fixed_t way too easily otherwise.
-	x_int = x>>FRACBITS;
-	y_int = y>>FRACBITS;
-	intHypotenuse = (x_int*x_int) + (y_int*y_int);
-	fixedHypotenuse = FixedSqrt(intHypotenuse) *256;
-
-	// a = g(y+/-sqrt(x^2+y^2)). a1 can be +, a2 can be -.
-	a1 = FixedMul(g,y+fixedHypotenuse);
-	a2 = FixedMul(g,y-fixedHypotenuse);
-
-	// Determine which one isn't actually an imaginary number (or the smaller of the two, if both are real), and use that for v.
-	if (a1 < 0 || a2 < 0)
-	{
-		if (a1 < 0 && a2 < 0)
-		{
-			//Somehow, v^2 is negative in both cases. v is therefore imaginary and something is horribly wrong. Abort!
-			return;
-		}
-		// Just find which one's NOT negative, and use that
-		aToUse = max(a1,a2);
-	}
-	else
-	{
-		// Both are positive; use whichever's smaller so it can decay faster
-		aToUse = min(a1,a2);
-	}
-	v = FixedSqrt(aToUse);
-	// Okay, so we know the velocity. Let's actually find theta.
-	// We can cut the "+/- sqrt" part out entirely, since v was calculated specifically for it to equal zero. So:
-	//theta = tantoangle[FixedDiv(aToUse,FixedMul(g,x)) >> DBITS];
-	theta = tantoangle[SlopeDiv(aToUse,FixedMul(g,x))];
-
-	// Okay, complicated math done. Let's fire our object already, sheesh.
-	A_FaceTarget(actor);
-	if (locvar1 <= 0 || locvar1 >= NUMMOBJTYPES)
-		typeOfShot = MT_CANNONBALL;
-	else typeOfShot = (mobjtype_t)locvar1;
-	shot = P_SpawnMobj(actor->x, actor->y, actor->z + FixedMul(locvar2*FRACUNIT, actor->scale), typeOfShot);
-	if (shot->info->seesound)
-		S_StartSound(shot, shot->info->seesound);
-	P_SetTarget(&shot->target, actor); // where it came from
-
-	shot->angle = actor->angle;
-
-	// Horizontal axes first. First parameter is initial horizontal impulse, second is to correct its angle.
-	shot->momx = FixedMul(FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)), FINECOSINE(shot->angle >> ANGLETOFINESHIFT));
-	shot->momy = FixedMul(FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)), FINESINE(shot->angle >> ANGLETOFINESHIFT));
-	// Then the vertical axis. No angle-correction needed here.
-	shot->momz = FixedMul(v, FINESINE(theta >> ANGLETOFINESHIFT));
-	// I hope that's all that's needed, ugh
-}
-
-// Function: A_NapalmScatter
-//
-// Description: Scatters a specific number of projectiles around in a circle.
-//				Intended for use with objects that are affected by gravity; would be kind of silly otherwise.
-//
-// var1:
-//		Lower 16 bits: object # to lob (TODO: come up with a default)
-//		Upper 16 bits: Number to lob (default 8)
-// var2:
-//		Lower 16 bits: distance to toss them (No default - 0 does just that - but negatives will revert to 128)
-//		Upper 16 bits: airtime in tics (default 16)
-//
-void A_NapalmScatter(mobj_t *actor)
-{
-	mobjtype_t typeOfShot = var1 & 0x0000FFFF; // Type
-	INT32 numToShoot = (var1 & 0xFFFF0000) >> 16; // How many
-	fixed_t distance = (var2 & 0x0000FFFF) << FRACBITS; // How far
-	fixed_t airtime = var2 & 0xFFFF0000; // How long until impact (assuming no obstacles)
-	fixed_t vx; // Horizontal momentum
-	fixed_t vy; // Vertical momentum
-	fixed_t g; // Gravity
-	INT32 i; // for-loop cursor
-	mobj_t *mo; // each and every spawned napalm burst
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_NapalmScatter", actor))
-		return;
-#endif
-
-	// Some quick sanity-checking
-	if (typeOfShot >= NUMMOBJTYPES) // I'd add a <0 check, too, but 0x0000FFFF isn't negative in this case
-		typeOfShot = MT_NULL;
-	if (numToShoot <= 0) // Presumably you forgot to set var1 up; else, why are you calling this to shoot nothing?
-		numToShoot = 8;
-	else if (numToShoot > 8192) // If you seriously need this many objects spawned, stop and ask yourself "Why am I doing this?"
-		numToShoot = 8192;
-	if (distance < 0) // Presumably you thought this was an unsigned integer, you naive fool
-		distance = 32767<<FRACBITS;
-	if (airtime <= 0) // Same deal as distance I guess
-		airtime = 16<<FRACBITS;
-
-	// Look up actor's current gravity situation
-	if (actor->subsector->sector->gravity)
-		g = FixedMul(gravity,(FixedDiv(*actor->subsector->sector->gravity>>FRACBITS, 1000)));
-	else
-		g = gravity;
-
-	// vy = (g*(airtime-1))/2
-	vy = FixedMul(g,(airtime-(1<<FRACBITS)))>>1;
-	// vx = distance/airtime
-	vx = FixedDiv(distance, airtime);
-
-	for (i = 0; i<numToShoot; i++)
-	{
-		const angle_t fa = (i*FINEANGLES/numToShoot) & FINEMASK;
-
-		mo = P_SpawnMobj(actor->x, actor->y, actor->z, typeOfShot);
-		P_SetTarget(&mo->target, actor->target); // Transfer target so Brak doesn't hit himself like an idiot
-
-		mo->angle = fa << ANGLETOFINESHIFT;
-		mo->momx = FixedMul(FINECOSINE(fa),vx);
-		mo->momy = FixedMul(FINESINE(fa),vx);
-		mo->momz = vy;
-	}
-}
-
-// Function: A_SpawnFreshCopy
-//
-// Description: Spawns a copy of the mobj. x, y, z, angle, scale, target and tracer carry over; everything else starts anew.
-//				Mostly writing this because I want to do multiple actions to pass these along in a single frame instead of several.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_SpawnFreshCopy(mobj_t *actor)
-{
-	mobj_t *newObject;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_SpawnFreshCopy", actor))
-		return;
-#endif
-
-	newObject = P_SpawnMobj(actor->x, actor->y, actor->z, actor->type);
-	newObject->angle = actor->angle;
-	P_SetScale(newObject, actor->scale);
-	newObject->destscale = actor->destscale;
-	P_SetTarget(&newObject->target, actor->target);
-	P_SetTarget(&newObject->tracer, actor->tracer);
-
-	if (newObject->info->seesound)
-		S_StartSound(newObject, newObject->info->seesound);
-}
-
-// Internal Flicky spawning function.
-mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz, boolean lookforplayers)
-{
-	mobj_t *flicky;
-
-	if (!flickytype)
-	{
-		if (!mapheaderinfo[gamemap-1] || !mapheaderinfo[gamemap-1]->numFlickies) // No mapheader, no shoes, no service.
-			return NULL;
-		else
-		{
-			INT32 prandom = P_RandomKey(mapheaderinfo[gamemap-1]->numFlickies);
-			flickytype = mapheaderinfo[gamemap-1]->flickies[prandom];
-		}
-	}
-
-	flicky = P_SpawnMobjFromMobj(actor, 0, 0, 0, flickytype);
-	flicky->angle = actor->angle;
-
-	if (flickytype == MT_SEED)
-		flicky->z += P_MobjFlip(actor)*(actor->height - flicky->height)/2;
-
-	if (actor->eflags & MFE_UNDERWATER)
-		momz = FixedDiv(momz, FixedSqrt(3*FRACUNIT));
-
-	P_SetObjectMomZ(flicky, momz, false);
-	flicky->movedir = (P_RandomChance(FRACUNIT/2) ?  -1 : 1);
-	flicky->fuse = P_RandomRange(595, 700);	// originally 300, 350
-	flicky->threshold = 0;
-
-	if (lookforplayers)
-		P_LookForPlayers(flicky, true, false, 0);
-
-	return flicky;
-}
-
-// Function: A_FlickySpawn
-//
-// Description: Flicky spawning function.
-//
-// var1:
-//		lower 16 bits: if 0, spawns random flicky based on level header. Else, spawns the designated thing type.
-//		upper 16 bits: if 0, no sound is played. Else, A_Scream is called.
-// var2 = upwards thrust for spawned flicky. If zero, default value is provided.
-//
-void A_FlickySpawn(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_FlickySpawn", actor))
-		return;
-#endif
-
-	if (locvar1 >> 16) {
-		A_Scream(actor); // A shortcut for the truly lazy.
-		locvar1 &= 65535;
-	}
-
-	P_InternalFlickySpawn(actor, locvar1, ((locvar2) ? locvar2 : 8*FRACUNIT), true);
-}
-
-// Internal Flicky bubbling function.
-void P_InternalFlickyBubble(mobj_t *actor)
-{
-	if (actor->eflags & MFE_UNDERWATER)
-	{
-		mobj_t *overlay;
-
-		if (!((actor->z + 3*actor->height/2) < actor->watertop) || !mobjinfo[actor->type].raisestate || actor->tracer)
-			return;
-
-		overlay = P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY);
-		P_SetMobjStateNF(overlay, mobjinfo[actor->type].raisestate);
-		P_SetTarget(&actor->tracer, overlay);
-		P_SetTarget(&overlay->target, actor);
-		return;
-	}
-
-	if (!actor->tracer || P_MobjWasRemoved(actor->tracer))
-		return;
-
-	P_RemoveMobj(actor->tracer);
-	P_SetTarget(&actor->tracer, NULL);
-}
-
-// Function: A_FlickyAim
-//
-// Description: Flicky aiming function.
-//
-// var1 = how far around the target (in angle constants) the flicky should look
-// var2 = distance from target to aim for
-//
-void A_FlickyAim(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	boolean flickyhitwall = false;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_FlickyAim", actor))
-		return;
-#endif
-
-	if (actor->momx == actor->momy && actor->momy == 0)
-		flickyhitwall = true;
-
-	P_InternalFlickyBubble(actor);
-	P_InstaThrust(actor, 0, 0);
-
-	if (!actor->target)
-	{
-		P_LookForPlayers(actor, true, false, 0);
-		actor->angle = P_RandomKey(36)*ANG10;
-		return;
-	}
-
-	if (actor->fuse > 2*TICRATE)
-	{
-		angle_t posvar;
-		fixed_t chasevar, chasex, chasey;
-
-		if (flickyhitwall)
-			actor->movedir *= -1;
-
-		posvar = ((R_PointToAngle2(actor->target->x, actor->target->y, actor->x, actor->y) + actor->movedir*locvar1) >> ANGLETOFINESHIFT) & FINEMASK;
-		chasevar = FixedSqrt(max(FRACUNIT, P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y) - locvar2)) + locvar2;
-
-		chasex = actor->target->x + FixedMul(FINECOSINE(posvar), chasevar);
-		chasey = actor->target->y + FixedMul(FINESINE(posvar), chasevar);
-
-		if (P_AproxDistance(chasex - actor->x, chasey - actor->y))
-			actor->angle = R_PointToAngle2(actor->x, actor->y, chasex, chasey);
-	}
-	else if (flickyhitwall)
-	{
-		actor->angle += ANGLE_180;
-		actor->threshold = 0;
-	}
-}
-
-//Internal Flicky flying function. Also usuable as an underwater swim thrust.
-void P_InternalFlickyFly(mobj_t *actor, fixed_t flyspeed, fixed_t targetdist, fixed_t chasez)
-{
-	angle_t vertangle;
-
-	flyspeed = FixedMul(flyspeed, actor->scale);
-	actor->flags |= MF_NOGRAVITY;
-
-	var1 = ANG30;
-	var2 = 32*FRACUNIT;
-	A_FlickyAim(actor);
-
-	chasez *= 8;
-	if (!actor->target || !(actor->fuse > 2*TICRATE))
-		chasez += ((actor->eflags & MFE_VERTICALFLIP) ? actor->ceilingz - 24*FRACUNIT : actor->floorz + 24*FRACUNIT);
-	else
-	{
-		fixed_t add = actor->target->z + (actor->target->height - actor->height)/2;
-		if (add > (actor->ceilingz - 24*actor->scale - actor->height))
-			add = actor->ceilingz - 24*actor->scale - actor->height;
-		else if (add < (actor->floorz + 24*actor->scale))
-			add = actor->floorz + 24*actor->scale;
-		chasez += add;
-	}
-
-	if (!targetdist)
-		targetdist = 16*FRACUNIT; //Default!
-
-	if (actor->target && abs(chasez - actor->z) > targetdist)
-		targetdist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
-
-	vertangle = (R_PointToAngle2(0, actor->z, targetdist, chasez) >> ANGLETOFINESHIFT) & FINEMASK;
-	P_InstaThrust(actor, actor->angle, FixedMul(FINECOSINE(vertangle), flyspeed));
-	actor->momz = FixedMul(FINESINE(vertangle), flyspeed);
-}
-
-// Function: A_FlickyFly
-//
-// Description: Flicky flying function.
-//
-// var1 = how fast to fly
-// var2 = how far ahead the target should be considered
-//
-void A_FlickyFly(mobj_t *actor)
-{
-	// We're not setting up locvars here - it passes var1 and var2 through to P_InternalFlickyFly instead.
-	//INT32 locvar1 = var1;
-	//INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_FlickyFly", actor))
-		return;
-#endif
-	P_InternalFlickyFly(actor, var1, var2,
-	FINECOSINE((((actor->fuse % 36) * ANG10) >> ANGLETOFINESHIFT) & FINEMASK)
-	);
-}
-
-// Function: A_FlickySoar
-//
-// Description: Flicky soaring function - specific to puffin.
-//
-// var1 = how fast to fly
-// var2 = how far ahead the target should be considered
-//
-void A_FlickySoar(mobj_t *actor)
-{
-	// We're not setting up locvars here - it passes var1 and var2 through to P_InternalFlickyFly instead.
-	//INT32 locvar1 = var1;
-	//INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_FlickySoar", actor))
-		return;
-#endif
-	P_InternalFlickyFly(actor, var1, var2,
-	2*(FRACUNIT/2 - abs(FINECOSINE((((actor->fuse % 144) * 5*ANG1/2) >> ANGLETOFINESHIFT) & FINEMASK)))
-	);
-
-	if (P_MobjFlip(actor)*actor->momz > 0 && actor->frame == 1 && actor->sprite == SPR_FL10)
-		actor->frame = 3;
-}
-
-//Function: A_FlickyCoast
-//
-// Description: Flicky swim-coasting function.
-//
-// var1 = speed to change state upon reaching
-// var2 = state to change to upon slowing down
-// the spawnstate of the mobj = state to change to when above water
-//
-void A_FlickyCoast(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_FlickyCoast", actor))
-		return;
-#endif
-	if (actor->eflags & MFE_UNDERWATER)
-	{
-		actor->momx = (11*actor->momx)/12;
-		actor->momy = (11*actor->momy)/12;
-		actor->momz = (11*actor->momz)/12;
-
-		if (P_AproxDistance(P_AproxDistance(actor->momx, actor->momy), actor->momz) < locvar1)
-			P_SetMobjState(actor, locvar2);
-
-		return;
-	}
-
-	actor->flags &= ~MF_NOGRAVITY;
-	P_SetMobjState(actor, mobjinfo[actor->type].spawnstate);
-}
-
-// Internal Flicky hopping function.
-void P_InternalFlickyHop(mobj_t *actor, fixed_t momz, fixed_t momh, angle_t angle)
-{
-	if (((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)
-	|| ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)))
-	{
-		if (momz)
-		{
-			if (actor->eflags & MFE_UNDERWATER)
-				momz = FixedDiv(momz, FixedSqrt(3*FRACUNIT));
-			P_SetObjectMomZ(actor, momz, false);
-		}
-		P_InstaThrust(actor, angle, FixedMul(momh, actor->scale));
-	}
-}
-
-// Function: A_FlickyHop
-//
-// Description: Flicky hopping function.
-//
-// var1 = vertical thrust
-// var2 = horizontal thrust
-//
-void A_FlickyHop(mobj_t *actor)
-{
-	// We're not setting up locvars here - it passes var1 and var2 through to P_InternalFlickyHop instead.
-	//INT32 locvar1 = var1;
-	//INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_FlickyHop", actor))
-		return;
-#endif
-	P_InternalFlickyHop(actor, var1, var2, actor->angle);
-}
-
-// Function: A_FlickyFlounder
-//
-// Description: Flicky floundering function.
-//
-// var1 = intended vertical thrust
-// var2 = intended horizontal thrust
-//
-void A_FlickyFlounder(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-	angle_t hopangle;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_FlickyFlounder", actor))
-		return;
-#endif
-	locvar1 *= (P_RandomKey(2) + 1);
-	locvar2 *= (P_RandomKey(2) + 1);
-	hopangle = (actor->angle + (P_RandomKey(9) - 4)*ANG2);
-	P_InternalFlickyHop(actor, locvar1, locvar2, hopangle);
-}
-
-// Function: A_FlickyCheck
-//
-// Description: Flicky airtime check function.
-//
-// var1 = state to change to upon touching the floor
-// var2 = state to change to upon falling
-// the meleestate of the mobj = state to change to when underwater
-//
-void A_FlickyCheck(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_FlickyCheck", actor))
-		return;
-#endif
-	if (locvar2 && P_MobjFlip(actor)*actor->momz < 1)
-		P_SetMobjState(actor, locvar2);
-	else if (locvar1 && ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)
-	|| ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)))
-		P_SetMobjState(actor, locvar1);
-	else if (mobjinfo[actor->type].meleestate && (actor->eflags & MFE_UNDERWATER))
-		P_SetMobjState(actor, mobjinfo[actor->type].meleestate);
-	P_InternalFlickyBubble(actor);
-}
-
-// Function: A_FlickyHeightCheck
-//
-// Description: Flicky height check function.
-//
-// var1 = state to change to when falling below height relative to target
-// var2 = height relative to target to change state at
-//
-void A_FlickyHeightCheck(mobj_t *actor)
-{
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_FlickyHeightCheck", actor))
-		return;
-#endif
-	if (locvar1 && actor->target && P_MobjFlip(actor)*actor->momz < 1
-	&& ((P_MobjFlip(actor)*((actor->z + actor->height/2) - (actor->target->z + actor->target->height/2)) < locvar2)
-	|| (actor->z - actor->height < actor->floorz) || (actor->z + 2*actor->height > actor->ceilingz)))
-		P_SetMobjState(actor, locvar1);
-	P_InternalFlickyBubble(actor);
-}
-
-// Function: A_FlickyFlutter
-//
-// Description: Flicky fluttering function - specific to chicken.
-//
-// var1 = state to change to upon touching the floor
-// var2 = state to change to upon falling
-// the meleestate of the mobj = state to change to when underwater
-//
-void A_FlickyFlutter(mobj_t *actor)
-{
-	// We're not setting up locvars here - it passes var1 and var2 through to A_FlickyCheck instead.
-	//INT32 locvar1 = var1;
-	//INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_FlickyFlutter", actor))
-		return;
-#endif
-	A_FlickyCheck(actor);
-
-	var1 = ANG30;
-	var2 = 32*FRACUNIT;
-	A_FlickyAim(actor);
-
-	P_InstaThrust(actor, actor->angle, 2*actor->scale);
-	if (P_MobjFlip(actor)*actor->momz < -FRACUNIT/2)
-		actor->momz = -P_MobjFlip(actor)*actor->scale/2;
-}
-
-#undef FLICKYHITWALL
-
-// Function: A_FlameParticle
-//
-// Description: Creates the mobj's painchance at a random position around the object's radius.
-//
-// var1 = momz of particle.
-// var2 = chance of particle spawn
-//
-void A_FlameParticle(mobj_t *actor)
-{
-	mobjtype_t type = (mobjtype_t)(mobjinfo[actor->type].painchance);
-	fixed_t rad, hei;
-	mobj_t *particle;
-	INT32 locvar1 = var1;
-	INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_FlameParticle", actor))
-		return;
-#endif
-
-	if (!P_RandomChance(locvar2))
-		return;
-
-	if (!type)
-		return;
-
-	rad = 2*actor->radius>>FRACBITS;
-	hei = actor->height>>FRACBITS;
-	particle = P_SpawnMobjFromMobj(actor,
-		P_RandomRange(rad, -rad)<<FRACBITS,
-		P_RandomRange(rad, -rad)<<FRACBITS,
-		P_RandomRange(hei/2, hei)<<FRACBITS,
-		type);
-	P_SetObjectMomZ(particle, locvar1<<FRACBITS, false);
-}
-
-// Function: A_FadeOverlay
-//
-// Description: Makes a pretty overlay (primarily for super/NiGHTS transformation).
-//
-// var1 = bit 1 = don't halt momentum, bit 2 = don't make fast, bit 3 = don't set tracer
-// var2 = unused
-//
-void A_FadeOverlay(mobj_t *actor)
-{
-	mobj_t *fade;
-	INT32 locvar1 = var1;
-	//INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_FadeOverlay", actor))
-		return;
-#endif
-
-	if (!(locvar1 & 1))
-		actor->momx = actor->momy = actor->momz = 0;
-
-	fade = P_SpawnGhostMobj(actor);
-	fade->frame = actor->frame;
-
-	if (!(locvar1 & 2))
-	{
-		fade->fuse = 15;
-		fade->flags2 |= MF2_BOSSNOTRAP;
-	}
-	else
-		fade->fuse = 20;
-
-	if (!(locvar1 & 4))
-		P_SetTarget(&actor->tracer, fade);
-}
-
-// Function: A_Boss5Jump
-//
-// Description: Makes an object jump in an arc to land on their tracer precicely.
-//				Adapted from A_BrakLobShot, see there for explanation.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_Boss5Jump(mobj_t *actor)
-{
-	fixed_t v; // Velocity to jump at
-	fixed_t a1, a2, aToUse; // Velocity squared
-	fixed_t g; // Gravity
-	fixed_t x; // Horizontal difference
-	INT32 x_int; // x! But in integer form!
-	fixed_t y; // Vertical difference (yes that's normally z in SRB2 shut up)
-	INT32 y_int; // y! But in integer form!
-	INT32 intHypotenuse; // x^2 + y^2. Frequently overflows fixed point, hence why we need integers proper.
-	fixed_t fixedHypotenuse; // However, we can work around that and still get a fixed-point number.
-	angle_t theta; // Angle of attack
-	// INT32 locvar1 = var1;
-	// INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_Boss5Jump", actor))
-		return;
-#endif
-
-	if (!actor->tracer)
-		return; // Don't even bother if we've got nothing to aim at.
-
-	// Look up actor's current gravity situation
-	if (actor->subsector->sector->gravity)
-		g = FixedMul(gravity,(FixedDiv(*actor->subsector->sector->gravity>>FRACBITS, 1000)));
-	else
-		g = gravity;
-
-	// Look up distance between actor and its tracer
-	x = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y);
-	// Look up height difference between actor and its tracer
-	y = actor->tracer->z - actor->z;
-
-	// Get x^2 + y^2. Have to do it in a roundabout manner, because this overflows fixed_t way too easily otherwise.
-	x_int = x>>FRACBITS;
-	y_int = y>>FRACBITS;
-	intHypotenuse = (x_int*x_int) + (y_int*y_int);
-	fixedHypotenuse = FixedSqrt(intHypotenuse) *256;
-
-	// a = g(y+/-sqrt(x^2+y^2)). a1 can be +, a2 can be -.
-	a1 = FixedMul(g,y+fixedHypotenuse);
-	a2 = FixedMul(g,y-fixedHypotenuse);
-
-	// Determine which one isn't actually an imaginary number (or the smaller of the two, if both are real), and use that for v.
-	if (a1 < 0 || a2 < 0)
-	{
-		if (a1 < 0 && a2 < 0)
-		{
-			//Somehow, v^2 is negative in both cases. v is therefore imaginary and something is horribly wrong. Abort!
-			return;
-		}
-		// Just find which one's NOT negative, and use that
-		aToUse = max(a1,a2);
-	}
-	else
-	{
-		// Both are positive; use whichever's smaller so it can decay faster
-		aToUse = min(a1,a2);
-	}
-	v = FixedSqrt(aToUse);
-	// Okay, so we know the velocity. Let's actually find theta.
-	// We can cut the "+/- sqrt" part out entirely, since v was calculated specifically for it to equal zero. So:
-	//theta = tantoangle[FixedDiv(aToUse,FixedMul(g,x)) >> DBITS];
-	theta = tantoangle[SlopeDiv(aToUse,FixedMul(g,x))];
-
-	// Okay, complicated math done. Let's make this object jump already.
-	A_FaceTracer(actor);
-
-	if (actor->eflags & MFE_VERTICALFLIP)
-		actor->z--;
-	else
-		actor->z++;
-
-	// Horizontal axes first. First parameter is initial horizontal impulse, second is to correct its angle.
-	actor->momx = FixedMul(FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)), FINECOSINE(actor->angle >> ANGLETOFINESHIFT));
-	actor->momy = FixedMul(FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)), FINESINE(actor->angle >> ANGLETOFINESHIFT));
-	// Then the vertical axis. No angle-correction needed here.
-	actor->momz = FixedMul(v, FINESINE(theta >> ANGLETOFINESHIFT));
-	// I hope that's all that's needed, ugh
-}
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 1993-1996 by id Software, Inc.
+// Copyright (C) 1998-2000 by DooM Legacy Team.
+// Copyright (C) 1999-2016 by Sonic Team Junior.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  p_enemy.c
+/// \brief Enemy thinking, AI
+///        Action Pointer Functions that are associated with states/frames
+
+#include "doomdef.h"
+#include "g_game.h"
+#include "p_local.h"
+#include "r_main.h"
+#include "r_state.h"
+#include "s_sound.h"
+#include "m_random.h"
+#include "m_misc.h"
+#include "r_things.h"
+#include "i_video.h"
+#include "lua_hook.h"
+
+#ifdef HW3SOUND
+#include "hardware/hw3sound.h"
+#endif
+
+#ifdef HAVE_BLUA
+boolean LUA_CallAction(const char *action, mobj_t *actor);
+#endif
+
+player_t *stplyr;
+INT32 var1;
+INT32 var2;
+
+//
+// P_NewChaseDir related LUT.
+//
+static dirtype_t opposite[] =
+{
+	DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST,
+	DI_EAST, DI_NORTHEAST, DI_NORTH, DI_NORTHWEST, DI_NODIR
+};
+
+static dirtype_t diags[] =
+{
+	DI_NORTHWEST, DI_NORTHEAST, DI_SOUTHWEST, DI_SOUTHEAST
+};
+
+//Real Prototypes to A_*
+void A_Fall(mobj_t *actor);
+void A_Look(mobj_t *actor);
+void A_Chase(mobj_t *actor);
+void A_FaceStabChase(mobj_t *actor);
+void A_FaceStabRev(mobj_t *actor);
+void A_FaceStabHurl(mobj_t *actor);
+void A_FaceStabMiss(mobj_t *actor);
+void A_StatueBurst(mobj_t *actor);
+void A_JetJawRoam(mobj_t *actor);
+void A_JetJawChomp(mobj_t *actor);
+void A_PointyThink(mobj_t *actor);
+void A_CheckBuddy(mobj_t *actor);
+void A_HoodFire(mobj_t *actor);
+void A_HoodThink(mobj_t *actor);
+void A_HoodFall(mobj_t *actor);
+void A_ArrowBonks(mobj_t *actor);
+void A_SnailerThink(mobj_t *actor);
+void A_SharpChase(mobj_t *actor);
+void A_SharpSpin(mobj_t *actor);
+void A_SharpDecel(mobj_t *actor);
+void A_CrushstaceanWalk(mobj_t *actor);
+void A_CrushstaceanPunch(mobj_t *actor);
+void A_CrushclawAim(mobj_t *actor);
+void A_CrushclawLaunch(mobj_t *actor);
+void A_VultureVtol(mobj_t *actor);
+void A_VultureCheck(mobj_t *actor);
+void A_SkimChase(mobj_t *actor);
+void A_FaceTarget(mobj_t *actor);
+void A_FaceTracer(mobj_t *actor);
+void A_LobShot(mobj_t *actor);
+void A_FireShot(mobj_t *actor);
+void A_SuperFireShot(mobj_t *actor);
+void A_BossFireShot(mobj_t *actor);
+void A_Boss7FireMissiles(mobj_t *actor);
+void A_Boss1Laser(mobj_t *actor);
+void A_FocusTarget(mobj_t *actor);
+void A_Boss4Reverse(mobj_t *actor);
+void A_Boss4SpeedUp(mobj_t *actor);
+void A_Boss4Raise(mobj_t *actor);
+void A_SkullAttack(mobj_t *actor);
+void A_BossZoom(mobj_t *actor);
+void A_BossScream(mobj_t *actor);
+void A_Scream(mobj_t *actor);
+void A_Pain(mobj_t *actor);
+void A_1upThinker(mobj_t *actor);
+void A_MonitorPop(mobj_t *actor);
+void A_GoldMonitorPop(mobj_t *actor);
+void A_GoldMonitorRestore(mobj_t *actor);
+void A_GoldMonitorSparkle(mobj_t *actor);
+void A_Explode(mobj_t *actor);
+void A_BossDeath(mobj_t *actor);
+void A_CustomPower(mobj_t *actor);
+void A_GiveWeapon(mobj_t *actor);
+void A_RingBox(mobj_t *actor);
+void A_Invincibility(mobj_t *actor);
+void A_SuperSneakers(mobj_t *actor);
+void A_AwardScore(mobj_t *actor);
+void A_ExtraLife(mobj_t *actor);
+void A_GiveShield(mobj_t *actor);
+void A_GravityBox(mobj_t *actor);
+void A_ScoreRise(mobj_t *actor);
+void A_BunnyHop(mobj_t *actor);
+void A_BubbleSpawn(mobj_t *actor);
+void A_FanBubbleSpawn(mobj_t *actor);
+void A_BubbleRise(mobj_t *actor);
+void A_BubbleCheck(mobj_t *actor);
+void A_AttractChase(mobj_t *actor);
+void A_DropMine(mobj_t *actor);
+void A_FishJump(mobj_t *actor);
+void A_ThrownRing(mobj_t *actor);
+void A_SetSolidSteam(mobj_t *actor);
+void A_UnsetSolidSteam(mobj_t *actor);
+void A_SignPlayer(mobj_t *actor);
+void A_OverlayThink(mobj_t *actor);
+void A_JetChase(mobj_t *actor);
+void A_JetbThink(mobj_t *actor);
+void A_JetgShoot(mobj_t *actor);
+void A_JetgThink(mobj_t *actor);
+void A_ShootBullet(mobj_t *actor);
+void A_MinusDigging(mobj_t *actor);
+void A_MinusPopup(mobj_t *actor);
+void A_MinusCheck(mobj_t *actor);
+void A_ChickenCheck(mobj_t *actor);
+void A_MouseThink(mobj_t *actor);
+void A_DetonChase(mobj_t *actor);
+void A_CapeChase(mobj_t *actor);
+void A_RotateSpikeBall(mobj_t *actor);
+void A_SlingAppear(mobj_t *actor);
+void A_UnidusBall(mobj_t *actor);
+void A_RockSpawn(mobj_t *actor);
+void A_SetFuse(mobj_t *actor);
+void A_CrawlaCommanderThink(mobj_t *actor);
+void A_RingExplode(mobj_t *actor);
+void A_OldRingExplode(mobj_t *actor);
+void A_MixUp(mobj_t *actor);
+void A_RecyclePowers(mobj_t *actor);
+void A_Boss2TakeDamage(mobj_t *actor);
+void A_Boss7Chase(mobj_t *actor);
+void A_GoopSplat(mobj_t *actor);
+void A_Boss2PogoSFX(mobj_t *actor);
+void A_Boss2PogoTarget(mobj_t *actor);
+void A_EggmanBox(mobj_t *actor);
+void A_TurretFire(mobj_t *actor);
+void A_SuperTurretFire(mobj_t *actor);
+void A_TurretStop(mobj_t *actor);
+void A_SparkFollow(mobj_t *actor);
+void A_BuzzFly(mobj_t *actor);
+void A_GuardChase(mobj_t *actor);
+void A_EggShield(mobj_t *actor);
+void A_SetReactionTime(mobj_t *actor);
+void A_Boss1Spikeballs(mobj_t *actor);
+void A_Boss3TakeDamage(mobj_t *actor);
+void A_Boss3Path(mobj_t *actor);
+void A_LinedefExecute(mobj_t *actor);
+void A_PlaySeeSound(mobj_t *actor);
+void A_PlayAttackSound(mobj_t *actor);
+void A_PlayActiveSound(mobj_t *actor);
+void A_SmokeTrailer(mobj_t *actor);
+void A_SpawnObjectAbsolute(mobj_t *actor);
+void A_SpawnObjectRelative(mobj_t *actor);
+void A_ChangeAngleRelative(mobj_t *actor);
+void A_ChangeAngleAbsolute(mobj_t *actor);
+void A_PlaySound(mobj_t *actor);
+void A_FindTarget(mobj_t *actor);
+void A_FindTracer(mobj_t *actor);
+void A_SetTics(mobj_t *actor);
+void A_SetRandomTics(mobj_t *actor);
+void A_ChangeColorRelative(mobj_t *actor);
+void A_ChangeColorAbsolute(mobj_t *actor);
+void A_MoveRelative(mobj_t *actor);
+void A_MoveAbsolute(mobj_t *actor);
+void A_Thrust(mobj_t *actor);
+void A_ZThrust(mobj_t *actor);
+void A_SetTargetsTarget(mobj_t *actor);
+void A_SetObjectFlags(mobj_t *actor);
+void A_SetObjectFlags2(mobj_t *actor);
+void A_RandomState(mobj_t *actor);
+void A_RandomStateRange(mobj_t *actor);
+void A_DualAction(mobj_t *actor);
+void A_RemoteAction(mobj_t *actor);
+void A_ToggleFlameJet(mobj_t *actor);
+void A_OrbitNights(mobj_t *actor);
+void A_GhostMe(mobj_t *actor);
+void A_SetObjectState(mobj_t *actor);
+void A_SetObjectTypeState(mobj_t *actor);
+void A_KnockBack(mobj_t *actor);
+void A_PushAway(mobj_t *actor);
+void A_RingDrain(mobj_t *actor);
+void A_SplitShot(mobj_t *actor);
+void A_MissileSplit(mobj_t *actor);
+void A_MultiShot(mobj_t *actor);
+void A_InstaLoop(mobj_t *actor);
+void A_Custom3DRotate(mobj_t *actor);
+void A_SearchForPlayers(mobj_t *actor);
+void A_CheckRandom(mobj_t *actor);
+void A_CheckTargetRings(mobj_t *actor);
+void A_CheckRings(mobj_t *actor);
+void A_CheckTotalRings(mobj_t *actor);
+void A_CheckHealth(mobj_t *actor);
+void A_CheckRange(mobj_t *actor);
+void A_CheckHeight(mobj_t *actor);
+void A_CheckTrueRange(mobj_t *actor);
+void A_CheckThingCount(mobj_t *actor);
+void A_CheckAmbush(mobj_t *actor);
+void A_CheckCustomValue(mobj_t *actor);
+void A_CheckCusValMemo(mobj_t *actor);
+void A_SetCustomValue(mobj_t *actor);
+void A_UseCusValMemo(mobj_t *actor);
+void A_RelayCustomValue(mobj_t *actor);
+void A_CusValAction(mobj_t *actor);
+void A_ForceStop(mobj_t *actor);
+void A_ForceWin(mobj_t *actor);
+void A_SpikeRetract(mobj_t *actor);
+void A_InfoState(mobj_t *actor);
+void A_Repeat(mobj_t *actor);
+void A_SetScale(mobj_t *actor);
+void A_RemoteDamage(mobj_t *actor);
+void A_HomingChase(mobj_t *actor);
+void A_TrapShot(mobj_t *actor);
+void A_Boss1Chase(mobj_t *actor);
+void A_Boss2Chase(mobj_t *actor);
+void A_Boss2Pogo(mobj_t *actor);
+void A_BossJetFume(mobj_t *actor);
+void A_VileTarget(mobj_t *actor);
+void A_VileAttack(mobj_t *actor);
+void A_VileFire(mobj_t *actor);
+void A_BrakChase(mobj_t *actor);
+void A_BrakFireShot(mobj_t *actor);
+void A_BrakLobShot(mobj_t *actor);
+void A_NapalmScatter(mobj_t *actor);
+void A_SpawnFreshCopy(mobj_t *actor);
+void A_FlickySpawn(mobj_t *actor);
+void A_FlickyCenter(mobj_t *actor);
+void A_FlickyAim(mobj_t *actor);
+void A_FlickyFly(mobj_t *actor);
+void A_FlickySoar(mobj_t *actor);
+void A_FlickyCoast(mobj_t *actor);
+void A_FlickyHop(mobj_t *actor);
+void A_FlickyFlounder(mobj_t *actor);
+void A_FlickyCheck(mobj_t *actor);
+void A_FlickyHeightCheck(mobj_t *actor);
+void A_FlickyFlutter(mobj_t *actor);
+void A_FlameParticle(mobj_t *actor);
+void A_FadeOverlay(mobj_t *actor);
+void A_Boss5Jump(mobj_t *actor);
+void A_LightBeamReset(mobj_t *actor);
+void A_MineExplode(mobj_t *actor);
+void A_MineRange(mobj_t *actor);
+void A_ConnectToGround(mobj_t *actor);
+void A_SpawnParticleRelative(mobj_t *actor);
+void A_MultiShotDist(mobj_t *actor);
+void A_WhoCaresIfYourSonIsABee(mobj_t *actor);
+void A_ParentTriesToSleep(mobj_t *actor);
+void A_CryingToMomma(mobj_t *actor);
+void A_CheckFlags2(mobj_t *actor);
+//for p_enemy.c
+
+//
+// ENEMY THINKING
+// Enemies are always spawned with targetplayer = -1, threshold = 0
+// Most monsters are spawned unaware of all players, but some can be made preaware.
+//
+
+//
+// P_CheckMeleeRange
+//
+boolean P_CheckMeleeRange(mobj_t *actor)
+{
+	mobj_t *pl;
+	fixed_t dist;
+
+	if (!actor->target)
+		return false;
+
+	pl = actor->target;
+	dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y);
+
+	if (dist >= FixedMul(MELEERANGE - 20*FRACUNIT, actor->scale) + pl->radius)
+		return false;
+
+	// check height now, so that damn crawlas cant attack
+	// you if you stand on a higher ledge.
+	if ((pl->z > actor->z + actor->height) || (actor->z > pl->z + pl->height))
+		return false;
+
+	if (!P_CheckSight(actor, actor->target))
+		return false;
+
+	return true;
+}
+
+// P_CheckMeleeRange for Jettysyn Bomber.
+boolean P_JetbCheckMeleeRange(mobj_t *actor)
+{
+	mobj_t *pl;
+	fixed_t dist;
+
+	if (!actor->target)
+		return false;
+
+	pl = actor->target;
+	dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y);
+
+	if (dist >= (actor->radius + pl->radius)*2)
+		return false;
+
+	if (actor->eflags & MFE_VERTICALFLIP)
+	{
+		if (pl->z < actor->z + actor->height + FixedMul(40<<FRACBITS, actor->scale))
+			return false;
+	}
+	else
+	{
+		if (pl->z + pl->height > actor->z - FixedMul(40<<FRACBITS, actor->scale))
+			return false;
+	}
+
+	return true;
+}
+
+// P_CheckMeleeRange for CastleBot FaceStabber.
+boolean P_FaceStabCheckMeleeRange(mobj_t *actor)
+{
+	mobj_t *pl;
+	fixed_t dist;
+
+	if (!actor->target)
+		return false;
+
+	pl = actor->target;
+	dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y);
+
+	if (dist >= (actor->radius + pl->radius)*4)
+		return false;
+
+	if ((pl->z > actor->z + actor->height) || (actor->z > pl->z + pl->height))
+		return false;
+
+	if (!P_CheckSight(actor, actor->target))
+		return false;
+
+	return true;
+}
+
+// P_CheckMeleeRange for Skim.
+boolean P_SkimCheckMeleeRange(mobj_t *actor)
+{
+	mobj_t *pl;
+	fixed_t dist;
+
+	if (!actor->target)
+		return false;
+
+	pl = actor->target;
+	dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y);
+
+	if (dist >= FixedMul(MELEERANGE - 20*FRACUNIT, actor->scale) + pl->radius)
+		return false;
+
+	if (actor->eflags & MFE_VERTICALFLIP)
+	{
+		if (pl->z < actor->z + actor->height + FixedMul(24<<FRACBITS, actor->scale))
+			return false;
+	}
+	else
+	{
+		if (pl->z + pl->height > actor->z - FixedMul(24<<FRACBITS, actor->scale))
+			return false;
+	}
+
+	return true;
+}
+
+//
+// P_CheckMissileRange
+//
+boolean P_CheckMissileRange(mobj_t *actor)
+{
+	fixed_t dist;
+
+	if (!actor->target)
+		return false;
+
+	if (actor->reactiontime)
+		return false; // do not attack yet
+
+	if (!P_CheckSight(actor, actor->target))
+		return false;
+
+	// OPTIMIZE: get this from a global checksight
+	dist = P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) - FixedMul(64*FRACUNIT, actor->scale);
+
+	if (!actor->info->meleestate)
+		dist -= FixedMul(128*FRACUNIT, actor->scale); // no melee attack, so fire more
+
+	dist >>= FRACBITS;
+
+	if (actor->type == MT_EGGMOBILE)
+		dist >>= 1;
+
+	if (dist > 200)
+		dist = 200;
+
+	if (actor->type == MT_EGGMOBILE && dist > 160)
+		dist = 160;
+
+	if (P_RandomByte() < dist)
+		return false;
+
+	return true;
+}
+
+/** Checks for water in a sector.
+  * Used by Skim movements.
+  *
+  * \param x X coordinate on the map.
+  * \param y Y coordinate on the map.
+  * \return True if there's water at this location, false if not.
+  * \sa ::MT_SKIM
+  */
+static boolean P_WaterInSector(mobj_t *mobj, fixed_t x, fixed_t y)
+{
+	sector_t *sector;
+
+	sector = R_PointInSubsector(x, y)->sector;
+
+	if (sector->ffloors)
+	{
+		ffloor_t *rover;
+
+		for (rover = sector->ffloors; rover; rover = rover->next)
+		{
+			if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_SWIMMABLE))
+				continue;
+
+			if (*rover->topheight >= mobj->floorz && *rover->topheight <= mobj->z)
+				return true; // we found water!!
+		}
+	}
+
+	return false;
+}
+
+static const fixed_t xspeed[NUMDIRS] = {FRACUNIT, 46341>>(16-FRACBITS), 0, -(46341>>(16-FRACBITS)), -FRACUNIT, -(46341>>(16-FRACBITS)), 0, 46341>>(16-FRACBITS)};
+static const fixed_t yspeed[NUMDIRS] = {0, 46341>>(16-FRACBITS), FRACUNIT, 46341>>(16-FRACBITS), 0, -(46341>>(16-FRACBITS)), -FRACUNIT, -(46341>>(16-FRACBITS))};
+
+/** Moves an actor in its current direction.
+  *
+  * \param actor Actor object to move.
+  * \return False if the move is blocked, otherwise true.
+  */
+boolean P_Move(mobj_t *actor, fixed_t speed)
+{
+	fixed_t tryx, tryy;
+	dirtype_t movedir = actor->movedir;
+
+	if (movedir == DI_NODIR || !actor->health)
+		return false;
+
+	I_Assert(movedir < NUMDIRS);
+
+	tryx = actor->x + FixedMul(speed*xspeed[movedir], actor->scale);
+	if (twodlevel || actor->flags2 & MF2_TWOD)
+		tryy = actor->y;
+	else
+		tryy = actor->y + FixedMul(speed*yspeed[movedir], actor->scale);
+
+	if (actor->type == MT_SKIM && !P_WaterInSector(actor, tryx, tryy)) // bail out if sector lacks water
+		return false;
+
+	if (!P_TryMove(actor, tryx, tryy, false))
+	{
+		if (actor->flags & MF_FLOAT && floatok)
+		{
+			// must adjust height
+			if (actor->z < tmfloorz)
+				actor->z += FixedMul(FLOATSPEED, actor->scale);
+			else
+				actor->z -= FixedMul(FLOATSPEED, actor->scale);
+
+			if (actor->type == MT_JETJAW && actor->z + actor->height > actor->watertop)
+				actor->z = actor->watertop - actor->height;
+
+			actor->flags2 |= MF2_INFLOAT;
+			return true;
+		}
+
+		return false;
+	}
+	else
+		actor->flags2 &= ~MF2_INFLOAT;
+
+	return true;
+}
+
+/** Attempts to move an actor on in its current direction.
+  * If the move succeeds, the actor's move count is reset
+  * randomly to a value from 0 to 15.
+  *
+  * \param actor Actor to move.
+  * \return True if the move succeeds, false if the move is blocked.
+  */
+static boolean P_TryWalk(mobj_t *actor)
+{
+	if (!P_Move(actor, actor->info->speed))
+		return false;
+	actor->movecount = P_RandomByte() & 15;
+	return true;
+}
+
+void P_NewChaseDir(mobj_t *actor)
+{
+	fixed_t deltax, deltay;
+	dirtype_t d[3];
+	dirtype_t tdir = DI_NODIR, olddir, turnaround;
+
+	I_Assert(actor->target != NULL);
+	I_Assert(!P_MobjWasRemoved(actor->target));
+
+	olddir = actor->movedir;
+
+	if (olddir >= NUMDIRS)
+		olddir = DI_NODIR;
+
+	if (olddir != DI_NODIR)
+		turnaround = opposite[olddir];
+	else
+		turnaround = olddir;
+
+	deltax = actor->target->x - actor->x;
+	deltay = actor->target->y - actor->y;
+
+	if (deltax > FixedMul(10*FRACUNIT, actor->scale))
+		d[1] = DI_EAST;
+	else if (deltax < -FixedMul(10*FRACUNIT, actor->scale))
+		d[1] = DI_WEST;
+	else
+		d[1] = DI_NODIR;
+
+	if (twodlevel || actor->flags2 & MF2_TWOD)
+		d[2] = DI_NODIR;
+	if (deltay < -FixedMul(10*FRACUNIT, actor->scale))
+		d[2] = DI_SOUTH;
+	else if (deltay > FixedMul(10*FRACUNIT, actor->scale))
+		d[2] = DI_NORTH;
+	else
+		d[2] = DI_NODIR;
+
+	// try direct route
+	if (d[1] != DI_NODIR && d[2] != DI_NODIR)
+	{
+		dirtype_t newdir = diags[((deltay < 0)<<1) + (deltax > 0)];
+
+		actor->movedir = newdir;
+		if ((newdir != turnaround) && P_TryWalk(actor))
+			return;
+	}
+
+	// try other directions
+	if (P_RandomChance(25*FRACUNIT/32) || abs(deltay) > abs(deltax))
+	{
+		tdir = d[1];
+		d[1] = d[2];
+		d[2] = tdir;
+	}
+
+	if (d[1] == turnaround)
+		d[1] = DI_NODIR;
+	if (d[2] == turnaround)
+		d[2] = DI_NODIR;
+
+	if (d[1] != DI_NODIR)
+	{
+		actor->movedir = d[1];
+
+		if (P_TryWalk(actor))
+			return; // either moved forward or attacked
+	}
+
+	if (d[2] != DI_NODIR)
+	{
+		actor->movedir = d[2];
+
+		if (P_TryWalk(actor))
+			return;
+	}
+
+	// there is no direct path to the player, so pick another direction.
+	if (olddir != DI_NODIR)
+	{
+		actor->movedir =olddir;
+
+		if (P_TryWalk(actor))
+			return;
+	}
+
+	// randomly determine direction of search
+	if (P_RandomChance(FRACUNIT/2))
+	{
+		for (tdir = DI_EAST; tdir <= DI_SOUTHEAST; tdir++)
+		{
+			if (tdir != turnaround)
+			{
+				actor->movedir = tdir;
+
+				if (P_TryWalk(actor))
+					return;
+			}
+		}
+	}
+	else
+	{
+		for (tdir = DI_SOUTHEAST; tdir >= DI_EAST; tdir--)
+		{
+			if (tdir != turnaround)
+			{
+				actor->movedir = tdir;
+
+				if (P_TryWalk(actor))
+					return;
+			}
+		}
+	}
+
+	if (turnaround != DI_NODIR)
+	{
+		actor->movedir = turnaround;
+
+		if (P_TryWalk(actor))
+			return;
+	}
+
+	actor->movedir = (angle_t)DI_NODIR; // cannot move
+}
+
+/** Looks for players to chase after, aim at, or whatever.
+  *
+  * \param actor     The object looking for flesh.
+  * \param allaround Look all around? If false, only players in a 180-degree
+  *                  range in front will be spotted.
+  * \param dist      If > 0, checks distance
+  * \return True if a player is found, otherwise false.
+  * \sa P_SupermanLook4Players
+  */
+boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed_t dist)
+{
+	INT32 c = 0, stop;
+	player_t *player;
+	angle_t an;
+
+	// BP: first time init, this allow minimum lastlook changes
+	if (actor->lastlook < 0)
+		actor->lastlook = P_RandomByte();
+
+	actor->lastlook %= MAXPLAYERS;
+
+	stop = (actor->lastlook - 1) & PLAYERSMASK;
+
+	for (; ; actor->lastlook = (actor->lastlook + 1) & PLAYERSMASK)
+	{
+		// done looking
+		if (actor->lastlook == stop)
+			return false;
+
+		if (!playeringame[actor->lastlook])
+			continue;
+
+		if (c++ == 2)
+			return false;
+
+		player = &players[actor->lastlook];
+
+		if ((netgame || multiplayer) && player->spectator)
+			continue;
+
+		if (player->pflags & PF_INVIS)
+			continue; // ignore notarget
+
+		if (!player->mo || P_MobjWasRemoved(player->mo))
+			continue;
+
+		if (player->mo->health <= 0)
+			continue; // dead
+
+		if (dist > 0
+			&& P_AproxDistance(P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y), player->mo->z - actor->z) > dist)
+			continue; // Too far away
+
+		if (!allaround)
+		{
+			an = R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y) - actor->angle;
+			if (an > ANGLE_90 && an < ANGLE_270)
+			{
+				dist = P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y);
+				// if real close, react anyway
+				if (dist > FixedMul(MELEERANGE, actor->scale))
+					continue; // behind back
+			}
+		}
+
+		if (!P_CheckSight(actor, player->mo))
+			continue; // out of sight
+
+		if (tracer)
+			P_SetTarget(&actor->tracer, player->mo);
+		else
+			P_SetTarget(&actor->target, player->mo);
+		return true;
+	}
+
+	//return false;
+}
+
+/** Looks for a player with a ring shield.
+  * Used by rings.
+  *
+  * \param actor Ring looking for a shield to be attracted to.
+  * \return True if a player with ring shield is found, otherwise false.
+  * \sa A_AttractChase
+  */
+static boolean P_LookForShield(mobj_t *actor)
+{
+	INT32 c = 0, stop;
+	player_t *player;
+
+	// BP: first time init, this allow minimum lastlook changes
+	if (actor->lastlook < 0)
+		actor->lastlook = P_RandomByte();
+
+	actor->lastlook %= MAXPLAYERS;
+
+	stop = (actor->lastlook - 1) & PLAYERSMASK;
+
+	for (; ; actor->lastlook = ((actor->lastlook + 1) & PLAYERSMASK))
+	{
+		// done looking
+		if (actor->lastlook == stop)
+			return false;
+
+		if (!playeringame[actor->lastlook])
+			continue;
+
+		if (c++ == 2)
+			return false;
+
+		player = &players[actor->lastlook];
+
+		if (!player->mo || player->mo->health <= 0)
+			continue; // dead
+
+		//When in CTF, don't pull rings that you cannot pick up.
+		if ((actor->type == MT_REDTEAMRING && player->ctfteam != 1) ||
+			(actor->type == MT_BLUETEAMRING && player->ctfteam != 2))
+			continue;
+
+		if ((player->powers[pw_shield] & SH_PROTECTELECTRIC)
+			&& (P_AproxDistance(P_AproxDistance(actor->x-player->mo->x, actor->y-player->mo->y), actor->z-player->mo->z) < FixedMul(RING_DIST, player->mo->scale)))
+		{
+			P_SetTarget(&actor->tracer, player->mo);
+
+			if (actor->hnext)
+				P_SetTarget(&actor->hnext->hprev, actor->hprev);
+			if (actor->hprev)
+				P_SetTarget(&actor->hprev->hnext, actor->hnext);
+
+			return true;
+		}
+	}
+
+	//return false;
+}
+
+#ifdef WEIGHTEDRECYCLER
+// Compares players to see who currently has the "best" items, etc.
+static int P_RecycleCompare(const void *p1, const void *p2)
+{
+	player_t *player1 = &players[*(const UINT8 *)p1];
+	player_t *player2 = &players[*(const UINT8 *)p2];
+
+	// Non-shooting gametypes
+	if (!G_PlatformGametype())
+	{
+		// Invincibility.
+		if (player1->powers[pw_invulnerability] > player2->powers[pw_invulnerability]) return -1;
+		else if (player2->powers[pw_invulnerability] > player1->powers[pw_invulnerability]) return 1;
+
+		// One has a shield, the other doesn't.
+		if (player1->powers[pw_shield] && !player2->powers[pw_shield]) return -1;
+		else if (player2->powers[pw_shield] && !player1->powers[pw_shield]) return 1;
+
+		// Sneakers.
+		if (player1->powers[pw_sneakers] > player2->powers[pw_sneakers]) return -1;
+		else if (player2->powers[pw_sneakers] > player1->powers[pw_sneakers]) return 1;
+	}
+	else // Match, Team Match, CTF, Tag, Etc.
+	{
+		UINT8 player1_em = M_CountBits((UINT32)player1->powers[pw_emeralds], 7);
+		UINT8 player2_em = M_CountBits((UINT32)player2->powers[pw_emeralds], 7);
+
+		UINT8 player1_rw = M_CountBits((UINT32)player1->ringweapons, NUM_WEAPONS-1);
+		UINT8 player2_rw = M_CountBits((UINT32)player2->ringweapons, NUM_WEAPONS-1);
+
+		UINT16 player1_am = player1->powers[pw_infinityring]          // max 800
+		                  + player1->powers[pw_automaticring]         // max 300
+		                  + (player1->powers[pw_bouncering]    * 3)   // max 100
+		                  + (player1->powers[pw_explosionring] * 6)   // max 50
+		                  + (player1->powers[pw_scatterring]   * 3)   // max 100
+		                  + (player1->powers[pw_grenadering]   * 6)   // max 50
+		                  + (player1->powers[pw_railring]      * 6);  // max 50
+		UINT16 player2_am = player2->powers[pw_infinityring]          // max 800
+		                  + player2->powers[pw_automaticring]         // max 300
+		                  + (player2->powers[pw_bouncering]    * 3)   // max 100
+		                  + (player2->powers[pw_explosionring] * 6)   // max 50
+		                  + (player2->powers[pw_scatterring]   * 3)   // max 100
+		                  + (player2->powers[pw_grenadering]   * 6)   // max 50
+		                  + (player2->powers[pw_railring]      * 6);  // max 50
+
+		// Super trumps everything.
+		if (player1->powers[pw_super] && !player2->powers[pw_super]) return -1;
+		else if (player2->powers[pw_super] && !player1->powers[pw_super]) return 1;
+
+		// Emerald count if neither player is Super.
+		if (player1_em > player2_em) return -1;
+		else if (player1_em < player2_em) return 1;
+
+		// One has a shield, the other doesn't.
+		// (the likelihood of a shielded player being worse off than one without one is low.)
+		if (player1->powers[pw_shield] && !player2->powers[pw_shield]) return -1;
+		else if (player2->powers[pw_shield] && !player1->powers[pw_shield]) return 1;
+
+		// Ring weapons count
+		if (player1_rw > player2_rw) return -1;
+		else if (player1_rw < player2_rw) return 1;
+
+		// Ring ammo if they have the same number of weapons
+		if (player1_am > player2_am) return -1;
+		else if (player1_am < player2_am) return 1;
+	}
+
+	// Identical for our purposes
+	return 0;
+}
+#endif
+
+// Handles random monitor weights via console.
+static mobjtype_t P_DoRandomBoxChances(void)
+{
+	mobjtype_t spawnchance[256];
+	INT32 numchoices = 0, i = 0;
+
+	if (!(netgame || multiplayer))
+	{
+		switch (P_RandomKey(10))
+		{
+			case 0:
+				return MT_RING_ICON;
+			case 1:
+				return MT_SNEAKERS_ICON;
+			case 2:
+				return MT_INVULN_ICON;
+			case 3:
+				return MT_WHIRLWIND_ICON;
+			case 4:
+				return MT_ELEMENTAL_ICON;
+			case 5:
+				return MT_ATTRACT_ICON;
+			case 6:
+				return MT_FORCE_ICON;
+			case 7:
+				return MT_ARMAGEDDON_ICON;
+			case 8:
+				return MT_1UP_ICON;
+			case 9:
+				return MT_EGGMAN_ICON;
+		}
+		return MT_NULL;
+	}
+
+#define QUESTIONBOXCHANCES(type, cvar) \
+for (i = cvar.value; i; --i) spawnchance[numchoices++] = type
+	QUESTIONBOXCHANCES(MT_RING_ICON,       cv_superring);
+	QUESTIONBOXCHANCES(MT_SNEAKERS_ICON,   cv_supersneakers);
+	QUESTIONBOXCHANCES(MT_INVULN_ICON,     cv_invincibility);
+	QUESTIONBOXCHANCES(MT_WHIRLWIND_ICON,  cv_jumpshield);
+	QUESTIONBOXCHANCES(MT_ELEMENTAL_ICON,  cv_watershield);
+	QUESTIONBOXCHANCES(MT_ATTRACT_ICON,    cv_ringshield);
+	QUESTIONBOXCHANCES(MT_FORCE_ICON,      cv_forceshield);
+	QUESTIONBOXCHANCES(MT_ARMAGEDDON_ICON, cv_bombshield);
+	QUESTIONBOXCHANCES(MT_1UP_ICON,        cv_1up);
+	QUESTIONBOXCHANCES(MT_EGGMAN_ICON,     cv_eggmanbox);
+	QUESTIONBOXCHANCES(MT_MIXUP_ICON,      cv_teleporters);
+	QUESTIONBOXCHANCES(MT_RECYCLER_ICON,   cv_recycler);
+#undef QUESTIONBOXCHANCES
+
+	if (numchoices == 0) return MT_NULL;
+	return spawnchance[P_RandomKey(numchoices)];
+}
+
+//
+// ACTION ROUTINES
+//
+
+// Function: A_Look
+//
+// Description: Look for a player and set your target to them.
+//
+// var1:
+//		lower 16 bits = look all around
+//		upper 16 bits = distance limit
+// var2 = If 1, only change to seestate. If 2, only play seesound. If 0, do both.
+//
+void A_Look(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Look", actor))
+		return;
+#endif
+
+	if (!P_LookForPlayers(actor, locvar1 & 65535, false , FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale)))
+		return;
+
+	// go into chase state
+	if (!locvar2)
+	{
+		P_SetMobjState(actor, actor->info->seestate);
+		A_PlaySeeSound(actor);
+	}
+	else if (locvar2 == 1) // Only go into seestate
+		P_SetMobjState(actor, actor->info->seestate);
+	else if (locvar2 == 2) // Only play seesound
+		A_PlaySeeSound(actor);
+}
+
+// Function: A_Chase
+//
+// Description: Chase after your target.
+//
+// var1:
+//		1 = don't check meleestate
+//		2 = don't check missilestate
+//		3 = don't check meleestate and missilestate
+// var2 = unused
+//
+void A_Chase(mobj_t *actor)
+{
+	INT32 delta;
+	INT32 locvar1 = var1;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Chase", actor))
+		return;
+#endif
+
+	I_Assert(actor != NULL);
+	I_Assert(!P_MobjWasRemoved(actor));
+
+	if (actor->reactiontime)
+		actor->reactiontime--;
+
+	// modify target threshold
+	if (actor->threshold)
+	{
+		if (!actor->target || actor->target->health <= 0)
+			actor->threshold = 0;
+		else
+			actor->threshold--;
+	}
+
+	// turn towards movement direction if not there yet
+	if (actor->movedir < NUMDIRS)
+	{
+		actor->angle &= (7<<29);
+		delta = actor->angle - (actor->movedir << 29);
+
+		if (delta > 0)
+			actor->angle -= ANGLE_45;
+		else if (delta < 0)
+			actor->angle += ANGLE_45;
+	}
+
+	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+	{
+		// look for a new target
+		if (P_LookForPlayers(actor, true, false, 0))
+			return; // got a new target
+
+		P_SetMobjStateNF(actor, actor->info->spawnstate);
+		return;
+	}
+
+	// do not attack twice in a row
+	if (actor->flags2 & MF2_JUSTATTACKED)
+	{
+		actor->flags2 &= ~MF2_JUSTATTACKED;
+		P_NewChaseDir(actor);
+		return;
+	}
+
+	// check for melee attack
+	if (!(locvar1 & 1) && actor->info->meleestate && P_CheckMeleeRange(actor))
+	{
+		if (actor->info->attacksound)
+			S_StartAttackSound(actor, actor->info->attacksound);
+
+		P_SetMobjState(actor, actor->info->meleestate);
+		return;
+	}
+
+	// check for missile attack
+	if (!(locvar1 & 2) && actor->info->missilestate)
+	{
+		if (actor->movecount || !P_CheckMissileRange(actor))
+			goto nomissile;
+
+		P_SetMobjState(actor, actor->info->missilestate);
+		actor->flags2 |= MF2_JUSTATTACKED;
+		return;
+	}
+
+nomissile:
+	// possibly choose another target
+	if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
+		&& P_LookForPlayers(actor, true, false, 0))
+		return; // got a new target
+
+	// chase towards player
+	if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
+		P_NewChaseDir(actor);
+}
+
+// Function: A_FaceStabChase
+//
+// Description: Unused variant of A_Chase for Castlebot Facestabber.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_FaceStabChase(mobj_t *actor)
+{
+	INT32 delta;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_FaceStabChase", actor))
+		return;
+#endif
+
+	if (actor->reactiontime)
+		actor->reactiontime--;
+
+	// modify target threshold
+	if (actor->threshold)
+	{
+		if (!actor->target || actor->target->health <= 0)
+			actor->threshold = 0;
+		else
+			actor->threshold--;
+	}
+
+	// turn towards movement direction if not there yet
+	if (actor->movedir < NUMDIRS)
+	{
+		actor->angle &= (7<<29);
+		delta = actor->angle - (actor->movedir << 29);
+
+		if (delta > 0)
+			actor->angle -= ANGLE_45;
+		else if (delta < 0)
+			actor->angle += ANGLE_45;
+	}
+
+	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+	{
+		// look for a new target
+		if (P_LookForPlayers(actor, true, false, 0))
+			return; // got a new target
+
+		P_SetMobjStateNF(actor, actor->info->spawnstate);
+		return;
+	}
+
+	// do not attack twice in a row
+	if (actor->flags2 & MF2_JUSTATTACKED)
+	{
+		actor->flags2 &= ~MF2_JUSTATTACKED;
+		P_NewChaseDir(actor);
+		return;
+	}
+
+	// check for melee attack
+	if (actor->info->meleestate && P_FaceStabCheckMeleeRange(actor))
+	{
+		if (actor->info->attacksound)
+			S_StartAttackSound(actor, actor->info->attacksound);
+
+		P_SetMobjState(actor, actor->info->meleestate);
+		return;
+	}
+
+	// check for missile attack
+	if (actor->info->missilestate)
+	{
+		if (actor->movecount || !P_CheckMissileRange(actor))
+			goto nomissile;
+
+		P_SetMobjState(actor, actor->info->missilestate);
+		actor->flags2 |= MF2_JUSTATTACKED;
+		return;
+	}
+
+nomissile:
+	// possibly choose another target
+	if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
+		&& P_LookForPlayers(actor, true, false, 0))
+		return; // got a new target
+
+	// chase towards player
+	if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
+		P_NewChaseDir(actor);
+}
+
+static void P_SharpDust(mobj_t *actor, mobjtype_t type, angle_t ang)
+{
+	mobj_t *dust;
+
+	if (!type || !P_IsObjectOnGround(actor))
+		return;
+
+	dust = P_SpawnMobjFromMobj(actor,
+			-P_ReturnThrustX(actor, ang, 16<<FRACBITS),
+			-P_ReturnThrustY(actor, ang, 16<<FRACBITS),
+			0, type);
+	P_SetObjectMomZ(dust, P_RandomRange(1, 4)<<FRACBITS, false);
+}
+
+static void P_FaceStabFlume(mobj_t *actor)
+{
+	mobj_t *flume;
+	if (leveltime & 1)
+		return;
+
+	flume = P_SpawnMobjFromMobj(actor,
+		-P_ReturnThrustX(actor, actor->angle, actor->radius),
+		-P_ReturnThrustY(actor, actor->angle, actor->radius),
+		actor->height/3,
+		MT_PARTICLE);
+	flume->destscale = actor->scale*3;
+	P_SetScale(flume, flume->destscale);
+	P_SetTarget(&flume->target, actor);
+	flume->sprite = SPR_JETF;
+	flume->frame = FF_FULLBRIGHT;
+	flume->tics = 2;
+}
+
+// Function: A_FaceStabRev
+//
+// Description: Facestabber rev action
+//
+// var1 = effective duration
+// var2 = effective nextstate
+//
+void A_FaceStabRev(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_FaceStabRev", actor))
+		return;
+#endif
+
+	if (!actor->target)
+	{
+		P_SetMobjState(actor, actor->info->spawnstate);
+		return;
+	}
+
+	actor->extravalue1 = 0;
+
+	if (!actor->reactiontime)
+	{
+		actor->reactiontime = locvar1;
+		S_StartSound(actor, actor->info->activesound);
+	}
+	else
+	{
+		if ((--actor->reactiontime) == 0)
+		{
+			S_StartSound(actor, actor->info->attacksound);
+			P_SetMobjState(actor, locvar2);
+		}
+		else
+		{
+			P_TryMove(actor, actor->x - P_ReturnThrustX(actor, actor->angle, 2<<FRACBITS), actor->y - P_ReturnThrustY(actor, actor->angle, 2<<FRACBITS), false);
+			P_FaceStabFlume(actor);
+		}
+	}
+}
+
+// Function: A_FaceStabHurl
+//
+// Description: Facestabber hurl action
+//
+// var1 = homing strength (recommended strength between 0-8)
+// var2 = effective nextstate
+//
+void A_FaceStabHurl(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_FaceStabHurl", actor))
+		return;
+#endif
+
+	if (actor->target)
+	{
+		angle_t visang = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+		// Calculate new direction.
+		angle_t dirang = actor->angle;
+		angle_t diffang = visang - dirang;
+
+		if (locvar1) // Allow homing?
+		{
+			if (diffang > ANGLE_180)
+			{
+				angle_t workang = locvar1*(InvAngle(diffang)>>5);
+				diffang += InvAngle(workang);
+			}
+			else
+				diffang += (locvar1*(diffang>>5));
+		}
+		diffang += ANGLE_45;
+
+		// Check the sight cone.
+		if (diffang < ANGLE_90)
+		{
+			actor->angle = dirang;
+			if (++actor->extravalue2 < 4)
+				actor->extravalue2 = 4;
+			else if (actor->extravalue2 > 26)
+				actor->extravalue2 = 26;
+
+			if (P_TryMove(actor,
+				actor->x + P_ReturnThrustX(actor, dirang, actor->extravalue2<<FRACBITS),
+				actor->y + P_ReturnThrustY(actor, dirang, actor->extravalue2<<FRACBITS),
+				false))
+			{
+				// Do the spear damage.
+#define NUMSTEPS 3
+#define NUMGRADS 5
+#define MAXVAL (NUMSTEPS*NUMGRADS)
+				SINT8 step = (++actor->extravalue1);
+				fixed_t basesize = FRACUNIT/MAXVAL;
+				mobj_t *hwork = actor;
+				INT32 dist = 113;
+				fixed_t xo = P_ReturnThrustX(actor, actor->angle, dist*basesize);
+				fixed_t yo = P_ReturnThrustY(actor, actor->angle, dist*basesize);
+
+				while (step > 0)
+				{
+					if (!hwork->hnext)
+						P_SetTarget(&hwork->hnext, P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_FACESTABBERSPEAR));
+					hwork = hwork->hnext;
+					hwork->angle = actor->angle + ANGLE_90;
+					hwork->destscale = FixedSqrt(step*basesize);
+					P_SetScale(hwork, hwork->destscale);
+					hwork->fuse = 2;
+					P_TeleportMove(hwork, actor->x + xo*(15-step), actor->y + yo*(15-step), actor->z + (actor->height - hwork->height)/2 + (P_MobjFlip(actor)*(8<<FRACBITS)));
+					step -= NUMGRADS;
+				}
+
+				if (actor->extravalue1 >= MAXVAL)
+					actor->extravalue1 -= NUMGRADS;
+
+				if ((step % 5) == 0)
+					P_SharpDust(actor, MT_SPINDUST, actor->angle);
+
+				P_FaceStabFlume(actor);
+				return;
+#undef MAXVAL
+#undef NUMGRADS
+#undef NUMSTEPS
+			}
+		}
+	}
+
+	P_SetMobjState(actor, locvar2);
+	actor->reactiontime = actor->info->reactiontime;
+}
+
+// Function: A_FaceStabMiss
+//
+// Description: Facestabber miss action
+//
+// var1 = unused
+// var2 = effective nextstate
+//
+void A_FaceStabMiss(mobj_t *actor)
+{
+	//INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_FaceStabMiss", actor))
+		return;
+#endif
+
+	if (++actor->extravalue1 >= 3)
+	{
+		actor->extravalue2 -= 2;
+		actor->extravalue1 = 0;
+		S_StartSound(actor, sfx_s3k47);
+		P_SharpDust(actor, MT_SPINDUST, actor->angle);
+	}
+
+	if (actor->extravalue2 <= 0 || !P_TryMove(actor,
+		actor->x + P_ReturnThrustX(actor, actor->angle, actor->extravalue2<<FRACBITS),
+		actor->y + P_ReturnThrustY(actor, actor->angle, actor->extravalue2<<FRACBITS),
+		false))
+	{
+		actor->extravalue2 = 0;
+		P_SetMobjState(actor, locvar2);
+	}
+}
+
+// Function: A_StatueBurst
+//
+// Description: For suspicious statues only...
+//
+// var1 = object to create
+// var2 = effective nextstate for created object
+//
+void A_StatueBurst(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	mobjtype_t chunktype = (mobjtype_t)actor->info->raisestate;
+	mobj_t *new;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_StatueBurst", actor))
+		return;
+#endif
+
+	if (!locvar1 || !(new = P_SpawnMobjFromMobj(actor, 0, 0, 0, locvar1)))
+		return;
+
+	new->angle = actor->angle;
+	new->target = actor->target;
+	if (locvar2)
+		P_SetMobjState(new, (statenum_t)locvar2);
+	S_StartSound(new, new->info->attacksound);
+	S_StopSound(actor);
+	S_StartSound(actor, sfx_s3k96);
+
+	{
+		fixed_t a, b;
+		fixed_t c = (actor->height>>2) - FixedMul(actor->scale, mobjinfo[chunktype].height>>1);
+		fixed_t v = 4<<FRACBITS;
+		const fixed_t r = (actor->radius>>1);
+		mobj_t *spawned;
+		UINT8 i;
+		for (i = 0; i < 8; i++)
+		{
+			a = ((i & 1) ? r : (-r));
+			b = ((i & 2) ? r : (-r));
+			if (i == 4)
+			{
+				c += (actor->height>>1);
+				v = 8<<FRACBITS;
+			}
+
+			spawned = P_SpawnMobjFromMobj(actor, a, b, c, chunktype);
+
+			P_InstaThrust(spawned, R_PointToAngle2(0, 0, a, b), 8<<FRACBITS);
+			P_SetObjectMomZ(spawned, v, false);
+
+			spawned->fuse = 3*TICRATE;
+		}
+	}
+}
+
+// Function: A_JetJawRoam
+//
+// Description: Roaming routine for JetJaw
+//
+// var1 = unused
+// var2 = unused
+//
+void A_JetJawRoam(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_JetJawRoam", actor))
+		return;
+#endif
+	if (actor->reactiontime)
+	{
+		actor->reactiontime--;
+		P_InstaThrust(actor, actor->angle, FixedMul(actor->info->speed*FRACUNIT/4, actor->scale));
+	}
+	else
+	{
+		actor->reactiontime = actor->info->reactiontime;
+		actor->angle += ANGLE_180;
+	}
+
+	if (P_LookForPlayers(actor, false, false, actor->radius * 16))
+		P_SetMobjState(actor, actor->info->seestate);
+}
+
+// Function: A_JetJawChomp
+//
+// Description: Chase and chomp at the target, as long as it is in view
+//
+// var1 = unused
+// var2 = unused
+//
+void A_JetJawChomp(mobj_t *actor)
+{
+	INT32 delta;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_JetJawChomp", actor))
+		return;
+#endif
+
+	// turn towards movement direction if not there yet
+	if (actor->movedir < NUMDIRS)
+	{
+		actor->angle &= (7<<29);
+		delta = actor->angle - (actor->movedir << 29);
+
+		if (delta > 0)
+			actor->angle -= ANGLE_45;
+		else if (delta < 0)
+			actor->angle += ANGLE_45;
+	}
+
+	// Stop chomping if target's dead or you can't see it
+	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)
+		|| actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
+	{
+		P_SetMobjStateNF(actor, actor->info->spawnstate);
+		return;
+	}
+
+	// chase towards player
+	if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
+		P_NewChaseDir(actor);
+}
+
+// Function: A_PointyThink
+//
+// Description: Thinker function for Pointy
+//
+// var1 = unused
+// var2 = unused
+//
+void A_PointyThink(mobj_t *actor)
+{
+	INT32 i;
+	player_t *player = NULL;
+	mobj_t *ball;
+	TVector v;
+	TVector *res;
+	angle_t fa;
+	fixed_t radius = FixedMul(actor->info->radius*actor->info->reactiontime, actor->scale);
+	boolean firsttime = true;
+	INT32 sign;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_PointyThink", actor))
+		return;
+#endif
+	actor->momx = actor->momy = actor->momz = 0;
+
+	// Find nearest player
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		if (!playeringame[i] || players[i].spectator)
+			continue;
+
+		if (!players[i].mo)
+			continue;
+
+		if (!players[i].mo->health)
+			continue;
+
+		if (!P_CheckSight(actor, players[i].mo))
+			continue;
+
+		if (firsttime)
+		{
+			firsttime = false;
+			player = &players[i];
+		}
+		else
+		{
+			if (P_AproxDistance(players[i].mo->x - actor->x, players[i].mo->y - actor->y) <
+				P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y))
+				player = &players[i];
+		}
+	}
+
+	if (!player)
+		return;
+
+	// Okay, we found the closest player. Let's move based on his movement.
+	P_SetTarget(&actor->target, player->mo);
+	A_FaceTarget(actor);
+
+	if (P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y) < P_AproxDistance(player->mo->x + player->mo->momx - actor->x, player->mo->y + player->mo->momy - actor->y))
+		sign = -1; // Player is moving away
+	else
+		sign = 1; // Player is moving closer
+
+	if (player->mo->momx || player->mo->momy)
+	{
+		P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y), FixedMul(actor->info->speed*sign, actor->scale));
+
+		// Rotate our spike balls
+		actor->lastlook += actor->info->damage;
+		actor->lastlook %= FINEANGLES/4;
+	}
+
+	if (!actor->tracer) // For some reason we do not have spike balls...
+		return;
+
+	// Position spike balls relative to the value of 'lastlook'.
+	ball = actor->tracer;
+
+	i = 0;
+	while (ball)
+	{
+		fa = actor->lastlook+i;
+		v[0] = FixedMul(FINECOSINE(fa),radius);
+		v[1] = 0;
+		v[2] = FixedMul(FINESINE(fa),radius);
+		v[3] = FRACUNIT;
+
+		res = VectorMatrixMultiply(v, *RotateXMatrix(FixedAngle(actor->lastlook+i)));
+		M_Memcpy(&v, res, sizeof (v));
+		res = VectorMatrixMultiply(v, *RotateZMatrix(actor->angle+ANGLE_180));
+		M_Memcpy(&v, res, sizeof (v));
+
+		P_UnsetThingPosition(ball);
+		ball->x = actor->x + v[0];
+		ball->y = actor->y + v[1];
+		ball->z = actor->z + (actor->height>>1) + v[2];
+		P_SetThingPosition(ball);
+
+		ball = ball->tracer;
+		i += ANGLE_90 >> ANGLETOFINESHIFT;
+	}
+}
+
+// Function: A_CheckBuddy
+//
+// Description: Checks if target/tracer exists/has health. If not, the object removes itself.
+//
+// var1:
+//		0 = target
+//		1 = tracer
+// var2 = unused
+//
+void A_CheckBuddy(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_CheckBuddy", actor))
+		return;
+#endif
+	if (locvar1 && (!actor->tracer || actor->tracer->health <= 0))
+		P_RemoveMobj(actor);
+	else if (!locvar1 && (!actor->target || actor->target->health <= 0))
+		P_RemoveMobj(actor);
+}
+
+// Helper function for the Robo Hood.
+// Don't ask me how it works. Nev3r made it with dark majyks.
+static void P_ParabolicMove(mobj_t *actor, fixed_t x, fixed_t y, fixed_t z, fixed_t speed)
+{
+	fixed_t dh;
+
+	x -= actor->x;
+	y -= actor->y;
+	z -= actor->z;
+
+	dh = P_AproxDistance(x, y);
+
+	actor->momx = FixedMul(FixedDiv(x, dh), speed);
+	actor->momy = FixedMul(FixedDiv(y, dh), speed);
+
+	if (!gravity)
+		return;
+
+	dh = FixedDiv(FixedMul(dh, gravity), speed);
+	actor->momz = (dh>>1) + FixedDiv(z, dh<<1);
+}
+
+// Function: A_HoodFire
+//
+// Description: Firing Robo-Hood
+//
+// var1 = object type to fire
+// var2 = unused
+//
+void A_HoodFire(mobj_t *actor)
+{
+	mobj_t *arrow;
+	INT32 locvar1 = var1;
+	//INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_HoodFire", actor))
+		return;
+#endif
+
+	// Check target first.
+	if (!actor->target)
+	{
+		actor->reactiontime = actor->info->reactiontime;
+		P_SetMobjState(actor, actor->info->spawnstate);
+		return;
+	}
+
+	A_FaceTarget(actor);
+
+	if (!(arrow = P_SpawnMissile(actor, actor->target, (mobjtype_t)locvar1)))
+		return;
+
+	// Set a parabolic trajectory for the arrow.
+	P_ParabolicMove(arrow, actor->target->x, actor->target->y, actor->target->z, arrow->info->speed);
+}
+
+// Function: A_HoodThink
+//
+// Description: Thinker for Robo-Hood
+//
+// var1 = unused
+// var2 = unused
+//
+void A_HoodThink(mobj_t *actor)
+{
+	fixed_t dx, dy, dz, dm;
+	boolean checksight;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_HoodThink", actor))
+		return;
+#endif
+
+	// Check target first.
+	if (!actor->target)
+	{
+		actor->reactiontime = actor->info->reactiontime;
+		P_SetMobjState(actor, actor->info->spawnstate);
+		return;
+	}
+
+	dx = (actor->target->x - actor->x), dy = (actor->target->y - actor->y), dz = (actor->target->z - actor->z);
+	dm = P_AproxDistance(dx, dy);
+	// Target dangerously close to robohood, retreat then.
+	if ((dm < 256<<FRACBITS) && (abs(dz) < 128<<FRACBITS))
+	{
+		P_SetMobjState(actor, actor->info->raisestate);
+		return;
+	}
+
+	// If target on sight, look at it.
+	if ((checksight = P_CheckSight(actor, actor->target)))
+	{
+		angle_t dang = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+		if (actor->angle >= ANGLE_180)
+		{
+			actor->angle = InvAngle(actor->angle)>>1;
+			actor->angle = InvAngle(actor->angle);
+		}
+		else
+			actor->angle >>= 1;
+
+		if (dang >= ANGLE_180)
+		{
+			dang = InvAngle(dang)>>1;
+			dang = InvAngle(dang);
+		}
+		else
+			dang >>= 1;
+
+		actor->angle += dang;
+	}
+
+	// Check whether to do anything.
+	if ((--actor->reactiontime) <= 0)
+	{
+		actor->reactiontime = actor->info->reactiontime;
+
+		// If way too far, don't shoot.
+		if ((dm < (3072<<FRACBITS)) && checksight)
+		{
+			P_SetMobjState(actor, actor->info->missilestate);
+			return;
+		}
+	}
+}
+
+// Function: A_HoodFall
+//
+// Description: Falling Robo-Hood
+//
+// var1 = unused
+// var2 = unused
+//
+void A_HoodFall(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_HoodFall", actor))
+		return;
+#endif
+
+	if (!P_IsObjectOnGround(actor))
+		return;
+
+	actor->momx = actor->momy = 0;
+	actor->reactiontime = actor->info->reactiontime;
+	P_SetMobjState(actor, actor->info->seestate);
+}
+
+// Function: A_ArrowBonks
+//
+// Description: Arrow momentum setting on collision
+//
+// var1 = unused
+// var2 = unused
+//
+void A_ArrowBonks(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_ArrowBonks", actor))
+		return;
+#endif
+
+	if (((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)
+		|| (!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz))
+		actor->angle += ANGLE_180;
+
+	P_SetObjectMomZ(actor, 8*actor->scale, false);
+	P_InstaThrust(actor, actor->angle, -6*actor->scale);
+
+	actor->flags = (actor->flags|MF_NOCLIPHEIGHT) & ~MF_NOGRAVITY;
+	actor->z += P_MobjFlip(actor);
+}
+
+// Function: A_SnailerThink
+//
+// Description: Thinker function for Snailer
+//
+// var1 = unused
+// var2 = unused
+//
+void A_SnailerThink(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SnailerThink", actor))
+		return;
+#endif
+
+	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+	{
+		// look for a new target
+		if (!P_LookForPlayers(actor, true, false, 0))
+			return;
+	}
+
+	// We now have a target. Oh bliss, rapture, and contentment!
+
+	if (actor->target->z + actor->target->height > actor->z - FixedMul(32*FRACUNIT, actor->scale)
+		&& actor->target->z < actor->z + actor->height + FixedMul(32*FRACUNIT, actor->scale)
+		&& !(leveltime % (TICRATE*2)))
+	{
+		angle_t an;
+		fixed_t z;
+
+		// Actor shouldn't face target, so we'll do things a bit differently here
+
+		an = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) - actor->angle;
+
+		z = actor->z + actor->height/2;
+
+		if (an > ANGLE_45 && an < ANGLE_315) // fire as close as you can to the target, even if too sharp an angle from your front
+		{
+			fixed_t dist;
+			fixed_t dx, dy;
+
+			dist = P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y);
+
+			if (an > ANGLE_45 && an <= ANGLE_90) // fire at 45 degrees to the left
+			{
+				dx = actor->x + P_ReturnThrustX(actor, actor->angle + ANGLE_45, dist);
+				dy = actor->y + P_ReturnThrustY(actor, actor->angle + ANGLE_45, dist);
+			}
+			else if (an >= ANGLE_270 && an < ANGLE_315) // fire at 45 degrees to the right
+			{
+				dx = actor->x + P_ReturnThrustX(actor, actor->angle - ANGLE_45, dist);
+				dy = actor->y + P_ReturnThrustY(actor, actor->angle - ANGLE_45, dist);
+			}
+			else // fire straight ahead
+			{
+				dx = actor->x + P_ReturnThrustX(actor, actor->angle, dist);
+				dy = actor->y + P_ReturnThrustY(actor, actor->angle, dist);
+			}
+
+			P_SpawnPointMissile(actor, dx, dy, actor->target->z, MT_ROCKET, actor->x, actor->y, z);
+		}
+		else
+			P_SpawnXYZMissile(actor, actor->target, MT_ROCKET, actor->x, actor->y, z);
+	}
+
+	if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->target->z > actor->z)
+	|| (actor->eflags & MFE_VERTICALFLIP && (actor->target->z + actor->target->height) > (actor->z + actor->height)))
+		actor->momz += FixedMul(actor->info->speed, actor->scale);
+	else if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->target->z < actor->z)
+	|| (actor->eflags & MFE_VERTICALFLIP && (actor->target->z + actor->target->height) < (actor->z + actor->height)))
+		actor->momz -= FixedMul(actor->info->speed, actor->scale);
+
+	actor->momz /= 2;
+}
+
+// Function: A_SharpChase
+//
+// Description: Thinker/Chase routine for Spincushions
+//
+// var1 = unused
+// var2 = unused
+//
+void A_SharpChase(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SharpChase", actor))
+		return;
+#endif
+
+	if (actor->reactiontime)
+	{
+		INT32 delta;
+
+		actor->reactiontime--;
+
+		// turn towards movement direction if not there yet
+		if (actor->movedir < NUMDIRS)
+		{
+			actor->angle &= (7<<29);
+			delta = actor->angle - (actor->movedir << 29);
+
+			if (delta > 0)
+				actor->angle -= ANGLE_45;
+			else if (delta < 0)
+				actor->angle += ANGLE_45;
+		}
+
+		if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+		{
+			// look for a new target
+			if (P_LookForPlayers(actor, true, false, 0))
+				return; // got a new target
+
+			P_SetMobjState(actor, actor->info->spawnstate);
+			return;
+		}
+
+		// chase towards player
+		if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
+			P_NewChaseDir(actor);
+	}
+	else
+	{
+		actor->threshold = actor->info->painchance;
+		P_SetMobjState(actor, actor->info->missilestate);
+		S_StartSound(actor, actor->info->attacksound);
+	}
+}
+
+// Function: A_SharpSpin
+//
+// Description: Spin chase routine for Spincushions
+//
+// var1 = object # to spawn as dust (if not provided not done)
+// var2 = if nonzero, do the old-style spinning using this as the angle difference
+//
+void A_SharpSpin(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	angle_t oldang = actor->angle;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SharpSpin", actor))
+		return;
+#endif
+
+	if (actor->threshold && actor->target)
+	{
+		angle_t ang = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+		P_Thrust(actor, ang, actor->info->speed*actor->scale);
+		if (locvar2)
+			actor->angle += locvar2; // ANGLE_22h;
+		else
+			actor->angle = ang;
+		actor->threshold--;
+		if (leveltime & 1)
+			S_StartSound(actor, actor->info->painsound);
+	}
+	else
+	{
+		actor->reactiontime = actor->info->reactiontime;
+		P_SetMobjState(actor, actor->info->meleestate);
+	}
+
+	P_SharpDust(actor, locvar1, oldang);
+}
+
+// Function: A_SharpDecel
+//
+// Description: Slow down the Spincushion
+//
+// var1 = unused
+// var2 = unused
+//
+void A_SharpDecel(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SharpDecel", actor))
+		return;
+#endif
+
+	if (actor->momx > 2 || actor->momy > 2)
+	{
+		actor->momx >>= 1;
+		actor->momy >>= 1;
+	}
+	else
+		P_SetMobjState(actor, actor->info->xdeathstate);
+}
+
+// Function: A_CrushstaceanWalk
+//
+// Description: Crushstacean movement
+//
+// var1 = speed (actor info's speed if 0)
+// var2 = state to switch to when blocked (spawnstate if 0)
+//
+void A_CrushstaceanWalk(mobj_t *actor)
+{
+	INT32 locvar1 = (var1 ? var1 : (INT32)actor->info->speed);
+	INT32 locvar2 = (var2 ? var2 : (INT32)actor->info->spawnstate);
+	angle_t ang = actor->angle + ((actor->flags2 & MF2_AMBUSH) ? ANGLE_90 : ANGLE_270);
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_CrushstaceanWalk", actor))
+		return;
+#endif
+
+	actor->reactiontime--;
+
+	if (!P_TryMove(actor,
+		actor->x + P_ReturnThrustX(actor, ang, locvar1*actor->scale),
+		actor->y + P_ReturnThrustY(actor, ang, locvar1*actor->scale),
+		false)
+	|| (actor->reactiontime-- <= 0))
+	{
+		actor->flags2 ^= MF2_AMBUSH;
+		P_SetMobjState(actor, locvar2);
+		actor->reactiontime = actor->info->reactiontime;
+	}
+}
+
+// Function: A_CrushstaceanPunch
+//
+// Description: Crushstacean attack
+//
+// var1 = unused
+// var2 = state to go to if unsuccessful (spawnstate if 0)
+//
+void A_CrushstaceanPunch(mobj_t *actor)
+{
+	//INT32 locvar1 = var1;
+	INT32 locvar2 = (var2 ? var2 : (INT32)actor->info->spawnstate);
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_CrushstaceanPunch", actor))
+		return;
+#endif
+
+	if (!actor->tracer)
+		return;
+
+	if (!actor->target)
+	{
+		P_SetMobjState(actor, locvar2);
+		return;
+	}
+
+	actor->tracer->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+	P_SetMobjState(actor->tracer, actor->tracer->info->missilestate);
+	actor->tracer->extravalue1 = actor->tracer->extravalue2 = 0;
+	S_StartSound(actor, actor->info->attacksound);
+}
+
+// Function: A_CrushclawAim
+//
+// Description: Crushstacean claw aiming
+//
+// var1 = sideways offset
+// var2 = vertical offset
+//
+void A_CrushclawAim(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	mobj_t *crab = actor->tracer;
+	angle_t ang;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_CrushclawAim", actor))
+		return;
+#endif
+
+	if (!crab)
+	{
+		P_RemoveMobj(actor);
+		return; // there is only one step and it is crab
+	}
+
+	if (crab->target || P_LookForPlayers(crab, true, false, 600*crab->scale))
+		ang = R_PointToAngle2(crab->x, crab->y, crab->target->x, crab->target->y);
+	else
+		ang = crab->angle + ((crab->flags2 & MF2_AMBUSH) ? ANGLE_90 : ANGLE_270);
+	ang -= actor->angle;
+
+#define anglimit ANGLE_22h
+#define angfactor 5
+	if (ang < ANGLE_180)
+	{
+		if (ang > anglimit)
+			ang = anglimit;
+		ang /= angfactor;
+	}
+	else
+	{
+		ang = InvAngle(ang);
+		if (ang > anglimit)
+			ang = anglimit;
+		ang = InvAngle(ang/angfactor);
+	}
+	actor->angle += ang;
+#undef anglimit
+#undef angfactor
+
+	P_TeleportMove(actor,
+		crab->x + P_ReturnThrustX(actor, actor->angle, locvar1*crab->scale),
+		crab->y + P_ReturnThrustY(actor, actor->angle, locvar1*crab->scale),
+		crab->z + locvar2*crab->scale);
+
+	if (!crab->target || !crab->info->missilestate || (statenum_t)(crab->state-states) == crab->info->missilestate)
+		return;
+
+	if (((ang + ANG1) < ANG2) || P_AproxDistance(crab->x - crab->target->x, crab->y - crab->target->y) < 333*crab->scale)
+		P_SetMobjState(crab, crab->info->missilestate);
+}
+
+// Function: A_CrushclawLaunch
+//
+// Description: Crushstacean claw launching
+//
+// var1:
+//		0 - forwards
+//		anything else - backwards
+// var2 = state to change to when done
+//
+void A_CrushclawLaunch(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	mobj_t *crab = actor->tracer;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_CrushclawLaunch", actor))
+		return;
+#endif
+
+	if (!crab)
+	{
+		mobj_t *chainnext;
+		while (actor)
+		{
+			chainnext = actor->target;
+			P_RemoveMobj(actor);
+			actor = chainnext;
+		}
+		return; // there is only one step and it is crab
+	}
+
+	if (!actor->extravalue1)
+	{
+		S_StartSound(actor, actor->info->activesound);
+		actor->extravalue1 = ((locvar1) ? -1 : 32);
+	}
+	else if (actor->extravalue1 != 1)
+		actor->extravalue1 -= 1;
+
+#define CSEGS 5
+	if (!actor->target)
+	{
+		mobj_t *prevchain = actor;
+		UINT8 i = 0;
+		for (i = 0; (i < CSEGS); i++)
+		{
+			mobj_t *newchain = P_SpawnMobjFromMobj(actor, 0, 0, 0, actor->info->raisestate);
+			prevchain->target = newchain;
+			prevchain = newchain;
+		}
+		actor->target->angle = R_PointToAngle2(actor->target->x, actor->target->y, crab->target->x, crab->target->y);
+	}
+
+	if ((!locvar1) && crab->target)
+	{
+#define anglimit ANGLE_22h
+#define angfactor 7
+		angle_t ang = R_PointToAngle2(actor->target->x, actor->target->y, crab->target->x, crab->target->y) - actor->target->angle;
+		if (ang < ANGLE_180)
+		{
+			if (ang > anglimit)
+				ang = anglimit;
+			ang /= angfactor;
+		}
+		else
+		{
+			ang = InvAngle(ang);
+			if (ang > anglimit)
+				ang = anglimit;
+			ang /= angfactor;
+			ang = InvAngle(ang);
+		}
+		actor->target->angle += ang;
+		actor->angle = actor->target->angle;
+	}
+
+	actor->extravalue2 += actor->extravalue1;
+
+	if (!P_TryMove(actor,
+		actor->target->x + P_ReturnThrustX(actor, actor->target->angle, actor->extravalue2*actor->scale),
+		actor->target->y + P_ReturnThrustY(actor, actor->target->angle, actor->extravalue2*actor->scale),
+		true)
+		&& !locvar1)
+	{
+		actor->extravalue1 = 0;
+		actor->extravalue2 = FixedHypot(actor->x - actor->target->x, actor->y - actor->target->y)>>FRACBITS;
+		P_SetMobjState(actor, locvar2);
+		S_StopSound(actor);
+		S_StartSound(actor, sfx_s3k49);
+	}
+	else
+	{
+		actor->z = actor->target->z;
+		if ((!locvar1 && (actor->extravalue2 > 256)) || (locvar1 && (actor->extravalue2 < 16)))
+		{
+			if (locvar1) // In case of retracting, resume crab and remove the chain.
+			{
+				mobj_t *chain = actor->target, *chainnext;
+				while (chain)
+				{
+					chainnext = chain->target;
+					P_RemoveMobj(chain);
+					chain = chainnext;
+				}
+				actor->extravalue2 = 0;
+				actor->angle = R_PointToAngle2(crab->x, crab->y, actor->x, actor->y);
+				P_SetTarget(&actor->target, NULL);
+				P_SetTarget(&crab->target, NULL);
+				P_SetMobjState(crab, crab->state->nextstate);
+			}
+			actor->extravalue1 = 0;
+			P_SetMobjState(actor, locvar2);
+			S_StopSound(actor);
+			if (!locvar1)
+				S_StartSound(actor, sfx_s3k64);
+		}
+	}
+
+	if (!actor->target)
+		return;
+
+	{
+		mobj_t *chain = actor->target->target;
+		fixed_t dx = (actor->x - actor->target->x)/CSEGS, dy = (actor->y - actor->target->y)/CSEGS, dz = (actor->z - actor->target->z)/CSEGS;
+		fixed_t idx = dx, idy = dy, idz = dz;
+		while (chain)
+		{
+			P_TeleportMove(chain, actor->target->x + idx, actor->target->y + idy, actor->target->z + idz);
+			chain->watertop = chain->z;
+			idx += dx;
+			idy += dy;
+			idz += dz;
+			chain = chain->target;
+		}
+	}
+#undef CSEGS
+}
+
+// Function: A_VultureVtol
+//
+// Description: Vulture rising up to match target's height
+//
+// var1 = unused
+// var2 = unused
+//
+void A_VultureVtol(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_VultureVtol", actor))
+		return;
+#endif
+
+	if (!actor->target)
+		return;
+
+	actor->flags |= MF_NOGRAVITY;
+	actor->flags |= MF_FLOAT;
+
+	A_FaceTarget(actor);
+
+	S_StopSound(actor);
+
+	if (actor->z < actor->target->z+(actor->target->height/4) && actor->z + actor->height < actor->ceilingz)
+		actor->momz = FixedMul(2*FRACUNIT, actor->scale);
+	else if (actor->z > (actor->target->z+(actor->target->height/4)*3) && actor->z > actor->floorz)
+		actor->momz = FixedMul(-2*FRACUNIT, actor->scale);
+	else
+	{
+		// Attack!
+		actor->momz = 0;
+		P_SetMobjState(actor, actor->info->missilestate);
+		S_StartSound(actor, actor->info->activesound);
+	}
+}
+
+// Function: A_VultureCheck
+//
+// Description: If the vulture is stopped, look for a new target
+//
+// var1 = unused
+// var2 = unused
+//
+void A_VultureCheck(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_VultureCheck", actor))
+		return;
+#endif
+
+	if (actor->momx || actor->momy)
+		return;
+
+	actor->flags &= ~MF_NOGRAVITY; // Fall down
+
+	if (actor->z <= actor->floorz)
+	{
+		actor->angle -= ANGLE_180; // turn around
+		P_SetMobjState(actor, actor->info->spawnstate);
+	}
+}
+
+// Function: A_SkimChase
+//
+// Description: Thinker/Chase routine for Skims
+//
+// var1 = unused
+// var2 = unused
+//
+void A_SkimChase(mobj_t *actor)
+{
+	INT32 delta;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SkimChase", actor))
+		return;
+#endif
+	if (actor->reactiontime)
+		actor->reactiontime--;
+
+	// modify target threshold
+	if (actor->threshold)
+	{
+		if (!actor->target || actor->target->health <= 0)
+			actor->threshold = 0;
+		else
+			actor->threshold--;
+	}
+
+	// turn towards movement direction if not there yet
+	if (actor->movedir < NUMDIRS)
+	{
+		actor->angle &= (7<<29);
+		delta = actor->angle - (actor->movedir << 29);
+
+		if (delta > 0)
+			actor->angle -= ANGLE_45;
+		else if (delta < 0)
+			actor->angle += ANGLE_45;
+	}
+
+	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+	{
+		// look for a new target
+		P_LookForPlayers(actor, true, false, 0);
+
+		// the spawnstate for skims already calls this function so just return either way
+		// without changing state
+		return;
+	}
+
+	// do not attack twice in a row
+	if (actor->flags2 & MF2_JUSTATTACKED)
+	{
+		actor->flags2 &= ~MF2_JUSTATTACKED;
+		P_NewChaseDir(actor);
+		return;
+	}
+
+	// check for melee attack
+	if (actor->info->meleestate && P_SkimCheckMeleeRange(actor))
+	{
+		if (actor->info->attacksound)
+			S_StartAttackSound(actor, actor->info->attacksound);
+
+		P_SetMobjState(actor, actor->info->meleestate);
+		return;
+	}
+
+	// check for missile attack
+	if (actor->info->missilestate)
+	{
+		if (actor->movecount || !P_CheckMissileRange(actor))
+			goto nomissile;
+
+		P_SetMobjState(actor, actor->info->missilestate);
+		actor->flags2 |= MF2_JUSTATTACKED;
+		return;
+	}
+
+nomissile:
+	// possibly choose another target
+	if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
+		&& P_LookForPlayers(actor, true, false, 0))
+		return; // got a new target
+
+	// chase towards player
+	if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
+		P_NewChaseDir(actor);
+}
+
+// Function: A_FaceTarget
+//
+// Description: Immediately turn to face towards your target.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_FaceTarget(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_FaceTarget", actor))
+		return;
+#endif
+	if (!actor->target)
+		return;
+
+	actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+}
+
+// Function: A_FaceTracer
+//
+// Description: Immediately turn to face towards your tracer.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_FaceTracer(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_FaceTracer", actor))
+		return;
+#endif
+	if (!actor->tracer)
+		return;
+
+	actor->angle = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y);
+}
+
+// Function: A_LobShot
+//
+// Description: Lob an object at your target.
+//
+// var1 = object # to lob
+// var2:
+//		var2 >> 16 = height offset
+//		var2 & 65535 = airtime
+//
+void A_LobShot(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2 >> 16;
+	mobj_t *shot, *hitspot;
+	angle_t an;
+	fixed_t z;
+	fixed_t dist;
+	fixed_t vertical, horizontal;
+	fixed_t airtime = var2 & 65535;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_LobShot", actor))
+		return;
+#endif
+	if (!actor->target)
+		return;
+
+	A_FaceTarget(actor);
+
+	if (actor->eflags & MFE_VERTICALFLIP)
+	{
+		z = actor->z + actor->height - FixedMul(locvar2*FRACUNIT, actor->scale);
+		if (actor->type == MT_BLACKEGGMAN)
+			z -= FixedMul(mobjinfo[locvar1].height, actor->scale/2);
+		else
+			z -= FixedMul(mobjinfo[locvar1].height, actor->scale);
+	}
+	else
+		z = actor->z + FixedMul(locvar2*FRACUNIT, actor->scale);
+
+	shot = P_SpawnMobj(actor->x, actor->y, z, locvar1);
+
+	if (actor->type == MT_BLACKEGGMAN)
+	{
+		shot->destscale = actor->scale/2;
+		P_SetScale(shot, actor->scale/2);
+	}
+	else
+	{
+		shot->destscale = actor->scale;
+		P_SetScale(shot, actor->scale);
+	}
+
+	// Keep track of where it's going to land
+	hitspot = P_SpawnMobj(actor->target->x&(64*FRACUNIT-1), actor->target->y&(64*FRACUNIT-1), actor->target->subsector->sector->floorheight, MT_NULL);
+	hitspot->tics = airtime;
+	P_SetTarget(&shot->tracer, hitspot);
+
+	P_SetTarget(&shot->target, actor); // where it came from
+
+	shot->angle = an = actor->angle;
+	an >>= ANGLETOFINESHIFT;
+
+	dist = P_AproxDistance(actor->target->x - shot->x, actor->target->y - shot->y);
+
+	horizontal = dist / airtime;
+	vertical = FixedMul((gravity*airtime)/2, shot->scale);
+
+	shot->momx = FixedMul(horizontal, FINECOSINE(an));
+	shot->momy = FixedMul(horizontal, FINESINE(an));
+	shot->momz = vertical;
+
+/* Try to adjust when destination is not the same height
+	if (actor->z != actor->target->z)
+	{
+		fixed_t launchhyp;
+		fixed_t diff;
+		fixed_t orig;
+
+		diff = actor->z - actor->target->z;
+		{
+			launchhyp = P_AproxDistance(horizontal, vertical);
+
+			orig = FixedMul(FixedDiv(vertical, horizontal), diff);
+
+			CONS_Debug(DBG_GAMELOGIC, "orig: %d\n", (orig)>>FRACBITS);
+
+			horizontal = dist / airtime;
+			vertical = (gravity*airtime)/2;
+		}
+		dist -= orig;
+		shot->momx = FixedMul(horizontal, FINECOSINE(an));
+		shot->momy = FixedMul(horizontal, FINESINE(an));
+		shot->momz = vertical;
+*/
+
+	if (shot->info->seesound)
+		S_StartSound(shot, shot->info->seesound);
+
+	if (!(actor->flags & MF_BOSS))
+	{
+		if (ultimatemode)
+			actor->reactiontime = actor->info->reactiontime*TICRATE;
+		else
+			actor->reactiontime = actor->info->reactiontime*TICRATE*2;
+	}
+}
+
+// Function: A_FireShot
+//
+// Description: Shoot an object at your target.
+//
+// var1 = object # to shoot
+// var2 = height offset
+//
+void A_FireShot(mobj_t *actor)
+{
+	fixed_t z;
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_FireShot", actor))
+		return;
+#endif
+	if (!actor->target)
+		return;
+
+	A_FaceTarget(actor);
+
+	if (actor->eflags & MFE_VERTICALFLIP)
+		z = actor->z + actor->height - FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
+	else
+		z = actor->z + FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
+
+	P_SpawnXYZMissile(actor, actor->target, locvar1, actor->x, actor->y, z);
+
+	if (!(actor->flags & MF_BOSS))
+	{
+		if (ultimatemode)
+			actor->reactiontime = actor->info->reactiontime*TICRATE;
+		else
+			actor->reactiontime = actor->info->reactiontime*TICRATE*2;
+	}
+}
+
+// Function: A_SuperFireShot
+//
+// Description: Shoot an object at your target that will even stall Super Sonic.
+//
+// var1 = object # to shoot
+// var2 = height offset
+//
+void A_SuperFireShot(mobj_t *actor)
+{
+	fixed_t z;
+	mobj_t *mo;
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SuperFireShot", actor))
+		return;
+#endif
+	if (!actor->target)
+		return;
+
+	A_FaceTarget(actor);
+
+	if (actor->eflags & MFE_VERTICALFLIP)
+		z = actor->z + actor->height - FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
+	else
+		z = actor->z + FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
+
+	mo = P_SpawnXYZMissile(actor, actor->target, locvar1, actor->x, actor->y, z);
+
+	if (mo)
+		mo->flags2 |= MF2_SUPERFIRE;
+
+	if (!(actor->flags & MF_BOSS))
+	{
+		if (ultimatemode)
+			actor->reactiontime = actor->info->reactiontime*TICRATE;
+		else
+			actor->reactiontime = actor->info->reactiontime*TICRATE*2;
+	}
+}
+
+// Function: A_BossFireShot
+//
+// Description: Shoot an object at your target ala Bosses:
+//
+// var1 = object # to shoot
+// var2:
+//		0 - Boss 1 Left side
+//		1 - Boss 1 Right side
+//		2 - Boss 3 Left side upper
+//		3 - Boss 3 Left side lower
+//		4 - Boss 3 Right side upper
+//		5 - Boss 3 Right side lower
+//
+void A_BossFireShot(mobj_t *actor)
+{
+	fixed_t x, y, z;
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_BossFireShot", actor))
+		return;
+#endif
+	if (!actor->target)
+		return;
+
+	A_FaceTarget(actor);
+
+	switch (locvar2)
+	{
+		case 0:
+			x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
+			y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
+			if (actor->eflags & MFE_VERTICALFLIP)
+				z = actor->z + actor->height - FixedMul(48*FRACUNIT, actor->scale);
+			else
+				z = actor->z + FixedMul(48*FRACUNIT, actor->scale);
+			break;
+		case 1:
+			x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
+			y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
+			if (actor->eflags & MFE_VERTICALFLIP)
+				z = actor->z + actor->height - FixedMul(48*FRACUNIT, actor->scale);
+			else
+				z = actor->z + FixedMul(48*FRACUNIT, actor->scale);
+			break;
+		case 2:
+			x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(56*FRACUNIT, actor->scale));
+			y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(56*FRACUNIT, actor->scale));
+			if (actor->eflags & MFE_VERTICALFLIP)
+				z = actor->z + actor->height - FixedMul(42*FRACUNIT, actor->scale);
+			else
+				z = actor->z + FixedMul(42*FRACUNIT, actor->scale);
+			break;
+		case 3:
+			x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(58*FRACUNIT, actor->scale));
+			y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(58*FRACUNIT, actor->scale));
+			if (actor->eflags & MFE_VERTICALFLIP)
+				z = actor->z + actor->height - FixedMul(30*FRACUNIT, actor->scale);
+			else
+				z = actor->z + FixedMul(30*FRACUNIT, actor->scale);
+			break;
+		case 4:
+			x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(56*FRACUNIT, actor->scale));
+			y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(56*FRACUNIT, actor->scale));
+			if (actor->eflags & MFE_VERTICALFLIP)
+				z = actor->z + actor->height - FixedMul(42*FRACUNIT, actor->scale);
+			else
+				z = actor->z + FixedMul(42*FRACUNIT, actor->scale);
+			break;
+		case 5:
+			x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(58*FRACUNIT, actor->scale));
+			y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(58*FRACUNIT, actor->scale));
+			if (actor->eflags & MFE_VERTICALFLIP)
+				z = actor->z + actor->height - FixedMul(30*FRACUNIT, actor->scale);
+			else
+				z = actor->z + FixedMul(30*FRACUNIT, actor->scale);
+			break;
+		default:
+			x = actor->x;
+			y = actor->y;
+			z = actor->z + actor->height/2;
+			break;
+	}
+
+	P_SpawnXYZMissile(actor, actor->target, locvar1, x, y, z);
+}
+
+// Function: A_Boss7FireMissiles
+//
+// Description: Shoot 4 missiles of a specific object type at your target ala Black Eggman
+//
+// var1 = object # to shoot
+// var2 = firing sound
+//
+void A_Boss7FireMissiles(mobj_t *actor)
+{
+	mobj_t dummymo;
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Boss7FireMissiles", actor))
+		return;
+#endif
+
+	if (!actor->target)
+	{
+		P_SetMobjState(actor, actor->info->spawnstate);
+		return;
+	}
+
+	A_FaceTarget(actor);
+
+	S_StartSound(NULL, locvar2);
+
+	// set dummymo's coordinates
+	dummymo.x = actor->target->x;
+	dummymo.y = actor->target->y;
+	dummymo.z = actor->target->z + FixedMul(16*FRACUNIT, actor->scale); // raised height
+
+	P_SpawnXYZMissile(actor, &dummymo, locvar1,
+		actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
+		actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
+		actor->z + FixedDiv(actor->height, 3*FRACUNIT/2));
+
+	P_SpawnXYZMissile(actor, &dummymo, locvar1,
+		actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
+		actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
+		actor->z + FixedDiv(actor->height, 3*FRACUNIT/2));
+
+	P_SpawnXYZMissile(actor, &dummymo, locvar1,
+		actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
+		actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
+		actor->z + actor->height/2);
+
+	P_SpawnXYZMissile(actor, &dummymo, locvar1,
+		actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
+		actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
+		actor->z + actor->height/2);
+}
+
+// Function: A_Boss1Laser
+//
+// Description: Shoot an object at your target ala Bosses:
+//
+// var1 = object # to shoot
+// var2:
+//		0 - Boss 1 Left side
+//		1 - Boss 1 Right side
+//
+void A_Boss1Laser(mobj_t *actor)
+{
+	fixed_t x, y, z, floorz, speed;
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	INT32 i;
+	angle_t angle;
+	mobj_t *point;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Boss1Laser", actor))
+		return;
+#endif
+	if (!actor->target)
+		return;
+
+	switch (locvar2)
+	{
+		case 0:
+			x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
+			y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
+			if (actor->eflags & MFE_VERTICALFLIP)
+				z = actor->z + actor->height - FixedMul(56*FRACUNIT, actor->scale) - mobjinfo[locvar1].height;
+			else
+				z = actor->z + FixedMul(56*FRACUNIT, actor->scale);
+			break;
+		case 1:
+			x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
+			y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
+			if (actor->eflags & MFE_VERTICALFLIP)
+				z = actor->z + actor->height - FixedMul(56*FRACUNIT, actor->scale) - mobjinfo[locvar1].height;
+			else
+				z = actor->z + FixedMul(56*FRACUNIT, actor->scale);
+			break;
+		default:
+			x = actor->x;
+			y = actor->y;
+			z = actor->z + actor->height/2;
+			break;
+	}
+
+	if (!(actor->flags2 & MF2_FIRING))
+	{
+		actor->angle = R_PointToAngle2(x, y, actor->target->x, actor->target->y);
+		if (mobjinfo[locvar1].seesound)
+			S_StartSound(actor, mobjinfo[locvar1].seesound);
+		if (!(actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH))
+		{
+			point = P_SpawnMobj(x + P_ReturnThrustX(actor, actor->angle, actor->radius), y + P_ReturnThrustY(actor, actor->angle, actor->radius), actor->z - actor->height / 2, MT_EGGMOBILE_TARGET);
+			point->angle = actor->angle;
+			point->fuse = actor->tics+1;
+			P_SetTarget(&point->target, actor->target);
+			P_SetTarget(&actor->target, point);
+		}
+	}
+	/* -- the following was relevant when the MT_EGGMOBILE_TARGET was allowed to move left and right from its path
+	else if (actor->target && !(actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH))
+		actor->angle = R_PointToAngle2(x, y, actor->target->x, actor->target->y);*/
+
+	if (actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH)
+		angle = FixedAngle(FixedDiv(actor->tics*160*FRACUNIT, actor->state->tics*FRACUNIT) + 10*FRACUNIT);
+	else
+		angle = R_PointToAngle2(z + (mobjinfo[locvar1].height>>1), 0, actor->target->z, R_PointToDist2(x, y, actor->target->x, actor->target->y));
+	point = P_SpawnMobj(x, y, z, locvar1);
+	P_SetTarget(&point->target, actor);
+	point->angle = actor->angle;
+	speed = point->radius*2;
+	point->momz = FixedMul(FINECOSINE(angle>>ANGLETOFINESHIFT), speed);
+	point->momx = FixedMul(FINESINE(angle>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(point->angle>>ANGLETOFINESHIFT), speed));
+	point->momy = FixedMul(FINESINE(angle>>ANGLETOFINESHIFT), FixedMul(FINESINE(point->angle>>ANGLETOFINESHIFT), speed));
+
+	for (i = 0; i < 256; i++)
+	{
+		mobj_t *mo = P_SpawnMobj(point->x, point->y, point->z, point->type);
+		mo->angle = point->angle;
+		P_UnsetThingPosition(mo);
+		mo->flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY;
+		P_SetThingPosition(mo);
+
+		x = point->x, y = point->y, z = point->z;
+		if (P_RailThinker(point))
+			break;
+	}
+
+	floorz = P_FloorzAtPos(x, y, z, mobjinfo[MT_EGGMOBILE_FIRE].height);
+	if (z - floorz < mobjinfo[MT_EGGMOBILE_FIRE].height>>1)
+	{
+		point = P_SpawnMobj(x, y, floorz+1, MT_EGGMOBILE_FIRE);
+		point->target = actor;
+		point->destscale = 3*FRACUNIT;
+		point->scalespeed = FRACUNIT>>2;
+		point->fuse = TICRATE;
+	}
+
+	if (actor->tics > 1)
+		actor->flags2 |= MF2_FIRING;
+	else
+		actor->flags2 &= ~MF2_FIRING;
+}
+
+// Function: A_FocusTarget
+//
+// Description: Home in on your target.
+//
+// var1:
+//		0 - accelerative focus with friction
+//		1 - steady focus with fixed movement speed
+//      anything else - don't move
+// var2:
+//		0 - don't trace target, just move forwards
+//      & 1 - change horizontal angle
+//      & 2 - change vertical angle
+//
+void A_FocusTarget(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_FocusTarget", actor))
+		return;
+#endif
+
+	if (actor->target)
+	{
+		fixed_t speed = FixedMul(actor->info->speed, actor->scale);
+		fixed_t dist = (locvar2 ? R_PointToDist2(actor->x, actor->y, actor->target->x, actor->target->y) : speed+1);
+		angle_t hangle = ((locvar2 & 1) ? R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) : actor->angle);
+		angle_t vangle = ((locvar2 & 2) ? R_PointToAngle2(actor->z , 0, actor->target->z + (actor->target->height>>1), dist) : ANGLE_90);
+		switch(locvar1)
+		{
+		case 0:
+			{
+				actor->momx -= actor->momx>>4, actor->momy -= actor->momy>>4, actor->momz -= actor->momz>>4;
+				actor->momz += FixedMul(FINECOSINE(vangle>>ANGLETOFINESHIFT), speed);
+				actor->momx += FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(hangle>>ANGLETOFINESHIFT), speed));
+				actor->momy += FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINESINE(hangle>>ANGLETOFINESHIFT), speed));
+			}
+			break;
+		case 1:
+			if (dist > speed)
+			{
+				actor->momz = FixedMul(FINECOSINE(vangle>>ANGLETOFINESHIFT), speed);
+				actor->momx = FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(hangle>>ANGLETOFINESHIFT), speed));
+				actor->momy = FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINESINE(hangle>>ANGLETOFINESHIFT), speed));
+			}
+			else
+			{
+				actor->momx = 0, actor->momy = 0, actor->momz = 0;
+				actor->z = actor->target->z + (actor->target->height>>1);
+				P_TryMove(actor, actor->target->x, actor->target->y, true);
+			}
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+// Function: A_Boss4Reverse
+//
+// Description: Reverse arms direction.
+//
+// var1 = sfx to play
+// var2 = unused
+//
+void A_Boss4Reverse(mobj_t *actor)
+{
+	sfxenum_t locvar1 = (sfxenum_t)var1;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Boss4Reverse", actor))
+		return;
+#endif
+	S_StartSound(NULL, locvar1);
+	actor->reactiontime = 0;
+	if (actor->movedir == 1)
+		actor->movedir = 2;
+	else
+		actor->movedir = 1;
+}
+
+// Function: A_Boss4SpeedUp
+//
+// Description: Speed up arms
+//
+// var1 = sfx to play
+// var2 = unused
+//
+void A_Boss4SpeedUp(mobj_t *actor)
+{
+	sfxenum_t locvar1 = (sfxenum_t)var1;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Boss4SpeedUp", actor))
+		return;
+#endif
+	S_StartSound(NULL, locvar1);
+	actor->reactiontime = 2;
+}
+
+// Function: A_Boss4Raise
+//
+// Description: Raise helmet
+//
+// var1 = sfx to play
+// var2 = unused
+//
+void A_Boss4Raise(mobj_t *actor)
+{
+	sfxenum_t locvar1 = (sfxenum_t)var1;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Boss4Raise", actor))
+		return;
+#endif
+	S_StartSound(NULL, locvar1);
+	actor->reactiontime = 1;
+}
+
+// Function: A_SkullAttack
+//
+// Description: Fly at the player like a missile.
+//
+// var1:
+//		0 - Fly at the player
+//		1 - Fly away from the player
+//		2 - Strafe in relation to the player
+// var2:
+//		0 - Fly horizontally and vertically
+//		1 - Fly horizontal-only (momz = 0)
+//
+#define SKULLSPEED (20*FRACUNIT)
+
+void A_SkullAttack(mobj_t *actor)
+{
+	mobj_t *dest;
+	angle_t an;
+	INT32 dist;
+	INT32 speed;
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SkullAttack", actor))
+		return;
+#endif
+	if (!actor->target)
+		return;
+
+	speed = FixedMul(SKULLSPEED, actor->scale);
+
+	dest = actor->target;
+	actor->flags2 |= MF2_SKULLFLY;
+	if (actor->info->activesound)
+		S_StartSound(actor, actor->info->activesound);
+	A_FaceTarget(actor);
+
+	if (locvar1 == 1)
+		actor->angle += ANGLE_180;
+	else if (locvar1 == 2)
+		actor->angle += (P_RandomChance(FRACUNIT/2)) ? ANGLE_90 : -ANGLE_90;
+
+	an = actor->angle >> ANGLETOFINESHIFT;
+
+	actor->momx = FixedMul(speed, FINECOSINE(an));
+	actor->momy = FixedMul(speed, FINESINE(an));
+	dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y);
+	dist = dist / speed;
+
+	if (dist < 1)
+		dist = 1;
+
+	actor->momz = (dest->z + (dest->height>>1) - actor->z) / dist;
+
+	if (locvar1 == 1)
+		actor->momz = -actor->momz;
+	if (locvar2 == 1)
+		actor->momz = 0;
+}
+
+// Function: A_BossZoom
+//
+// Description: Like A_SkullAttack, but used by Boss 1.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_BossZoom(mobj_t *actor)
+{
+	mobj_t *dest;
+	angle_t an;
+	INT32 dist;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_BossZoom", actor))
+		return;
+#endif
+	if (!actor->target)
+		return;
+
+	dest = actor->target;
+	actor->flags2 |= MF2_SKULLFLY;
+	if (actor->info->attacksound)
+		S_StartAttackSound(actor, actor->info->attacksound);
+	A_FaceTarget(actor);
+	an = actor->angle >> ANGLETOFINESHIFT;
+	actor->momx = FixedMul(FixedMul(actor->info->speed*5*FRACUNIT, actor->scale), FINECOSINE(an));
+	actor->momy = FixedMul(FixedMul(actor->info->speed*5*FRACUNIT, actor->scale), FINESINE(an));
+	dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y);
+	dist = dist / FixedMul(actor->info->speed*5*FRACUNIT, actor->scale);
+
+	if (dist < 1)
+		dist = 1;
+	actor->momz = (dest->z + (dest->height>>1) - actor->z) / dist;
+}
+
+// Function: A_BossScream
+//
+// Description: Spawns explosions and plays appropriate sounds around the defeated boss.
+//
+// var1:
+//		0 - Use movecount to spawn explosions evenly
+//		1 - Use P_Random to spawn explosions at complete random
+// var2 = Object to spawn. Default is MT_BOSSEXPLODE.
+//
+void A_BossScream(mobj_t *actor)
+{
+	mobj_t *mo;
+	fixed_t x, y, z;
+	angle_t fa;
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	mobjtype_t explodetype;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_BossScream", actor))
+		return;
+#endif
+	switch (locvar1)
+	{
+	default:
+	case 0:
+		actor->movecount += 4*16;
+		actor->movecount %= 360;
+		fa = (FixedAngle(actor->movecount*FRACUNIT)>>ANGLETOFINESHIFT) & FINEMASK;
+		break;
+	case 1:
+		fa = (FixedAngle(P_RandomKey(360)*FRACUNIT)>>ANGLETOFINESHIFT) & FINEMASK;
+		break;
+	}
+	x = actor->x + FixedMul(FINECOSINE(fa),actor->radius);
+	y = actor->y + FixedMul(FINESINE(fa),actor->radius);
+
+	// Determine what mobj to spawn. If undefined or invalid, use MT_BOSSEXPLODE as default.
+	if (locvar2 <= 0 || locvar2 >= NUMMOBJTYPES)
+		explodetype = MT_BOSSEXPLODE;
+	else
+		explodetype = (mobjtype_t)locvar2;
+
+	if (actor->eflags & MFE_VERTICALFLIP)
+		z = actor->z + actor->height - mobjinfo[explodetype].height - FixedMul((P_RandomByte()<<(FRACBITS-2)) - 8*FRACUNIT, actor->scale);
+	else
+		z = actor->z + FixedMul((P_RandomByte()<<(FRACBITS-2)) - 8*FRACUNIT, actor->scale);
+
+	mo = P_SpawnMobj(x, y, z, explodetype);
+	if (actor->eflags & MFE_VERTICALFLIP)
+		mo->flags2 |= MF2_OBJECTFLIP;
+	mo->destscale = actor->scale;
+	P_SetScale(mo, mo->destscale);
+	if (actor->info->deathsound)
+		S_StartSound(mo, actor->info->deathsound);
+}
+
+// Function: A_Scream
+//
+// Description: Starts the death sound of the object.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_Scream(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Scream", actor))
+		return;
+#endif
+	if (actor->tracer && (actor->tracer->type == MT_SHELL || actor->tracer->type == MT_FIREBALL))
+		S_StartScreamSound(actor, sfx_mario2);
+	else if (actor->info->deathsound)
+		S_StartScreamSound(actor, actor->info->deathsound);
+}
+
+// Function: A_Pain
+//
+// Description: Starts the pain sound of the object.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_Pain(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Pain", actor))
+		return;
+#endif
+	if (actor->info->painsound)
+		S_StartSound(actor, actor->info->painsound);
+
+	actor->flags2 &= ~MF2_FIRING;
+	actor->flags2 &= ~MF2_SUPERFIRE;
+}
+
+// Function: A_Fall
+//
+// Description: Changes a dying object's flags to reflect its having fallen to the ground.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_Fall(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Fall", actor))
+		return;
+#endif
+	// actor is on ground, it can be walked over
+	actor->flags &= ~MF_SOLID;
+
+	// fall through the floor
+	actor->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY;
+
+	// So change this if corpse objects
+	// are meant to be obstacles.
+}
+
+#define LIVESBOXDISPLAYPLAYER // Use displayplayer instead of closest player
+
+// Function: A_1upThinker
+//
+// Description: Used by the 1up box to show the player's face.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_1upThinker(mobj_t *actor)
+{
+	INT32 i;
+	fixed_t dist = INT32_MAX;
+	fixed_t temp;
+	INT32 closestplayer = -1;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_1upThinker", actor))
+		return;
+#endif
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		if (!playeringame[i] || players[i].bot || players[i].spectator)
+			continue;
+
+		if (!players[i].mo)
+			continue;
+
+		if ((netgame || multiplayer) && players[i].playerstate != PST_LIVE)
+			continue;
+
+		temp = P_AproxDistance(players[i].mo->x-actor->x, players[i].mo->y-actor->y);
+
+		if (temp < dist)
+		{
+			closestplayer = i;
+			dist = temp;
+		}
+	}
+
+	if (closestplayer == -1 || skins[players[closestplayer].skin].sprites[SPR2_LIFE].numframes == 0)
+	{ // Closest player not found (no players in game?? may be empty dedicated server!), or does not have correct sprite.
+		if (actor->tracer) {
+			P_RemoveMobj(actor->tracer);
+			actor->tracer = NULL;
+		}
+		return;
+	}
+
+	// We're using the overlay, so use the overlay 1up box (no text)
+	actor->sprite = SPR_TV1P;
+
+	if (!actor->tracer)
+	{
+		P_SetTarget(&actor->tracer, P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY));
+		P_SetTarget(&actor->tracer->target, actor);
+		actor->tracer->skin = &skins[players[closestplayer].skin]; // required here to prevent spr2 default showing stand for a single frame
+		P_SetMobjState(actor->tracer, actor->info->seestate);
+
+		// The overlay is going to be one tic early turning off and on
+		// because it's going to get its thinker run the frame we spawned it.
+		// So make it take one tic longer if it just spawned.
+		++actor->tracer->tics;
+	}
+
+	actor->tracer->color = players[closestplayer].mo->color;
+	actor->tracer->skin = &skins[players[closestplayer].skin];
+}
+
+// Function: A_MonitorPop
+//
+// Description: Used by monitors when they explode.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_MonitorPop(mobj_t *actor)
+{
+	mobjtype_t item = 0;
+	mobj_t *newmobj;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_MonitorPop", actor))
+		return;
+#endif
+
+	// Spawn the "pop" explosion.
+	if (actor->info->deathsound)
+		S_StartSound(actor, actor->info->deathsound);
+	P_SpawnMobjFromMobj(actor, 0, 0, actor->height/4, MT_EXPLODE);
+
+	// We're dead now. De-solidify.
+	actor->health = 0;
+	P_UnsetThingPosition(actor);
+	actor->flags &= ~MF_SOLID;
+	actor->flags |= MF_NOCLIP;
+	P_SetThingPosition(actor);
+
+	if (actor->info->damage == MT_UNKNOWN)
+	{
+		// MT_UNKNOWN is random. Because it's unknown to us... get it?
+		item = P_DoRandomBoxChances();
+
+		if (item == MT_NULL)
+		{
+			CONS_Alert(CONS_WARNING, M_GetText("All monitors turned off.\n"));
+			return;
+		}
+	}
+	else
+		item = actor->info->damage;
+
+	if (item == 0)
+	{
+		CONS_Debug(DBG_GAMELOGIC, "Powerup item not defined in 'damage' field for A_MonitorPop\n");
+		return;
+	}
+
+	newmobj = P_SpawnMobjFromMobj(actor, 0, 0, 13*FRACUNIT, item);
+	P_SetTarget(&newmobj->target, actor->target); // Transfer target
+
+	if (item == MT_1UP_ICON)
+	{
+		if (actor->tracer) // Remove the old lives icon.
+			P_RemoveMobj(actor->tracer);
+
+		if (!newmobj->target
+		 || !newmobj->target->player
+		 || !newmobj->target->skin
+		 || ((skin_t *)newmobj->target->skin)->sprites[SPR2_LIFE].numframes == 0)
+			{} // No lives icon for this player, use the default.
+		else
+		{ // Spawn the lives icon.
+			mobj_t *livesico = P_SpawnMobjFromMobj(newmobj, 0, 0, 0, MT_OVERLAY);
+			P_SetTarget(&livesico->target, newmobj);
+			P_SetTarget(&newmobj->tracer, livesico);
+
+			livesico->color = newmobj->target->player->mo->color;
+			livesico->skin = &skins[newmobj->target->player->skin];
+			P_SetMobjState(livesico, newmobj->info->seestate);
+
+			// We're using the overlay, so use the overlay 1up sprite (no text)
+			newmobj->sprite = SPR_TV1P;
+		}
+	}
+}
+
+// Function: A_GoldMonitorPop
+//
+// Description: Used by repeating monitors when they turn off. They don't really pop, but, you know...
+//
+// var1 = unused
+// var2 = unused
+//
+void A_GoldMonitorPop(mobj_t *actor)
+{
+	mobjtype_t item = 0;
+	mobj_t *newmobj;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_GoldMonitorPop", actor))
+		return;
+#endif
+
+	// Don't spawn the "pop" explosion, because the monitor isn't broken.
+	if (actor->info->deathsound)
+		S_StartSound(actor, actor->info->deathsound);
+	//P_SpawnMobjFromMobj(actor, 0, 0, actor.height/4, MT_EXPLODE);
+
+	// Remove our flags for a bit.
+	// Players can now stand on top of us.
+	P_UnsetThingPosition(actor);
+	actor->flags  &= ~(MF_MONITOR|MF_SHOOTABLE);
+	P_SetThingPosition(actor);
+
+	// Don't count this box in statistics. Sorry.
+	if (actor->target && actor->target->player)
+		--actor->target->player->numboxes;
+	actor->fuse = 0; // Don't let the monitor code screw us up.
+
+	if (actor->info->damage == MT_UNKNOWN)
+	{
+		// MT_UNKNOWN is random. Because it's unknown to us... get it?
+		item = P_DoRandomBoxChances();
+
+		if (item == MT_NULL)
+		{
+			CONS_Alert(CONS_WARNING, M_GetText("All monitors turned off.\n"));
+			return;
+		}
+	}
+	else
+		item = actor->info->damage;
+
+	if (item == 0)
+	{
+		CONS_Debug(DBG_GAMELOGIC, "Powerup item not defined in 'damage' field for A_GoldMonitorPop\n");
+		return;
+	}
+
+	// Note: the icon spawns 1 fracunit higher
+	newmobj = P_SpawnMobjFromMobj(actor, 0, 0, 14*FRACUNIT, item);
+	P_SetTarget(&newmobj->target, actor->target); // Transfer target
+
+	if (item == MT_1UP_ICON)
+	{
+		if (actor->tracer) // Remove the old lives icon.
+			P_RemoveMobj(actor->tracer);
+
+		if (!newmobj->target
+		 || !newmobj->target->player
+		 || !newmobj->target->skin
+		 || ((skin_t *)newmobj->target->skin)->sprites[SPR2_LIFE].numframes == 0)
+			{} // No lives icon for this player, use the default.
+		else
+		{ // Spawn the lives icon.
+			mobj_t *livesico = P_SpawnMobjFromMobj(newmobj, 0, 0, 0, MT_OVERLAY);
+			P_SetTarget(&livesico->target, newmobj);
+			P_SetTarget(&newmobj->tracer, livesico);
+
+			livesico->color = newmobj->target->player->mo->color;
+			livesico->skin = &skins[newmobj->target->player->skin];
+			P_SetMobjState(livesico, newmobj->info->seestate);
+
+			// We're using the overlay, so use the overlay 1up sprite (no text)
+			newmobj->sprite = SPR_TV1P;
+		}
+	}
+}
+
+// Function: A_GoldMonitorRestore
+//
+// Description: A repeating monitor is coming back to life. Reset monitor flags, etc.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_GoldMonitorRestore(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_GoldMonitorRestore", actor))
+		return;
+#endif
+
+	actor->flags |= MF_MONITOR|MF_SHOOTABLE;
+	actor->health = 1; // Just in case.
+}
+
+// Function: A_GoldMonitorSparkle
+//
+// Description: Spawns the little sparkly effect around big monitors. Looks pretty, doesn't it?
+//
+// var1 = unused
+// var2 = unused
+//
+void A_GoldMonitorSparkle(mobj_t *actor)
+{
+	fixed_t i, ngangle, xofs, yofs;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_GoldMonitorSparkle", actor))
+		return;
+#endif
+
+	ngangle = FixedAngle(((leveltime * 21) % 360) << FRACBITS);
+	xofs = FINESINE((ngangle>>ANGLETOFINESHIFT) & FINEMASK)   * (actor->radius>>FRACBITS);
+	yofs = FINECOSINE((ngangle>>ANGLETOFINESHIFT) & FINEMASK) * (actor->radius>>FRACBITS);
+
+	for (i = FRACUNIT*2; i <= FRACUNIT*3; i += FRACUNIT/2)
+		P_SetObjectMomZ(P_SpawnMobjFromMobj(actor, xofs, yofs, 0, MT_BOXSPARKLE), i, false);
+}
+
+// Function: A_Explode
+//
+// Description: Explodes an object, doing damage to any objects nearby. The target is used as the cause of the explosion. Damage value is used as explosion range.
+//
+// var1 = damagetype
+// var2 = unused
+//
+void A_Explode(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Explode", actor))
+		return;
+#endif
+	P_RadiusAttack(actor, actor->target, actor->info->damage, locvar1);
+}
+
+// Function: A_BossDeath
+//
+// Description: Possibly trigger special effects when boss dies.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_BossDeath(mobj_t *mo)
+{
+	thinker_t *th;
+	mobj_t *mo2;
+	line_t junk;
+	INT32 i;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_BossDeath", mo))
+		return;
+#endif
+
+	P_LinedefExecute(LE_BOSSDEAD, mo, NULL);
+	mo->health = 0;
+
+	// Boss is dead (but not necessarily fleeing...)
+	// Lua may use this to ignore bosses after they start fleeing
+	mo->flags2 |= MF2_BOSSDEAD;
+
+	// make sure there is a player alive for victory
+	for (i = 0; i < MAXPLAYERS; i++)
+		if (playeringame[i] && ((players[i].mo && players[i].mo->health)
+			|| ((netgame || multiplayer) && (players[i].lives || players[i].continues))))
+			break;
+
+	if (i == MAXPLAYERS)
+		return; // no one left alive, so do not end game
+
+	// scan the remaining thinkers to see
+	// if all bosses are dead
+	for (th = thinkercap.next; th != &thinkercap; th = th->next)
+	{
+		if (th->function.acp1 != (actionf_p1)P_MobjThinker)
+			continue;
+
+		mo2 = (mobj_t *)th;
+		if (mo2 != mo && (mo2->flags & MF_BOSS) && mo2->health > 0)
+			goto bossjustdie; // other boss not dead - just go straight to dying!
+	}
+
+	// victory!
+	P_LinedefExecute(LE_ALLBOSSESDEAD, mo, NULL);
+	if (mo->flags2 & MF2_BOSSNOTRAP)
+	{
+		for (i = 0; i < MAXPLAYERS; i++)
+			P_DoPlayerExit(&players[i]);
+	}
+	else
+	{
+		// Bring the egg trap up to the surface
+		junk.tag = 680;
+		EV_DoElevator(&junk, elevateHighest, false);
+		junk.tag = 681;
+		EV_DoElevator(&junk, elevateUp, false);
+		junk.tag = 682;
+		EV_DoElevator(&junk, elevateHighest, false);
+	}
+
+bossjustdie:
+#ifdef HAVE_BLUA
+	if (LUAh_BossDeath(mo))
+		return;
+	else if (P_MobjWasRemoved(mo))
+		return;
+#endif
+	if (mo->type == MT_BLACKEGGMAN || mo->type == MT_CYBRAKDEMON)
+	{
+		mo->flags |= MF_NOCLIP;
+		mo->flags &= ~MF_SPECIAL;
+
+		S_StartSound(NULL, sfx_befall);
+	}
+	else if (mo->type == MT_KOOPA)
+	{
+		junk.tag = 650;
+		EV_DoCeiling(&junk, raiseToHighest);
+		return;
+	}
+	else // eggmobiles
+	{
+		// Stop exploding and prepare to run.
+		P_SetMobjState(mo, mo->info->xdeathstate);
+		if (P_MobjWasRemoved(mo))
+			return;
+
+		P_SetTarget(&mo->target, NULL);
+
+		// Flee! Flee! Find a point to escape to! If none, just shoot upward!
+		// scan the thinkers to find the runaway point
+		for (th = thinkercap.next; th != &thinkercap; th = th->next)
+		{
+			if (th->function.acp1 != (actionf_p1)P_MobjThinker)
+				continue;
+
+			mo2 = (mobj_t *)th;
+
+			if (mo2->type == MT_BOSSFLYPOINT)
+			{
+				// If this one's closer then the last one, go for it.
+				if (!mo->target ||
+					P_AproxDistance(P_AproxDistance(mo->x - mo2->x, mo->y - mo2->y), mo->z - mo2->z) <
+					P_AproxDistance(P_AproxDistance(mo->x - mo->target->x, mo->y - mo->target->y), mo->z - mo->target->z))
+						P_SetTarget(&mo->target, mo2);
+				// Otherwise... Don't!
+			}
+		}
+
+		mo->flags |= MF_NOGRAVITY|MF_NOCLIP;
+		mo->flags |= MF_NOCLIPHEIGHT;
+
+		if (mo->target)
+		{
+			mo->angle = R_PointToAngle2(mo->x, mo->y, mo->target->x, mo->target->y);
+			mo->flags2 |= MF2_BOSSFLEE;
+			mo->momz = FixedMul(FixedDiv(mo->target->z - mo->z, P_AproxDistance(mo->x-mo->target->x,mo->y-mo->target->y)), FixedMul(2*FRACUNIT, mo->scale));
+		}
+		else
+			mo->momz = FixedMul(2*FRACUNIT, mo->scale);
+	}
+
+	if (mo->type == MT_EGGMOBILE2)
+	{
+		mo2 = P_SpawnMobj(mo->x + P_ReturnThrustX(mo, mo->angle - ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)),
+			mo->y + P_ReturnThrustY(mo, mo->angle - ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)),
+			mo->z + mo->height/2 + ((mo->eflags & MFE_VERTICALFLIP)? FixedMul(8*FRACUNIT, mo->scale)-mobjinfo[MT_BOSSTANK1].height : -FixedMul(8*FRACUNIT, mo->scale)), MT_BOSSTANK1); // Right tank
+		mo2->angle = mo->angle;
+		mo2->destscale = mo->scale;
+		P_SetScale(mo2, mo2->destscale);
+		if (mo->eflags & MFE_VERTICALFLIP)
+		{
+			mo2->eflags |= MFE_VERTICALFLIP;
+			mo2->flags2 |= MF2_OBJECTFLIP;
+		}
+		P_InstaThrust(mo2, mo2->angle - ANGLE_90, FixedMul(4*FRACUNIT, mo2->scale));
+		P_SetObjectMomZ(mo2, 4*FRACUNIT, false);
+
+		mo2 = P_SpawnMobj(mo->x + P_ReturnThrustX(mo, mo->angle + ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)),
+			mo->y + P_ReturnThrustY(mo, mo->angle + ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)),
+			mo->z + mo->height/2 + ((mo->eflags & MFE_VERTICALFLIP)? FixedMul(8*FRACUNIT, mo->scale)-mobjinfo[MT_BOSSTANK2].height : -FixedMul(8*FRACUNIT, mo->scale)), MT_BOSSTANK2); // Left tank
+		mo2->angle = mo->angle;
+		mo2->destscale = mo->scale;
+		P_SetScale(mo2, mo2->destscale);
+		if (mo->eflags & MFE_VERTICALFLIP)
+		{
+			mo2->eflags |= MFE_VERTICALFLIP;
+			mo2->flags2 |= MF2_OBJECTFLIP;
+		}
+		P_InstaThrust(mo2, mo2->angle + ANGLE_90, FixedMul(4*FRACUNIT, mo2->scale));
+		P_SetObjectMomZ(mo2, 4*FRACUNIT, false);
+
+		mo2 = P_SpawnMobj(mo->x, mo->y,
+			mo->z + ((mo->eflags & MFE_VERTICALFLIP)? mobjinfo[MT_BOSSSPIGOT].height-FixedMul(32*FRACUNIT,mo->scale): mo->height + FixedMul(32*FRACUNIT, mo->scale)), MT_BOSSSPIGOT);
+		mo2->angle = mo->angle;
+		mo2->destscale = mo->scale;
+		P_SetScale(mo2, mo2->destscale);
+		if (mo->eflags & MFE_VERTICALFLIP)
+		{
+			mo2->eflags |= MFE_VERTICALFLIP;
+			mo2->flags2 |= MF2_OBJECTFLIP;
+		}
+		P_SetObjectMomZ(mo2, 4*FRACUNIT, false);
+		return;
+	}
+}
+
+// Function: A_CustomPower
+//
+// Description: Provides a custom powerup. Target (must be a player) is awarded the powerup. Reactiontime of the object is used as an index to the powers array.
+//
+// var1 = Power index #
+// var2 = Power duration in tics
+//
+void A_CustomPower(mobj_t *actor)
+{
+	player_t *player;
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	boolean spawnshield = false;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_CustomPower", actor))
+		return;
+#endif
+	if (!actor->target || !actor->target->player)
+	{
+		CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
+		return;
+	}
+
+	if (locvar1 >= NUMPOWERS)
+	{
+		CONS_Debug(DBG_GAMELOGIC, "Power #%d out of range!\n", locvar1);
+		return;
+	}
+
+	player = actor->target->player;
+
+	if (locvar1 == pw_shield && player->powers[pw_shield] != locvar2)
+		spawnshield = true;
+
+	player->powers[locvar1] = (UINT16)locvar2;
+	if (actor->info->seesound)
+		S_StartSound(player->mo, actor->info->seesound);
+
+	if (spawnshield) //workaround for a bug
+		P_SpawnShieldOrb(player);
+}
+
+// Function: A_GiveWeapon
+//
+// Description: Gives the player the specified weapon panels.
+//
+// var1 = Weapon index #
+// var2 = unused
+//
+void A_GiveWeapon(mobj_t *actor)
+{
+	player_t *player;
+	INT32 locvar1 = var1;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_GiveWeapon", actor))
+		return;
+#endif
+	if (!actor->target || !actor->target->player)
+	{
+		CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
+		return;
+	}
+
+	if (locvar1 >= 1<<(NUM_WEAPONS-1))
+	{
+		CONS_Debug(DBG_GAMELOGIC, "Weapon #%d out of range!\n", locvar1);
+		return;
+	}
+
+	player = actor->target->player;
+
+	player->ringweapons |= locvar1;
+	if (actor->info->seesound)
+		S_StartSound(player->mo, actor->info->seesound);
+}
+
+// Function: A_RingBox
+//
+// Description: Awards the player 10 rings.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_RingBox(mobj_t *actor)
+{
+	player_t *player;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_RingBox", actor))
+		return;
+#endif
+	if (!actor->target || !actor->target->player)
+	{
+		CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
+		return;
+	}
+
+	player = actor->target->player;
+
+	P_GivePlayerRings(player, actor->info->reactiontime);
+	if (actor->info->seesound)
+		S_StartSound(player->mo, actor->info->seesound);
+}
+
+// Function: A_Invincibility
+//
+// Description: Awards the player invincibility.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_Invincibility(mobj_t *actor)
+{
+	player_t *player;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Invincibility", actor))
+		return;
+#endif
+	if (!actor->target || !actor->target->player)
+	{
+		CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
+		return;
+	}
+
+	player = actor->target->player;
+	player->powers[pw_invulnerability] = invulntics + 1;
+
+	if (P_IsLocalPlayer(player) && !player->powers[pw_super])
+	{
+		S_StopMusic();
+		if (mariomode)
+			G_GhostAddColor(GHC_INVINCIBLE);
+		strlcpy(S_sfx[sfx_None].caption, "Invincibility", 14);
+		S_StartCaption(sfx_None, -1, player->powers[pw_invulnerability]);
+		S_ChangeMusicInternal((mariomode) ? "_minv" : "_inv", false);
+	}
+}
+
+// Function: A_SuperSneakers
+//
+// Description: Awards the player super sneakers.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_SuperSneakers(mobj_t *actor)
+{
+	player_t *player;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SuperSneakers", actor))
+		return;
+#endif
+	if (!actor->target || !actor->target->player)
+	{
+		CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
+		return;
+	}
+
+	player = actor->target->player;
+
+	actor->target->player->powers[pw_sneakers] = sneakertics + 1;
+
+	if (P_IsLocalPlayer(player) && !player->powers[pw_super])
+	{
+		if (S_SpeedMusic(0.0f) && (mapheaderinfo[gamemap-1]->levelflags & LF_SPEEDMUSIC))
+			S_SpeedMusic(1.4f);
+		else
+		{
+			S_StopMusic();
+			S_ChangeMusicInternal("_shoes", false);
+		}
+		strlcpy(S_sfx[sfx_None].caption, "Speed shoes", 12);
+		S_StartCaption(sfx_None, -1, player->powers[pw_sneakers]);
+	}
+}
+
+// Function: A_AwardScore
+//
+// Description: Adds a set amount of points to the player's score.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_AwardScore(mobj_t *actor)
+{
+	player_t *player;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_AwardScore", actor))
+		return;
+#endif
+	if (!actor->target || !actor->target->player)
+	{
+		CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
+		return;
+	}
+
+	player = actor->target->player;
+
+	P_AddPlayerScore(player, actor->info->reactiontime);
+	if (actor->info->seesound)
+		S_StartSound(player->mo, actor->info->seesound);
+}
+
+// Function: A_ExtraLife
+//
+// Description: Awards the player an extra life.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_ExtraLife(mobj_t *actor)
+{
+	player_t *player;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_ExtraLife", actor))
+		return;
+#endif
+	if (!actor->target || !actor->target->player)
+	{
+		CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
+		return;
+	}
+
+	player = actor->target->player;
+
+	if (actor->type == MT_1UP_ICON && actor->tracer)
+	{
+		// We're using the overlay, so use the overlay 1up sprite (no text)
+		actor->sprite = SPR_TV1P;
+	}
+
+	if (ultimatemode) //I don't THINK so!
+	{
+		S_StartSound(player->mo, sfx_lose);
+		return;
+	}
+
+	P_GiveCoopLives(player, 1, true);
+}
+
+// Function: A_GiveShield
+//
+// Description: Awards the player a specified shield.
+//
+// var1 = Shield type (make with SH_ constants)
+// var2 = unused
+//
+void A_GiveShield(mobj_t *actor)
+{
+	player_t *player;
+	UINT16 locvar1 = var1;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_GiveShield", actor))
+		return;
+#endif
+	if (!actor->target || !actor->target->player)
+	{
+		CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
+		return;
+	}
+
+	player = actor->target->player;
+
+	P_SwitchShield(player, locvar1);
+	S_StartSound(player->mo, actor->info->seesound);
+}
+
+// Function: A_GravityBox
+//
+// Description: Awards the player gravity boots.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_GravityBox(mobj_t *actor)
+{
+	player_t *player;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_GravityBox", actor))
+		return;
+#endif
+	if (!actor->target || !actor->target->player)
+	{
+		CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
+		return;
+	}
+
+	player = actor->target->player;
+
+	S_StartSound(player, actor->info->activesound);
+
+	player->powers[pw_gravityboots] = (UINT16)(actor->info->reactiontime + 1);
+}
+
+// Function: A_ScoreRise
+//
+// Description: Makes the little score logos rise. Speed value sets speed.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_ScoreRise(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_ScoreRise", actor))
+		return;
+#endif
+	// make logo rise!
+	P_SetObjectMomZ(actor, actor->info->speed, false);
+}
+
+// Function: A_BunnyHop
+//
+// Description: Makes object hop like a bunny.
+//
+// var1 = jump strength
+// var2 = horizontal movement
+//
+void A_BunnyHop(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_BunnyHop", actor))
+		return;
+#endif
+	if (((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)
+		|| (!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz))
+	{
+		P_SetObjectMomZ(actor, locvar1*FRACUNIT, false);
+		P_InstaThrust(actor, actor->angle, FixedMul(locvar2*FRACUNIT, actor->scale)); // Launch the hopping action! PHOOM!!
+	}
+}
+
+// Function: A_BubbleSpawn
+//
+// Description: Spawns a randomly sized bubble from the object's location. Only works underwater.
+//
+// var1 = Distance to look for players.  If no player is in this distance, bubbles aren't spawned. (Ambush overrides)
+// var2 = unused
+//
+void A_BubbleSpawn(mobj_t *actor)
+{
+	INT32 i, locvar1 = var1;
+	UINT8 prandom;
+	mobj_t *bubble = NULL;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_BubbleSpawn", actor))
+		return;
+#endif
+	if (!(actor->eflags & MFE_UNDERWATER))
+	{
+		// Don't draw or spawn bubbles above water
+		actor->flags2 |= MF2_DONTDRAW;
+		return;
+	}
+	actor->flags2 &= ~MF2_DONTDRAW;
+
+	if (!(actor->flags2 & MF2_AMBUSH))
+	{
+		// Quick! Look through players!
+		// Don't spawn bubbles unless a player is relatively close by (var1).
+		for (i = 0; i < MAXPLAYERS; ++i)
+			if (playeringame[i] && players[i].mo
+			 && P_AproxDistance(actor->x - players[i].mo->x, actor->y - players[i].mo->y) < (locvar1<<FRACBITS))
+				break; // Stop looking.
+		if (i == MAXPLAYERS)
+			return; // don't make bubble!
+	}
+
+	prandom = P_RandomByte();
+
+	if (leveltime % (3*TICRATE) < 8)
+		bubble = P_SpawnMobj(actor->x, actor->y, actor->z + (actor->height / 2), MT_EXTRALARGEBUBBLE);
+	else if (prandom > 128)
+		bubble = P_SpawnMobj(actor->x, actor->y, actor->z + (actor->height / 2), MT_SMALLBUBBLE);
+	else if (prandom < 128 && prandom > 96)
+		bubble = P_SpawnMobj(actor->x, actor->y, actor->z + (actor->height / 2), MT_MEDIUMBUBBLE);
+
+	if (bubble)
+	{
+		bubble->destscale = actor->scale;
+		P_SetScale(bubble, actor->scale);
+	}
+}
+
+// Function: A_FanBubbleSpawn
+//
+// Description: Spawns bubbles from fans, if they're underwater.
+//
+// var1 = Distance to look for players.  If no player is in this distance, bubbles aren't spawned. (Ambush overrides)
+// var2 = unused
+//
+void A_FanBubbleSpawn(mobj_t *actor)
+{
+	INT32 i, locvar1 = var1;
+	UINT8 prandom;
+	mobj_t *bubble = NULL;
+	fixed_t hz = actor->z + (4*actor->height)/5;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_FanBubbleSpawn", actor))
+		return;
+#endif
+	if (!(actor->eflags & MFE_UNDERWATER))
+		return;
+
+	if (!(actor->flags2 & MF2_AMBUSH))
+	{
+	// Quick! Look through players!
+	// Don't spawn bubbles unless a player is relatively close by (var2).
+		for (i = 0; i < MAXPLAYERS; ++i)
+			if (playeringame[i] && players[i].mo
+			 && P_AproxDistance(actor->x - players[i].mo->x, actor->y - players[i].mo->y) < (locvar1<<FRACBITS))
+				break; // Stop looking.
+		if (i == MAXPLAYERS)
+			return; // don't make bubble!
+	}
+
+	prandom = P_RandomByte();
+
+	if ((prandom & 0x7) == 0x7)
+		bubble = P_SpawnMobj(actor->x, actor->y, hz, MT_SMALLBUBBLE);
+	else if ((prandom & 0xF0) == 0xF0)
+		bubble = P_SpawnMobj(actor->x, actor->y, hz, MT_MEDIUMBUBBLE);
+
+	if (bubble)
+	{
+		bubble->destscale = actor->scale;
+		P_SetScale(bubble, actor->scale);
+	}
+}
+
+// Function: A_BubbleRise
+//
+// Description: Raises a bubble
+//
+// var1:
+//		0 = Bend around the water abit, looking more realistic
+//		1 = Rise straight up
+// var2 = rising speed
+//
+void A_BubbleRise(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_BubbleRise", actor))
+		return;
+#endif
+	if (actor->type == MT_EXTRALARGEBUBBLE)
+		P_SetObjectMomZ(actor, FixedDiv(6*FRACUNIT,5*FRACUNIT), false); // make bubbles rise!
+	else
+	{
+		P_SetObjectMomZ(actor, locvar2, true); // make bubbles rise!
+
+		// Move around slightly to make it look like it's bending around the water
+		if (!locvar1)
+		{
+			UINT8 prandom = P_RandomByte();
+			if (!(prandom & 0x7)) // *****000
+			{
+				P_InstaThrust(actor, prandom & 0x70 ? actor->angle + ANGLE_90 : actor->angle,
+					FixedMul(prandom & 0xF0 ? FRACUNIT/2 : -FRACUNIT/2, actor->scale));
+			}
+			else if (!(prandom & 0x38)) // **000***
+			{
+				P_InstaThrust(actor, prandom & 0x70 ? actor->angle - ANGLE_90 : actor->angle - ANGLE_180,
+					FixedMul(prandom & 0xF0 ? FRACUNIT/2 : -FRACUNIT/2, actor->scale));
+			}
+		}
+	}
+}
+
+// Function: A_BubbleCheck
+//
+// Description: Checks if a bubble should be drawn or not. Bubbles are not drawn above water.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_BubbleCheck(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_BubbleCheck", actor))
+		return;
+#endif
+	if (actor->eflags & MFE_UNDERWATER)
+		actor->flags2 &= ~MF2_DONTDRAW; // underwater so draw
+	else
+		actor->flags2 |= MF2_DONTDRAW; // above water so don't draw
+}
+
+// Function: A_AttractChase
+//
+// Description: Makes a ring chase after a player with a ring shield and also causes spilled rings to flicker.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_AttractChase(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_AttractChase", actor))
+		return;
+#endif
+	if (actor->flags2 & MF2_NIGHTSPULL || !actor->health)
+		return;
+
+	// spilled rings flicker before disappearing
+	if (leveltime & 1 && actor->type == (mobjtype_t)actor->info->reactiontime && actor->fuse && actor->fuse < 2*TICRATE)
+		actor->flags2 |= MF2_DONTDRAW;
+	else
+		actor->flags2 &= ~MF2_DONTDRAW;
+
+	// Turn flingrings back into regular rings if attracted.
+	if (actor->tracer && actor->tracer->player
+		&& !(actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC) && actor->info->reactiontime && actor->type != (mobjtype_t)actor->info->reactiontime)
+	{
+		mobj_t *newring;
+		newring = P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->reactiontime);
+		newring->momx = actor->momx;
+		newring->momy = actor->momy;
+		newring->momz = actor->momz;
+		P_RemoveMobj(actor);
+		return;
+	}
+
+	P_LookForShield(actor); // Go find 'em, boy!
+
+	if (!actor->tracer
+		|| !actor->tracer->player
+		|| !actor->tracer->health
+		|| !P_CheckSight(actor, actor->tracer)) // You have to be able to SEE it...sorta
+	{
+		// Lost attracted rings don't through walls anymore.
+		actor->flags &= ~MF_NOCLIP;
+		P_SetTarget(&actor->tracer, NULL);
+		return;
+	}
+
+	// If a FlingRing gets attracted by a shield, change it into a normal ring.
+	if (actor->type == (mobjtype_t)actor->info->reactiontime)
+	{
+		P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->painchance);
+		P_RemoveMobj(actor);
+		return;
+	}
+
+	// Keep stuff from going down inside floors and junk
+	actor->flags &= ~MF_NOCLIPHEIGHT;
+
+	// Let attracted rings move through walls and such.
+	actor->flags |= MF_NOCLIP;
+
+	P_Attract(actor, actor->tracer, false);
+}
+
+// Function: A_DropMine
+//
+// Description: Drops a mine. Raisestate specifies the object # to use for the mine.
+//
+// var1 = height offset
+// var2:
+//		lower 16 bits = proximity check distance (0 disables)
+//		upper 16 bits = 0 to check proximity with target, 1 for tracer
+//
+void A_DropMine(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	fixed_t z;
+	mobj_t *mine;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_DropMine", actor))
+		return;
+#endif
+
+	if (locvar2 & 65535)
+	{
+		fixed_t dist;
+		mobj_t *target;
+
+		if (locvar2 >> 16)
+			target = actor->tracer;
+		else
+			target = actor->target;
+
+		if (!target)
+			return;
+
+		dist = P_AproxDistance(actor->x-target->x, actor->y-target->y)>>FRACBITS;
+
+		if (dist > FixedMul((locvar2 & 65535), actor->scale))
+			return;
+	}
+
+	if (actor->eflags & MFE_VERTICALFLIP)
+		z = actor->z + actor->height - mobjinfo[actor->info->raisestate].height - FixedMul((locvar1*FRACUNIT) - 12*FRACUNIT, actor->scale);
+	else
+		z = actor->z + FixedMul((locvar1*FRACUNIT) - 12*FRACUNIT, actor->scale);
+
+	// Use raisestate instead of MT_MINE
+	mine = P_SpawnMobj(actor->x, actor->y, z, (mobjtype_t)actor->info->raisestate);
+	if (actor->eflags & MFE_VERTICALFLIP)
+		mine->eflags |= MFE_VERTICALFLIP;
+	mine->momz = actor->momz + actor->pmomz;
+
+	S_StartSound(actor, actor->info->attacksound);
+}
+
+// Function: A_FishJump
+//
+// Description: Makes the stupid harmless fish in Greenflower Zone jump.
+//
+// var1 = Jump strength (in FRACBITS), if specified. Otherwise, uses the angle value.
+// var2 = unused
+//
+void A_FishJump(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_FishJump", actor))
+		return;
+#endif
+
+	if (locvar2)
+	{
+		fixed_t rad = actor->radius>>FRACBITS;
+		P_SpawnMobjFromMobj(actor, P_RandomRange(rad, -rad)<<FRACBITS, P_RandomRange(rad, -rad)<<FRACBITS, 0, (mobjtype_t)locvar2);
+	}
+
+	if ((actor->z <= actor->floorz) || (actor->z <= actor->watertop - FixedMul((64 << FRACBITS), actor->scale)))
+	{
+		fixed_t jumpval;
+
+		if (locvar1)
+			jumpval = var1;
+		else
+			jumpval = FixedMul(AngleFixed(actor->angle)/4, actor->scale);
+
+		if (!jumpval) jumpval = FixedMul(44*(FRACUNIT/4), actor->scale);
+		actor->momz = jumpval;
+		P_SetMobjStateNF(actor, actor->info->seestate);
+	}
+
+	if (actor->momz < 0
+		&& (actor->state < &states[actor->info->meleestate] || actor->state > &states[actor->info->xdeathstate]))
+		P_SetMobjStateNF(actor, actor->info->meleestate);
+}
+
+// Function:A_ThrownRing
+//
+// Description: Thinker for thrown rings/sparkle trail
+//
+// var1 = unused
+// var2 = unused
+//
+void A_ThrownRing(mobj_t *actor)
+{
+	INT32 c = 0;
+	INT32 stop;
+	player_t *player;
+	fixed_t dist;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_ThrownRing", actor))
+		return;
+#endif
+
+	if (leveltime % (TICRATE/7) == 0)
+	{
+		mobj_t *ring = NULL;
+
+		if (actor->flags2 & MF2_EXPLOSION)
+		{
+			if (actor->momx != 0 || actor->momy != 0)
+				ring = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMOKE);
+			// Else spawn nothing because it's totally stationary and constantly smoking would be weird -SH
+		}
+		else if (actor->flags2 & MF2_AUTOMATIC)
+			ring = P_SpawnGhostMobj(actor);
+		else if (!(actor->flags2 & MF2_RAILRING))
+			ring = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SPARK);
+
+		if (ring)
+		{
+			/*
+			P_SetTarget(&ring->target, actor);
+			ring->color = actor->color; //copy color
+			*/
+			ring->destscale = actor->scale;
+			P_SetScale(ring, actor->scale);
+		}
+	}
+
+	// A_GrenadeRing beeping lives once moooooore -SH
+	if (actor->type == MT_THROWNGRENADE && actor->fuse % TICRATE == 0)
+		S_StartSound(actor, actor->info->attacksound);
+
+	// decrement bounce ring time
+	if (actor->flags2 & MF2_BOUNCERING)
+	{
+		if (actor->fuse)
+			actor->fuse--;
+		else {
+			P_RemoveMobj(actor);
+			return;
+		}
+	}
+
+	// spilled rings (and thrown bounce) flicker before disappearing
+	if (leveltime & 1 && actor->fuse > 0 && actor->fuse < 2*TICRATE
+		&& actor->type != MT_THROWNGRENADE)
+		actor->flags2 |= MF2_DONTDRAW;
+	else
+		actor->flags2 &= ~MF2_DONTDRAW;
+
+	if (actor->tracer && actor->tracer->health <= 0)
+		P_SetTarget(&actor->tracer, NULL);
+
+	// Updated homing ring special capability
+	// If you have a ring shield, all rings thrown
+	// at you become homing (except rail)!
+	if (actor->tracer)
+	{
+		// A non-homing ring getting attracted by a
+		// magnetic player. If he gets too far away, make
+		// sure to stop the attraction!
+		if ((!actor->tracer->health) || (actor->tracer->player && (actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC)
+		    && P_AproxDistance(P_AproxDistance(actor->tracer->x-actor->x,
+		    actor->tracer->y-actor->y), actor->tracer->z-actor->z) > FixedMul(RING_DIST/4, actor->tracer->scale)))
+		{
+			P_SetTarget(&actor->tracer, NULL);
+		}
+
+		if (actor->tracer && (actor->tracer->health)
+			&& (actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC))// Already found someone to follow.
+		{
+			const INT32 temp = actor->threshold;
+			actor->threshold = 32000;
+			P_HomingAttack(actor, actor->tracer);
+			actor->threshold = temp;
+			return;
+		}
+	}
+
+	// first time init, this allow minimum lastlook changes
+	if (actor->lastlook < 0)
+		actor->lastlook = P_RandomByte();
+
+	actor->lastlook %= MAXPLAYERS;
+
+	stop = (actor->lastlook - 1) & PLAYERSMASK;
+
+	for (; ; actor->lastlook = (actor->lastlook + 1) & PLAYERSMASK)
+	{
+		// done looking
+		if (actor->lastlook == stop)
+			return;
+
+		if (!playeringame[actor->lastlook])
+			continue;
+
+		if (c++ == 2)
+			return;
+
+		player = &players[actor->lastlook];
+
+		if (!player->mo)
+			continue;
+
+		if (player->mo->health <= 0)
+			continue; // dead
+
+		if ((netgame || multiplayer) && player->spectator)
+			continue; // spectator
+
+		if (actor->target && actor->target->player)
+		{
+			if (player->mo == actor->target)
+				continue;
+
+			// Don't home in on teammates.
+			if (gametype == GT_CTF
+				&& actor->target->player->ctfteam == player->ctfteam)
+				continue;
+		}
+
+		dist = P_AproxDistance(P_AproxDistance(player->mo->x-actor->x,
+			player->mo->y-actor->y), player->mo->z-actor->z);
+
+		// check distance
+		if (actor->flags2 & MF2_RAILRING)
+		{
+			if (dist > FixedMul(RING_DIST/2, player->mo->scale))
+				continue;
+		}
+		else if (dist > FixedMul(RING_DIST, player->mo->scale))
+			continue;
+
+		// do this after distance check because it's more computationally expensive
+		if (!P_CheckSight(actor, player->mo))
+			continue; // out of sight
+
+		if ((player->powers[pw_shield] & SH_PROTECTELECTRIC)
+			&& dist < FixedMul(RING_DIST/4, player->mo->scale))
+			P_SetTarget(&actor->tracer, player->mo);
+		return;
+	}
+
+	return;
+}
+
+// Function: A_SetSolidSteam
+//
+// Description: Makes steam solid so it collides with the player to boost them.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_SetSolidSteam(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SetSolidSteam", actor))
+		return;
+#endif
+	actor->flags &= ~MF_NOCLIP;
+	actor->flags |= MF_SOLID;
+	if (!(actor->flags2 & MF2_AMBUSH))
+	{
+		if (P_RandomChance(FRACUNIT/8))
+		{
+			if (actor->info->deathsound)
+				S_StartSound(actor, actor->info->deathsound); // Hiss!
+		}
+		else
+		{
+			if (actor->info->painsound)
+				S_StartSound(actor, actor->info->painsound);
+		}
+	}
+
+	P_SetObjectMomZ (actor, 1, true);
+}
+
+// Function: A_UnsetSolidSteam
+//
+// Description: Makes an object non-solid and also noclip. Used by the steam.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_UnsetSolidSteam(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_UnsetSolidSteam", actor))
+		return;
+#endif
+	actor->flags &= ~MF_SOLID;
+	actor->flags |= MF_NOCLIP;
+}
+
+// Function: A_SignPlayer
+//
+// Description: Changes the state of a level end sign to reflect the player that hit it.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_SignPlayer(mobj_t *actor)
+{
+	mobj_t *ov;
+	skin_t *skin;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SignPlayer", actor))
+		return;
+#endif
+	if (!actor->target)
+		return;
+
+	if (!actor->target->player)
+		return;
+
+	skin = &skins[actor->target->player->skin];
+
+	if ((actor->target->player->skincolor == skin->prefcolor) && (skin->prefoppositecolor)) // Set it as the skin's preferred oppositecolor?
+	{
+		actor->color = skin->prefoppositecolor;
+		/*
+		If you're here from the comment above Color_Opposite,
+		the following line is the one which is dependent on the
+		array being symmetrical. It gets the opposite of the
+		opposite of your desired colour just so it can get the
+		brightness frame for the End Sign. It's not a great
+		design choice, but it's constant time array access and
+		the idea that the colours should be OPPOSITES is kind
+		of in the name. If you have a better idea, feel free
+		to let me know. ~toast 2016/07/20
+		*/
+		actor->frame += (15 - Color_Opposite[(Color_Opposite[(skin->prefoppositecolor - 1)*2] - 1)*2 + 1]);
+	}
+	else if (actor->target->player->skincolor) // Set the sign to be an appropriate background color for this player's skincolor.
+	{
+		actor->color = Color_Opposite[(actor->target->player->skincolor - 1)*2];
+		actor->frame += (15 - Color_Opposite[(actor->target->player->skincolor - 1)*2 + 1]);
+	}
+
+	if (skin->sprites[SPR2_SIGN].numframes)
+	{
+		// spawn an overlay of the player's face.
+		ov = P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY);
+		P_SetTarget(&ov->target, actor);
+		ov->color = actor->target->player->skincolor;
+		ov->skin = skin;
+		P_SetMobjState(ov, actor->info->seestate); // S_PLAY_SIGN
+	}
+}
+
+// Function: A_OverlayThink
+//
+// Description: Moves the overlay to the position of its target.
+//
+// var1 = unused
+// var2 = invert, z offset
+//
+void A_OverlayThink(mobj_t *actor)
+{
+	fixed_t destx, desty;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_OverlayThink", actor))
+		return;
+#endif
+	if (!actor->target)
+		return;
+
+	if (!splitscreen && rendermode != render_soft)
+	{
+		angle_t viewingangle;
+
+		if (players[displayplayer].awayviewtics)
+			viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, players[displayplayer].awayviewmobj->x, players[displayplayer].awayviewmobj->y);
+		else if (!camera.chase && players[displayplayer].mo)
+			viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, players[displayplayer].mo->x, players[displayplayer].mo->y);
+		else
+			viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, camera.x, camera.y);
+
+		destx = actor->target->x + P_ReturnThrustX(actor->target, viewingangle, FixedMul(FRACUNIT, actor->scale));
+		desty = actor->target->y + P_ReturnThrustY(actor->target, viewingangle, FixedMul(FRACUNIT, actor->scale));
+	}
+	else
+	{
+		destx = actor->target->x;
+		desty = actor->target->y;
+	}
+	P_UnsetThingPosition(actor);
+	actor->x = destx;
+	actor->y = desty;
+	P_SetThingPosition(actor);
+	if (actor->eflags & MFE_VERTICALFLIP)
+		actor->z = actor->target->z + actor->target->height - mobjinfo[actor->type].height  - ((var2>>16) ? -1 : 1)*(var2&0xFFFF)*FRACUNIT;
+	else
+		actor->z = actor->target->z + ((var2>>16) ? -1 : 1)*(var2&0xFFFF)*FRACUNIT;
+	actor->angle = actor->target->angle;
+	actor->eflags = actor->target->eflags;
+
+	actor->momx = actor->target->momx;
+	actor->momy = actor->target->momy;
+	actor->momz = actor->target->momz; // assume target has correct momz! Do not use P_SetObjectMomZ!
+}
+
+// Function: A_JetChase
+//
+// Description: A_Chase for Jettysyns
+//
+// var1 = unused
+// var2 = unused
+//
+void A_JetChase(mobj_t *actor)
+{
+	fixed_t thefloor;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_JetChase", actor))
+		return;
+#endif
+
+	if (actor->flags2 & MF2_AMBUSH)
+		return;
+
+	if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz
+		&& actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale))
+		thefloor = actor->watertop;
+	else
+		thefloor = actor->floorz;
+
+	if (actor->reactiontime)
+		actor->reactiontime--;
+
+	if (P_RandomChance(FRACUNIT/32))
+	{
+		actor->momx = actor->momx / 2;
+		actor->momy = actor->momy / 2;
+		actor->momz = actor->momz / 2;
+	}
+
+	// Bounce if too close to floor or ceiling -
+	// ideal for Jetty-Syns above you on 3d floors
+	if (actor->momz && ((actor->z - FixedMul((32<<FRACBITS), actor->scale)) < thefloor) && !((thefloor + FixedMul(32*FRACUNIT, actor->scale) + actor->height) > actor->ceilingz))
+		actor->momz = -actor->momz/2;
+
+	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+	{
+		// look for a new target
+		if (P_LookForPlayers(actor, true, false, 0))
+			return; // got a new target
+
+		actor->momx = actor->momy = actor->momz = 0;
+		P_SetMobjState(actor, actor->info->spawnstate);
+		return;
+	}
+
+	// modify target threshold
+	if (actor->threshold)
+	{
+		if (!actor->target || actor->target->health <= 0)
+			actor->threshold = 0;
+		else
+			actor->threshold--;
+	}
+
+	// turn towards movement direction if not there yet
+	actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+
+	if ((multiplayer || netgame) && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target)))
+		if (P_LookForPlayers(actor, true, false, 0))
+			return; // got a new target
+
+	// If the player is over 3072 fracunits away, then look for another player
+	if (P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y),
+		actor->target->z - actor->z) > FixedMul(3072*FRACUNIT, actor->scale) && P_LookForPlayers(actor, true, false, FixedMul(3072*FRACUNIT, actor->scale)))
+	{
+		return; // got a new target
+	}
+
+	// chase towards player
+	if (ultimatemode)
+		P_Thrust(actor, actor->angle, FixedMul(actor->info->speed/2, actor->scale));
+	else
+		P_Thrust(actor, actor->angle, FixedMul(actor->info->speed/4, actor->scale));
+
+	// must adjust height
+	if (ultimatemode)
+	{
+		if (actor->z < (actor->target->z + actor->target->height + FixedMul((64<<FRACBITS), actor->scale)))
+			actor->momz += FixedMul(FRACUNIT/2, actor->scale);
+		else
+			actor->momz -= FixedMul(FRACUNIT/2, actor->scale);
+	}
+	else
+	{
+		if (actor->z < (actor->target->z + actor->target->height + FixedMul((32<<FRACBITS), actor->scale)))
+			actor->momz += FixedMul(FRACUNIT/2, actor->scale);
+		else
+			actor->momz -= FixedMul(FRACUNIT/2, actor->scale);
+	}
+}
+
+// Function: A_JetbThink
+//
+// Description: Thinker for Jetty-Syn bombers
+//
+// var1 = unused
+// var2 = unused
+//
+void A_JetbThink(mobj_t *actor)
+{
+	sector_t *nextsector;
+	fixed_t thefloor;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_JetbThink", actor))
+		return;
+#endif
+
+	if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz
+		&& actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale))
+		thefloor = actor->watertop;
+	else
+		thefloor = actor->floorz;
+
+	if (actor->target)
+	{
+		A_JetChase(actor);
+		// check for melee attack
+		if (actor->info->raisestate
+			&& (actor->z > (actor->floorz + FixedMul((32<<FRACBITS), actor->scale)))
+			&& P_JetbCheckMeleeRange(actor) && !actor->reactiontime
+			&& (actor->target->z >= actor->floorz))
+		{
+			mobj_t *bomb;
+			if (actor->info->attacksound)
+				S_StartAttackSound(actor, actor->info->attacksound);
+
+			// use raisestate instead of MT_MINE
+			bomb = P_SpawnMobj(actor->x, actor->y, actor->z - FixedMul((32<<FRACBITS), actor->scale), (mobjtype_t)actor->info->raisestate);
+
+			P_SetTarget(&bomb->target, actor);
+			bomb->destscale = actor->scale;
+			P_SetScale(bomb, actor->scale);
+			actor->reactiontime = TICRATE; // one second
+			S_StartSound(actor, actor->info->attacksound);
+		}
+	}
+	else if (((actor->z - FixedMul((32<<FRACBITS), actor->scale)) < thefloor) && !((thefloor + FixedMul((32<<FRACBITS), actor->scale) + actor->height) > actor->ceilingz))
+			actor->z = thefloor+FixedMul((32<<FRACBITS), actor->scale);
+
+	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+	{
+		// look for a new target
+		if (P_LookForPlayers(actor, true, false, 0))
+			return; // got a new target
+
+		P_SetMobjState(actor, actor->info->spawnstate);
+		return;
+	}
+
+	nextsector = R_PointInSubsector(actor->x + actor->momx, actor->y + actor->momy)->sector;
+
+	// Move downwards or upwards to go through a passageway.
+	if (nextsector->ceilingheight < actor->z + actor->height)
+		actor->momz -= FixedMul(5*FRACUNIT, actor->scale);
+	else if (nextsector->floorheight > actor->z)
+		actor->momz += FixedMul(5*FRACUNIT, actor->scale);
+}
+
+// Function: A_JetgShoot
+//
+// Description: Firing function for Jetty-Syn gunners.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_JetgShoot(mobj_t *actor)
+{
+	fixed_t dist;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_JetgShoot", actor))
+		return;
+#endif
+
+	if (!actor->target)
+		return;
+
+	if (actor->reactiontime)
+		return;
+
+	dist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
+
+	if (dist > FixedMul(actor->info->painchance*FRACUNIT, actor->scale))
+		return;
+
+	if (dist < FixedMul(64*FRACUNIT, actor->scale))
+		return;
+
+	A_FaceTarget(actor);
+	P_SpawnMissile(actor, actor->target, (mobjtype_t)actor->info->raisestate);
+
+	if (ultimatemode)
+		actor->reactiontime = actor->info->reactiontime*TICRATE;
+	else
+		actor->reactiontime = actor->info->reactiontime*TICRATE*2;
+
+	if (actor->info->attacksound)
+		S_StartSound(actor, actor->info->attacksound);
+}
+
+// Function: A_ShootBullet
+//
+// Description: Shoots a bullet. Raisestate defines object # to use as projectile.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_ShootBullet(mobj_t *actor)
+{
+	fixed_t dist;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_ShootBullet", actor))
+		return;
+#endif
+
+	if (!actor->target)
+		return;
+
+	dist = P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y), actor->target->z - actor->z);
+
+	if (dist > FixedMul(actor->info->painchance*FRACUNIT, actor->scale))
+		return;
+
+	A_FaceTarget(actor);
+	P_SpawnMissile(actor, actor->target, (mobjtype_t)actor->info->raisestate);
+
+	if (actor->info->attacksound)
+		S_StartSound(actor, actor->info->attacksound);
+}
+
+// Function: A_MinusDigging
+//
+// Description: Minus digging in the ground.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_MinusDigging(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_MinusDigging", actor))
+		return;
+#endif
+	actor->flags &= ~MF_SPECIAL;
+	actor->flags &= ~MF_SHOOTABLE;
+
+	if (!actor->target)
+	{
+		A_Look(actor);
+		return;
+	}
+
+	if (actor->reactiontime)
+	{
+		actor->reactiontime--;
+		return;
+	}
+
+	// Dirt trail
+	P_SpawnGhostMobj(actor);
+
+	actor->flags |= MF_NOCLIPTHING;
+	var1 = 3;
+	A_Chase(actor);
+	actor->flags &= ~MF_NOCLIPTHING;
+
+	// Play digging sound
+	if (!(leveltime & 15))
+		S_StartSound(actor, actor->info->activesound);
+
+	// If we're close enough to our target, pop out of the ground
+	if (P_AproxDistance(actor->target->x-actor->x, actor->target->y-actor->y) < actor->radius
+		&& abs(actor->target->z - actor->z) < 2*actor->height)
+		P_SetMobjState(actor, actor->info->missilestate);
+
+	// Snap to ground
+	if (actor->eflags & MFE_VERTICALFLIP)
+		actor->z = actor->ceilingz - actor->height;
+	else
+		actor->z = actor->floorz;
+}
+
+// Function: A_MinusPopup
+//
+// Description: Minus popping out of the ground.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_MinusPopup(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_MinusPopup", actor))
+		return;
+#endif
+	P_SetObjectMomZ(actor, 10*FRACUNIT, false);
+
+	actor->flags |= MF_SPECIAL;
+	actor->flags |= MF_SHOOTABLE;
+
+	// Sound for busting out of the ground.
+	S_StartSound(actor, actor->info->attacksound);
+}
+
+// Function: A_MinusCheck
+//
+// Description: If the minus hits the floor, dig back into the ground.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_MinusCheck(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_MinusCheck", actor))
+		return;
+#endif
+	if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)
+	|| ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz))
+	{
+		actor->flags &= ~MF_SPECIAL;
+		actor->flags &= ~MF_SHOOTABLE;
+		actor->reactiontime = TICRATE;
+		P_SetMobjState(actor, actor->info->seestate);
+		return;
+	}
+
+	// 'Falling' animation
+	if (P_MobjFlip(actor)*actor->momz < 0 && actor->state < &states[actor->info->meleestate])
+		P_SetMobjState(actor, actor->info->meleestate);
+}
+
+// Function: A_ChickenCheck
+//
+// Description: Resets the chicken once it hits the floor again.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_ChickenCheck(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_ChickenCheck", actor))
+		return;
+#endif
+	if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)
+	|| (actor->eflags & MFE_VERTICALFLIP && actor->z + actor->height >= actor->ceilingz))
+	{
+		if (!(actor->momx || actor->momy || actor->momz)
+			&& actor->state > &states[actor->info->seestate])
+		{
+			A_Chase(actor);
+			P_SetMobjState(actor, actor->info->seestate);
+		}
+
+		actor->momx >>= 2;
+		actor->momy >>= 2;
+	}
+}
+
+// Function: A_JetgThink
+//
+// Description: Thinker for Jetty-Syn Gunners
+//
+// var1 = unused
+// var2 = unused
+//
+void A_JetgThink(mobj_t *actor)
+{
+	sector_t *nextsector;
+
+	fixed_t thefloor;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_JetgThink", actor))
+		return;
+#endif
+
+	if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz
+		&& actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale))
+		thefloor = actor->watertop;
+	else
+		thefloor = actor->floorz;
+
+	if (actor->target)
+	{
+		if (P_RandomChance(FRACUNIT/8) && !actor->reactiontime)
+			P_SetMobjState(actor, actor->info->missilestate);
+		else
+			A_JetChase (actor);
+	}
+	else if (actor->z - FixedMul((32<<FRACBITS), actor->scale) < thefloor && !(thefloor + FixedMul((32<<FRACBITS), actor->scale)
+		+ actor->height > actor->ceilingz))
+	{
+		actor->z = thefloor + FixedMul((32<<FRACBITS), actor->scale);
+	}
+
+	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+	{
+		// look for a new target
+		if (P_LookForPlayers(actor, true, false, 0))
+			return; // got a new target
+
+		P_SetMobjState(actor, actor->info->spawnstate);
+		return;
+	}
+
+	nextsector = R_PointInSubsector(actor->x + actor->momx, actor->y + actor->momy)->sector;
+
+	// Move downwards or upwards to go through a passageway.
+	if (nextsector->ceilingheight < actor->z + actor->height)
+		actor->momz -= FixedMul(5*FRACUNIT, actor->scale);
+	else if (nextsector->floorheight > actor->z)
+		actor->momz += FixedMul(5*FRACUNIT, actor->scale);
+}
+
+// Function: A_MouseThink
+//
+// Description: Thinker for scurrying mice.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_MouseThink(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_MouseThink", actor))
+		return;
+#endif
+
+	if (actor->reactiontime)
+		actor->reactiontime--;
+
+	if (((!(actor->eflags & MFE_VERTICALFLIP) && actor->z == actor->floorz)
+		|| (actor->eflags & MFE_VERTICALFLIP && actor->z + actor->height == actor->ceilingz))
+		&& !actor->reactiontime)
+	{
+		if (twodlevel || actor->flags2 & MF2_TWOD)
+		{
+			if (P_RandomChance(FRACUNIT/2))
+				actor->angle += ANGLE_180;
+		}
+		else if (P_RandomChance(FRACUNIT/2))
+			actor->angle += ANGLE_90;
+		else
+			actor->angle -= ANGLE_90;
+
+		P_InstaThrust(actor, actor->angle, FixedMul(actor->info->speed, actor->scale));
+		actor->reactiontime = TICRATE/5;
+	}
+}
+
+// Function: A_DetonChase
+//
+// Description: Chases a Deton after a player.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_DetonChase(mobj_t *actor)
+{
+	angle_t exact;
+	fixed_t xydist, dist;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_DetonChase", actor))
+		return;
+#endif
+
+	// modify tracer threshold
+	if (!actor->tracer || actor->tracer->health <= 0)
+		actor->threshold = 0;
+	else
+		actor->threshold = 1;
+
+	if (!actor->tracer || !(actor->tracer->flags & MF_SHOOTABLE))
+	{
+		// look for a new target
+		if (P_LookForPlayers(actor, true, true, 0))
+			return; // got a new target
+
+		actor->momx = actor->momy = actor->momz = 0;
+		P_SetMobjState(actor, actor->info->spawnstate);
+		return;
+	}
+
+	if (multiplayer && !actor->threshold && P_LookForPlayers(actor, true, true, 0))
+		return; // got a new target
+
+	// Face movement direction if not doing so
+	exact = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y);
+	actor->angle = exact;
+	/*if (exact != actor->angle)
+	{
+		if (exact - actor->angle > ANGLE_180)
+		{
+			actor->angle -= actor->info->raisestate;
+			if (exact - actor->angle < ANGLE_180)
+				actor->angle = exact;
+		}
+		else
+		{
+			actor->angle += actor->info->raisestate;
+			if (exact - actor->angle > ANGLE_180)
+				actor->angle = exact;
+		}
+	}*/
+	// movedir is up/down angle: how much it has to go up as it goes over to the player
+	xydist = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y);
+	exact = R_PointToAngle2(0, 0, xydist, actor->tracer->z - actor->z);
+	actor->movedir = exact;
+	/*if (exact != actor->movedir)
+	{
+		if (exact - actor->movedir > ANGLE_180)
+		{
+			actor->movedir -= actor->info->raisestate;
+			if (exact - actor->movedir < ANGLE_180)
+				actor->movedir = exact;
+		}
+		else
+		{
+			actor->movedir += actor->info->raisestate;
+			if (exact - actor->movedir > ANGLE_180)
+				actor->movedir = exact;
+		}
+	}*/
+
+	// check for melee attack
+	if (actor->tracer)
+	{
+		if (P_AproxDistance(actor->tracer->x-actor->x, actor->tracer->y-actor->y) < actor->radius+actor->tracer->radius)
+		{
+			if (!((actor->tracer->z > actor->z + actor->height) || (actor->z > actor->tracer->z + actor->tracer->height)))
+			{
+				P_ExplodeMissile(actor);
+				return;
+			}
+		}
+	}
+
+	// chase towards player
+	if ((dist = P_AproxDistance(xydist, actor->tracer->z-actor->z))
+		> FixedMul((actor->info->painchance << FRACBITS), actor->scale))
+	{
+		P_SetTarget(&actor->tracer, NULL); // Too far away
+		return;
+	}
+
+	if (actor->reactiontime == 0)
+	{
+		actor->reactiontime = actor->info->reactiontime;
+		return;
+	}
+
+	if (actor->reactiontime > 1)
+	{
+		actor->reactiontime--;
+		return;
+	}
+
+	if (actor->reactiontime > 0)
+	{
+		actor->reactiontime = -42;
+
+		if (actor->info->seesound)
+			S_StartScreamSound(actor, actor->info->seesound);
+	}
+
+	if (actor->reactiontime == -42)
+	{
+		fixed_t xyspeed;
+
+		actor->reactiontime = -42;
+
+		exact = actor->movedir>>ANGLETOFINESHIFT;
+		xyspeed = FixedMul(FixedMul(actor->tracer->player->normalspeed,3*FRACUNIT/4), FINECOSINE(exact));
+		actor->momz = FixedMul(FixedMul(actor->tracer->player->normalspeed,3*FRACUNIT/4), FINESINE(exact));
+
+		exact = actor->angle>>ANGLETOFINESHIFT;
+		actor->momx = FixedMul(xyspeed, FINECOSINE(exact));
+		actor->momy = FixedMul(xyspeed, FINESINE(exact));
+
+		// Variable re-use
+		xyspeed = (P_AproxDistance(actor->tracer->x - actor->x, P_AproxDistance(actor->tracer->y - actor->y, actor->tracer->z - actor->z))>>(FRACBITS+6));
+
+		if (xyspeed < 1)
+			xyspeed = 1;
+
+		if (leveltime % xyspeed == 0)
+			S_StartSound(actor, sfx_deton);
+	}
+}
+
+// Function: A_CapeChase
+//
+// Description: Set an object's location to its target or tracer.
+//
+// var1:
+//		0 = Use target
+//		1 = Use tracer
+//		upper 16 bits = Z offset
+// var2:
+//		upper 16 bits = forward/backward offset
+//		lower 16 bits = sideways offset
+//
+void A_CapeChase(mobj_t *actor)
+{
+	mobj_t *chaser;
+	fixed_t foffsetx, foffsety, boffsetx, boffsety;
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	angle_t angle;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_CapeChase", actor))
+		return;
+#endif
+
+	CONS_Debug(DBG_GAMELOGIC, "A_CapeChase called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
+
+	if (locvar1 & 65535)
+		chaser = actor->tracer;
+	else
+		chaser = actor->target;
+
+	if (!chaser || (chaser->health <= 0))
+	{
+		if (chaser)
+			CONS_Debug(DBG_GAMELOGIC, "Hmm, the guy I'm chasing (object type %d) has no health.. so I'll die too!\n", chaser->type);
+
+		P_RemoveMobj(actor);
+		return;
+	}
+
+	angle = (chaser->player ? chaser->player->drawangle : chaser->angle);
+
+	foffsetx = P_ReturnThrustX(chaser, angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale));
+	foffsety = P_ReturnThrustY(chaser, angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale));
+
+	boffsetx = P_ReturnThrustX(chaser, angle-ANGLE_90, FixedMul((locvar2 & 65535)*FRACUNIT, actor->scale));
+	boffsety = P_ReturnThrustY(chaser, angle-ANGLE_90, FixedMul((locvar2 & 65535)*FRACUNIT, actor->scale));
+
+	P_UnsetThingPosition(actor);
+	actor->x = chaser->x + foffsetx + boffsetx;
+	actor->y = chaser->y + foffsety + boffsety;
+	if (chaser->eflags & MFE_VERTICALFLIP)
+	{
+		actor->eflags |= MFE_VERTICALFLIP;
+		actor->flags2 |= MF2_OBJECTFLIP;
+		actor->z = chaser->z + chaser->height - actor->height - FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale);
+	}
+	else
+	{
+		actor->eflags &= ~MFE_VERTICALFLIP;
+		actor->flags2 &= ~MF2_OBJECTFLIP;
+		actor->z = chaser->z + FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale);
+	}
+	actor->angle = angle;
+	P_SetThingPosition(actor);
+}
+
+// Function: A_RotateSpikeBall
+//
+// Description: Rotates a spike ball around its target/tracer.
+//
+// var1:
+//		0 = Use target
+//		1 = Use tracer
+// var2 = unused
+//
+void A_RotateSpikeBall(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	const fixed_t radius = FixedMul(12*actor->info->speed, actor->scale);
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_RotateSpikeBall", actor))
+		return;
+#endif
+
+	if (!((!locvar1 && (actor->target)) || (locvar1 && (actor->tracer))))// This should NEVER happen.
+	{
+		CONS_Debug(DBG_GAMELOGIC, "A_RotateSpikeBall: Spikeball has no target\n");
+		P_RemoveMobj(actor);
+		return;
+	}
+
+	if (!actor->info->speed)
+	{
+		CONS_Debug(DBG_GAMELOGIC, "A_RotateSpikeBall: Object has no speed.\n");
+		return;
+	}
+
+	actor->angle += FixedAngle(actor->info->speed);
+	P_UnsetThingPosition(actor);
+	{
+		const angle_t fa = actor->angle>>ANGLETOFINESHIFT;
+		if (!locvar1)
+		{
+			actor->x = actor->target->x + FixedMul(FINECOSINE(fa),radius);
+			actor->y = actor->target->y + FixedMul(FINESINE(fa),radius);
+			actor->z = actor->target->z + actor->target->height/2;
+		}
+		else
+		{
+			actor->x = actor->tracer->x + FixedMul(FINECOSINE(fa),radius);
+			actor->y = actor->tracer->y + FixedMul(FINESINE(fa),radius);
+			actor->z = actor->tracer->z + actor->tracer->height/2;
+		}
+		P_SetThingPosition(actor);
+	}
+}
+
+// Function: A_UnidusBall
+//
+// Description: Rotates a spike ball around its target.
+//
+// var1:
+//		0 = Don't throw
+//		1 = Throw
+//		2 = Throw when target leaves MF2_SKULLFLY.
+// var2 = unused
+//
+void A_UnidusBall(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	boolean canthrow = false;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_UnidusBall", actor))
+		return;
+#endif
+
+	actor->angle += ANGLE_11hh;
+
+	if (actor->movecount)
+	{
+		if (P_AproxDistance(actor->momx, actor->momy) < FixedMul(actor->info->damage/2, actor->scale))
+			P_ExplodeMissile(actor);
+		return;
+	}
+
+	if (!actor->target || !actor->target->health)
+	{
+		CONS_Debug(DBG_GAMELOGIC, "A_UnidusBall: Removing unthrown spikeball from nonexistant Unidus\n");
+		P_RemoveMobj(actor);
+		return;
+	}
+
+	P_UnsetThingPosition(actor);
+	{
+		const angle_t angle = actor->movedir + FixedAngle(actor->info->speed*(leveltime%360));
+		const UINT16 fa = angle>>ANGLETOFINESHIFT;
+
+		actor->x = actor->target->x + FixedMul(FINECOSINE(fa),actor->threshold);
+		actor->y = actor->target->y + FixedMul(  FINESINE(fa),actor->threshold);
+		actor->z = actor->target->z + actor->target->height/2 - actor->height/2;
+
+		if (locvar1 == 1 && actor->target->target)
+		{
+			const angle_t tang = R_PointToAngle2(actor->target->x, actor->target->y, actor->target->target->x, actor->target->target->y);
+			const angle_t mina = tang-ANGLE_11hh;
+			canthrow = (angle-mina < FixedAngle(actor->info->speed*3));
+		}
+	}
+	P_SetThingPosition(actor);
+
+	if (locvar1 == 1 && canthrow)
+	{
+		if (P_AproxDistance(actor->target->target->x - actor->target->x, actor->target->target->y - actor->target->y) > FixedMul(MISSILERANGE>>1, actor->scale)
+		|| !P_CheckSight(actor, actor->target->target))
+			return;
+
+		actor->movecount = actor->info->damage>>FRACBITS;
+		actor->flags &= ~(MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING);
+		P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, actor->target->target->x, actor->target->target->y), FixedMul(actor->info->damage, actor->scale));
+	}
+	else if (locvar1 == 2)
+	{
+		boolean skull = (actor->target->flags2 & MF2_SKULLFLY) == MF2_SKULLFLY;
+		if (actor->target->state == &states[actor->target->info->painstate])
+		{
+			P_KillMobj(actor, NULL, NULL, 0);
+			return;
+		}
+		switch(actor->extravalue2)
+		{
+		case 0: // at least one frame where not dashing
+			if (!skull) ++actor->extravalue2;
+			else break;
+			/* FALLTHRU */
+		case 1: // at least one frame where ARE dashing
+			if (skull) ++actor->extravalue2;
+			else break;
+			/* FALLTHRU */
+		case 2: // not dashing again?
+			if (skull) break;
+			// launch.
+		{
+			mobj_t *target = actor->target;
+			if (actor->target->target)
+				target = actor->target->target;
+			actor->movecount = actor->info->damage>>FRACBITS;
+			actor->flags &= ~(MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING);
+			P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, target->x, target->y), FixedMul(actor->info->damage, actor->scale));
+		}
+		default: // from our compiler appeasement program (CAP).
+			break;
+		}
+	}
+}
+
+// Function: A_RockSpawn
+//
+// Spawns rocks at a specified interval
+//
+// var1 = unused
+// var2 = unused
+void A_RockSpawn(mobj_t *actor)
+{
+	mobj_t *mo;
+	mobjtype_t type;
+	INT32 i = P_FindSpecialLineFromTag(12, (INT16)actor->threshold, -1);
+	line_t *line;
+	fixed_t dist;
+	fixed_t randomoomph;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_RockSpawn", actor))
+		return;
+#endif
+
+	if (i == -1)
+	{
+		CONS_Debug(DBG_GAMELOGIC, "A_RockSpawn: Unable to find parameter line 12 (tag %d)!\n", actor->threshold);
+		return;
+	}
+
+	line = &lines[i];
+
+	if (!(sides[line->sidenum[0]].textureoffset >> FRACBITS))
+	{
+		CONS_Debug(DBG_GAMELOGIC, "A_RockSpawn: No X-offset detected! (tag %d)!\n", actor->threshold);
+		return;
+	}
+
+	dist = P_AproxDistance(line->dx, line->dy)/16;
+
+	if (dist < 1)
+		dist = 1;
+
+	type = MT_ROCKCRUMBLE1 + (sides[line->sidenum[0]].rowoffset >> FRACBITS);
+
+	if (line->flags & ML_NOCLIMB)
+		randomoomph = P_RandomByte() * (FRACUNIT/32);
+	else
+		randomoomph = 0;
+
+	mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_FALLINGROCK);
+	P_SetMobjState(mo, mobjinfo[type].spawnstate);
+	mo->angle = R_PointToAngle2(line->v2->x, line->v2->y, line->v1->x, line->v1->y);
+
+	P_InstaThrust(mo, mo->angle, dist + randomoomph);
+	mo->momz = dist + randomoomph;
+
+	var1 = sides[line->sidenum[0]].textureoffset >> FRACBITS;
+	A_SetTics(actor);
+}
+
+//
+// Function: A_SlingAppear
+//
+// Appears a sling.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_SlingAppear(mobj_t *actor)
+{
+	UINT8 mlength = 4;
+	mobj_t *spawnee, *hprev;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SlingAppear", actor))
+		return;
+#endif
+
+	P_UnsetThingPosition(actor);
+	actor->flags &= ~(MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_NOCLIPHEIGHT);
+	P_SetThingPosition(actor);
+	actor->lastlook = 128;
+	actor->movecount = actor->lastlook;
+	actor->threshold = 0;
+	actor->movefactor = actor->threshold;
+	actor->friction = 128;
+
+	hprev = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMALLGRABCHAIN);
+	P_SetTarget(&hprev->tracer, actor);
+	P_SetTarget(&hprev->hprev, actor);
+	P_SetTarget(&actor->hnext, hprev);
+	hprev->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT;
+	hprev->movecount = mlength;
+
+	mlength--;
+
+	while (mlength > 0)
+	{
+		spawnee = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMALLMACECHAIN);
+		P_SetTarget(&spawnee->tracer, actor);
+		P_SetTarget(&spawnee->hprev, hprev);
+		P_SetTarget(&hprev->hnext, spawnee);
+		hprev = spawnee;
+
+		spawnee->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT;
+		spawnee->movecount = mlength;
+
+		mlength--;
+	}
+}
+
+// Function: A_SetFuse
+//
+// Description: Sets the actor's fuse timer if not set already. May also change state when fuse reaches the last tic, otherwise by default the actor will die or disappear. (Replaces A_SnowBall)
+//
+// var1 = fuse timer duration (in tics).
+// var2:
+//		lower 16 bits = if > 0, state to change to when fuse = 1
+//		upper 16 bits: 0 = (default) don't set fuse unless 0, 1 = force change, 2 = force no change
+//
+void A_SetFuse(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SetFuse", actor))
+		return;
+#endif
+
+	if ((!actor->fuse || (locvar2 >> 16)) && (locvar2 >> 16) != 2) // set the actor's fuse value
+		actor->fuse = locvar1;
+
+	if (actor->fuse == 1 && (locvar2 & 65535)) // change state on the very last tic (fuse is handled before actions in P_MobjThinker)
+	{
+		actor->fuse = 0; // don't die/disappear the next tic!
+		P_SetMobjState(actor, locvar2 & 65535);
+	}
+}
+
+// Function: A_CrawlaCommanderThink
+//
+// Description: Thinker for Crawla Commander.
+//
+// var1 = shoot bullets?
+// var2 = "pogo mode" speed
+//
+void A_CrawlaCommanderThink(mobj_t *actor)
+{
+	fixed_t dist;
+	sector_t *nextsector;
+	fixed_t thefloor;
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	boolean hovermode = (actor->health > 1 || actor->fuse);
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_CrawlaCommanderThink", actor))
+		return;
+#endif
+
+	if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz
+		&& actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale))
+		thefloor = actor->watertop;
+	else
+		thefloor = actor->floorz;
+
+	if (!actor->fuse && actor->flags2 & MF2_FRET)
+	{
+		if (actor->info->painsound)
+			S_StartSound(actor, actor->info->painsound);
+
+		actor->fuse = TICRATE/2;
+		actor->momz = 0;
+
+		P_InstaThrust(actor, actor->angle-ANGLE_180, FixedMul(5*FRACUNIT, actor->scale));
+	}
+
+	if (actor->reactiontime > 0)
+		actor->reactiontime--;
+
+	if (actor->fuse < 2)
+	{
+		actor->fuse = 0;
+		actor->flags2 &= ~MF2_FRET;
+	}
+
+	// Hover mode
+	if (hovermode)
+	{
+		if (actor->z < thefloor + FixedMul(16*FRACUNIT, actor->scale))
+			actor->momz += FixedMul(FRACUNIT, actor->scale);
+		else if (actor->z < thefloor + FixedMul(32*FRACUNIT, actor->scale))
+			actor->momz += FixedMul(FRACUNIT/2, actor->scale);
+		else
+			actor->momz += FixedMul(16, actor->scale);
+	}
+
+	if (!actor->target)
+	{
+		// look for a new target
+		if (P_LookForPlayers(actor, true, false, 0))
+			return; // got a new target
+
+		if (actor->state != &states[actor->info->spawnstate])
+			P_SetMobjState(actor, actor->info->spawnstate);
+		return;
+	}
+
+	dist = P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y);
+
+	if (actor->target->player && (!hovermode || actor->reactiontime <= 2*TICRATE))
+	{
+		if (dist < FixedMul(64<<(FRACBITS+(hovermode ? 1 : 0)), actor->scale)
+			&& ((actor->target->player->pflags & PF_JUMPED) || (actor->target->player->pflags & PF_SPINNING)))
+		{
+			// Auugh! She's trying to kill you! Strafe! STRAAAAFFEEE!!
+			P_InstaThrust(actor, actor->angle - ANGLE_180, FixedMul(20*FRACUNIT, actor->scale));
+			return;
+		}
+	}
+
+	if (locvar1)
+	{
+		if (actor->health < 2 && P_RandomChance(FRACUNIT/128))
+			P_SpawnMissile(actor, actor->target, locvar1);
+	}
+
+	// Face the player
+	actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+
+	if (actor->threshold && dist > FixedMul(256*FRACUNIT, actor->scale))
+		actor->momx = actor->momy = 0;
+
+	if (actor->reactiontime && actor->reactiontime <= 2*TICRATE && dist > actor->target->radius - FixedMul(FRACUNIT, actor->scale))
+	{
+		actor->threshold = 0;
+
+		// Roam around, somewhat in the player's direction.
+		actor->angle += (P_RandomByte()<<10);
+		actor->angle -= (P_RandomByte()<<10);
+
+		if (hovermode)
+		{
+			fixed_t mom;
+			P_Thrust(actor, actor->angle, 2*actor->scale);
+			mom = P_AproxDistance(actor->momx, actor->momy);
+			if (mom > 20*actor->scale)
+			{
+				mom += 20*actor->scale;
+				mom >>= 1;
+				P_InstaThrust(actor, R_PointToAngle2(0, 0, actor->momx, actor->momy), mom);
+			}
+		}
+	}
+	else if (!actor->reactiontime)
+	{
+		if (hovermode && !(actor->flags2 & MF2_FRET)) // Hover Mode
+		{
+			if (dist < FixedMul(512*FRACUNIT, actor->scale))
+			{
+				actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+				P_InstaThrust(actor, actor->angle, FixedMul(40*FRACUNIT, actor->scale));
+				actor->threshold = 1;
+				if (actor->info->attacksound)
+					S_StartSound(actor, actor->info->attacksound);
+			}
+		}
+		actor->reactiontime = 3*TICRATE + (P_RandomByte()>>2);
+	}
+
+	if (actor->health == 1)
+		P_Thrust(actor, actor->angle, 1);
+
+	// Pogo Mode
+	if (!hovermode && actor->z <= actor->floorz)
+	{
+		if (actor->info->activesound)
+			S_StartSound(actor, actor->info->activesound);
+
+		if (dist < FixedMul(256*FRACUNIT, actor->scale))
+		{
+			actor->momz = FixedMul(locvar2, actor->scale);
+			actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+			P_InstaThrust(actor, actor->angle, FixedMul(locvar2/8, actor->scale));
+			// pogo on player
+		}
+		else
+		{
+			UINT8 prandom = P_RandomByte();
+			actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom);
+			P_InstaThrust(actor, actor->angle, FixedDiv(FixedMul(locvar2, actor->scale), 3*FRACUNIT/2));
+			actor->momz = FixedMul(locvar2, actor->scale); // Bounce up in air
+		}
+	}
+
+	nextsector = R_PointInSubsector(actor->x + actor->momx, actor->y + actor->momy)->sector;
+
+	// Move downwards or upwards to go through a passageway.
+	if (nextsector->floorheight > actor->z && nextsector->floorheight - actor->z < FixedMul(128*FRACUNIT, actor->scale))
+		actor->momz += (nextsector->floorheight - actor->z) / 4;
+}
+
+// Function: A_RingExplode
+//
+// Description: An explosion ring exploding
+//
+// var1 = unused
+// var2 = unused
+//
+void A_RingExplode(mobj_t *actor)
+{
+	mobj_t *mo2;
+	thinker_t *th;
+	angle_t d;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_RingExplode", actor))
+		return;
+#endif
+
+	for (d = 0; d < 16; d++)
+		P_SpawnParaloop(actor->x, actor->y, actor->z + actor->height, FixedMul(actor->info->painchance, actor->scale), 16, MT_NIGHTSPARKLE, S_NULL, d*(ANGLE_22h), true);
+
+	S_StartSound(actor, sfx_prloop);
+
+	for (th = thinkercap.next; th != &thinkercap; th = th->next)
+	{
+		if (th->function.acp1 != (actionf_p1)P_MobjThinker)
+			continue;
+
+		mo2 = (mobj_t *)th;
+
+		if (mo2 == actor) // Don't explode yourself! Endless loop!
+			continue;
+
+		if (P_AproxDistance(P_AproxDistance(mo2->x - actor->x, mo2->y - actor->y), mo2->z - actor->z) > FixedMul(actor->info->painchance, actor->scale))
+			continue;
+
+		if (mo2->flags & MF_SHOOTABLE)
+		{
+			actor->flags2 |= MF2_DEBRIS;
+			P_DamageMobj(mo2, actor, actor->target, 1, 0);
+			continue;
+		}
+	}
+	return;
+}
+
+// Function: A_OldRingExplode
+//
+// Description: An explosion ring exploding, 1.09.4 style
+//
+// var1 = object # to explode as debris
+// var2 = unused
+//
+void A_OldRingExplode(mobj_t *actor) {
+	UINT8 i;
+	mobj_t *mo;
+	const fixed_t ns = FixedMul(20 * FRACUNIT, actor->scale);
+	INT32 locvar1 = var1;
+	//INT32 locvar2 = var2;
+	boolean changecolor = (actor->target && actor->target->player);
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_OldRingExplode", actor))
+		return;
+#endif
+
+	for (i = 0; i < 32; i++)
+	{
+		const angle_t fa = (i*FINEANGLES/16) & FINEMASK;
+
+		mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1);
+		P_SetTarget(&mo->target, actor->target); // Transfer target so player gets the points
+
+		mo->momx = FixedMul(FINECOSINE(fa),ns);
+		mo->momy = FixedMul(FINESINE(fa),ns);
+
+		if (i > 15)
+		{
+			if (i & 1)
+				mo->momz = ns;
+			else
+				mo->momz = -ns;
+		}
+
+		mo->flags2 |= MF2_DEBRIS;
+		mo->fuse = TICRATE/5;
+
+		if (changecolor)
+		{
+			if (gametype != GT_CTF)
+				mo->color = actor->target->color; //copy color
+			else if (actor->target->player->ctfteam == 2)
+				mo->color = skincolor_bluering;
+		}
+	}
+
+	mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1);
+
+	P_SetTarget(&mo->target, actor->target);
+	mo->momz = ns;
+	mo->flags2 |= MF2_DEBRIS;
+	mo->fuse = TICRATE/5;
+
+	if (changecolor)
+	{
+		if (gametype != GT_CTF)
+			mo->color = actor->target->color; //copy color
+		else if (actor->target->player->ctfteam == 2)
+			mo->color = skincolor_bluering;
+	}
+
+	mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1);
+
+	P_SetTarget(&mo->target, actor->target);
+	mo->momz = -ns;
+	mo->flags2 |= MF2_DEBRIS;
+	mo->fuse = TICRATE/5;
+
+	if (changecolor)
+	{
+		if (gametype != GT_CTF)
+			mo->color = actor->target->color; //copy color
+		else if (actor->target->player->ctfteam == 2)
+			mo->color = skincolor_bluering;
+	}
+}
+
+// Function: A_MixUp
+//
+// Description: Mix up all of the player positions.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_MixUp(mobj_t *actor)
+{
+	boolean teleported[MAXPLAYERS];
+	INT32 i, numplayers = 0, prandom = 0;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_MixUp", actor))
+		return;
+#else
+	(void)actor;
+#endif
+
+	if (!multiplayer)
+		return;
+
+	// No mix-up monitors in hide and seek or time only race.
+	// The random factor is okay for other game modes, but in these, it is cripplingly unfair.
+	if (gametype == GT_HIDEANDSEEK || gametype == GT_RACE)
+	{
+		S_StartSound(actor, sfx_lose);
+		return;
+	}
+
+	numplayers = 0;
+	memset(teleported, 0, sizeof (teleported));
+
+	// Count the number of players in the game
+	// and grab their xyz coords
+	for (i = 0; i < MAXPLAYERS; i++)
+		if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE
+			&& !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE)
+		{
+			if ((netgame || multiplayer) && players[i].spectator) // Ignore spectators
+				continue;
+
+			numplayers++;
+		}
+
+	if (numplayers <= 1) // Not enough players to mix up.
+	{
+		S_StartSound(actor, sfx_lose);
+		return;
+	}
+	else if (numplayers == 2) // Special case -- simple swap
+	{
+		fixed_t x, y, z;
+		angle_t angle;
+		INT32 one = -1, two = 0; // default value 0 to make the compiler shut up
+
+		// Zoom tube stuff
+		mobj_t *tempthing = NULL; //tracer
+		UINT16 carry1,carry2;     //carry
+		INT32 transspeed;         //player speed
+
+		// Starpost stuff
+		INT16 starpostx, starposty, starpostz;
+		INT32 starpostnum;
+		tic_t starposttime;
+		angle_t starpostangle;
+
+		INT32 mflags2;
+
+		for (i = 0; i < MAXPLAYERS; i++)
+			if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE
+				&& !players[i].exiting && !players[i].powers[pw_super])
+			{
+				if ((netgame || multiplayer) && players[i].spectator) // Ignore spectators
+					continue;
+
+				if (one == -1)
+					one = i;
+				else
+				{
+					two = i;
+					break;
+				}
+			}
+
+		//get this done first!
+		tempthing = players[one].mo->tracer;
+		P_SetTarget(&players[one].mo->tracer, players[two].mo->tracer);
+		P_SetTarget(&players[two].mo->tracer, tempthing);
+
+		//zoom tubes use player->speed to determine direction and speed
+		transspeed = players[one].speed;
+		players[one].speed = players[two].speed;
+		players[two].speed = transspeed;
+
+		//set flags variables now but DON'T set them.
+		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;
+		z = players[one].mo->z;
+		angle = players[one].mo->angle;
+
+		starpostx = players[one].starpostx;
+		starposty = players[one].starposty;
+		starpostz = players[one].starpostz;
+		starpostangle = players[one].starpostangle;
+		starpostnum = players[one].starpostnum;
+		starposttime = players[one].starposttime;
+
+		mflags2 = players[one].mo->flags2;
+
+		P_MixUp(players[one].mo, players[two].mo->x, players[two].mo->y, players[two].mo->z, players[two].mo->angle,
+				players[two].starpostx, players[two].starposty, players[two].starpostz,
+				players[two].starpostnum, players[two].starposttime, players[two].starpostangle,
+				players[two].mo->flags2);
+
+		P_MixUp(players[two].mo, x, y, z, angle, starpostx, starposty, starpostz,
+				starpostnum, starposttime, starpostangle,
+				mflags2);
+
+		//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;
+	}
+	else
+	{
+		fixed_t position[MAXPLAYERS][3];
+		angle_t anglepos[MAXPLAYERS];
+		INT32 pindex[MAXPLAYERS], counter = 0, teleportfrom = 0;
+
+		// Zoom tube stuff
+		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
+		INT16 spposition[MAXPLAYERS][3];
+		INT32 starpostnum[MAXPLAYERS];
+		tic_t starposttime[MAXPLAYERS];
+		angle_t starpostangle[MAXPLAYERS];
+
+		INT32 flags2[MAXPLAYERS];
+
+		for (i = 0; i < MAXPLAYERS; i++)
+		{
+			position[i][0] = position[i][1] = position[i][2] = anglepos[i] = pindex[i] = -1;
+			teleported[i] = false;
+		}
+
+		for (i = 0; i < MAXPLAYERS; i++)
+		{
+			if (playeringame[i] && players[i].playerstate == PST_LIVE
+				&& players[i].mo && players[i].mo->health > 0 && !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE)
+			{
+				if ((netgame || multiplayer) && players[i].spectator)// Ignore spectators
+					continue;
+
+				position[counter][0] = players[i].mo->x;
+				position[counter][1] = players[i].mo->y;
+				position[counter][2] = players[i].mo->z;
+				pindex[counter] = i;
+				anglepos[counter] = players[i].mo->angle;
+				players[i].mo->momx = players[i].mo->momy = players[i].mo->momz =
+					players[i].rmomx = players[i].rmomy = 1;
+				players[i].cmomx = players[i].cmomy = 0;
+
+				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;
+
+				spposition[counter][0] = players[i].starpostx;
+				spposition[counter][1] = players[i].starposty;
+				spposition[counter][2] = players[i].starpostz;
+				starpostnum[counter] = players[i].starpostnum;
+				starposttime[counter] = players[i].starposttime;
+				starpostangle[counter] = players[i].starpostangle;
+
+				flags2[counter] = players[i].mo->flags2;
+
+				counter++;
+			}
+		}
+
+		counter = 0;
+
+		// Mix them up!
+		for (;;)
+		{
+			if (counter > 255) // fail-safe to avoid endless loop
+				break;
+			prandom = P_RandomByte();
+			prandom %= numplayers; // I love modular arithmetic, don't you?
+			if (prandom) // Make sure it's not a useless mix
+				break;
+			counter++;
+		}
+
+		counter = 0;
+
+		for (i = 0; i < MAXPLAYERS; i++)
+		{
+			if (playeringame[i] && players[i].playerstate == PST_LIVE
+				&& players[i].mo && players[i].mo->health > 0 && !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE)
+			{
+				if ((netgame || multiplayer) && players[i].spectator)// Ignore spectators
+					continue;
+
+				teleportfrom = (counter + prandom) % numplayers;
+
+				//speed and tracer come before...
+				players[i].speed = transspeed[teleportfrom];
+				P_SetTarget(&players[i].mo->tracer, transtracer[teleportfrom]);
+
+				P_MixUp(players[i].mo, position[teleportfrom][0], position[teleportfrom][1], position[teleportfrom][2], anglepos[teleportfrom],
+					spposition[teleportfrom][0], spposition[teleportfrom][1], spposition[teleportfrom][2],
+					starpostnum[teleportfrom], starposttime[teleportfrom], starpostangle[teleportfrom],
+					flags2[teleportfrom]);
+
+				//...carry after.  same reasoning.
+				players[i].powers[pw_carry] = transcarry[teleportfrom];
+
+				teleported[i] = true;
+				counter++;
+			}
+		}
+	}
+
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		if (teleported[i])
+		{
+			if (playeringame[i] && players[i].playerstate == PST_LIVE
+				&& players[i].mo && players[i].mo->health > 0 && !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE)
+			{
+				if ((netgame || multiplayer) && players[i].spectator)// Ignore spectators
+					continue;
+
+				P_SetThingPosition(players[i].mo);
+
+#ifdef ESLOPE
+				players[i].mo->floorz = P_GetFloorZ(players[i].mo, players[i].mo->subsector->sector, players[i].mo->x, players[i].mo->y, NULL);
+				players[i].mo->ceilingz = P_GetCeilingZ(players[i].mo, players[i].mo->subsector->sector, players[i].mo->x, players[i].mo->y, NULL);
+#else
+				players[i].mo->floorz = players[i].mo->subsector->sector->floorheight;
+				players[i].mo->ceilingz = players[i].mo->subsector->sector->ceilingheight;
+#endif
+
+				P_CheckPosition(players[i].mo, players[i].mo->x, players[i].mo->y);
+			}
+		}
+	}
+
+	// Play the 'bowrwoosh!' sound
+	S_StartSound(NULL, sfx_mixup);
+}
+
+// Function: A_RecyclePowers
+//
+// Description: Take all player's powers, and swap 'em.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_RecyclePowers(mobj_t *actor)
+{
+	INT32 i, j, k, numplayers = 0;
+
+#ifdef WEIGHTEDRECYCLER
+	UINT8 beneficiary = 255;
+#endif
+	UINT8 playerslist[MAXPLAYERS];
+	UINT8 postscramble[MAXPLAYERS];
+
+	UINT16 powers[MAXPLAYERS][NUMPOWERS];
+	INT32 weapons[MAXPLAYERS];
+	INT32 weaponheld[MAXPLAYERS];
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_RecyclePowers", actor))
+		return;
+#endif
+
+#if !defined(WEIGHTEDRECYCLER) && !defined(HAVE_BLUA)
+	// actor is used in all scenarios but this one, funny enough
+	(void)actor;
+#endif
+
+	if (!multiplayer)
+	{
+		S_StartSound(actor, sfx_lose);
+		return;
+	}
+
+	numplayers = 0;
+
+	// Count the number of players in the game
+	for (i = 0, j = 0; i < MAXPLAYERS; i++)
+	{
+		if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE
+			&& !players[i].exiting && !((netgame || multiplayer) && players[i].spectator))
+		{
+#ifndef WEIGHTEDRECYCLER
+			if (players[i].powers[pw_super])
+				continue; // Ignore super players
+#endif
+
+			numplayers++;
+			postscramble[j] = playerslist[j] = (UINT8)i;
+
+#ifdef WEIGHTEDRECYCLER
+			// The guy who started the recycle gets the best result
+			if (actor && actor->target && actor->target->player && &players[i] == actor->target->player)
+				beneficiary = (UINT8)i;
+#endif
+
+			// Save powers
+			for (k = 0; k < NUMPOWERS; k++)
+				powers[i][k] = players[i].powers[k];
+			//1.1: ring weapons too
+			weapons[i] = players[i].ringweapons;
+			weaponheld[i] = players[i].currentweapon;
+
+			j++;
+		}
+	}
+
+	if (numplayers <= 1)
+	{
+		S_StartSound(actor, sfx_lose);
+		return; //nobody to touch!
+	}
+
+	//shuffle the post scramble list, whee!
+	// hardcoded 0-1 to 1-0 for two players
+	if (numplayers == 2)
+	{
+		postscramble[0] = playerslist[1];
+		postscramble[1] = playerslist[0];
+	}
+	else
+	for (j = 0; j < numplayers; j++)
+	{
+		UINT8 tempint;
+
+		i = j + ((P_RandomByte() + leveltime) % (numplayers - j));
+		tempint = postscramble[j];
+		postscramble[j] = postscramble[i];
+		postscramble[i] = tempint;
+	}
+
+#ifdef WEIGHTEDRECYCLER
+	//the joys of qsort...
+	if (beneficiary != 255) {
+		qsort(playerslist, numplayers, sizeof(UINT8), P_RecycleCompare);
+
+		// now, make sure the benificiary is in the best slot
+		// swap out whatever poor sap was going to get the best items
+		for (i = 0; i < numplayers; i++)
+		{
+			if (postscramble[i] == beneficiary)
+			{
+				postscramble[i] = postscramble[0];
+				postscramble[0] = beneficiary;
+				break;
+			}
+		}
+	}
+#endif
+
+	// now assign!
+	for (i = 0; i < numplayers; i++)
+	{
+		UINT8 send_pl = playerslist[i];
+		UINT8 recv_pl = postscramble[i];
+
+		// debugF
+		CONS_Debug(DBG_GAMELOGIC, "sending player %hu's items to %hu\n", (UINT16)send_pl, (UINT16)recv_pl);
+
+		for (j = 0; j < NUMPOWERS; j++)
+		{
+			if (j == pw_flashing || j == pw_underwater || j == pw_spacetime || j == pw_carry
+			    || j == pw_tailsfly || j == pw_extralife || j == pw_nocontrol || j == pw_super)
+				continue;
+			players[recv_pl].powers[j] = powers[send_pl][j];
+		}
+
+		//1.1: weapon rings too
+		players[recv_pl].ringweapons = weapons[send_pl];
+		players[recv_pl].currentweapon = weaponheld[send_pl];
+
+		P_SpawnShieldOrb(&players[recv_pl]);
+		if (P_IsLocalPlayer(&players[recv_pl]))
+			P_RestoreMusic(&players[recv_pl]);
+		P_FlashPal(&players[recv_pl], PAL_RECYCLE, 10);
+	}
+
+	S_StartSound(NULL, sfx_gravch); //heh, the sound effect I used is already in
+}
+
+// Function: A_Boss1Chase
+//
+// Description: Like A_Chase, but for Boss 1.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_Boss1Chase(mobj_t *actor)
+{
+	INT32 delta;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Boss1Chase", actor))
+		return;
+#endif
+
+	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+	{
+		// look for a new target
+		if (P_LookForPlayers(actor, true, false, 0))
+			return; // got a new target
+
+		P_SetMobjStateNF(actor, actor->info->spawnstate);
+		return;
+	}
+
+	if (actor->reactiontime)
+		actor->reactiontime--;
+
+	// turn towards movement direction if not there yet
+	if (actor->movedir < NUMDIRS)
+	{
+		actor->angle &= (7<<29);
+		delta = actor->angle - (actor->movedir << 29);
+
+		if (delta > 0)
+			actor->angle -= ANGLE_45;
+		else if (delta < 0)
+			actor->angle += ANGLE_45;
+	}
+
+	// do not attack twice in a row
+	if (actor->flags2 & MF2_JUSTATTACKED)
+	{
+		actor->flags2 &= ~MF2_JUSTATTACKED;
+		P_NewChaseDir(actor);
+		return;
+	}
+
+	if (actor->movecount)
+		goto nomissile;
+
+	if (!P_CheckMissileRange(actor))
+		goto nomissile;
+
+	if (actor->reactiontime <= 0)
+	{
+		if (actor->health > actor->info->damage)
+		{
+			if (P_RandomChance(FRACUNIT/2))
+				P_SetMobjState(actor, actor->info->missilestate);
+			else
+				P_SetMobjState(actor, actor->info->meleestate);
+		}
+		else
+		{
+			P_LinedefExecute(LE_PINCHPHASE, actor, NULL);
+			P_SetMobjState(actor, actor->info->raisestate);
+		}
+
+		actor->flags2 |= MF2_JUSTATTACKED;
+		actor->reactiontime = actor->info->reactiontime;
+		return;
+	}
+
+	// ?
+nomissile:
+	// possibly choose another target
+	if (multiplayer && P_RandomChance(FRACUNIT/128))
+	{
+		if (P_LookForPlayers(actor, true, false, 0))
+			return; // got a new target
+	}
+
+	if (actor->flags & MF_FLOAT && !(actor->flags2 & MF2_SKULLFLY))
+	{ // Float up/down to your target's position. Stay above them, but not out of jump range.
+		fixed_t target_min = actor->target->floorz+FixedMul(64*FRACUNIT, actor->scale);
+		if (target_min < actor->target->z - actor->height)
+			target_min = actor->target->z - actor->height;
+		if (target_min < actor->floorz+FixedMul(33*FRACUNIT, actor->scale))
+			target_min = actor->floorz+FixedMul(33*FRACUNIT, actor->scale);
+		if (actor->z > target_min+FixedMul(16*FRACUNIT, actor->scale))
+			actor->momz = FixedMul((-actor->info->speed<<(FRACBITS-1)), actor->scale);
+		else if (actor->z < target_min)
+			actor->momz = FixedMul(actor->info->speed<<(FRACBITS-1), actor->scale);
+		else
+			actor->momz = FixedMul(actor->momz,7*FRACUNIT/8);
+	}
+
+	// chase towards player
+	if (P_AproxDistance(actor->target->x-actor->x, actor->target->y-actor->y) > actor->radius+actor->target->radius)
+	{
+		if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
+			P_NewChaseDir(actor);
+	}
+	// too close, don't want to chase.
+	else if (--actor->movecount < 0)
+	{
+		// A mini-A_FaceTarget based on P_NewChaseDir.
+		// Yes, it really is this simple when you get down to it.
+		fixed_t deltax, deltay;
+
+		deltax = actor->target->x - actor->x;
+		deltay = actor->target->y - actor->y;
+
+		actor->movedir = diags[((deltay < 0)<<1) + (deltax > 0)];
+		actor->movecount = P_RandomByte() & 15;
+	}
+}
+
+// Function: A_Boss2Chase
+//
+// Description: Really doesn't 'chase', but rather goes in a circle.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_Boss2Chase(mobj_t *actor)
+{
+	fixed_t radius;
+	boolean reverse = false;
+	INT32 speedvar;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Boss2Chase", actor))
+		return;
+#endif
+
+	if (actor->health <= 0)
+		return;
+
+	// Startup randomness
+	if (actor->reactiontime <= -666)
+		actor->reactiontime = 2*TICRATE + P_RandomByte();
+
+	// When reactiontime hits zero, he will go the other way
+	if (--actor->reactiontime <= 0)
+	{
+		reverse = true;
+		actor->reactiontime = 2*TICRATE + P_RandomByte();
+	}
+
+	P_SetTarget(&actor->target, P_GetClosestAxis(actor));
+
+	if (!actor->target) // This should NEVER happen.
+	{
+		CONS_Debug(DBG_GAMELOGIC, "Boss2 has no target!\n");
+		A_BossDeath(actor);
+		return;
+	}
+
+	radius = actor->target->radius;
+
+	if (reverse)
+	{
+		actor->watertop = -actor->watertop;
+		actor->extravalue1 = 18;
+		if (actor->flags2 & MF2_AMBUSH)
+			actor->extravalue1 -= (actor->info->spawnhealth - actor->health)*2;
+		actor->extravalue2 = actor->extravalue1;
+	}
+
+	// Turnaround
+	if (actor->extravalue1 > 0)
+	{
+		--actor->extravalue1;
+
+		// Set base angle
+		{
+			const angle_t fa = (actor->target->angle + FixedAngle(actor->watertop))>>ANGLETOFINESHIFT;
+			const fixed_t fc = FixedMul(FINECOSINE(fa),radius);
+			const fixed_t fs = FixedMul(FINESINE(fa),radius);
+			actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x + fc, actor->target->y + fs);
+		}
+
+		// Now turn you around!
+		// Note that the start position is the final position, we move it back around
+		// to intermediary positions...
+		actor->angle -= FixedAngle(FixedMul(FixedDiv(180<<FRACBITS, actor->extravalue2<<FRACBITS), actor->extravalue1<<FRACBITS));
+	}
+	else
+	{
+		// Only speed up if you have the 'Deaf' flag.
+		if (actor->flags2 & MF2_AMBUSH)
+			speedvar = actor->health;
+		else
+			speedvar = actor->info->spawnhealth;
+
+		actor->target->angle += // Don't use FixedAngleC!
+			FixedAngle(FixedDiv(FixedMul(actor->watertop, (actor->info->spawnhealth*(FRACUNIT/4)*3)), speedvar*FRACUNIT));
+
+		P_UnsetThingPosition(actor);
+		{
+			const angle_t fa = actor->target->angle>>ANGLETOFINESHIFT;
+			const fixed_t fc = FixedMul(FINECOSINE(fa),radius);
+			const fixed_t fs = FixedMul(FINESINE(fa),radius);
+			actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x + fc, actor->target->y + fs);
+			actor->x = actor->target->x + fc;
+			actor->y = actor->target->y + fs;
+		}
+		P_SetThingPosition(actor);
+
+		// Spray goo once every second
+		if (leveltime % (speedvar*15/10)-1 == 0)
+		{
+			const fixed_t ns = FixedMul(3 * FRACUNIT, actor->scale);
+			mobj_t *goop;
+			fixed_t fz = actor->z+actor->height+FixedMul(24*FRACUNIT, actor->scale);
+			angle_t fa;
+			// actor->movedir is used to determine the last
+			// direction goo was sprayed in. There are 8 possible
+			// directions to spray. (45-degree increments)
+
+			actor->movedir++;
+			actor->movedir %= NUMDIRS;
+			fa = (actor->movedir*FINEANGLES/8) & FINEMASK;
+
+			goop = P_SpawnMobj(actor->x, actor->y, fz, actor->info->painchance);
+			goop->momx = FixedMul(FINECOSINE(fa),ns);
+			goop->momy = FixedMul(FINESINE(fa),ns);
+			goop->momz = FixedMul(4*FRACUNIT, actor->scale);
+			goop->fuse = 10*TICRATE;
+
+			if (actor->info->attacksound)
+				S_StartAttackSound(actor, actor->info->attacksound);
+
+			if (P_RandomChance(FRACUNIT/2))
+			{
+				goop->momx *= 2;
+				goop->momy *= 2;
+			}
+			else if (P_RandomChance(129*FRACUNIT/256))
+			{
+				goop->momx *= 3;
+				goop->momy *= 3;
+			}
+
+			actor->flags2 |= MF2_JUSTATTACKED;
+		}
+	}
+}
+
+// Function: A_Boss2Pogo
+//
+// Description: Pogo part of Boss 2 AI.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_Boss2Pogo(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Boss2Pogo", actor))
+		return;
+#endif
+	if (actor->z <= actor->floorz + FixedMul(8*FRACUNIT, actor->scale) && actor->momz <= 0)
+	{
+		if (actor->state != &states[actor->info->raisestate])
+			P_SetMobjState(actor, actor->info->raisestate);
+		// Pogo Mode
+	}
+	else if (actor->momz < 0 && actor->reactiontime)
+	{
+		const fixed_t ns = FixedMul(3 * FRACUNIT, actor->scale);
+		mobj_t *goop;
+		fixed_t fz = actor->z+actor->height+FixedMul(24*FRACUNIT, actor->scale);
+		angle_t fa;
+		INT32 i;
+		// spray in all 8 directions!
+		for (i = 0; i < 8; i++)
+		{
+			actor->movedir++;
+			actor->movedir %= NUMDIRS;
+			fa = (actor->movedir*FINEANGLES/8) & FINEMASK;
+
+			goop = P_SpawnMobj(actor->x, actor->y, fz, actor->info->painchance);
+			goop->momx = FixedMul(FINECOSINE(fa),ns);
+			goop->momy = FixedMul(FINESINE(fa),ns);
+			goop->momz = FixedMul(4*FRACUNIT, actor->scale);
+
+			goop->fuse = 10*TICRATE;
+		}
+		actor->reactiontime = 0; // we already shot goop, so don't do it again!
+		if (actor->info->attacksound)
+			S_StartAttackSound(actor, actor->info->attacksound);
+		actor->flags2 |= MF2_JUSTATTACKED;
+	}
+}
+
+// Function: A_Boss2TakeDamage
+//
+// Description: Special function for Boss 2 so you can't just sit and destroy him.
+//
+// var1 = Invincibility duration
+// var2 = unused
+//
+void A_Boss2TakeDamage(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Boss2TakeDamage", actor))
+		return;
+#endif
+	A_Pain(actor);
+	actor->reactiontime = 1; // turn around
+	if (locvar1 == 0) // old A_Invincibilerize behavior
+		actor->movecount = TICRATE;
+	else
+		actor->movecount = locvar1; // become flashing invulnerable for this long.
+}
+
+// Function: A_Boss7Chase
+//
+// Description: Like A_Chase, but for Black Eggman
+//
+// var1 = unused
+// var2 = unused
+//
+void A_Boss7Chase(mobj_t *actor)
+{
+	INT32 delta;
+	INT32 i;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Boss7Chase", actor))
+		return;
+#endif
+
+	if (actor->z != actor->floorz)
+		return;
+
+	// Self-adjust if stuck on the edge
+	if (actor->tracer)
+	{
+		if (P_AproxDistance(actor->x - actor->tracer->x, actor->y - actor->tracer->y) > 128*FRACUNIT - actor->radius)
+			P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y), FRACUNIT);
+	}
+
+	if (actor->flags2 & MF2_FRET)
+	{
+		P_SetMobjState(actor, S_BLACKEGG_DESTROYPLAT1);
+		S_StartSound(0, sfx_s3k53);
+		actor->flags2 &= ~MF2_FRET;
+		return;
+	}
+
+	// turn towards movement direction if not there yet
+	if (actor->movedir < NUMDIRS)
+	{
+		actor->angle &= (7<<29);
+		delta = actor->angle - (actor->movedir << 29);
+
+		if (delta > 0)
+			actor->angle -= ANGLE_45;
+		else if (delta < 0)
+			actor->angle += ANGLE_45;
+	}
+
+	// Is a player on top of us?
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		if (!playeringame[i] || players[i].spectator)
+			continue;
+
+		if (!players[i].mo)
+			continue;
+
+		if (players[i].mo->health <= 0)
+			continue;
+
+		if (P_AproxDistance(players[i].mo->x - actor->x, players[i].mo->y - actor->y) > actor->radius)
+			continue;
+
+		if (players[i].mo->z > actor->z + actor->height - 2*FRACUNIT
+			&& players[i].mo->z < actor->z + actor->height + 32*FRACUNIT)
+		{
+			// Punch him!
+			P_SetMobjState(actor, actor->info->meleestate);
+			S_StartSound(0, sfx_begrnd); // warning sound
+			return;
+		}
+	}
+
+	if (actor->health <= actor->info->damage
+		&& actor->target
+		&& actor->target->player
+		&& (actor->target->player->powers[pw_carry] == CR_GENERIC))
+	{
+		A_FaceTarget(actor);
+		P_SetMobjState(actor, S_BLACKEGG_SHOOT1);
+		actor->movecount = TICRATE + P_RandomByte()/2;
+		return;
+	}
+
+	if (actor->reactiontime)
+		actor->reactiontime--;
+
+	if (actor->reactiontime <= 0 && actor->z == actor->floorz)
+	{
+		// Here, we'll call P_RandomByte() and decide what kind of attack to do
+		switch(actor->threshold)
+		{
+			case 0: // Lob cannon balls
+				if (actor->z < 1056*FRACUNIT)
+				{
+					A_FaceTarget(actor);
+					P_SetMobjState(actor, actor->info->xdeathstate);
+					actor->movecount = 7*TICRATE + P_RandomByte();
+					break;
+				}
+				actor->threshold++;
+				/* FALLTHRU */
+			case 1: // Chaingun Goop
+				A_FaceTarget(actor);
+				P_SetMobjState(actor, S_BLACKEGG_SHOOT1);
+
+				if (actor->health > actor->info->damage)
+					actor->movecount = TICRATE + P_RandomByte()/3;
+				else
+					actor->movecount = TICRATE + P_RandomByte()/2;
+				break;
+			case 2: // Homing Missile
+				A_FaceTarget(actor);
+				P_SetMobjState(actor, actor->info->missilestate);
+				S_StartSound(0, sfx_beflap);
+				break;
+		}
+
+		actor->threshold++;
+		actor->threshold %= 3;
+		return;
+	}
+
+	// possibly choose another target
+	if (multiplayer && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
+		&& P_BossTargetPlayer(actor, false))
+		return; // got a new target
+
+	if (leveltime & 1)
+	{
+		// chase towards player
+		if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
+			P_NewChaseDir(actor);
+	}
+}
+
+// Function: A_GoopSplat
+//
+// Description: Black Eggman goop hits a target and sticks around for awhile.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_GoopSplat(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_GoopSplat", actor))
+		return;
+#endif
+	P_UnsetThingPosition(actor);
+	if (sector_list)
+	{
+		P_DelSeclist(sector_list);
+		sector_list = NULL;
+	}
+	actor->flags = MF_SPECIAL; // Not a typo
+	P_SetThingPosition(actor);
+}
+
+// Function: A_Boss2PogoSFX
+//
+// Description: Pogoing for Boss 2
+//
+// var1 = pogo jump strength
+// var2 = idle pogo speed
+//
+void A_Boss2PogoSFX(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Boss2PogoSFX", actor))
+		return;
+#endif
+
+	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+	{
+		// look for a new target
+		if (P_LookForPlayers(actor, true, false, 0))
+			return; // got a new target
+
+		return;
+	}
+
+	// Boing!
+	if (P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) < FixedMul(256*FRACUNIT, actor->scale))
+	{
+		actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+		P_InstaThrust(actor, actor->angle, FixedMul(actor->info->speed, actor->scale));
+		// pogo on player
+	}
+	else
+	{
+		UINT8 prandom = P_RandomByte();
+		actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom);
+		P_InstaThrust(actor, actor->angle, FixedMul(FixedMul(actor->info->speed,(locvar2)), actor->scale));
+	}
+	if (actor->info->activesound) S_StartSound(actor, actor->info->activesound);
+	actor->momz = FixedMul(locvar1, actor->scale); // Bounce up in air
+	actor->reactiontime = 1;
+}
+
+// Function: A_Boss2PogoTarget
+//
+// Description: Pogoing for Boss 2, tries to actually land on the player directly.
+//
+// var1 = pogo jump strength
+// var2 = idle pogo speed
+//
+void A_Boss2PogoTarget(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Boss2PogoTarget", actor))
+		return;
+#endif
+
+	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE) || (actor->target->player && actor->target->player->powers[pw_flashing])
+	|| P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) >= FixedMul(512*FRACUNIT, actor->scale))
+	{
+		// look for a new target
+		if (P_LookForPlayers(actor, true, false, 512*FRACUNIT))
+			; // got a new target
+		else if (P_LookForPlayers(actor, true, false, 0))
+			; // got a new target
+		else
+			return;
+	}
+
+	// Target hit, retreat!
+	if (actor->target->player->powers[pw_flashing] > TICRATE || actor->flags2 & MF2_FRET)
+	{
+		UINT8 prandom = P_RandomByte();
+		actor->z++; // unstick from the floor
+		actor->momz = FixedMul(locvar1, actor->scale); // Bounce up in air
+		actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom); // Pick a direction, and randomize it.
+		P_InstaThrust(actor, actor->angle+ANGLE_180, FixedMul(FixedMul(actor->info->speed,(locvar2)), actor->scale)); // Move at wandering speed
+	}
+	// Try to land on top of the player.
+	else if (P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) < FixedMul(512*FRACUNIT, actor->scale))
+	{
+		fixed_t airtime, gravityadd, zoffs;
+
+		// check gravity in the sector (for later math)
+		P_CheckGravity(actor, true);
+		gravityadd = actor->momz;
+
+		actor->z++; // unstick from the floor
+		actor->momz = FixedMul(locvar1 + (locvar1>>2), actor->scale); // Bounce up in air
+
+		/*badmath = 0;
+		airtime = 0;
+		do {
+			badmath += momz;
+			momz += gravityadd;
+			airtime++;
+		} while(badmath > 0);
+		airtime = 2*airtime<<FRACBITS;
+		*/
+
+		// Remember, kids!
+		// Reduced down Calculus lets you avoid bad 'logic math' loops!
+		//airtime = FixedDiv(-actor->momz<<1, gravityadd)<<1; // going from 0 to 0 is much simpler
+		zoffs = (P_GetPlayerHeight(actor->target->player)>>1) + (actor->target->floorz - actor->floorz); // offset by the difference in floor height plus half the player height,
+		airtime = FixedDiv((-actor->momz - FixedSqrt(FixedMul(actor->momz,actor->momz)+zoffs)), gravityadd)<<1; // to try and land on their head rather than on their feet
+
+		actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+		P_InstaThrust(actor, actor->angle, FixedDiv(P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y), airtime));
+	}
+	// Wander semi-randomly towards the player to get closer.
+	else
+	{
+		UINT8 prandom = P_RandomByte();
+		actor->z++; // unstick from the floor
+		actor->momz = FixedMul(locvar1, actor->scale); // Bounce up in air
+		actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom); // Pick a direction, and randomize it.
+		P_InstaThrust(actor, actor->angle, FixedMul(FixedMul(actor->info->speed,(locvar2)), actor->scale)); // Move at wandering speed
+	}
+	// Boing!
+	if (actor->info->activesound) S_StartSound(actor, actor->info->activesound);
+
+	if (actor->info->missilestate) // spawn the pogo stick collision box
+	{
+		mobj_t *pogo = P_SpawnMobj(actor->x, actor->y, actor->z - mobjinfo[actor->info->missilestate].height, (mobjtype_t)actor->info->missilestate);
+		pogo->target = actor;
+	}
+
+	actor->reactiontime = 1;
+}
+
+// Function: A_EggmanBox
+//
+// Description: Harms the player
+//
+// var1 = unused
+// var2 = unused
+//
+void A_EggmanBox(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_EggmanBox", actor))
+		return;
+#endif
+	if (!actor->target || !actor->target->player)
+	{
+		CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
+		return;
+	}
+
+	P_DamageMobj(actor->target, actor, actor, 1, 0); // Ow!
+}
+
+// Function: A_TurretFire
+//
+// Description: Initiates turret fire.
+//
+// var1 = object # to repeatedly fire
+// var2 = distance threshold
+//
+void A_TurretFire(mobj_t *actor)
+{
+	INT32 count = 0;
+	fixed_t dist;
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_TurretFire", actor))
+		return;
+#endif
+
+	if (locvar2)
+		dist = FixedMul(locvar2*FRACUNIT, actor->scale);
+	else
+		dist = FixedMul(2048*FRACUNIT, actor->scale);
+
+	if (!locvar1)
+		locvar1 = MT_TURRETLASER;
+
+	while (P_SupermanLook4Players(actor) && count < MAXPLAYERS)
+	{
+		if (P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y) < dist)
+		{
+			actor->flags2 |= MF2_FIRING;
+			actor->extravalue1 = locvar1;
+			break;
+		}
+
+		count++;
+	}
+}
+
+// Function: A_SuperTurretFire
+//
+// Description: Initiates turret fire that even stops Super Sonic.
+//
+// var1 = object # to repeatedly fire
+// var2 = distance threshold
+//
+void A_SuperTurretFire(mobj_t *actor)
+{
+	INT32 count = 0;
+	fixed_t dist;
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SuperTurretFire", actor))
+		return;
+#endif
+
+	if (locvar2)
+		dist = FixedMul(locvar2*FRACUNIT, actor->scale);
+	else
+		dist = FixedMul(2048*FRACUNIT, actor->scale);
+
+	if (!locvar1)
+		locvar1 = MT_TURRETLASER;
+
+	while (P_SupermanLook4Players(actor) && count < MAXPLAYERS)
+	{
+		if (P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y) < dist)
+		{
+			actor->flags2 |= MF2_FIRING;
+			actor->flags2 |= MF2_SUPERFIRE;
+			actor->extravalue1 = locvar1;
+			break;
+		}
+
+		count++;
+	}
+}
+
+// Function: A_TurretStop
+//
+// Description: Stops the turret fire.
+//
+// var1 = Don't play activesound?
+// var2 = unused
+//
+void A_TurretStop(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_TurretStop", actor))
+		return;
+#endif
+
+	actor->flags2 &= ~MF2_FIRING;
+	actor->flags2 &= ~MF2_SUPERFIRE;
+
+	if (actor->target && actor->info->activesound && !locvar1)
+		S_StartSound(actor, actor->info->activesound);
+}
+
+// Function: A_SparkFollow
+//
+// Description: Used by the hyper sparks to rotate around their target.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_SparkFollow(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SparkFollow", actor))
+		return;
+#endif
+
+	if ((!actor->target || (actor->target->health <= 0))
+		|| (actor->target->player && !actor->target->player->powers[pw_super]))
+	{
+		P_RemoveMobj(actor);
+		return;
+	}
+
+	actor->angle += FixedAngle(actor->info->damage*FRACUNIT);
+	P_UnsetThingPosition(actor);
+	{
+		const angle_t fa = actor->angle>>ANGLETOFINESHIFT;
+		actor->x = actor->target->x + FixedMul(FINECOSINE(fa),FixedMul(actor->info->speed, actor->scale));
+		actor->y = actor->target->y + FixedMul(FINESINE(fa),FixedMul(actor->info->speed, actor->scale));
+		if (actor->target->eflags & MFE_VERTICALFLIP)
+			actor->z = actor->target->z + actor->target->height - FixedDiv(actor->target->height,3*FRACUNIT);
+		else
+			actor->z = actor->target->z + FixedDiv(actor->target->height,3*FRACUNIT) - actor->height;
+	}
+	P_SetThingPosition(actor);
+}
+
+// Function: A_BuzzFly
+//
+// Description: Makes an object slowly fly after a player, in the manner of a Buzz.
+//
+// var1 = sfx to play
+// var2 = length of sfx, set to threshold if played
+//
+void A_BuzzFly(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_BuzzFly", actor))
+		return;
+#endif
+	if (actor->flags2 & MF2_AMBUSH)
+		return;
+
+	if (actor->reactiontime)
+		actor->reactiontime--;
+
+	// modify target threshold
+	if (actor->threshold)
+	{
+		if (!actor->target || actor->target->health <= 0)
+			actor->threshold = 0;
+		else
+			actor->threshold--;
+	}
+
+	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+	{
+		// look for a new target
+		if (P_LookForPlayers(actor, true, false, 0))
+			return; // got a new target
+
+		actor->momz = actor->momy = actor->momx = 0;
+		P_SetMobjState(actor, actor->info->spawnstate);
+		return;
+	}
+
+	// turn towards movement direction if not there yet
+	actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+
+	if (actor->target->health <= 0 || (!actor->threshold && !P_CheckSight(actor, actor->target)))
+	{
+		if ((multiplayer || netgame) && P_LookForPlayers(actor, true, false, FixedMul(3072*FRACUNIT, actor->scale)))
+			return; // got a new target
+
+		actor->momx = actor->momy = actor->momz = 0;
+		P_SetMobjState(actor, actor->info->spawnstate); // Go back to looking around
+		return;
+	}
+
+	// If the player is over 3072 fracunits away, then look for another player
+	if (P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y),
+		actor->target->z - actor->z) > FixedMul(3072*FRACUNIT, actor->scale))
+	{
+		if (multiplayer || netgame)
+			P_LookForPlayers(actor, true, false, FixedMul(3072*FRACUNIT, actor->scale)); // maybe get a new target
+
+		return;
+	}
+
+	// chase towards player
+	{
+		INT32 dist, realspeed;
+		const fixed_t mf = 5*(FRACUNIT/4);
+
+		if (ultimatemode)
+			realspeed = FixedMul(FixedMul(actor->info->speed,mf), actor->scale);
+		else
+			realspeed = FixedMul(actor->info->speed, actor->scale);
+
+		dist = P_AproxDistance(P_AproxDistance(actor->target->x - actor->x,
+			actor->target->y - actor->y), actor->target->z - actor->z);
+
+		if (dist < 1)
+			dist = 1;
+
+		actor->momx = FixedMul(FixedDiv(actor->target->x - actor->x, dist), realspeed);
+		actor->momy = FixedMul(FixedDiv(actor->target->y - actor->y, dist), realspeed);
+		actor->momz = FixedMul(FixedDiv(actor->target->z - actor->z, dist), realspeed);
+
+		if (actor->z+actor->momz >= actor->waterbottom && actor->watertop > actor->floorz
+			&& actor->z+actor->momz > actor->watertop - FixedMul(256*FRACUNIT, actor->scale)
+			&& actor->z+actor->momz <= actor->watertop)
+		{
+			actor->momz = 0;
+			actor->z = actor->watertop;
+		}
+	}
+
+	if (locvar1 != sfx_None && !actor->threshold)
+	{
+		S_StartSound(actor, locvar1);
+		actor->threshold = locvar2;
+	}
+}
+
+// Function: A_GuardChase
+//
+// Description: Modified A_Chase for Egg Guard
+//
+// var1 = unused
+// var2 = unused
+//
+void A_GuardChase(mobj_t *actor)
+{
+	INT32 delta;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_GuardChase", actor))
+		return;
+#endif
+
+	if (actor->reactiontime)
+		actor->reactiontime--;
+
+	if (actor->threshold != 42) // In formation...
+	{
+		fixed_t speed;
+
+		if (!actor->tracer || !actor->tracer->health)
+		{
+			P_SetTarget(&actor->tracer, NULL);
+			actor->threshold = 42;
+			P_SetMobjState(actor, actor->info->painstate);
+			actor->flags |= MF_SPECIAL|MF_SHOOTABLE;
+			return;
+		}
+
+		speed = actor->extravalue1*actor->scale;
+
+		if (actor->flags2 & MF2_AMBUSH)
+			speed <<= 1;
+
+		if (speed
+		&& !P_TryMove(actor,
+			actor->x + P_ReturnThrustX(actor, actor->angle, speed),
+			actor->y + P_ReturnThrustY(actor, actor->angle, speed),
+			false)
+		&& speed > 0) // can't be the same check as previous so that P_TryMove gets to happen.
+		{
+			if (actor->spawnpoint && ((actor->spawnpoint->options & (MTF_EXTRA|MTF_OBJECTSPECIAL)) == MTF_OBJECTSPECIAL))
+				actor->angle += ANGLE_90;
+			else if (actor->spawnpoint && ((actor->spawnpoint->options & (MTF_EXTRA|MTF_OBJECTSPECIAL)) == MTF_EXTRA))
+				actor->angle -= ANGLE_90;
+			else
+				actor->angle += ANGLE_180;
+		}
+
+		if (actor->extravalue1 < actor->info->speed)
+			actor->extravalue1++;
+	}
+	else // Break ranks!
+	{
+		// turn towards movement direction if not there yet
+		if (actor->movedir < NUMDIRS)
+		{
+			actor->angle &= (7<<29);
+			delta = actor->angle - (actor->movedir << 29);
+
+			if (delta > 0)
+				actor->angle -= ANGLE_45;
+			else if (delta < 0)
+				actor->angle += ANGLE_45;
+		}
+
+		if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+		{
+			// look for a new target
+			if (P_LookForPlayers(actor, true, false, 0))
+				return; // got a new target
+
+			P_SetMobjStateNF(actor, actor->info->spawnstate);
+			return;
+		}
+
+		// possibly choose another target
+		if (multiplayer && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
+			&& P_LookForPlayers(actor, true, false, 0))
+			return; // got a new target
+
+		// chase towards player
+		if (--actor->movecount < 0 || !P_Move(actor, (actor->flags2 & MF2_AMBUSH) ? actor->info->speed * 2 : actor->info->speed))
+		{
+			P_NewChaseDir(actor);
+			actor->movecount += 5; // Increase tics before change in direction allowed.
+		}
+	}
+
+	// Now that we've moved, its time for our shield to move!
+	// Otherwise it'll never act as a proper overlay.
+	if (actor->tracer && actor->tracer->state
+	&& actor->tracer->state->action.acp1)
+	{
+		var1 = actor->tracer->state->var1, var2 = actor->tracer->state->var2;
+		actor->tracer->state->action.acp1(actor->tracer);
+	}
+}
+
+// Function: A_EggShield
+//
+// Description: Modified A_Chase for Egg Guard's shield
+//
+// var1 = unused
+// var2 = unused
+//
+void A_EggShield(mobj_t *actor)
+{
+	INT32 i;
+	player_t *player;
+	fixed_t blockdist;
+	fixed_t newx, newy;
+	fixed_t movex, movey;
+	angle_t angle;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_EggShield", actor))
+		return;
+#endif
+
+	if (!actor->target || !actor->target->health)
+	{
+		P_RemoveMobj(actor);
+		return;
+	}
+
+	newx = actor->target->x + P_ReturnThrustX(actor, actor->target->angle, FixedMul(FRACUNIT, actor->scale));
+	newy = actor->target->y + P_ReturnThrustY(actor, actor->target->angle, FixedMul(FRACUNIT, actor->scale));
+
+	movex = newx - actor->x;
+	movey = newy - actor->y;
+
+	actor->angle = actor->target->angle;
+	if (actor->target->eflags & MFE_VERTICALFLIP)
+	{
+		actor->eflags |= MFE_VERTICALFLIP;
+		actor->z = actor->target->z + actor->target->height - actor->height;
+	}
+	else
+		actor->z = actor->target->z;
+
+	actor->destscale = actor->target->destscale;
+	P_SetScale(actor, actor->target->scale);
+
+	actor->floorz = actor->target->floorz;
+	actor->ceilingz = actor->target->ceilingz;
+
+	if (!movex && !movey)
+		return;
+
+	P_UnsetThingPosition(actor);
+	actor->x = newx;
+	actor->y = newy;
+	P_SetThingPosition(actor);
+
+	// Search for players to push
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		if (!playeringame[i] || players[i].spectator)
+			continue;
+
+		player = &players[i];
+
+		if (!player->mo)
+			continue;
+
+		if (player->mo->z > actor->z + actor->height)
+			continue;
+
+		if (player->mo->z + player->mo->height < actor->z)
+			continue;
+
+		blockdist = actor->radius + player->mo->radius;
+
+		if (abs(actor->x - player->mo->x) >= blockdist || abs(actor->y - player->mo->y) >= blockdist)
+			continue; // didn't hit it
+
+		angle = R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y) - actor->angle;
+
+		if (angle > ANGLE_90 && angle < ANGLE_270)
+			continue;
+
+		// Blocked by the shield
+		player->mo->momx += movex;
+		player->mo->momy += movey;
+		return;
+	}
+}
+
+
+// Function: A_SetReactionTime
+//
+// Description: Sets the object's reaction time.
+//
+// var1 = 1 (use value in var2); 0 (use info table value)
+// var2 = if var1 = 1, then value to set
+//
+void A_SetReactionTime(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SetReactionTime", actor))
+		return;
+#endif
+	if (var1)
+		actor->reactiontime = var2;
+	else
+		actor->reactiontime = actor->info->reactiontime;
+}
+
+// Function: A_Boss1Spikeballs
+//
+// Description: Boss 1 spikeball spawning loop.
+//
+// var1 = ball number
+// var2 = total balls
+//
+void A_Boss1Spikeballs(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	mobj_t *ball;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Boss1Spikeballs", actor))
+		return;
+#endif
+
+	ball = P_SpawnMobj(actor->x, actor->y, actor->z, MT_EGGMOBILE_BALL);
+	P_SetTarget(&ball->target, actor);
+	ball->movedir = FixedAngle(FixedMul(FixedDiv(locvar1<<FRACBITS, locvar2<<FRACBITS), 360<<FRACBITS));
+	ball->threshold = ball->radius + actor->radius + ball->info->painchance;
+
+	S_StartSound(ball, ball->info->seesound);
+	var1 = ball->state->var1, var2 = ball->state->var2;
+	ball->state->action.acp1(ball);
+}
+
+// Function: A_Boss3TakeDamage
+//
+// Description: Called when Boss 3 takes damage.
+//
+// var1 = movecount value
+// var2 = unused
+//
+void A_Boss3TakeDamage(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Boss3TakeDamage", actor))
+		return;
+#endif
+	actor->movecount = var1;
+
+	if (actor->target && actor->target->spawnpoint)
+		actor->threshold = actor->target->spawnpoint->extrainfo;
+}
+
+// Function: A_Boss3Path
+//
+// Description: Does pathfinding along Boss 3's nodes.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_Boss3Path(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Boss3Path", actor))
+		return;
+#endif
+
+	if (actor->tracer && actor->tracer->health && actor->tracer->movecount)
+		actor->movecount |= 1;
+	else if (actor->movecount & 1)
+		actor->movecount = 0;
+
+	if (actor->movecount & 2) // We've reached a firing point?
+	{
+		// Wait here and pretend to be angry or something.
+		actor->momx = 0;
+		actor->momy = 0;
+		actor->momz = 0;
+		P_SetTarget(&actor->target, actor->tracer->target);
+		var1 = 0, var2 = 0;
+		A_FaceTarget(actor);
+		if (actor->tracer->state == &states[actor->tracer->info->missilestate])
+			P_SetMobjState(actor, actor->info->missilestate);
+		return;
+	}
+	else if (actor->threshold >= 0) // Traveling mode
+	{
+		thinker_t *th;
+		mobj_t *mo2;
+		fixed_t dist, dist2;
+		fixed_t speed;
+
+		P_SetTarget(&actor->target, NULL);
+
+		// scan the thinkers
+		// to find a point that matches
+		// the number
+		for (th = thinkercap.next; th != &thinkercap; th = th->next)
+		{
+			if (th->function.acp1 != (actionf_p1)P_MobjThinker)
+				continue;
+
+			mo2 = (mobj_t *)th;
+			if (mo2->type == MT_BOSS3WAYPOINT && mo2->spawnpoint && mo2->spawnpoint->angle == actor->threshold)
+			{
+				P_SetTarget(&actor->target, mo2);
+				break;
+			}
+		}
+
+		if (!actor->target) // Should NEVER happen
+		{
+			CONS_Debug(DBG_GAMELOGIC, "Error: Boss 3 Dummy was unable to find specified waypoint: %d\n", actor->threshold);
+			return;
+		}
+
+		dist = P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y), actor->target->z - actor->z);
+
+		if (dist < 1)
+			dist = 1;
+
+		if (actor->tracer && ((actor->tracer->movedir)
+		|| (actor->tracer->health <= actor->tracer->info->damage)))
+			speed = actor->info->speed * 2;
+		else
+			speed = actor->info->speed;
+
+		actor->momx = FixedMul(FixedDiv(actor->target->x - actor->x, dist), speed);
+		actor->momy = FixedMul(FixedDiv(actor->target->y - actor->y, dist), speed);
+		actor->momz = FixedMul(FixedDiv(actor->target->z - actor->z, dist), speed);
+
+		if (actor->momx != 0 || actor->momy != 0)
+			actor->angle = R_PointToAngle2(0, 0, actor->momx, actor->momy);
+
+		dist2 = P_AproxDistance(P_AproxDistance(actor->target->x - (actor->x + actor->momx), actor->target->y - (actor->y + actor->momy)), actor->target->z - (actor->z + actor->momz));
+
+		if (dist2 < 1)
+			dist2 = 1;
+
+		if ((dist >> FRACBITS) <= (dist2 >> FRACBITS))
+		{
+			// If further away, set XYZ of mobj to waypoint location
+			P_UnsetThingPosition(actor);
+			actor->x = actor->target->x;
+			actor->y = actor->target->y;
+			actor->z = actor->target->z;
+			actor->momx = actor->momy = actor->momz = 0;
+			P_SetThingPosition(actor);
+
+			if (actor->threshold == 0)
+			{
+				P_RemoveMobj(actor); // Cycle completed. Dummy removed.
+				return;
+			}
+
+			// Set to next waypoint in sequence
+			if (actor->target->spawnpoint)
+			{
+				// From the center point, choose one of the five paths
+				if (actor->target->spawnpoint->angle == 0)
+				{
+					P_RemoveMobj(actor); // Cycle completed. Dummy removed.
+					return;
+				}
+				else
+					actor->threshold = actor->target->spawnpoint->extrainfo;
+
+				// If the deaf flag is set, go into firing mode
+				if (actor->target->spawnpoint->options & MTF_AMBUSH)
+					actor->movecount |= 2;
+			}
+			else // This should never happen, as well
+				CONS_Debug(DBG_GAMELOGIC, "Error: Boss 3 Dummy waypoint has no spawnpoint associated with it.\n");
+		}
+	}
+}
+
+// Function: A_LinedefExecute
+//
+// Description: Object's location is used to set the calling sector. The tag used is var1. Optionally, if var2 is set, the actor's angle (multiplied by var2) is added to the tag number as well.
+//
+// var1 = tag
+// var2 = add angle to tag (optional)
+//
+void A_LinedefExecute(mobj_t *actor)
+{
+	INT32 tagnum;
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_LinedefExecute", actor))
+		return;
+#endif
+
+	tagnum = locvar1;
+	// state numbers option is no more, custom states cannot be guaranteed to stay the same number anymore, now that they can be defined by names instead
+
+	if (locvar2)
+		tagnum += locvar2*(AngleFixed(actor->angle)>>FRACBITS);
+
+	CONS_Debug(DBG_GAMELOGIC, "A_LinedefExecute: Running mobjtype %d's sector with tag %d\n", actor->type, tagnum);
+
+	// tag 32768 displayed in map editors is actually tag -32768, tag 32769 is -32767, 65535 is -1 etc.
+	P_LinedefExecute((INT16)tagnum, actor, actor->subsector->sector);
+}
+
+// Function: A_PlaySeeSound
+//
+// Description: Plays the object's seesound.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_PlaySeeSound(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_PlaySeeSound", actor))
+		return;
+#endif
+	if (actor->info->seesound)
+		S_StartScreamSound(actor, actor->info->seesound);
+}
+
+// Function: A_PlayAttackSound
+//
+// Description: Plays the object's attacksound.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_PlayAttackSound(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_PlayAttackSound", actor))
+		return;
+#endif
+	if (actor->info->attacksound)
+		S_StartAttackSound(actor, actor->info->attacksound);
+}
+
+// Function: A_PlayActiveSound
+//
+// Description: Plays the object's activesound.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_PlayActiveSound(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_PlayActiveSound", actor))
+		return;
+#endif
+	if (actor->info->activesound)
+		S_StartSound(actor, actor->info->activesound);
+}
+
+// Function: A_SmokeTrailer
+//
+// Description: Adds smoke trails to an object.
+//
+// var1 = object # to spawn as smoke
+// var2 = unused
+//
+void A_SmokeTrailer(mobj_t *actor)
+{
+	mobj_t *th;
+	INT32 locvar1 = var1;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SmokeTrailer", actor))
+		return;
+#endif
+
+	if (leveltime % 4)
+		return;
+
+	// add the smoke behind the rocket
+	if (actor->eflags & MFE_VERTICALFLIP)
+	{
+		th = P_SpawnMobj(actor->x-actor->momx, actor->y-actor->momy, actor->z + actor->height - FixedMul(mobjinfo[locvar1].height, actor->scale), locvar1);
+		th->flags2 |= MF2_OBJECTFLIP;
+	}
+	else
+		th = P_SpawnMobj(actor->x-actor->momx, actor->y-actor->momy, actor->z, locvar1);
+	P_SetObjectMomZ(th, FRACUNIT, false);
+	th->destscale = actor->scale;
+	P_SetScale(th, actor->scale);
+	th->tics -= P_RandomByte() & 3;
+	if (th->tics < 1)
+		th->tics = 1;
+}
+
+// Function: A_SpawnObjectAbsolute
+//
+// Description: Spawns an object at an absolute position
+//
+// var1:
+//		var1 >> 16 = x
+//		var1 & 65535 = y
+// var2:
+//		var2 >> 16 = z
+//		var2 & 65535 = type
+//
+void A_SpawnObjectAbsolute(mobj_t *actor)
+{
+	INT16 x, y, z; // Want to be sure we can use negative values
+	mobjtype_t type;
+	mobj_t *mo;
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SpawnObjectAbsolute", actor))
+		return;
+#endif
+
+	x = (INT16)(locvar1>>16);
+	y = (INT16)(locvar1&65535);
+	z = (INT16)(locvar2>>16);
+	type = (mobjtype_t)(locvar2&65535);
+
+	mo = P_SpawnMobj(x<<FRACBITS, y<<FRACBITS, z<<FRACBITS, type);
+
+	// Spawn objects with an angle matching the spawner's, rather than spawning Eastwards - Monster Iestyn
+	mo->angle = actor->angle;
+
+	if (actor->eflags & MFE_VERTICALFLIP)
+		mo->flags2 |= MF2_OBJECTFLIP;
+}
+
+// Function: A_SpawnObjectRelative
+//
+// Description: Spawns an object relative to the location of the actor
+//
+// var1:
+//		var1 >> 16 = x
+//		var1 & 65535 = y
+// var2:
+//		var2 >> 16 = z
+//		var2 & 65535 = type
+//
+void A_SpawnObjectRelative(mobj_t *actor)
+{
+	INT16 x, y, z; // Want to be sure we can use negative values
+	mobjtype_t type;
+	mobj_t *mo;
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SpawnObjectRelative", actor))
+		return;
+#endif
+
+	CONS_Debug(DBG_GAMELOGIC, "A_SpawnObjectRelative called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
+
+	x = (INT16)(locvar1>>16);
+	y = (INT16)(locvar1&65535);
+	z = (INT16)(locvar2>>16);
+	type = (mobjtype_t)(locvar2&65535);
+
+	// Spawn objects correctly in reverse gravity.
+	// NOTE: Doing actor->z + actor->height is the bottom of the object while the object has reverse gravity. - Flame
+	mo = P_SpawnMobj(actor->x + FixedMul(x<<FRACBITS, actor->scale),
+		actor->y + FixedMul(y<<FRACBITS, actor->scale),
+		(actor->eflags & MFE_VERTICALFLIP) ? ((actor->z + actor->height - mobjinfo[type].height) - FixedMul(z<<FRACBITS, actor->scale)) : (actor->z + FixedMul(z<<FRACBITS, actor->scale)), type);
+
+	// Spawn objects with an angle matching the spawner's, rather than spawning Eastwards - Monster Iestyn
+	mo->angle = actor->angle;
+
+	if (actor->eflags & MFE_VERTICALFLIP)
+		mo->flags2 |= MF2_OBJECTFLIP;
+
+}
+
+// Function: A_ChangeAngleRelative
+//
+// Description: Changes the angle to a random relative value between the min and max. Set min and max to the same value to eliminate randomness
+//
+// var1 = min
+// var2 = max
+//
+void A_ChangeAngleRelative(mobj_t *actor)
+{
+	// Oh god, the old code /sucked/. Changed this and the absolute version to get a random range using amin and amax instead of
+	//  getting a random angle from the _entire_ spectrum and then clipping. While we're at it, do the angle conversion to the result
+	//  rather than the ranges, so <0 and >360 work as possible values. -Red
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	//angle_t angle = (P_RandomByte()+1)<<24;
+	const fixed_t amin = locvar1*FRACUNIT;
+	const fixed_t amax = locvar2*FRACUNIT;
+	//const angle_t amin = FixedAngle(locvar1*FRACUNIT);
+	//const angle_t amax = FixedAngle(locvar2*FRACUNIT);
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_ChangeAngleRelative", actor))
+		return;
+#endif
+
+#ifdef PARANOIA
+	if (amin > amax)
+		I_Error("A_ChangeAngleRelative: var1 is greater then var2");
+#endif
+/*
+	if (angle < amin)
+		angle = amin;
+	if (angle > amax)
+		angle = amax;*/
+
+	actor->angle += FixedAngle(P_RandomRange(amin, amax));
+}
+
+// Function: A_ChangeAngleAbsolute
+//
+// Description: Changes the angle to a random absolute value between the min and max. Set min and max to the same value to eliminate randomness
+//
+// var1 = min
+// var2 = max
+//
+void A_ChangeAngleAbsolute(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	//angle_t angle = (P_RandomByte()+1)<<24;
+	const fixed_t amin = locvar1*FRACUNIT;
+	const fixed_t amax = locvar2*FRACUNIT;
+	//const angle_t amin = FixedAngle(locvar1*FRACUNIT);
+	//const angle_t amax = FixedAngle(locvar2*FRACUNIT);
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_ChangeAngleAbsolute", actor))
+		return;
+#endif
+
+#ifdef PARANOIA
+	if (amin > amax)
+		I_Error("A_ChangeAngleAbsolute: var1 is greater then var2");
+#endif
+/*
+	if (angle < amin)
+		angle = amin;
+	if (angle > amax)
+		angle = amax;*/
+
+	actor->angle = FixedAngle(P_RandomRange(amin, amax));
+}
+
+// Function: A_PlaySound
+//
+// Description: Plays a sound
+//
+// var1 = sound # to play
+// var2:
+//		0 = Play sound without an origin
+//		1 = Play sound using calling object as origin
+//
+void A_PlaySound(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_PlaySound", actor))
+		return;
+#endif
+
+	S_StartSound(locvar2 ? actor : NULL, locvar1);
+}
+
+// Function: A_FindTarget
+//
+// Description: Finds the nearest/furthest mobj of the specified type and sets actor->target to it.
+//
+// var1 = mobj type
+// var2 = if (0) nearest; else furthest;
+//
+void A_FindTarget(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	mobj_t *targetedmobj = NULL;
+	thinker_t *th;
+	mobj_t *mo2;
+	fixed_t dist1 = 0, dist2 = 0;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_FindTarget", actor))
+		return;
+#endif
+
+	CONS_Debug(DBG_GAMELOGIC, "A_FindTarget called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
+
+	// scan the thinkers
+	for (th = thinkercap.next; th != &thinkercap; th = th->next)
+	{
+		if (th->function.acp1 != (actionf_p1)P_MobjThinker)
+			continue;
+
+		mo2 = (mobj_t *)th;
+
+		if (mo2->type == (mobjtype_t)locvar1)
+		{
+			if (mo2->player && (mo2->player->spectator || mo2->player->pflags & PF_INVIS))
+				continue; // Ignore spectators
+			if ((mo2->player || mo2->flags & MF_ENEMY) && mo2->health <= 0)
+				continue; // Ignore dead things
+			if (targetedmobj == NULL)
+			{
+				targetedmobj = mo2;
+				dist2 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
+			}
+			else
+			{
+				dist1 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
+
+				if ((!locvar2 && dist1 < dist2) || (locvar2 && dist1 > dist2))
+				{
+					targetedmobj = mo2;
+					dist2 = dist1;
+				}
+			}
+		}
+	}
+
+	if (!targetedmobj)
+	{
+		CONS_Debug(DBG_GAMELOGIC, "A_FindTarget: Unable to find the specified object to target.\n");
+		return; // Oops, nothing found..
+	}
+
+	CONS_Debug(DBG_GAMELOGIC, "A_FindTarget: Found a target.\n");
+
+	P_SetTarget(&actor->target, targetedmobj);
+}
+
+// Function: A_FindTracer
+//
+// Description: Finds the nearest/furthest mobj of the specified type and sets actor->tracer to it.
+//
+// var1 = mobj type
+// var2 = if (0) nearest; else furthest;
+//
+void A_FindTracer(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	mobj_t *targetedmobj = NULL;
+	thinker_t *th;
+	mobj_t *mo2;
+	fixed_t dist1 = 0, dist2 = 0;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_FindTracer", actor))
+		return;
+#endif
+
+	CONS_Debug(DBG_GAMELOGIC, "A_FindTracer called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
+
+	// scan the thinkers
+	for (th = thinkercap.next; th != &thinkercap; th = th->next)
+	{
+		if (th->function.acp1 != (actionf_p1)P_MobjThinker)
+			continue;
+
+		mo2 = (mobj_t *)th;
+
+		if (mo2->type == (mobjtype_t)locvar1)
+		{
+			if (mo2->player && (mo2->player->spectator || mo2->player->pflags & PF_INVIS))
+				continue; // Ignore spectators
+			if ((mo2->player || mo2->flags & MF_ENEMY) && mo2->health <= 0)
+				continue; // Ignore dead things
+			if (targetedmobj == NULL)
+			{
+				targetedmobj = mo2;
+				dist2 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
+			}
+			else
+			{
+				dist1 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
+
+				if ((!locvar2 && dist1 < dist2) || (locvar2 && dist1 > dist2))
+				{
+					targetedmobj = mo2;
+					dist2 = dist1;
+				}
+			}
+		}
+	}
+
+	if (!targetedmobj)
+	{
+		CONS_Debug(DBG_GAMELOGIC, "A_FindTracer: Unable to find the specified object to target.\n");
+		return; // Oops, nothing found..
+	}
+
+	CONS_Debug(DBG_GAMELOGIC, "A_FindTracer: Found a target.\n");
+
+	P_SetTarget(&actor->tracer, targetedmobj);
+}
+
+// Function: A_SetTics
+//
+// Description: Sets the animation tics of an object
+//
+// var1 = tics to set to
+// var2 = if this is set, and no var1 is supplied, the mobj's threshold value will be used.
+//
+void A_SetTics(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SetTics", actor))
+		return;
+#endif
+
+	if (locvar1)
+		actor->tics = locvar1;
+	else if (locvar2)
+		actor->tics = actor->threshold;
+}
+
+// Function: A_SetRandomTics
+//
+// Description: Sets the animation tics of an object to a random value
+//
+// var1 = lower bound
+// var2 = upper bound
+//
+void A_SetRandomTics(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SetRandomTics", actor))
+		return;
+#endif
+
+	actor->tics = P_RandomRange(locvar1, locvar2);
+}
+
+// Function: A_ChangeColorRelative
+//
+// Description: Changes the color of an object
+//
+// var1 = if (var1 > 0), find target and add its color value to yours
+// var2 = if (var1 = 0), color value to add
+//
+void A_ChangeColorRelative(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_ChangeColorRelative", actor))
+		return;
+#endif
+
+	if (locvar1)
+	{
+		// Have you ever seen anything so hideous?
+		if (actor->target)
+			actor->color = (UINT8)(actor->color + actor->target->color);
+	}
+	else
+		actor->color = (UINT8)(actor->color + locvar2);
+}
+
+// Function: A_ChangeColorAbsolute
+//
+// Description: Changes the color of an object by an absolute value. Note: 0 is default colormap.
+//
+// var1 = if (var1 > 0), set your color to your target's color
+// var2 = if (var1 = 0), color value to set to
+//
+void A_ChangeColorAbsolute(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_ChangeColorAbsolute", actor))
+		return;
+#endif
+
+	if (locvar1)
+	{
+		if (actor->target)
+			actor->color = actor->target->color;
+	}
+	else
+		actor->color = (UINT8)locvar2;
+}
+
+// Function: A_MoveRelative
+//
+// Description: Moves an object (wrapper for P_Thrust)
+//
+// var1 = angle
+// var2 = force
+//
+void A_MoveRelative(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_MoveRelative", actor))
+		return;
+#endif
+
+	P_Thrust(actor, actor->angle+FixedAngle(locvar1*FRACUNIT), FixedMul(locvar2*FRACUNIT, actor->scale));
+}
+
+// Function: A_MoveAbsolute
+//
+// Description: Moves an object (wrapper for P_InstaThrust)
+//
+// var1 = angle
+// var2 = force
+//
+void A_MoveAbsolute(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_MoveAbsolute", actor))
+		return;
+#endif
+
+	P_InstaThrust(actor, FixedAngle(locvar1*FRACUNIT), FixedMul(locvar2*FRACUNIT, actor->scale));
+}
+
+// Function: A_Thrust
+//
+// Description: Pushes the object horizontally at its current angle.
+//
+// var1 = amount of force
+// var2 = If 1, xy momentum is lost. If 0, xy momentum is kept
+//
+void A_Thrust(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Thrust", actor))
+		return;
+#endif
+
+	if (!locvar1)
+		CONS_Debug(DBG_GAMELOGIC, "A_Thrust: Var1 not specified!\n");
+
+	if (locvar2)
+		P_InstaThrust(actor, actor->angle, FixedMul(locvar1*FRACUNIT, actor->scale));
+	else
+		P_Thrust(actor, actor->angle, FixedMul(locvar1*FRACUNIT, actor->scale));
+}
+
+// Function: A_ZThrust
+//
+// Description: Pushes the object up or down.
+//
+// var1 = amount of force
+// var2:
+//		lower 16 bits = If 1, xy momentum is lost. If 0, xy momentum is kept
+//		upper 16 bits = If 1, z momentum is lost. If 0, z momentum is kept
+//
+void A_ZThrust(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_ZThrust", actor))
+		return;
+#endif
+
+	if (!locvar1)
+		CONS_Debug(DBG_GAMELOGIC, "A_ZThrust: Var1 not specified!\n");
+
+	if (locvar2 & 65535)
+		actor->momx = actor->momy = 0;
+
+	if (actor->eflags & MFE_VERTICALFLIP)
+		actor->z--;
+	else
+		actor->z++;
+
+	P_SetObjectMomZ(actor, locvar1*FRACUNIT, !(locvar2 >> 16));
+}
+
+// Function: A_SetTargetsTarget
+//
+// Description: Sets your target to the object who your target is targeting. Yikes! If it happens to be NULL, you're just out of luck.
+//
+// var1: (Your target)
+//		0 = target
+//		1 = tracer
+// var2: (Your target's target)
+//		0 = target/tracer's target
+//		1 = target/tracer's tracer
+//
+void A_SetTargetsTarget(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	mobj_t *oldtarg = NULL, *newtarg = NULL;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SetTargetsTarget", actor))
+		return;
+#endif
+
+	// actor's target
+	if (locvar1) // or tracer
+		oldtarg = actor->tracer;
+	else
+		oldtarg = actor->target;
+
+	if (P_MobjWasRemoved(oldtarg))
+		return;
+
+	// actor's target's target!
+	if (locvar2) // or tracer
+		newtarg = oldtarg->tracer;
+	else
+		newtarg = oldtarg->target;
+
+	if (P_MobjWasRemoved(newtarg))
+		return;
+
+	// set actor's new target
+	if (locvar1) // or tracer
+		P_SetTarget(&actor->tracer, newtarg);
+	else
+		P_SetTarget(&actor->target, newtarg);
+}
+
+// Function: A_SetObjectFlags
+//
+// Description: Sets the flags of an object
+//
+// var1 = flag value to set
+// var2:
+//		if var2 == 2, add the flag to the current flags
+//		else if var2 == 1, remove the flag from the current flags
+//		else if var2 == 0, set the flags to the exact value
+//
+void A_SetObjectFlags(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	boolean unlinkthings = false;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SetObjectFlags", actor))
+		return;
+#endif
+
+	if (locvar2 == 2)
+		locvar1 = actor->flags | locvar1;
+	else if (locvar2 == 1)
+		locvar1 = actor->flags & ~locvar1;
+
+	if ((UINT32)(locvar1 & (MF_NOBLOCKMAP|MF_NOSECTOR)) != (actor->flags & (MF_NOBLOCKMAP|MF_NOSECTOR))) // Blockmap/sector status has changed, so reset the links
+		unlinkthings = true;
+
+	if (unlinkthings) {
+		P_UnsetThingPosition(actor);
+		if (sector_list)
+		{
+			P_DelSeclist(sector_list);
+			sector_list = NULL;
+		}
+	}
+
+	actor->flags = locvar1;
+
+	if (unlinkthings)
+		P_SetThingPosition(actor);
+}
+
+// Function: A_SetObjectFlags2
+//
+// Description: Sets the flags2 of an object
+//
+// var1 = flag value to set
+// var2:
+//		if var2 == 2, add the flag to the current flags
+//		else if var2 == 1, remove the flag from the current flags
+//		else if var2 == 0, set the flags to the exact value
+//
+void A_SetObjectFlags2(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SetObjectFlags2", actor))
+		return;
+#endif
+
+	if (locvar2 == 2)
+		actor->flags2 |= locvar1;
+	else if (locvar2 == 1)
+		actor->flags2 &= ~locvar1;
+	else
+		actor->flags2 = locvar1;
+}
+
+// Function: A_BossJetFume
+//
+// Description: Spawns jet fumes/other attachment miscellany for the boss. To only be used when he is spawned.
+//
+// var1:
+//		0 - Triple jet fume pattern
+//		1 - Boss 3's propeller
+//		2 - Metal Sonic jet fume
+//		3 - Boss 4 jet flame
+// var2 = unused
+//
+void A_BossJetFume(mobj_t *actor)
+{
+	mobj_t *filler;
+	INT32 locvar1 = var1;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_BossJetFume", actor))
+		return;
+#endif
+
+	if (locvar1 == 0) // Boss1 jet fumes
+	{
+		fixed_t jetx, jety, jetz;
+
+		jetx = actor->x + P_ReturnThrustX(actor, actor->angle, -FixedMul(64*FRACUNIT, actor->scale));
+		jety = actor->y + P_ReturnThrustY(actor, actor->angle, -FixedMul(64*FRACUNIT, actor->scale));
+		if (actor->eflags & MFE_VERTICALFLIP)
+			jetz = actor->z + actor->height - FixedMul(38*FRACUNIT + mobjinfo[MT_JETFUME1].height, actor->scale);
+		else
+			jetz = actor->z + FixedMul(38*FRACUNIT, actor->scale);
+
+		filler = P_SpawnMobj(jetx, jety, jetz, MT_JETFUME1);
+		P_SetTarget(&filler->target, actor);
+		filler->destscale = actor->scale;
+		P_SetScale(filler, filler->destscale);
+		if (actor->eflags & MFE_VERTICALFLIP)
+			filler->flags2 |= MF2_OBJECTFLIP;
+		filler->fuse = 56;
+
+		if (actor->eflags & MFE_VERTICALFLIP)
+			jetz = actor->z + actor->height - FixedMul(12*FRACUNIT + mobjinfo[MT_JETFUME1].height, actor->scale);
+		else
+			jetz = actor->z + FixedMul(12*FRACUNIT, actor->scale);
+
+		filler = P_SpawnMobj(jetx + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)),
+				jety + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)),
+				jetz, MT_JETFUME1);
+		P_SetTarget(&filler->target, actor);
+		filler->destscale = actor->scale;
+		P_SetScale(filler, filler->destscale);
+		if (actor->eflags & MFE_VERTICALFLIP)
+			filler->flags2 |= MF2_OBJECTFLIP;
+		filler->fuse = 57;
+
+		filler = P_SpawnMobj(jetx + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)),
+				jety + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)),
+				jetz, MT_JETFUME1);
+		P_SetTarget(&filler->target, actor);
+		filler->destscale = actor->scale;
+		P_SetScale(filler, filler->destscale);
+		if (actor->eflags & MFE_VERTICALFLIP)
+			filler->flags2 |= MF2_OBJECTFLIP;
+		filler->fuse = 58;
+
+		P_SetTarget(&actor->tracer, filler);
+	}
+	else if (locvar1 == 1) // Boss 3 propeller
+	{
+		fixed_t jetx, jety, jetz;
+
+		jetx = actor->x + P_ReturnThrustX(actor, actor->angle, -FixedMul(60*FRACUNIT, actor->scale));
+		jety = actor->y + P_ReturnThrustY(actor, actor->angle, -FixedMul(60*FRACUNIT, actor->scale));
+		if (actor->eflags & MFE_VERTICALFLIP)
+			jetz = actor->z + actor->height - FixedMul(17*FRACUNIT + mobjinfo[MT_PROPELLER].height, actor->scale);
+		else
+			jetz = actor->z + FixedMul(17*FRACUNIT, actor->scale);
+
+		filler = P_SpawnMobj(jetx, jety, jetz, MT_PROPELLER);
+		P_SetTarget(&filler->target, actor);
+		filler->destscale = actor->scale;
+		P_SetScale(filler, filler->destscale);
+		if (actor->eflags & MFE_VERTICALFLIP)
+			filler->flags2 |= MF2_OBJECTFLIP;
+		filler->angle = actor->angle - ANGLE_180;
+
+		P_SetTarget(&actor->tracer, filler);
+	}
+	else if (locvar1 == 2) // Metal Sonic jet fumes
+	{
+		filler = P_SpawnMobj(actor->x, actor->y, actor->z, MT_JETFUME1);
+		P_SetTarget(&filler->target, actor);
+		filler->fuse = 59;
+		P_SetTarget(&actor->tracer, filler);
+		filler->destscale = actor->scale/2;
+		P_SetScale(filler, filler->destscale);
+		if (actor->eflags & MFE_VERTICALFLIP)
+			filler->flags2 |= MF2_OBJECTFLIP;
+	}
+	else if (locvar1 == 3) // Boss 4 jet flame
+	{
+		fixed_t jetz;
+		if (actor->eflags & MFE_VERTICALFLIP)
+			jetz = actor->z + actor->height + FixedMul(50*FRACUNIT - mobjinfo[MT_JETFLAME].height, actor->scale);
+		else
+			jetz = actor->z - FixedMul(50*FRACUNIT, actor->scale);
+		filler = P_SpawnMobj(actor->x, actor->y, jetz, MT_JETFLAME);
+		P_SetTarget(&filler->target, actor);
+		// Boss 4 already uses its tracer for other things
+		filler->destscale = actor->scale;
+		P_SetScale(filler, filler->destscale);
+		if (actor->eflags & MFE_VERTICALFLIP)
+			filler->flags2 |= MF2_OBJECTFLIP;
+	}
+}
+
+// Function: A_RandomState
+//
+// Description: Chooses one of the two state numbers supplied randomly.
+//
+// var1 = state number 1
+// var2 = state number 2
+//
+void A_RandomState(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_RandomState", actor))
+		return;
+#endif
+
+	P_SetMobjState(actor, P_RandomChance(FRACUNIT/2) ? locvar1 : locvar2);
+}
+
+// Function: A_RandomStateRange
+//
+// Description: Chooses a random state within the range supplied.
+//
+// var1 = Minimum state number to choose.
+// var2 = Maximum state number to use.
+//
+void A_RandomStateRange(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_RandomStateRange", actor))
+		return;
+#endif
+
+	P_SetMobjState(actor, P_RandomRange(locvar1, locvar2));
+}
+
+// Function: A_DualAction
+//
+// Description: Calls two actions. Be careful, if you reference the same state this action is called from, you can create an infinite loop.
+//
+// var1 = state # to use 1st action from
+// var2 = state # to use 2nd action from
+//
+void A_DualAction(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_DualAction", actor))
+		return;
+#endif
+
+	CONS_Debug(DBG_GAMELOGIC, "A_DualAction called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
+
+	var1 = states[locvar1].var1;
+	var2 = states[locvar1].var2;
+#ifdef HAVE_BLUA
+	astate = &states[locvar1];
+#endif
+
+	CONS_Debug(DBG_GAMELOGIC, "A_DualAction: Calling First Action (state %d)...\n", locvar1);
+	states[locvar1].action.acp1(actor);
+
+	var1 = states[locvar2].var1;
+	var2 = states[locvar2].var2;
+#ifdef HAVE_BLUA
+	astate = &states[locvar2];
+#endif
+
+	CONS_Debug(DBG_GAMELOGIC, "A_DualAction: Calling Second Action (state %d)...\n", locvar2);
+	states[locvar2].action.acp1(actor);
+}
+
+// Function: A_RemoteAction
+//
+// Description: var1 is the remote object. var2 is the state reference for calling the action called on var1. var1 becomes the actor's target, the action (var2) is called on var1. actor's target is restored
+//
+// var1 = remote object (-2 uses tracer, -1 uses target)
+// var2 = state reference for calling an action
+//
+void A_RemoteAction(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	mobj_t *originaltarget = actor->target; // Hold on to the target for later.
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_RemoteAction", actor))
+		return;
+#endif
+
+	// If >=0, find the closest target.
+	if (locvar1 >= 0)
+	{
+		///* DO A_FINDTARGET STUFF *///
+		mobj_t *targetedmobj = NULL;
+		thinker_t *th;
+		mobj_t *mo2;
+		fixed_t dist1 = 0, dist2 = 0;
+
+		// scan the thinkers
+		for (th = thinkercap.next; th != &thinkercap; th = th->next)
+		{
+			if (th->function.acp1 != (actionf_p1)P_MobjThinker)
+				continue;
+
+			mo2 = (mobj_t *)th;
+
+			if (mo2->type == (mobjtype_t)locvar1)
+			{
+				if (targetedmobj == NULL)
+				{
+					targetedmobj = mo2;
+					dist2 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
+				}
+				else
+				{
+					dist1 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
+
+					if ((locvar2 && dist1 < dist2) || (!locvar2 && dist1 > dist2))
+					{
+						targetedmobj = mo2;
+						dist2 = dist1;
+					}
+				}
+			}
+		}
+
+		if (!targetedmobj)
+		{
+			CONS_Debug(DBG_GAMELOGIC, "A_RemoteAction: Unable to find the specified object to target.\n");
+			return; // Oops, nothing found..
+		}
+
+		CONS_Debug(DBG_GAMELOGIC, "A_RemoteAction: Found a target.\n");
+
+		P_SetTarget(&actor->target, targetedmobj);
+
+		///* END A_FINDTARGET STUFF *///
+	}
+
+	// If -2, use the tracer as the target
+	else if (locvar1 == -2)
+		P_SetTarget(&actor->target, actor->tracer);
+	// if -1 or anything else, just use the target.
+
+	if (actor->target)
+	{
+		// Steal the var1 and var2 from "locvar2"
+		var1 = states[locvar2].var1;
+		var2 = states[locvar2].var2;
+#ifdef HAVE_BLUA
+		astate = &states[locvar2];
+#endif
+
+		CONS_Debug(DBG_GAMELOGIC, "A_RemoteAction: Calling action on %p\n"
+				"var1 is %d\nvar2 is %d\n", actor->target, var1, var2);
+		states[locvar2].action.acp1(actor->target);
+	}
+
+	P_SetTarget(&actor->target, originaltarget); // Restore the original target.
+}
+
+// Function: A_ToggleFlameJet
+//
+// Description: Turns a flame jet on and off.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_ToggleFlameJet(mobj_t* actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_ToggleFlameJet", actor))
+		return;
+#endif
+	// threshold - off delay
+	// movecount - on timer
+
+	if (actor->flags2 & MF2_FIRING)
+	{
+		actor->flags2 &= ~MF2_FIRING;
+
+		if (actor->threshold)
+			actor->tics = actor->threshold;
+	}
+	else
+	{
+		actor->flags2 |= MF2_FIRING;
+
+		if (actor->movecount)
+			actor->tics = actor->movecount;
+	}
+}
+
+// Function: A_OrbitNights
+//
+// Description: Used by Chaos Emeralds to orbit around Nights (aka Super Sonic.)
+//
+// var1 = Angle adjustment (aka orbit speed)
+// var2 = Lower four bits: height offset, Upper 4 bits = set if object is Nightopian Helper
+//
+void A_OrbitNights(mobj_t* actor)
+{
+	INT32 ofs = (var2 & 0xFFFF);
+	boolean ishelper = (var2 & 0xFFFF0000);
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_OrbitNights", actor))
+		return;
+#endif
+
+	if (!actor->target
+	|| (actor->target->player &&
+		// if NiGHTS special stage and not NiGHTSmode.
+	    (((maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap) && !(actor->target->player->powers[pw_carry] == CR_NIGHTSMODE))
+	    // Also remove this object if they no longer have a NiGHTS helper
+		|| (ishelper && !actor->target->player->powers[pw_nights_helper]))))
+	{
+		P_RemoveMobj(actor);
+		return;
+	}
+	else
+	{
+		actor->extravalue1 += var1;
+		P_UnsetThingPosition(actor);
+		{
+			const angle_t fa  = (angle_t)actor->extravalue1 >> ANGLETOFINESHIFT;
+			const angle_t ofa = ((angle_t)actor->extravalue1 + (ofs*ANG1)) >> ANGLETOFINESHIFT;
+
+			const fixed_t fc = FixedMul(FINECOSINE(fa),FixedMul(32*FRACUNIT, actor->scale));
+			const fixed_t fh = FixedMul(FINECOSINE(ofa),FixedMul(20*FRACUNIT, actor->scale));
+			const fixed_t fs = FixedMul(FINESINE(fa),FixedMul(32*FRACUNIT, actor->scale));
+
+			actor->x = actor->target->x + fc;
+			actor->y = actor->target->y + fs;
+			actor->z = actor->target->z + fh + FixedMul(16*FRACUNIT, actor->scale);
+
+			// Semi-lazy hack
+			actor->angle = (angle_t)actor->extravalue1 + ANGLE_90;
+		}
+		P_SetThingPosition(actor);
+
+		if (ishelper && actor->target->player) // Flash a helper that's about to be removed.
+		{
+			if ((actor->target->player->powers[pw_nights_helper] < TICRATE)
+			&& (actor->target->player->powers[pw_nights_helper] & 1))
+				actor->flags2 |= MF2_DONTDRAW;
+			else
+				actor->flags2 &= ~MF2_DONTDRAW;
+		}
+	}
+}
+
+// Function: A_GhostMe
+//
+// Description: Spawns a "ghost" mobj of this actor, ala spindash trails and the minus's digging "trails"
+//
+// var1 = duration in tics
+// var2 = unused
+//
+void A_GhostMe(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	mobj_t *ghost;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_GhostMe", actor))
+		return;
+#endif
+	ghost = P_SpawnGhostMobj(actor);
+	if (ghost && locvar1 > 0)
+		ghost->fuse = locvar1;
+}
+
+// Function: A_SetObjectState
+//
+// Description: Changes the state of an object's target/tracer.
+//
+// var1 = state number
+// var2:
+//		0 = target
+//		1 = tracer
+//
+void A_SetObjectState(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	mobj_t *target;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SetObjectState", actor))
+		return;
+#endif
+
+	if ((!locvar2 && !actor->target) || (locvar2 && !actor->tracer))
+	{
+		if (cv_debug)
+			CONS_Printf("A_SetObjectState: No target to change state!\n");
+		return;
+	}
+
+	if (!locvar2) // target
+		target = actor->target;
+	else // tracer
+		target = actor->tracer;
+
+	if (target->health > 0)
+	{
+		if (!target->player)
+			P_SetMobjState(target, locvar1);
+		else
+			P_SetPlayerMobjState(target, locvar1);
+	}
+}
+
+// Function: A_SetObjectTypeState
+//
+// Description: Changes the state of all active objects of a certain type in a certain range of the actor.
+//
+// var1 = state number
+// var2:
+//		lower 16 bits = type
+//		upper 16 bits = range (if == 0, across whole map)
+//
+void A_SetObjectTypeState(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	const UINT16 loc2lw = (UINT16)(locvar2 & 65535);
+	const UINT16 loc2up = (UINT16)(locvar2 >> 16);
+
+	thinker_t *th;
+	mobj_t *mo2;
+	fixed_t dist = 0;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SetObjectTypeState", actor))
+		return;
+#endif
+
+	for (th = thinkercap.next; th != &thinkercap; th = th->next)
+	{
+		if (th->function.acp1 != (actionf_p1)P_MobjThinker)
+			continue;
+
+		mo2 = (mobj_t *)th;
+
+		if (mo2->type == (mobjtype_t)loc2lw)
+		{
+			dist = P_AproxDistance(mo2->x - actor->x, mo2->y - actor->y);
+
+			if (mo2->health > 0)
+			{
+				if (loc2up == 0)
+					P_SetMobjState(mo2, locvar1);
+				else
+				{
+					if (dist <= FixedMul(loc2up*FRACUNIT, actor->scale))
+						P_SetMobjState(mo2, locvar1);
+				}
+			}
+		}
+	}
+}
+
+// Function: A_KnockBack
+//
+// Description: Knocks back the object's target at its current speed.
+//
+// var1:
+//		0 = target
+//		1 = tracer
+// var2 = unused
+//
+void A_KnockBack(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	mobj_t *target;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_KnockBack", actor))
+		return;
+#endif
+
+	if (!locvar1)
+		target = actor->target;
+	else
+		target = actor->tracer;
+
+	if (!target)
+	{
+		if(cv_debug)
+			CONS_Printf("A_KnockBack: No target!\n");
+		return;
+	}
+
+	target->momx *= -1;
+	target->momy *= -1;
+}
+
+// Function: A_PushAway
+//
+// Description: Pushes an object's target away from the calling object.
+//
+// var1 = amount of force
+// var2:
+//		lower 16 bits = If 1, xy momentum is lost. If 0, xy momentum is kept
+//		upper 16 bits = 0 - target, 1 - tracer
+//
+void A_PushAway(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	mobj_t *target; // target
+	angle_t an; // actor to target angle
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_PushAway", actor))
+		return;
+#endif
+
+	if ((!(locvar2 >> 16) && !actor->target) || ((locvar2 >> 16) && !actor->tracer))
+		return;
+
+	if (!locvar1)
+		CONS_Printf("A_Thrust: Var1 not specified!\n");
+
+	if (!(locvar2 >> 16)) // target
+		target = actor->target;
+	else // tracer
+		target = actor->tracer;
+
+	an = R_PointToAngle2(actor->x, actor->y, target->x, target->y);
+
+	if (locvar2 & 65535)
+		P_InstaThrust(target, an, FixedMul(locvar1*FRACUNIT, actor->scale));
+	else
+		P_Thrust(target, an, FixedMul(locvar1*FRACUNIT, actor->scale));
+}
+
+// Function: A_RingDrain
+//
+// Description: Drain targeted player's rings.
+//
+// var1 = ammount of drained rings
+// var2 = unused
+//
+void A_RingDrain(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	player_t *player;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_RingDrain", actor))
+		return;
+#endif
+
+	if (!actor->target || !actor->target->player)
+	{
+		if(cv_debug)
+			CONS_Printf("A_RingDrain: No player targeted!\n");
+		return;
+	}
+
+	player = actor->target->player;
+	P_GivePlayerRings(player, -min(locvar1, player->rings));
+}
+
+// Function: A_SplitShot
+//
+// Description: Shoots 2 missiles that hit next to the player.
+//
+// var1 = target x-y-offset
+// var2:
+//		lower 16 bits = missile type
+//		upper 16 bits = height offset
+//
+void A_SplitShot(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	const UINT16 loc2lw = (UINT16)(locvar2 & 65535);
+	const UINT16 loc2up = (UINT16)(locvar2 >> 16);
+	const fixed_t offs = (fixed_t)(locvar1*FRACUNIT);
+	const fixed_t hoffs = (fixed_t)(loc2up*FRACUNIT);
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SplitShot", actor))
+		return;
+#endif
+
+	A_FaceTarget(actor);
+	{
+		const angle_t an = (actor->angle + ANGLE_90) >> ANGLETOFINESHIFT;
+		const fixed_t fasin = FINESINE(an);
+		const fixed_t facos = FINECOSINE(an);
+		fixed_t xs = FixedMul(facos,FixedMul(offs, actor->scale));
+		fixed_t ys = FixedMul(fasin,FixedMul(offs, actor->scale));
+		fixed_t z;
+
+		if (actor->eflags & MFE_VERTICALFLIP)
+			z = actor->z + actor->height - FixedMul(hoffs, actor->scale);
+		else
+			z = actor->z + FixedMul(hoffs, actor->scale);
+
+		P_SpawnPointMissile(actor, actor->target->x+xs, actor->target->y+ys, actor->target->z, loc2lw, actor->x, actor->y, z);
+		P_SpawnPointMissile(actor, actor->target->x-xs, actor->target->y-ys, actor->target->z, loc2lw, actor->x, actor->y, z);
+	}
+}
+
+// Function: A_MissileSplit
+//
+// Description: If the object is a missile it will create a new missile with an alternate flight path owned by the one who shot the former missile.
+//
+// var1 = splitting missile type
+// var2 = splitting angle
+//
+void A_MissileSplit(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_MissileSplit", actor))
+		return;
+#endif
+	if (actor->eflags & MFE_VERTICALFLIP)
+		P_SpawnAlteredDirectionMissile(actor, locvar1, actor->x, actor->y, actor->z+actor->height, locvar2);
+	else
+		P_SpawnAlteredDirectionMissile(actor, locvar1, actor->x, actor->y, actor->z, locvar2);
+}
+
+// Function: A_MultiShot
+//
+// Description: Shoots objects horizontally that spread evenly in all directions.
+//
+// var1:
+//		lower 16 bits = number of missiles
+//		upper 16 bits = missile type #
+// var2 = height offset
+//
+void A_MultiShot(mobj_t *actor)
+{
+	fixed_t z, xr, yr;
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	const UINT16 loc1lw = (UINT16)(locvar1 & 65535);
+	const UINT16 loc1up = (UINT16)(locvar1 >> 16);
+	INT32 count = 0;
+	fixed_t ad;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_MultiShot", actor))
+		return;
+#endif
+
+	if (actor->target)
+		A_FaceTarget(actor);
+
+	if(loc1lw > 90)
+		ad = FixedMul(90*FRACUNIT, actor->scale);
+	else
+		ad = FixedMul(loc1lw*FRACUNIT, actor->scale);
+
+	if (actor->eflags & MFE_VERTICALFLIP)
+		z = actor->z + actor->height - FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
+	else
+		z = actor->z + FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
+	xr = FixedMul((P_SignedRandom()/3)<<FRACBITS, actor->scale); // please note p_mobj.c's P_Rand() abuse
+	yr = FixedMul((P_SignedRandom()/3)<<FRACBITS, actor->scale); // of rand(), RAND_MAX and signness mess
+
+	while(count <= loc1lw && loc1lw >= 1)
+	{
+		const angle_t fa = FixedAngleC(count*FRACUNIT*360, ad)>>ANGLETOFINESHIFT;
+		const fixed_t rc = FINECOSINE(fa);
+		const fixed_t rs = FINESINE(fa);
+		const fixed_t xrc = FixedMul(xr, rc);
+		const fixed_t yrs = FixedMul(yr, rs);
+		const fixed_t xrs = FixedMul(xr, rs);
+		const fixed_t yrc = FixedMul(yr, rc);
+
+		P_SpawnPointMissile(actor, xrc-yrs+actor->x, xrs+yrc+actor->y, z, loc1up, actor->x, actor->y, z);
+		count++;
+	}
+
+	if (!(actor->flags & MF_BOSS))
+	{
+		if (ultimatemode)
+			actor->reactiontime = actor->info->reactiontime*TICRATE;
+		else
+			actor->reactiontime = actor->info->reactiontime*TICRATE*2;
+	}
+}
+
+// Function: A_InstaLoop
+//
+// Description: Makes the object move along a 2d (view angle, z) polygon.
+//
+// var1:
+//		lower 16 bits = current step
+//		upper 16 bits = maximum step #
+// var2 = force
+//
+void A_InstaLoop(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	fixed_t force = max(locvar2, 1)*FRACUNIT; // defaults to 1 if var2 < 1
+	const UINT16 loc1lw = (UINT16)(locvar1 & 65535);
+	const UINT16 loc1up = (UINT16)(locvar1 >> 16);
+	const angle_t fa = FixedAngleC(loc1lw*FRACUNIT*360, loc1up*FRACUNIT)>>ANGLETOFINESHIFT;
+	const fixed_t ac = FINECOSINE(fa);
+	const fixed_t as = FINESINE(fa);
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_InstaLoop", actor))
+		return;
+#endif
+
+	P_InstaThrust(actor, actor->angle, FixedMul(ac, FixedMul(force, actor->scale)));
+	P_SetObjectMomZ(actor, FixedMul(as, force), false);
+}
+
+// Function: A_Custom3DRotate
+//
+// Description: Rotates the actor around its target in 3 dimensions.
+//
+// var1:
+//		lower 16 bits = radius in fracunits
+//		upper 16 bits = vertical offset
+// var2:
+//		lower 16 bits = vertical rotation speed in 1/10 fracunits per tic
+//		upper 16 bits = horizontal rotation speed in 1/10 fracunits per tic
+//
+void A_Custom3DRotate(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+
+	const UINT16 loc1lw = (UINT16)(locvar1 & 65535);
+	const UINT16 loc1up = (UINT16)(locvar1 >> 16);
+	const UINT16 loc2lw = (UINT16)(locvar2 & 65535);
+	const UINT16 loc2up = (UINT16)(locvar2 >> 16);
+
+	const fixed_t radius = FixedMul(loc1lw*FRACUNIT, actor->scale);
+	const fixed_t hOff = FixedMul(loc1up*FRACUNIT, actor->scale);
+	const fixed_t hspeed = FixedMul(loc2up*FRACUNIT/10, actor->scale);
+	const fixed_t vspeed = FixedMul(loc2lw*FRACUNIT/10, actor->scale);
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Custom3DRotate", actor))
+		return;
+#endif
+
+	if (actor->target->health == 0)
+	{
+		P_RemoveMobj(actor);
+		return;
+	}
+
+	if (!actor->target) // This should NEVER happen.
+	{
+		if (cv_debug)
+			CONS_Printf("Error: Object has no target\n");
+		P_RemoveMobj(actor);
+		return;
+	}
+	if (hspeed==0 && vspeed==0)
+	{
+		CONS_Printf("Error: A_Custom3DRotate: Object has no speed.\n");
+		return;
+	}
+
+	actor->angle += FixedAngle(hspeed);
+	actor->movedir += FixedAngle(vspeed);
+	P_UnsetThingPosition(actor);
+	{
+		const angle_t fa = actor->angle>>ANGLETOFINESHIFT;
+
+		if (vspeed == 0 && hspeed != 0)
+		{
+			actor->x = actor->target->x + FixedMul(FINECOSINE(fa),radius);
+			actor->y = actor->target->y + FixedMul(FINESINE(fa),radius);
+			actor->z = actor->target->z + actor->target->height/2 - actor->height/2 + hOff;
+		}
+		else
+		{
+			const angle_t md = actor->movedir>>ANGLETOFINESHIFT;
+			actor->x = actor->target->x + FixedMul(FixedMul(FINESINE(md),FINECOSINE(fa)),radius);
+			actor->y = actor->target->y + FixedMul(FixedMul(FINESINE(md),FINESINE(fa)),radius);
+			actor->z = actor->target->z + FixedMul(FINECOSINE(md),radius) + actor->target->height/2 - actor->height/2 + hOff;
+		}
+	}
+	P_SetThingPosition(actor);
+}
+
+// Function: A_SearchForPlayers
+//
+// Description: Checks if the actor has targeted a vulnerable player. If not a new player will be searched all around. If no players are available the object can call a specific state. (Useful for not moving enemies)
+//
+// var1:
+//		if var1 == 0, if necessary call state with same state number as var2
+//		else, do not call a specific state if no players are available
+// var2 = state number
+//
+void A_SearchForPlayers(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SearchForPlayers", actor))
+		return;
+#endif
+
+	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+	{
+		// look for a new target
+		if (P_LookForPlayers(actor, true, false, 0))
+			return; // got a new target
+
+		if(locvar1==0)
+		{
+			P_SetMobjStateNF(actor, locvar2);
+			return;
+		}
+	}
+}
+
+// Function: A_CheckRandom
+//
+// Description: Calls a state by chance.
+//
+// var1:
+//		lower 16 bits = denominator
+//		upper 16 bits = numerator (defaults to 1 if zero)
+// var2 = state number
+//
+void A_CheckRandom(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	fixed_t chance = FRACUNIT;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_CheckRandom", actor))
+		return;
+#endif
+	if ((locvar1 & 0xFFFF) == 0)
+		return;
+
+	// The PRNG doesn't suck anymore, OK?
+	if (locvar1 >> 16)
+		chance *= (locvar1 >> 16);
+	chance /= (locvar1 & 0xFFFF);
+
+	if (P_RandomChance(chance))
+		P_SetMobjState(actor, locvar2);
+}
+
+// Function: A_CheckTargetRings
+//
+// Description: Calls a state depending on the ammount of rings currently owned by targeted players.
+//
+// var1 = if player rings >= var1 call state
+// var2 = state number
+//
+void A_CheckTargetRings(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_CheckTargetRings", actor))
+		return;
+#endif
+
+	if (!(actor->target) || !(actor->target->player))
+	   return;
+
+	if (actor->target->player->rings >= locvar1)
+		P_SetMobjState(actor, locvar2);
+}
+
+// Function: A_CheckRings
+//
+// Description: Calls a state depending on the ammount of rings currently owned by all players.
+//
+// var1 = if player rings >= var1 call state
+// var2 = state number
+//
+void A_CheckRings(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	INT32 i, cntr = 0;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_CheckRings", actor))
+		return;
+#endif
+
+	for (i = 0; i < MAXPLAYERS; i++)
+		cntr += players[i].rings;
+
+	if (cntr >= locvar1)
+		P_SetMobjState(actor, locvar2);
+}
+
+// Function: A_CheckTotalRings
+//
+// Description: Calls a state depending on the maximum ammount of rings owned by all players during this try.
+//
+// var1 = if total player rings >= var1 call state
+// var2 = state number
+//
+void A_CheckTotalRings(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+
+	INT32 i, cntr = 0;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_CheckTotalRings", actor))
+		return;
+#endif
+
+	for (i = 0; i < MAXPLAYERS; i++)
+		cntr += players[i].totalring;
+
+	if (cntr >= locvar1)
+		P_SetMobjState(actor, locvar2);
+}
+
+// Function: A_CheckHealth
+//
+// Description: Calls a state depending on the object's current health.
+//
+// var1 = if health <= var1 call state
+// var2 = state number
+//
+void A_CheckHealth(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_CheckHealth", actor))
+		return;
+#endif
+
+	if (actor->health <= locvar1)
+		P_SetMobjState(actor, locvar2);
+}
+
+// Function: A_CheckRange
+//
+// Description: Calls a state if the object's target is in range.
+//
+// var1:
+//		lower 16 bits = range
+//		upper 16 bits = 0 - target, 1 - tracer
+// var2 = state number
+//
+void A_CheckRange(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	fixed_t dist;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_CheckRange", actor))
+		return;
+#endif
+
+	if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer))
+		return;
+
+	if (!(locvar1 >> 16)) //target
+		dist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
+	else //tracer
+		dist = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y);
+
+	if (dist <= FixedMul((locvar1 & 65535)*FRACUNIT, actor->scale))
+		P_SetMobjState(actor, locvar2);
+}
+
+// Function: A_CheckHeight
+//
+// Description: Calls a state if the object and it's target have a height offset <= var1 compared to each other.
+//
+// var1:
+//		lower 16 bits = height offset
+//		upper 16 bits = 0 - target, 1 - tracer
+// var2 = state number
+//
+void A_CheckHeight(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	fixed_t height;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_CheckHeight", actor))
+		return;
+#endif
+
+	if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer))
+		return;
+
+	if (!(locvar1 >> 16)) // target
+		height = abs(actor->target->z - actor->z);
+	else // tracer
+		height = abs(actor->tracer->z - actor->z);
+
+	if (height <= FixedMul((locvar1 & 65535)*FRACUNIT, actor->scale))
+		P_SetMobjState(actor, locvar2);
+}
+
+// Function: A_CheckTrueRange
+//
+// Description: Calls a state if the object's target is in true range. (Checks height, too.)
+//
+// var1:
+//		lower 16 bits = range
+//		upper 16 bits = 0 - target, 1 - tracer
+// var2 = state number
+//
+void A_CheckTrueRange(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	fixed_t height; // vertical range
+	fixed_t dist; // horizontal range
+	fixed_t l; // true range
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_CheckTrueRange", actor))
+		return;
+#endif
+
+	if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer))
+		return;
+
+	if (!(locvar1 >> 16)) // target
+	{
+		height = actor->target->z - actor->z;
+		dist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
+
+	}
+	else // tracer
+	{
+		height = actor->tracer->z - actor->z;
+		dist = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y);
+	}
+
+	l = P_AproxDistance(dist, height);
+
+	if (l <= FixedMul((locvar1 & 65535)*FRACUNIT, actor->scale))
+		P_SetMobjState(actor, locvar2);
+
+}
+
+// Function: A_CheckThingCount
+//
+// Description: Calls a state depending on the number of active things in range.
+//
+// var1:
+//		lower 16 bits = number of things
+//		upper 16 bits = thing type
+// var2:
+//		lower 16 bits = state to call
+//		upper 16 bits = range (if == 0, check whole map)
+//
+void A_CheckThingCount(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+
+	const UINT16 loc1lw = (UINT16)(locvar1 & 65535);
+	const UINT16 loc1up = (UINT16)(locvar1 >> 16);
+	const UINT16 loc2lw = (UINT16)(locvar2 & 65535);
+	const UINT16 loc2up = (UINT16)(locvar2 >> 16);
+
+	INT32 count = 0;
+	thinker_t *th;
+	mobj_t *mo2;
+	fixed_t dist = 0;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_CheckThingCount", actor))
+		return;
+#endif
+
+	for (th = thinkercap.next; th != &thinkercap; th = th->next)
+	{
+		if (th->function.acp1 != (actionf_p1)P_MobjThinker)
+			continue;
+
+		mo2 = (mobj_t *)th;
+
+		if (mo2->type == (mobjtype_t)loc1up)
+		{
+			dist = P_AproxDistance(mo2->x - actor->x, mo2->y - actor->y);
+
+			if (loc2up == 0)
+				count++;
+			else
+			{
+				if (dist <= FixedMul(loc2up*FRACUNIT, actor->scale))
+					count++;
+			}
+		}
+	}
+
+	if(loc1lw <= count)
+		P_SetMobjState(actor, loc2lw);
+}
+
+// Function: A_CheckAmbush
+//
+// Description: Calls a state if the actor is behind its targeted player.
+//
+// var1:
+//		0 = target
+//		1 = tracer
+// var2 = state number
+//
+void A_CheckAmbush(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	angle_t at; // angle target is currently facing
+	angle_t atp; // actor to target angle
+	angle_t an; // angle between at and atp
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_CheckAmbush", actor))
+		return;
+#endif
+
+	if ((!locvar1 && !actor->target) || (locvar1 && !actor->tracer))
+		return;
+
+	if (!locvar1) // target
+	{
+		at = actor->target->angle;
+		atp = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+	}
+	else // tracer
+	{
+		at = actor->tracer->angle;
+		atp = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y);
+	}
+
+	an = atp - at;
+
+	if (an > ANGLE_180) // flip angle if bigger than 180
+		an = InvAngle(an);
+
+	if (an < ANGLE_90+ANGLE_22h) // within an angle of 112.5 from each other?
+		P_SetMobjState(actor, locvar2);
+}
+
+// Function: A_CheckCustomValue
+//
+// Description: Calls a state depending on the object's custom value.
+//
+// var1 = if custom value >= var1, call state
+// var2 = state number
+//
+void A_CheckCustomValue(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_CheckCustomValue", actor))
+		return;
+#endif
+
+	if (actor->cusval >= locvar1)
+		P_SetMobjState(actor, locvar2);
+}
+
+// Function: A_CheckCusValMemo
+//
+// Description: Calls a state depending on the object's custom memory value.
+//
+// var1 = if memory value >= var1, call state
+// var2 = state number
+//
+void A_CheckCusValMemo(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_CheckCusValMemo", actor))
+		return;
+#endif
+
+	if (actor->cvmem >= locvar1)
+		P_SetMobjState(actor, locvar2);
+}
+
+// Function: A_SetCustomValue
+//
+// Description: Changes the custom value of an object.
+//
+// var1 = manipulating value
+// var2:
+//      if var2 == 5, multiply the custom value by var1
+//      else if var2 == 4, divide the custom value by var1
+//      else if var2 == 3, apply modulo var1 to the custom value
+//      else if var2 == 2, add var1 to the custom value
+//      else if var2 == 1, substract var1 from the custom value
+//      else if var2 == 0, replace the custom value with var1
+//
+void A_SetCustomValue(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SetCustomValue", actor))
+		return;
+#endif
+
+	if (cv_debug)
+		CONS_Printf("Init custom value is %d\n", actor->cusval);
+
+	if (locvar1 == 0 && locvar2 == 4)
+		return; // DON'T DIVIDE BY ZERO
+
+	// no need for a "temp" value here, just modify the cusval directly
+	if (locvar2 == 5) // multiply
+		actor->cusval *= locvar1;
+	else if (locvar2 == 4) // divide
+		actor->cusval /= locvar1;
+	else if (locvar2 == 3) // modulo
+		actor->cusval %= locvar1;
+	else if (locvar2 == 2) // add
+		actor->cusval += locvar1;
+	else if (locvar2 == 1) // subtract
+		actor->cusval -= locvar1;
+	else // replace
+		actor->cusval = locvar1;
+
+	if(cv_debug)
+		CONS_Printf("New custom value is %d\n", actor->cusval);
+}
+
+// Function: A_UseCusValMemo
+//
+// Description: Memorizes or recalls a current custom value.
+//
+// var1:
+//      if var1 == 1, manipulate memory value
+//      else, recall memory value replacing the custom value
+// var2:
+//      if var2 == 5, mem = mem*cv  ||  cv = cv*mem
+//      else if var2 == 4,  mem = mem/cv  ||  cv = cv/mem
+//      else if var2 == 3, mem = mem%cv  ||  cv = cv%mem
+//      else if var2 == 2, mem += cv  ||  cv += mem
+//      else if var2 == 1,  mem -= cv  ||  cv -= mem
+//      else mem = cv  ||  cv = mem
+//
+void A_UseCusValMemo(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+
+	INT32 temp = actor->cusval; // value being manipulated
+	INT32 tempM = actor->cvmem; // value used to manipulate temp with
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_UseCusValMemo", actor))
+		return;
+#endif
+
+	if (locvar1 == 1) // cvmem being changed using cusval
+	{
+		temp = actor->cvmem;
+		tempM = actor->cusval;
+	}
+	else // cusval being changed with cvmem
+	{
+		temp = actor->cusval;
+		tempM = actor->cvmem;
+	}
+
+	if (tempM == 0 && locvar2 == 4)
+		return; // DON'T DIVIDE BY ZERO
+
+	// now get new value for cusval/cvmem using the other
+	if (locvar2 == 5) // multiply
+		temp *= tempM;
+	else if (locvar2 == 4) // divide
+		temp /= tempM;
+	else if (locvar2 == 3) // modulo
+		temp %= tempM;
+	else if (locvar2 == 2) // add
+		temp += tempM;
+	else if (locvar2 == 1) // subtract
+		temp -= tempM;
+	else // replace
+		temp = tempM;
+
+	// finally, give cusval/cvmem the new value!
+	if (locvar1 == 1)
+		actor->cvmem = temp;
+	else
+		actor->cusval = temp;
+}
+
+// Function: A_RelayCustomValue
+//
+// Description: Manipulates the custom value of the object's target/tracer.
+//
+// var1:
+//		lower 16 bits:
+//					if var1 == 0, use own custom value
+//					else, use var1 value
+//		upper 16 bits = 0 - target, 1 - tracer
+// var2:
+//      if var2 == 5, multiply the target's custom value by var1
+//      else if var2 == 4, divide the target's custom value by var1
+//      else if var2 == 3, apply modulo var1 to the target's custom value
+//      else if var2 == 2, add var1 to the target's custom value
+//      else if var2 == 1, substract var1 from the target's custom value
+//      else if var2 == 0, replace the target's custom value with var1
+//
+void A_RelayCustomValue(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+
+	INT32 temp; // reference value - var1 lower 16 bits changes this
+	INT32 tempT; // target's value - changed to tracer if var1 upper 16 bits set, then modified to become final value
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_RelayCustomValue", actor))
+		return;
+#endif
+
+	if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer))
+		return;
+
+	// reference custom value
+	if ((locvar1 & 65535) == 0)
+		temp = actor->cusval; // your own custom value
+	else
+		temp = (locvar1 & 65535); // var1 value
+
+	if (!(locvar1 >> 16)) // target's custom value
+		tempT = actor->target->cusval;
+	else // tracer's custom value
+		tempT = actor->tracer->cusval;
+
+	if (temp == 0 && locvar2 == 4)
+		return; // DON'T DIVIDE BY ZERO
+
+	// now get new cusval using target's and the reference
+	if (locvar2 == 5) // multiply
+		tempT *= temp;
+	else if (locvar2 == 4) // divide
+		tempT /= temp;
+	else if (locvar2 == 3) // modulo
+		tempT %= temp;
+	else if (locvar2 == 2) // add
+		tempT += temp;
+	else if (locvar2 == 1) // subtract
+		tempT -= temp;
+	else // replace
+		tempT = temp;
+
+	// finally, give target/tracer the new cusval!
+	if (!(locvar1 >> 16)) // target
+		actor->target->cusval = tempT;
+	else // tracer
+		actor->tracer->cusval = tempT;
+}
+
+// Function: A_CusValAction
+//
+// Description: Calls an action from a reference state applying custom value parameters.
+//
+// var1 = state # to use action from
+// var2:
+//      if var2 == 5, only replace new action's var2 with memory value
+//      else if var2 == 4, only replace new action's var1 with memory value
+//      else if var2 == 3, replace new action's var2 with custom value and var1 with memory value
+//      else if var2 == 2, replace new action's var1 with custom value and var2 with memory value
+//      else if var2 == 1, only replace new action's var2 with custom value
+//      else if var2 == 0, only replace new action's var1 with custom value
+//
+void A_CusValAction(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_CusValAction", actor))
+		return;
+#endif
+
+	if (locvar2 == 5)
+	{
+		var1 = states[locvar1].var1;
+		var2 = (INT32)actor->cvmem;
+	}
+	else if (locvar2 == 4)
+	{
+		var1 = (INT32)actor->cvmem;
+		var2 = states[locvar1].var2;
+	}
+	else if (locvar2 == 3)
+	{
+		var1 = (INT32)actor->cvmem;
+		var2 = (INT32)actor->cusval;
+	}
+	else if (locvar2 == 2)
+	{
+		var1 = (INT32)actor->cusval;
+		var2 = (INT32)actor->cvmem;
+	}
+	else if (locvar2 == 1)
+	{
+		var1 = states[locvar1].var1;
+		var2 = (INT32)actor->cusval;
+	}
+	else
+	{
+		var1 = (INT32)actor->cusval;
+		var2 = states[locvar1].var2;
+	}
+
+#ifdef HAVE_BLUA
+	astate = &states[locvar1];
+#endif
+	states[locvar1].action.acp1(actor);
+}
+
+// Function: A_ForceStop
+//
+// Description: Actor immediately stops its current movement.
+//
+// var1:
+//      if var1 == 0, stop x-y-z-movement
+//      else, stop x-y-movement only
+// var2 = unused
+//
+void A_ForceStop(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_ForceStop", actor))
+		return;
+#endif
+
+	actor->momx = actor->momy = 0;
+	if (locvar1 == 0)
+		actor->momz = 0;
+}
+
+// Function: A_ForceWin
+//
+// Description: Makes all players win the level.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_ForceWin(mobj_t *actor)
+{
+	INT32 i;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_ForceWin", actor))
+		return;
+#else
+	(void)actor;
+#endif
+
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		if (playeringame[i] && ((players[i].mo && players[i].mo->health)
+		    || ((netgame || multiplayer) && (players[i].lives || players[i].continues))))
+			break;
+	}
+
+	if (i == MAXPLAYERS)
+		return;
+
+	for (i = 0; i < MAXPLAYERS; i++)
+		P_DoPlayerExit(&players[i]);
+}
+
+// Function: A_SpikeRetract
+//
+// Description: Toggles actor solid flag.
+//
+// var1:
+//        if var1 == 0, actor no collide
+//        else, actor solid
+// var2 = unused
+//
+void A_SpikeRetract(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SpikeRetract", actor))
+		return;
+#endif
+
+	if (actor->flags & MF_NOBLOCKMAP)
+		return;
+
+	if (locvar1 == 0)
+	{
+		actor->flags &= ~MF_SOLID;
+		actor->flags |= MF_NOCLIPTHING;
+	}
+	else
+	{
+		actor->flags |= MF_SOLID;
+		actor->flags &= ~MF_NOCLIPTHING;
+	}
+	if (actor->flags & MF_SOLID)
+		P_CheckPosition(actor, actor->x, actor->y);
+}
+
+// Function: A_InfoState
+//
+// Description: Set mobj state to one predefined in mobjinfo.
+//
+// var1:
+//        if var1 == 0, set actor to spawnstate
+//        else if var1 == 1, set actor to seestate
+//        else if var1 == 2, set actor to meleestate
+//        else if var1 == 3, set actor to missilestate
+//        else if var1 == 4, set actor to deathstate
+//        else if var1 == 5, set actor to xdeathstate
+//        else if var1 == 6, set actor to raisestate
+// var2 = unused
+//
+void A_InfoState(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	switch (locvar1)
+	{
+	case 0:
+		if (actor->state != &states[actor->info->spawnstate])
+			P_SetMobjState(actor, actor->info->spawnstate);
+		break;
+	case 1:
+		if (actor->state != &states[actor->info->seestate])
+			P_SetMobjState(actor, actor->info->seestate);
+		break;
+	case 2:
+		if (actor->state != &states[actor->info->meleestate])
+			P_SetMobjState(actor, actor->info->meleestate);
+		break;
+	case 3:
+		if (actor->state != &states[actor->info->missilestate])
+			P_SetMobjState(actor, actor->info->missilestate);
+		break;
+	case 4:
+		if (actor->state != &states[actor->info->deathstate])
+			P_SetMobjState(actor, actor->info->deathstate);
+		break;
+	case 5:
+		if (actor->state != &states[actor->info->xdeathstate])
+			P_SetMobjState(actor, actor->info->xdeathstate);
+		break;
+	case 6:
+		if (actor->state != &states[actor->info->raisestate])
+			P_SetMobjState(actor, actor->info->raisestate);
+		break;
+	default:
+		break;
+	}
+}
+
+// Function: A_Repeat
+//
+// Description: Returns to state var2 until animation has been used var1 times, then continues to nextstate.
+//
+// var1 = repeat count
+// var2 = state to return to if extravalue2 > 0
+//
+void A_Repeat(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Repeat", actor))
+		return;
+#endif
+
+	if (locvar1 && (!actor->extravalue2 || actor->extravalue2 > locvar1))
+		actor->extravalue2 = locvar1;
+
+	if (--actor->extravalue2 > 0)
+		P_SetMobjState(actor, locvar2);
+}
+
+// Function: A_SetScale
+//
+// Description: Changes the scale of the actor or its target/tracer
+//
+// var1 = new scale (1*FRACUNIT = 100%)
+// var2:
+//        upper 16 bits: 0 = actor, 1 = target, 2 = tracer
+//        lower 16 bits: 0 = instant change, 1 = smooth change
+//
+void A_SetScale(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	mobj_t *target;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SetScale", actor))
+		return;
+#endif
+
+	if (locvar1 <= 0)
+	{
+		if(cv_debug)
+			CONS_Printf("A_SetScale: Valid scale not specified!\n");
+		return;
+	}
+
+	if ((locvar2>>16) == 1)
+		target = actor->target;
+	else if ((locvar2>>16) == 2)
+		target = actor->tracer;
+	else // default to yourself!
+		target = actor;
+
+	if (!target)
+	{
+		if(cv_debug)
+			CONS_Printf("A_SetScale: No target!\n");
+		return;
+	}
+
+	target->destscale = locvar1; // destination scale
+	if (!(locvar2 & 65535))
+		P_SetScale(target, locvar1); // this instantly changes current scale to var1 if used, if not destscale will alter scale to var1 anyway
+}
+
+// Function: A_RemoteDamage
+//
+// Description: Damages, kills or even removes either the actor or its target/tracer. Actor acts as the inflictor/source unless harming itself
+//
+// var1 = Mobj affected: 0 - actor, 1 - target, 2 - tracer
+// var2 = Action: 0 - Damage, 1 - Kill, 2 - Remove
+//
+void A_RemoteDamage(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	mobj_t *target; // we MUST have a target
+	mobj_t *source = NULL; // on the other hand we don't necessarily need a source
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_RemoteDamage", actor))
+		return;
+#endif
+	if (locvar1 == 1)
+		target = actor->target;
+	else if (locvar1 == 2)
+		target = actor->tracer;
+	else // default to yourself!
+		target = actor;
+
+	if (locvar1 == 1 || locvar1 == 2)
+		source = actor;
+
+	if (!target)
+	{
+		if(cv_debug)
+			CONS_Printf("A_RemoteDamage: No target!\n");
+		return;
+	}
+
+	if (locvar2 == 1) // Kill mobj!
+	{
+		if (target->player) // players die using P_DamageMobj instead for some reason
+			P_DamageMobj(target, source, source, 1, DMG_INSTAKILL);
+		else
+			P_KillMobj(target, source, source, 0);
+	}
+	else if (locvar2 == 2) // Remove mobj!
+	{
+		if (target->player) //don't remove players!
+			return;
+
+		P_RemoveMobj(target);
+	}
+	else // default: Damage mobj!
+		P_DamageMobj(target, source, source, 1, 0);
+}
+
+// Function: A_HomingChase
+//
+// Description: Actor chases directly towards its destination object
+//
+// var1 = speed multiple
+// var2 = destination: 0 = target, 1 = tracer
+//
+void A_HomingChase(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	mobj_t *dest;
+	fixed_t dist;
+	fixed_t speedmul;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_HomingChase", actor))
+		return;
+#endif
+
+	if (locvar2 == 1)
+		dest = actor->tracer;
+	else //default
+		dest = actor->target;
+
+	if (!dest || !dest->health)
+		return;
+
+	actor->angle = R_PointToAngle2(actor->x, actor->y, dest->x, dest->y);
+
+	dist = P_AproxDistance(P_AproxDistance(dest->x - actor->x, dest->y - actor->y), dest->z - actor->z);
+
+	if (dist < 1)
+		dist = 1;
+
+	speedmul = FixedMul(locvar1, actor->scale);
+
+	actor->momx = FixedMul(FixedDiv(dest->x - actor->x, dist), speedmul);
+	actor->momy = FixedMul(FixedDiv(dest->y - actor->y, dist), speedmul);
+	actor->momz = FixedMul(FixedDiv(dest->z - actor->z, dist), speedmul);
+}
+
+// Function: A_TrapShot
+//
+// Description: Fires a missile in a particular direction and angle rather than AT something, Trapgoyle-style!
+//
+// var1:
+//        lower 16 bits = object # to fire
+//        upper 16 bits = front offset
+// var2:
+//        lower 15 bits = vertical angle variable
+//        16th bit:
+//			- 0: use vertical angle variable as vertical angle in degrees
+//			- 1: mimic P_SpawnXYZMissile
+//				use z of actor minus z of missile as vertical distance to cover during momz calculation
+//				use vertical angle variable as horizontal distance to cover during momz calculation
+//        upper 16 bits = height offset
+//
+void A_TrapShot(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	boolean oldstyle = (locvar2 & 32768) ? true : false;
+	mobjtype_t type = (mobjtype_t)(locvar1 & 65535);
+	mobj_t *missile;
+	INT16 frontoff = (INT16)(locvar1 >> 16);
+	INT16 vertoff = (INT16)(locvar2 >> 16);
+	fixed_t x, y, z;
+	fixed_t speed;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_TrapShot", actor))
+		return;
+#endif
+
+	x = actor->x + P_ReturnThrustX(actor, actor->angle, FixedMul(frontoff*FRACUNIT, actor->scale));
+	y = actor->y + P_ReturnThrustY(actor, actor->angle, FixedMul(frontoff*FRACUNIT, actor->scale));
+
+	if (actor->eflags & MFE_VERTICALFLIP)
+		z = actor->z + actor->height - FixedMul(vertoff*FRACUNIT, actor->scale) - FixedMul(mobjinfo[type].height, actor->scale);
+	else
+		z = actor->z + FixedMul(vertoff*FRACUNIT, actor->scale);
+
+	CONS_Debug(DBG_GAMELOGIC, "A_TrapShot: missile no. = %d, front offset = %d, vertical angle = %d, z offset = %d\n",
+		type, frontoff, (INT16)(locvar2 & 65535), vertoff);
+
+	missile = P_SpawnMobj(x, y, z, type);
+
+	if (actor->eflags & MFE_VERTICALFLIP)
+		missile->flags2 |= MF2_OBJECTFLIP;
+
+	missile->destscale = actor->scale;
+	P_SetScale(missile, actor->scale);
+
+	if (missile->info->seesound)
+		S_StartSound(missile, missile->info->seesound);
+
+	P_SetTarget(&missile->target, actor);
+	missile->angle = actor->angle;
+
+	speed = FixedMul(missile->info->speed, missile->scale);
+
+	if (oldstyle)
+	{
+		missile->momx = FixedMul(FINECOSINE(missile->angle>>ANGLETOFINESHIFT), speed);
+		missile->momy = FixedMul(FINESINE(missile->angle>>ANGLETOFINESHIFT), speed);
+		// The below line basically mimics P_SpawnXYZMissile's momz calculation.
+		missile->momz = (actor->z + ((actor->eflags & MFE_VERTICALFLIP) ? actor->height : 0) - z) / ((fixed_t)(locvar2 & 32767)*FRACUNIT / speed);
+		P_CheckMissileSpawn(missile);
+	}
+	else
+	{
+		angle_t vertang = FixedAngle(((INT16)(locvar2 & 32767))*FRACUNIT);
+		if (actor->eflags & MFE_VERTICALFLIP)
+				vertang = InvAngle(vertang); // flip firing angle
+		missile->momx = FixedMul(FINECOSINE(vertang>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(missile->angle>>ANGLETOFINESHIFT), speed));
+		missile->momy = FixedMul(FINECOSINE(vertang>>ANGLETOFINESHIFT), FixedMul(FINESINE(missile->angle>>ANGLETOFINESHIFT), speed));
+		missile->momz = FixedMul(FINESINE(vertang>>ANGLETOFINESHIFT), speed);
+	}
+}
+
+// Function: A_VileTarget
+//
+// Description: Spawns an object directly on the target, and sets this object as the actor's tracer.
+//              Originally used by Archviles to summon a pillar of hellfire, hence the name.
+//
+// var1 = mobj to spawn
+// var2 = If 0, target only the actor's target. Else, target every player, period.
+//
+void A_VileTarget(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	mobj_t *fog;
+	mobjtype_t fogtype;
+	INT32 i;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_VileTarget", actor))
+		return;
+#endif
+
+	if (!actor->target)
+		return;
+
+	A_FaceTarget(actor);
+
+	// Determine object to spawn
+	if (locvar1 <= 0 || locvar1 >= NUMMOBJTYPES)
+		fogtype = MT_CYBRAKDEMON_TARGET_RETICULE;
+	else
+		fogtype = (mobjtype_t)locvar1;
+
+	if (!locvar2)
+	{
+		fog = P_SpawnMobj(actor->target->x,
+							actor->target->y,
+							actor->target->z + ((actor->target->eflags & MFE_VERTICALFLIP) ? actor->target->height - mobjinfo[fogtype].height : 0),
+							fogtype);
+		if (actor->target->eflags & MFE_VERTICALFLIP)
+		{
+			fog->eflags |= MFE_VERTICALFLIP;
+			fog->flags2 |= MF2_OBJECTFLIP;
+		}
+		fog->destscale = actor->target->scale;
+		P_SetScale(fog, fog->destscale);
+
+		P_SetTarget(&actor->tracer, fog);
+		P_SetTarget(&fog->target, actor);
+		P_SetTarget(&fog->tracer, actor->target);
+		A_VileFire(fog);
+	}
+	else
+	{
+		// Our "Archvile" here is actually Oprah. "YOU GET A TARGET! YOU GET A TARGET! YOU ALL GET A TARGET!"
+		for (i = 0; i < MAXPLAYERS; i++)
+		{
+			if (!playeringame[i] || players[i].spectator)
+				continue;
+
+			if (!players[i].mo)
+				continue;
+
+			if (!players[i].mo->health)
+				continue;
+
+			fog = P_SpawnMobj(players[i].mo->x,
+							players[i].mo->y,
+							players[i].mo->z + ((players[i].mo->eflags & MFE_VERTICALFLIP) ? players[i].mo->height - mobjinfo[fogtype].height : 0),
+							fogtype);
+			if (players[i].mo->eflags & MFE_VERTICALFLIP)
+			{
+				fog->eflags |= MFE_VERTICALFLIP;
+				fog->flags2 |= MF2_OBJECTFLIP;
+			}
+			fog->destscale = players[i].mo->scale;
+			P_SetScale(fog, fog->destscale);
+
+			if (players[i].mo == actor->target) // We only care to track the fog targeting who we REALLY hate right now
+				P_SetTarget(&actor->tracer, fog);
+			P_SetTarget(&fog->target, actor);
+			P_SetTarget(&fog->tracer, players[i].mo);
+			A_VileFire(fog);
+		}
+	}
+}
+
+// Function: A_VileAttack
+//
+// Description: Instantly hurts the actor's target, if it's in the actor's line of sight.
+//              Originally used by Archviles to cause explosions where their hellfire pillars were, hence the name.
+//
+// var1 = sound to play
+// var2:
+//		Lower 16 bits = optional explosion object
+//		Upper 16 bits = If 0, attack only the actor's target. Else, attack all the players. All of them.
+//
+void A_VileAttack(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	sfxenum_t soundtoplay;
+	mobjtype_t explosionType = MT_NULL;
+	mobj_t *fire;
+	INT32 i;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_VileAttack", actor))
+		return;
+#endif
+
+	if (!actor->target)
+		return;
+
+	A_FaceTarget(actor);
+
+	if (locvar1 <= 0 || locvar1 >= NUMSFX)
+		soundtoplay = sfx_brakrx;
+	else
+		soundtoplay = (sfxenum_t)locvar1;
+
+	if ((locvar2 & 0xFFFF) > 0 && (locvar2 & 0xFFFF) <= NUMMOBJTYPES)
+	{
+		explosionType = (mobjtype_t)(locvar2 & 0xFFFF);
+	}
+
+	if (!(locvar2 & 0xFFFF0000)) {
+		if (!P_CheckSight(actor, actor->target))
+			return;
+
+		S_StartSound(actor, soundtoplay);
+		P_DamageMobj(actor->target, actor, actor, 1, 0);
+		//actor->target->momz = 1000*FRACUNIT/actor->target->info->mass; // How id did it
+		actor->target->momz += FixedMul(10*FRACUNIT, actor->scale)*P_MobjFlip(actor->target); // How we're doing it
+		if (explosionType != MT_NULL)
+		{
+			P_SpawnMobj(actor->target->x, actor->target->y, actor->target->z, explosionType);
+		}
+
+		// Extra attack. This was for additional damage in Doom. Doesn't really belong in SRB2, but the heck with it, it's here anyway.
+		fire = actor->tracer;
+
+		if (!fire)
+			return;
+
+		// move the fire between the vile and the player
+		//fire->x = actor->target->x - FixedMul (24*FRACUNIT, finecosine[an]);
+		//fire->y = actor->target->y - FixedMul (24*FRACUNIT, finesine[an]);
+		P_TeleportMove(fire,
+						actor->target->x - P_ReturnThrustX(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)),
+						actor->target->y - P_ReturnThrustY(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)),
+						fire->z);
+		P_RadiusAttack(fire, actor, 70*FRACUNIT, 0);
+	}
+	else
+	{
+		// Oprahvile strikes again, but this time, she brings HOT PAIN
+		for (i = 0; i < MAXPLAYERS; i++)
+		{
+			if (!playeringame[i] || players[i].spectator)
+				continue;
+
+			if (!players[i].mo)
+				continue;
+
+			if (!players[i].mo->health)
+				continue;
+
+			if (!P_CheckSight(actor, players[i].mo))
+				continue;
+
+			S_StartSound(actor, soundtoplay);
+			P_DamageMobj(players[i].mo, actor, actor, 1, 0);
+			//actor->target->momz = 1000*FRACUNIT/actor->target->info->mass; // How id did it
+			players[i].mo->momz += FixedMul(10*FRACUNIT, actor->scale)*P_MobjFlip(players[i].mo); // How we're doing it
+			if (explosionType != MT_NULL)
+			{
+				P_SpawnMobj(players[i].mo->x, players[i].mo->y, players[i].mo->z, explosionType);
+			}
+
+			// Extra attack. This was for additional damage in Doom. Doesn't really belong in SRB2, but the heck with it, it's here anyway.
+			// However, it ONLY applies to the actor's target. Nobody else matters!
+			if (actor->target != players[i].mo)
+				continue;
+
+			fire = actor->tracer;
+
+			if (!fire)
+				continue;
+
+			// move the fire between the vile and the player
+			//fire->x = actor->target->x - FixedMul (24*FRACUNIT, finecosine[an]);
+			//fire->y = actor->target->y - FixedMul (24*FRACUNIT, finesine[an]);
+			P_TeleportMove(fire,
+							actor->target->x - P_ReturnThrustX(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)),
+							actor->target->y - P_ReturnThrustY(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)),
+							fire->z);
+			P_RadiusAttack(fire, actor, 70*FRACUNIT, 0);
+		}
+	}
+
+}
+
+// Function: A_VileFire
+//
+// Description: Kind of like A_CapeChase; keeps this object in front of its tracer, unless its target can't see it.
+//				Originally used by Archviles to keep their hellfire pillars on top of the player, hence the name (although it was just "A_Fire" there; added "Vile" to make it more specific).
+//				Added some functionality to optionally draw a line directly to the enemy doing the targetting. Y'know, to hammer things in a bit.
+//
+// var1 = sound to play
+// var2:
+//		Lower 16 bits = mobj to spawn (0 doesn't spawn a line at all)
+//		Upper 16 bits = # to spawn (default is 8)
+//
+void A_VileFire(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	mobj_t *dest;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_VileFire", actor))
+		return;
+#endif
+
+	dest = actor->tracer;
+	if (!dest)
+		return;
+
+	// don't move it if the vile lost sight
+	if (!P_CheckSight(actor->target, dest))
+		return;
+
+	// keep to same scale and gravity as tracer ALWAYS
+	actor->destscale = dest->scale;
+	P_SetScale(actor, actor->destscale);
+	if (dest->eflags & MFE_VERTICALFLIP)
+	{
+		actor->eflags |= MFE_VERTICALFLIP;
+		actor->flags2 |= MF2_OBJECTFLIP;
+	}
+	else
+	{
+		actor->eflags &= ~MFE_VERTICALFLIP;
+		actor->flags2 &= ~MF2_OBJECTFLIP;
+	}
+
+	P_UnsetThingPosition(actor);
+	actor->x = dest->x + P_ReturnThrustX(actor, dest->angle, FixedMul(24*FRACUNIT, actor->scale));
+	actor->y = dest->y + P_ReturnThrustY(actor, dest->angle, FixedMul(24*FRACUNIT, actor->scale));
+	actor->z = dest->z + ((actor->eflags & MFE_VERTICALFLIP) ? dest->height-actor->height : 0);
+	P_SetThingPosition(actor);
+
+	// Play sound, if one's specified
+	if (locvar1 > 0 && locvar1 < NUMSFX)
+		S_StartSound(actor, (sfxenum_t)locvar1);
+
+	// Now draw the line to the actor's target
+	if (locvar2 & 0xFFFF)
+	{
+		mobjtype_t lineMobj;
+		UINT16 numLineMobjs;
+		fixed_t distX;
+		fixed_t distY;
+		fixed_t distZ;
+		UINT16 i;
+
+		lineMobj = (mobjtype_t)(locvar2 & 0xFFFF);
+		numLineMobjs = (UINT16)(locvar2 >> 16);
+		if (numLineMobjs == 0) {
+			numLineMobjs = 8;
+		}
+
+		// Get distance for each step
+		distX = (actor->target->x - actor->x) / numLineMobjs;
+		distY = (actor->target->y - actor->y) / numLineMobjs;
+		distZ = ((actor->target->z + FixedMul(actor->target->height/2, actor->target->scale)) - (actor->z + FixedMul(actor->height/2, actor->scale))) / numLineMobjs;
+
+		for (i = 1; i <= numLineMobjs; i++)
+		{
+			P_SpawnMobj(actor->x + (distX * i), actor->y + (distY * i), actor->z + (distZ * i) + FixedMul(actor->height/2, actor->scale), lineMobj);
+		}
+	}
+}
+
+// Function: A_BrakChase
+//
+// Description: Chase after your target, but speed and attack are tied to health.
+//
+// Every time this is called, generate a random number from a 1/4 to 3/4 of mobj's spawn health.
+// If health is above that value, use missilestate to attack.
+// If health is at or below that value, use meleestate to attack (default to missile state if not available).
+//
+// Likewise, state will linearly speed up as health goes down.
+// Upper bound will be the frame's normal length.
+// Lower bound defaults to 1 tic (technically 0, but we round up), unless a lower bound is specified in var1.
+//
+// var1 = lower-bound of frame length, in tics
+// var2 = optional sound to play
+//
+void A_BrakChase(mobj_t *actor)
+{
+	INT32 delta;
+	INT32 lowerbound;
+	INT32 newtics;
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_BrakChase", actor))
+		return;
+#endif
+
+	// Set new tics NOW, in case the state changes while we're doing this and we try applying this to the painstate or something silly
+	if (actor->tics > 1 && locvar1 < actor->tics) // Not much point, otherwise
+	{
+		if (locvar1 < 0)
+			lowerbound = 0;
+		else
+			lowerbound = locvar1;
+
+		newtics = (((actor->tics - lowerbound) * actor->health) / actor->info->spawnhealth) + lowerbound;
+		if (newtics < 1)
+			newtics = 1;
+
+		actor->tics = newtics;
+	}
+
+	if (actor->reactiontime)
+	{
+		actor->reactiontime--;
+		if (actor->reactiontime == 0 && actor->type == MT_CYBRAKDEMON)
+			S_StartSound(0, sfx_bewar1 + P_RandomKey(4));
+	}
+
+	// modify target threshold
+	if (actor->threshold)
+	{
+		if (!actor->target || actor->target->health <= 0)
+			actor->threshold = 0;
+		else
+			actor->threshold--;
+	}
+
+	// turn towards movement direction if not there yet
+	if (actor->movedir < NUMDIRS)
+	{
+		actor->angle &= (7<<29);
+		delta = actor->angle - (actor->movedir << 29);
+
+		if (delta > 0)
+			actor->angle -= ANGLE_45;
+		else if (delta < 0)
+			actor->angle += ANGLE_45;
+	}
+
+	if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+	{
+		// look for a new target
+		if (P_LookForPlayers(actor, true, false, 0))
+			return; // got a new target
+
+		P_SetMobjStateNF(actor, actor->info->spawnstate);
+		return;
+	}
+
+	// do not attack twice in a row
+	if (actor->flags2 & MF2_JUSTATTACKED)
+	{
+		actor->flags2 &= ~MF2_JUSTATTACKED;
+		P_NewChaseDir(actor);
+		return;
+	}
+
+	// Check if we can attack
+	if (P_CheckMissileRange(actor) && !actor->movecount)
+	{
+		// Check if we should use "melee" attack first. (Yes, this still runs outside of melee range. Quiet, you.)
+		if (actor->info->meleestate
+			&& actor->health <= P_RandomRange(actor->info->spawnhealth/4, (actor->info->spawnhealth * 3)/4)) // Guaranteed true if <= 1/4 health, guaranteed false if > 3/4 health
+		{
+			if (actor->info->attacksound)
+				S_StartAttackSound(actor, actor->info->attacksound);
+
+			P_SetMobjState(actor, actor->info->meleestate);
+			actor->flags2 |= MF2_JUSTATTACKED;
+			return;
+		}
+		// Else, check for missile attack.
+		else if (actor->info->missilestate)
+		{
+			P_SetMobjState(actor, actor->info->missilestate);
+			actor->flags2 |= MF2_JUSTATTACKED;
+			return;
+		}
+	}
+
+	// possibly choose another target
+	if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
+		&& P_LookForPlayers(actor, true, false, 0))
+		return; // got a new target
+
+	// chase towards player
+	if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
+		P_NewChaseDir(actor);
+
+	// Optionally play a sound effect
+	if (locvar2 > 0 && locvar2 < NUMSFX)
+		S_StartSound(actor, (sfxenum_t)locvar2);
+
+	// make active sound
+	if (actor->type != MT_CYBRAKDEMON && actor->info->activesound && P_RandomChance(3*FRACUNIT/256))
+	{
+		S_StartSound(actor, actor->info->activesound);
+	}
+}
+
+// Function: A_BrakFireShot
+//
+// Description: Shoot an object at your target, offset to match where Brak's gun is.
+// Also, sets Brak's reaction time; behaves normally otherwise.
+//
+// var1 = object # to shoot
+// var2 = unused
+//
+void A_BrakFireShot(mobj_t *actor)
+{
+	fixed_t x, y, z;
+	INT32 locvar1 = var1;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_BrakFireShot", actor))
+		return;
+#endif
+	if (!actor->target)
+		return;
+
+	A_FaceTarget(actor);
+
+	x = actor->x
+		+ P_ReturnThrustX(actor, actor->angle, FixedMul(64*FRACUNIT, actor->scale))
+		+ P_ReturnThrustX(actor, actor->angle+ANGLE_270, FixedMul(32*FRACUNIT, actor->scale));
+	y = actor->y
+		+ P_ReturnThrustY(actor, actor->angle, FixedMul(64*FRACUNIT, actor->scale))
+		+ P_ReturnThrustY(actor, actor->angle+ANGLE_270, FixedMul(32*FRACUNIT, actor->scale));
+	if (actor->eflags & MFE_VERTICALFLIP)
+		z = actor->z + actor->height - FixedMul(144*FRACUNIT, actor->scale);
+	else
+		z = actor->z + FixedMul(144*FRACUNIT, actor->scale);
+
+	P_SpawnXYZMissile(actor, actor->target, locvar1, x, y, z);
+
+	if (!(actor->flags & MF_BOSS))
+	{
+		if (ultimatemode)
+			actor->reactiontime = actor->info->reactiontime*TICRATE;
+		else
+			actor->reactiontime = actor->info->reactiontime*TICRATE*2;
+	}
+}
+
+// Function: A_BrakLobShot
+//
+// Description: Lobs an object at the floor about a third of the way toward your target.
+//				Implication is it'll bounce the rest of the way.
+//				(You can also just aim straight at the target, but whatever)
+//				Formula grabbed from http://en.wikipedia.org/wiki/Trajectory_of_a_projectile#Angle_required_to_hit_coordinate_.28x.2Cy.29
+//
+// var1 = object # to lob
+// var2:
+//		Lower 16 bits: height offset to shoot from, from the actor's bottom (none that "airtime" malarky)
+//		Upper 16 bits: if 0, aim 1/3 of the way. Else, aim directly at target.
+//
+
+void A_BrakLobShot(mobj_t *actor)
+{
+	fixed_t v; // Velocity to shoot object
+	fixed_t a1, a2, aToUse; // Velocity squared
+	fixed_t g; // Gravity
+	fixed_t x; // Horizontal difference
+	INT32 x_int; // x! But in integer form!
+	fixed_t y; // Vertical difference (yes that's normally z in SRB2 shut up)
+	INT32 y_int; // y! But in integer form!
+	INT32 intHypotenuse; // x^2 + y^2. Frequently overflows fixed point, hence why we need integers proper.
+	fixed_t fixedHypotenuse; // However, we can work around that and still get a fixed-point number.
+	angle_t theta; // Angle of attack
+	mobjtype_t typeOfShot;
+	mobj_t *shot; // Object to shoot
+	fixed_t newTargetX; // If not aiming directly
+	fixed_t newTargetY; // If not aiming directly
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2 & 0x0000FFFF;
+	INT32 aimDirect = var2 & 0xFFFF0000;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_BrakLobShot", actor))
+		return;
+#endif
+
+	if (!actor->target)
+		return; // Don't even bother if we've got nothing to aim at.
+
+	// Look up actor's current gravity situation
+	if (actor->subsector->sector->gravity)
+		g = FixedMul(gravity,(FixedDiv(*actor->subsector->sector->gravity>>FRACBITS, 1000)));
+	else
+		g = gravity;
+
+	// Look up distance between actor and its target
+	x = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
+	if (!aimDirect)
+	{
+		// Distance should actually be a third of the way over
+		x = FixedDiv(x, 3<<FRACBITS);
+		newTargetX = actor->x + P_ReturnThrustX(actor, actor->angle, x);
+		newTargetY = actor->y + P_ReturnThrustY(actor, actor->angle, x);
+		x = P_AproxDistance(newTargetX - actor->x, newTargetY - actor->y);
+		// Look up height difference between actor and the ground 1/3 of the way to its target
+		y = P_FloorzAtPos(newTargetX, newTargetY, actor->target->z, actor->target->height) - (actor->z + FixedMul(locvar2*FRACUNIT, actor->scale));
+	}
+	else
+	{
+		// Look up height difference between actor and its target
+		y = actor->target->z - (actor->z + FixedMul(locvar2*FRACUNIT, actor->scale));
+	}
+
+	// Get x^2 + y^2. Have to do it in a roundabout manner, because this overflows fixed_t way too easily otherwise.
+	x_int = x>>FRACBITS;
+	y_int = y>>FRACBITS;
+	intHypotenuse = (x_int*x_int) + (y_int*y_int);
+	fixedHypotenuse = FixedSqrt(intHypotenuse) *256;
+
+	// a = g(y+/-sqrt(x^2+y^2)). a1 can be +, a2 can be -.
+	a1 = FixedMul(g,y+fixedHypotenuse);
+	a2 = FixedMul(g,y-fixedHypotenuse);
+
+	// Determine which one isn't actually an imaginary number (or the smaller of the two, if both are real), and use that for v.
+	if (a1 < 0 || a2 < 0)
+	{
+		if (a1 < 0 && a2 < 0)
+		{
+			//Somehow, v^2 is negative in both cases. v is therefore imaginary and something is horribly wrong. Abort!
+			return;
+		}
+		// Just find which one's NOT negative, and use that
+		aToUse = max(a1,a2);
+	}
+	else
+	{
+		// Both are positive; use whichever's smaller so it can decay faster
+		aToUse = min(a1,a2);
+	}
+	v = FixedSqrt(aToUse);
+	// Okay, so we know the velocity. Let's actually find theta.
+	// We can cut the "+/- sqrt" part out entirely, since v was calculated specifically for it to equal zero. So:
+	//theta = tantoangle[FixedDiv(aToUse,FixedMul(g,x)) >> DBITS];
+	theta = tantoangle[SlopeDiv(aToUse,FixedMul(g,x))];
+
+	// Okay, complicated math done. Let's fire our object already, sheesh.
+	A_FaceTarget(actor);
+	if (locvar1 <= 0 || locvar1 >= NUMMOBJTYPES)
+		typeOfShot = MT_CANNONBALL;
+	else typeOfShot = (mobjtype_t)locvar1;
+	shot = P_SpawnMobj(actor->x, actor->y, actor->z + FixedMul(locvar2*FRACUNIT, actor->scale), typeOfShot);
+	if (shot->info->seesound)
+		S_StartSound(shot, shot->info->seesound);
+	P_SetTarget(&shot->target, actor); // where it came from
+
+	shot->angle = actor->angle;
+
+	// Horizontal axes first. First parameter is initial horizontal impulse, second is to correct its angle.
+	shot->momx = FixedMul(FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)), FINECOSINE(shot->angle >> ANGLETOFINESHIFT));
+	shot->momy = FixedMul(FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)), FINESINE(shot->angle >> ANGLETOFINESHIFT));
+	// Then the vertical axis. No angle-correction needed here.
+	shot->momz = FixedMul(v, FINESINE(theta >> ANGLETOFINESHIFT));
+	// I hope that's all that's needed, ugh
+}
+
+// Function: A_NapalmScatter
+//
+// Description: Scatters a specific number of projectiles around in a circle.
+//				Intended for use with objects that are affected by gravity; would be kind of silly otherwise.
+//
+// var1:
+//		Lower 16 bits: object # to lob (TODO: come up with a default)
+//		Upper 16 bits: Number to lob (default 8)
+// var2:
+//		Lower 16 bits: distance to toss them (No default - 0 does just that - but negatives will revert to 128)
+//		Upper 16 bits: airtime in tics (default 16)
+//
+void A_NapalmScatter(mobj_t *actor)
+{
+	mobjtype_t typeOfShot = var1 & 0x0000FFFF; // Type
+	INT32 numToShoot = (var1 & 0xFFFF0000) >> 16; // How many
+	fixed_t distance = (var2 & 0x0000FFFF) << FRACBITS; // How far
+	fixed_t airtime = var2 & 0xFFFF0000; // How long until impact (assuming no obstacles)
+	fixed_t vx; // Horizontal momentum
+	fixed_t vy; // Vertical momentum
+	fixed_t g; // Gravity
+	INT32 i; // for-loop cursor
+	mobj_t *mo; // each and every spawned napalm burst
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_NapalmScatter", actor))
+		return;
+#endif
+
+	// Some quick sanity-checking
+	if (typeOfShot >= NUMMOBJTYPES) // I'd add a <0 check, too, but 0x0000FFFF isn't negative in this case
+		typeOfShot = MT_NULL;
+	if (numToShoot <= 0) // Presumably you forgot to set var1 up; else, why are you calling this to shoot nothing?
+		numToShoot = 8;
+	else if (numToShoot > 8192) // If you seriously need this many objects spawned, stop and ask yourself "Why am I doing this?"
+		numToShoot = 8192;
+	if (distance < 0) // Presumably you thought this was an unsigned integer, you naive fool
+		distance = 32767<<FRACBITS;
+	if (airtime <= 0) // Same deal as distance I guess
+		airtime = 16<<FRACBITS;
+
+	// Look up actor's current gravity situation
+	if (actor->subsector->sector->gravity)
+		g = FixedMul(gravity,(FixedDiv(*actor->subsector->sector->gravity>>FRACBITS, 1000)));
+	else
+		g = gravity;
+
+	// vy = (g*(airtime-1))/2
+	vy = FixedMul(g,(airtime-(1<<FRACBITS)))>>1;
+	// vx = distance/airtime
+	vx = FixedDiv(distance, airtime);
+
+	for (i = 0; i<numToShoot; i++)
+	{
+		const angle_t fa = (i*FINEANGLES/numToShoot) & FINEMASK;
+
+		mo = P_SpawnMobj(actor->x, actor->y, actor->z, typeOfShot);
+		P_SetTarget(&mo->target, actor->target); // Transfer target so Brak doesn't hit himself like an idiot
+
+		mo->angle = fa << ANGLETOFINESHIFT;
+		mo->momx = FixedMul(FINECOSINE(fa),vx);
+		mo->momy = FixedMul(FINESINE(fa),vx);
+		mo->momz = vy;
+	}
+}
+
+// Function: A_SpawnFreshCopy
+//
+// Description: Spawns a copy of the mobj. x, y, z, angle, scale, target and tracer carry over; everything else starts anew.
+//				Mostly writing this because I want to do multiple actions to pass these along in a single frame instead of several.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_SpawnFreshCopy(mobj_t *actor)
+{
+	mobj_t *newObject;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SpawnFreshCopy", actor))
+		return;
+#endif
+
+	newObject = P_SpawnMobjFromMobj(actor, 0, 0, 0, actor->type);
+	newObject->flags2 = actor->flags2 & MF2_AMBUSH;
+	newObject->angle = actor->angle;
+	newObject->color = actor->color;
+	P_SetTarget(&newObject->target, actor->target);
+	P_SetTarget(&newObject->tracer, actor->tracer);
+
+	if (newObject->info->seesound)
+		S_StartSound(newObject, newObject->info->seesound);
+}
+
+// Internal Flicky spawning function.
+mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz, boolean lookforplayers)
+{
+	mobj_t *flicky;
+
+	if (!flickytype)
+	{
+		if (!mapheaderinfo[gamemap-1] || !mapheaderinfo[gamemap-1]->numFlickies) // No mapheader, no shoes, no service.
+			return NULL;
+		else
+		{
+			INT32 prandom = P_RandomKey(mapheaderinfo[gamemap-1]->numFlickies);
+			flickytype = mapheaderinfo[gamemap-1]->flickies[prandom];
+		}
+	}
+
+	flicky = P_SpawnMobjFromMobj(actor, 0, 0, 0, flickytype);
+	flicky->angle = actor->angle;
+
+	if (flickytype == MT_SEED)
+		flicky->z += P_MobjFlip(actor)*(actor->height - flicky->height)/2;
+
+	if (actor->eflags & MFE_UNDERWATER)
+		momz = FixedDiv(momz, FixedSqrt(3*FRACUNIT));
+
+	P_SetObjectMomZ(flicky, momz, false);
+	flicky->movedir = (P_RandomChance(FRACUNIT/2) ?  -1 : 1);
+	flicky->fuse = P_RandomRange(595, 700);	// originally 300, 350
+	flicky->threshold = 0;
+
+	if (lookforplayers)
+		P_LookForPlayers(flicky, true, false, 0);
+
+	return flicky;
+}
+
+// Function: A_FlickySpawn
+//
+// Description: Flicky spawning function.
+//
+// var1:
+//		lower 16 bits: if 0, spawns random flicky based on level header. Else, spawns the designated thing type.
+//		upper 16 bits: if 0, no sound is played. Else, A_Scream is called.
+// var2 = upwards thrust for spawned flicky. If zero, default value is provided.
+//
+void A_FlickySpawn(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_FlickySpawn", actor))
+		return;
+#endif
+
+	if (locvar1 >> 16) {
+		A_Scream(actor); // A shortcut for the truly lazy.
+		locvar1 &= 65535;
+	}
+
+	P_InternalFlickySpawn(actor, locvar1, ((locvar2) ? locvar2 : 8*FRACUNIT), true);
+}
+
+// Internal Flicky color setting
+void P_InternalFlickySetColor(mobj_t *actor, UINT8 extrainfo)
+{
+	UINT8 flickycolors[] = {
+		SKINCOLOR_RED,
+		SKINCOLOR_CYAN,
+		SKINCOLOR_BLUE,
+		SKINCOLOR_VAPOR,
+		SKINCOLOR_PURPLE,
+		SKINCOLOR_BUBBLEGUM,
+		SKINCOLOR_NEON,
+		SKINCOLOR_BLACK,
+		SKINCOLOR_BEIGE,
+		SKINCOLOR_LAVENDER,
+		SKINCOLOR_RUBY,
+		SKINCOLOR_SALMON,
+		SKINCOLOR_SUNSET,
+		SKINCOLOR_ORANGE,
+		SKINCOLOR_YELLOW,
+	};
+
+	if (extrainfo == 0)
+		// until we can customize flicky colors by level header, just stick to SRB2's defaults
+		actor->color = flickycolors[P_RandomKey(2)]; //flickycolors[P_RandomKey(sizeof(flickycolors))];
+	else
+		actor->color = flickycolors[min(extrainfo-1, 14)]; // sizeof(flickycolors)-1
+}
+
+// Function: A_FlickyCenter
+//
+// Description: Place flickies in-level.
+//
+// var1:
+//        Lower 16 bits = if 0, spawns random flicky based on level header. Else, spawns the designated thing type.
+//        Bits 17-20 = Flicky color, up to 15. Applies to fish.
+//        Bit 21 = Flag MF_SLIDEME (see below)
+//        Bit 22 = Flag MF_GRENADEBOUNCE (see below)
+//        Bit 23 = Flag MF_NOCLIPTHING (see below)
+//
+//        If actor is placed from a spawnpoint (map Thing), the Thing's properties take precedence.
+//
+// var2 = maximum default distance away from spawn the flickies are allowed to travel. If angle != 0, then that's the radius.
+//
+// If MTF_EXTRA (MF_SLIDEME): is flagged, Flickies move aimlessly. Else, orbit around the target.
+// If MTF_OBJECTSPECIAL (MF_GRENADEBOUNCE): Flickies stand in-place without gravity (unless they hop, then gravity is applied.)
+// If MTF_AMBUSH (MF_NOCLIPTHING): is flagged, Flickies hop.
+//
+void A_FlickyCenter(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	UINT16 flickytype = (locvar1 & 0xFFFF);
+	UINT8 flickycolor = ((locvar1 >> 16) & 0xFF);
+	UINT8 flickyflags = ((locvar1 >> 20) & 0xF);
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_FlickyCenter", actor))
+		return;
+#endif
+
+	if (!actor->tracer)
+	{
+		mobj_t *flicky = P_InternalFlickySpawn(actor, locvar1, 1, false);
+		P_SetTarget(&flicky->target, actor);
+		P_SetTarget(&actor->tracer, flicky);
+
+		if (actor->spawnpoint)
+		{
+			actor->flags &= ~(MF_SLIDEME|MF_GRENADEBOUNCE|MF_NOCLIPTHING);
+			actor->flags |= (
+				((actor->spawnpoint->options & MTF_EXTRA) ? MF_SLIDEME : 0)
+				| ((actor->spawnpoint->options & MTF_OBJECTSPECIAL) ? MF_GRENADEBOUNCE : 0)
+				| ((actor->spawnpoint->options & MTF_AMBUSH) ? MF_NOCLIPTHING : 0)
+			);
+			actor->extravalue1 = actor->spawnpoint->angle ? abs(actor->spawnpoint->angle) * FRACUNIT
+				: locvar2 ? abs(locvar2) : 384 * FRACUNIT;
+			actor->extravalue2 = actor->spawnpoint->extrainfo;
+			actor->friction = actor->spawnpoint->x*FRACUNIT;
+			actor->movefactor = actor->spawnpoint->y*FRACUNIT;
+			actor->watertop = actor->spawnpoint->z*FRACUNIT;
+		}
+		else
+		{
+			actor->flags &= ~(MF_SLIDEME|MF_GRENADEBOUNCE|MF_NOCLIPTHING);
+			actor->flags |= (
+				((flickyflags & 1) ? MF_SLIDEME : 0)
+				| ((flickyflags & 2) ? MF_GRENADEBOUNCE : 0)
+				| ((flickyflags & 4) ? MF_NOCLIPTHING : 0)
+			);
+			actor->extravalue1 = abs(locvar2);
+			actor->extravalue2 = flickycolor;
+			actor->friction = actor->x;
+			actor->movefactor = actor->y;
+			actor->watertop = actor->z;
+			locvar1 = flickytype;
+		}
+
+		if (actor->flags & MF_GRENADEBOUNCE) // in-place
+			actor->tracer->fuse = 0;
+		else if (actor->flags & MF_SLIDEME) // aimless
+		{
+			actor->tracer->fuse = 0; // less than 2*TICRATE means move aimlessly.
+			actor->tracer->angle = P_RandomKey(180)*ANG2;
+		}
+		else //orbit
+			actor->tracer->fuse = FRACUNIT;
+
+		if (locvar1 == MT_FLICKY_08)
+			P_InternalFlickySetColor(actor->tracer, actor->extravalue2);
+
+		actor->extravalue2 = 0;
+	}
+
+	if (!(actor->flags & MF_SLIDEME) && !(actor->flags & MF_GRENADEBOUNCE))
+	{
+		fixed_t originx = actor->friction;
+		fixed_t originy = actor->movefactor;
+		fixed_t originz = actor->watertop;
+
+		actor->tracer->fuse = FRACUNIT;
+
+		// Impose default home radius if flicky orbits around player
+		if (!actor->extravalue1)
+			actor->extravalue1 = locvar2 ? abs(locvar2) : 384 * FRACUNIT;
+
+		P_LookForPlayers(actor, true, false, actor->extravalue1);
+
+		if (actor->target && P_AproxDistance(actor->target->x - originx, actor->target->y - originy) < actor->extravalue1)
+		{
+			actor->extravalue2 = 1;
+		 	P_TeleportMove(actor, actor->target->x, actor->target->y, actor->target->z);
+		}
+		else if(actor->extravalue2)
+		{
+			actor->extravalue2 = 0;
+			P_TeleportMove(actor, originx, originy, originz);
+		}
+	}
+}
+
+// Internal Flicky bubbling function.
+void P_InternalFlickyBubble(mobj_t *actor)
+{
+	if (actor->eflags & MFE_UNDERWATER)
+	{
+		mobj_t *overlay;
+
+		if (!((actor->z + 3*actor->height/2) < actor->watertop) || !mobjinfo[actor->type].raisestate || actor->tracer)
+			return;
+
+		overlay = P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY);
+		P_SetMobjStateNF(overlay, mobjinfo[actor->type].raisestate);
+		P_SetTarget(&actor->tracer, overlay);
+		P_SetTarget(&overlay->target, actor);
+		return;
+	}
+
+	if (!actor->tracer || P_MobjWasRemoved(actor->tracer))
+		return;
+
+	P_RemoveMobj(actor->tracer);
+	P_SetTarget(&actor->tracer, NULL);
+}
+
+// Function: A_FlickyAim
+//
+// Description: Flicky aiming function.
+//
+// var1 = how far around the target (in angle constants) the flicky should look
+// var2 = distance from target to aim for
+//
+void A_FlickyAim(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	boolean flickyhitwall = false;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_FlickyAim", actor))
+		return;
+#endif
+
+	if ((actor->momx == actor->momy && actor->momy == 0)
+		|| (actor->target && P_IsFlickyCenter(actor->target->type)
+			&& actor->target->extravalue1 && (actor->target->flags & MF_SLIDEME)
+			&& P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y) >= actor->target->extravalue1))
+		flickyhitwall = true;
+
+	P_InternalFlickyBubble(actor);
+	P_InstaThrust(actor, 0, 0);
+
+	if (!actor->target)
+	{
+		P_LookForPlayers(actor, true, false, 0);
+		actor->angle = P_RandomKey(36)*ANG10;
+		return;
+	}
+
+	if (actor->fuse > 2*TICRATE)
+	{
+		angle_t posvar;
+		fixed_t chasevar, chasex, chasey;
+
+		if (flickyhitwall)
+			actor->movedir *= -1;
+
+		posvar = ((R_PointToAngle2(actor->target->x, actor->target->y, actor->x, actor->y) + actor->movedir*locvar1) >> ANGLETOFINESHIFT) & FINEMASK;
+		chasevar = FixedSqrt(max(FRACUNIT, P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y) - locvar2)) + locvar2;
+
+		chasex = actor->target->x + FixedMul(FINECOSINE(posvar), chasevar);
+		chasey = actor->target->y + FixedMul(FINESINE(posvar), chasevar);
+
+		if (P_AproxDistance(chasex - actor->x, chasey - actor->y))
+			actor->angle = R_PointToAngle2(actor->x, actor->y, chasex, chasey);
+	}
+	else if (flickyhitwall)
+	{
+		if (actor->target && P_IsFlickyCenter(actor->target->type))
+			actor->angle = R_PointToAngle2(actor->target->x, actor->target->y, actor->x, actor->y) + P_RandomRange(112, 248) * ANG1;
+		else
+			actor->angle += P_RandomRange(112, 248)*ANG1;
+		actor->threshold = 0;
+	}
+}
+
+//Internal Flicky flying function. Also usuable as an underwater swim thrust.
+void P_InternalFlickyFly(mobj_t *actor, fixed_t flyspeed, fixed_t targetdist, fixed_t chasez)
+{
+	angle_t vertangle;
+
+	flyspeed = FixedMul(flyspeed, actor->scale);
+	actor->flags |= MF_NOGRAVITY;
+
+	var1 = ANG30;
+	var2 = 32*FRACUNIT;
+	A_FlickyAim(actor);
+
+	chasez *= 8;
+	if (!actor->target || !(actor->fuse > 2*TICRATE))
+		chasez += ((actor->eflags & MFE_VERTICALFLIP) ? actor->ceilingz - 24*FRACUNIT : actor->floorz + 24*FRACUNIT);
+	else
+	{
+		fixed_t add = actor->target->z + (actor->target->height - actor->height)/2;
+		if (add > (actor->ceilingz - 24*actor->scale - actor->height))
+			add = actor->ceilingz - 24*actor->scale - actor->height;
+		else if (add < (actor->floorz + 24*actor->scale))
+			add = actor->floorz + 24*actor->scale;
+		chasez += add;
+	}
+
+	if (!targetdist)
+		targetdist = 16*FRACUNIT; //Default!
+
+	if (actor->target && abs(chasez - actor->z) > targetdist)
+		targetdist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
+
+	if (actor->target
+		&& P_IsFlickyCenter(actor->target->type)
+		&& (actor->target->flags & MF_SLIDEME))
+		vertangle = 0;
+	else
+		vertangle = (R_PointToAngle2(0, actor->z, targetdist, chasez) >> ANGLETOFINESHIFT) & FINEMASK;
+
+	P_InstaThrust(actor, actor->angle, FixedMul(FINECOSINE(vertangle), flyspeed));
+	actor->momz = FixedMul(FINESINE(vertangle), flyspeed);
+}
+
+// Function: A_FlickyFly
+//
+// Description: Flicky flying function.
+//
+// var1 = how fast to fly
+// var2 = how far ahead the target should be considered
+//
+void A_FlickyFly(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_FlickyFly", actor))
+		return;
+#endif
+	P_InternalFlickyFly(actor, locvar1, locvar2,
+	FINECOSINE((((actor->fuse % 36) * ANG10) >> ANGLETOFINESHIFT) & FINEMASK)
+	);
+}
+
+// Function: A_FlickySoar
+//
+// Description: Flicky soaring function - specific to puffin.
+//
+// var1 = how fast to fly
+// var2 = how far ahead the target should be considered
+//
+void A_FlickySoar(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_FlickySoar", actor))
+		return;
+#endif
+	P_InternalFlickyFly(actor, locvar1, locvar2,
+	2*(FRACUNIT/2 - abs(FINECOSINE((((actor->fuse % 144) * 5*ANG1/2) >> ANGLETOFINESHIFT) & FINEMASK)))
+	);
+
+	if (P_MobjFlip(actor)*actor->momz > 0 && actor->frame == 1 && actor->sprite == SPR_FL10)
+		actor->frame = 3;
+}
+
+//Function: A_FlickyCoast
+//
+// Description: Flicky swim-coasting function.
+//
+// var1 = speed to change state upon reaching
+// var2 = state to change to upon slowing down
+// the spawnstate of the mobj = state to change to when above water
+//
+void A_FlickyCoast(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_FlickyCoast", actor))
+		return;
+#endif
+	if (actor->eflags & MFE_UNDERWATER)
+	{
+		actor->momx = (11*actor->momx)/12;
+		actor->momy = (11*actor->momy)/12;
+		actor->momz = (11*actor->momz)/12;
+
+		if (P_AproxDistance(P_AproxDistance(actor->momx, actor->momy), actor->momz) < locvar1)
+			P_SetMobjState(actor, locvar2);
+
+		return;
+	}
+
+	actor->flags &= ~MF_NOGRAVITY;
+	P_SetMobjState(actor, mobjinfo[actor->type].spawnstate);
+}
+
+// Internal Flicky hopping function.
+void P_InternalFlickyHop(mobj_t *actor, fixed_t momz, fixed_t momh, angle_t angle)
+{
+	if (((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)
+	|| ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)))
+	{
+		if (momz)
+		{
+			if (actor->eflags & MFE_UNDERWATER)
+				momz = FixedDiv(momz, FixedSqrt(3*FRACUNIT));
+			P_SetObjectMomZ(actor, momz, false);
+		}
+		P_InstaThrust(actor, angle, FixedMul(momh, actor->scale));
+	}
+}
+
+// Function: A_FlickyHop
+//
+// Description: Flicky hopping function.
+//
+// var1 = vertical thrust
+// var2 = horizontal thrust
+//
+void A_FlickyHop(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_FlickyHop", actor))
+		return;
+#endif
+	P_InternalFlickyHop(actor, locvar1, locvar2, actor->angle);
+}
+
+// Function: A_FlickyFlounder
+//
+// Description: Flicky floundering function.
+//
+// var1 = intended vertical thrust
+// var2 = intended horizontal thrust
+//
+void A_FlickyFlounder(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	angle_t hopangle;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_FlickyFlounder", actor))
+		return;
+#endif
+	locvar1 *= (P_RandomKey(2) + 1);
+	locvar2 *= (P_RandomKey(2) + 1);
+	hopangle = (actor->angle + (P_RandomKey(9) - 4)*ANG2);
+	P_InternalFlickyHop(actor, locvar1, locvar2, hopangle);
+}
+
+// Function: A_FlickyCheck
+//
+// Description: Flicky airtime check function.
+//
+// var1 = state to change to upon touching the floor
+// var2 = state to change to upon falling
+// the meleestate of the mobj = state to change to when underwater
+//
+void A_FlickyCheck(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_FlickyCheck", actor))
+		return;
+#endif
+	if (actor->target
+		&& P_IsFlickyCenter(actor->target->type)
+		&& (actor->target->flags & MF_GRENADEBOUNCE))
+	{
+		if (!(actor->target->flags & MF_NOCLIPTHING)) // no hopping
+		{
+			actor->momz = 0;
+			actor->flags |= MF_NOGRAVITY;
+		}
+		actor->flags |= MF_NOCLIP | MF_NOBLOCKMAP | MF_SCENERY;
+		P_SetMobjState(actor, mobjinfo[actor->type].seestate);
+	}
+	else if (locvar2 && P_MobjFlip(actor)*actor->momz < 1)
+		P_SetMobjState(actor, locvar2);
+	else if (locvar1 && ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)
+	|| ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)))
+		P_SetMobjState(actor, locvar1);
+	else if (mobjinfo[actor->type].meleestate && (actor->eflags & MFE_UNDERWATER))
+		P_SetMobjState(actor, mobjinfo[actor->type].meleestate);
+	P_InternalFlickyBubble(actor);
+}
+
+// Function: A_FlickyHeightCheck
+//
+// Description: Flicky height check function.
+//
+// var1 = state to change to when falling below height relative to target
+// var2 = height relative to target to change state at
+//
+void A_FlickyHeightCheck(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_FlickyHeightCheck", actor))
+		return;
+#endif
+	if (actor->target
+		&& P_IsFlickyCenter(actor->target->type)
+		&& (actor->target->flags & MF_GRENADEBOUNCE))
+	{
+		if (!(actor->target->flags & MF_NOCLIPTHING)) // no hopping
+		{
+			actor->momz = 0;
+			actor->flags |= MF_NOGRAVITY;
+		}
+		actor->flags |= MF_NOCLIP | MF_NOBLOCKMAP | MF_SCENERY;
+		P_SetMobjState(actor, mobjinfo[actor->type].seestate);
+	}
+	else if (locvar1 && actor->target && P_MobjFlip(actor)*actor->momz < 1
+	&& ((P_MobjFlip(actor)*((actor->z + actor->height/2) - (actor->target->z + actor->target->height/2)) < locvar2)
+	|| (actor->z - actor->height < actor->floorz) || (actor->z + 2*actor->height > actor->ceilingz)))
+		P_SetMobjState(actor, locvar1);
+	P_InternalFlickyBubble(actor);
+}
+
+// Function: A_FlickyFlutter
+//
+// Description: Flicky fluttering function - specific to chicken.
+//
+// var1 = state to change to upon touching the floor
+// var2 = state to change to upon falling
+// the meleestate of the mobj = state to change to when underwater
+//
+void A_FlickyFlutter(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_FlickyFlutter", actor))
+		return;
+#endif
+	var1 = locvar1;
+	var2 = locvar2;
+	A_FlickyCheck(actor);
+
+	var1 = ANG30;
+	var2 = 32*FRACUNIT;
+	A_FlickyAim(actor);
+
+	P_InstaThrust(actor, actor->angle, 2*actor->scale);
+	if (P_MobjFlip(actor)*actor->momz < -FRACUNIT/2)
+		actor->momz = -P_MobjFlip(actor)*actor->scale/2;
+}
+
+#undef FLICKYHITWALL
+
+// Function: A_FlameParticle
+//
+// Description: Creates the mobj's painchance at a random position around the object's radius.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_FlameParticle(mobj_t *actor)
+{
+	mobjtype_t type = (mobjtype_t)(mobjinfo[actor->type].painchance);
+	fixed_t rad, hei;
+	mobj_t *particle;
+	//INT32 locvar1 = var1;
+	//INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_FlameParticle", actor))
+		return;
+#endif
+
+	if (!type)
+		return;
+
+	rad = actor->radius>>FRACBITS;
+	hei = actor->height>>FRACBITS;
+	particle = P_SpawnMobjFromMobj(actor,
+		P_RandomRange(rad, -rad)<<FRACBITS,
+		P_RandomRange(rad, -rad)<<FRACBITS,
+		P_RandomRange(hei/2, hei)<<FRACBITS,
+		type);
+	P_SetObjectMomZ(particle, 2<<FRACBITS, false);
+}
+
+// Function: A_FadeOverlay
+//
+// Description: Makes a pretty overlay (primarily for super/NiGHTS transformation).
+//
+// var1 = bit 1 = bit 1 = don't make fast, bit 2 = don't set tracer
+// var2 = unused
+//
+void A_FadeOverlay(mobj_t *actor)
+{
+	mobj_t *fade;
+	INT32 locvar1 = var1;
+	//INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_FadeOverlay", actor))
+		return;
+#endif
+
+	fade = P_SpawnGhostMobj(actor);
+	fade->frame = actor->frame;
+
+	if (!(locvar1 & 1))
+	{
+		fade->fuse = 15;
+		fade->flags2 |= MF2_BOSSNOTRAP;
+	}
+	else
+		fade->fuse = 20;
+
+	if (!(locvar1 & 2))
+		P_SetTarget(&actor->tracer, fade);
+}
+
+// Function: A_Boss5Jump
+//
+// Description: Makes an object jump in an arc to land on their tracer precicely.
+//				Adapted from A_BrakLobShot, see there for explanation.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_Boss5Jump(mobj_t *actor)
+{
+	fixed_t v; // Velocity to jump at
+	fixed_t a1, a2, aToUse; // Velocity squared
+	fixed_t g; // Gravity
+	fixed_t x; // Horizontal difference
+	INT32 x_int; // x! But in integer form!
+	fixed_t y; // Vertical difference (yes that's normally z in SRB2 shut up)
+	INT32 y_int; // y! But in integer form!
+	INT32 intHypotenuse; // x^2 + y^2. Frequently overflows fixed point, hence why we need integers proper.
+	fixed_t fixedHypotenuse; // However, we can work around that and still get a fixed-point number.
+	angle_t theta; // Angle of attack
+	// INT32 locvar1 = var1;
+	// INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Boss5Jump", actor))
+		return;
+#endif
+
+	if (!actor->tracer)
+		return; // Don't even bother if we've got nothing to aim at.
+
+	// Look up actor's current gravity situation
+	if (actor->subsector->sector->gravity)
+		g = FixedMul(gravity,(FixedDiv(*actor->subsector->sector->gravity>>FRACBITS, 1000)));
+	else
+		g = gravity;
+
+	// Look up distance between actor and its tracer
+	x = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y);
+	// Look up height difference between actor and its tracer
+	y = actor->tracer->z - actor->z;
+
+	// Get x^2 + y^2. Have to do it in a roundabout manner, because this overflows fixed_t way too easily otherwise.
+	x_int = x>>FRACBITS;
+	y_int = y>>FRACBITS;
+	intHypotenuse = (x_int*x_int) + (y_int*y_int);
+	fixedHypotenuse = FixedSqrt(intHypotenuse) *256;
+
+	// a = g(y+/-sqrt(x^2+y^2)). a1 can be +, a2 can be -.
+	a1 = FixedMul(g,y+fixedHypotenuse);
+	a2 = FixedMul(g,y-fixedHypotenuse);
+
+	// Determine which one isn't actually an imaginary number (or the smaller of the two, if both are real), and use that for v.
+	if (a1 < 0 || a2 < 0)
+	{
+		if (a1 < 0 && a2 < 0)
+		{
+			//Somehow, v^2 is negative in both cases. v is therefore imaginary and something is horribly wrong. Abort!
+			return;
+		}
+		// Just find which one's NOT negative, and use that
+		aToUse = max(a1,a2);
+	}
+	else
+	{
+		// Both are positive; use whichever's smaller so it can decay faster
+		aToUse = min(a1,a2);
+	}
+	v = FixedSqrt(aToUse);
+	// Okay, so we know the velocity. Let's actually find theta.
+	// We can cut the "+/- sqrt" part out entirely, since v was calculated specifically for it to equal zero. So:
+	//theta = tantoangle[FixedDiv(aToUse,FixedMul(g,x)) >> DBITS];
+	theta = tantoangle[SlopeDiv(aToUse,FixedMul(g,x))];
+
+	// Okay, complicated math done. Let's make this object jump already.
+	A_FaceTracer(actor);
+
+	if (actor->eflags & MFE_VERTICALFLIP)
+		actor->z--;
+	else
+		actor->z++;
+
+	// Horizontal axes first. First parameter is initial horizontal impulse, second is to correct its angle.
+	fixedHypotenuse = FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)); // variable reuse
+	actor->momx = FixedMul(fixedHypotenuse, FINECOSINE(actor->angle >> ANGLETOFINESHIFT));
+	actor->momy = FixedMul(fixedHypotenuse, FINESINE(actor->angle >> ANGLETOFINESHIFT));
+	// Then the vertical axis. No angle-correction needed here.
+	actor->momz = FixedMul(v, FINESINE(theta >> ANGLETOFINESHIFT));
+	// I hope that's all that's needed, ugh
+}
+
+// Function: A_LightBeamReset
+// Description: Resets momentum and position for DSZ's projecting light beams
+//
+// var1 = unused
+// var2 = unused
+//
+void A_LightBeamReset(mobj_t *actor)
+{
+	// INT32 locvar1 = var1;
+	// INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_LightBeamReset", actor))
+		return;
+#endif
+
+	actor->destscale = FRACUNIT + P_SignedRandom()*FRACUNIT/256;
+	P_SetScale(actor, actor->destscale);
+
+	if (!actor->spawnpoint)
+		return; // this can't work properly welp
+
+	actor->momx = -(P_SignedRandom()*FINESINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/128;
+	actor->momy = (P_SignedRandom()*FINECOSINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/128;
+	actor->momz = (P_SignedRandom()*FRACUNIT)/128;
+
+	P_TeleportMove(actor,
+		actor->spawnpoint->x*FRACUNIT - (P_SignedRandom()*FINESINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/2,
+		actor->spawnpoint->y*FRACUNIT + (P_SignedRandom()*FINECOSINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/2,
+		actor->spawnpoint->z*FRACUNIT + (P_SignedRandom()*FRACUNIT)/2);
+}
+
+// Function: A_MineExplode
+// Description: Handles the explosion of a DSZ mine.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_MineExplode(mobj_t *actor)
+{
+	// INT32 locvar1 = var1;
+	// INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_MineExplode", actor))
+		return;
+#endif
+
+	A_Scream(actor);
+	actor->flags = MF_NOGRAVITY|MF_NOCLIP;
+
+	quake.epicenter = NULL;
+	quake.radius = 512*FRACUNIT;
+	quake.intensity = 8*FRACUNIT;
+	quake.time = TICRATE/3;
+
+	P_RadiusAttack(actor, actor->tracer, 192*FRACUNIT, DMG_CANHURTSELF);
+	P_MobjCheckWater(actor);
+
+	{
+#define dist 64
+		UINT8 i;
+		mobjtype_t type = ((actor->eflags & MFE_UNDERWATER) ? MT_UWEXPLODE : MT_BOSSEXPLODE);
+		S_StartSound(actor, ((actor->eflags & MFE_UNDERWATER) ? sfx_s3k57 : sfx_s3k4e));
+		P_SpawnMobj(actor->x, actor->y, actor->z, type);
+		for (i = 0; i < 16; i++)
+		{
+			mobj_t *b = P_SpawnMobj(actor->x+P_RandomRange(-dist, dist)*FRACUNIT,
+				actor->y+P_RandomRange(-dist, dist)*FRACUNIT,
+				actor->z+P_RandomRange(((actor->eflags & MFE_UNDERWATER) ? -dist : 0), dist)*FRACUNIT,
+				type);
+			fixed_t dx = b->x - actor->x, dy = b->y - actor->y, dz = b->z - actor->z;
+			fixed_t dm = P_AproxDistance(dz, P_AproxDistance(dy, dx));
+			b->momx = FixedDiv(dx, dm)*3;
+			b->momy = FixedDiv(dy, dm)*3;
+			b->momz = FixedDiv(dz, dm)*3;
+			if ((actor->watertop == INT32_MAX) || (b->z + b->height > actor->watertop))
+				b->flags &= ~MF_NOGRAVITY;
+		}
+#undef dist
+
+		if (actor->watertop != INT32_MAX)
+			P_SpawnMobj(actor->x, actor->y, actor->watertop, MT_SPLISH);
+	}
+}
+
+// Function: A_MineRange
+// Description: If the target gets too close, change the state to meleestate.
+//
+// var1 = Distance to alert at
+// var2 = unused
+//
+void A_MineRange(mobj_t *actor)
+{
+	fixed_t dm;
+	INT32 locvar1 = var1;
+	// INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_MineRange", actor))
+		return;
+#endif
+
+	if (!actor->target)
+		return;
+
+	dm = P_AproxDistance(actor->z - actor->target->z, P_AproxDistance(actor->y - actor->target->y, actor->x - actor->target->x));
+	if ((dm>>FRACBITS) < locvar1)
+		P_SetMobjState(actor, actor->info->meleestate);
+}
+
+// Function: A_ConnectToGround
+// Description: Create a palm tree trunk/mine chain.
+//
+// var1 = Object type to connect to ground
+// var2 = Object type to place on ground
+//
+void A_ConnectToGround(mobj_t *actor)
+{
+	mobj_t *work;
+	fixed_t workz;
+	fixed_t workh;
+	SINT8 dir;
+	angle_t ang;
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_ConnectToGround", actor))
+		return;
+#endif
+
+	if (actor->subsector->sector->ffloors)
+		P_AdjustMobjFloorZ_FFloors(actor, actor->subsector->sector, 2);
+
+	if (actor->flags2 & MF2_OBJECTFLIP)
+	{
+		workz = actor->ceilingz - (actor->z + actor->height);
+		dir = -1;
+	}
+	else
+	{
+		workz = actor->floorz - actor->z;
+		dir = 1;
+	}
+
+	if (locvar2)
+	{
+		workh = FixedMul(mobjinfo[locvar2].height, actor->scale);
+		if (actor->flags2 & MF2_OBJECTFLIP)
+			workz -= workh;
+		work = P_SpawnMobjFromMobj(actor, 0, 0, workz, locvar2);
+		workz += dir*workh;
+	}
+
+	if (!locvar1)
+		return;
+
+	if (!(workh = FixedMul(mobjinfo[locvar1].height, actor->scale)))
+		return;
+
+	if (actor->flags2 & MF2_OBJECTFLIP)
+		workz -= workh;
+
+	ang = actor->angle + ANGLE_45;
+	while (dir*workz < 0)
+	{
+		work = P_SpawnMobjFromMobj(actor, 0, 0, workz, locvar1);
+		if (work)
+			work->angle = ang;
+		ang += ANGLE_90;
+		workz += dir*workh;
+	}
+
+	if (workz != 0)
+		actor->z += workz;
+}
+
+// Function: A_SpawnParticleRelative
+//
+// Description: Spawns a particle effect relative to the location of the actor
+//
+// var1:
+//		var1 >> 16 = x
+//		var1 & 65535 = y
+// var2:
+//		var2 >> 16 = z
+//		var2 & 65535 = state
+//
+void A_SpawnParticleRelative(mobj_t *actor)
+{
+	INT16 x, y, z; // Want to be sure we can use negative values
+	statenum_t state;
+	mobj_t *mo;
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_SpawnParticleRelative", actor))
+		return;
+#endif
+
+	CONS_Debug(DBG_GAMELOGIC, "A_SpawnParticleRelative called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
+
+	x = (INT16)(locvar1>>16);
+	y = (INT16)(locvar1&65535);
+	z = (INT16)(locvar2>>16);
+	state = (statenum_t)(locvar2&65535);
+
+	// Spawn objects correctly in reverse gravity.
+	// NOTE: Doing actor->z + actor->height is the bottom of the object while the object has reverse gravity. - Flame
+	mo = P_SpawnMobj(actor->x + FixedMul(x<<FRACBITS, actor->scale),
+		actor->y + FixedMul(y<<FRACBITS, actor->scale),
+		(actor->eflags & MFE_VERTICALFLIP) ? ((actor->z + actor->height - mobjinfo[MT_PARTICLE].height) - FixedMul(z<<FRACBITS, actor->scale)) : (actor->z + FixedMul(z<<FRACBITS, actor->scale)), MT_PARTICLE);
+
+	// Spawn objects with an angle matching the spawner's, rather than spawning Eastwards - Monster Iestyn
+	mo->angle = actor->angle;
+
+	if (actor->eflags & MFE_VERTICALFLIP)
+		mo->flags2 |= MF2_OBJECTFLIP;
+
+	P_SetMobjState(mo, state);
+}
+
+// Function: A_MultiShotDist
+//
+// Description: Spawns multiple shots based on player proximity
+//
+// var1 = same as A_MultiShot
+// var2 = same as A_MultiShot
+//
+void A_MultiShotDist(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_MultiShotDist", actor))
+		return;
+#endif
+
+	{
+		UINT8 i;
+		// Quick! Look through players!
+		// Don't spawn dust unless a player is relatively close by (var1).
+		for (i = 0; i < MAXPLAYERS; ++i)
+			if (playeringame[i] && players[i].mo
+			 && P_AproxDistance(actor->x - players[i].mo->x, actor->y - players[i].mo->y) < (1600<<FRACBITS))
+				break; // Stop looking.
+		if (i == MAXPLAYERS)
+			return; // don't make bubble!
+	}
+
+	var1 = locvar1;
+	var2 = locvar2;
+	A_MultiShot(actor);
+}
+
+// Function: A_WhoCaresIfYourSonIsABee
+//
+// Description: Makes a child object, storing the number of created children in the parent's extravalue1.
+//
+// var1 = mobjtype of child
+//		var2 >> 16 = mobjtype of child
+//		var2 & 65535 = vertical momentum
+// var2:
+//		var2 >> 16 = forward offset
+//		var2 & 65535 = vertical offset
+//
+void A_WhoCaresIfYourSonIsABee(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	fixed_t foffsetx;
+	fixed_t foffsety;
+	mobj_t *son;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_WhoCaresIfYourSonIsABee", actor))
+		return;
+#endif
+
+	A_FaceTarget(actor);
+
+	if (actor->extravalue1)
+		actor->extravalue1--;
+
+	if (actor->info->attacksound)
+		S_StartSound(actor, actor->info->attacksound);
+
+	foffsetx = P_ReturnThrustX(actor, actor->angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale));
+	foffsety = P_ReturnThrustY(actor, actor->angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale));
+
+	if (!(son = P_SpawnMobjFromMobj(actor, foffsetx, foffsety, (locvar2&65535)*FRACUNIT, (mobjtype_t)(locvar1 >> 16))))
+		return;
+
+	P_SetObjectMomZ(son, (locvar1 & 65535)<<FRACBITS, true);
+
+	P_SetTarget(&son->tracer, actor);
+	P_SetTarget(&son->target, actor->target);
+}
+
+// Function: A_ParentTriesToSleep
+//
+// Description: If extravalue1 is less than or equal to var1, go to var2.
+//
+// var1 = state to go to when extravalue1
+// var2 = unused
+//
+void A_ParentTriesToSleep(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	//INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_ParentTriesToSleep", actor))
+		return;
+#endif
+
+	if (actor->extravalue1)
+	{
+		if (actor->info->seesound)
+			S_StartSound(actor, actor->info->seesound);
+		actor->reactiontime = 0;
+		P_SetMobjState(actor, locvar1);
+	}
+	else if (!actor->reactiontime)
+	{
+		actor->reactiontime = 1;
+		if (actor->info->activesound) // more like INactivesound doy hoy hoy
+			S_StartSound(actor, actor->info->activesound);
+	}
+}
+
+
+// Function: A_CryingToMomma
+//
+// Description: If you're a child, let your parent know something's happened to you through extravalue1. Also, prepare to die.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_CryingToMomma(mobj_t *actor)
+{
+	//INT32 locvar1 = var1;
+	//INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_CryingToMomma", actor))
+		return;
+#endif
+
+	if (actor->tracer)
+		actor->tracer->extravalue1++;
+
+	actor->momx = actor->momy = actor->momz = 0;
+
+	P_UnsetThingPosition(actor);
+	if (sector_list)
+	{
+		P_DelSeclist(sector_list);
+		sector_list = NULL;
+	}
+	actor->flags = MF_NOBLOCKMAP|MF_NOCLIPTHING;
+	P_SetThingPosition(actor);
+}
+
+// Function: A_CheckFlags2
+//
+// Description: If actor->flags2 & var1, goto var2.
+//
+// var1 = mask
+// var2 = state to go
+//
+void A_CheckFlags2(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_CheckFlags2", actor))
+		return;
+#endif
+
+	if (actor->flags2 & locvar1)
+		P_SetMobjState(actor, (statenum_t)locvar2);
+}
diff --git a/src/p_floor.c b/src/p_floor.c
index 427dc293d36b99817d1276e5e524d3249ba0d398..8f1afaeba1712b93b7f91c82a5839857744c8172 100644
--- a/src/p_floor.c
+++ b/src/p_floor.c
@@ -1839,6 +1839,7 @@ void T_ThwompSector(levelspecthink_t *thwomp)
 #define ceilingwasheight vars[5]
 	fixed_t thwompx, thwompy;
 	sector_t *actionsector;
+	ffloor_t *rover = NULL;
 	INT32 secnum;
 
 	// If you just crashed down, wait a second before coming back up.
@@ -1853,7 +1854,16 @@ void T_ThwompSector(levelspecthink_t *thwomp)
 	secnum = P_FindSectorFromTag((INT16)thwomp->vars[0], -1);
 
 	if (secnum > 0)
+	{
 		actionsector = &sectors[secnum];
+
+		// Look for thwomp FFloor
+		for (rover = actionsector->ffloors; rover; rover = rover->next)
+		{
+			if (rover->master == thwomp->sourceline)
+				break;
+		}
+	}
 	else
 		return; // Bad bad bad!
 
@@ -1942,10 +1952,13 @@ void T_ThwompSector(levelspecthink_t *thwomp)
 		{
 			mobj_t *mp = (void *)&actionsector->soundorg;
 
-			if (thwomp->sourceline->flags & ML_EFFECT4)
-				S_StartSound(mp, sides[thwomp->sourceline->sidenum[0]].textureoffset>>FRACBITS);
-			else
-				S_StartSound(mp, sfx_thwomp);
+			if (!rover || (rover->flags & FF_EXISTS))
+			{
+				if (thwomp->sourceline->flags & ML_EFFECT4)
+					S_StartSound(mp, sides[thwomp->sourceline->sidenum[0]].textureoffset>>FRACBITS);
+				else
+					S_StartSound(mp, sfx_thwomp);
+			}
 
 			thwomp->direction = 1; // start heading back up
 			thwomp->distance = TICRATE; // but only after a small delay
@@ -1959,18 +1972,22 @@ void T_ThwompSector(levelspecthink_t *thwomp)
 		thinker_t *th;
 		mobj_t *mo;
 
-		// scan the thinkers to find players!
-		for (th = thinkercap.next; th != &thinkercap; th = th->next)
+		if (!rover || (rover->flags & FF_EXISTS))
 		{
-			if (th->function.acp1 != (actionf_p1)P_MobjThinker)
-				continue;
-
-			mo = (mobj_t *)th;
-			if (mo->type == MT_PLAYER && mo->health && mo->z <= thwomp->sector->ceilingheight
-				&& P_AproxDistance(thwompx - mo->x, thwompy - mo->y) <= 96*FRACUNIT)
+			// scan the thinkers to find players!
+			for (th = thinkercap.next; th != &thinkercap; th = th->next)
 			{
-				thwomp->direction = -1;
-				break;
+				if (th->function.acp1 != (actionf_p1)P_MobjThinker)
+					continue;
+
+				mo = (mobj_t *)th;
+				if (mo->type == MT_PLAYER && mo->health && mo->player && !mo->player->spectator 
+				    && mo->z <= thwomp->sector->ceilingheight
+					&& P_AproxDistance(thwompx - mo->x, thwompy - mo->y) <= 96*FRACUNIT)
+				{
+					thwomp->direction = -1;
+					break;
+				}
 			}
 		}
 
@@ -3318,7 +3335,7 @@ INT32 EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher)
 		P_SetThingPosition(thing);
 		if (thing->flags & MF_SHOOTABLE)
 			P_DamageMobj(thing, puncher, puncher, 1, 0);
-		else if (thing->type == MT_RING || thing->type == MT_COIN)
+		else if (thing->type == MT_RING || thing->type == MT_COIN || thing->type == MT_TOKEN)
 		{
 			thing->momz = FixedMul(3*FRACUNIT, thing->scale);
 			P_TouchSpecialThing(thing, puncher, false);
diff --git a/src/p_inter.c b/src/p_inter.c
index 7892e0bcfa6f9f243808126816d9b3a8d5a43236..63abf4c0ed1601934ffa97bfd5e86b6bd31a072f 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -23,6 +23,7 @@
 #include "hu_stuff.h"
 #include "lua_hook.h"
 #include "m_cond.h" // unlockables, emblems, etc
+#include "p_setup.h"
 #include "m_cheat.h" // objectplace
 #include "m_misc.h"
 #include "v_video.h" // video flags for CEchos
@@ -103,8 +104,13 @@ void P_ClearStarPost(INT32 postnum)
 
 		mo2 = (mobj_t *)th;
 
-		if (mo2->type == MT_STARPOST && mo2->health <= postnum)
-			P_SetMobjState(mo2, mo2->info->seestate);
+		if (mo2->type != MT_STARPOST)
+			continue;
+
+		if (mo2->health > postnum)
+			continue;
+
+		P_SetMobjState(mo2, mo2->info->seestate);
 	}
 	return;
 }
@@ -176,14 +182,14 @@ void P_DoNightsScore(player_t *player)
 			{
 				if (++players[i].linkcount > players[i].maxlink)
 					players[i].maxlink = players[i].linkcount;
-				players[i].linktimer = 2*TICRATE;
+				players[i].linktimer = nightslinktics;
 			}
 	}
 	else // Individual link counts
 	{
 		if (++player->linkcount > player->maxlink)
 			player->maxlink = player->linkcount;
-		player->linktimer = 2*TICRATE;
+		player->linktimer = nightslinktics;
 	}
 
 	if (player->linkcount < 10)
@@ -349,8 +355,8 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 	if (player->spectator)
 		return;
 
-	// Ignore eggman in "ouchie" mode
-	if (special->flags & MF_BOSS && special->flags2 & MF2_FRET)
+	// Ignore multihits in "ouchie" mode
+	if (special->flags & (MF_ENEMY|MF_BOSS) && special->flags2 & MF2_FRET)
 		return;
 
 #ifdef HAVE_BLUA
@@ -363,81 +369,59 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 	? (((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) ? 1 : 2)
 	: 0);
 
-	if (special->flags & MF_BOSS)
+	if ((special->flags & (MF_ENEMY|MF_BOSS)) && !(special->flags & MF_MISSILE))
 	{
+		////////////////////////////////////////////////////////
+		/////ENEMIES & BOSSES!!/////////////////////////////////
+		////////////////////////////////////////////////////////
+
 		if (special->type == MT_BLACKEGGMAN)
 		{
 			P_DamageMobj(toucher, special, special, 1, 0); // ouch
 			return;
 		}
 
-		if (((player->powers[pw_carry] == CR_NIGHTSMODE) && (player->pflags & PF_DRILLING))
-		|| ((player->pflags & PF_JUMPED) && (!(player->pflags & PF_NOJUMPDAMAGE) || (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY)))
-		|| (player->pflags & (PF_SPINNING|PF_GLIDING))
-		|| (player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2)
-		|| ((player->charflags & SF_STOMPDAMAGE || player->pflags & PF_BOUNCING) && (P_MobjFlip(toucher)*(toucher->z - (special->z + special->height/2)) > 0) && (P_MobjFlip(toucher)*toucher->momz < 0))
-		|| player->powers[pw_invulnerability] || player->powers[pw_super]
-		|| elementalpierce) // Do you possess the ability to subdue the object?
+		if (special->type == MT_BIGMINE)
 		{
-			if ((P_MobjFlip(toucher)*toucher->momz < 0) && (elementalpierce != 1))
-			{
-				if (elementalpierce == 2)
-					P_DoBubbleBounce(player);
-				else if (!(player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2))
-					toucher->momz = -toucher->momz;
-			}
-			if (player->pflags & PF_BOUNCING)
-				P_DoAbilityBounce(player, false);
-			toucher->momx = -toucher->momx;
-			toucher->momy = -toucher->momy;
-			P_DamageMobj(special, toucher, toucher, 1, 0);
-		}
-		else if (((toucher->z < special->z && !(toucher->eflags & MFE_VERTICALFLIP))
-		|| (toucher->z + toucher->height > special->z + special->height && (toucher->eflags & MFE_VERTICALFLIP)))
-		&& player->charability == CA_FLY
-		&& (player->powers[pw_tailsfly]
-		|| toucher->state-states == S_PLAY_FLY_TIRED)) // Tails can shred stuff with his propeller.
-		{
-			toucher->momz = -toucher->momz/2;
-
-			P_DamageMobj(special, toucher, toucher, 1, 0);
+			special->momx = toucher->momx/3;
+			special->momy = toucher->momy/3;
+			special->momz = toucher->momz/3;
+			toucher->momx /= -8;
+			toucher->momy /= -8;
+			toucher->momz /= -8;
+			special->flags &= ~MF_SPECIAL;
+			if (special->info->activesound)
+				S_StartSound(special, special->info->activesound);
+			P_SetTarget(&special->tracer, toucher);
+			player->homing = 0;
+			return;
 		}
-		else
-			P_DamageMobj(toucher, special, special, 1, 0);
 
-		return;
-	}
-	else if ((special->flags & MF_ENEMY) && !(special->flags & MF_MISSILE))
-	{
-		////////////////////////////////////////////////////////
-		/////ENEMIES!!//////////////////////////////////////////
-		////////////////////////////////////////////////////////
-		if (special->type == MT_GSNAPPER && !(((player->powers[pw_carry] == CR_NIGHTSMODE) && (player->pflags & PF_DRILLING))
-		|| player->powers[pw_invulnerability] || player->powers[pw_super] || elementalpierce)
+		if (special->type == MT_GSNAPPER && !elementalpierce
 		&& toucher->z < special->z + special->height && toucher->z + toucher->height > special->z
-		&& !(player->powers[pw_shield] & SH_PROTECTSPIKE))
-		{
-			// Can only hit snapper from above
-			P_DamageMobj(toucher, special, special, 1, DMG_SPIKE);
-		}
-		else if (special->type == MT_SHARP
-		&& ((special->state == &states[special->info->xdeathstate]) || (toucher->z > special->z + special->height/2))
-		&& !(player->powers[pw_shield] & SH_PROTECTSPIKE))
+		&& P_DamageMobj(toucher, special, special, 1, DMG_SPIKE))
+			return; // Can only hit snapper from above
+
+		if (special->type == MT_SPINCUSHION
+		&& (P_MobjFlip(toucher)*(toucher->z - (special->z + special->height/2)) > 0))
 		{
 			if (player->pflags & PF_BOUNCING)
 			{
 				toucher->momz = -toucher->momz;
 				P_DoAbilityBounce(player, false);
+				return;
 			}
-			else // Cannot hit sharp from above or when red and angry
-				P_DamageMobj(toucher, special, special, 1, DMG_SPIKE);
+			else if (P_DamageMobj(toucher, special, special, 1, DMG_SPIKE))
+				return; // Cannot hit sharp from above
 		}
-		else if (((player->powers[pw_carry] == CR_NIGHTSMODE) && (player->pflags & PF_DRILLING))
+
+		if (((player->powers[pw_carry] == CR_NIGHTSMODE) && (player->pflags & PF_DRILLING))
 		|| ((player->pflags & PF_JUMPED) && (!(player->pflags & PF_NOJUMPDAMAGE) || (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY)))
 		|| (player->pflags & (PF_SPINNING|PF_GLIDING))
 		|| (player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2)
 		|| ((player->charflags & SF_STOMPDAMAGE || player->pflags & PF_BOUNCING) && (P_MobjFlip(toucher)*(toucher->z - (special->z + special->height/2)) > 0) && (P_MobjFlip(toucher)*toucher->momz < 0))
-		|| player->powers[pw_invulnerability] || player->powers[pw_super]) // Do you possess the ability to subdue the object?
+		|| player->powers[pw_invulnerability] || player->powers[pw_super]
+		|| elementalpierce) // Do you possess the ability to subdue the object?
 		{
 			if ((P_MobjFlip(toucher)*toucher->momz < 0) && (elementalpierce != 1))
 			{
@@ -448,17 +432,20 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 			}
 			if (player->pflags & PF_BOUNCING)
 				P_DoAbilityBounce(player, false);
-
+			if (special->info->spawnhealth > 1) // Multi-hit? Bounce back!
+			{
+				toucher->momx = -toucher->momx;
+				toucher->momy = -toucher->momy;
+			}
 			P_DamageMobj(special, toucher, toucher, 1, 0);
 		}
 		else if (((toucher->z < special->z && !(toucher->eflags & MFE_VERTICALFLIP))
-		|| (toucher->z + toucher->height > special->z + special->height && (toucher->eflags & MFE_VERTICALFLIP))) // Flame is bad at logic - JTE
+		|| (toucher->z + toucher->height > special->z + special->height && (toucher->eflags & MFE_VERTICALFLIP)))
 		&& player->charability == CA_FLY
 		&& (player->powers[pw_tailsfly]
-		|| toucher->state-states == S_PLAY_FLY_TIRED)) // Tails can shred stuff with his propeller.
+		|| toucher->state-states == S_PLAY_FLY_TIRED)) // Tails can shred stuff with her propeller.
 		{
-			if (P_MobjFlip(toucher)*toucher->momz < 0)
-				toucher->momz = -toucher->momz/2;
+			toucher->momz = -toucher->momz/2;
 
 			P_DamageMobj(special, toucher, toucher, 1, 0);
 		}
@@ -490,42 +477,46 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 			/* FALLTHRU */
 		case MT_RING:
 		case MT_FLINGRING:
-			if (!(P_CanPickupItem(player, false)))
+		case MT_COIN:
+		case MT_FLINGCOIN:
+		case MT_NIGHTSSTAR:
+			if (!(P_CanPickupItem(player, false)) && !(special->flags2 & MF2_NIGHTSPULL))
 				return;
 
 			special->momx = special->momy = special->momz = 0;
 			P_GivePlayerRings(player, 1);
 
-			if ((maptol & TOL_NIGHTS) && special->type != MT_FLINGRING)
+			if ((maptol & TOL_NIGHTS) && special->type != MT_FLINGRING && special->type != MT_FLINGCOIN)
 				P_DoNightsScore(player);
 			break;
-
-		case MT_COIN:
-		case MT_FLINGCOIN:
-			if (!(P_CanPickupItem(player, false)))
+		case MT_BLUESPHERE:
+		case MT_FLINGBLUESPHERE:
+		case MT_NIGHTSCHIP:
+		case MT_FLINGNIGHTSCHIP:
+			if (!(P_CanPickupItem(player, false)) && !(special->flags2 & MF2_NIGHTSPULL))
 				return;
 
-			special->momx = special->momy = 0;
-			P_GivePlayerRings(player, 1);
+			special->momx = special->momy = special->momz = 0;
+			P_GivePlayerSpheres(player, 1);
+
+			if (special->type == MT_BLUESPHERE)
+			{
+				special->destscale = ((player->powers[pw_carry] == CR_NIGHTSMODE) ? 4 : 2)*special->scale;
+				if (states[special->info->deathstate].tics > 0)
+					special->scalespeed = FixedDiv(FixedDiv(special->destscale, special->scale), states[special->info->deathstate].tics<<FRACBITS);
+				else
+					special->scalespeed = 4*FRACUNIT/5;
+			}
 
-			if ((maptol & TOL_NIGHTS) && special->type != MT_FLINGCOIN)
+			if (maptol & TOL_NIGHTS)
 				P_DoNightsScore(player);
 			break;
-		case MT_BLUEBALL:
-			if (!(P_CanPickupItem(player, false)))
+		case MT_BOMBSPHERE:
+			if (!(P_CanPickupItem(player, false)) && !(special->flags2 & MF2_NIGHTSPULL))
 				return;
 
-			P_GivePlayerRings(player, 1);
-
 			special->momx = special->momy = special->momz = 0;
-			special->destscale = FixedMul(8*FRACUNIT, special->scale);
-			if (states[special->info->deathstate].tics > 0)
-				special->scalespeed = FixedDiv(FixedDiv(special->destscale, special->scale), states[special->info->deathstate].tics<<FRACBITS);
-			else
-				special->scalespeed = 4*FRACUNIT/5;
-
-			if (maptol & TOL_NIGHTS)
-				P_DoNightsScore(player);
+			P_DamageMobj(toucher, special, special, 1, 0);
 			break;
 		case MT_AUTOPICKUP:
 		case MT_BOUNCEPICKUP:
@@ -581,7 +572,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 			P_AddPlayerScore(player, 1000);
 
 			if (gametype != GT_COOP || modeattacking) // score only?
+			{
+				S_StartSound(toucher, sfx_chchng);
 				break;
+			}
 
 			tokenlist += special->health;
 
@@ -593,10 +587,17 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 					players->gotcontinue = true;
 					if (P_IsLocalPlayer(player))
 						S_StartSound(NULL, sfx_s3kac);
+					else
+						S_StartSound(toucher, sfx_chchng);
 				}
+				else
+					S_StartSound(toucher, sfx_chchng);
 			}
 			else
+			{
 				token++;
+				S_StartSound(toucher, sfx_token);
+			}
 
 			break;
 
@@ -621,7 +622,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 
 					players[i].exiting = (14*TICRATE)/5 + 1;
 				}
-				S_StartSound(NULL, sfx_lvpass);
+				//S_StartSound(NULL, sfx_lvpass);
 			}
 			break;
 
@@ -752,45 +753,86 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 // NiGHTS gameplay items and powerups //
 // ********************************** //
 		case MT_NIGHTSDRONE:
-			if (player->bot)
-				return;
-			if (player->exiting)
-				return;
-			if (player->bonustime)
 			{
-				if (G_IsSpecialStage(gamemap)) //After-mare bonus time/emerald reward in special stages.
+				boolean spec = G_IsSpecialStage(gamemap);
+				boolean cangiveemmy = false;
+				if (player->bot)
+					return;
+				if (player->exiting)
+					return;
+				if (player->bonustime)
 				{
-					// only allow the player with the emerald in-hand to leave.
-					if (toucher->tracer
-					&& toucher->tracer->type == MT_GOTEMERALD)
+					if (spec) //After-mare bonus time/emerald reward in special stages.
 					{
+						// only allow the player with the emerald in-hand to leave.
+						if (toucher->tracer
+						&& toucher->tracer->type == MT_GOTEMERALD)
+						{}
+						else // Make sure that SOMEONE has the emerald, at least!
+						{
+							for (i = 0; i < MAXPLAYERS; i++)
+								if (playeringame[i] && players[i].playerstate == PST_LIVE
+								&& players[i].mo->tracer
+								&& players[i].mo->tracer->type == MT_GOTEMERALD)
+									return;
+							// Well no one has an emerald, so exit anyway!
+						}
+						cangiveemmy = true;
+						// Don't play Ideya sound in special stage mode
 					}
-					else // Make sure that SOMEONE has the emerald, at least!
+					else
+						S_StartSound(toucher, special->info->activesound);
+				}
+				else //Initial transformation. Don't allow second chances in special stages!
+				{
+					if (player->powers[pw_carry] == CR_NIGHTSMODE)
+						return;
+
+					S_StartSound(toucher, sfx_supert);
+				}
+				P_SwitchSpheresBonusMode(false);
+				if (!(netgame || multiplayer) && !(player->powers[pw_carry] == CR_NIGHTSMODE))
+					P_SetTarget(&special->tracer, toucher);
+				P_NightserizePlayer(player, special->health); // Transform!
+				if (!spec)
+				{
+					if (toucher->tracer) // Move the ideya over to the drone!
 					{
-						for (i = 0; i < MAXPLAYERS; i++)
-							if (playeringame[i] && players[i].playerstate == PST_LIVE
-							&& players[i].mo->tracer
-							&& players[i].mo->tracer->type == MT_GOTEMERALD)
-								return;
-						// Well no one has an emerald, so exit anyway!
+						mobj_t *hnext = special->hnext;
+						P_SetTarget(&special->hnext, toucher->tracer);
+						P_SetTarget(&special->hnext->hnext, hnext); // Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo.
+						P_SetTarget(&special->hnext->target, special);
+						P_SetTarget(&toucher->tracer, NULL);
+						if (hnext)
+						{
+							special->hnext->extravalue1 = (angle_t)(hnext->extravalue1 - 72*ANG1);
+							if (special->hnext->extravalue1 > hnext->extravalue1)
+								special->hnext->extravalue1 -= (72*ANG1)/special->hnext->extravalue1;
+						}
 					}
-					P_GiveEmerald(false);
-					// Don't play Ideya sound in special stage mode
+					if (player->exiting) // ...then move it back?
+					{
+						mobj_t *hnext = special;
+						while ((hnext = hnext->hnext))
+							P_SetTarget(&hnext->target, toucher);
+					}
+					return;
 				}
-				else
-					S_StartSound(toucher, special->info->activesound);
-			}
-			else //Initial transformation. Don't allow second chances in special stages!
-			{
-				if (player->powers[pw_carry] == CR_NIGHTSMODE)
+
+				if (!cangiveemmy)
 					return;
 
-				S_StartSound(toucher, sfx_supert);
+				if (player->exiting)
+					P_GiveEmerald(false);
+				else if (player->mo->tracer && player->mare)
+				{
+					P_KillMobj(toucher->tracer, NULL, NULL, 0); // No emerald for you just yet!
+					S_StartSound(NULL, sfx_ghosty);
+					special->flags2 |= MF2_DONTDRAW;
+				}
+
+				return;
 			}
-			if (!(netgame || multiplayer) && !(player->powers[pw_carry] == CR_NIGHTSMODE))
-				P_SetTarget(&special->tracer, toucher);
-			P_NightserizePlayer(player, special->health); // Transform!
-			return;
 		case MT_NIGHTSLOOPHELPER:
 			// One second delay
 			if (special->fuse < toucher->fuse - TICRATE)
@@ -895,14 +937,18 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 						}
 					}
 
-					if (!(mo2->type == MT_NIGHTSWING || mo2->type == MT_RING || mo2->type == MT_COIN
-					   || mo2->type == MT_BLUEBALL
-					   || ((mo2->type == MT_EMBLEM) && (mo2->reactiontime & GE_NIGHTSPULL))))
+					if (!(mo2->type == MT_RING || mo2->type == MT_COIN
+						|| mo2->type == MT_BLUESPHERE || mo2->type == MT_BOMBSPHERE
+						|| mo2->type == MT_NIGHTSCHIP || mo2->type == MT_NIGHTSSTAR
+						|| ((mo2->type == MT_EMBLEM) && (mo2->reactiontime & GE_NIGHTSPULL))))
 						continue;
 
 					// Yay! The thing's in reach! Pull it in!
 					mo2->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT;
 					mo2->flags2 |= MF2_NIGHTSPULL;
+					// New NiGHTS attract speed dummied out because the older behavior
+					// is exploited as a mechanic. Uncomment to enable.
+					mo2->movefactor = 0; // 32*FRACUNIT; // initialize the NightsItemChase timer
 					P_SetTarget(&mo2->tracer, toucher);
 				}
 			}
@@ -915,6 +961,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 			if (player->powers[pw_carry] == CR_NIGHTSMODE && !toucher->target)
 				return;
 
+			if (toucher->tracer)
+				return; // Don't have multiple ideya
+
 			if (player->mare != special->threshold) // wrong mare
 				return;
 
@@ -922,16 +971,16 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 				return;
 
 			if (G_IsSpecialStage(gamemap) && !player->exiting)
-			{ // In special stages, share rings. Everyone gives up theirs to the player who touched the capsule
+			{ // In special stages, share spheres. Everyone gives up theirs to the player who touched the capsule
 				for (i = 0; i < MAXPLAYERS; i++)
-					if (playeringame[i] && (&players[i] != player) && players[i].rings > 0)
+					if (playeringame[i] && (&players[i] != player) && players[i].spheres > 0)
 					{
-						player->rings += players[i].rings;
-						players[i].rings = 0;
+						player->spheres += players[i].spheres;
+						players[i].spheres = 0;
 					}
 			}
 
-			if (player->rings <= 0 || player->exiting)
+			if (player->spheres <= 0 || player->exiting)
 				return;
 
 			// Mark the player as 'pull into the capsule'
@@ -959,13 +1008,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 						player->flyangle = special->threshold;
 
 					player->speed = FixedMul(special->info->speed, special->scale);
-					// Potentially causes axis transfer failures.
-					// Also rarely worked properly anyway.
-					//P_UnsetThingPosition(player->mo);
-					//player->mo->x = special->x;
-					//player->mo->y = special->y;
-					//P_SetThingPosition(player->mo);
-					toucher->z = special->z+(special->height/4);
+					P_SetTarget(&player->mo->hnext, special); // Reference bumper for position correction on next tic
 				}
 				else // More like a spring
 				{
@@ -1088,6 +1131,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 			{
 				player->nightstime += special->info->speed;
 				player->startedtime += special->info->speed;
+				player->lapstartedtime += special->info->speed;
 				P_RestoreMusic(player);
 			}
 			else
@@ -1097,6 +1141,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 					{
 						players[i].nightstime += special->info->speed;
 						players[i].startedtime += special->info->speed;
+						players[i].lapstartedtime += special->info->speed;
 						P_RestoreMusic(&players[i]);
 					}
 				if (special->info->deathsound != sfx_None)
@@ -1117,7 +1162,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 			if (!G_IsSpecialStage(gamemap))
 			{
 				player->powers[pw_nights_linkfreeze] = (UINT16)special->info->speed;
-				player->linktimer = 2*TICRATE;
+				player->linktimer = nightslinktics;
 			}
 			else
 			{
@@ -1125,7 +1170,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 					if (playeringame[i] && players[i].powers[pw_carry] == CR_NIGHTSMODE)
 					{
 						players[i].powers[pw_nights_linkfreeze] += (UINT16)special->info->speed;
-						players[i].linktimer = 2*TICRATE;
+						players[i].linktimer = nightslinktics;
 					}
 				if (special->info->deathsound != sfx_None)
 					S_StartSound(NULL, special->info->deathsound);
@@ -1139,17 +1184,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 				HU_DoCEcho(M_GetText("\\\\\\\\\\\\\\\\Link Freeze"));
 			}
 			break;
-		case MT_NIGHTSWING:
-			if (G_IsSpecialStage(gamemap) && useNightsSS)
-			{ // Pseudo-ring.
-				S_StartSound(toucher, special->info->painsound);
-				player->totalring++;
-			}
-			else
-				S_StartSound(toucher, special->info->activesound);
-
-			P_DoNightsScore(player);
-			break;
 		case MT_HOOPCOLLIDE:
 			// This produces a kind of 'domino effect' with the hoop's pieces.
 			for (; special->hprev != NULL; special = special->hprev); // Move to the first sprite in the hoop
@@ -1336,7 +1370,8 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 
 			P_ClearStarPost(special->health);
 
-			// Find all starposts in the level with this value.
+			// Find all starposts in the level with this value - INCLUDING this one!
+			if (!(netgame && circuitmap && player != &players[consoleplayer]))
 			{
 				thinker_t *th;
 				mobj_t *mo2;
@@ -1348,21 +1383,16 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 
 					mo2 = (mobj_t *)th;
 
-					if (mo2 == special)
+					if (mo2->type != MT_STARPOST)
+						continue;
+					if (mo2->health != special->health)
 						continue;
 
-					if (mo2->type == MT_STARPOST && mo2->health == special->health)
-					{
-						if (!(netgame && circuitmap && player != &players[consoleplayer]))
-							P_SetMobjState(mo2, mo2->info->painstate);
-					}
+					P_SetMobjState(mo2, mo2->info->painstate);
 				}
 			}
 
 			S_StartSound(toucher, special->info->painsound);
-
-			if (!(netgame && circuitmap && player != &players[consoleplayer]))
-				P_SetMobjState(special, special->info->painstate);
 			return;
 
 		case MT_FAKEMOBILE:
@@ -1425,23 +1455,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 			return;
 		case MT_EGGSHIELD:
 			{
-				fixed_t touchx, touchy, touchspeed;
-				angle_t angle;
-
-				if (P_AproxDistance(toucher->x-special->x, toucher->y-special->y) >
-					P_AproxDistance((toucher->x-toucher->momx)-special->x, (toucher->y-toucher->momy)-special->y))
-				{
-					touchx = toucher->x + toucher->momx;
-					touchy = toucher->y + toucher->momy;
-				}
-				else
-				{
-					touchx = toucher->x;
-					touchy = toucher->y;
-				}
-
-				angle = R_PointToAngle2(special->x, special->y, touchx, touchy) - special->angle;
-				touchspeed = P_AproxDistance(toucher->momx, toucher->momy);
+				angle_t angle = R_PointToAngle2(special->x, special->y, toucher->x, toucher->y) - special->angle;
+				fixed_t touchspeed = P_AproxDistance(toucher->momx, toucher->momy);
+				if (touchspeed < special->scale)
+					touchspeed = special->scale;
 
 				// Blocked by the shield?
 				if (!(angle > ANGLE_90 && angle < ANGLE_270))
@@ -1458,13 +1475,12 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 
 					// Play a bounce sound?
 					S_StartSound(toucher, special->info->painsound);
-					return;
+
+					// experimental bounce
+					if (special->target)
+						special->target->extravalue1 = -special->target->info->speed;
 				}
-				else if (((player->powers[pw_carry] == CR_NIGHTSMODE) && (player->pflags & PF_DRILLING))
-						|| ((player->pflags & PF_JUMPED) && (!(player->pflags & PF_NOJUMPDAMAGE) || (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY)))
-						|| ((player->charflags & SF_STOMPDAMAGE || player->pflags & PF_BOUNCING) && (P_MobjFlip(toucher)*(toucher->z - (special->z + special->height/2)) > 0) && (P_MobjFlip(toucher)*toucher->momz < 0))
-						|| (player->pflags & (PF_SPINNING|PF_GLIDING))
-						|| player->powers[pw_invulnerability] || player->powers[pw_super]) // Do you possess the ability to subdue the object?
+				else
 				{
 					// Shatter the shield!
 					toucher->momx = -toucher->momx/2;
@@ -1489,10 +1505,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 				P_SetMobjState(special, special->info->seestate);
 			}
 			return;
-		case MT_SMALLMACECHAIN:
-		case MT_BIGMACECHAIN:
-			// Is this the last link in the chain?
-			if (toucher->momz > 0 || !(special->flags2 & MF2_AMBUSH)
+		case MT_SMALLGRABCHAIN:
+		case MT_BIGGRABCHAIN:
+			if (P_MobjFlip(toucher)*toucher->momz > 0
 				|| (player->powers[pw_carry]))
 				return;
 
@@ -1530,20 +1545,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 			player->pflags |= PF_JUMPSTASIS;
 
 			return;
-		case MT_BIGMINE:
-		case MT_BIGAIRMINE:
-			// Spawn explosion!
-			P_SpawnMobj(special->x, special->y, special->z, special->info->mass);
-			P_RadiusAttack(special, special, special->info->damage);
-			S_StartSound(special, special->info->deathsound);
-			P_SetMobjState(special, special->info->deathstate);
-			return;
-		case MT_SPECIALSPIKEBALL:
-			if (!useNightsSS && G_IsSpecialStage(gamemap)) // Only for old special stages
-				P_SpecialStageDamage(player, special, NULL);
-			else
-				P_DamageMobj(toucher, special, special, 1, 0);
-			return;
 		case MT_EGGMOBILE2_POGO:
 			// sanity checks
 			if (!special->target || !special->target->health)
@@ -2112,8 +2113,8 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 	if (inflictor && (inflictor->type == MT_SHELL || inflictor->type == MT_FIREBALL))
 		P_SetTarget(&target->tracer, inflictor);
 
-	if (!useNightsSS && G_IsSpecialStage(gamemap) && target->player && sstimer > 6)
-		sstimer = 6; // Just let P_Ticker take care of the rest.
+	if (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap) && target->player && target->player->nightstime > 6)
+		target->player->nightstime = 6; // Just let P_Ticker take care of the rest.
 
 	if (target->flags & (MF_ENEMY|MF_BOSS))
 		target->momx = target->momy = target->momz = 0;
@@ -2122,7 +2123,10 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 		target->flags |= MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT; // Don't drop Tails 03-08-2000
 
 	if (target->flags2 & MF2_NIGHTSPULL)
+	{
 		P_SetTarget(&target->tracer, NULL);
+		target->movefactor = 0; // reset NightsItemChase timer
+	}
 
 	// dead target is no more shootable
 	target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SPECIAL);
@@ -2181,28 +2185,34 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 					// to make people want to actually dash towards/paraloop enemies
 					if (++source->player->linkcount > source->player->maxlink)
 						source->player->maxlink = source->player->linkcount;
-					source->player->linktimer = 2*TICRATE;
+					source->player->linktimer = nightslinktics;
 				}
 			}
 			else
 			{
 				if (target->flags & MF_BOSS)
 					score = 1000;
-				else if ((target->flags & MF_ENEMY) && !(target->flags & MF_MISSILE))
+				else if ((target->flags & MF_ENEMY) && !(target->flags & MF_MISSILE) && target->info->spawnhealth)
 				{
+					UINT8 locscoreadd = source->player->scoreadd + target->info->spawnhealth;
 					mobj_t *scoremobj;
 					UINT32 scorestate = mobjinfo[MT_SCORE].spawnstate;
 
 					scoremobj = P_SpawnMobj(target->x, target->y, target->z + (target->height / 2), MT_SCORE);
 
-					// On ground? No chain starts.
-					if (!source->player->powers[pw_invulnerability] && P_IsObjectOnGround(source))
+					// More Sonic-like point system
+					if (!mariomode) switch (locscoreadd)
 					{
-						source->player->scoreadd = 0;
-						score = 100;
+						case 1:  score = 100;   break;
+						case 2:  score = 200;   scorestate += 1; break;
+						case 3:  score = 500;   scorestate += 2; break;
+						case 4: case 5: case 6: case 7: case 8: case 9:
+						case 10: case 11: case 12: case 13: case 14:
+						         score = 1000;  scorestate += 3; break;
+						default: score = 10000; scorestate += 4; break;
 					}
 					// Mario Mode has Mario-like chain point values
-					else if (mariomode) switch (++source->player->scoreadd)
+					else switch (locscoreadd)
 					{
 						case 1: score = 100;  break;
 						case 2: score = 200;  scorestate += 1; break;
@@ -2222,19 +2232,12 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 							scorestate += 10;
 							break;
 					}
-					// More Sonic-like point system
-					else switch (++source->player->scoreadd)
-					{
-						case 1:  score = 100;   break;
-						case 2:  score = 200;   scorestate += 1; break;
-						case 3:  score = 500;   scorestate += 2; break;
-						case 4: case 5: case 6: case 7: case 8: case 9:
-						case 10: case 11: case 12: case 13: case 14:
-						         score = 1000;  scorestate += 3; break;
-						default: score = 10000; scorestate += 4; break;
-					}
 
 					P_SetMobjState(scoremobj, scorestate);
+
+					// On ground? No chain starts.
+					if (source->player->powers[pw_invulnerability] || !P_IsObjectOnGround(source))
+						source->player->scoreadd = locscoreadd;
 				}
 			}
 
@@ -2252,7 +2255,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
@@ -2349,78 +2352,6 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 	if (source && target && target->player && source->player)
 		P_PlayVictorySound(source); // Killer laughs at you. LAUGHS! BWAHAHAHA!
 
-#ifdef OLDANIMALSPAWNING
-	// Drop stuff.
-	// This determines the kind of object spawned
-	// during the death frame of a thing.
-	if (!mariomode // Don't show birds, etc. in Mario Mode Tails 12-23-2001
-	&& target->flags & MF_ENEMY)
-	{
-		mobjtype_t item;
-		INT32 prandom;
-
-		switch (target->type)
-		{
-			case MT_REDCRAWLA:
-			case MT_GOLDBUZZ:
-			case MT_SKIM:
-			case MT_UNIDUS:
-				item = MT_FLICKY_02/*MT_BUNNY*/;
-				break;
-
-			case MT_BLUECRAWLA:
-			case MT_JETTBOMBER:
-			case MT_GFZFISH:
-				item = MT_FLICKY_01/*MT_BIRD*/;
-				break;
-
-			case MT_JETTGUNNER:
-			case MT_CRAWLACOMMANDER:
-			case MT_REDBUZZ:
-			case MT_DETON:
-				item = MT_FLICKY_12/*MT_MOUSE*/;
-				break;
-
-			case MT_GSNAPPER:
-			case MT_EGGGUARD:
-			case MT_SPRINGSHELL:
-				item = MT_FLICKY_11/*MT_COW*/;
-				break;
-
-			case MT_MINUS:
-			case MT_VULTURE:
-			case MT_POINTY:
-			case MT_YELLOWSHELL:
-				item = MT_FLICKY_03/*MT_CHICKEN*/;
-				break;
-
-			case MT_AQUABUZZ:
-				item = MT_FLICKY_01/*MT_REDBIRD*/;
-				break;
-
-			default:
-				if (target->info->doomednum)
-					prandom = target->info->doomednum%5; // "Random" animal for new enemies.
-				else
-					prandom = P_RandomKey(5); // No placable object, just use a random number.
-
-				switch(prandom)
-				{
-					default: item = MT_FLICKY_02/*MT_BUNNY*/; break;
-					case 1: item = MT_FLICKY_01/*MT_BIRD*/; break;
-					case 2: item = MT_FLICKY_12/*MT_MOUSE*/; break;
-					case 3: item = MT_FLICKY_11/*MT_COW*/; break;
-					case 4: item = MT_FLICKY_03/*MT_CHICKEN*/; break;
-				}
-				break;
-		}
-
-		mo = P_SpawnMobj(target->x, target->y, target->z + (target->height / 2) - FixedMul(mobjinfo[item].height / 2, target->scale), item);
-		mo->destscale = target->scale;
-		P_SetScale(mo, mo->destscale);
-	}
-	else
-#endif
 	// Other death animation effects
 	switch(target->type)
 	{
@@ -2434,6 +2365,96 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 			target->fuse = target->info->damage;
 			break;
 
+		case MT_BUBBLEBUZZ:
+			if (inflictor && inflictor->player // did a player kill you? Spawn relative to the player so they're bound to get it
+			&& P_AproxDistance(inflictor->x - target->x, inflictor->y - target->y) <= inflictor->radius + target->radius + FixedMul(8*FRACUNIT, inflictor->scale) // close enough?
+			&& inflictor->z <= target->z + target->height + FixedMul(8*FRACUNIT, inflictor->scale)
+			&& inflictor->z + inflictor->height >= target->z - FixedMul(8*FRACUNIT, inflictor->scale))
+				mo = P_SpawnMobj(inflictor->x + inflictor->momx, inflictor->y + inflictor->momy, inflictor->z + (inflictor->height / 2) + inflictor->momz, MT_EXTRALARGEBUBBLE);
+			else
+				mo = P_SpawnMobj(target->x, target->y, target->z, MT_EXTRALARGEBUBBLE);
+			mo->destscale = target->scale;
+			P_SetScale(mo, mo->destscale);
+			break;
+
+		case MT_YELLOWSHELL:
+			P_SpawnMobjFromMobj(target, 0, 0, 0, MT_YELLOWSPRING);
+			break;
+
+		case MT_CRAWLACOMMANDER:
+			target->momx = target->momy = target->momz = 0;
+			break;
+
+		case MT_CRUSHSTACEAN:
+			if (target->tracer)
+			{
+				mobj_t *chain = target->tracer->target, *chainnext;
+				while (chain)
+				{
+					chainnext = chain->target;
+					P_RemoveMobj(chain);
+					chain = chainnext;
+				}
+				S_StopSound(target->tracer);
+				P_KillMobj(target->tracer, inflictor, source, damagetype);
+			}
+			break;
+
+		case MT_EGGSHIELD:
+			P_SetObjectMomZ(target, 4*target->scale, false);
+			P_InstaThrust(target, target->angle, 3*target->scale);
+			target->flags = (target->flags|MF_NOCLIPHEIGHT) & ~MF_NOGRAVITY;
+			break;
+
+		case MT_EGGMOBILE3:
+			{
+				thinker_t *th;
+				UINT32 i = 0; // to check how many clones we've removed
+
+				// scan the thinkers to make sure all the old pinch dummies are gone on death
+				// this can happen if the boss was hurt earlier than expected
+				for (th = thinkercap.next; th != &thinkercap; th = th->next)
+				{
+					if (th->function.acp1 != (actionf_p1)P_MobjThinker)
+						continue;
+
+					mo = (mobj_t *)th;
+					if (mo->type == (mobjtype_t)target->info->mass && mo->tracer == target)
+					{
+						P_RemoveMobj(mo);
+						i++;
+					}
+					if (i == 2) // we've already removed 2 of these, let's stop now
+						break;
+				}
+			}
+			break;
+
+		case MT_BIGMINE:
+			if (inflictor)
+			{
+				fixed_t dx = target->x - inflictor->x, dy = target->y - inflictor->y, dz = target->z - inflictor->z;
+				fixed_t dm = FixedHypot(dz, FixedHypot(dy, dx));
+				target->momx = FixedDiv(FixedDiv(dx, dm), dm)*512;
+				target->momy = FixedDiv(FixedDiv(dy, dm), dm)*512;
+				target->momz = FixedDiv(FixedDiv(dz, dm), dm)*512;
+			}
+			if (source)
+				P_SetTarget(&target->tracer, source);
+			break;
+
+		case MT_BLASTEXECUTOR:
+			if (target->spawnpoint)
+				P_LinedefExecute(target->spawnpoint->angle, (source ? source : inflictor), target->subsector->sector);
+			break;
+
+		case MT_SPINBOBERT:
+			if (target->hnext)
+				P_KillMobj(target->hnext, inflictor, source, damagetype);
+			if (target->hprev)
+				P_KillMobj(target->hprev, inflictor, source, damagetype);
+			break;
+
 		case MT_EGGTRAP:
 			// Time for birdies! Yaaaaaaaay!
 			target->fuse = TICRATE*2;
@@ -2467,57 +2488,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 			break;
 	}
 
-	// Enemy drops that ALWAYS occur regardless of mode
-	if (target->type == MT_AQUABUZZ) // Additionally spawns breathable bubble for players to get
-	{
-		if (inflictor && inflictor->player // did a player kill you? Spawn relative to the player so he's bound to get it
-		&& P_AproxDistance(inflictor->x - target->x, inflictor->y - target->y) <= inflictor->radius + target->radius + FixedMul(8*FRACUNIT, inflictor->scale) // close enough?
-		&& inflictor->z <= target->z + target->height + FixedMul(8*FRACUNIT, inflictor->scale)
-		&& inflictor->z + inflictor->height >= target->z - FixedMul(8*FRACUNIT, inflictor->scale))
-			mo = P_SpawnMobj(inflictor->x + inflictor->momx, inflictor->y + inflictor->momy, inflictor->z + (inflictor->height / 2) + inflictor->momz, MT_EXTRALARGEBUBBLE);
-		else
-			mo = P_SpawnMobj(target->x, target->y, target->z, MT_EXTRALARGEBUBBLE);
-		mo->destscale = target->scale;
-		P_SetScale(mo, mo->destscale);
-	}
-	else if (target->type == MT_YELLOWSHELL) // Spawns a spring that falls to the ground
-	{
-		mobjtype_t spawnspring = MT_YELLOWSPRING;
-		fixed_t spawnheight = target->z;
-		if (!(target->eflags & MFE_VERTICALFLIP))
-			spawnheight += target->height;
-
-		mo = P_SpawnMobj(target->x, target->y, spawnheight, spawnspring);
-		mo->destscale = target->scale;
-		P_SetScale(mo, mo->destscale);
-
-		if (target->flags2 & MF2_OBJECTFLIP)
-			mo->flags2 |= MF2_OBJECTFLIP;
-	}
-
-	if (target->type == MT_EGGMOBILE3)
-	{
-		thinker_t *th;
-		UINT32 i = 0; // to check how many clones we've removed
-
-		// scan the thinkers to make sure all the old pinch dummies are gone on death
-		// this can happen if the boss was hurt earlier than expected
-		for (th = thinkercap.next; th != &thinkercap; th = th->next)
-		{
-			if (th->function.acp1 != (actionf_p1)P_MobjThinker)
-				continue;
-
-			mo = (mobj_t *)th;
-			if (mo->type == (mobjtype_t)target->info->mass && mo->tracer == target)
-			{
-				P_RemoveMobj(mo);
-				i++;
-			}
-			if (i == 2) // we've already removed 2 of these, let's stop now
-				break;
-		}
-	}
-
+	// Final state setting - do something instead of P_SetMobjState;
 	if (target->type == MT_SPIKE && target->info->deathstate != S_NULL)
 	{
 		const angle_t ang = ((inflictor) ? inflictor->angle : 0) + ANGLE_90;
@@ -2696,6 +2667,8 @@ static inline void P_NiGHTSDamage(mobj_t *target, mobj_t *source)
 	player_t *player = target->player;
 	tic_t oldnightstime = player->nightstime;
 
+	(void)source; // unused
+
 	if (!player->powers[pw_flashing])
 	{
 		angle_t fa;
@@ -2709,20 +2682,10 @@ static inline void P_NiGHTSDamage(mobj_t *target, mobj_t *source)
 			player->drillmeter -= 5*20;
 		else
 		{
-			if (source && source->player)
-			{
-				if (player->nightstime > 20*TICRATE)
-					player->nightstime -= 20*TICRATE;
-				else
-					player->nightstime = 1;
-			}
+			if (player->nightstime > 5*TICRATE)
+				player->nightstime -= 5*TICRATE;
 			else
-			{
-				if (player->nightstime > 5*TICRATE)
-					player->nightstime -= 5*TICRATE;
-				else
-					player->nightstime = 1;
-			}
+				player->nightstime = 1;
 		}
 
 		if (player->pflags & PF_TRANSFERTOCLOSEST)
@@ -2746,12 +2709,12 @@ static inline void P_NiGHTSDamage(mobj_t *target, mobj_t *source)
 			&& player->nightstime < 10*TICRATE)
 		{
 			//S_StartSound(NULL, sfx_timeup); // that creepy "out of time" music from NiGHTS. Dummied out, as some on the dev team thought it wasn't Sonic-y enough (Mystic, notably). Uncomment to restore. -SH
-			S_ChangeMusicInternal("_drown",false);
+			S_ChangeMusicInternal((((maptol & TOL_NIGHTS) && !G_IsSpecialStage(gamemap)) ? "_ntime" : "_drown"), false);
 		}
 	}
 }
 
-static inline boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage)
+static inline boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
 {
 	player_t *player = target->player;
 	(void)damage; //unused parm
@@ -2761,7 +2724,7 @@ static inline boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *sou
 		return false;
 
 	// Ignore IT players shooting each other, unless friendlyfire is on.
-	if ((player->pflags & PF_TAGIT && !(cv_friendlyfire.value &&
+	if ((player->pflags & PF_TAGIT && !((cv_friendlyfire.value || (damagetype & DMG_CANHURTSELF)) &&
 		source && source->player && source->player->pflags & PF_TAGIT)))
 		return false;
 
@@ -2771,7 +2734,7 @@ static inline boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *sou
 
 	// Don't allow players on the same team to hurt one another,
 	// unless cv_friendlyfire is on.
-	if (!cv_friendlyfire.value && (player->pflags & PF_TAGIT) == (source->player->pflags & PF_TAGIT))
+	if (!(cv_friendlyfire.value || (damagetype & DMG_CANHURTSELF)) && (player->pflags & PF_TAGIT) == (source->player->pflags & PF_TAGIT))
 	{
 		if (!(inflictor->flags & MF_FIRE))
 			P_GivePlayerRings(player, 1);
@@ -2811,36 +2774,49 @@ static inline boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *sou
 		return true;
 	}
 
-	if (player->rings > 0) // Ring loss
+	if (player->powers[pw_carry] == CR_NIGHTSFALL)
+	{
+		if (player->spheres > 0)
+		{
+			P_PlayRinglossSound(target);
+			P_PlayerRingBurst(player, player->spheres);
+			player->spheres = 0;
+		}
+	}
+	else if (player->rings > 0) // Ring loss
 	{
 		P_PlayRinglossSound(target);
 		P_PlayerRingBurst(player, player->rings);
+		player->rings = 0;
 	}
 	else // Death
 	{
 		P_PlayDeathSound(target);
 		P_PlayVictorySound(source); // Killer laughs at you! LAUGHS! BWAHAHAHHAHAA!!
 	}
-
-	player->rings = 0;
 	return true;
 }
 
-static inline boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage)
+static inline boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
 {
 	player_t *player = target->player;
 
-	// You can't kill yourself, idiot...
-	if (source == target)
-		return false;
+	if (!(damagetype & DMG_CANHURTSELF))
+	{
+		// You can't kill yourself, idiot...
+		if (source == target)
+			return false;
 
-	// In COOP/RACE/CHAOS, you can't hurt other players unless cv_friendlyfire is on
-	if (!cv_friendlyfire.value && (G_PlatformGametype()))
-		return false;
+		// In COOP/RACE, you can't hurt other players unless cv_friendlyfire is on
+		if (!cv_friendlyfire.value && (G_PlatformGametype()))
+			return false;
+	}
 
 	// Tag handling
 	if (G_TagGametype())
-		return P_TagDamage(target, inflictor, source, damage);
+		return P_TagDamage(target, inflictor, source, damage, damagetype);
+	else if (damagetype & DMG_CANHURTSELF)
+		return true;
 	else if (G_GametypeHasTeams()) // CTF + Team Match
 	{
 		// Don't allow players on the same team to hurt one another,
@@ -3032,7 +3008,7 @@ static void P_ShieldDamage(player_t *player, mobj_t *inflictor, mobj_t *source,
 	}
 }
 
-static void P_RingDamage(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
+static void P_RingDamage(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype, boolean dospheres)
 {
 	P_DoPlayerPain(player, source, inflictor);
 
@@ -3062,20 +3038,32 @@ static void P_RingDamage(player_t *player, mobj_t *inflictor, mobj_t *source, IN
 	// Ring loss sound plays despite hitting spikes
 	P_PlayRinglossSound(player->mo); // Ringledingle!
 	P_PlayerRingBurst(player, damage);
-	player->rings -= damage;
-	if (player->rings < 0)
-		player->rings = 0;
+
+	if (dospheres)
+	{
+		player->spheres -= damage;
+		if (player->spheres < 0)
+			player->spheres = 0;
+	}
+	else
+	{
+		player->rings -= damage;
+		if (player->rings < 0)
+			player->rings = 0;
+	}
 }
 
 //
 // P_SpecialStageDamage
 //
 // Do old special stage-style damaging
-// Removes 10 rings from the player, or knocks off their shield if they have one.
+// Removes 5 seconds from the player, or knocks off their shield if they have one.
 // If they don't have anything, just knock the player back anyway (this doesn't kill them).
 //
 void P_SpecialStageDamage(player_t *player, mobj_t *inflictor, mobj_t *source)
 {
+	tic_t oldnightstime = player->nightstime;
+
 	if (player->powers[pw_invulnerability] || player->powers[pw_flashing] || player->powers[pw_super])
 		return;
 
@@ -3086,17 +3074,24 @@ void P_SpecialStageDamage(player_t *player, mobj_t *inflictor, mobj_t *source)
 	}
 	else
 	{
-		P_PlayRinglossSound(player->mo);
-		if (player->rings >= 10)
-			player->rings -= 10;
+		S_StartSound(player->mo, sfx_nghurt);
+		if (player->nightstime > 5*TICRATE)
+			player->nightstime -= 5*TICRATE;
 		else
-			player->rings = 0;
+			player->nightstime = 0;
 	}
 
 	P_DoPlayerPain(player, inflictor, source);
 
 	if (gametype == GT_CTF && player->gotflag & (GF_REDFLAG|GF_BLUEFLAG))
 		P_PlayerFlagBurst(player, false);
+
+	if (oldnightstime > 10*TICRATE
+		&& player->nightstime < 10*TICRATE)
+	{
+		//S_StartSound(NULL, sfx_timeup); // that creepy "out of time" music from NiGHTS. Dummied out, as some on the dev team thought it wasn't Sonic-y enough (Mystic, notably). Uncomment to restore. -SH
+		S_ChangeMusicInternal((((maptol & TOL_NIGHTS) && !G_IsSpecialStage(gamemap)) ? "_ntime" : "_drown"), false);
+	}
 }
 
 /** Damages an object, which may or may not be a player.
@@ -3181,36 +3176,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
 			return false;
 	}
 
-	// Special case for Crawla Commander
-	if (target->type == MT_CRAWLACOMMANDER)
-	{
-		if (!force && target->fuse) // Invincible
-			return false;
-
-#ifdef HAVE_BLUA
-		if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype) || P_MobjWasRemoved(target))
-			return true;
-#endif
-
-		if (target->health > 1)
-		{
-			if (target->info->painsound)
-				S_StartSound(target, target->info->painsound);
-
-			target->fuse = TICRATE/2;
-			target->flags2 |= MF2_FRET;
-		}
-		else
-		{
-			target->flags |= MF_NOGRAVITY;
-			target->fuse = 0;
-		}
-
-		target->momx = target->momy = target->momz = 0;
-
-		P_InstaThrust(target, target->angle-ANGLE_180, FixedMul(5*FRACUNIT, target->scale));
-	}
-	else if (target->flags & MF_BOSS)
+	if (target->flags & (MF_ENEMY|MF_BOSS))
 	{
 		if (!force && target->flags2 & MF2_FRET) // Currently flashing from being hit
 			return false;
@@ -3223,13 +3189,6 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
 		if (target->health > 1)
 			target->flags2 |= MF2_FRET;
 	}
-#ifdef HAVE_BLUA
-	else if (target->flags & MF_ENEMY)
-	{
-		if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype) || P_MobjWasRemoved(target))
-			return true;
-	}
-#endif
 
 	player = target->player;
 
@@ -3282,6 +3241,12 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
 			return true;
 		}
 
+		if (G_IsSpecialStage(gamemap))
+		{
+			P_SpecialStageDamage(player, inflictor, source);
+			return true;
+		}
+
 		if (!force && inflictor && inflictor->flags & MF_FIRE)
 		{
 			if (player->powers[pw_shield] & SH_PROTECTFIRE)
@@ -3294,7 +3259,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
 		// Player hits another player
 		if (!force && source && source->player)
 		{
-			if (!P_PlayerHitsPlayer(target, inflictor, source, damage))
+			if (!P_PlayerHitsPlayer(target, inflictor, source, damage, damagetype))
 				return false;
 		}
 
@@ -3302,7 +3267,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
 		if (damagetype & DMG_DEATHMASK)
 		{
 			P_KillPlayer(player, source, damage);
-			player->rings = 0;
+			player->rings = player->spheres = 0;
 		}
 		else if (metalrecording)
 		{
@@ -3346,10 +3311,17 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
 			P_ShieldDamage(player, inflictor, source, damage, damagetype);
 			damage = 0;
 		}
+		else if (player->powers[pw_carry] == CR_NIGHTSFALL)
+		{
+			// always damage so we can recoil upon losing points
+			damage = player->spheres;
+			P_RingDamage(player, inflictor, source, damage, damagetype, true);
+			damage = 0;
+		}
 		else if (player->rings > 0) // No shield but have rings.
 		{
 			damage = player->rings;
-			P_RingDamage(player, inflictor, source, damage, damagetype);
+			P_RingDamage(player, inflictor, source, damage, damagetype, false);
 			damage = 0;
 		}
 		// To reduce griefing potential, don't allow players to be killed
@@ -3395,20 +3367,14 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
 
 	if (player)
 		P_ResetPlayer(target->player);
+	else if ((target->type == MT_EGGMOBILE2) // egg slimer
+	&& (target->health < target->info->damage)) // in pinch phase
+		P_SetMobjState(target, target->info->meleestate); // go to pinch pain state
 	else
-		switch (target->type)
-		{
-		case MT_EGGMOBILE2: // egg slimer
-			if (target->health < target->info->damage) // in pinch phase
-			{
-				P_SetMobjState(target, target->info->meleestate); // go to pinch pain state
-				break;
-			}
-			/* FALLTHRU */
-		default:
-			P_SetMobjState(target, target->info->painstate);
-			break;
-		}
+		P_SetMobjState(target, target->info->painstate);
+
+	if (target->type == MT_HIVEELEMENTAL)
+		target->extravalue1 += 3;
 
 	target->reactiontime = 0; // we're awake now...
 
@@ -3436,13 +3402,14 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings)
 	angle_t fa;
 	fixed_t ns;
 	fixed_t z;
+	boolean nightsreplace = ((maptol & TOL_NIGHTS) && !G_IsSpecialStage(gamemap));
 
 	// Better safe than sorry.
 	if (!player)
 		return;
 
 	// If no health, don't spawn ring!
-	if (player->rings <= 0)
+	if (((maptol & TOL_NIGHTS) && player->spheres <= 0) || (!(maptol & TOL_NIGHTS) && player->rings <= 0))
 		num_rings = 0;
 
 	if (num_rings > 32 && player->powers[pw_carry] != CR_NIGHTSFALL)
@@ -3459,6 +3426,8 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings)
 		INT32 objType = mobjinfo[MT_RING].reactiontime;
 		if (mariomode)
 			objType = mobjinfo[MT_COIN].reactiontime;
+		else if (player->powers[pw_carry] == CR_NIGHTSFALL)
+			objType = mobjinfo[(nightsreplace ? MT_NIGHTSCHIP : MT_BLUESPHERE)].reactiontime;
 
 		z = player->mo->z;
 		if (player->mo->eflags & MFE_VERTICALFLIP)
@@ -3487,6 +3456,9 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings)
 
 			P_SetObjectMomZ(mo, 8*FRACUNIT, false);
 			mo->fuse = 20*TICRATE; // Adjust fuse for NiGHTS
+
+			// Toggle bonus time colors
+			P_SetMobjState(mo, (player->bonustime ? mo->info->raisestate : mo->info->spawnstate));
 		}
 		else
 		{
diff --git a/src/p_lights.c b/src/p_lights.c
index 8aa2eedca49c41f877faf74dbb01f0bae23b15d3..c0b46f74c1e4e3ebc42ff96ba73cb80091dcf981 100644
--- a/src/p_lights.c
+++ b/src/p_lights.c
@@ -23,7 +23,7 @@
   *
   * \param sector The sector to remove effects from.
   */
-static void P_RemoveLighting(sector_t *sector)
+void P_RemoveLighting(sector_t *sector)
 {
 	if (sector->lightingdata)
 	{
@@ -322,39 +322,70 @@ glow_t *P_SpawnAdjustableGlowingLight(sector_t *minsector, sector_t *maxsector,
 	return g;
 }
 
-/** Fades all the lights in sectors with a particular tag to a new
+/** Fades all the lights in specified sector to a new
   * value.
   *
-  * \param tag       Tag to look for sectors by.
+  * \param sector    Target sector
   * \param destvalue The final light value in these sectors.
-  * \param speed     Speed of the fade; the change to the ligh
+  * \param speed     If tic-based: total duration of effect.
+  *                  If speed-based: Speed of the fade; the change to the ligh
   *                  level in each sector per tic.
-  * \todo Calculate speed better so that it is possible to specify
-  *       the time for completion of the fade, and all lights fade
-  *       in this time regardless of initial values.
+  * \param ticbased  Use a specific duration for the fade, defined by speed
   * \sa T_LightFade
   */
-void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed)
+void P_FadeLightBySector(sector_t *sector, INT32 destvalue, INT32 speed, boolean ticbased)
 {
-	INT32 i;
 	lightlevel_t *ll;
-	sector_t *sector;
 
-	// search all sectors for ones with tag
-	for (i = -1; (i = P_FindSectorFromTag(tag, i)) >= 0 ;)
+	P_RemoveLighting(sector); // remove the old lighting effect first
+
+	if ((ticbased && !speed) || sector->lightlevel == destvalue) // set immediately
 	{
-		sector = &sectors[i];
+		sector->lightlevel = destvalue;
+		return;
+	}
+
+	ll = Z_Calloc(sizeof (*ll), PU_LEVSPEC, NULL);
+	ll->thinker.function.acp1 = (actionf_p1)T_LightFade;
+	sector->lightingdata = ll; // set it to the lightlevel_t
+
+	P_AddThinker(&ll->thinker); // add thinker
 
-		P_RemoveLighting(sector); // remove the old lighting effect first
-		ll = Z_Calloc(sizeof (*ll), PU_LEVSPEC, NULL);
-		ll->thinker.function.acp1 = (actionf_p1)T_LightFade;
-		sector->lightingdata = ll; // set it to the lightlevel_t
+	ll->sector = sector;
+	ll->sourcelevel = sector->lightlevel;
+	ll->destlevel = destvalue;
 
-		P_AddThinker(&ll->thinker); // add thinker
+	ll->fixedcurlevel = sector->lightlevel<<FRACBITS;
 
-		ll->sector = sector;
-		ll->destlevel = destvalue;
-		ll->speed = speed;
+	if (ticbased)
+	{
+		// Speed means duration.
+		ll->timer = abs(speed);
+		ll->fixedpertic = FixedDiv((destvalue<<FRACBITS) - ll->fixedcurlevel, speed<<FRACBITS);
+	}
+	else
+	{
+		// Speed means increment per tic (literally speed).
+		ll->timer = FixedDiv((destvalue<<FRACBITS) - ll->fixedcurlevel, speed<<FRACBITS)>>FRACBITS;
+		ll->fixedpertic = speed<<FRACBITS;
+	}
+}
+
+void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed, boolean ticbased, boolean force)
+{
+	INT32 i;
+	// search all sectors for ones with tag
+	for (i = -1; (i = P_FindSectorFromTag(tag, i)) >= 0 ;)
+	{
+		if (!force && ticbased // always let speed fader execute
+			&& sectors[i].lightingdata
+			&& ((lightlevel_t*)sectors[i].lightingdata)->thinker.function.acp1 == (actionf_p1)T_LightFade)
+			// && ((lightlevel_t*)sectors[i].lightingdata)->timer > 2)
+		{
+			CONS_Debug(DBG_GAMELOGIC, "Line type 420 Executor: Fade light thinker already exists, timer: %d\n", ((lightlevel_t*)sectors[i].lightingdata)->timer);
+			continue;
+		}
+		P_FadeLightBySector(&sectors[i], destvalue, speed, ticbased);
 	}
 }
 
@@ -365,30 +396,13 @@ void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed)
   */
 void T_LightFade(lightlevel_t *ll)
 {
-	if (ll->sector->lightlevel < ll->destlevel)
+	if (--ll->timer <= 0)
 	{
-		// increase the lightlevel
-		if (ll->sector->lightlevel + ll->speed >= ll->destlevel)
-		{
-			// stop changing light level
-			ll->sector->lightlevel = (INT16)ll->destlevel; // set to dest lightlevel
-
-			P_RemoveLighting(ll->sector); // clear lightingdata, remove thinker
-		}
-		else
-			ll->sector->lightlevel = (INT16)(ll->sector->lightlevel + (INT16)ll->speed); // move lightlevel
+		ll->sector->lightlevel = ll->destlevel; // set to dest lightlevel
+		P_RemoveLighting(ll->sector); // clear lightingdata, remove thinker
+		return;
 	}
-	else
-	{
-		// decrease lightlevel
-		if (ll->sector->lightlevel - ll->speed <= ll->destlevel)
-		{
-			// stop changing light level
-			ll->sector->lightlevel = (INT16)ll->destlevel; // set to dest lightlevel
 
-			P_RemoveLighting(ll->sector); // clear lightingdata, remove thinker
-		}
-		else
-			ll->sector->lightlevel = (INT16)(ll->sector->lightlevel - (INT16)ll->speed); // move lightlevel
-	}
+	ll->fixedcurlevel = ll->fixedcurlevel + ll->fixedpertic;
+	ll->sector->lightlevel = (ll->fixedcurlevel)>>FRACBITS;
 }
diff --git a/src/p_local.h b/src/p_local.h
index 83c33407639e3a39279f1b67aa14a1a3e454e215..b57a1dd4a3c2a89c4311e44190514698ae2cc1b5 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -146,6 +146,7 @@ void P_SpawnShieldOrb(player_t *player);
 void P_SwitchShield(player_t *player, UINT16 shieldtype);
 mobj_t *P_SpawnGhostMobj(mobj_t *mobj);
 void P_GivePlayerRings(player_t *player, INT32 num_rings);
+void P_GivePlayerSpheres(player_t *player, INT32 num_spheres);
 void P_GivePlayerLives(player_t *player, INT32 numlives);
 void P_GiveCoopLives(player_t *player, INT32 numlives, boolean sound);
 UINT8 P_GetNextEmerald(void);
@@ -257,6 +258,7 @@ fixed_t P_CameraCeilingZ(camera_t *mobj, sector_t *sector, sector_t *boundsec, f
 boolean P_InsideANonSolidFFloor(mobj_t *mobj, ffloor_t *rover);
 boolean P_CheckDeathPitCollide(mobj_t *mo);
 boolean P_CheckSolidLava(mobj_t *mo, ffloor_t *rover);
+void P_AdjustMobjFloorZ_FFloors(mobj_t *mo, sector_t *sector, UINT8 motype);
 
 mobj_t *P_SpawnMobjFromMobj(mobj_t *mobj, fixed_t xofs, fixed_t yofs, fixed_t zofs, mobjtype_t type);
 
@@ -310,6 +312,8 @@ void P_NewChaseDir(mobj_t *actor);
 boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed_t dist);
 
 mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz, boolean lookforplayers);
+void P_InternalFlickySetColor(mobj_t *actor, UINT8 extrainfo);
+#define P_IsFlickyCenter(type) (type > MT_FLICKY_01 && type < MT_SEED && (type - MT_FLICKY_01) % 2 ? 1 : 0)
 void P_InternalFlickyBubble(mobj_t *actor);
 void P_InternalFlickyFly(mobj_t *actor, fixed_t flyspeed, fixed_t targetdist, fixed_t chasez);
 void P_InternalFlickyHop(mobj_t *actor, fixed_t momz, fixed_t momh, angle_t angle);
@@ -323,6 +327,7 @@ void P_InternalFlickyHop(mobj_t *actor, fixed_t momz, fixed_t momh, angle_t angl
 extern boolean floatok;
 extern fixed_t tmfloorz;
 extern fixed_t tmceilingz;
+extern ffloor_t *tmfloorrover, *tmceilingrover;
 extern mobj_t *tmfloorthing, *tmhitthing, *tmthing;
 extern camera_t *mapcampointer;
 extern fixed_t tmx;
@@ -363,7 +368,7 @@ void P_DelPrecipSeclist(mprecipsecnode_t *node);
 void P_CreateSecNodeList(mobj_t *thing, fixed_t x, fixed_t y);
 void P_Initsecnode(void);
 
-void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist);
+void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 damagetype);
 
 fixed_t P_FloorzAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t height);
 boolean PIT_PushableMoved(mobj_t *thing);
@@ -413,6 +418,8 @@ typedef struct BasicFF_s
 #define DMG_DEATHPIT   0x80+3
 #define DMG_CRUSHED    0x80+4
 #define DMG_SPECTATOR  0x80+5
+// Masks
+#define DMG_CANHURTSELF 0x40 // Flag - can hurt self/team indirectly, such as through mines
 #define DMG_DEATHMASK  DMG_INSTAKILL // if bit 7 is set, this is a death type instead of a damage type
 
 void P_ForceFeed(const player_t *player, INT32 attack, INT32 fade, tic_t duration, INT32 period);
diff --git a/src/p_map.c b/src/p_map.c
index 6d1760596520f13043d8e6048800fd264d127ffe..648bb2bccc164d1fde2b3903e42870ca58361f2c 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -52,6 +52,7 @@ fixed_t tmfloorz, tmceilingz;
 static fixed_t tmdropoffz, tmdrpoffceilz; // drop-off floor/ceiling heights
 mobj_t *tmfloorthing; // the thing corresponding to tmfloorz or NULL if tmfloorz is from a sector
 mobj_t *tmhitthing; // the solid thing you bumped into (for collisions)
+ffloor_t *tmfloorrover, *tmceilingrover;
 #ifdef ESLOPE
 pslope_t *tmfloorslope, *tmceilingslope;
 #endif
@@ -101,6 +102,8 @@ boolean P_TeleportMove(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z)
 
 	thing->floorz = tmfloorz;
 	thing->ceilingz = tmceilingz;
+	thing->floorrover = tmfloorrover;
+	thing->ceilingrover = tmceilingrover;
 
 	return true;
 }
@@ -109,21 +112,148 @@ boolean P_TeleportMove(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z)
 //                       MOVEMENT ITERATOR FUNCTIONS
 // =========================================================================
 
+// P_DoSpring
+//
+// MF_SPRING does some weird, mildly hacky stuff sometimes.
+//   mass = vertical speed
+//   damage = horizontal speed
+//   raisestate = state to change spring to on collision
+//   reactiontime = number of times it can give 10 points (0 is standard)
+//   painchance = spring mode:
+//       0 = standard vanilla spring behaviour
+//     Positive spring modes are minor variants of vanilla spring behaviour.
+//       1 = launch players in jump
+//       2 = don't modify player at all, just add momentum
+//     Negative spring modes are mildly-related gimmicks with customisation.
+//      -1 = pinball bumper
+//     Any other spring mode defaults to standard vanilla spring behaviour,
+//     ****** but forward compatibility is not guaranteed for these. ******
+//
+
 boolean P_DoSpring(mobj_t *spring, mobj_t *object)
 {
-	INT32 pflags;
-	fixed_t offx, offy;
 	fixed_t vertispeed = spring->info->mass;
 	fixed_t horizspeed = spring->info->damage;
-	UINT8 secondjump;
+	boolean final;
 
-	if (object->eflags & MFE_SPRUNG) // Object was already sprung this tic
+	// Object was already sprung this tic
+	if (object->eflags & MFE_SPRUNG)
 		return false;
 
 	// Spectators don't trigger springs.
 	if (object->player && object->player->spectator)
 		return false;
 
+	// "Even in Death" is a song from Volume 8, not a command.
+	if (!spring->health || !object->health)
+		return false;
+
+	if (spring->info->painchance == -1) // Pinball bumper mode.
+	{
+		// The first of the entirely different spring modes!
+		// Some of the attributes mean different things here.
+		//   mass = default strength (can be controlled by mapthing's spawnangle)
+		//   damage = unused
+		angle_t horizangle, vertiangle;
+
+		if (!vertispeed)
+			return false;
+
+		if (object->player && object->player->homing) // Sonic Heroes and Shadow the Hedgehog are the only games to contain homing-attackable bumpers!
+		{
+			horizangle = 0;
+			vertiangle = ((object->eflags & MFE_VERTICALFLIP) ? ANGLE_270 : ANGLE_90) >> ANGLETOFINESHIFT;
+			object->player->pflags &= ~PF_THOKKED;
+			if (spring->eflags & MFE_VERTICALFLIP)
+				object->z = spring->z - object->height - 1;
+			else
+				object->z = spring->z + spring->height + 1;
+		}
+		else
+		{
+			horizangle = R_PointToAngle2(spring->x, spring->y, object->x, object->y);
+			vertiangle = (R_PointToAngle2(
+							0,
+							spring->z + spring->height/2,
+							FixedHypot(object->x - spring->x, object->y - spring->y),
+							object->z + object->height/2)
+								>> ANGLETOFINESHIFT) & FINEMASK;
+		}
+
+		if (spring->spawnpoint && spring->spawnpoint->angle > 0)
+			vertispeed = (spring->spawnpoint->angle<<(FRACBITS-1))/5;
+		vertispeed = FixedMul(vertispeed, FixedMul(object->scale, spring->scale));
+
+		if (object->player)
+		{
+			fixed_t playervelocity;
+
+			if (!(object->player->pflags & PF_THOKKED) && !(object->player->homing)
+			&& ((playervelocity = FixedDiv(9*FixedHypot(object->player->speed, object->momz), 10<<FRACBITS)) > vertispeed))
+				vertispeed = playervelocity;
+
+			if (object->player->powers[pw_carry] == CR_NIGHTSMODE) // THIS has NiGHTS support, at least...
+			{
+				angle_t nightsangle = 0;
+
+				if (object->player->bumpertime >= TICRATE/4)
+					return false;
+
+				if ((object->player->pflags & PF_TRANSFERTOCLOSEST) && object->player->axis1 && object->player->axis2)
+				{
+					nightsangle = R_PointToAngle2(object->player->axis1->x, object->player->axis1->y, object->player->axis2->x, object->player->axis2->y);
+					nightsangle += ANGLE_90;
+				}
+				else if (object->target)
+				{
+					if (object->target->flags2 & MF2_AMBUSH)
+						nightsangle = R_PointToAngle2(object->target->x, object->target->y, object->x, object->y);
+					else
+						nightsangle = R_PointToAngle2(object->x, object->y, object->target->x, object->target->y);
+				}
+
+				object->player->flyangle = AngleFixed(R_PointToAngle2(
+					0,
+					spring->z + spring->height/2,
+					FixedMul(
+						FINESINE(((nightsangle - horizangle) >> ANGLETOFINESHIFT) & FINEMASK),
+						FixedHypot(object->x - spring->x, object->y - spring->y)),
+					object->z + object->height/2))>>FRACBITS;
+
+				object->player->bumpertime = TICRATE/2;
+			}
+			else
+			{
+				INT32 pflags = object->player->pflags & (PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING|PF_THOKKED|PF_BOUNCING); // Not identical to below...
+				UINT8 secondjump = object->player->secondjump;
+				if (object->player->pflags & PF_GLIDING)
+					P_SetPlayerMobjState(object, S_PLAY_FALL);
+				P_ResetPlayer(object->player);
+				object->player->pflags |= pflags;
+				object->player->secondjump = secondjump;
+			}
+		}
+
+		if (!P_IsObjectOnGround(object)) // prevents uncurling when spinning due to "landing"
+			object->momz = FixedMul(vertispeed, FINESINE(vertiangle));
+		P_InstaThrust(object, horizangle, FixedMul(vertispeed, FINECOSINE(vertiangle)));
+
+		object->eflags |= MFE_SPRUNG; // apply this flag asap!
+
+		goto springstate;
+	}
+
+	// Does nothing?
+	if (!vertispeed && !horizspeed)
+		return false;
+
+#ifdef ESLOPE
+	object->standingslope = NULL; // Okay, now we know it's not going to be relevant - no launching off at silly angles for you.
+#endif
+
+	if (spring->eflags & MFE_VERTICALFLIP)
+		vertispeed *= -1;
+
 	if (object->player && (object->player->powers[pw_carry] == CR_NIGHTSMODE))
 	{
 		/*Someone want to make these work like bumpers?*/
@@ -135,48 +265,51 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
 	|| (object->player->charability2 == CA2_MELEE && object->player->panim == PA_ABILITY2)))
 	{
 		S_StartSound(object, sfx_s3k8b);
-		horizspeed = FixedMul(horizspeed, (4*FRACUNIT)/3);
-		vertispeed = FixedMul(vertispeed, (6*FRACUNIT)/5); // aprox square root of above
+		if (horizspeed)
+			horizspeed = FixedMul(horizspeed, (4*FRACUNIT)/3);
+		if (vertispeed)
+			vertispeed = FixedMul(vertispeed, (6*FRACUNIT)/5); // aprox square root of above
 	}
 
 	object->eflags |= MFE_SPRUNG; // apply this flag asap!
-	spring->flags &= ~(MF_SOLID|MF_SPECIAL); // De-solidify
-
-	if ((horizspeed && vertispeed) || (object->player && object->player->homing)) // Mimic SA
-	{
-		object->momx = object->momy = 0;
-		P_TryMove(object, spring->x, spring->y, true);
-	}
+	spring->flags &= ~(MF_SPRING|MF_SPECIAL); // De-solidify
 
-	if (spring->eflags & MFE_VERTICALFLIP)
-		vertispeed *= -1;
-
-	if (vertispeed > 0)
-		object->z = spring->z + spring->height + 1;
-	else if (vertispeed < 0)
-		object->z = spring->z - object->height - 1;
-	else
+	if (spring->info->painchance != 2)
 	{
-		// Horizontal springs teleport you in FRONT of them.
-		object->momx = object->momy = 0;
-
-		// Overestimate the distance to position you at
-		offx = P_ReturnThrustX(spring, spring->angle, (spring->radius + object->radius + 1) * 2);
-		offy = P_ReturnThrustY(spring, spring->angle, (spring->radius + object->radius + 1) * 2);
-
-		// Make it square by clipping
-		if (offx > (spring->radius + object->radius + 1))
-			offx = spring->radius + object->radius + 1;
-		else if (offx < -(spring->radius + object->radius + 1))
-			offx = -(spring->radius + object->radius + 1);
-
-		if (offy > (spring->radius + object->radius + 1))
-			offy = spring->radius + object->radius + 1;
-		else if (offy < -(spring->radius + object->radius + 1))
-			offy = -(spring->radius + object->radius + 1);
+		if ((horizspeed && vertispeed) || (object->player && object->player->homing)) // Mimic SA
+		{
+			object->momx = object->momy = 0;
+			P_TryMove(object, spring->x, spring->y, true);
+		}
 
-		// Set position!
-		P_TryMove(object, spring->x + offx, spring->y + offy, true);
+		if (vertispeed > 0)
+			object->z = spring->z + spring->height + 1;
+		else if (vertispeed < 0)
+			object->z = spring->z - object->height - 1;
+		else
+		{
+			fixed_t offx, offy;
+			// Horizontal springs teleport you in FRONT of them.
+			object->momx = object->momy = 0;
+
+			// Overestimate the distance to position you at
+			offx = P_ReturnThrustX(spring, spring->angle, (spring->radius + object->radius + 1) * 2);
+			offy = P_ReturnThrustY(spring, spring->angle, (spring->radius + object->radius + 1) * 2);
+
+			// Make it square by clipping
+			if (offx > (spring->radius + object->radius + 1))
+				offx = spring->radius + object->radius + 1;
+			else if (offx < -(spring->radius + object->radius + 1))
+				offx = -(spring->radius + object->radius + 1);
+
+			if (offy > (spring->radius + object->radius + 1))
+				offy = spring->radius + object->radius + 1;
+			else if (offy < -(spring->radius + object->radius + 1))
+				offy = -(spring->radius + object->radius + 1);
+
+			// Set position!
+			P_TryMove(object, spring->x + offx, spring->y + offy, true);
+		}
 	}
 
 	if (vertispeed)
@@ -186,12 +319,14 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
 		P_InstaThrustEvenIn2D(object, spring->angle, FixedMul(horizspeed,FixedSqrt(FixedMul(object->scale, spring->scale))));
 
 	// Re-solidify
-	spring->flags |= (spring->info->flags & (MF_SPECIAL|MF_SOLID));
-
-	P_SetMobjState(spring, spring->info->raisestate);
+	spring->flags |= (spring->info->flags & (MF_SPRING|MF_SPECIAL));
 
 	if (object->player)
 	{
+		INT32 pflags;
+		UINT8 secondjump;
+		boolean washoming;
+
 		if (spring->flags & MF_ENEMY) // Spring shells
 			P_SetTarget(&spring->target, object);
 
@@ -212,19 +347,28 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
 			}
 		}
 
-		pflags = object->player->pflags & (PF_STARTJUMP|PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING|PF_THOKKED|PF_SHIELDABILITY|PF_BOUNCING); // I still need these.
+		pflags = object->player->pflags & (PF_STARTJUMP|PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING|PF_THOKKED|PF_BOUNCING); // I still need these.
 		secondjump = object->player->secondjump;
+		washoming = object->player->homing;
+		if (object->player->pflags & PF_GLIDING)
+			P_SetPlayerMobjState(object, S_PLAY_FALL);
 		P_ResetPlayer(object->player);
 
-		if (spring->info->painchance)
+		if (spring->info->painchance == 1) // For all those ancient, SOC'd abilities.
 		{
 			object->player->pflags |= P_GetJumpFlags(object->player);
 			P_SetPlayerMobjState(object, S_PLAY_JUMP);
 		}
-		else if (!vertispeed || (pflags & PF_BOUNCING)) // horizontal spring or bouncing
+		else if ((spring->info->painchance == 2) || (pflags & PF_BOUNCING)) // Adding momentum only.
 		{
-			if ((pflags & PF_BOUNCING)
-			|| (pflags & (PF_JUMPED|PF_SPINNING) && (object->player->panim == PA_ROLL || object->player->panim == PA_JUMP || object->player->panim == PA_FALL)))
+			object->player->pflags |= (pflags &~ PF_STARTJUMP);
+			object->player->secondjump = secondjump;
+			if (washoming)
+				object->player->pflags &= ~PF_THOKKED;
+		}
+		else if (!vertispeed)
+		{
+			if (pflags & (PF_JUMPED|PF_SPINNING))
 			{
 				object->player->pflags |= pflags;
 				object->player->secondjump = secondjump;
@@ -239,10 +383,25 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
 	}
 
 #ifdef ESLOPE
-	object->standingslope = NULL; // Okay, now we know it's not going to be relevant - no launching off at silly angles for you.
+	object->standingslope = NULL; // And again.
 #endif
 
-	return true;
+	final = true;
+
+springstate:
+	if ((statenum_t)(spring->state-states) < spring->info->raisestate)
+	{
+		P_SetMobjState(spring, spring->info->raisestate);
+		if (object->player && spring->reactiontime && !(spring->info->flags & MF_ENEMY))
+		{
+			if (object->player->powers[pw_carry] != CR_NIGHTSMODE) // don't make graphic in NiGHTS
+				P_SetMobjState(P_SpawnMobj(spring->x, spring->y, spring->z + (spring->height/2), MT_SCORE), mobjinfo[MT_SCORE].spawnstate+11);
+			P_AddPlayerScore(object->player, 10);
+			spring->reactiontime--;
+		}
+	}
+
+	return final;
 }
 
 static void P_DoFanAndGasJet(mobj_t *spring, mobj_t *object)
@@ -398,7 +557,6 @@ static void P_DoTailsCarry(player_t *sonic, player_t *tails)
 static boolean PIT_CheckThing(mobj_t *thing)
 {
 	fixed_t blockdist;
-	boolean iwassprung = false;
 
 	// don't clip against self
 	if (thing == tmthing)
@@ -514,7 +672,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		return true;
 	}
 
-	if (!(thing->flags & (MF_SOLID|MF_SPECIAL|MF_PAIN|MF_SHOOTABLE)))
+	if (!(thing->flags & (MF_SOLID|MF_SPECIAL|MF_PAIN|MF_SHOOTABLE|MF_SPRING)))
 		return true;
 
 	// Don't collide with your buddies while NiGHTS-flying.
@@ -622,6 +780,54 @@ static boolean PIT_CheckThing(mobj_t *thing)
 	}
 #endif
 
+	// Billiards mines!
+	if (thing->type == MT_BIGMINE)
+	{
+		if (tmthing->type == MT_BIGMINE)
+		{
+			if (!tmthing->momx && !tmthing->momy)
+				return true;
+			if ((statenum_t)(thing->state-states) >= thing->info->meleestate)
+				return true;
+			if (thing->z > tmthing->z + tmthing->height)
+				return true; // overhead
+			if (thing->z + thing->height < tmthing->z)
+				return true; // underneath
+
+			thing->momx = tmthing->momx/3;
+			thing->momy = tmthing->momy/3;
+			thing->momz = tmthing->momz/3;
+			tmthing->momx /= -8;
+			tmthing->momy /= -8;
+			tmthing->momz /= -8;
+			if (thing->info->activesound)
+				S_StartSound(thing, thing->info->activesound);
+			P_SetMobjState(thing, thing->info->meleestate);
+			P_SetTarget(&thing->tracer, tmthing->tracer);
+			return true;
+		}
+		else if (tmthing->type == MT_CRUSHCLAW)
+		{
+			if (tmthing->extravalue1 <= 0)
+				return true;
+			if ((statenum_t)(thing->state-states) >= thing->info->meleestate)
+				return true;
+			if (thing->z > tmthing->z + tmthing->height)
+				return true; // overhead
+			if (thing->z + thing->height < tmthing->z)
+				return true; // underneath
+
+			thing->momx = P_ReturnThrustX(tmthing, tmthing->angle, 2*tmthing->extravalue1*tmthing->scale/3);
+			thing->momy = P_ReturnThrustY(tmthing, tmthing->angle, 2*tmthing->extravalue1*tmthing->scale/3);
+			if (thing->info->activesound)
+				S_StartSound(thing, thing->info->activesound);
+			P_SetMobjState(thing, thing->info->meleestate);
+			if (tmthing->tracer)
+				P_SetTarget(&thing->tracer, tmthing->tracer->target);
+			return false;
+		}
+	}
+
 	// When solid spikes move, assume they just popped up and teleport things on top of them to hurt.
 	if (tmthing->type == MT_SPIKE && tmthing->flags & MF_SOLID)
 	{
@@ -639,35 +845,37 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		return true;
 	}
 
-	if (thing->flags & MF_PAIN)
+	if (thing->flags & MF_PAIN && tmthing->player)
 	{ // Player touches painful thing sitting on the floor
 		// see if it went over / under
 		if (thing->z > tmthing->z + tmthing->height)
 			return true; // overhead
 		if (thing->z + thing->height < tmthing->z)
 			return true; // underneath
-		if (tmthing->player && tmthing->flags & MF_SHOOTABLE && thing->health > 0)
+		if (tmthing->flags & MF_SHOOTABLE && thing->health > 0)
 		{
-			UINT8 damagetype = 0;
-			if (thing->flags & MF_FIRE) // BURN!
+			UINT8 damagetype = (thing->info->mass & 0xFF);
+			if (!damagetype && thing->flags & MF_FIRE) // BURN!
 				damagetype = DMG_FIRE;
-			P_DamageMobj(tmthing, thing, thing, 1, damagetype);
+			if (P_DamageMobj(tmthing, thing, thing, 1, damagetype) && (damagetype = (thing->info->mass>>8)))
+				S_StartSound(thing, damagetype);
 		}
 		return true;
 	}
-	else if (tmthing->flags & MF_PAIN)
+	else if (tmthing->flags & MF_PAIN && thing->player)
 	{ // Painful thing splats player in the face
 		// see if it went over / under
 		if (tmthing->z > thing->z + thing->height)
 			return true; // overhead
 		if (tmthing->z + tmthing->height < thing->z)
 			return true; // underneath
-		if (thing->player && thing->flags & MF_SHOOTABLE && tmthing->health > 0)
+		if (thing->flags & MF_SHOOTABLE && tmthing->health > 0)
 		{
-			UINT8 damagetype = 0;
-			if (tmthing->flags & MF_FIRE) // BURN!
+			UINT8 damagetype = (tmthing->info->mass & 0xFF);
+			if (!damagetype && tmthing->flags & MF_FIRE) // BURN!
 				damagetype = DMG_FIRE;
-			P_DamageMobj(thing, tmthing, tmthing, 1, damagetype);
+			if (P_DamageMobj(thing, tmthing, tmthing, 1, damagetype) && (damagetype = (tmthing->info->mass>>8)))
+				S_StartSound(tmthing, damagetype);
 		}
 		return true;
 	}
@@ -714,6 +922,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		else if (tmz > thzh - sprarea && tmz < thzh) // Don't damage people springing up / down
 			return true;
 	}
+
 	// missiles can hit other things
 	if (tmthing->flags & MF_MISSILE || tmthing->type == MT_SHELL)
 	{
@@ -768,30 +977,11 @@ static boolean PIT_CheckThing(mobj_t *thing)
 
 		if (thing->type == MT_EGGSHIELD)
 		{
-			fixed_t touchx, touchy;
-			angle_t angle;
+			angle_t angle = (R_PointToAngle2(thing->x, thing->y, tmthing->x - tmthing->momx, tmthing->y - tmthing->momy) - thing->angle) - ANGLE_90;
 
-			if (P_AproxDistance(tmthing->x-thing->x, tmthing->y-thing->y) >
-				P_AproxDistance((tmthing->x-tmthing->momx)-thing->x, (tmthing->y-tmthing->momy)-thing->y))
-			{
-				touchx = tmthing->x + tmthing->momx;
-				touchy = tmthing->y + tmthing->momy;
-			}
-			else
-			{
-				touchx = tmthing->x;
-				touchy = tmthing->y;
-			}
-
-			angle = R_PointToAngle2(thing->x, thing->y, touchx, touchy) - thing->angle;
-
-			if (!(angle > ANGLE_90 && angle < ANGLE_270)) // hit front of shield, didn't destroy it
-				return false;
-			else // hit shield from behind, shield is destroyed!
-			{
+			if (angle < ANGLE_180) // hit shield from behind, shield is destroyed!
 				P_KillMobj(thing, tmthing, tmthing, 0);
-				return false;
-			}
+			return false;
 		}
 
 		// damage / explode
@@ -835,7 +1025,12 @@ static boolean PIT_CheckThing(mobj_t *thing)
 			P_SetThingPosition(tmthing);
 		}
 		else if (!(tmthing->type == MT_SHELL && thing->player)) // player collision handled in touchspecial
-			P_DamageMobj(thing, tmthing, tmthing->target, 1, 0);
+		{
+			UINT8 damagetype = tmthing->info->mass;
+			if (!damagetype && tmthing->flags & MF_FIRE) // BURN!
+				damagetype = DMG_FIRE;
+			P_DamageMobj(thing, tmthing, tmthing->target, 1, damagetype);
+		}
 
 		// don't traverse any more
 
@@ -905,26 +1100,39 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		P_SetTarget(&thing->target, tmthing);
 	}
 
-	// Respawn rings and items
+	// NiGHTS lap logic
 	if ((tmthing->type == MT_NIGHTSDRONE || thing->type == MT_NIGHTSDRONE)
 	 && (tmthing->player || thing->player))
 	{
 		mobj_t *droneobj = (tmthing->type == MT_NIGHTSDRONE) ? tmthing : thing;
 		player_t *pl = (droneobj == thing) ? tmthing->player : thing->player;
 
-		// Must be in bonus time, and must be NiGHTS, must wait about a second
+		// Must be NiGHTS, must wait about a second
 		// must be flying in the SAME DIRECTION as the last time you came through.
 		// not (your direction) xor (stored direction)
 		// In other words, you can't u-turn and respawn rings near the drone.
-		if (pl->bonustime && (pl->powers[pw_carry] == CR_NIGHTSMODE) && (INT32)leveltime > droneobj->extravalue2 && (
-		   !(pl->anotherflyangle >= 90 &&   pl->anotherflyangle <= 270)
-		^ (droneobj->extravalue1 >= 90 && droneobj->extravalue1 <= 270)
+		if ((pl->powers[pw_carry] == CR_NIGHTSMODE) && (INT32)leveltime > droneobj->extravalue2 && (
+		   !(pl->flyangle > 90 &&   pl->flyangle < 270)
+		^ (droneobj->extravalue1 > 90 && droneobj->extravalue1 < 270)
 		))
 		{
-			// Reload all the fancy ring stuff!
-			P_ReloadRings();
+			pl->marelap++;
+			pl->totalmarelap++;
+			pl->lapbegunat = leveltime;
+			pl->lapstartedtime = pl->nightstime;
+
+			if (pl->bonustime)
+			{
+				pl->marebonuslap++;
+				pl->totalmarebonuslap++;
+
+				// Respawn rings and items
+				P_ReloadRings();
+			}
+
+			P_RunNightsLapExecutors(pl->mo);
 		}
-		droneobj->extravalue1 = pl->anotherflyangle;
+		droneobj->extravalue1 = pl->flyangle;
 		droneobj->extravalue2 = (INT32)leveltime + TICRATE;
 	}
 
@@ -1030,15 +1238,28 @@ static boolean PIT_CheckThing(mobj_t *thing)
 	if (tmthing->flags & MF_PUSHABLE)
 	{
 		if (thing->type == MT_FAN || thing->type == MT_STEAM)
+		{
 			P_DoFanAndGasJet(thing, tmthing);
+			return true;
+		}
 		else if (thing->flags & MF_SPRING)
 		{
 			if ( thing->z <= tmthing->z + tmthing->height
 			&& tmthing->z <= thing->z + thing->height)
-				iwassprung = P_DoSpring(thing, tmthing);
+				if (P_DoSpring(thing, tmthing))
+					return false;
+			return true;
 		}
 	}
 
+	// thanks to sal for solidenemies dot lua
+	if (thing->flags & (MF_ENEMY|MF_BOSS) && tmthing->flags & (MF_ENEMY|MF_BOSS))
+	{
+		if ((thing->z + thing->height >= tmthing->z)
+		&& (tmthing->z + tmthing->height >= thing->z))
+			return false;
+	}
+
 	// Damage other players when invincible
 	if (tmthing->player && thing->player
 	// Make sure they aren't able to damage you ANYWHERE along the Z axis, you have to be TOUCHING the person.
@@ -1103,7 +1324,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 			{
 				// Objects kill you if it falls from above.
 				if (thing != tmthing->target)
-					P_DamageMobj(thing, tmthing, tmthing->target, 1, DMG_INSTAKILL);
+					P_DamageMobj(thing, tmthing, tmthing->target, 1, DMG_CRUSHED);
 
 				tmthing->momz = -tmthing->momz/2; // Bounce, just for fun!
 				// The tmthing->target allows the pusher of the object
@@ -1126,75 +1347,62 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		{
 			if ( thing->z <= tmthing->z + tmthing->height
 			&& tmthing->z <= thing->z + thing->height)
-				iwassprung = P_DoSpring(thing, tmthing);
+				if (P_DoSpring(thing, tmthing))
+					return false;
+			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)
+		// Monitor?
+		else if (thing->flags & MF_MONITOR
+		&& !((thing->type == MT_RING_REDBOX && tmthing->player->ctfteam != 1) || (thing->type == MT_RING_BLUEBOX && tmthing->player->ctfteam != 2)))
 		{
 			// 0 = none, 1 = elemental pierce, 2 = bubble bounce
 			UINT8 elementalpierce = (((tmthing->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL || (tmthing->player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP) && (tmthing->player->pflags & PF_SHIELDABILITY)
 			? (((tmthing->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) ? 1 : 2)
 			: 0);
-			if (thing->flags & MF_MONITOR
-				&& (tmthing->player->pflags & (PF_SPINNING|PF_GLIDING)
-				|| ((tmthing->player->pflags & PF_JUMPED)
-					&& (!(tmthing->player->pflags & PF_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 || tmthing->player->pflags & PF_BOUNCING)
-					&& (P_MobjFlip(tmthing)*(tmthing->z - (thing->z + thing->height/2)) > 0) && (P_MobjFlip(tmthing)*tmthing->momz < 0))
-				|| elementalpierce))
+			if (!(thing->flags & MF_SOLID)
+			|| tmthing->player->pflags & (PF_SPINNING|PF_GLIDING)
+			|| ((tmthing->player->pflags & PF_JUMPED)
+				&& (!(tmthing->player->pflags & PF_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 || tmthing->player->pflags & PF_BOUNCING)
+				&& (P_MobjFlip(tmthing)*(tmthing->z - (thing->z + thing->height/2)) > 0) && (P_MobjFlip(tmthing)*tmthing->momz < 0))
+			|| elementalpierce)
 			{
-				player_t *player = tmthing->player;
-				SINT8 flipval = P_MobjFlip(thing); // Save this value in case monitor gets removed.
-				fixed_t *momz = &tmthing->momz; // tmthing gets changed by P_DamageMobj, so we need a new pointer?! X_x;;
-				fixed_t *z = &tmthing->z; // aau.
-				P_DamageMobj(thing, tmthing, tmthing, 1, 0); // break the monitor
-				// Going down? Then bounce back up.
-				if ((P_MobjWasRemoved(thing) // Monitor was removed
-					|| !thing->health) // or otherwise popped
-				&& (flipval*(*momz) < 0) // monitor is on the floor and you're going down, or on the ceiling and you're going up
-				&& (elementalpierce != 1)) // you're not piercing through the monitor...
+				if (thing->z - thing->scale <= tmthing->z + tmthing->height
+				&& thing->z + thing->height + thing->scale >= tmthing->z)
 				{
-					if (elementalpierce == 2)
-						P_DoBubbleBounce(player);
-					else if (!(player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2))
-						*momz = -*momz; // Therefore, you should be thrust in the opposite direction, vertically.
-				}
-				if (!(elementalpierce == 1 && thing->flags & MF_GRENADEBOUNCE)) // prevent gold monitor clipthrough.
-				{
-					if (player->pflags & PF_BOUNCING)
-						P_DoAbilityBounce(player, false);
-					return false;
+					player_t *player = tmthing->player;
+					SINT8 flipval = P_MobjFlip(thing); // Save this value in case monitor gets removed.
+					fixed_t *momz = &tmthing->momz; // tmthing gets changed by P_DamageMobj, so we need a new pointer?! X_x;;
+					fixed_t *z = &tmthing->z; // aau.
+					// Going down? Then bounce back up.
+					if (P_DamageMobj(thing, tmthing, tmthing, 1, 0) // break the monitor
+					&& (flipval*(*momz) < 0) // monitor is on the floor and you're going down, or on the ceiling and you're going up
+					&& (elementalpierce != 1)) // you're not piercing through the monitor...
+					{
+						if (elementalpierce == 2)
+							P_DoBubbleBounce(player);
+						else if (!(player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2))
+							*momz = -*momz; // Therefore, you should be thrust in the opposite direction, vertically.
+					}
+					if (!(elementalpierce == 1 && thing->flags & MF_GRENADEBOUNCE)) // prevent gold monitor clipthrough.
+					{
+						if (player->pflags & PF_BOUNCING)
+							P_DoAbilityBounce(player, false);
+						return false;
+					}
+					else
+						*z -= *momz; // to ensure proper collision.
 				}
-				else
-					*z -= *momz; // to ensure proper collision.
+
+				return true;
 			}
 		}
 	}
 
-	if (!(tmthing->player) && (thing->player))
+	if ((!tmthing->player) && (thing->player))
 		; // no solid thing should ever be able to step up onto a player
-	else if (thing->flags & MF_SPRING && (tmthing->player || tmthing->flags & MF_PUSHABLE))
-	{
-		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 if (tmthing->flags & MF_SPRING && (thing->flags & MF_PUSHABLE))
-		; // Fix a few nasty spring-jumping bugs that happen sometimes.
-	// 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->pflags & PF_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 || tmthing->player->pflags & PF_BOUNCING)
-			&& (P_MobjFlip(tmthing)*(tmthing->z - (thing->z + thing->height/2)) > 0) && (P_MobjFlip(tmthing)*tmthing->momz < 0)))
-	&& !((thing->type == MT_RING_REDBOX && tmthing->player->ctfteam != 1) || (thing->type == MT_RING_BLUEBOX && tmthing->player->ctfteam != 2)))
-		;
 	// z checking at last
 	// Treat noclip things as non-solid!
 	else if ((thing->flags & (MF_SOLID|MF_NOCLIP)) == MF_SOLID
@@ -1212,6 +1420,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 				if (thing->z + thing->height > tmfloorz)
 				{
 					tmfloorz = thing->z + thing->height;
+					tmfloorrover = NULL;
 #ifdef ESLOPE
 					tmfloorslope = NULL;
 #endif
@@ -1221,19 +1430,18 @@ static boolean PIT_CheckThing(mobj_t *thing)
 
 			topz = thing->z - thing->scale; // FixedMul(FRACUNIT, thing->scale), but thing->scale == FRACUNIT in base scale anyways
 
-			if (thing->flags & MF_SPRING)
-				;
 			// block only when jumping not high enough,
 			// (dont climb max. 24units while already in air)
 			// since return false doesn't handle momentum properly,
 			// we lie to P_TryMove() so it's always too high
-			else if (tmthing->player && tmthing->z + tmthing->height > topz
+			if (tmthing->player && tmthing->z + tmthing->height > topz
 				&& tmthing->z + tmthing->height < tmthing->ceilingz)
 			{
-				if (thing->flags & MF_GRENADEBOUNCE && (thing->flags & MF_MONITOR || thing->flags2 & MF2_STANDONME)) // Gold monitor hack...
+				if (thing->flags & MF_GRENADEBOUNCE && (thing->flags & MF_MONITOR || thing->info->flags & MF_MONITOR)) // Gold monitor hack...
 					return false;
 
 				tmfloorz = tmceilingz = topz; // block while in air
+				tmceilingrover = NULL;
 #ifdef ESLOPE
 				tmceilingslope = NULL;
 #endif
@@ -1242,6 +1450,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 			else if (topz < tmceilingz && tmthing->z <= thing->z+thing->height)
 			{
 				tmceilingz = topz;
+				tmceilingrover = NULL;
 #ifdef ESLOPE
 				tmceilingslope = NULL;
 #endif
@@ -1258,6 +1467,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 				if (thing->z < tmceilingz)
 				{
 					tmceilingz = thing->z;
+					tmceilingrover = NULL;
 #ifdef ESLOPE
 					tmceilingslope = NULL;
 #endif
@@ -1267,19 +1477,18 @@ static boolean PIT_CheckThing(mobj_t *thing)
 
 			topz = thing->z + thing->height + thing->scale; // FixedMul(FRACUNIT, thing->scale), but thing->scale == FRACUNIT in base scale anyways
 
-			if (thing->flags & MF_SPRING)
-				;
 			// block only when jumping not high enough,
 			// (dont climb max. 24units while already in air)
 			// since return false doesn't handle momentum properly,
 			// we lie to P_TryMove() so it's always too high
-			else if (tmthing->player && tmthing->z < topz
+			if (tmthing->player && tmthing->z < topz
 				&& tmthing->z > tmthing->floorz)
 			{
-				if (thing->flags & MF_GRENADEBOUNCE && (thing->flags & MF_MONITOR || thing->flags2 & MF2_STANDONME)) // Gold monitor hack...
+				if (thing->flags & MF_GRENADEBOUNCE && (thing->flags & MF_MONITOR || thing->info->flags & MF_MONITOR)) // Gold monitor hack...
 					return false;
 
 				tmfloorz = tmceilingz = topz; // block while in air
+				tmfloorrover = NULL;
 #ifdef ESLOPE
 				tmfloorslope = NULL;
 #endif
@@ -1288,6 +1497,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 			else if (topz > tmfloorz && tmthing->z+tmthing->height >= thing->z)
 			{
 				tmfloorz = topz;
+				tmfloorrover = NULL;
 #ifdef ESLOPE
 				tmfloorslope = NULL;
 #endif
@@ -1439,6 +1649,7 @@ static boolean PIT_CheckLine(line_t *ld)
 	{
 		tmceilingz = opentop;
 		ceilingline = ld;
+		tmceilingrover = NULL;
 #ifdef ESLOPE
 		tmceilingslope = opentopslope;
 #endif
@@ -1447,6 +1658,7 @@ static boolean PIT_CheckLine(line_t *ld)
 	if (openbottom > tmfloorz)
 	{
 		tmfloorz = openbottom;
+		tmfloorrover = NULL;
 #ifdef ESLOPE
 		tmfloorslope = openbottomslope;
 #endif
@@ -1528,6 +1740,8 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y)
 	// will adjust them.
 	tmfloorz = tmdropoffz = P_GetFloorZ(thing, newsubsec->sector, x, y, NULL); //newsubsec->sector->floorheight;
 	tmceilingz = P_GetCeilingZ(thing, newsubsec->sector, x, y, NULL); //newsubsec->sector->ceilingheight;
+	tmfloorrover = NULL;
+	tmceilingrover = NULL;
 #ifdef ESLOPE
 	tmfloorslope = newsubsec->sector->f_slope;
 	tmceilingslope = newsubsec->sector->c_slope;
@@ -1571,6 +1785,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y)
 					{
 						if (tmfloorz < topheight - sinklevel) {
 							tmfloorz = topheight - sinklevel;
+							tmfloorrover = rover;
 #ifdef ESLOPE
 							tmfloorslope = *rover->t_slope;
 #endif
@@ -1580,6 +1795,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y)
 					{
 						if (tmceilingz > bottomheight + sinklevel) {
 							tmceilingz = bottomheight + sinklevel;
+							tmceilingrover = rover;
 #ifdef ESLOPE
 							tmceilingslope = *rover->b_slope;
 #endif
@@ -1604,6 +1820,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y)
 				{
 					if (tmfloorz < thing->z) {
 						tmfloorz = thing->z;
+						tmfloorrover = rover;
 #ifdef ESLOPE
 						tmfloorslope = NULL;
 #endif
@@ -1622,6 +1839,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y)
 				&& !(rover->flags & FF_REVERSEPLATFORM))
 			{
 				tmfloorz = tmdropoffz = topheight;
+				tmfloorrover = rover;
 #ifdef ESLOPE
 				tmfloorslope = *rover->t_slope;
 #endif
@@ -1631,6 +1849,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y)
 				&& !(thing->type == MT_SKIM && (rover->flags & FF_SWIMMABLE)))
 			{
 				tmceilingz = tmdrpoffceilz = bottomheight;
+				tmceilingrover = rover;
 #ifdef ESLOPE
 				tmceilingslope = *rover->b_slope;
 #endif
@@ -2127,6 +2346,8 @@ boolean PIT_PushableMoved(mobj_t *thing)
 		mobj_t *oldthing = tmthing;
 		line_t *oldceilline = ceilingline;
 		line_t *oldblockline = blockingline;
+		ffloor_t *oldflrrover = tmfloorrover;
+		ffloor_t *oldceilrover = tmceilingrover;
 #ifdef ESLOPE
 		pslope_t *oldfslope = tmfloorslope;
 		pslope_t *oldcslope = tmceilingslope;
@@ -2143,6 +2364,8 @@ boolean PIT_PushableMoved(mobj_t *thing)
 		P_SetTarget(&tmthing, oldthing);
 		ceilingline = oldceilline;
 		blockingline = oldblockline;
+		tmfloorrover = oldflrrover;
+		tmceilingrover = oldceilrover;
 #ifdef ESLOPE
 		tmfloorslope = oldfslope;
 		tmceilingslope = oldcslope;
@@ -2264,6 +2487,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
 					if (thingtop == thing->ceilingz && tmceilingz > thingtop && tmceilingz - thingtop <= maxstep)
 					{
 						thing->z = (thing->ceilingz = thingtop = tmceilingz) - thing->height;
+						thing->ceilingrover = tmceilingrover;
 						thing->eflags |= MFE_JUSTSTEPPEDDOWN;
 					}
 #ifdef ESLOPE
@@ -2271,6 +2495,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
 					else if (tmceilingslope && tmceilingz < thingtop && thingtop - tmceilingz <= maxstep)
 					{
 						thing->z = (thing->ceilingz = thingtop = tmceilingz) - thing->height;
+						thing->ceilingrover = tmceilingrover;
 						thing->eflags |= MFE_JUSTSTEPPEDDOWN;
 					}
 #endif
@@ -2278,6 +2503,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
 				else if (thing->z == thing->floorz && tmfloorz < thing->z && thing->z - tmfloorz <= maxstep)
 				{
 					thing->z = thing->floorz = tmfloorz;
+					thing->floorrover = tmfloorrover;
 					thing->eflags |= MFE_JUSTSTEPPEDDOWN;
 				}
 #ifdef ESLOPE
@@ -2285,6 +2511,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
 				else if (tmfloorslope && tmfloorz > thing->z && tmfloorz - thing->z <= maxstep)
 				{
 					thing->z = thing->floorz = tmfloorz;
+					thing->floorrover = tmfloorrover;
 					thing->eflags |= MFE_JUSTSTEPPEDDOWN;
 				}
 #endif
@@ -2356,6 +2583,8 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
 
 	thing->floorz = tmfloorz;
 	thing->ceilingz = tmceilingz;
+	thing->floorrover = tmfloorrover;
+	thing->ceilingrover = tmceilingrover;
 
 #ifdef ESLOPE
 	if (!(thing->flags & MF_NOCLIPHEIGHT))
@@ -2436,6 +2665,8 @@ boolean P_SceneryTryMove(mobj_t *thing, fixed_t x, fixed_t y)
 
 	thing->floorz = tmfloorz;
 	thing->ceilingz = tmceilingz;
+	thing->floorrover = tmfloorrover;
+	thing->ceilingrover = tmceilingrover;
 	thing->x = x;
 	thing->y = y;
 
@@ -2462,7 +2693,10 @@ static boolean P_ThingHeightClip(mobj_t *thing)
 {
 	boolean floormoved;
 	fixed_t oldfloorz = thing->floorz;
+	ffloor_t *oldfloorrover = thing->floorrover;
+	ffloor_t *oldceilingrover = thing->ceilingrover;
 	boolean onfloor = P_IsObjectOnGround(thing);//(thing->z <= thing->floorz);
+	ffloor_t *rover = NULL;
 
 	if (thing->flags & MF_NOCLIPHEIGHT)
 		return true;
@@ -2477,6 +2711,8 @@ static boolean P_ThingHeightClip(mobj_t *thing)
 
 	thing->floorz = tmfloorz;
 	thing->ceilingz = tmceilingz;
+	thing->floorrover = tmfloorrover;
+	thing->ceilingrover = tmceilingrover;
 
 	// Ugly hack?!?! As long as just ceilingz is the lowest,
 	// you'll still get crushed, right?
@@ -2485,16 +2721,23 @@ static boolean P_ThingHeightClip(mobj_t *thing)
 
 	if (onfloor && !(thing->flags & MF_NOGRAVITY) && floormoved)
 	{
-		if (thing->eflags & MFE_VERTICALFLIP)
-			thing->pmomz = thing->ceilingz - (thing->z + thing->height);
-		else
-			thing->pmomz = thing->floorz - thing->z;
-		thing->eflags |= MFE_APPLYPMOMZ;
+		rover = (thing->eflags & MFE_VERTICALFLIP) ? oldceilingrover : oldfloorrover;
 
-		if (thing->eflags & MFE_VERTICALFLIP)
-			thing->z = thing->ceilingz - thing->height;
-		else
-			thing->z = thing->floorz;
+		// Match the Thing's old floorz to an FOF and check for FF_EXISTS
+		// If ~FF_EXISTS, don't set mobj Z.
+		if (!rover || ((rover->flags & FF_EXISTS) && (rover->flags & FF_SOLID)))
+		{
+			if (thing->eflags & MFE_VERTICALFLIP)
+				thing->pmomz = thing->ceilingz - (thing->z + thing->height);
+			else
+				thing->pmomz = thing->floorz - thing->z;
+			thing->eflags |= MFE_APPLYPMOMZ;
+
+			if (thing->eflags & MFE_VERTICALFLIP)
+				thing->z = thing->ceilingz - thing->height;
+			else
+				thing->z = thing->floorz;
+		}
 	}
 	else if (!tmfloorthing)
 	{
@@ -3498,6 +3741,7 @@ bounceback:
 static fixed_t bombdamage;
 static mobj_t *bombsource;
 static mobj_t *bombspot;
+static UINT8 bombdamagetype;
 
 //
 // PIT_RadiusAttack
@@ -3508,17 +3752,13 @@ static boolean PIT_RadiusAttack(mobj_t *thing)
 {
 	fixed_t dx, dy, dz, dist;
 
-	if (thing == bombspot // ignore the bomb itself (Deton fix)
-	|| (bombsource && thing->type == bombsource->type)) // ignore the type of guys who dropped the bomb (Jetty-Syn Bomber or Skim can bomb eachother, but not themselves.)
-		return true;
-
-	if (!(thing->flags & MF_SHOOTABLE))
+	if (thing == bombspot) // ignore the bomb itself (Deton fix)
 		return true;
 
-	if (thing->flags & MF_BOSS)
+	if ((thing->flags & (MF_MONITOR|MF_SHOOTABLE)) != MF_SHOOTABLE)
 		return true;
 
-	if (thing->flags & MF_MONITOR)
+	 if (bombsource && thing->type == bombsource->type && !(bombdamagetype & DMG_CANHURTSELF)) // ignore the type of guys who dropped the bomb (Jetty-Syn Bomber or Skim can bomb eachother, but not themselves.)
 		return true;
 
 	dx = abs(thing->x - bombspot->x);
@@ -3542,7 +3782,7 @@ static boolean PIT_RadiusAttack(mobj_t *thing)
 
 	if (P_CheckSight(thing, bombspot))
 	{	// must be in direct path
-		P_DamageMobj(thing, bombspot, bombsource, 1, 0); // Tails 01-11-2001
+		P_DamageMobj(thing, bombspot, bombsource, 1, bombdamagetype); // Tails 01-11-2001
 	}
 
 	return true;
@@ -3552,7 +3792,7 @@ static boolean PIT_RadiusAttack(mobj_t *thing)
 // P_RadiusAttack
 // Source is the creature that caused the explosion at spot.
 //
-void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist)
+void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 damagetype)
 {
 	INT32 x, y;
 	INT32 xl, xh, yl, yh;
@@ -3569,6 +3809,7 @@ void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist)
 	bombspot = spot;
 	bombsource = source;
 	bombdamage = FixedMul(damagedist, spot->scale);
+	bombdamagetype = damagetype;
 
 	for (y = yl; y <= yh; y++)
 		for (x = xl; x <= xh; x++)
diff --git a/src/p_mobj.c b/src/p_mobj.c
index e3dcd3c2e8a4025c8baaf54fdad9d1c84f56be46..b5aae65d187296c1e5d4d64a00380606c924a2f6 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -880,7 +880,7 @@ void P_ExplodeMissile(mobj_t *mo)
 
 	if (mo->type == MT_DETON)
 	{
-		P_RadiusAttack(mo, mo, 96*FRACUNIT);
+		P_RadiusAttack(mo, mo, 96*FRACUNIT, 0);
 
 		explodemo = P_SpawnMobj(mo->x, mo->y, mo->z, MT_EXPLODE);
 		P_SetScale(explodemo, mo->scale);
@@ -1547,6 +1547,8 @@ fixed_t P_GetMobjGravity(mobj_t *mo)
 			{
 				case MT_FLINGRING:
 				case MT_FLINGCOIN:
+				case MT_FLINGBLUESPHERE:
+				case MT_FLINGNIGHTSCHIP:
 				case MT_FLINGEMERALD:
 				case MT_BOUNCERING:
 				case MT_RAILRING:
@@ -2167,7 +2169,7 @@ void P_XYMovement(mobj_t *mo)
 	if (mo->flags & MF_NOCLIPHEIGHT)
 		return; // no frictions for objects that can pass through floors
 
-	if (mo->flags & MF_MISSILE || mo->flags2 & MF2_SKULLFLY || mo->type == MT_SHELL || mo->type == MT_VULTURE)
+	if (mo->flags & MF_MISSILE || mo->flags2 & MF2_SKULLFLY || mo->type == MT_SHELL || mo->type == MT_VULTURE || mo->type == MT_PENGUINATOR)
 		return; // no friction for missiles ever
 
 	if (player && player->homing) // no friction for homing
@@ -2234,7 +2236,7 @@ static void P_SceneryXYMovement(mobj_t *mo)
 // 1 - forces false check for water (rings)
 // 2 - forces false check for water + different quicksand behaviour (scenery)
 //
-static void P_AdjustMobjFloorZ_FFloors(mobj_t *mo, sector_t *sector, UINT8 motype)
+void P_AdjustMobjFloorZ_FFloors(mobj_t *mo, sector_t *sector, UINT8 motype)
 {
 	ffloor_t *rover;
 	fixed_t delta1, delta2, thingtop;
@@ -2438,6 +2440,7 @@ boolean P_CheckSolidLava(mobj_t *mo, ffloor_t *rover)
 static boolean P_ZMovement(mobj_t *mo)
 {
 	fixed_t dist, delta;
+	boolean onground;
 
 	I_Assert(mo != NULL);
 	I_Assert(!P_MobjWasRemoved(mo));
@@ -2455,13 +2458,14 @@ static boolean P_ZMovement(mobj_t *mo)
 		mo->eflags &= ~MFE_APPLYPMOMZ;
 	}
 	mo->z += mo->momz;
+	onground = P_IsObjectOnGround(mo);
 
 #ifdef ESLOPE
 	if (mo->standingslope)
 	{
 		if (mo->flags & MF_NOCLIPHEIGHT)
 			mo->standingslope = NULL;
-		else if (!P_IsObjectOnGround(mo))
+		else if (!onground)
 			P_SlopeLaunch(mo);
 	}
 #endif
@@ -2547,11 +2551,16 @@ static boolean P_ZMovement(mobj_t *mo)
 
 		case MT_RING: // Ignore still rings
 		case MT_COIN:
-		case MT_BLUEBALL:
+		case MT_BLUESPHERE:
+		case MT_BOMBSPHERE:
+		case MT_NIGHTSCHIP:
+		case MT_NIGHTSSTAR:
 		case MT_REDTEAMRING:
 		case MT_BLUETEAMRING:
 		case MT_FLINGRING:
 		case MT_FLINGCOIN:
+		case MT_FLINGBLUESPHERE:
+		case MT_FLINGNIGHTSCHIP:
 		case MT_FLINGEMERALD:
 			// Remove flinged stuff from death pits.
 			if (P_CheckDeathPitCollide(mo))
@@ -2584,10 +2593,6 @@ static boolean P_ZMovement(mobj_t *mo)
 			if (!(mo->momx || mo->momy || mo->momz))
 				return true;
 			break;
-		case MT_NIGHTSWING:
-			if (!(mo->momx || mo->momy || mo->momz))
-				return true;
-			break;
 		case MT_FLAMEJET:
 		case MT_VERTICALFLAMEJET:
 			if (!(mo->flags & MF_BOUNCE))
@@ -2606,15 +2611,9 @@ static boolean P_ZMovement(mobj_t *mo)
 			break;
 	}
 
-	if (P_CheckDeathPitCollide(mo))
+	if (!mo->player && P_CheckDeathPitCollide(mo))
 	{
-		if (mo->flags & MF_PUSHABLE)
-		{
-			// Remove other pushable items from death pits.
-			P_RemoveMobj(mo);
-			return false;
-		}
-		else if (mo->flags & MF_ENEMY || mo->flags & MF_BOSS)
+		if (mo->flags & MF_ENEMY || mo->flags & MF_BOSS)
 		{
 			// Kill enemies and bosses that fall into death pits.
 			if (mo->health)
@@ -2623,6 +2622,11 @@ static boolean P_ZMovement(mobj_t *mo)
 				return false;
 			}
 		}
+		else
+		{
+			P_RemoveMobj(mo);
+			return false;
+		}
 	}
 
 	if (P_MobjFlip(mo)*mo->momz < 0
@@ -2735,9 +2739,7 @@ static boolean P_ZMovement(mobj_t *mo)
 
 		if (P_MobjFlip(mo)*mom.z < 0) // falling
 		{
-			if (!tmfloorthing || tmfloorthing->flags & (MF_PUSHABLE|MF_MONITOR)
-			|| tmfloorthing->flags2 & MF2_STANDONME || tmfloorthing->type == MT_PLAYER)
-				mo->eflags |= MFE_JUSTHITFLOOR;
+			mo->eflags |= MFE_JUSTHITFLOOR;
 
 			if (mo->flags2 & MF2_SKULLFLY) // the skull slammed into something
 				mom.z = -mom.z;
@@ -2745,6 +2747,8 @@ static boolean P_ZMovement(mobj_t *mo)
 			// Flingrings bounce
 			if (mo->type == MT_FLINGRING
 				|| mo->type == MT_FLINGCOIN
+				|| mo->type == MT_FLINGBLUESPHERE
+				|| mo->type == MT_FLINGNIGHTSCHIP
 				|| P_WeaponOrPanel(mo->type)
 				|| mo->type == MT_FLINGEMERALD
 				|| mo->type == MT_BIGTUMBLEWEED
@@ -2798,7 +2802,7 @@ static boolean P_ZMovement(mobj_t *mo)
 				else if (mo->type == MT_FALLINGROCK)
 				{
 					if (P_MobjFlip(mo)*mom.z > FixedMul(2*FRACUNIT, mo->scale))
-						S_StartSound(mo, mo->info->activesound + P_RandomKey(mo->info->mass));
+						S_StartSound(mo, mo->info->activesound + P_RandomKey(mo->info->reactiontime));
 
 					mom.z /= 2; // Rocks not so bouncy
 
@@ -2817,14 +2821,11 @@ static boolean P_ZMovement(mobj_t *mo)
 						mom.z = 0;
 				}
 			}
-			else if (tmfloorthing && (tmfloorthing->flags & (MF_PUSHABLE|MF_MONITOR)
-			|| tmfloorthing->flags2 & MF2_STANDONME || tmfloorthing->type == MT_PLAYER))
-				mom.z = tmfloorthing->momz;
-			else if (!tmfloorthing)
-				mom.z = 0;
+			else
+				mom.z = (tmfloorthing ? tmfloorthing->momz : 0);
+
 		}
-		else if (tmfloorthing && (tmfloorthing->flags & (MF_PUSHABLE|MF_MONITOR)
-		|| tmfloorthing->flags2 & MF2_STANDONME || tmfloorthing->type == MT_PLAYER))
+		else if (tmfloorthing)
 			mom.z = tmfloorthing->momz;
 
 #ifdef ESLOPE
@@ -2910,6 +2911,8 @@ static boolean P_ZMovement(mobj_t *mo)
 
 static void P_PlayerZMovement(mobj_t *mo)
 {
+	boolean onground;
+
 	I_Assert(mo != NULL);
 	I_Assert(!P_MobjWasRemoved(mo));
 
@@ -2943,6 +2946,7 @@ static void P_PlayerZMovement(mobj_t *mo)
 	}
 
 	mo->z += mo->momz / NEWTICRATERATIO;
+	onground = P_IsObjectOnGround(mo);
 
 	// Have player fall through floor?
 	if (mo->player->playerstate == PST_DEAD
@@ -2954,13 +2958,13 @@ static void P_PlayerZMovement(mobj_t *mo)
 	{
 		if (mo->flags & MF_NOCLIPHEIGHT)
 			mo->standingslope = NULL;
-		else if (!P_IsObjectOnGround(mo))
+		else if (!onground)
 			P_SlopeLaunch(mo);
 	}
 #endif
 
 	// clip movement
-	if (P_IsObjectOnGround(mo) && !(mo->flags & MF_NOCLIPHEIGHT))
+	if (onground && !(mo->flags & MF_NOCLIPHEIGHT))
 	{
 		if (mo->eflags & MFE_VERTICALFLIP)
 			mo->z = mo->ceilingz - mo->height;
@@ -3002,100 +3006,89 @@ static void P_PlayerZMovement(mobj_t *mo)
 			if (P_MobjFlip(mo)*mo->momz < -FixedMul(8*FRACUNIT, mo->scale))
 				mo->player->deltaviewheight = (P_MobjFlip(mo)*mo->momz)>>3; // make sure momz is negative
 
-			if (!tmfloorthing || tmfloorthing->flags & (MF_PUSHABLE|MF_MONITOR)
-				|| tmfloorthing->flags2 & MF2_STANDONME || tmfloorthing->type == MT_PLAYER) // Spin Attack
+			mo->eflags |= MFE_JUSTHITFLOOR; // Spin Attack
+
 			{
-				mo->eflags |= MFE_JUSTHITFLOOR; // Spin Attack
+#ifdef POLYOBJECTS
+				// Check if we're on a polyobject
+				// that triggers a linedef executor.
+				msecnode_t *node;
+				boolean stopmovecut = false;
 
-				if (mo->eflags & MFE_JUSTHITFLOOR)
+				for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next)
 				{
-#ifdef POLYOBJECTS
-					// Check if we're on a polyobject
-					// that triggers a linedef executor.
-					msecnode_t *node;
-					boolean stopmovecut = false;
+					sector_t *sec = node->m_sector;
+					subsector_t *newsubsec;
+					size_t i;
 
-					for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next)
+					for (i = 0; i < numsubsectors; i++)
 					{
-						sector_t *sec = node->m_sector;
-						subsector_t *newsubsec;
-						size_t i;
+						newsubsec = &subsectors[i];
 
-						for (i = 0; i < numsubsectors; i++)
-						{
-							newsubsec = &subsectors[i];
+						if (newsubsec->sector != sec)
+							continue;
 
-							if (newsubsec->sector != sec)
-								continue;
+						if (newsubsec->polyList)
+						{
+							polyobj_t *po = newsubsec->polyList;
+							sector_t *polysec;
 
-							if (newsubsec->polyList)
+							while(po)
 							{
-								polyobj_t *po = newsubsec->polyList;
-								sector_t *polysec;
-
-								while(po)
+								if (!P_MobjInsidePolyobj(po, mo) || !(po->flags & POF_SOLID))
 								{
-									if (!P_MobjInsidePolyobj(po, mo) || !(po->flags & POF_SOLID))
-									{
-										po = (polyobj_t *)(po->link.next);
-										continue;
-									}
-
-									// We're inside it! Yess...
-									polysec = po->lines[0]->backsector;
-
-									// Moving polyobjects should act like conveyors if the player lands on one. (I.E. none of the momentum cut thing below) -Red
-									if ((mo->z == polysec->ceilingheight || mo->z+mo->height == polysec->floorheight) && po->thinker)
-										stopmovecut = true;
-
-									if (!(po->flags & POF_LDEXEC))
-									{
-										po = (polyobj_t *)(po->link.next);
-										continue;
-									}
-
-									if (mo->z == polysec->ceilingheight)
-									{
-										// We're landing on a PO, so check for
-										// a linedef executor.
-										// Trigger tags are 32000 + the PO's ID number.
-										P_LinedefExecute((INT16)(32000 + po->id), mo, NULL);
-									}
+									po = (polyobj_t *)(po->link.next);
+									continue;
+								}
 
+								// We're inside it! Yess...
+								polysec = po->lines[0]->backsector;
+
+								// Moving polyobjects should act like conveyors if the player lands on one. (I.E. none of the momentum cut thing below) -Red
+								if ((mo->z == polysec->ceilingheight || mo->z+mo->height == polysec->floorheight) && po->thinker)
+									stopmovecut = true;
+
+								if (!(po->flags & POF_LDEXEC))
+								{
 									po = (polyobj_t *)(po->link.next);
+									continue;
+								}
+
+								if (mo->z == polysec->ceilingheight)
+								{
+									// We're landing on a PO, so check for
+									// a linedef executor.
+									// Trigger tags are 32000 + the PO's ID number.
+									P_LinedefExecute((INT16)(32000 + po->id), mo, NULL);
 								}
+
+								po = (polyobj_t *)(po->link.next);
 							}
 						}
 					}
+				}
 
-					if (!stopmovecut)
+			if (!stopmovecut)
 #endif
 
-					// Cut momentum in half when you hit the ground and
-					// aren't pressing any controls.
-					if (!(mo->player->cmd.forwardmove || mo->player->cmd.sidemove) && !mo->player->cmomx && !mo->player->cmomy && !(mo->player->pflags & PF_SPINNING))
-					{
-						mo->momx >>= 1;
-						mo->momy >>= 1;
-					}
+				// Cut momentum in half when you hit the ground and
+				// aren't pressing any controls.
+				if (!(mo->player->cmd.forwardmove || mo->player->cmd.sidemove) && !mo->player->cmomx && !mo->player->cmomy && !(mo->player->pflags & PF_SPINNING))
+				{
+					mo->momx >>= 1;
+					mo->momy >>= 1;
 				}
-
-				clipmomz = P_PlayerHitFloor(mo->player);
 			}
+
+			clipmomz = P_PlayerHitFloor(mo->player);
+
 			if (!(mo->player->pflags & PF_SPINNING) && mo->player->powers[pw_carry] != CR_NIGHTSMODE)
 				mo->player->pflags &= ~PF_STARTDASH;
 
 			if (clipmomz)
-			{
-				if (tmfloorthing && (tmfloorthing->flags & (MF_PUSHABLE|MF_MONITOR)
-				|| tmfloorthing->flags2 & MF2_STANDONME || tmfloorthing->type == MT_PLAYER))
-					mo->momz = tmfloorthing->momz;
-				else if (!tmfloorthing)
-					mo->momz = 0;
-			}
+				mo->momz = (tmfloorthing ? tmfloorthing->momz : 0);
 		}
-		else if (tmfloorthing && (tmfloorthing->flags & (MF_PUSHABLE|MF_MONITOR)
-		|| tmfloorthing->flags2 & MF2_STANDONME || tmfloorthing->type == MT_PLAYER))
+		else if (tmfloorthing)
 			mo->momz = tmfloorthing->momz;
 	}
 	else if (!(mo->flags & MF_NOGRAVITY)) // Gravity here!
@@ -3273,19 +3266,14 @@ static boolean P_SceneryZMovement(mobj_t *mo)
 				P_RemoveMobj(mo);
 				return false;
 			}
-
 		default:
 			break;
 	}
 
-	// Fix for any silly pushables like the egg statues that are also scenery for some reason -- Monster Iestyn
 	if (P_CheckDeathPitCollide(mo))
 	{
-		if (mo->flags & MF_PUSHABLE)
-		{
-			P_RemoveMobj(mo);
-			return false;
-		}
+		P_RemoveMobj(mo);
+		return false;
 	}
 
 	// clip movement
@@ -3300,12 +3288,9 @@ static boolean P_SceneryZMovement(mobj_t *mo)
 
 		if (P_MobjFlip(mo)*mo->momz < 0) // falling
 		{
-			if (!tmfloorthing || tmfloorthing->flags & (MF_PUSHABLE|MF_MONITOR)
-				|| tmfloorthing->flags2 & MF2_STANDONME || tmfloorthing->type == MT_PLAYER)
-					mo->eflags |= MFE_JUSTHITFLOOR; // Spin Attack
+			mo->eflags |= MFE_JUSTHITFLOOR; // Spin Attack
 
-			if (tmfloorthing && (tmfloorthing->flags & (MF_PUSHABLE|MF_MONITOR)
-			|| tmfloorthing->flags2 & MF2_STANDONME || tmfloorthing->type == MT_PLAYER))
+			if (tmfloorthing)
 				mo->momz = tmfloorthing->momz;
 			else if (!tmfloorthing)
 				mo->momz = 0;
@@ -4108,7 +4093,8 @@ void P_RecalcPrecipInSector(sector_t *sector)
 //
 void P_NullPrecipThinker(precipmobj_t *mobj)
 {
-	(void)mobj;
+	//(void)mobj;
+	mobj->precipflags &= ~PCF_THUNK;
 }
 
 void P_SnowThinker(precipmobj_t *mobj)
@@ -4128,25 +4114,26 @@ void P_RainThinker(precipmobj_t *mobj)
 	{
 		// cycle through states,
 		// calling action functions at transitions
-		if (mobj->tics > 0 && --mobj->tics == 0)
-		{
-			// you can cycle through multiple states in a tic
-			if (!P_SetPrecipMobjState(mobj, mobj->state->nextstate))
-				return; // freed itself
-		}
+		if (mobj->tics <= 0)
+			return;
+
+		if (--mobj->tics)
+			return;
+
+		if (!P_SetPrecipMobjState(mobj, mobj->state->nextstate))
+			return;
+
+		if (mobj->state != &states[S_RAINRETURN])
+			return;
+
+		mobj->z = mobj->ceilingz;
+		P_SetPrecipMobjState(mobj, S_RAIN1);
 
-		if (mobj->state == &states[S_RAINRETURN])
-		{
-			mobj->z = mobj->ceilingz;
-			P_SetPrecipMobjState(mobj, S_RAIN1);
-		}
 		return;
 	}
 
 	// adjust height
-	mobj->z += mobj->momz;
-
-	if (mobj->z <= mobj->floorz)
+	if ((mobj->z += mobj->momz) <= mobj->floorz)
 	{
 		// no splashes on sky or bottomless pits
 		if (mobj->precipflags & PCF_PIT)
@@ -6151,9 +6138,11 @@ void P_SetScale(mobj_t *mobj, fixed_t newscale)
 void P_Attract(mobj_t *source, mobj_t *dest, boolean nightsgrab) // Home in on your target
 {
 	fixed_t dist, ndist, speedmul;
+	angle_t vangle;
 	fixed_t tx = dest->x;
 	fixed_t ty = dest->y;
 	fixed_t tz = dest->z + (dest->height/2); // Aim for center
+	fixed_t xydist = P_AproxDistance(tx - source->x, ty - source->y);
 
 	if (!dest || dest->health <= 0 || !dest->player || !source->tracer)
 		return;
@@ -6162,19 +6151,40 @@ void P_Attract(mobj_t *source, mobj_t *dest, boolean nightsgrab) // Home in on y
 	source->angle = R_PointToAngle2(source->x, source->y, tx, ty);
 
 	// change slope
-	dist = P_AproxDistance(P_AproxDistance(tx - source->x, ty - source->y), tz - source->z);
+	dist = P_AproxDistance(xydist, tz - source->z);
 
 	if (dist < 1)
 		dist = 1;
 
-	if (nightsgrab)
-		speedmul = P_AproxDistance(dest->momx, dest->momy) + FixedMul(8*FRACUNIT, source->scale);
+	if (nightsgrab && source->movefactor)
+	{
+		source->movefactor += FRACUNIT/2;
+
+		if (dist < source->movefactor)
+		{
+			source->momx = source->momy = source->momz = 0;
+			P_TeleportMove(source, tx, ty, tz);
+		}
+		else
+		{
+			vangle = R_PointToAngle2(source->z, 0, tz, xydist);
+
+			source->momx = FixedMul(FINESINE(vangle >> ANGLETOFINESHIFT), FixedMul(FINECOSINE(source->angle >> ANGLETOFINESHIFT), source->movefactor));
+			source->momy = FixedMul(FINESINE(vangle >> ANGLETOFINESHIFT), FixedMul(FINESINE(source->angle >> ANGLETOFINESHIFT), source->movefactor));
+			source->momz = FixedMul(FINECOSINE(vangle >> ANGLETOFINESHIFT), source->movefactor);
+		}
+	}
 	else
-		speedmul = P_AproxDistance(dest->momx, dest->momy) + FixedMul(source->info->speed, source->scale);
+	{
+		if (nightsgrab)
+			speedmul = P_AproxDistance(dest->momx, dest->momy) + FixedMul(8*FRACUNIT, source->scale);
+		else
+			speedmul = P_AproxDistance(dest->momx, dest->momy) + FixedMul(source->info->speed, source->scale);
 
-	source->momx = FixedMul(FixedDiv(tx - source->x, dist), speedmul);
-	source->momy = FixedMul(FixedDiv(ty - source->y, dist), speedmul);
-	source->momz = FixedMul(FixedDiv(tz - source->z, dist), speedmul);
+		source->momx = FixedMul(FixedDiv(tx - source->x, dist), speedmul);
+		source->momy = FixedMul(FixedDiv(ty - source->y, dist), speedmul);
+		source->momz = FixedMul(FixedDiv(tz - source->z, dist), speedmul);
+	}
 
 	// Instead of just unsetting NOCLIP like an idiot, let's check the distance to our target.
 	ndist = P_AproxDistance(P_AproxDistance(tx - (source->x+source->momx),
@@ -6199,6 +6209,7 @@ static void P_NightsItemChase(mobj_t *thing)
 	{
 		P_SetTarget(&thing->tracer, NULL);
 		thing->flags2 &= ~MF2_NIGHTSPULL;
+		thing->movefactor = 0;
 		return;
 	}
 
@@ -6377,7 +6388,7 @@ void P_MaceRotate(mobj_t *center, INT32 baserot, INT32 baseprevrot)
 		mobj->y += pos_sideways[1];
 
 		// Cut the height to align the link with the axis.
-		if (mobj->type == MT_SMALLMACECHAIN || mobj->type == MT_BIGMACECHAIN)
+		if (mobj->type == MT_SMALLMACECHAIN || mobj->type == MT_BIGMACECHAIN || mobj->type == MT_SMALLGRABCHAIN || mobj->type == MT_BIGGRABCHAIN)
 			zstore -= P_MobjFlip(mobj)*mobj->height/4;
 		else
 			zstore -= P_MobjFlip(mobj)*mobj->height/2;
@@ -6744,6 +6755,10 @@ void P_MobjThinker(mobj_t *mobj)
 		P_SetTarget(&mobj->target, NULL);
 	if (mobj->tracer && P_MobjWasRemoved(mobj->tracer))
 		P_SetTarget(&mobj->tracer, NULL);
+	if (mobj->hnext && P_MobjWasRemoved(mobj->hnext))
+		P_SetTarget(&mobj->hnext, NULL);
+	if (mobj->hprev && P_MobjWasRemoved(mobj->hprev))
+		P_SetTarget(&mobj->hprev, NULL);
 
 	mobj->eflags &= ~(MFE_PUSHED|MFE_SPRUNG);
 
@@ -7161,6 +7176,34 @@ void P_MobjThinker(mobj_t *mobj)
 					S_StartSound(flame, sfx_fire);
 				}
 				break;
+			case MT_FLICKY_01_CENTER:
+			case MT_FLICKY_02_CENTER:
+			case MT_FLICKY_03_CENTER:
+			case MT_FLICKY_04_CENTER:
+			case MT_FLICKY_05_CENTER:
+			case MT_FLICKY_06_CENTER:
+			case MT_FLICKY_07_CENTER:
+			case MT_FLICKY_08_CENTER:
+			case MT_FLICKY_09_CENTER:
+			case MT_FLICKY_10_CENTER:
+			case MT_FLICKY_11_CENTER:
+			case MT_FLICKY_12_CENTER:
+			case MT_FLICKY_13_CENTER:
+			case MT_FLICKY_14_CENTER:
+			case MT_FLICKY_15_CENTER:
+			case MT_FLICKY_16_CENTER:
+			case MT_SECRETFLICKY_01_CENTER:
+			case MT_SECRETFLICKY_02_CENTER:
+				if (mobj->tracer && (mobj->flags & MF_NOCLIPTHING)
+					&& (mobj->flags & MF_GRENADEBOUNCE))
+					// for now: only do this bounce routine if flicky is in-place. \todo allow in all movements
+				{
+					if (!(mobj->tracer->flags2 & MF2_OBJECTFLIP) && mobj->tracer->z <= mobj->tracer->floorz)
+						mobj->tracer->momz = 7*FRACUNIT;
+					else if ((mobj->tracer->flags2 & MF2_OBJECTFLIP) && mobj->tracer->z >= mobj->tracer->ceilingz - mobj->tracer->height)
+						mobj->tracer->momz = -7*FRACUNIT;
+				}
+				break;
 			case MT_SEED:
 				if (P_MobjFlip(mobj)*mobj->momz < mobj->info->speed)
 					mobj->momz = P_MobjFlip(mobj)*mobj->info->speed;
@@ -7188,6 +7231,59 @@ void P_MobjThinker(mobj_t *mobj)
 					return;
 				}
 				break;
+			case MT_PARTICLEGEN:
+				if (!mobj->lastlook)
+					return;
+
+				if (!mobj->threshold)
+					return;
+
+				if (--mobj->fuse <= 0)
+				{
+					INT32 i = 0;
+					mobj_t *spawn;
+					fixed_t bottomheight, topheight;
+					INT32 type = mobj->threshold, line = mobj->cvmem;
+
+					mobj->fuse = (tic_t)mobj->reactiontime;
+
+					bottomheight = lines[line].frontsector->floorheight;
+					topheight = lines[line].frontsector->ceilingheight - mobjinfo[(mobjtype_t)type].height;
+
+					if (mobj->waterbottom != bottomheight || mobj->watertop != topheight)
+					{
+						if (mobj->movefactor && (topheight > bottomheight))
+							mobj->health = (tic_t)(FixedDiv((topheight - bottomheight), abs(mobj->movefactor)) >> FRACBITS);
+						else
+							mobj->health = 0;
+
+						mobj->z = ((mobj->flags2 & MF2_OBJECTFLIP) ? topheight : bottomheight);
+					}
+
+					if (!mobj->health)
+						return;
+
+					for (i = 0; i < mobj->lastlook; i++)
+					{
+						spawn = P_SpawnMobj(
+							mobj->x + FixedMul(FixedMul(mobj->friction, mobj->scale), FINECOSINE(mobj->angle>>ANGLETOFINESHIFT)),
+							mobj->y + FixedMul(FixedMul(mobj->friction, mobj->scale), FINESINE(mobj->angle>>ANGLETOFINESHIFT)),
+							mobj->z,
+							(mobjtype_t)mobj->threshold);
+						P_SetScale(spawn, mobj->scale);
+						spawn->momz = FixedMul(mobj->movefactor, spawn->scale);
+						spawn->destscale = spawn->scale/100;
+						spawn->scalespeed = spawn->scale/mobj->health;
+						spawn->tics = (tic_t)mobj->health;
+						spawn->flags2 |= (mobj->flags2 & MF2_OBJECTFLIP);
+						spawn->angle += P_RandomKey(36)*ANG10; // irrelevant for default objects but might make sense for some custom ones
+
+						mobj->angle += mobj->movedir;
+					}
+
+					mobj->angle += (angle_t)mobj->movecount;
+				}
+				break;
 			default:
 				if (mobj->fuse)
 				{ // Scenery object fuse! Very basic!
@@ -7295,7 +7391,7 @@ void P_MobjThinker(mobj_t *mobj)
 	else if (mobj->health <= 0) // Dead things think differently than the living.
 		switch (mobj->type)
 		{
-		case MT_BLUEBALL:
+		case MT_BLUESPHERE:
 			if ((mobj->tics>>2)+1 > 0 && (mobj->tics>>2)+1 <= tr_trans60) // tr_trans50 through tr_trans90, shifting once every second frame
 				mobj->frame = (NUMTRANSMAPS-((mobj->tics>>2)+1))<<FF_TRANSSHIFT;
 			else // tr_trans60 otherwise
@@ -7308,6 +7404,9 @@ void P_MobjThinker(mobj_t *mobj)
 				return;
 			}
 			break;
+		case MT_EGGSHIELD:
+			mobj->flags2 ^= MF2_DONTDRAW;
+			break;
 		case MT_EGGTRAP: // Egg Capsule animal release
 			if (mobj->fuse > 0 && mobj->fuse < 2*TICRATE-(TICRATE/7)
 				&& (mobj->fuse & 3))
@@ -7386,335 +7485,496 @@ void P_MobjThinker(mobj_t *mobj)
 		default:
 			break;
 		}
-	else switch (mobj->type)
+	else
 	{
-		case MT_WALLSPIKEBASE:
-			if (!mobj->target) {
-				P_RemoveMobj(mobj);
-				return;
-			}
-			mobj->frame = (mobj->frame & ~FF_FRAMEMASK)|(mobj->target->frame & FF_FRAMEMASK);
+		if ((mobj->flags & MF_ENEMY) && (mobj->state->nextstate == mobj->info->spawnstate && mobj->tics == 1))
+			mobj->flags2 &= ~MF2_FRET;
+
+		switch (mobj->type)
+		{
+			case MT_WALLSPIKEBASE:
+				if (!mobj->target) {
+					P_RemoveMobj(mobj);
+					return;
+				}
+				mobj->frame = (mobj->frame & ~FF_FRAMEMASK)|(mobj->target->frame & FF_FRAMEMASK);
 #if 0
-			if (mobj->angle != mobj->target->angle + ANGLE_90) // reposition if not the correct angle
-			{
-				mobj_t *target = mobj->target; // shortcut
-				const fixed_t baseradius = target->radius - (target->scale/2); //FixedMul(FRACUNIT/2, target->scale);
-				P_UnsetThingPosition(mobj);
-				mobj->x = target->x - P_ReturnThrustX(target, target->angle, baseradius);
-				mobj->y = target->y - P_ReturnThrustY(target, target->angle, baseradius);
-				P_SetThingPosition(mobj);
-				mobj->angle = target->angle + ANGLE_90;
-			}
+				if (mobj->angle != mobj->target->angle + ANGLE_90) // reposition if not the correct angle
+				{
+					mobj_t *target = mobj->target; // shortcut
+					const fixed_t baseradius = target->radius - (target->scale/2); //FixedMul(FRACUNIT/2, target->scale);
+					P_UnsetThingPosition(mobj);
+					mobj->x = target->x - P_ReturnThrustX(target, target->angle, baseradius);
+					mobj->y = target->y - P_ReturnThrustY(target, target->angle, baseradius);
+					P_SetThingPosition(mobj);
+					mobj->angle = target->angle + ANGLE_90;
+				}
 #endif
-			break;
-		case MT_FALLINGROCK:
-			// Despawn rocks here in case zmovement code can't do so (blame slopes)
-			if (!mobj->momx && !mobj->momy && !mobj->momz
-			&& ((mobj->eflags & MFE_VERTICALFLIP) ?
-				  mobj->z + mobj->height >= mobj->ceilingz
-				: mobj->z <= mobj->floorz))
-			{
-				P_RemoveMobj(mobj);
-				return;
-			}
-			P_MobjCheckWater(mobj);
-			break;
-		case MT_EMERALDSPAWN:
-			if (mobj->threshold)
-			{
-				mobj->threshold--;
-
-				if (!mobj->threshold && !mobj->target && mobj->reactiontime)
+				break;
+			case MT_FALLINGROCK:
+				// Despawn rocks here in case zmovement code can't do so (blame slopes)
+				if (!mobj->momx && !mobj->momy && !mobj->momz
+				&& ((mobj->eflags & MFE_VERTICALFLIP) ?
+					  mobj->z + mobj->height >= mobj->ceilingz
+					: mobj->z <= mobj->floorz))
 				{
-					mobj_t *emerald = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobj->reactiontime);
-					emerald->threshold = 42;
-					P_SetTarget(&mobj->target, emerald);
-					P_SetTarget(&emerald->target, mobj);
+					P_RemoveMobj(mobj);
+					return;
 				}
-			}
-			break;
-		case MT_AQUABUZZ:
-			P_MobjCheckWater(mobj); // solely for MFE_UNDERWATER for A_FlickySpawn
-			/* FALLTHRU */
-		case MT_BIGAIRMINE:
-			{
-				if (mobj->tracer && mobj->tracer->player && mobj->tracer->health > 0
-					&& P_AproxDistance(P_AproxDistance(mobj->tracer->x - mobj->x, mobj->tracer->y - mobj->y), mobj->tracer->z - mobj->z) <= mobj->radius * 16)
+				P_MobjCheckWater(mobj);
+				break;
+			case MT_ARROW:
+				if (mobj->flags & MF_MISSILE)
 				{
-					// Home in on the target.
-					P_HomingAttack(mobj, mobj->tracer);
-
-					if (mobj->z < mobj->floorz)
-						mobj->z = mobj->floorz;
+					// Calculate the angle of movement.
+					/*
+						   momz
+						 / |
+					   /   |
+					 /     |
+					0------dist(momx,momy)
+					*/
+
+					fixed_t dist = P_AproxDistance(mobj->momx, mobj->momy);
+					angle_t angle = R_PointToAngle2(0, 0, dist, mobj->momz);
+
+					if (angle > ANG20 && angle <= ANGLE_180)
+						mobj->frame = 2;
+					else if (angle < ANG340 && angle > ANGLE_180)
+						mobj->frame = 0;
+					else
+						mobj->frame = 1;
 
-					if (leveltime % mobj->info->painchance == 0)
+					if (!(mobj->extravalue1) && (mobj->momz < 0))
+					{
+						mobj->extravalue1 = 1;
 						S_StartSound(mobj, mobj->info->activesound);
+					}
+					if (leveltime & 1)
+					{
+						mobj_t *dust = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_PARTICLE);
+						dust->tics = 18;
+						dust->scalespeed = 4096;
+						dust->destscale = FRACUNIT/32;
+					}
 				}
 				else
+					mobj->flags2 ^= MF2_DONTDRAW;
+				break;
+			case MT_EMERALDSPAWN:
+				if (mobj->threshold)
 				{
-					// Try to find a player
-					P_LookForPlayers(mobj, true, true, mobj->radius * 16);
-					mobj->momx >>= 1;
-					mobj->momy >>= 1;
-					mobj->momz >>= 1;
+					mobj->threshold--;
+
+					if (!mobj->threshold && !mobj->target && mobj->reactiontime)
+					{
+						mobj_t *emerald = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobj->reactiontime);
+						emerald->threshold = 42;
+						P_SetTarget(&mobj->target, emerald);
+						P_SetTarget(&emerald->target, mobj);
+					}
 				}
-			}
-			break;
-		case MT_BIGMINE:
-			{
-				if (mobj->tracer && mobj->tracer->player && mobj->tracer->health > 0
-					&& P_AproxDistance(P_AproxDistance(mobj->tracer->x - mobj->x, mobj->tracer->y - mobj->y), mobj->tracer->z - mobj->z) <= mobj->radius * 16)
+				break;
+			case MT_BUBBLEBUZZ:
+				mobj->eflags |= MFE_UNDERWATER; //P_MobjCheckWater(mobj); // solely for MFE_UNDERWATER for A_FlickySpawn
 				{
-					P_MobjCheckWater(mobj);
-
-					// Home in on the target.
-					P_HomingAttack(mobj, mobj->tracer);
-
-					// Don't let it go out of water
-					if (mobj->z + mobj->height > mobj->watertop)
-						mobj->z = mobj->watertop - mobj->height;
+					if (mobj->tracer && mobj->tracer->player && mobj->tracer->health > 0
+						&& P_AproxDistance(P_AproxDistance(mobj->tracer->x - mobj->x, mobj->tracer->y - mobj->y), mobj->tracer->z - mobj->z) <= mobj->radius * 16)
+					{
+						// Home in on the target.
+						P_HomingAttack(mobj, mobj->tracer);
 
-					if (mobj->z < mobj->floorz)
-						mobj->z = mobj->floorz;
+						if (mobj->z < mobj->floorz)
+							mobj->z = mobj->floorz;
 
-					if (leveltime % mobj->info->painchance == 0)
-						S_StartSound(mobj, mobj->info->activesound);
+						if (leveltime % mobj->info->painchance == 0)
+							S_StartSound(mobj, mobj->info->activesound);
+					}
+					else
+					{
+						// Try to find a player
+						P_LookForPlayers(mobj, true, true, mobj->radius * 16);
+						mobj->momx >>= 1;
+						mobj->momy >>= 1;
+						mobj->momz >>= 1;
+					}
 				}
-				else
+				break;
+			case MT_BUMBLEBORE:
 				{
-					// Try to find a player
-					P_LookForPlayers(mobj, true, true, mobj->radius * 16);
-					mobj->momx >>= 1;
-					mobj->momy >>= 1;
-					mobj->momz >>= 1;
-				}
-			}
-			break;
-		case MT_EGGCAPSULE:
-			if (!mobj->reactiontime)
-			{
-				// Target nearest player on your mare.
-				// (You can make it float up/down by adding MF_FLOAT,
-				//  but beware level design pitfalls.)
-				fixed_t shortest = 1024*FRACUNIT;
-				INT32 i;
-				P_SetTarget(&mobj->target, NULL);
-				for (i = 0; i < MAXPLAYERS; i++)
-					if (playeringame[i] && players[i].mo
-					&& players[i].mare == mobj->threshold && players[i].rings > 0)
+					statenum_t st = mobj->state-states;
+					if (st == S_BUMBLEBORE_FLY1 || st == S_BUMBLEBORE_FLY2)
 					{
-						fixed_t dist = P_AproxDistance(players[i].mo->x - mobj->x, players[i].mo->y - mobj->y);
-						if (dist < shortest)
+						if (!mobj->target)
+							P_SetMobjState(mobj, mobj->info->spawnstate);
+						else if (P_MobjFlip(mobj)*((mobj->z + (mobj->height>>1)) - (mobj->target->z + (mobj->target->height>>1))) > 0
+							&& R_PointToDist2(mobj->x, mobj->y, mobj->target->x, mobj->target->y) <= 32*FRACUNIT)
 						{
-							P_SetTarget(&mobj->target, players[i].mo);
-							shortest = dist;
+							mobj->momx >>= 1;
+							mobj->momy >>= 1;
+							if (++mobj->movefactor == 4)
+							{
+								S_StartSound(mobj, mobj->info->seesound);
+								mobj->momx = mobj->momy = mobj->momz = 0;
+								mobj->flags = (mobj->flags|MF_PAIN) & ~MF_NOGRAVITY;
+								P_SetMobjState(mobj, mobj->info->meleestate);
+							}
 						}
+						else
+							mobj->movefactor = 0;
 					}
-			}
-			break;
-		case MT_EGGMOBILE2_POGO:
-			if (!mobj->target
-			|| !mobj->target->health
-			|| mobj->target->state == &states[mobj->target->info->spawnstate]
-			|| mobj->target->state == &states[mobj->target->info->raisestate])
-			{
-				P_RemoveMobj(mobj);
-				return;
-			}
-			P_TeleportMove(mobj, mobj->target->x, mobj->target->y, mobj->target->z - mobj->height);
-			break;
-		case MT_HAMMER:
-			if (mobj->z <= mobj->floorz)
-			{
-				P_RemoveMobj(mobj);
-				return;
-			}
-			break;
-		case MT_KOOPA:
-			P_KoopaThinker(mobj);
-			break;
-		case MT_REDRING:
-			if (((mobj->z < mobj->floorz) || (mobj->z + mobj->height > mobj->ceilingz))
-				&& mobj->flags & MF_MISSILE)
-			{
-				P_ExplodeMissile(mobj);
-				return;
-			}
-			break;
-		case MT_BOSSFLYPOINT:
-			return;
-		case MT_NIGHTSCORE:
-			mobj->color = (UINT8)(leveltime % SKINCOLOR_WHITE);
-			break;
-		case MT_JETFUME1:
-			{
-				fixed_t jetx, jety;
-
-				if (!mobj->target // if you have no target
-				|| (!(mobj->target->flags & MF_BOSS) && mobj->target->health <= 0)) // or your target isn't a boss and it's popped now
-				{ // then remove yourself as well!
-					P_RemoveMobj(mobj);
-					return;
+					else if (st == S_BUMBLEBORE_RAISE || st == S_BUMBLEBORE_FALL2) // no _FALL1 because it's an 0-tic
+					{
+						if (P_IsObjectOnGround(mobj))
+						{
+							S_StopSound(mobj);
+							S_StartSound(mobj, mobj->info->attacksound);
+							mobj->flags = (mobj->flags|MF_NOGRAVITY) & ~MF_PAIN;
+							mobj->momx = mobj->momy = mobj->momz = 0;
+							P_SetMobjState(mobj, mobj->info->painstate);
+						}
+						else
+						{
+							mobj->angle += ANGLE_22h;
+							mobj->frame = mobj->state->frame + ((mobj->tics & 2)>>1);
+						}
+					}
+					else if (st == S_BUMBLEBORE_STUCK2 && mobj->tics < TICRATE)
+						mobj->frame = mobj->state->frame + ((mobj->tics & 2)>>1);
 				}
-
-				jetx = mobj->target->x + P_ReturnThrustX(mobj->target, mobj->target->angle, FixedMul(-64*FRACUNIT, mobj->target->scale));
-				jety = mobj->target->y + P_ReturnThrustY(mobj->target, mobj->target->angle, FixedMul(-64*FRACUNIT, mobj->target->scale));
-
-				if (mobj->fuse == 56) // First one
+				break;
+			case MT_BIGMINE:
+				mobj->extravalue1 += 3;
+				mobj->extravalue1 %= 360;
+				P_UnsetThingPosition(mobj);
+				mobj->z += FINESINE(mobj->extravalue1*(FINEMASK+1)/360);
+				P_SetThingPosition(mobj);
+				break;
+			case MT_FLAME:
+				if (mobj->flags2 & MF2_BOSSNOTRAP)
 				{
-					P_UnsetThingPosition(mobj);
-					mobj->x = jetx;
-					mobj->y = jety;
-					if (mobj->target->eflags & MFE_VERTICALFLIP)
-						mobj->z = mobj->target->z + mobj->target->height - mobj->height - FixedMul(38*FRACUNIT, mobj->target->scale);
-					else
-						mobj->z = mobj->target->z + FixedMul(38*FRACUNIT, mobj->target->scale);
-					mobj->floorz = mobj->z;
-					mobj->ceilingz = mobj->z+mobj->height;
-					P_SetThingPosition(mobj);
+					if (!mobj->target || P_MobjWasRemoved(mobj->target))
+					{
+						if (mobj->tracer && !P_MobjWasRemoved(mobj->tracer))
+							P_RemoveMobj(mobj->tracer);
+						P_RemoveMobj(mobj);
+						return;
+					}
+					mobj->z = mobj->target->z + mobj->target->momz;
+					if (!(mobj->eflags & MFE_VERTICALFLIP))
+						mobj->z += mobj->target->height;
 				}
-				else if (mobj->fuse == 57)
+				if (mobj->tracer && !P_MobjWasRemoved(mobj->tracer))
 				{
-					P_UnsetThingPosition(mobj);
-					mobj->x = jetx + P_ReturnThrustX(mobj->target, mobj->target->angle-ANGLE_90, FixedMul(24*FRACUNIT, mobj->target->scale));
-					mobj->y = jety + P_ReturnThrustY(mobj->target, mobj->target->angle-ANGLE_90, FixedMul(24*FRACUNIT, mobj->target->scale));
-					if (mobj->target->eflags & MFE_VERTICALFLIP)
-						mobj->z = mobj->target->z + mobj->target->height - mobj->height - FixedMul(12*FRACUNIT, mobj->target->scale);
-					else
-						mobj->z = mobj->target->z + FixedMul(12*FRACUNIT, mobj->target->scale);
-					mobj->floorz = mobj->z;
-					mobj->ceilingz = mobj->z+mobj->height;
-					P_SetThingPosition(mobj);
+					mobj->tracer->z = mobj->z + P_MobjFlip(mobj)*20*mobj->scale;
+					if (mobj->eflags & MFE_VERTICALFLIP)
+						mobj->tracer->z += mobj->height;
 				}
-				else if (mobj->fuse == 58)
+				break;
+			case MT_WAVINGFLAG:
 				{
-					P_UnsetThingPosition(mobj);
-					mobj->x = jetx + P_ReturnThrustX(mobj->target, mobj->target->angle+ANGLE_90, FixedMul(24*FRACUNIT, mobj->target->scale));
-					mobj->y = jety + P_ReturnThrustY(mobj->target, mobj->target->angle+ANGLE_90, FixedMul(24*FRACUNIT, mobj->target->scale));
-					if (mobj->target->eflags & MFE_VERTICALFLIP)
-						mobj->z = mobj->target->z + mobj->target->height - mobj->height - FixedMul(12*FRACUNIT, mobj->target->scale);
-					else
-						mobj->z = mobj->target->z + FixedMul(12*FRACUNIT, mobj->target->scale);
-					mobj->floorz = mobj->z;
-					mobj->ceilingz = mobj->z+mobj->height;
-					P_SetThingPosition(mobj);
+					fixed_t base = (leveltime<<(FRACBITS+1));
+					mobj_t *seg = mobj->tracer, *prev = mobj;
+					mobj->movedir = mobj->angle
+						+ ((((FINESINE((FixedAngle(base<<1)>>ANGLETOFINESHIFT) & FINEMASK)
+							+ FINESINE((FixedAngle(base<<4)>>ANGLETOFINESHIFT) & FINEMASK))>>1)
+						+ FINESINE((FixedAngle(base*9)>>ANGLETOFINESHIFT) & FINEMASK)
+						+ FINECOSINE(((FixedAngle(base*9))>>ANGLETOFINESHIFT) & FINEMASK))<<12); //*2^12
+					while (seg)
+					{
+						seg->movedir = seg->angle;
+						seg->angle = prev->movedir;
+						P_UnsetThingPosition(seg);
+						seg->x = prev->x + P_ReturnThrustX(prev, prev->angle, prev->radius);
+						seg->y = prev->y + P_ReturnThrustY(prev, prev->angle, prev->radius);
+						seg->z = prev->z + prev->height - (seg->scale>>1);
+						P_SetThingPosition(seg);
+						prev = seg;
+						seg = seg->tracer;
+					}
 				}
-				else if (mobj->fuse == 59)
+				break;
+			case MT_SPINCUSHION:
+				if (mobj->target && mobj->state-states >= S_SPINCUSHION_AIM1 && mobj->state-states <= S_SPINCUSHION_AIM5)
+					mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y);
+				break;
+			case MT_CRUSHCLAW:
+				if (mobj->state-states == S_CRUSHCLAW_STAY && mobj->target)
 				{
-					jetx = mobj->target->x + P_ReturnThrustX(mobj->target, mobj->target->angle, -mobj->target->radius);
-					jety = mobj->target->y + P_ReturnThrustY(mobj->target, mobj->target->angle, -mobj->target->radius);
-					P_UnsetThingPosition(mobj);
-					mobj->x = jetx;
-					mobj->y = jety;
-					if (mobj->target->eflags & MFE_VERTICALFLIP)
-						mobj->z = mobj->target->z + mobj->target->height/2 + mobj->height/2;
-					else
-						mobj->z = mobj->target->z + mobj->target->height/2 - mobj->height/2;
-					mobj->floorz = mobj->z;
-					mobj->ceilingz = mobj->z+mobj->height;
-					P_SetThingPosition(mobj);
+					mobj_t *chain = mobj->target->target;
+					SINT8 sign = ((mobj->tics & 1) ? mobj->tics : -(SINT8)(mobj->tics));
+					while (chain)
+					{
+						chain->z = chain->watertop + sign*mobj->scale;
+						sign = -sign;
+						chain = chain->target;
+					}
 				}
-				mobj->fuse++;
-			}
-			break;
-		case MT_PROPELLER:
-			{
-				fixed_t jetx, jety;
+				break;
+			case MT_SMASHINGSPIKEBALL:
+				mobj->momx = mobj->momy = 0;
+				if (mobj->state-states == S_SMASHSPIKE_FALL && P_IsObjectOnGround(mobj))
+				{
+					P_SetMobjState(mobj, S_SMASHSPIKE_STOMP1);
+					S_StartSound(mobj, sfx_spsmsh);
+				}
+				else if (mobj->state-states == S_SMASHSPIKE_RISE2 && P_MobjFlip(mobj)*(mobj->z - mobj->movecount) >= 0)
+				{
+					mobj->momz = 0;
+					P_SetMobjState(mobj, S_SMASHSPIKE_FLOAT);
+				}
+				break;
+			case MT_HANGSTER:
+				{
+					statenum_t st =  mobj->state-states;
+					//ghost image trail when flying down
+					if (st == S_HANGSTER_SWOOP1 || st == S_HANGSTER_SWOOP2)
+					{
+						P_SpawnGhostMobj(mobj);
+						//curve when in line with target, otherwise curve to avoid crashing into floor
+						if ((mobj->z - mobj->floorz <= 80*FRACUNIT) || (mobj->target && (mobj->z - mobj->target->z <= 80*FRACUNIT)))
+							P_SetMobjState(mobj, (st = S_HANGSTER_ARC1));
+					}
 
-				if (!mobj->target // if you have no target
-				|| (!(mobj->target->flags & MF_BOSS) && mobj->target->health <= 0)) // or your target isn't a boss and it's popped now
-				{ // then remove yourself as well!
+					//swoop arc movement stuff
+					if (st == S_HANGSTER_ARC1)
+					{
+						A_FaceTarget(mobj);
+						P_Thrust(mobj, mobj->angle, 1*FRACUNIT);
+					}
+					else if (st == S_HANGSTER_ARC2)
+						P_Thrust(mobj, mobj->angle, 2*FRACUNIT);
+					else if (st == S_HANGSTER_ARC3)
+						P_Thrust(mobj, mobj->angle, 4*FRACUNIT);
+					//if movement has stopped while flying (like hitting a wall), fly up immediately
+					else if (st == S_HANGSTER_FLY1 && !mobj->momx && !mobj->momy)
+					{
+						mobj->extravalue1 = 0;
+						P_SetMobjState(mobj, S_HANGSTER_ARCUP1);
+					}
+					//after swooping back up, check for ceiling
+					else if ((st == S_HANGSTER_RETURN1 || st == S_HANGSTER_RETURN2) && mobj->momz == 0 && mobj->ceilingz == (mobj->z + mobj->height))
+						P_SetMobjState(mobj, (st = S_HANGSTER_RETURN3));
+
+					//should you roost on a ceiling with F_SKY1 as its flat, disappear forever
+					if (st == S_HANGSTER_RETURN3 && mobj->momz == 0 && mobj->ceilingz == (mobj->z + mobj->height)
+					&& mobj->subsector->sector->ceilingpic == skyflatnum
+					&& mobj->subsector->sector->ceilingheight == mobj->ceilingz)
+					{
+						P_RemoveMobj(mobj);
+						return;
+					}
+				}
+				break;
+			case MT_EGGCAPSULE:
+				if (!mobj->reactiontime)
+				{
+					// Target nearest player on your mare.
+					// (You can make it float up/down by adding MF_FLOAT,
+					//  but beware level design pitfalls.)
+					fixed_t shortest = 1024*FRACUNIT;
+					INT32 i;
+					P_SetTarget(&mobj->target, NULL);
+					for (i = 0; i < MAXPLAYERS; i++)
+						if (playeringame[i] && players[i].mo
+						&& players[i].mare == mobj->threshold && players[i].spheres > 0)
+						{
+							fixed_t dist = P_AproxDistance(players[i].mo->x - mobj->x, players[i].mo->y - mobj->y);
+							if (dist < shortest)
+							{
+								P_SetTarget(&mobj->target, players[i].mo);
+								shortest = dist;
+							}
+						}
+				}
+				break;
+			case MT_EGGMOBILE2_POGO:
+				if (!mobj->target
+				|| !mobj->target->health
+				|| mobj->target->state == &states[mobj->target->info->spawnstate]
+				|| mobj->target->state == &states[mobj->target->info->raisestate])
+				{
 					P_RemoveMobj(mobj);
 					return;
 				}
-
-				jetx = mobj->target->x + P_ReturnThrustX(mobj->target, mobj->target->angle, FixedMul(-60*FRACUNIT, mobj->target->scale));
-				jety = mobj->target->y + P_ReturnThrustY(mobj->target, mobj->target->angle, FixedMul(-60*FRACUNIT, mobj->target->scale));
-
-				P_UnsetThingPosition(mobj);
-				mobj->x = jetx;
-				mobj->y = jety;
-				mobj->z = mobj->target->z + FixedMul(17*FRACUNIT, mobj->target->scale);
-				mobj->angle = mobj->target->angle - ANGLE_180;
-				mobj->floorz = mobj->z;
-				mobj->ceilingz = mobj->z+mobj->height;
-				P_SetThingPosition(mobj);
-			}
-			break;
-		case MT_JETFLAME:
-			{
-				if (!mobj->target // if you have no target
-				|| (!(mobj->target->flags & MF_BOSS) && mobj->target->health <= 0)) // or your target isn't a boss and it's popped now
-				{ // then remove yourself as well!
+				P_TeleportMove(mobj, mobj->target->x, mobj->target->y, mobj->target->z - mobj->height);
+				break;
+			case MT_HAMMER:
+				if (mobj->z <= mobj->floorz)
+				{
 					P_RemoveMobj(mobj);
 					return;
 				}
+				break;
+			case MT_KOOPA:
+				P_KoopaThinker(mobj);
+				break;
+			case MT_REDRING:
+				if (((mobj->z < mobj->floorz) || (mobj->z + mobj->height > mobj->ceilingz))
+					&& mobj->flags & MF_MISSILE)
+				{
+					P_ExplodeMissile(mobj);
+					return;
+				}
+				break;
+			case MT_BOSSFLYPOINT:
+				return;
+			case MT_NIGHTSCORE:
+				mobj->color = (UINT8)(leveltime % SKINCOLOR_WHITE);
+				break;
+			case MT_JETFUME1:
+				{
+					fixed_t jetx, jety;
 
-				P_UnsetThingPosition(mobj);
-				mobj->x = mobj->target->x;
-				mobj->y = mobj->target->y;
-				mobj->z = mobj->target->z - FixedMul(50*FRACUNIT, mobj->target->scale);
-				mobj->floorz = mobj->z;
-				mobj->ceilingz = mobj->z+mobj->height;
-				P_SetThingPosition(mobj);
-			}
-			break;
-		case MT_NIGHTSDRONE:
-			if (mobj->state >= &states[S_NIGHTSDRONE_SPARKLING1] && mobj->state <= &states[S_NIGHTSDRONE_SPARKLING16])
-			{
-				mobj->flags2 &= ~MF2_DONTDRAW;
-				mobj->z = mobj->floorz + mobj->height + (mobj->spawnpoint->options >> ZSHIFT) * FRACUNIT;
-				mobj->angle = 0;
+					if (!mobj->target // if you have no target
+					|| (!(mobj->target->flags & MF_BOSS) && mobj->target->health <= 0)) // or your target isn't a boss and it's popped now
+					{ // then remove yourself as well!
+						P_RemoveMobj(mobj);
+						return;
+					}
 
-				if (!mobj->target)
+					jetx = mobj->target->x + P_ReturnThrustX(mobj->target, mobj->target->angle, FixedMul(-64*FRACUNIT, mobj->target->scale));
+					jety = mobj->target->y + P_ReturnThrustY(mobj->target, mobj->target->angle, FixedMul(-64*FRACUNIT, mobj->target->scale));
+
+					if (mobj->fuse == 56) // First one
+					{
+						P_UnsetThingPosition(mobj);
+						mobj->x = jetx;
+						mobj->y = jety;
+						if (mobj->target->eflags & MFE_VERTICALFLIP)
+							mobj->z = mobj->target->z + mobj->target->height - mobj->height - FixedMul(38*FRACUNIT, mobj->target->scale);
+						else
+							mobj->z = mobj->target->z + FixedMul(38*FRACUNIT, mobj->target->scale);
+						mobj->floorz = mobj->z;
+						mobj->ceilingz = mobj->z+mobj->height;
+						P_SetThingPosition(mobj);
+					}
+					else if (mobj->fuse == 57)
+					{
+						P_UnsetThingPosition(mobj);
+						mobj->x = jetx + P_ReturnThrustX(mobj->target, mobj->target->angle-ANGLE_90, FixedMul(24*FRACUNIT, mobj->target->scale));
+						mobj->y = jety + P_ReturnThrustY(mobj->target, mobj->target->angle-ANGLE_90, FixedMul(24*FRACUNIT, mobj->target->scale));
+						if (mobj->target->eflags & MFE_VERTICALFLIP)
+							mobj->z = mobj->target->z + mobj->target->height - mobj->height - FixedMul(12*FRACUNIT, mobj->target->scale);
+						else
+							mobj->z = mobj->target->z + FixedMul(12*FRACUNIT, mobj->target->scale);
+						mobj->floorz = mobj->z;
+						mobj->ceilingz = mobj->z+mobj->height;
+						P_SetThingPosition(mobj);
+					}
+					else if (mobj->fuse == 58)
+					{
+						P_UnsetThingPosition(mobj);
+						mobj->x = jetx + P_ReturnThrustX(mobj->target, mobj->target->angle+ANGLE_90, FixedMul(24*FRACUNIT, mobj->target->scale));
+						mobj->y = jety + P_ReturnThrustY(mobj->target, mobj->target->angle+ANGLE_90, FixedMul(24*FRACUNIT, mobj->target->scale));
+						if (mobj->target->eflags & MFE_VERTICALFLIP)
+							mobj->z = mobj->target->z + mobj->target->height - mobj->height - FixedMul(12*FRACUNIT, mobj->target->scale);
+						else
+							mobj->z = mobj->target->z + FixedMul(12*FRACUNIT, mobj->target->scale);
+						mobj->floorz = mobj->z;
+						mobj->ceilingz = mobj->z+mobj->height;
+						P_SetThingPosition(mobj);
+					}
+					else if (mobj->fuse == 59)
+					{
+						jetx = mobj->target->x + P_ReturnThrustX(mobj->target, mobj->target->angle, -mobj->target->radius);
+						jety = mobj->target->y + P_ReturnThrustY(mobj->target, mobj->target->angle, -mobj->target->radius);
+						P_UnsetThingPosition(mobj);
+						mobj->x = jetx;
+						mobj->y = jety;
+						if (mobj->target->eflags & MFE_VERTICALFLIP)
+							mobj->z = mobj->target->z + mobj->target->height/2 + mobj->height/2;
+						else
+							mobj->z = mobj->target->z + mobj->target->height/2 - mobj->height/2;
+						mobj->floorz = mobj->z;
+						mobj->ceilingz = mobj->z+mobj->height;
+						P_SetThingPosition(mobj);
+					}
+					mobj->fuse++;
+				}
+				break;
+			case MT_PROPELLER:
 				{
-					mobj_t *goalpost = P_SpawnMobj(mobj->x, mobj->y, mobj->z + FRACUNIT, MT_NIGHTSGOAL);
-					CONS_Debug(DBG_NIGHTSBASIC, "Adding goal post\n");
-					goalpost->angle = mobj->angle;
-					P_SetTarget(&mobj->target, goalpost);
+					fixed_t jetx, jety;
+
+					if (!mobj->target // if you have no target
+					|| (!(mobj->target->flags & MF_BOSS) && mobj->target->health <= 0)) // or your target isn't a boss and it's popped now
+					{ // then remove yourself as well!
+						P_RemoveMobj(mobj);
+						return;
+					}
+
+					jetx = mobj->target->x + P_ReturnThrustX(mobj->target, mobj->target->angle, FixedMul(-60*FRACUNIT, mobj->target->scale));
+					jety = mobj->target->y + P_ReturnThrustY(mobj->target, mobj->target->angle, FixedMul(-60*FRACUNIT, mobj->target->scale));
+
+					P_UnsetThingPosition(mobj);
+					mobj->x = jetx;
+					mobj->y = jety;
+					mobj->z = mobj->target->z + FixedMul(17*FRACUNIT, mobj->target->scale);
+					mobj->angle = mobj->target->angle - ANGLE_180;
+					mobj->floorz = mobj->z;
+					mobj->ceilingz = mobj->z+mobj->height;
+					P_SetThingPosition(mobj);
 				}
+				break;
+			case MT_JETFLAME:
+				{
+					if (!mobj->target // if you have no target
+					|| (!(mobj->target->flags & MF_BOSS) && mobj->target->health <= 0)) // or your target isn't a boss and it's popped now
+					{ // then remove yourself as well!
+						P_RemoveMobj(mobj);
+						return;
+					}
 
-				if (G_IsSpecialStage(gamemap))
-				{ // Never show the NiGHTS drone in special stages. Check ANYONE for bonustime.
+					P_UnsetThingPosition(mobj);
+					mobj->x = mobj->target->x;
+					mobj->y = mobj->target->y;
+					mobj->z = mobj->target->z - FixedMul(50*FRACUNIT, mobj->target->scale);
+					mobj->floorz = mobj->z;
+					mobj->ceilingz = mobj->z+mobj->height;
+					P_SetThingPosition(mobj);
+				}
+				break;
+			case MT_NIGHTSDRONE:
+				// GOAL mode?
+				if (mobj->state >= &states[S_NIGHTSDRONE_SPARKLING1] && mobj->state <= &states[S_NIGHTSDRONE_SPARKLING16])
+				{
 					INT32 i;
 					boolean bonustime = false;
+
 					for (i = 0; i < MAXPLAYERS; i++)
-						if (playeringame[i] && players[i].bonustime)
+						if (playeringame[i] && players[i].bonustime && players[i].powers[pw_carry] == CR_NIGHTSMODE)
 						{
 							bonustime = true;
 							break;
 						}
+
 					if (!bonustime)
 					{
+						CONS_Debug(DBG_NIGHTSBASIC, "Removing goal post\n");
+						P_RemoveMobj(mobj->target);
+						P_SetTarget(&mobj->target, NULL);
+
 						mobj->flags &= ~MF_NOGRAVITY;
-						P_SetMobjState(mobj, S_NIGHTSDRONE1);
 						mobj->flags2 |= MF2_DONTDRAW;
-					}
-				}
-				else if (mobj->tracer && mobj->tracer->player)
-				{
-					if (!(mobj->tracer->player->powers[pw_carry] == CR_NIGHTSMODE))
-					{
-						mobj->flags &= ~MF_NOGRAVITY;
-						mobj->flags2 &= ~MF2_DONTDRAW;
-						P_SetMobjState(mobj, S_NIGHTSDRONE1);
-					}
-					else if (!mobj->tracer->player->bonustime)
-					{
-						mobj->flags &= ~MF_NOGRAVITY;
 						P_SetMobjState(mobj, S_NIGHTSDRONE1);
 					}
 				}
-			}
-			else
-			{
-				if (G_IsSpecialStage(gamemap))
-				{ // Never show the NiGHTS drone in special stages. Check ANYONE for bonustime.
+				// Invisible/bouncing mode.
+				else
+				{
 					INT32 i;
-
 					boolean bonustime = false;
+
+					// Bouncy bouncy!
+					mobj->angle += ANG10;
+					if (mobj->flags2 & MF2_DONTDRAW)
+						mobj->momz = 0;
+					else if (mobj->z <= mobj->floorz)
+						mobj->momz = 5*FRACUNIT;
+
 					for (i = 0; i < MAXPLAYERS; i++)
-						if (playeringame[i] && players[i].bonustime)
+						if (playeringame[i] && players[i].bonustime && players[i].powers[pw_carry] == CR_NIGHTSMODE)
 						{
 							bonustime = true;
 							break;
@@ -7722,203 +7982,195 @@ void P_MobjThinker(mobj_t *mobj)
 
 					if (bonustime)
 					{
-						P_SetMobjState(mobj, S_NIGHTSDRONE_SPARKLING1);
+						mobj->z = mobj->floorz + mobj->height;
+						mobj->angle = mobj->momz = 0;
+
+						if (mobj->spawnpoint)
+							mobj->z += (mobj->spawnpoint->options >> ZSHIFT)<<FRACBITS;
+
+						CONS_Debug(DBG_NIGHTSBASIC, "Adding goal post\n");
+						P_SetTarget(&mobj->target, P_SpawnMobjFromMobj(mobj, 0, 0, FRACUNIT, MT_NIGHTSGOAL));
+
+						mobj->flags2 &= ~MF2_DONTDRAW;
 						mobj->flags |= MF_NOGRAVITY;
+						P_SetMobjState(mobj, S_NIGHTSDRONE_SPARKLING1);
 					}
-					else
+					else if (!G_IsSpecialStage(gamemap))
 					{
-						if (mobj->target)
-						{
-							CONS_Debug(DBG_NIGHTSBASIC, "Removing goal post\n");
-							P_RemoveMobj(mobj->target);
-							P_SetTarget(&mobj->target, NULL);
-						}
-						mobj->flags2 |= MF2_DONTDRAW;
+						for (i = 0; i < MAXPLAYERS; i++)
+							if (playeringame[i] && players[i].powers[pw_carry] != CR_NIGHTSMODE)
+							{
+								bonustime = true; // variable reuse
+								break;
+							}
+
+						if (bonustime)
+							mobj->flags2 &= ~MF2_DONTDRAW;
+						else
+							mobj->flags2 |= MF2_DONTDRAW;
 					}
 				}
-				else if (mobj->tracer && mobj->tracer->player)
-				{
-					if (mobj->target)
-					{
-						CONS_Debug(DBG_NIGHTSBASIC, "Removing goal post\n");
-						P_RemoveMobj(mobj->target);
-						P_SetTarget(&mobj->target, NULL);
-					}
+				break;
+			case MT_PLAYER:
+				if (mobj->player)
+					P_PlayerMobjThinker(mobj);
+				return;
+			case MT_SKIM:
+				// check mobj against possible water content, before movement code
+				P_MobjCheckWater(mobj);
 
-					if (mobj->tracer->player->powers[pw_carry] == CR_NIGHTSMODE)
+				// Keep Skim at water surface
+				if (mobj->z <= mobj->watertop)
+				{
+					mobj->flags |= MF_NOGRAVITY;
+					if (mobj->z < mobj->watertop)
 					{
-						if (mobj->tracer->player->bonustime)
-						{
-							P_SetMobjState(mobj, S_NIGHTSDRONE_SPARKLING1);
-							mobj->flags |= MF_NOGRAVITY;
-						}
+						if (mobj->watertop - mobj->z <= FixedMul(mobj->info->speed*FRACUNIT, mobj->scale))
+							mobj->z = mobj->watertop;
 						else
-							mobj->flags2 |= MF2_DONTDRAW;
+							mobj->momz = FixedMul(mobj->info->speed*FRACUNIT, mobj->scale);
 					}
-					else // Not NiGHTS
-						mobj->flags2 &= ~MF2_DONTDRAW;
 				}
-				mobj->angle += ANG10;
-				if (mobj->z <= mobj->floorz)
-					mobj->momz = 5*FRACUNIT;
-			}
-			break;
-		case MT_PLAYER:
-			if (mobj->player)
-				P_PlayerMobjThinker(mobj);
-			return;
-		case MT_SKIM:
-			// check mobj against possible water content, before movement code
-			P_MobjCheckWater(mobj);
-
-			// Keep Skim at water surface
-			if (mobj->z <= mobj->watertop)
-			{
-				mobj->flags |= MF_NOGRAVITY;
-				if (mobj->z < mobj->watertop)
+				else
 				{
-					if (mobj->watertop - mobj->z <= FixedMul(mobj->info->speed*FRACUNIT, mobj->scale))
+					mobj->flags &= ~MF_NOGRAVITY;
+					if (mobj->z > mobj->watertop && mobj->z - mobj->watertop < FixedMul(MAXSTEPMOVE, mobj->scale))
 						mobj->z = mobj->watertop;
-					else
-						mobj->momz = FixedMul(mobj->info->speed*FRACUNIT, mobj->scale);
 				}
-			}
-			else
-			{
-				mobj->flags &= ~MF_NOGRAVITY;
-				if (mobj->z > mobj->watertop && mobj->z - mobj->watertop < FixedMul(MAXSTEPMOVE, mobj->scale))
-					mobj->z = mobj->watertop;
-			}
-			break;
-		case MT_RING:
-		case MT_COIN:
-		case MT_BLUEBALL:
-		case MT_REDTEAMRING:
-		case MT_BLUETEAMRING:
-			// No need to check water. Who cares?
-			P_RingThinker(mobj);
-			if (mobj->flags2 & MF2_NIGHTSPULL)
-				P_NightsItemChase(mobj);
-			else
-				A_AttractChase(mobj);
-			return;
-		// Flung items
-		case MT_FLINGRING:
-		case MT_FLINGCOIN:
-			if (mobj->flags2 & MF2_NIGHTSPULL)
-				P_NightsItemChase(mobj);
-			else
-				A_AttractChase(mobj);
-			break;
-		case MT_NIGHTSWING:
-			if (mobj->flags2 & MF2_NIGHTSPULL)
-				P_NightsItemChase(mobj);
-			break;
-		case MT_EMBLEM:
-			if (mobj->flags2 & MF2_NIGHTSPULL)
-				P_NightsItemChase(mobj);
-			break;
-		case MT_SHELL:
-			if (mobj->threshold && mobj->threshold != TICRATE)
-				mobj->threshold--;
-
-			if (mobj->threshold >= TICRATE)
-			{
-				mobj->angle += ((mobj->movedir == 1) ? ANGLE_22h : ANGLE_337h);
-				P_InstaThrust(mobj, R_PointToAngle2(0, 0, mobj->momx, mobj->momy), (mobj->info->speed*mobj->scale));
-			}
-			break;
-		case MT_TURRET:
-			P_MobjCheckWater(mobj);
-			P_CheckPosition(mobj, mobj->x, mobj->y);
-			if (P_MobjWasRemoved(mobj))
+				break;
+			case MT_RING:
+			case MT_COIN:
+			case MT_BLUESPHERE:
+			case MT_BOMBSPHERE:
+			case MT_NIGHTSCHIP:
+			case MT_NIGHTSSTAR:
+			case MT_REDTEAMRING:
+			case MT_BLUETEAMRING:
+				// No need to check water. Who cares?
+				P_RingThinker(mobj);
+				if (mobj->flags2 & MF2_NIGHTSPULL)
+					P_NightsItemChase(mobj);
+				else
+					A_AttractChase(mobj);
 				return;
-			mobj->floorz = tmfloorz;
-			mobj->ceilingz = tmceilingz;
+			// Flung items
+			case MT_FLINGRING:
+			case MT_FLINGCOIN:
+			case MT_FLINGBLUESPHERE:
+			case MT_FLINGNIGHTSCHIP:
+				if (mobj->flags2 & MF2_NIGHTSPULL)
+					P_NightsItemChase(mobj);
+				else
+					A_AttractChase(mobj);
+				break;
+			case MT_EMBLEM:
+				if (mobj->flags2 & MF2_NIGHTSPULL)
+					P_NightsItemChase(mobj);
+				break;
+			case MT_SHELL:
+				if (mobj->threshold && mobj->threshold != TICRATE)
+					mobj->threshold--;
 
-			if ((mobj->eflags & MFE_UNDERWATER) && mobj->health > 0)
-			{
-				P_SetMobjState(mobj, mobj->info->deathstate);
-				mobj->health = 0;
-				mobj->flags2 &= ~MF2_FIRING;
-			}
-			else if (mobj->health > 0 && mobj->z + mobj->height > mobj->ceilingz) // Crushed
-			{
-				INT32 i,j;
-				fixed_t ns;
-				fixed_t x,y,z;
-				mobj_t *mo2;
+				if (mobj->threshold >= TICRATE)
+				{
+					mobj->angle += ((mobj->movedir == 1) ? ANGLE_22h : ANGLE_337h);
+					P_InstaThrust(mobj, R_PointToAngle2(0, 0, mobj->momx, mobj->momy), (mobj->info->speed*mobj->scale));
+				}
+				break;
+			case MT_TURRET:
+				P_MobjCheckWater(mobj);
+				P_CheckPosition(mobj, mobj->x, mobj->y);
+				if (P_MobjWasRemoved(mobj))
+					return;
+				mobj->floorz = tmfloorz;
+				mobj->ceilingz = tmceilingz;
+				mobj->floorrover = tmfloorrover;
+				mobj->ceilingrover = tmceilingrover;
 
-				z = mobj->subsector->sector->floorheight + FixedMul(64*FRACUNIT, mobj->scale);
-				for (j = 0; j < 2; j++)
+				if ((mobj->eflags & MFE_UNDERWATER) && mobj->health > 0)
 				{
-					for (i = 0; i < 32; i++)
+					P_SetMobjState(mobj, mobj->info->deathstate);
+					mobj->health = 0;
+					mobj->flags2 &= ~MF2_FIRING;
+				}
+				else if (mobj->health > 0 && mobj->z + mobj->height > mobj->ceilingz) // Crushed
+				{
+					INT32 i,j;
+					fixed_t ns;
+					fixed_t x,y,z;
+					mobj_t *mo2;
+
+					z = mobj->subsector->sector->floorheight + FixedMul(64*FRACUNIT, mobj->scale);
+					for (j = 0; j < 2; j++)
 					{
-						const angle_t fa = (i*FINEANGLES/16) & FINEMASK;
-						ns = FixedMul(64 * FRACUNIT, mobj->scale);
-						x = mobj->x + FixedMul(FINESINE(fa),ns);
-						y = mobj->y + FixedMul(FINECOSINE(fa),ns);
-
-						mo2 = P_SpawnMobj(x, y, z, MT_EXPLODE);
-						ns = FixedMul(16 * FRACUNIT, mobj->scale);
-						mo2->momx = FixedMul(FINESINE(fa),ns);
-						mo2->momy = FixedMul(FINECOSINE(fa),ns);
+						for (i = 0; i < 32; i++)
+						{
+							const angle_t fa = (i*FINEANGLES/16) & FINEMASK;
+							ns = FixedMul(64 * FRACUNIT, mobj->scale);
+							x = mobj->x + FixedMul(FINESINE(fa),ns);
+							y = mobj->y + FixedMul(FINECOSINE(fa),ns);
+
+							mo2 = P_SpawnMobj(x, y, z, MT_EXPLODE);
+							ns = FixedMul(16 * FRACUNIT, mobj->scale);
+							mo2->momx = FixedMul(FINESINE(fa),ns);
+							mo2->momy = FixedMul(FINECOSINE(fa),ns);
+						}
+						z -= FixedMul(32*FRACUNIT, mobj->scale);
 					}
-					z -= FixedMul(32*FRACUNIT, mobj->scale);
+					P_SetMobjState(mobj, mobj->info->deathstate);
+					mobj->health = 0;
+					mobj->flags2 &= ~MF2_FIRING;
 				}
-				P_SetMobjState(mobj, mobj->info->deathstate);
-				mobj->health = 0;
-				mobj->flags2 &= ~MF2_FIRING;
-			}
-			break;
-		case MT_BLUEFLAG:
-		case MT_REDFLAG:
-			{
-				sector_t *sec2;
-				sec2 = P_ThingOnSpecial3DFloor(mobj);
-				if ((sec2 && GETSECSPECIAL(sec2->special, 4) == 2) || (GETSECSPECIAL(mobj->subsector->sector->special, 4) == 2))
-					mobj->fuse = 1; // Return to base.
 				break;
-			}
-		case MT_CANNONBALL:
+			case MT_BLUEFLAG:
+			case MT_REDFLAG:
+				{
+					sector_t *sec2;
+					sec2 = P_ThingOnSpecial3DFloor(mobj);
+					if ((sec2 && GETSECSPECIAL(sec2->special, 4) == 2) || (GETSECSPECIAL(mobj->subsector->sector->special, 4) == 2))
+						mobj->fuse = 1; // Return to base.
+					break;
+				}
+			case MT_CANNONBALL:
 #ifdef FLOORSPLATS
-			R_AddFloorSplat(mobj->tracer->subsector, mobj->tracer, "TARGET", mobj->tracer->x,
-				mobj->tracer->y, mobj->tracer->floorz, SPLATDRAWMODE_SHADE);
+				R_AddFloorSplat(mobj->tracer->subsector, mobj->tracer, "TARGET", mobj->tracer->x,
+					mobj->tracer->y, mobj->tracer->floorz, SPLATDRAWMODE_SHADE);
 #endif
-			break;
-		case MT_SPINDUST: // Spindash dust
-				mobj->momx = FixedMul(mobj->momx, (3*FRACUNIT)/4); // originally 50000
-				mobj->momy = FixedMul(mobj->momy, (3*FRACUNIT)/4); // same
-				//mobj->momz = mobj->momz+P_MobjFlip(mobj)/3; // no meaningful change in value to be frank
-				if (mobj->state >= &states[S_SPINDUST_BUBBLE1] && mobj->state <= &states[S_SPINDUST_BUBBLE4]) // bubble dust!
+				break;
+			case MT_SPINDUST: // Spindash dust
+					mobj->momx = FixedMul(mobj->momx, (3*FRACUNIT)/4); // originally 50000
+					mobj->momy = FixedMul(mobj->momy, (3*FRACUNIT)/4); // same
+					//mobj->momz = mobj->momz+P_MobjFlip(mobj)/3; // no meaningful change in value to be frank
+					if (mobj->state >= &states[S_SPINDUST_BUBBLE1] && mobj->state <= &states[S_SPINDUST_BUBBLE4]) // bubble dust!
+					{
+						P_MobjCheckWater(mobj);
+						if (mobj->watertop != mobj->subsector->sector->floorheight - 1000*FRACUNIT
+							&& mobj->z+mobj->height >= mobj->watertop - 5*FRACUNIT)
+							mobj->flags2 |= MF2_DONTDRAW;
+					}
+				break;
+			case MT_SPINFIRE:
+				if (mobj->flags & MF_NOGRAVITY)
 				{
-					P_MobjCheckWater(mobj);
-					if (mobj->watertop != mobj->subsector->sector->floorheight - 1000*FRACUNIT
-						&& mobj->z+mobj->height >= mobj->watertop - 5*FRACUNIT)
-						mobj->flags2 |= MF2_DONTDRAW;
+					if (mobj->eflags & MFE_VERTICALFLIP)
+						mobj->z = mobj->ceilingz - mobj->height;
+					else
+						mobj->z = mobj->floorz;
 				}
-			break;
-		case MT_SPINFIRE:
-			if (mobj->flags & MF_NOGRAVITY)
-			{
-				if (mobj->eflags & MFE_VERTICALFLIP)
-					mobj->z = mobj->ceilingz - mobj->height;
-				else
-					mobj->z = mobj->floorz;
-			}
-			/* FALLTHRU */
-		default:
-			// check mobj against possible water content, before movement code
-			P_MobjCheckWater(mobj);
+				/* FALLTHRU */
+			default:
+				// check mobj against possible water content, before movement code
+				P_MobjCheckWater(mobj);
 
-			// Extinguish fire objects in water
-			if (mobj->flags & MF_FIRE && mobj->type != MT_PUMA && mobj->type != MT_FIREBALL
-				&& (mobj->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)))
-			{
-				P_KillMobj(mobj, NULL, NULL, 0);
-				return;
-			}
-			break;
+				// Extinguish fire objects in water
+				if (mobj->flags & MF_FIRE && mobj->type != MT_PUMA && mobj->type != MT_FIREBALL
+					&& (mobj->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)))
+				{
+					P_KillMobj(mobj, NULL, NULL, 0);
+					return;
+				}
+				break;
+		}
 	}
 	if (P_MobjWasRemoved(mobj))
 		return;
@@ -7935,7 +8187,7 @@ void P_MobjThinker(mobj_t *mobj)
 		{
 			mobj_t *missile;
 
-			if (mobj->target->player && mobj->target->player->nightstime)
+			if (mobj->target->player && mobj->target->player->powers[pw_carry] == CR_NIGHTSMODE)
 			{
 				fixed_t oldval = mobjinfo[mobj->extravalue1].speed;
 
@@ -8095,6 +8347,8 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s
 				case MT_WALLSPIKE:
 					P_SetMobjState(mobj, mobj->state->nextstate);
 					mobj->fuse = mobj->info->speed;
+					if (mobj->spawnpoint)
+						mobj->fuse += (mobj->spawnpoint->angle/360);
 					break;
 				case MT_NIGHTSCORE:
 					P_RemoveMobj(mobj);
@@ -8143,6 +8397,8 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s
 #ifdef ESLOPE // Sliding physics for slidey mobjs!
 	if (mobj->type == MT_FLINGRING
 		|| mobj->type == MT_FLINGCOIN
+		|| mobj->type == MT_FLINGBLUESPHERE
+		|| mobj->type == MT_FLINGNIGHTSCHIP
 		|| P_WeaponOrPanel(mobj->type)
 		|| mobj->type == MT_FLINGEMERALD
 		|| mobj->type == MT_BIGTUMBLEWEED
@@ -8348,6 +8604,8 @@ void P_SceneryThinker(mobj_t *mobj)
 			return;
 		mobj->floorz = tmfloorz;
 		mobj->ceilingz = tmceilingz;
+		mobj->floorrover = tmfloorrover;
+		mobj->ceilingrover = tmceilingrover;
 	}
 	else
 	{
@@ -8383,7 +8641,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
 	mobj->height = info->height;
 	mobj->flags = info->flags;
 
-	mobj->health = info->spawnhealth;
+	mobj->health = (info->spawnhealth ? info->spawnhealth : 1);
 
 	mobj->reactiontime = info->reactiontime;
 
@@ -8430,6 +8688,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
 #endif
 				mobj->subsector->sector->ceilingheight;
 
+	mobj->floorrover = NULL;
+	mobj->ceilingrover = NULL;
+
 	// Tells MobjCheckWater that the water height was not set.
 	mobj->watertop = INT32_MAX;
 
@@ -8476,7 +8737,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
 			if (titlemapinaction) mobj->flags &= ~MF_NOTHINK;
 			break;
 		case MT_CYBRAKDEMON_NAPALM_BOMB_LARGE:
-			mobj->fuse = mobj->info->mass;
+			mobj->fuse = mobj->info->painchance;
 			break;
 		case MT_BLACKEGGMAN:
 			{
@@ -8486,20 +8747,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
 				P_SetTarget(&spawn->target, mobj);
 			}
 			break;
-		case MT_BLACKEGGMAN_HELPER:
-			// Collision helper can be stood on but not pushed
-			mobj->flags2 |= MF2_STANDONME;
-			break;
-		case MT_WALLSPIKE:
-		case MT_SPIKE:
-			mobj->flags2 |= MF2_STANDONME;
-			break;
-		case MT_GFZTREE:
-		case MT_GFZBERRYTREE:
-		case MT_GFZCHERRYTREE:
-		case MT_LAMPPOST1:
-		case MT_LAMPPOST2:
-			mobj->flags2 |= MF2_STANDONME;
+		case MT_FAKEMOBILE:
+		case MT_EGGSHIELD:
+			mobj->flags2 |= MF2_INVERTAIMABLE;
 			break;
 		case MT_DETON:
 			mobj->movedir = 0;
@@ -8514,41 +8764,68 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
 			}
 			break;
 		case MT_UNIDUS:
-		{
-			INT32 i;
-			mobj_t *ball;
-			// Spawn "damage" number of "painchance" spikeball mobjs
-			// threshold is the distance they should keep from the MT_UNIDUS (touching radius + ball painchance)
-			for (i = 0; i < mobj->info->damage; i++)
 			{
-				ball = P_SpawnMobj(x, y, z, mobj->info->painchance);
-				ball->destscale = mobj->scale;
-				P_SetScale(ball, mobj->scale);
-				P_SetTarget(&ball->target, mobj);
-				ball->movedir = FixedAngle(FixedMul(FixedDiv(i<<FRACBITS, mobj->info->damage<<FRACBITS), 360<<FRACBITS));
-				ball->threshold = ball->radius + mobj->radius + FixedMul(ball->info->painchance, ball->scale);
-
-				var1 = ball->state->var1, var2 = ball->state->var2;
-				ball->state->action.acp1(ball);
+				INT32 i;
+				mobj_t *ball;
+				// Spawn "damage" number of "painchance" spikeball mobjs
+				// threshold is the distance they should keep from the MT_UNIDUS (touching radius + ball painchance)
+				for (i = 0; i < mobj->info->damage; i++)
+				{
+					ball = P_SpawnMobj(x, y, z, mobj->info->painchance);
+					ball->destscale = mobj->scale;
+					P_SetScale(ball, mobj->scale);
+					P_SetTarget(&ball->target, mobj);
+					ball->movedir = FixedAngle(FixedMul(FixedDiv(i<<FRACBITS, mobj->info->damage<<FRACBITS), 360<<FRACBITS));
+					ball->threshold = ball->radius + mobj->radius + FixedMul(ball->info->painchance, ball->scale);
+
+					var1 = ball->state->var1, var2 = ball->state->var2;
+					ball->state->action.acp1(ball);
+				}
 			}
 			break;
-		}
 		case MT_POINTY:
-		{
-			INT32 q;
-			mobj_t *ball, *lastball = mobj;
+			{
+				INT32 q;
+				mobj_t *ball, *lastball = mobj;
 
-			for (q = 0; q < mobj->info->painchance; q++)
+				for (q = 0; q < mobj->info->painchance; q++)
+				{
+					ball = P_SpawnMobj(x, y, z, mobj->info->mass);
+					ball->destscale = mobj->scale;
+					P_SetScale(ball, mobj->scale);
+					P_SetTarget(&lastball->tracer, ball);
+					P_SetTarget(&ball->target, mobj);
+					lastball = ball;
+				}
+			}
+			break;
+		case MT_CRUSHSTACEAN:
 			{
-				ball = P_SpawnMobj(x, y, z, mobj->info->mass);
-				ball->destscale = mobj->scale;
-				P_SetScale(ball, mobj->scale);
-				P_SetTarget(&lastball->tracer, ball);
-				P_SetTarget(&ball->target, mobj);
-				lastball = ball;
+				mobj_t *bigmeatyclaw = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_CRUSHCLAW);
+				bigmeatyclaw->angle = mobj->angle + ((mobj->flags2 & MF2_AMBUSH) ? ANGLE_90 : ANGLE_270);;
+				P_SetTarget(&mobj->tracer, bigmeatyclaw);
+				P_SetTarget(&bigmeatyclaw->tracer, mobj);
+				mobj->reactiontime >>= 1;
+			}
+			break;
+		case MT_BIGMINE:
+			mobj->extravalue1 = FixedHypot(mobj->x, mobj->y)>>FRACBITS;
+			break;
+		case MT_WAVINGFLAG:
+			{
+				mobj_t *prev = mobj, *cur;
+				UINT8 i;
+				mobj->destscale <<= 2;
+				P_SetScale(mobj, mobj->destscale);
+				for (i = 0; i <= 16; i++) // probably should be < but staying authentic to the Lua version
+				{
+					cur = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_WAVINGFLAGSEG);
+					P_SetTarget(&prev->tracer, cur);
+					cur->extravalue1 = i;
+					prev = cur;
+				}
 			}
 			break;
-		}
 		case MT_EGGMOBILE2:
 			// Special condition for the 2nd boss.
 			mobj->watertop = mobj->info->speed;
@@ -8556,6 +8833,26 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
 		case MT_FLICKY_08:
 			mobj->color = (P_RandomChance(FRACUNIT/2) ? SKINCOLOR_RED : SKINCOLOR_AQUA);
 			break;
+		case MT_BALLOON:
+			mobj->color = SKINCOLOR_RED;
+			break;
+		case MT_HIVEELEMENTAL:
+			mobj->extravalue1 = 5;
+			break;
+		case MT_SMASHINGSPIKEBALL:
+			mobj->movecount = mobj->z;
+			break;
+		case MT_SPINBOBERT:
+			{
+				mobj_t *fire;
+				fire = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_SPINBOBERT_FIRE1);
+				P_SetTarget(&fire->target, mobj);
+				P_SetTarget(&mobj->hnext, fire);
+				fire = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_SPINBOBERT_FIRE2);
+				P_SetTarget(&fire->target, mobj);
+				P_SetTarget(&mobj->hprev, fire);
+			}
+			break;
 		case MT_REDRING: // Make MT_REDRING red by default
 			mobj->color = skincolor_redring;
 			break;
@@ -8564,8 +8861,14 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
 		case MT_EXTRALARGEBUBBLE:
 			mobj->fuse += 30 * TICRATE;
 			break;
+		case MT_NIGHTSDRONE:
+			if (G_IsSpecialStage(gamemap))
+				mobj->flags2 |= MF2_DONTDRAW;
+			nummaprings = -1; // no perfect bonus, rings are free
+			break;
 		case MT_EGGCAPSULE:
-			mobj->extravalue1 = -1; // timer for how long a player has been at the capsule
+			mobj->extravalue1 = -1; // sphere timer for how long a player has been at the capsule
+			mobj->extravalue2 = -1; // tic timer for how long a player has been at the capsule
 			break;
 		case MT_REDTEAMRING:
 			mobj->color = skincolor_redteam;
@@ -8575,8 +8878,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
 			break;
 		case MT_RING:
 		case MT_COIN:
-		case MT_BLUEBALL:
-			nummaprings++;
+		case MT_NIGHTSSTAR:
+			if (nummaprings >= 0)
+				nummaprings++;
 		default:
 			break;
 	}
@@ -8650,6 +8954,9 @@ static precipmobj_t *P_SpawnPrecipMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype
 #endif
 				mobj->subsector->sector->ceilingheight;
 
+	mobj->floorrover = NULL;
+	mobj->ceilingrover = NULL;
+
 	mobj->z = z;
 	mobj->momz = mobjinfo[type].speed;
 
@@ -8671,14 +8978,15 @@ static precipmobj_t *P_SpawnPrecipMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype
 static inline precipmobj_t *P_SpawnRainMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
 {
 	precipmobj_t *mo = P_SpawnPrecipMobj(x,y,z,type);
-	mo->thinker.function.acp1 = (actionf_p1)P_RainThinker;
+	mo->precipflags |= PCF_RAIN;
+	//mo->thinker.function.acp1 = (actionf_p1)P_RainThinker;
 	return mo;
 }
 
 static inline precipmobj_t *P_SpawnSnowMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
 {
 	precipmobj_t *mo = P_SpawnPrecipMobj(x,y,z,type);
-	mo->thinker.function.acp1 = (actionf_p1)P_SnowThinker;
+	//mo->thinker.function.acp1 = (actionf_p1)P_SnowThinker;
 	return mo;
 }
 
@@ -8710,7 +9018,7 @@ void P_RemoveMobj(mobj_t *mobj)
 	if (mobj->spawnpoint &&
 		(mobj->type == MT_RING
 		|| mobj->type == MT_COIN
-		|| mobj->type == MT_BLUEBALL
+		|| mobj->type == MT_NIGHTSSTAR
 		|| mobj->type == MT_REDTEAMRING
 		|| mobj->type == MT_BLUETEAMRING
 		|| P_WeaponOrPanel(mobj->type))
@@ -8730,7 +9038,7 @@ void P_RemoveMobj(mobj_t *mobj)
 	if (mobj->player && mobj->player->followmobj)
 	{
 		P_RemoveMobj(mobj->player->followmobj);
-		mobj->player->followmobj = NULL;
+		P_SetTarget(&mobj->player->followmobj, NULL);
 	}
 
 	mobj->health = 0; // Just because
@@ -8985,7 +9293,7 @@ void P_PrecipitationEffects(void)
 	if (!playeringame[displayplayer] || !players[displayplayer].mo)
 		return;
 
-	if (nosound || sound_disabled)
+	if (sound_disabled)
 		return; // Sound off? D'aw, no fun.
 
 	if (players[displayplayer].mo->subsector->sector->ceilingpic == skyflatnum)
@@ -9114,7 +9422,7 @@ void P_RespawnSpecials(void)
 #endif
 			ss->sector->ceilingheight) - (mthing->options >> ZSHIFT) * FRACUNIT;
 			if (mthing->options & MTF_AMBUSH
-			&& (i == MT_RING || i == MT_REDTEAMRING || i == MT_BLUETEAMRING || i == MT_COIN || P_WeaponOrPanel(i)))
+			&& (i == MT_RING || i == MT_REDTEAMRING || i == MT_BLUETEAMRING || i == MT_COIN || i == MT_NIGHTSSTAR || P_WeaponOrPanel(i)))
 				z -= 24*FRACUNIT;
 			z -= mobjinfo[i].height; // Don't forget the height!
 		}
@@ -9126,7 +9434,7 @@ void P_RespawnSpecials(void)
 #endif
 			ss->sector->floorheight) + (mthing->options >> ZSHIFT) * FRACUNIT;
 			if (mthing->options & MTF_AMBUSH
-			&& (i == MT_RING || i == MT_REDTEAMRING || i == MT_BLUETEAMRING || i == MT_COIN || P_WeaponOrPanel(i)))
+			&& (i == MT_RING || i == MT_REDTEAMRING || i == MT_BLUETEAMRING || i == MT_COIN || i == MT_NIGHTSSTAR || P_WeaponOrPanel(i)))
 				z += 24*FRACUNIT;
 		}
 
@@ -9163,7 +9471,7 @@ void P_SpawnPlayer(INT32 playernum)
 		p->spectator = p->outofcoop =
 		(((multiplayer || netgame) && gametype == GT_COOP) // only question status in coop
 		&& ((leveltime > 0
-		&& ((G_IsSpecialStage(gamemap) && useNightsSS) // late join special stage
+		&& ((G_IsSpecialStage(gamemap) && (maptol & TOL_NIGHTS)) // late join special stage
 		|| (cv_coopstarposts.value == 2 && (p->jointime < 1 || p->outofcoop)))) // late join or die in new coop
 		|| (((cv_cooplives.value == 1) || !P_GetLives(p)) && p->lives <= 0))); // game over and can't redistribute lives
 	}
@@ -9232,7 +9540,7 @@ void P_SpawnPlayer(INT32 playernum)
 	P_SetupStateAnimation(mobj, mobj->state);
 
 	mobj->health = 1;
-	p->rings = 0;
+	p->rings = p->spheres = 0;
 	p->playerstate = PST_LIVE;
 
 	p->bonustime = false;
@@ -9251,6 +9559,22 @@ void P_SpawnPlayer(INT32 playernum)
 	mobj->radius = FixedMul(skins[p->skin].radius, mobj->scale);
 	mobj->height = P_GetPlayerHeight(p);
 
+	if (!leveltime && ((maptol & TOL_NIGHTS) == TOL_NIGHTS) != (G_IsSpecialStage(gamemap))) // non-special NiGHTS stage or special non-NiGHTS stage
+	{
+		if (maptol & TOL_NIGHTS)
+		{
+			if (p == players) // this is totally the wrong place to do this aaargh.
+			{
+				mobj_t *idya = P_SpawnMobjFromMobj(mobj, 0, 0, mobj->height, MT_GOTEMERALD);
+				P_SetTarget(&idya->target, mobj);
+				P_SetMobjState(idya, mobjinfo[MT_GOTEMERALD].missilestate);
+				P_SetTarget(&mobj->tracer, idya);
+			}
+		}
+		else if (sstimer)
+			p->nightstime = sstimer;
+	}
+
 	// Spawn with a pity shield if necessary.
 	P_DoPityCheck(p);
 }
@@ -9509,19 +9833,21 @@ void P_SpawnMapThing(mapthing_t *mthing)
 	else if (mthing->type == 750) // Slope vertex point (formerly chaos spawn)
 		return;
 
-	else if (mthing->type == 300 // Ring
-		|| mthing->type == 308 || mthing->type == 309 // Team Rings
-		|| mthing->type == 1706 // Nights Wing
-		|| (mthing->type >= 600 && mthing->type <= 609) // Placement patterns
-		|| mthing->type == 1705 || mthing->type == 1713 // NiGHTS Hoops
-		|| mthing->type == 1800) // Mario Coin
+	else if (mthing->type == mobjinfo[MT_RING].doomednum || mthing->type == mobjinfo[MT_COIN].doomednum
+	 || mthing->type == mobjinfo[MT_REDTEAMRING].doomednum || mthing->type == mobjinfo[MT_BLUETEAMRING].doomednum
+	 || mthing->type == mobjinfo[MT_BLUESPHERE].doomednum || mthing->type == mobjinfo[MT_BOMBSPHERE].doomednum
+	 || (mthing->type >= 600 && mthing->type <= 609) // circles and diagonals
+	 || mthing->type == 1705 || mthing->type == 1713 || mthing->type == 1800) // hoops
 	{
 		// Don't spawn hoops, wings, or rings yet!
 		return;
 	}
 
 	// check for players specially
-	if (mthing->type > 0 && mthing->type <= 32)
+#if MAXPLAYERS > 32
+You should think about modifying the deathmatch starts to take full advantage of this!
+#endif
+	if (mthing->type > 0 && mthing->type <= MAXPLAYERS)
 	{
 		// save spots for respawning in network games
 		if (!metalrecording)
@@ -9564,10 +9890,6 @@ void P_SpawnMapThing(mapthing_t *mthing)
 			return;
 	}
 
-	if (!G_RingSlingerGametype() || !cv_specialrings.value)
-		if (P_WeaponOrPanel(i))
-			return; // Don't place weapons/panels in non-ringslinger modes
-
 	if (i == MT_EMERHUNT)
 	{
 		// Emerald Hunt is Coop only.
@@ -9601,6 +9923,10 @@ void P_SpawnMapThing(mapthing_t *mthing)
 		if ((mobjinfo[i].flags & MF_ENEMY) || (mobjinfo[i].flags & MF_BOSS))
 			return;
 
+	if (!G_RingSlingerGametype() || !cv_specialrings.value)
+		if (P_WeaponOrPanel(i))
+			return; // Don't place weapons/panels in non-ringslinger modes
+
 	// Altering monitor spawns via cvars
 	// If MF_GRENADEBOUNCE is set in the monitor's info,
 	// skip this step. (Used for gold monitors)
@@ -9637,9 +9963,7 @@ void P_SpawnMapThing(mapthing_t *mthing)
 
 	if (gametype != GT_CTF) // CTF specific things
 	{
-		if (i == MT_BLUETEAMRING || i == MT_REDTEAMRING)
-			i = MT_RING;
-		else if (i == MT_RING_BLUEBOX || i == MT_RING_REDBOX)
+		if (i == MT_RING_BLUEBOX || i == MT_RING_REDBOX)
 			i = MT_RING_BOX;
 		else if (i == MT_BLUEFLAG || i == MT_REDFLAG)
 			return; // No flags in non-CTF modes!
@@ -9677,11 +10001,9 @@ void P_SpawnMapThing(mapthing_t *mthing)
 	{
 		if (i == MT_PITY_BOX || i == MT_ELEMENTAL_BOX || i == MT_ATTRACT_BOX
 		 || i == MT_FORCE_BOX || i == MT_ARMAGEDDON_BOX || i == MT_WHIRLWIND_BOX
-		 || i == MT_FLAMEAURA_BOX || i == MT_BUBBLEWRAP_BOX || i == MT_THUNDERCOIN_BOX)
-			return; // No shields in Ultimate mode
-
-		if (i == MT_RING_BOX && !G_IsSpecialStage(gamemap))
-			return; // No rings in Ultimate mode (except special stages)
+		 || i == MT_FLAMEAURA_BOX || i == MT_BUBBLEWRAP_BOX || i == MT_THUNDERCOIN_BOX
+		 || i == MT_RING_BOX)
+			return; // No rings or shields in Ultimate mode
 
 		// Don't include the gold repeating boxes here please.
 		// They're likely facets of the level's design and therefore required to progress.
@@ -9706,7 +10028,7 @@ void P_SpawnMapThing(mapthing_t *mthing)
 			ss->sector->floorheight) + ((mthing->options >> ZSHIFT) << FRACBITS);
 	else if (i == MT_AXIS || i == MT_AXISTRANSFER || i == MT_AXISTRANSFERLINE)
 		z = ONFLOORZ;
-	else if (i == MT_SPECIALSPIKEBALL || P_WeaponOrPanel(i) || i == MT_EMERALDSPAWN || i == MT_TOKEN)
+	else if (i == MT_SPIKEBALL || P_WeaponOrPanel(i) || i == MT_EMERALDSPAWN || i == MT_TOKEN)
 	{
 		if (mthing->options & MTF_OBJECTFLIP)
 		{
@@ -9831,6 +10153,58 @@ void P_SpawnMapThing(mapthing_t *mthing)
 		else
 			mobj->health = FixedMul(ss->sector->ceilingheight-ss->sector->floorheight, 3*(FRACUNIT/4))>>FRACBITS;
 		break;
+	case MT_BALLOON:
+		if (mthing->angle > 0)
+			mobj->color = ((mthing->angle-1) % (MAXSKINCOLORS-1))+1;
+		break;
+#define makesoftwarecorona(mo, h) \
+			corona = P_SpawnMobjFromMobj(mo, 0, 0, h<<FRACBITS, MT_PARTICLE);\
+			corona->sprite = SPR_FLAM;\
+			corona->frame = (FF_FULLBRIGHT|FF_TRANS90|12);\
+			corona->tics = -1
+	case MT_FLAME:
+		if (mthing->options & MTF_EXTRA)
+		{
+			mobj_t *corona;
+			makesoftwarecorona(mobj, 20);
+			P_SetScale(corona, (corona->destscale = mobj->scale*3));
+			P_SetTarget(&mobj->tracer, corona);
+		}
+		break;
+	case MT_FLAMEHOLDER:
+		if (!(mthing->options & MTF_OBJECTSPECIAL)) // Spawn the fire
+		{
+			mobj_t *flame = P_SpawnMobjFromMobj(mobj, 0, 0, mobj->height, MT_FLAME);
+			P_SetTarget(&flame->target, mobj);
+			flame->flags2 |= MF2_BOSSNOTRAP;
+			if (mthing->options & MTF_EXTRA)
+			{
+				mobj_t *corona;
+				makesoftwarecorona(flame, 20);
+				P_SetScale(corona, (corona->destscale = flame->scale*3));
+				P_SetTarget(&flame->tracer, corona);
+			}
+		}
+		break;
+	case MT_CANDLE:
+	case MT_CANDLEPRICKET:
+		if (mthing->options & MTF_EXTRA)
+		{
+			mobj_t *corona;
+			makesoftwarecorona(mobj, ((mobj->type == MT_CANDLE) ? 42 : 176));
+		}
+		break;
+#undef makesoftwarecorona
+	case MT_JACKO1:
+	case MT_JACKO2:
+	case MT_JACKO3:
+		if (!(mthing->options & MTF_EXTRA)) // take the torch out of the crafting recipe
+		{
+			mobj_t *overlay = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_OVERLAY);
+			P_SetTarget(&overlay->target, mobj);
+			P_SetMobjState(overlay, mobj->info->raisestate);
+		}
+		break;
 	case MT_WATERDRIP:
 		if (mthing->angle)
 			mobj->tics = 3*TICRATE + mthing->angle;
@@ -9857,7 +10231,7 @@ void P_SpawnMapThing(mapthing_t *mthing)
 		fixed_t mlength, mmaxlength, mlengthset, mspeed, mphase, myaw, mpitch, mminlength, mnumspokes, mpinch, mroll, mnumnospokes, mwidth, mwidthset, mmin, msound, radiusfactor, widthfactor;
 		angle_t mspokeangle;
 		mobjtype_t chainlink, macetype, firsttype, linktype;
-		boolean mdosound, mdocenter;
+		boolean mdosound, mdocenter, mchainlike = false;
 		mobj_t *spawnee = NULL, *hprev = mobj;
 		mobjflag_t mflagsapply;
 		mobjflag2_t mflags2apply;
@@ -9963,6 +10337,19 @@ ML_EFFECT4 : Don't clip inside the ground
 				else
 					chainlink = MT_NULL;
 				break;
+			case MT_CHAINPOINT:
+				if (mthing->options & MTF_AMBUSH)
+				{
+					macetype = MT_BIGGRABCHAIN;
+					chainlink = MT_BIGMACECHAIN;
+				}
+				else
+				{
+					macetype = MT_SMALLGRABCHAIN;
+					chainlink = MT_SMALLMACECHAIN;
+				}
+				mchainlike = true;
+				break;
 			default:
 				if (mthing->options & MTF_AMBUSH)
 				{
@@ -9980,17 +10367,15 @@ ML_EFFECT4 : Don't clip inside the ground
 		if (!macetype && !chainlink)
 			break;
 
-		if (mobj->type != MT_CHAINPOINT)
-		{
-			firsttype = macetype;
-			mlength++;
-		}
-		else
+		if (mobj->type == MT_CHAINPOINT)
 		{
 			if (!mlength)
 				break;
-			firsttype = chainlink;
 		}
+		else
+			mlength++;
+
+		firsttype = macetype;
 
 		// Adjustable direction
 		if (lines[line].flags & ML_NOCLIMB)
@@ -10014,13 +10399,15 @@ ML_EFFECT4 : Don't clip inside the ground
 		else
 			radiusfactor = (((linktype = chainlink) == MT_NULL) ? 2 : 1);
 
-		widthfactor = ((firsttype == chainlink) ? 1 : 2);
+		if (!mchainlike)
+			mchainlike = (firsttype == chainlink);
+		widthfactor = (mchainlike ? 1 : 2);
 
 		mflagsapply = ((lines[line].flags & ML_EFFECT4) ? 0 : (MF_NOCLIP|MF_NOCLIPHEIGHT));
 		mflags2apply = ((mthing->options & MTF_OBJECTFLIP) ? MF2_OBJECTFLIP : 0);
 		meflagsapply = ((mthing->options & MTF_OBJECTFLIP) ? MFE_VERTICALFLIP : 0);
 
-		msound = ((firsttype == chainlink) ? 0 : (mwidth & 1));
+		msound = (mchainlike ? 0 : (mwidth & 1));
 
 		// Quick and easy preparatory variable setting
 		mphase = (FixedAngle(mphase*FRACUNIT)>>ANGLETOFINESHIFT);
@@ -10059,7 +10446,8 @@ ML_EFFECT4 : Don't clip inside the ground
 				if (mobj->type != MT_CHAINMACEPOINT)
 					continue;
 
-				firsttype = linktype = chainlink;
+				linktype = chainlink;
+				firsttype = ((mthing->options & MTF_AMBUSH) ? MT_BIGGRABCHAIN : MT_SMALLGRABCHAIN);
 				mmaxlength = 1 + (mlength - 1)*radiusfactor;
 				radiusfactor = widthfactor = 1;
 			}
@@ -10074,10 +10462,7 @@ ML_EFFECT4 : Don't clip inside the ground
 						radiusfactor = 2;
 					}
 					else
-					{
-						linktype = chainlink;
 						radiusfactor = (((linktype = chainlink) == MT_NULL) ? 2 : 1);
-					}
 
 					firsttype = macetype;
 					widthfactor = 2;
@@ -10089,8 +10474,8 @@ ML_EFFECT4 : Don't clip inside the ground
 			mwidthset = mwidth;
 			mlengthset = mminlength;
 
-			if (mdocenter) // Innermost mace/link
-				makemace(macetype, 0, 0);
+			if (mdocenter) // Innermost link
+				makemace(linktype, 0, 0);
 
 			// Out from the center...
 			if (linktype)
@@ -10144,8 +10529,8 @@ ML_EFFECT4 : Don't clip inside the ground
 					while (mlengthset > mminlength)
 						makemace(linktype, radiusfactor*(mlengthset--), 0);
 
-				if (mdocenter) // Innermost mace/link
-					makemace(macetype, 0, 0);
+				if (mdocenter) // Innermost link
+					makemace(linktype, 0, 0);
 			}
 		}
 
@@ -10155,8 +10540,8 @@ ML_EFFECT4 : Don't clip inside the ground
 	}
 	case MT_PARTICLEGEN:
 	{
-		fixed_t radius, speed, bottomheight, topheight;
-		INT32 type, numdivisions, time, anglespeed, ticcount;
+		fixed_t radius, speed;
+		INT32 type, numdivisions, anglespeed, ticcount;
 		angle_t angledivision;
 		INT32 line;
 		const size_t mthingi = (size_t)(mthing - mapthings);
@@ -10175,13 +10560,9 @@ ML_EFFECT4 : Don't clip inside the ground
 		else
 			type = (INT32)MT_PARTICLE;
 
-		speed = abs(sides[lines[line].sidenum[0]].textureoffset);
-		bottomheight = lines[line].frontsector->floorheight;
-		topheight = lines[line].frontsector->ceilingheight - mobjinfo[(mobjtype_t)type].height;
-
 		if (!lines[line].backsector
 		|| (ticcount = (sides[lines[line].sidenum[1]].textureoffset >> FRACBITS)) < 1)
-			ticcount = states[S_PARTICLEGEN].tics;
+			ticcount = 3;
 
 		numdivisions = (mthing->options >> ZSHIFT);
 
@@ -10199,18 +10580,9 @@ ML_EFFECT4 : Don't clip inside the ground
 			angledivision = 0;
 		}
 
-		if ((speed) && (topheight > bottomheight))
-			time = (INT32)(FixedDiv((topheight - bottomheight), speed) >> FRACBITS);
-		else
-			time = 1; // There's no reasonable way to set it, so just show the object for one tic and move on.
-
+		speed = abs(sides[lines[line].sidenum[0]].textureoffset);
 		if (mthing->options & MTF_OBJECTFLIP)
-		{
-			mobj->z = topheight;
 			speed *= -1;
-		}
-		else
-			mobj->z = bottomheight;
 
 		CONS_Debug(DBG_GAMELOGIC, "Particle Generator (mapthing #%s):\n"
 				"Radius is %d\n"
@@ -10218,20 +10590,20 @@ ML_EFFECT4 : Don't clip inside the ground
 				"Anglespeed is %d\n"
 				"Numdivisions is %d\n"
 				"Angledivision is %d\n"
-				"Time is %d\n"
 				"Type is %d\n"
 				"Tic seperation is %d\n",
-				sizeu1(mthingi), radius, speed, anglespeed, numdivisions, angledivision, time, type, ticcount);
+				sizeu1(mthingi), radius, speed, anglespeed, numdivisions, angledivision, type, ticcount);
 
 		mobj->angle = 0;
 		mobj->movefactor = speed;
 		mobj->lastlook = numdivisions;
 		mobj->movedir = angledivision*ANG1;
 		mobj->movecount = anglespeed*ANG1;
-		mobj->health = time;
 		mobj->friction = radius;
 		mobj->threshold = type;
 		mobj->reactiontime = ticcount;
+		mobj->cvmem = line;
+		mobj->watertop = mobj->waterbottom = 0;
 
 		break;
 	}
@@ -10266,12 +10638,62 @@ ML_EFFECT4 : Don't clip inside the ground
 		if (mthing->angle > 0)
 			mobj->health = mthing->angle;
 		break;
+	case MT_HIVEELEMENTAL:
+		if (mthing->extrainfo)
+			mobj->extravalue1 = mthing->extrainfo;
+		break;
 	case MT_TRAPGOYLE:
 	case MT_TRAPGOYLEUP:
 	case MT_TRAPGOYLEDOWN:
 	case MT_TRAPGOYLELONG:
 		if (mthing->angle >= 360)
 			mobj->tics += 7*(mthing->angle / 360) + 1; // starting delay
+		break;
+	case MT_DSZSTALAGMITE:
+	case MT_DSZ2STALAGMITE:
+	case MT_KELP:
+		if (mthing->options & MTF_OBJECTSPECIAL) { // make mobj twice as big as normal
+			P_SetScale(mobj, 2*mobj->scale); // not 2*FRACUNIT in case of something like the old ERZ3 mode
+			mobj->destscale = mobj->scale;
+		}
+		break;
+	case MT_THZTREE:
+		{ // Spawn the branches
+			angle_t mobjangle = FixedAngle((mthing->angle % 113)<<FRACBITS);
+			P_SpawnMobjFromMobj(mobj, 1*FRACUNIT,  0,          0, MT_THZTREEBRANCH)->angle = mobjangle + ANGLE_22h;
+			P_SpawnMobjFromMobj(mobj, 0,           1*FRACUNIT, 0, MT_THZTREEBRANCH)->angle = mobjangle + ANGLE_157h;
+			P_SpawnMobjFromMobj(mobj, -1*FRACUNIT, 0,          0, MT_THZTREEBRANCH)->angle = mobjangle + ANGLE_270;
+		}
+		break;
+	case MT_CEZPOLE:
+		{ // Spawn the banner
+			angle_t mobjangle = FixedAngle(mthing->angle<<FRACBITS);
+			P_SpawnMobjFromMobj(mobj,
+				P_ReturnThrustX(mobj, mobjangle, 4<<FRACBITS),
+				P_ReturnThrustY(mobj, mobjangle, 4<<FRACBITS),
+				0, MT_CEZBANNER)->angle = mobjangle + ANGLE_90;
+		}
+			break;
+	case MT_HHZTREE_TOP:
+		{ // Spawn the branches
+			angle_t mobjangle = FixedAngle(mthing->angle<<FRACBITS) & (ANGLE_90-1);
+			mobj_t *leaf;
+#define doleaf(x, y) \
+			leaf = P_SpawnMobjFromMobj(mobj, x, y, 0, MT_HHZTREE_PART);\
+			leaf->angle = mobjangle;\
+			P_SetMobjState(leaf, leaf->info->seestate);\
+			mobjangle += ANGLE_90
+			doleaf(1*FRACUNIT, 0);
+			doleaf(0, 1*FRACUNIT);
+			doleaf(-1*FRACUNIT, 0);
+			doleaf(0, -1*FRACUNIT);
+#undef doleaf
+		}
+		break;
+	case MT_SMASHINGSPIKEBALL:
+		if (mthing->angle > 0)
+			mobj->tics += mthing->angle;
+		break;
 	default:
 		break;
 	}
@@ -10304,9 +10726,6 @@ ML_EFFECT4 : Don't clip inside the ground
 	}
 	else if (i == MT_TOKEN)
 	{
-		if (mthing->options & MTF_OBJECTSPECIAL) // Mario Block version
-			mobj->flags &= ~(MF_NOGRAVITY|MF_NOCLIPHEIGHT);
-
 		// We advanced tokenbits earlier due to the return check.
 		// Subtract 1 here for the correct value.
 		mobj->health = 1 << (tokenbits - 1);
@@ -10316,7 +10735,7 @@ ML_EFFECT4 : Don't clip inside the ground
 		mobj_t *elecmobj;
 		elecmobj = P_SpawnMobj(x, y, z, MT_CYBRAKDEMON_ELECTRIC_BARRIER);
 		P_SetTarget(&elecmobj->target, mobj);
-		elecmobj->angle = FixedAngle(mthing->angle*FRACUNIT);;
+		elecmobj->angle = FixedAngle(mthing->angle<<FRACBITS);;
 		elecmobj->destscale = mobj->scale*2;
 		P_SetScale(elecmobj, elecmobj->destscale);
 	}
@@ -10354,7 +10773,9 @@ ML_EFFECT4 : Don't clip inside the ground
 		if (mthing->options & MTF_OBJECTSPECIAL)
 		{
 			mobj->flags &= ~MF_SCENERY;
-			mobj->fuse = mthing->angle + mobj->info->speed;
+			mobj->fuse = (16 - mthing->extrainfo) * (mthing->angle + mobj->info->speed) / 16;
+			if (mthing->options & MTF_EXTRA)
+				P_SetMobjState(mobj, mobj->info->meleestate);
 		}
 		// Use per-thing collision for spikes if the deaf flag isn't checked.
 		if (!(mthing->options & MTF_AMBUSH) && !metalrecording)
@@ -10371,7 +10792,9 @@ ML_EFFECT4 : Don't clip inside the ground
 		if (mthing->options & MTF_OBJECTSPECIAL)
 		{
 			mobj->flags &= ~MF_SCENERY;
-			mobj->fuse = mobj->info->speed;
+			mobj->fuse = (16 - mthing->extrainfo) * ((mthing->angle/360) + mobj->info->speed) / 16;
+			if (mthing->options & MTF_EXTRA)
+				P_SetMobjState(mobj, mobj->info->meleestate);
 		}
 		// Use per-thing collision for spikes if the deaf flag isn't checked.
 		if (!(mthing->options & MTF_AMBUSH) && !metalrecording)
@@ -10384,7 +10807,7 @@ ML_EFFECT4 : Don't clip inside the ground
 
 		// spawn base
 		{
-			const angle_t mobjangle = FixedAngle(mthing->angle*FRACUNIT); // the mobj's own angle hasn't been set quite yet so...
+			const angle_t mobjangle = FixedAngle(mthing->angle<<FRACBITS); // the mobj's own angle hasn't been set quite yet so...
 			const fixed_t baseradius = mobj->radius - mobj->scale;
 			mobj_t *base = P_SpawnMobj(
 					mobj->x - P_ReturnThrustX(mobj, mobjangle, baseradius),
@@ -10399,7 +10822,7 @@ ML_EFFECT4 : Don't clip inside the ground
 	}
 
 	//count 10 ring boxes into the number of rings equation too.
-	if (i == MT_RING_BOX)
+	if (i == MT_RING_BOX && nummaprings >= 0)
 		nummaprings += 10;
 
 	if (i == MT_BIGTUMBLEWEED || i == MT_LITTLETUMBLEWEED)
@@ -10452,7 +10875,7 @@ ML_EFFECT4 : Don't clip inside the ground
 	}
 
 	if (doangle)
-		mobj->angle = FixedAngle(mthing->angle*FRACUNIT);
+		mobj->angle = FixedAngle(mthing->angle<<FRACBITS);
 
 	// ignore MTF_ flags and return early
 	if (i == MT_NIGHTSBUMPER)
@@ -10480,10 +10903,7 @@ ML_EFFECT4 : Don't clip inside the ground
 			}
 
 			if (mobj->flags & MF_PUSHABLE)
-			{
 				mobj->flags &= ~MF_PUSHABLE;
-				mobj->flags2 |= MF2_STANDONME;
-			}
 
 			if ((mobj->flags & MF_MONITOR) && mobj->info->speed != 0)
 			{
@@ -10536,14 +10956,16 @@ ML_EFFECT4 : Don't clip inside the ground
 	mthing->mobj = mobj;
 }
 
-void P_SpawnHoopsAndRings(mapthing_t *mthing)
+void P_SpawnHoopsAndRings(mapthing_t *mthing, boolean bonustime)
 {
+	mobjtype_t ringthing = MT_RING;
 	mobj_t *mobj = NULL;
 	INT32 r, i;
 	fixed_t x, y, z, finalx, finaly, finalz;
 	sector_t *sec;
 	TVector v, *res;
 	angle_t closestangle, fa;
+	boolean nightsreplace = ((maptol & TOL_NIGHTS) && !G_IsSpecialStage(gamemap));
 
 	x = mthing->x << FRACBITS;
 	y = mthing->y << FRACBITS;
@@ -10816,105 +11238,6 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
 
 		return;
 	}
-	// Wing logo item.
-	else if (mthing->type == mobjinfo[MT_NIGHTSWING].doomednum)
-	{
-		z =
-#ifdef ESLOPE
-			sec->f_slope ? P_GetZAt(sec->f_slope, x, y) :
-#endif
-			sec->floorheight;
-		if (mthing->options >> ZSHIFT)
-			z += ((mthing->options >> ZSHIFT) << FRACBITS);
-
-		mthing->z = (INT16)(z>>FRACBITS);
-
-		mobj = P_SpawnMobj(x, y, z, MT_NIGHTSWING);
-		mobj->spawnpoint = mthing;
-
-		if (G_IsSpecialStage(gamemap) && useNightsSS)
-			P_SetMobjState(mobj, mobj->info->meleestate);
-		else if (maptol & TOL_XMAS)
-			P_SetMobjState(mobj, mobj->info->seestate);
-
-		mobj->angle = FixedAngle(mthing->angle*FRACUNIT);
-		mobj->flags2 |= MF2_AMBUSH;
-		mthing->mobj = mobj;
-	}
-	// All manners of rings and coins
-	else if (mthing->type == mobjinfo[MT_RING].doomednum || mthing->type == mobjinfo[MT_COIN].doomednum ||
-	         mthing->type == mobjinfo[MT_REDTEAMRING].doomednum || mthing->type == mobjinfo[MT_BLUETEAMRING].doomednum)
-	{
-		mobjtype_t ringthing = MT_RING;
-
-		// No rings in Ultimate!
-		if (ultimatemode && !(G_IsSpecialStage(gamemap) || maptol & TOL_NIGHTS))
-			return;
-
-		// Which ringthing to use
-		switch (mthing->type)
-		{
-			case 1800:
-				ringthing = MT_COIN;
-				break;
-			case 308: // No team rings in non-CTF
-				ringthing = (gametype == GT_CTF) ? MT_REDTEAMRING : MT_RING;
-				break;
-			case 309: // No team rings in non-CTF
-				ringthing = (gametype == GT_CTF) ? MT_BLUETEAMRING : MT_RING;
-				break;
-			default:
-				// Spawn rings as blue spheres in special stages, ala S3+K.
-				if (G_IsSpecialStage(gamemap) && useNightsSS)
-					ringthing = MT_BLUEBALL;
-				break;
-		}
-
-		// Set proper height
-		if (mthing->options & MTF_OBJECTFLIP)
-		{
-			z = (
-#ifdef ESLOPE
-			sec->c_slope ? P_GetZAt(sec->c_slope, x, y) :
-#endif
-			sec->ceilingheight) - mobjinfo[ringthing].height;
-			if (mthing->options >> ZSHIFT)
-				z -= ((mthing->options >> ZSHIFT) << FRACBITS);
-		}
-		else
-		{
-			z =
-#ifdef ESLOPE
-			sec->f_slope ? P_GetZAt(sec->f_slope, x, y) :
-#endif
-			sec->floorheight;
-			if (mthing->options >> ZSHIFT)
-				z += ((mthing->options >> ZSHIFT) << FRACBITS);
-		}
-
-		if (mthing->options & MTF_AMBUSH) // Special flag for rings
-		{
-			if (mthing->options & MTF_OBJECTFLIP)
-				z -= 24*FRACUNIT;
-			else
-				z += 24*FRACUNIT;
-		}
-
-		mthing->z = (INT16)(z>>FRACBITS);
-
-		mobj = P_SpawnMobj(x, y, z, ringthing);
-		mobj->spawnpoint = mthing;
-
-		if (mthing->options & MTF_OBJECTFLIP)
-		{
-			mobj->eflags |= MFE_VERTICALFLIP;
-			mobj->flags2 |= MF2_OBJECTFLIP;
-		}
-
-		mobj->angle = FixedAngle(mthing->angle*FRACUNIT);
-		mobj->flags2 |= MF2_AMBUSH;
-		mthing->mobj = mobj;
-	}
 	// ***
 	// Special placement patterns
 	// ***
@@ -10923,17 +11246,14 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
 	else if (mthing->type == 600 || mthing->type == 601)
 	{
 		INT32 dist = 64*FRACUNIT;
-		mobjtype_t ringthing = MT_RING;
 		if (mthing->type == 601)
 			dist = 128*FRACUNIT;
 
-		// No rings in Ultimate!
-		if (ultimatemode && !(G_IsSpecialStage(gamemap) || maptol & TOL_NIGHTS))
-			return;
+		if (ultimatemode)
+			return; // No rings in Ultimate!
 
-		// Spawn rings as blue spheres in special stages, ala S3+K.
-		if (G_IsSpecialStage(gamemap) && useNightsSS)
-			ringthing = MT_BLUEBALL;
+		if (nightsreplace)
+			ringthing = MT_NIGHTSSTAR;
 
 		for (r = 1; r <= 5; r++)
 		{
@@ -10969,31 +11289,31 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
 			mobj->angle = FixedAngle(mthing->angle*FRACUNIT);
 			if (mthing->options & MTF_AMBUSH)
 				mobj->flags2 |= MF2_AMBUSH;
+
+			if ((maptol & TOL_XMAS) && (ringthing == MT_NIGHTSSTAR))
+				P_SetMobjState(mobj, mobj->info->seestate);
 		}
 	}
 	// Diagonal rings (handles both types)
 	else if (mthing->type == 602 || mthing->type == 603) // Diagonal rings (5)
 	{
-		angle_t angle = FixedAngle(mthing->angle*FRACUNIT);
-		mobjtype_t ringthing = MT_RING;
 		INT32 iterations = 5;
 		if (mthing->type == 603)
 			iterations = 10;
 
-		// No rings in Ultimate!
-		if (ultimatemode && !(G_IsSpecialStage(gamemap) || maptol & TOL_NIGHTS))
-			return;
+		if (ultimatemode)
+			return; // No rings in Ultimate!
 
-		// Spawn rings as blue spheres in special stages, ala S3+K.
-		if (G_IsSpecialStage(gamemap) && useNightsSS)
-			ringthing = MT_BLUEBALL;
+		if (nightsreplace)
+			ringthing = MT_NIGHTSSTAR;
 
-		angle >>= ANGLETOFINESHIFT;
+		closestangle = FixedAngle(mthing->angle*FRACUNIT);
+		fa = (closestangle >> ANGLETOFINESHIFT);
 
 		for (r = 1; r <= iterations; r++)
 		{
-			x += FixedMul(64*FRACUNIT, FINECOSINE(angle));
-			y += FixedMul(64*FRACUNIT, FINESINE(angle));
+			x += FixedMul(64*FRACUNIT, FINECOSINE(fa));
+			y += FixedMul(64*FRACUNIT, FINESINE(fa));
 
 			if (mthing->options & MTF_OBJECTFLIP)
 			{
@@ -11024,9 +11344,12 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
 				mobj->flags2 |= MF2_OBJECTFLIP;
 			}
 
-			mobj->angle = FixedAngle(mthing->angle*FRACUNIT);
+			mobj->angle = closestangle;
 			if (mthing->options & MTF_AMBUSH)
 				mobj->flags2 |= MF2_AMBUSH;
+
+			if ((maptol & TOL_XMAS) && (ringthing == MT_NIGHTSSTAR))
+				P_SetMobjState(mobj, mobj->info->seestate);
 		}
 	}
 	// Rings of items (all six of them)
@@ -11034,7 +11357,6 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
 	{
 		INT32 numitems = 8;
 		INT32 size = 96*FRACUNIT;
-		mobjtype_t itemToSpawn = MT_NIGHTSWING;
 
 		if (mthing->type & 1)
 		{
@@ -11052,36 +11374,40 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
 
 		closestangle = FixedAngle(mthing->angle*FRACUNIT);
 
+		switch (mthing->type)
+		{
+			case 604:
+			case 605:
+				if (ultimatemode)
+					return; // No rings in Ultimate!
+				if (nightsreplace)
+					ringthing = MT_NIGHTSSTAR;
+				break;
+			case 608:
+			case 609:
+				/*ringthing = (i & 1) ? MT_RING : MT_BLUESPHERE; -- i == 0 is bluesphere
+				break;*/
+			case 606:
+			case 607:
+				ringthing = (nightsreplace) ? MT_NIGHTSCHIP : MT_BLUESPHERE;
+				break;
+			default:
+				break;
+		}
+
 		// Create the hoop!
 		for (i = 0; i < numitems; i++)
 		{
-			switch (mthing->type)
-			{
-				case 604:
-				case 605:
-					itemToSpawn = MT_RING;
-					break;
-				case 608:
-				case 609:
-					itemToSpawn = (i & 1) ? MT_NIGHTSWING : MT_RING;
-					break;
-				case 606:
-				case 607:
-					itemToSpawn = MT_NIGHTSWING;
-					break;
-				default:
-					break;
-			}
-
-			// No rings in Ultimate!
-			if (itemToSpawn == MT_RING)
+			if (mthing->type == 608 || mthing->type == 609)
 			{
-				if (ultimatemode && !(G_IsSpecialStage(gamemap) || (maptol & TOL_NIGHTS)))
-					continue;
-
-				// Spawn rings as blue spheres in special stages, ala S3+K.
-				if (G_IsSpecialStage(gamemap) && useNightsSS)
-					itemToSpawn = MT_BLUEBALL;
+				if (i & 1)
+				{
+					if (ultimatemode)
+						continue; // No rings in Ultimate!
+					ringthing = (nightsreplace) ? MT_NIGHTSSTAR : MT_RING;
+				}
+				else
+					ringthing = (nightsreplace) ? MT_NIGHTSCHIP : MT_BLUESPHERE;
 			}
 
 			fa = i*FINEANGLES/numitems;
@@ -11097,18 +11423,99 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
 			finaly = y + v[1];
 			finalz = z + v[2];
 
-			mobj = P_SpawnMobj(finalx, finaly, finalz, itemToSpawn);
+			mobj = P_SpawnMobj(finalx, finaly, finalz, ringthing);
 			mobj->z -= mobj->height/2;
 
-			if (itemToSpawn == MT_NIGHTSWING)
+			if (mthing->options & MTF_OBJECTFLIP)
 			{
-				if (G_IsSpecialStage(gamemap) && useNightsSS)
-					P_SetMobjState(mobj, mobj->info->meleestate);
-				else if ((maptol & TOL_XMAS))
-					P_SetMobjState(mobj, mobj->info->seestate);
+				mobj->eflags |= MFE_VERTICALFLIP;
+				mobj->flags2 |= MF2_OBJECTFLIP;
 			}
+
+			mobj->angle = closestangle;
+			if (mthing->options & MTF_AMBUSH)
+				mobj->flags2 |= MF2_AMBUSH;
+
+			if (bonustime && (ringthing == MT_BLUESPHERE || ringthing == MT_NIGHTSCHIP))
+				P_SetMobjState(mobj, mobj->info->raisestate);
+			else if ((maptol & TOL_XMAS) && (ringthing == MT_NIGHTSSTAR))
+				P_SetMobjState(mobj, mobj->info->seestate);
 		}
-		return;
+	}
+	// All manners of rings and coins
+	else
+	{
+
+		// Which ringthing to use
+		if (mthing->type == mobjinfo[MT_BLUESPHERE].doomednum)
+			ringthing = (nightsreplace) ? MT_NIGHTSCHIP : MT_BLUESPHERE;
+		else if (mthing->type == mobjinfo[MT_BOMBSPHERE].doomednum)
+			ringthing = MT_BOMBSPHERE;
+		else
+		{
+			if (ultimatemode)
+				return; // No rings in Ultimate!
+
+			if (nightsreplace)
+				ringthing = MT_NIGHTSSTAR;
+			else if (mthing->type == mobjinfo[MT_COIN].doomednum)
+				ringthing = MT_COIN;
+			else if (mthing->type == mobjinfo[MT_REDTEAMRING].doomednum) // No team rings in non-CTF
+				ringthing = (gametype == GT_CTF) ? MT_REDTEAMRING : MT_RING;
+			else if (mthing->type == mobjinfo[MT_BLUETEAMRING].doomednum) // Ditto
+				ringthing = (gametype == GT_CTF) ? MT_BLUETEAMRING : MT_RING;
+		}
+
+		// Set proper height
+		if (mthing->options & MTF_OBJECTFLIP)
+		{
+			z = (
+#ifdef ESLOPE
+			sec->c_slope ? P_GetZAt(sec->c_slope, x, y) :
+#endif
+			sec->ceilingheight) - mobjinfo[ringthing].height;
+			if (mthing->options >> ZSHIFT)
+				z -= ((mthing->options >> ZSHIFT) << FRACBITS);
+		}
+		else
+		{
+			z =
+#ifdef ESLOPE
+			sec->f_slope ? P_GetZAt(sec->f_slope, x, y) :
+#endif
+			sec->floorheight;
+			if (mthing->options >> ZSHIFT)
+				z += ((mthing->options >> ZSHIFT) << FRACBITS);
+		}
+
+		if (mthing->options & MTF_AMBUSH) // Special flag for rings
+		{
+			if (mthing->options & MTF_OBJECTFLIP)
+				z -= 24*FRACUNIT;
+			else
+				z += 24*FRACUNIT;
+		}
+
+		mthing->z = (INT16)(z>>FRACBITS);
+
+		mobj = P_SpawnMobj(x, y, z, ringthing);
+		mobj->spawnpoint = mthing;
+
+		if (mthing->options & MTF_OBJECTFLIP)
+		{
+			mobj->eflags |= MFE_VERTICALFLIP;
+			mobj->flags2 |= MF2_OBJECTFLIP;
+		}
+
+		mobj->angle = FixedAngle(mthing->angle*FRACUNIT);
+		mthing->mobj = mobj;
+		if (mthing->options & MTF_AMBUSH)
+			mobj->flags2 |= MF2_AMBUSH;
+
+		if (bonustime && (ringthing == MT_BLUESPHERE || ringthing == MT_NIGHTSCHIP))
+			P_SetMobjState(mobj, mobj->info->raisestate);
+		else if ((maptol & TOL_XMAS) && (ringthing == MT_NIGHTSSTAR))
+			P_SetMobjState(mobj, mobj->info->seestate);
 	}
 }
 
diff --git a/src/p_mobj.h b/src/p_mobj.h
index 2ca8ebd70670247c80c027f98478c57802478ffb..5c0408e1bc2a9e5b2a05d45b2985d060d3f4c444 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -175,8 +175,8 @@ typedef enum
 	MF2_SCATTER        = 1<<8,  // Thrown ring has scatter properties
 	MF2_BEYONDTHEGRAVE = 1<<9,  // Source of this missile has died and has since respawned.
 	MF2_SLIDEPUSH      = 1<<10, // MF_PUSHABLE that pushes continuously.
-	MF2_CLASSICPUSH    = 1<<11, // Drops straight down when object has negative Z.
-	MF2_STANDONME      = 1<<12, // While not pushable, stand on me anyway.
+	MF2_CLASSICPUSH    = 1<<11, // Drops straight down when object has negative momz.
+	MF2_INVERTAIMABLE  = 1<<12, // Flips whether it's targetable by A_LookForEnemies (enemies no, decoys yes)
 	MF2_INFLOAT        = 1<<13, // Floating to a height for a move, don't auto float to target's height.
 	MF2_DEBRIS         = 1<<14, // Splash ring from explosion ring
 	MF2_NIGHTSPULL     = 1<<15, // Attracted from a paraloop
@@ -254,6 +254,10 @@ typedef enum {
 	PCF_FOF = 4,
 	// Above MOVING FOF (this means we need to keep floorz up to date...)
 	PCF_MOVINGFOF = 8,
+	// Is rain.
+	PCF_RAIN = 16,
+	// Ran the thinker this tic.
+	PCF_THUNK = 32,
 } precipflag_t;
 // Map Object definition.
 typedef struct mobj_s
@@ -282,6 +286,8 @@ typedef struct mobj_s
 	// The closest interval over all contacted sectors (or things).
 	fixed_t floorz; // Nearest floor below.
 	fixed_t ceilingz; // Nearest ceiling above.
+	struct ffloor_s *floorrover; // FOF referred by floorz
+	struct ffloor_s *ceilingrover; // FOF referred by ceilingz
 
 	// For movement checking.
 	fixed_t radius;
@@ -314,7 +320,7 @@ typedef struct mobj_s
 	mobjtype_t type;
 	const mobjinfo_t *info; // &mobjinfo[mobj->type]
 
-	INT32 health; // for player this is rings + 1
+	INT32 health; // for player this is rings + 1 -- no it isn't, not any more!!
 
 	// Movement direction, movement generation (zig-zagging).
 	angle_t movedir; // dirtype_t 0-7; also used by Deton for up/down angle
@@ -398,6 +404,8 @@ typedef struct precipmobj_s
 	// The closest interval over all contacted sectors (or things).
 	fixed_t floorz; // Nearest floor below.
 	fixed_t ceilingz; // Nearest ceiling above.
+	struct ffloor_s *floorrover; // FOF referred by floorz
+	struct ffloor_s *ceilingrover; // FOF referred by ceilingz
 
 	// For movement checking.
 	fixed_t radius; // Fixed at 2*FRACUNIT
@@ -436,7 +444,7 @@ void P_MovePlayerToStarpost(INT32 playernum);
 void P_AfterPlayerSpawn(INT32 playernum);
 
 void P_SpawnMapThing(mapthing_t *mthing);
-void P_SpawnHoopsAndRings(mapthing_t *mthing);
+void P_SpawnHoopsAndRings(mapthing_t *mthing, boolean bonustime);
 void P_SpawnHoopOfSomething(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT32 number, mobjtype_t type, angle_t rotangle);
 void P_SpawnPrecipitation(void);
 void P_SpawnParaloop(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT32 number, mobjtype_t type, statenum_t nstate, angle_t rotangle, boolean spawncenter);
diff --git a/src/p_polyobj.c b/src/p_polyobj.c
index fd3237c9da8f71f53686270f6ff7ee5daa9fb465..8752104352c24e976568373719a2586cb3e6eca9 100644
--- a/src/p_polyobj.c
+++ b/src/p_polyobj.c
@@ -985,6 +985,8 @@ static void Polyobj_pushThing(polyobj_t *po, line_t *line, mobj_t *mo)
 		P_CheckPosition(mo, mo->x + momx, mo->y + momy);
 		mo->floorz = tmfloorz;
 		mo->ceilingz = tmceilingz;
+		mo->floorrover = tmfloorrover;
+		mo->ceilingrover = tmceilingrover;
 	}
 }
 
@@ -2853,6 +2855,165 @@ INT32 EV_DoPolyObjFlag(line_t *pfdata)
 	return 1;
 }
 
+void T_PolyObjFade(polyfade_t *th)
+{
+	boolean stillfading = false;
+	polyobj_t *po = Polyobj_GetForNum(th->polyObjNum);
+
+	if (!po)
+#ifdef RANGECHECK
+		I_Error("T_PolyObjFade: thinker has invalid id %d\n", th->polyObjNum);
+#else
+	{
+		CONS_Debug(DBG_POLYOBJ, "T_PolyObjFade: thinker with invalid id %d removed.\n", th->polyObjNum);
+		P_RemoveThinkerDelayed(&th->thinker);
+		return;
+	}
+#endif
+
+	// check for displacement due to override and reattach when possible
+	if (po->thinker == NULL)
+		po->thinker = &th->thinker;
+
+	stillfading = th->ticbased ? !(--(th->timer) <= 0)
+		: !((th->timer -= th->duration) <= 0);
+
+	if (th->timer <= 0)
+	{
+		po->translucency = max(min(th->destvalue, NUMTRANSMAPS), 0);
+
+		// remove thinker
+		if (po->thinker == &th->thinker)
+			po->thinker = NULL;
+		P_RemoveThinker(&th->thinker);
+	}
+	else
+	{
+		INT16 delta = abs(th->destvalue - th->sourcevalue);
+		INT32 duration = th->ticbased ? th->duration
+			: abs(FixedMul(FixedDiv(256, NUMTRANSMAPS), NUMTRANSMAPS - th->destvalue)
+				- FixedMul(FixedDiv(256, NUMTRANSMAPS), NUMTRANSMAPS - th->sourcevalue)); // speed-based internal counter duration: delta in 256 scale
+		fixed_t factor = min(FixedDiv(duration - th->timer, duration), 1*FRACUNIT);
+		if (th->destvalue < th->sourcevalue)
+			po->translucency = max(min(po->translucency, th->sourcevalue - (INT16)FixedMul(delta, factor)), th->destvalue);
+		else if (th->destvalue > th->sourcevalue)
+			po->translucency = min(max(po->translucency, th->sourcevalue + (INT16)FixedMul(delta, factor)), th->destvalue);
+	}
+
+	if (!stillfading)
+	{
+		// set render flags
+		if (po->translucency >= NUMTRANSMAPS) // invisible
+			po->flags &= ~POF_RENDERALL;
+		else
+			po->flags |= (po->spawnflags & POF_RENDERALL);
+
+		// set collision
+		if (th->docollision)
+		{
+			if (th->destvalue > th->sourcevalue) // faded out
+			{
+				po->flags &= ~POF_SOLID;
+				po->flags |= POF_NOSPECIALS;
+			}
+			else
+			{
+				po->flags |= (po->spawnflags & POF_SOLID);
+				if (!(po->spawnflags & POF_NOSPECIALS))
+					po->flags &= ~POF_NOSPECIALS;
+			}
+		}
+	}
+	else
+	{
+		if (po->translucency >= NUMTRANSMAPS)
+			// HACK: OpenGL renders fully opaque when >= NUMTRANSMAPS
+			po->translucency = NUMTRANSMAPS-1;
+
+		po->flags |= (po->spawnflags & POF_RENDERALL);
+
+		// set collision
+		if (th->docollision)
+		{
+			if (th->doghostfade)
+			{
+				po->flags &= ~POF_SOLID;
+				po->flags |= POF_NOSPECIALS;
+			}
+			else
+			{
+				po->flags |= (po->spawnflags & POF_SOLID);
+				if (!(po->spawnflags & POF_NOSPECIALS))
+					po->flags &= ~POF_NOSPECIALS;
+			}
+		}
+	}
+}
+
+INT32 EV_DoPolyObjFade(polyfadedata_t *pfdata)
+{
+	polyobj_t *po;
+	polyobj_t *oldpo;
+	polyfade_t *th;
+	INT32 start;
+
+	if (!(po = Polyobj_GetForNum(pfdata->polyObjNum)))
+	{
+		CONS_Debug(DBG_POLYOBJ, "EV_DoPolyObjFade: bad polyobj %d\n", pfdata->polyObjNum);
+		return 0;
+	}
+
+	// don't allow line actions to affect bad polyobjects
+	if (po->isBad)
+		return 0;
+
+	// already equal, nothing to do
+	if (po->translucency == pfdata->destvalue)
+		return 1;
+
+	if (po->thinker && po->thinker->function.acp1 == (actionf_p1)T_PolyObjFade)
+		P_RemoveThinker(po->thinker);
+
+	// create a new thinker
+	th = Z_Malloc(sizeof(polyfade_t), PU_LEVSPEC, NULL);
+	th->thinker.function.acp1 = (actionf_p1)T_PolyObjFade;
+	PolyObj_AddThinker(&th->thinker);
+	po->thinker = &th->thinker;
+
+	// set fields
+	th->polyObjNum = pfdata->polyObjNum;
+	th->sourcevalue = po->translucency;
+	th->destvalue = pfdata->destvalue;
+	th->docollision = pfdata->docollision;
+	th->doghostfade = pfdata->doghostfade;
+
+	if (pfdata->ticbased)
+	{
+		th->ticbased = true;
+		th->timer = th->duration = abs(pfdata->speed); // pfdata->speed is duration
+	}
+	else
+	{
+		th->ticbased = false;
+		th->timer = abs(FixedMul(FixedDiv(256, NUMTRANSMAPS), NUMTRANSMAPS - th->destvalue)
+			- FixedMul(FixedDiv(256, NUMTRANSMAPS), NUMTRANSMAPS - th->sourcevalue)); // delta converted to 256 scale, use as internal counter
+		th->duration = abs(pfdata->speed); // use th->duration as speed decrement
+	}
+
+	oldpo = po;
+
+	// apply action to mirroring polyobjects as well
+	start = 0;
+	while ((po = Polyobj_GetChild(oldpo, &start)))
+	{
+		pfdata->polyObjNum = po->id;
+		EV_DoPolyObjFade(pfdata);
+	}
+
+	// action was successful
+	return 1;
+}
+
 #endif // ifdef POLYOBJECTS
 
 // EOF
diff --git a/src/p_polyobj.h b/src/p_polyobj.h
index c9838a9224bb42aea8d64b473e0fa9781eaf3368..524518f2ac224a5815dfe96e22b5a146562fae16 100644
--- a/src/p_polyobj.h
+++ b/src/p_polyobj.h
@@ -207,6 +207,20 @@ typedef struct polydisplace_s
 	fixed_t oldHeights;
 } polydisplace_t;
 
+typedef struct polyfade_s
+{
+	thinker_t thinker; // must be first
+
+	INT32 polyObjNum;
+	INT32 sourcevalue;
+	INT32 destvalue;
+	boolean docollision;
+	boolean doghostfade;
+	boolean ticbased;
+	INT32 duration;
+	INT32 timer;
+} polyfade_t;
+
 //
 // Line Activation Data Structures
 //
@@ -266,6 +280,16 @@ typedef struct polydisplacedata_s
 	fixed_t dy;
 } polydisplacedata_t;
 
+typedef struct polyfadedata_s
+{
+	INT32 polyObjNum;
+	INT32 destvalue;
+	boolean docollision;
+	boolean doghostfade;
+	boolean ticbased;
+	INT32 speed;
+} polyfadedata_t;
+
 //
 // Functions
 //
@@ -287,6 +311,7 @@ void T_PolyDoorSlide(polyslidedoor_t *);
 void T_PolyDoorSwing(polyswingdoor_t *);
 void T_PolyObjDisplace  (polydisplace_t *);
 void T_PolyObjFlag  (polymove_t *);
+void T_PolyObjFade  (polyfade_t *);
 
 INT32 EV_DoPolyDoor(polydoordata_t *);
 INT32 EV_DoPolyObjMove(polymovedata_t *);
@@ -294,6 +319,7 @@ INT32 EV_DoPolyObjWaypoint(polywaypointdata_t *);
 INT32 EV_DoPolyObjRotate(polyrotdata_t *);
 INT32 EV_DoPolyObjDisplace(polydisplacedata_t *);
 INT32 EV_DoPolyObjFlag(struct line_s *);
+INT32 EV_DoPolyObjFade(polyfadedata_t *);
 
 
 //
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 029df08f4befdbe0f897ee781cb52038f099bcd8..6a07e513f20d2f20407d6ad24cd0063d7de65b31 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -21,6 +21,7 @@
 #include "p_local.h"
 #include "p_setup.h"
 #include "p_saveg.h"
+#include "r_data.h"
 #include "r_things.h"
 #include "r_state.h"
 #include "w_wad.h"
@@ -116,7 +117,8 @@ static void P_NetArchivePlayers(void)
 		WRITEANGLE(save_p, players[i].drawangle);
 		WRITEANGLE(save_p, players[i].awayviewaiming);
 		WRITEINT32(save_p, players[i].awayviewtics);
-		WRITEINT32(save_p, players[i].rings);
+		WRITEINT16(save_p, players[i].rings);
+		WRITEINT16(save_p, players[i].spheres);
 
 		WRITESINT8(save_p, players[i].pity);
 		WRITEINT32(save_p, players[i].currentweapon);
@@ -197,14 +199,24 @@ static void P_NetArchivePlayers(void)
 		WRITEUINT8(save_p, players[i].drilldelay);
 		WRITEUINT8(save_p, players[i].bonustime);
 		WRITEUINT8(save_p, players[i].mare);
+		WRITEUINT8(save_p, players[i].marelap);
+		WRITEUINT8(save_p, players[i].marebonuslap);
 
 		WRITEUINT32(save_p, players[i].marebegunat);
 		WRITEUINT32(save_p, players[i].startedtime);
 		WRITEUINT32(save_p, players[i].finishedtime);
+		WRITEUINT32(save_p, players[i].lapbegunat);
+		WRITEUINT32(save_p, players[i].lapstartedtime);
+		WRITEINT16(save_p, players[i].finishedspheres);
 		WRITEINT16(save_p, players[i].finishedrings);
 		WRITEUINT32(save_p, players[i].marescore);
 		WRITEUINT32(save_p, players[i].lastmarescore);
+		WRITEUINT32(save_p, players[i].totalmarescore);
 		WRITEUINT8(save_p, players[i].lastmare);
+		WRITEUINT8(save_p, players[i].lastmarelap);
+		WRITEUINT8(save_p, players[i].lastmarebonuslap);
+		WRITEUINT8(save_p, players[i].totalmarelap);
+		WRITEUINT8(save_p, players[i].totalmarebonuslap);
 		WRITEINT32(save_p, players[i].maxlink);
 		WRITEUINT8(save_p, players[i].texttimer);
 		WRITEUINT8(save_p, players[i].textvar);
@@ -303,7 +315,8 @@ static void P_NetUnArchivePlayers(void)
 		players[i].drawangle = READANGLE(save_p);
 		players[i].awayviewaiming = READANGLE(save_p);
 		players[i].awayviewtics = READINT32(save_p);
-		players[i].rings = READINT32(save_p);
+		players[i].rings = READINT16(save_p);
+		players[i].spheres = READINT16(save_p);
 
 		players[i].pity = READSINT8(save_p);
 		players[i].currentweapon = READINT32(save_p);
@@ -384,14 +397,24 @@ static void P_NetUnArchivePlayers(void)
 		players[i].drilldelay = READUINT8(save_p);
 		players[i].bonustime = (boolean)READUINT8(save_p);
 		players[i].mare = READUINT8(save_p);
+		players[i].marelap = READUINT8(save_p);
+		players[i].marebonuslap = READUINT8(save_p);
 
 		players[i].marebegunat = READUINT32(save_p);
 		players[i].startedtime = READUINT32(save_p);
 		players[i].finishedtime = READUINT32(save_p);
+		players[i].lapbegunat = READUINT32(save_p);
+		players[i].lapstartedtime = READUINT32(save_p);
+		players[i].finishedspheres = READINT16(save_p);
 		players[i].finishedrings = READINT16(save_p);
 		players[i].marescore = READUINT32(save_p);
 		players[i].lastmarescore = READUINT32(save_p);
+		players[i].totalmarescore = READUINT32(save_p);
 		players[i].lastmare = READUINT8(save_p);
+		players[i].lastmarelap = READUINT8(save_p);
+		players[i].lastmarebonuslap = READUINT8(save_p);
+		players[i].totalmarelap = READUINT8(save_p);
+		players[i].totalmarebonuslap = READUINT8(save_p);
 		players[i].maxlink = READINT32(save_p);
 		players[i].texttimer = READUINT8(save_p);
 		players[i].textvar = READUINT8(save_p);
@@ -451,6 +474,243 @@ static void P_NetUnArchivePlayers(void)
 	}
 }
 
+///
+/// Colormaps
+///
+
+static extracolormap_t *net_colormaps = NULL;
+static UINT32 num_net_colormaps = 0;
+static UINT32 num_ffloors = 0; // for loading
+
+// Copypasta from r_data.c AddColormapToList
+// But also check for equality and return the matching index
+static UINT32 CheckAddNetColormapToList(extracolormap_t *extra_colormap)
+{
+	extracolormap_t *exc, *exc_prev;
+	UINT32 i = 0;
+
+	if (!net_colormaps)
+	{
+		net_colormaps = R_CopyColormap(extra_colormap, false);
+		net_colormaps->next = 0;
+		net_colormaps->prev = 0;
+		num_net_colormaps = i+1;
+		return i;
+	}
+
+	for (exc = net_colormaps; exc; exc_prev = exc, exc = exc->next)
+	{
+		if (R_CheckEqualColormaps(exc, extra_colormap, true, true, true))
+			return i;
+		i++;
+	}
+
+	exc_prev->next = R_CopyColormap(extra_colormap, false);
+	extra_colormap->prev = exc_prev;
+	extra_colormap->next = 0;
+
+	num_net_colormaps = i+1;
+	return i;
+}
+
+static extracolormap_t *GetNetColormapFromList(UINT32 index)
+{
+	// For loading, we have to be tricky:
+	// We load the sectors BEFORE knowing the colormap values
+	// So if an index doesn't exist, fill our list with dummy colormaps
+	// until we get the index we want
+	// Then when we load the color data, we set up the dummy colormaps
+
+	extracolormap_t *exc, *last_exc = NULL;
+	UINT32 i = 0;
+
+	if (!net_colormaps) // initialize our list
+		net_colormaps = R_CreateDefaultColormap(false);
+
+	for (exc = net_colormaps; exc; last_exc = exc, exc = exc->next)
+	{
+		if (i++ == index)
+			return exc;
+	}
+
+
+	// LET'S HOPE that index is a sane value, because we create up to [index]
+	// entries in net_colormaps. At this point, we don't know
+	// what the total colormap count is
+	if (index >= numsectors*3 + num_ffloors)
+		// if every sector had a unique colormap change AND a fade color thinker which has two colormap entries
+		// AND every ffloor had a fade FOF thinker with one colormap entry
+		I_Error("Colormap %d from server is too high for sectors %d", index, (UINT32)numsectors);
+
+	// our index doesn't exist, so just make the entry
+	for (; i <= index; i++)
+	{
+		exc = R_CreateDefaultColormap(false);
+		if (last_exc)
+			last_exc->next = exc;
+		exc->prev = last_exc;
+		exc->next = NULL;
+		last_exc = exc;
+	}
+	return exc;
+}
+
+static void ClearNetColormaps(void)
+{
+	// We're actually Z_Freeing each entry here,
+	// so don't call this in P_NetUnArchiveColormaps (where entries will be used in-game)
+	extracolormap_t *exc, *exc_next;
+
+	for (exc = net_colormaps; exc; exc = exc_next)
+	{
+		exc_next = exc->next;
+		Z_Free(exc);
+	}
+	num_net_colormaps = 0;
+	num_ffloors = 0;
+	net_colormaps = NULL;
+}
+
+static void P_NetArchiveColormaps(void)
+{
+	// We save and then we clean up our colormap mess
+	extracolormap_t *exc, *exc_next;
+	UINT32 i = 0;
+	WRITEUINT32(save_p, num_net_colormaps); // save for safety
+
+	for (exc = net_colormaps; i < num_net_colormaps; i++, exc = exc_next)
+	{
+		// We must save num_net_colormaps worth of data
+		// So fill non-existent entries with default.
+		if (!exc)
+			exc = R_CreateDefaultColormap(false);
+
+		WRITEUINT8(save_p, exc->fadestart);
+		WRITEUINT8(save_p, exc->fadeend);
+		WRITEUINT8(save_p, exc->fog);
+
+		WRITEINT32(save_p, exc->rgba);
+		WRITEINT32(save_p, exc->fadergba);
+
+#ifdef EXTRACOLORMAPLUMPS
+		WRITESTRINGN(save_p, exc->lumpname, 9);
+#endif
+
+		exc_next = exc->next;
+		Z_Free(exc); // don't need anymore
+	}
+
+	num_net_colormaps = 0;
+	num_ffloors = 0;
+	net_colormaps = NULL;
+}
+
+static void P_NetUnArchiveColormaps(void)
+{
+	// When we reach this point, we already populated our list with
+	// dummy colormaps. Now that we are loading the color data,
+	// set up the dummies.
+	extracolormap_t *exc, *existing_exc, *exc_next = NULL;
+	UINT32 i = 0;
+
+	num_net_colormaps = READUINT32(save_p);
+
+	for (exc = net_colormaps; i < num_net_colormaps; i++, exc = exc_next)
+	{
+		UINT8 fadestart, fadeend, fog;
+		INT32 rgba, fadergba;
+#ifdef EXTRACOLORMAPLUMPS
+		char lumpname[9];
+#endif
+
+		fadestart = READUINT8(save_p);
+		fadeend = READUINT8(save_p);
+		fog = READUINT8(save_p);
+
+		rgba = READINT32(save_p);
+		fadergba = READINT32(save_p);
+
+#ifdef EXTRACOLORMAPLUMPS
+		READSTRINGN(save_p, lumpname, 9);
+
+		if (lumpname[0])
+		{
+			if (!exc)
+				// no point making a new entry since nothing points to it,
+				// but we needed to read the data so now continue
+				continue;
+
+			exc_next = exc->next; // this gets overwritten during our operations here, so get it now
+			existing_exc = R_ColormapForName(lumpname);
+			*exc = *existing_exc;
+			R_AddColormapToList(exc); // see HACK note below on why we're adding duplicates
+			continue;
+		}
+#endif
+
+		if (!exc)
+			// no point making a new entry since nothing points to it,
+			// but we needed to read the data so now continue
+			continue;
+
+		exc_next = exc->next; // this gets overwritten during our operations here, so get it now
+
+		exc->fadestart = fadestart;
+		exc->fadeend = fadeend;
+		exc->fog = fog;
+
+		exc->rgba = rgba;
+		exc->fadergba = fadergba;
+
+#ifdef EXTRACOLORMAPLUMPS
+		exc->lump = LUMPERROR;
+		exc->lumpname[0] = 0;
+#endif
+
+		existing_exc = R_GetColormapFromListByValues(rgba, fadergba, fadestart, fadeend, fog);
+
+		if (existing_exc)
+			exc->colormap = existing_exc->colormap;
+		else
+			// CONS_Debug(DBG_RENDER, "Creating Colormap: rgba(%d,%d,%d,%d) fadergba(%d,%d,%d,%d)\n",
+			// 	R_GetRgbaR(rgba), R_GetRgbaG(rgba), R_GetRgbaB(rgba), R_GetRgbaA(rgba),
+			//	R_GetRgbaR(fadergba), R_GetRgbaG(fadergba), R_GetRgbaB(fadergba), R_GetRgbaA(fadergba));
+			exc->colormap = R_CreateLightTable(exc);
+
+		// HACK: If this dummy is a duplicate, we're going to add it
+		// to the extra_colormaps list anyway. I think this is faster
+		// than going through every loaded sector and correcting their
+		// colormap address to the pre-existing one, PER net_colormap entry
+		R_AddColormapToList(exc);
+
+		if (i < num_net_colormaps-1 && !exc_next)
+			exc_next = R_CreateDefaultColormap(false);
+	}
+
+	// if we still have a valid net_colormap after iterating up to num_net_colormaps,
+	// some sector had a colormap index higher than num_net_colormaps. We done goofed or $$$ was corrupted.
+	// In any case, add them to the colormap list too so that at least the sectors' colormap
+	// addresses are valid and accounted properly
+	if (exc_next)
+	{
+		existing_exc = R_GetDefaultColormap();
+		for (exc = exc_next; exc; exc = exc->next)
+		{
+			exc->colormap = existing_exc->colormap; // all our dummies are default values
+			R_AddColormapToList(exc);
+		}
+	}
+
+	// Don't need these anymore
+	num_net_colormaps = 0;
+	num_ffloors = 0;
+	net_colormaps = NULL;
+}
+
+///
+/// World Archiving
+///
+
 #define SD_FLOORHT  0x01
 #define SD_CEILHT   0x02
 #define SD_FLOORPIC 0x04
@@ -465,10 +725,14 @@ static void P_NetUnArchivePlayers(void)
 #define SD_FYOFFS    0x02
 #define SD_CXOFFS    0x04
 #define SD_CYOFFS    0x08
-#define SD_TAG       0x10
-#define SD_FLOORANG  0x20
-#define SD_CEILANG   0x40
-#define SD_TAGLIST   0x80
+#define SD_FLOORANG  0x10
+#define SD_CEILANG   0x20
+#define SD_TAG       0x40
+#define SD_DIFF3     0x80
+
+// diff3 flags
+#define SD_TAGLIST   0x01
+#define SD_COLORMAP  0x02
 
 #define LD_FLAG     0x01
 #define LD_SPECIAL  0x02
@@ -501,7 +765,10 @@ static void P_NetArchiveWorld(void)
 	mapsidedef_t *msd;
 	maplinedef_t *mld;
 	const sector_t *ss = sectors;
-	UINT8 diff, diff2;
+	UINT8 diff, diff2, diff3;
+
+	// initialize colormap vars because paranoia
+	ClearNetColormaps();
 
 	WRITEUINT32(save_p, ARCHIVEBLOCK_WORLD);
 	put = save_p;
@@ -528,7 +795,7 @@ static void P_NetArchiveWorld(void)
 
 	for (i = 0; i < numsectors; i++, ss++, ms++)
 	{
-		diff = diff2 = 0;
+		diff = diff2 = diff3 = 0;
 		if (ss->floorheight != SHORT(ms->floorheight)<<FRACBITS)
 			diff |= SD_FLOORHT;
 		if (ss->ceilingheight != SHORT(ms->ceilingheight)<<FRACBITS)
@@ -562,7 +829,10 @@ static void P_NetArchiveWorld(void)
 		if (ss->tag != SHORT(ms->tag))
 			diff2 |= SD_TAG;
 		if (ss->nexttag != ss->spawn_nexttag || ss->firsttag != ss->spawn_firsttag)
-			diff2 |= SD_TAGLIST;
+			diff3 |= SD_TAGLIST;
+
+		if (ss->extra_colormap != ss->spawn_extra_colormap)
+			diff3 |= SD_COLORMAP;
 
 		// Check if any of the sector's FOFs differ from how they spawned
 		if (ss->ffloors)
@@ -579,6 +849,9 @@ static void P_NetArchiveWorld(void)
 			}
 		}
 
+		if (diff3)
+			diff2 |= SD_DIFF3;
+
 		if (diff2)
 			diff |= SD_DIFF2;
 
@@ -590,6 +863,8 @@ static void P_NetArchiveWorld(void)
 			WRITEUINT8(put, diff);
 			if (diff & SD_DIFF2)
 				WRITEUINT8(put, diff2);
+			if (diff2 & SD_DIFF3)
+				WRITEUINT8(put, diff3);
 			if (diff & SD_FLOORHT)
 				WRITEFIXED(put, ss->floorheight);
 			if (diff & SD_CEILHT)
@@ -610,18 +885,22 @@ static void P_NetArchiveWorld(void)
 				WRITEFIXED(put, ss->ceiling_xoffs);
 			if (diff2 & SD_CYOFFS)
 				WRITEFIXED(put, ss->ceiling_yoffs);
-			if (diff2 & SD_TAG) // save only the tag
-				WRITEINT16(put, ss->tag);
 			if (diff2 & SD_FLOORANG)
 				WRITEANGLE(put, ss->floorpic_angle);
 			if (diff2 & SD_CEILANG)
 				WRITEANGLE(put, ss->ceilingpic_angle);
-			if (diff2 & SD_TAGLIST) // save both firsttag and nexttag
+			if (diff2 & SD_TAG) // save only the tag
+				WRITEINT16(put, ss->tag);
+			if (diff3 & SD_TAGLIST) // save both firsttag and nexttag
 			{ // either of these could be changed even if tag isn't
 				WRITEINT32(put, ss->firsttag);
 				WRITEINT32(put, ss->nexttag);
 			}
 
+			if (diff3 & SD_COLORMAP)
+				WRITEUINT32(put, CheckAddNetColormapToList(ss->extra_colormap));
+					// returns existing index if already added, or appends to net_colormaps and returns new index
+
 			// Special case: save the stats of all modified ffloors along with their ffloor "number"s
 			// we don't bother with ffloors that haven't changed, that would just add to savegame even more than is really needed
 			if (diff & SD_FFLOORS)
@@ -658,7 +937,7 @@ static void P_NetArchiveWorld(void)
 	// do lines
 	for (i = 0; i < numlines; i++, mld++, li++)
 	{
-		diff = diff2 = 0;
+		diff = diff2 = diff3 = 0;
 
 		if (li->special != SHORT(mld->special))
 			diff |= LD_SPECIAL;
@@ -750,11 +1029,22 @@ static void P_NetUnArchiveWorld(void)
 	line_t *li;
 	side_t *si;
 	UINT8 *get;
-	UINT8 diff, diff2;
+	UINT8 diff, diff2, diff3;
 
 	if (READUINT32(save_p) != ARCHIVEBLOCK_WORLD)
 		I_Error("Bad $$$.sav at archive block World");
 
+	// initialize colormap vars because paranoia
+	ClearNetColormaps();
+
+	// count the level's ffloors so that colormap loading can have an upper limit
+	for (i = 0; i < numsectors; i++)
+	{
+		ffloor_t *rover;
+		for (rover = sectors[i].ffloors; rover; rover = rover->next)
+			num_ffloors++;
+	}
+
 	get = save_p;
 
 	for (;;)
@@ -772,6 +1062,10 @@ static void P_NetUnArchiveWorld(void)
 			diff2 = READUINT8(get);
 		else
 			diff2 = 0;
+		if (diff2 & SD_DIFF3)
+			diff3 = READUINT8(get);
+		else
+			diff3 = 0;
 
 		if (diff & SD_FLOORHT)
 			sectors[i].floorheight = READFIXED(get);
@@ -800,17 +1094,20 @@ static void P_NetUnArchiveWorld(void)
 			sectors[i].ceiling_xoffs = READFIXED(get);
 		if (diff2 & SD_CYOFFS)
 			sectors[i].ceiling_yoffs = READFIXED(get);
+		if (diff2 & SD_FLOORANG)
+			sectors[i].floorpic_angle  = READANGLE(get);
+		if (diff2 & SD_CEILANG)
+			sectors[i].ceilingpic_angle = READANGLE(get);
 		if (diff2 & SD_TAG)
 			sectors[i].tag = READINT16(get); // DON'T use P_ChangeSectorTag
-		if (diff2 & SD_TAGLIST)
+		if (diff3 & SD_TAGLIST)
 		{
 			sectors[i].firsttag = READINT32(get);
 			sectors[i].nexttag = READINT32(get);
 		}
-		if (diff2 & SD_FLOORANG)
-			sectors[i].floorpic_angle  = READANGLE(get);
-		if (diff2 & SD_CEILANG)
-			sectors[i].ceilingpic_angle = READANGLE(get);
+
+		if (diff3 & SD_COLORMAP)
+			sectors[i].extra_colormap = GetNetColormapFromList(READUINT32(get));
 
 		if (diff & SD_FFLOORS)
 		{
@@ -869,6 +1166,9 @@ static void P_NetUnArchiveWorld(void)
 			diff2 = READUINT8(get);
 		else
 			diff2 = 0;
+
+		diff3 = 0;
+
 		if (diff & LD_FLAG)
 			li->flags = READINT16(get);
 		if (diff & LD_SPECIAL)
@@ -950,11 +1250,13 @@ typedef enum
 	MD2_EXTVAL1     = 1<<5,
 	MD2_EXTVAL2     = 1<<6,
 	MD2_HNEXT       = 1<<7,
-#ifdef ESLOPE
 	MD2_HPREV       = 1<<8,
-	MD2_SLOPE       = 1<<9
+	MD2_FLOORROVER  = 1<<9,
+#ifdef ESLOPE
+	MD2_CEILINGROVER = 1<<10,
+	MD2_SLOPE        = 1<<11
 #else
-	MD2_HPREV       = 1<<8
+	MD2_CEILINGROVER = 1<<10
 #endif
 } mobj_diff2_t;
 
@@ -974,6 +1276,7 @@ typedef enum
 	tc_bouncecheese,
 	tc_startcrumble,
 	tc_marioblock,
+	tc_marioblockchecker,
 	tc_spikesector,
 	tc_floatsector,
 	tc_bridgethinker,
@@ -988,6 +1291,8 @@ typedef enum
 	tc_noenemies,
 	tc_eachtime,
 	tc_disappear,
+	tc_fade,
+	tc_fadecolormap,
 	tc_planedisplace,
 #ifdef POLYOBJECTS
 	tc_polyrotate, // haleyjd 03/26/06: polyobjects
@@ -997,6 +1302,7 @@ typedef enum
 	tc_polyswingdoor,
 	tc_polyflag,
 	tc_polydisplace,
+	tc_polyfade,
 #endif
 	tc_end
 } specials_e;
@@ -1148,6 +1454,10 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
 		diff2 |= MD2_HNEXT;
 	if (mobj->hprev)
 		diff2 |= MD2_HPREV;
+	if (mobj->floorrover)
+		diff2 |= MD2_FLOORROVER;
+	if (mobj->ceilingrover)
+		diff2 |= MD2_CEILINGROVER;
 #ifdef ESLOPE
 	if (mobj->standingslope)
 		diff2 |= MD2_SLOPE;
@@ -1171,6 +1481,46 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
 	WRITEFIXED(save_p, mobj->floorz);
 	WRITEFIXED(save_p, mobj->ceilingz);
 
+	if (diff2 & MD2_FLOORROVER)
+	{
+		ffloor_t *rover;
+		size_t i = 0;
+		UINT32 roverindex = 0;
+
+		for (rover = mobj->floorrover->target->ffloors; rover; rover = rover->next)
+		{
+			if (rover == mobj->floorrover)
+			{
+				roverindex = i;
+				break;
+			}
+			i++;
+		}
+
+		WRITEUINT32(save_p, (UINT32)(mobj->floorrover->target - sectors));
+		WRITEUINT32(save_p, rover ? roverindex : i); // store max index to denote invalid ffloor ref
+	}
+
+	if (diff2 & MD2_CEILINGROVER)
+	{
+		ffloor_t *rover;
+		size_t i = 0;
+		UINT32 roverindex = 0;
+
+		for (rover = mobj->ceilingrover->target->ffloors; rover; rover = rover->next)
+		{
+			if (rover == mobj->ceilingrover)
+			{
+				roverindex = i;
+				break;
+			}
+			i++;
+		}
+
+		WRITEUINT32(save_p, (UINT32)(mobj->ceilingrover->target - sectors));
+		WRITEUINT32(save_p, rover ? roverindex : i); // store max index to denote invalid ffloor ref
+	}
+
 	if (diff & MD_SPAWNPOINT)
 	{
 		size_t z;
@@ -1289,7 +1639,10 @@ static void SaveSpecialLevelThinker(const thinker_t *th, const UINT8 type)
 	size_t i;
 	WRITEUINT8(save_p, type);
 	for (i = 0; i < 16; i++)
+	{
 		WRITEFIXED(save_p, ht->vars[i]); //var[16]
+		WRITEFIXED(save_p, ht->var2s[i]); //var[16]
+	}
 	WRITEUINT32(save_p, SaveLine(ht->sourceline));
 	WRITEUINT32(save_p, SaveSector(ht->sector));
 }
@@ -1515,8 +1868,11 @@ static void SaveLightlevelThinker(const thinker_t *th, const UINT8 type)
 	const lightlevel_t *ht = (const void *)th;
 	WRITEUINT8(save_p, type);
 	WRITEUINT32(save_p, SaveSector(ht->sector));
-	WRITEINT32(save_p, ht->destlevel);
-	WRITEINT32(save_p, ht->speed);
+	WRITEINT16(save_p, ht->sourcelevel);
+	WRITEINT16(save_p, ht->destlevel);
+	WRITEFIXED(save_p, ht->fixedcurlevel);
+	WRITEFIXED(save_p, ht->fixedpertic);
+	WRITEINT32(save_p, ht->timer);
 }
 
 //
@@ -1552,6 +1908,51 @@ static void SaveDisappearThinker(const thinker_t *th, const UINT8 type)
 	WRITEINT32(save_p, ht->exists);
 }
 
+//
+// SaveFadeThinker
+//
+// Saves a fade_t thinker
+//
+static void SaveFadeThinker(const thinker_t *th, const UINT8 type)
+{
+	const fade_t *ht = (const void *)th;
+	WRITEUINT8(save_p, type);
+	WRITEUINT32(save_p, CheckAddNetColormapToList(ht->dest_exc));
+	WRITEUINT32(save_p, ht->sectornum);
+	WRITEUINT32(save_p, ht->ffloornum);
+	WRITEINT32(save_p, ht->alpha);
+	WRITEINT16(save_p, ht->sourcevalue);
+	WRITEINT16(save_p, ht->destvalue);
+	WRITEINT16(save_p, ht->destlightlevel);
+	WRITEINT16(save_p, ht->speed);
+	WRITEUINT8(save_p, (UINT8)ht->ticbased);
+	WRITEINT32(save_p, ht->timer);
+	WRITEUINT8(save_p, ht->doexists);
+	WRITEUINT8(save_p, ht->dotranslucent);
+	WRITEUINT8(save_p, ht->dolighting);
+	WRITEUINT8(save_p, ht->docolormap);
+	WRITEUINT8(save_p, ht->docollision);
+	WRITEUINT8(save_p, ht->doghostfade);
+	WRITEUINT8(save_p, ht->exactalpha);
+}
+
+//
+// SaveFadeColormapThinker
+//
+// Saves a fadecolormap_t thinker
+//
+static void SaveFadeColormapThinker(const thinker_t *th, const UINT8 type)
+{
+	const fadecolormap_t *ht = (const void *)th;
+	WRITEUINT8(save_p, type);
+	WRITEUINT32(save_p, SaveSector(ht->sector));
+	WRITEUINT32(save_p, CheckAddNetColormapToList(ht->source_exc));
+	WRITEUINT32(save_p, CheckAddNetColormapToList(ht->dest_exc));
+	WRITEUINT8(save_p, (UINT8)ht->ticbased);
+	WRITEINT32(save_p, ht->duration);
+	WRITEINT32(save_p, ht->timer);
+}
+
 //
 // SavePlaneDisplaceThinker
 //
@@ -1677,6 +2078,20 @@ static void SavePolydisplaceThinker(const thinker_t *th, const UINT8 type)
 	WRITEFIXED(save_p, ht->oldHeights);
 }
 
+static void SavePolyfadeThinker(const thinker_t *th, const UINT8 type)
+{
+	const polyfade_t *ht = (const void *)th;
+	WRITEUINT8(save_p, type);
+	WRITEINT32(save_p, ht->polyObjNum);
+	WRITEINT32(save_p, ht->sourcevalue);
+	WRITEINT32(save_p, ht->destvalue);
+	WRITEUINT8(save_p, (UINT8)ht->docollision);
+	WRITEUINT8(save_p, (UINT8)ht->doghostfade);
+	WRITEUINT8(save_p, (UINT8)ht->ticbased);
+	WRITEINT32(save_p, ht->duration);
+	WRITEINT32(save_p, ht->timer);
+}
+
 #endif
 /*
 //
@@ -1706,8 +2121,7 @@ static void P_NetArchiveThinkers(void)
 	for (th = thinkercap.next; th != &thinkercap; th = th->next)
 	{
 		if (!(th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed
-		 || th->function.acp1 == (actionf_p1)P_RainThinker
-		 || th->function.acp1 == (actionf_p1)P_SnowThinker))
+		 || th->function.acp1 == (actionf_p1)P_NullPrecipThinker))
 			numsaved++;
 
 		if (th->function.acp1 == (actionf_p1)P_MobjThinker)
@@ -1716,8 +2130,7 @@ static void P_NetArchiveThinkers(void)
 			continue;
 		}
 #ifdef PARANOIA
-		else if (th->function.acp1 == (actionf_p1)P_RainThinker
-			|| th->function.acp1 == (actionf_p1)P_SnowThinker);
+		else if (th->function.acp1 == (actionf_p1)P_NullPrecipThinker);
 #endif
 		else if (th->function.acp1 == (actionf_p1)T_MoveCeiling)
 		{
@@ -1819,6 +2232,11 @@ static void P_NetArchiveThinkers(void)
 			SaveSpecialLevelThinker(th, tc_marioblock);
 			continue;
 		}
+		else if (th->function.acp1 == (actionf_p1)T_MarioBlockChecker)
+		{
+			SaveSpecialLevelThinker(th, tc_marioblockchecker);
+			continue;
+		}
 		else if (th->function.acp1 == (actionf_p1)T_SpikeSector)
 		{
 			SaveSpecialLevelThinker(th, tc_spikesector);
@@ -1854,7 +2272,16 @@ static void P_NetArchiveThinkers(void)
 			SaveDisappearThinker(th, tc_disappear);
 			continue;
 		}
-
+		else if (th->function.acp1 == (actionf_p1)T_Fade)
+		{
+			SaveFadeThinker(th, tc_fade);
+			continue;
+		}
+		else if (th->function.acp1 == (actionf_p1)T_FadeColormap)
+		{
+			SaveFadeColormapThinker(th, tc_fadecolormap);
+			continue;
+		}
 		else if (th->function.acp1 == (actionf_p1)T_PlaneDisplace)
 		{
 			SavePlaneDisplaceThinker(th, tc_planedisplace);
@@ -1896,6 +2323,11 @@ static void P_NetArchiveThinkers(void)
 			SavePolydisplaceThinker(th, tc_polydisplace);
 			continue;
 		}
+		else if (th->function.acp1 == (actionf_p1)T_PolyObjFade)
+		{
+			SavePolyfadeThinker(th, tc_polyfade);
+			continue;
+		}
 #endif
 #ifdef PARANOIA
 		else if (th->function.acv != P_RemoveThinkerDelayed) // wait garbage collection
@@ -1967,6 +2399,7 @@ static void LoadMobjThinker(actionf_p1 thinker)
 	UINT16 diff2;
 	INT32 i;
 	fixed_t z, floorz, ceilingz;
+	ffloor_t *floorrover = NULL, *ceilingrover = NULL;
 
 	diff = READUINT32(save_p);
 	if (diff & MD_MORE)
@@ -1980,13 +2413,45 @@ static void LoadMobjThinker(actionf_p1 thinker)
 	floorz = READFIXED(save_p);
 	ceilingz = READFIXED(save_p);
 
+	if (diff2 & MD2_FLOORROVER)
+	{
+		size_t floor_sectornum = (size_t)READUINT32(save_p);
+		size_t floor_rovernum = (size_t)READUINT32(save_p);
+		ffloor_t *rover = NULL;
+		size_t rovernum = 0;
+
+		for (rover = sectors[floor_sectornum].ffloors; rover; rover = rover->next)
+		{
+			if (rovernum == floor_rovernum)
+				break;
+			rovernum++;
+		}
+		floorrover = rover;
+	}
+
+	if (diff2 & MD2_CEILINGROVER)
+	{
+		size_t ceiling_sectornum = (size_t)READUINT32(save_p);
+		size_t ceiling_rovernum = (size_t)READUINT32(save_p);
+		ffloor_t *rover = NULL;
+		size_t rovernum = 0;
+
+		for (rover = sectors[ceiling_sectornum].ffloors; rover; rover = rover->next)
+		{
+			if (rovernum == ceiling_rovernum)
+				break;
+			rovernum++;
+		}
+		ceilingrover = rover;
+	}
+
 	if (diff & MD_SPAWNPOINT)
 	{
 		UINT16 spawnpointnum = READUINT16(save_p);
 
 		if (mapthings[spawnpointnum].type == 1705 || mapthings[spawnpointnum].type == 1713) // NiGHTS Hoop special case
 		{
-			P_SpawnHoopsAndRings(&mapthings[spawnpointnum]);
+			P_SpawnHoopsAndRings(&mapthings[spawnpointnum], false);
 			return;
 		}
 
@@ -2004,6 +2469,8 @@ static void LoadMobjThinker(actionf_p1 thinker)
 	mobj->z = z;
 	mobj->floorz = floorz;
 	mobj->ceilingz = ceilingz;
+	mobj->floorrover = floorrover;
+	mobj->ceilingrover = ceilingrover;
 
 	if (diff & MD_TYPE)
 		mobj->type = READUINT32(save_p);
@@ -2214,7 +2681,10 @@ static void LoadSpecialLevelThinker(actionf_p1 thinker, UINT8 floorOrCeiling)
 	size_t i;
 	ht->thinker.function.acp1 = thinker;
 	for (i = 0; i < 16; i++)
+	{
 		ht->vars[i] = READFIXED(save_p); //var[16]
+		ht->var2s[i] = READFIXED(save_p); //var[16]
+	}
 	ht->sourceline = LoadLine(READUINT32(save_p));
 	ht->sector = LoadSector(READUINT32(save_p));
 
@@ -2488,8 +2958,11 @@ static inline void LoadLightlevelThinker(actionf_p1 thinker)
 	lightlevel_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
 	ht->thinker.function.acp1 = thinker;
 	ht->sector = LoadSector(READUINT32(save_p));
-	ht->destlevel = READINT32(save_p);
-	ht->speed = READINT32(save_p);
+	ht->sourcelevel = READINT16(save_p);
+	ht->destlevel = READINT16(save_p);
+	ht->fixedcurlevel = READFIXED(save_p);
+	ht->fixedpertic = READFIXED(save_p);
+	ht->timer = READINT32(save_p);
 	if (ht->sector)
 		ht->sector->lightingdata = ht;
 	P_AddThinker(&ht->thinker);
@@ -2530,6 +3003,72 @@ static inline void LoadDisappearThinker(actionf_p1 thinker)
 	P_AddThinker(&ht->thinker);
 }
 
+//
+// LoadFadeThinker
+//
+// Loads a fade_t thinker
+//
+static inline void LoadFadeThinker(actionf_p1 thinker)
+{
+	sector_t *ss;
+	fade_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
+	ht->thinker.function.acp1 = thinker;
+	ht->dest_exc = GetNetColormapFromList(READUINT32(save_p));
+	ht->sectornum = READUINT32(save_p);
+	ht->ffloornum = READUINT32(save_p);
+	ht->alpha = READINT32(save_p);
+	ht->sourcevalue = READINT16(save_p);
+	ht->destvalue = READINT16(save_p);
+	ht->destlightlevel = READINT16(save_p);
+	ht->speed = READINT16(save_p);
+	ht->ticbased = (boolean)READUINT8(save_p);
+	ht->timer = READINT32(save_p);
+	ht->doexists = READUINT8(save_p);
+	ht->dotranslucent = READUINT8(save_p);
+	ht->dolighting = READUINT8(save_p);
+	ht->docolormap = READUINT8(save_p);
+	ht->docollision = READUINT8(save_p);
+	ht->doghostfade = READUINT8(save_p);
+	ht->exactalpha = READUINT8(save_p);
+
+	ss = LoadSector(ht->sectornum);
+	if (ss)
+	{
+		size_t j = 0; // ss->ffloors is saved as ffloor #0, ss->ffloors->next is #1, etc
+		ffloor_t *rover;
+		for (rover = ss->ffloors; rover; rover = rover->next)
+		{
+			if (j == ht->ffloornum)
+			{
+				ht->rover = rover;
+				rover->fadingdata = ht;
+				break;
+			}
+			j++;
+		}
+	}
+	P_AddThinker(&ht->thinker);
+}
+
+// LoadFadeColormapThinker
+//
+// Loads a fadecolormap_t from a save game
+//
+static inline void LoadFadeColormapThinker(actionf_p1 thinker)
+{
+	fadecolormap_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
+	ht->thinker.function.acp1 = thinker;
+	ht->sector = LoadSector(READUINT32(save_p));
+	ht->source_exc = GetNetColormapFromList(READUINT32(save_p));
+	ht->dest_exc = GetNetColormapFromList(READUINT32(save_p));
+	ht->ticbased = (boolean)READUINT8(save_p);
+	ht->duration = READINT32(save_p);
+	ht->timer = READINT32(save_p);
+	if (ht->sector)
+		ht->sector->fadecolormapdata = ht;
+	P_AddThinker(&ht->thinker);
+}
+
 //
 // LoadPlaneDisplaceThinker
 //
@@ -2667,6 +3206,26 @@ static inline void LoadPolydisplaceThinker(actionf_p1 thinker)
 	ht->oldHeights = READFIXED(save_p);
 	P_AddThinker(&ht->thinker);
 }
+
+//
+// LoadPolyfadeThinker
+//
+// Loads a polyfadet_t thinker
+//
+static void LoadPolyfadeThinker(actionf_p1 thinker)
+{
+	polyfade_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
+	ht->thinker.function.acp1 = thinker;
+	ht->polyObjNum = READINT32(save_p);
+	ht->sourcevalue = READINT32(save_p);
+	ht->destvalue = READINT32(save_p);
+	ht->docollision = (boolean)READUINT8(save_p);
+	ht->doghostfade = (boolean)READUINT8(save_p);
+	ht->ticbased = (boolean)READUINT8(save_p);
+	ht->duration = READINT32(save_p);
+	ht->timer = READINT32(save_p);
+	P_AddThinker(&ht->thinker);
+}
 #endif
 
 /*
@@ -2716,7 +3275,7 @@ static void P_NetUnArchiveThinkers(void)
 	// clear sector thinker pointers so they don't point to non-existant thinkers for all of eternity
 	for (i = 0; i < numsectors; i++)
 	{
-		sectors[i].floordata = sectors[i].ceilingdata = sectors[i].lightingdata = NULL;
+		sectors[i].floordata = sectors[i].ceilingdata = sectors[i].lightingdata = sectors[i].fadecolormapdata = NULL;
 	}
 
 	// read in saved thinkers
@@ -2804,6 +3363,10 @@ static void P_NetUnArchiveThinkers(void)
 				LoadSpecialLevelThinker((actionf_p1)T_MarioBlock, 3);
 				break;
 
+			case tc_marioblockchecker:
+				LoadSpecialLevelThinker((actionf_p1)T_MarioBlockChecker, 0);
+				break;
+
 			case tc_spikesector:
 				LoadSpecialLevelThinker((actionf_p1)T_SpikeSector, 0);
 				break;
@@ -2833,6 +3396,14 @@ static void P_NetUnArchiveThinkers(void)
 				LoadDisappearThinker((actionf_p1)T_Disappear);
 				break;
 
+			case tc_fade:
+				LoadFadeThinker((actionf_p1)T_Fade);
+				break;
+
+			case tc_fadecolormap:
+				LoadFadeColormapThinker((actionf_p1)T_FadeColormap);
+				break;
+
 			case tc_planedisplace:
 				LoadPlaneDisplaceThinker((actionf_p1)T_PlaneDisplace);
 				break;
@@ -2864,6 +3435,10 @@ static void P_NetUnArchiveThinkers(void)
 			case tc_polydisplace:
 				LoadPolydisplaceThinker((actionf_p1)T_PolyObjDisplace);
 				break;
+
+			case tc_polyfade:
+				LoadPolyfadeThinker((actionf_p1)T_PolyObjFade);
+				break;
 #endif
 			case tc_scroll:
 				LoadScrollThinker((actionf_p1)T_Scroll);
@@ -3261,7 +3836,7 @@ static void P_NetArchiveMisc(void)
 	WRITEUINT32(save_p, tokenlist);
 
 	WRITEUINT32(save_p, leveltime);
-	WRITEUINT32(save_p, totalrings);
+	WRITEUINT32(save_p, ssspheres);
 	WRITEINT16(save_p, lastmap);
 
 	WRITEUINT16(save_p, emeralds);
@@ -3338,7 +3913,7 @@ static inline boolean P_NetUnArchiveMisc(void)
 
 	// get the time
 	leveltime = READUINT32(save_p);
-	totalrings = READUINT32(save_p);
+	ssspheres = READUINT32(save_p);
 	lastmap = READINT16(save_p);
 
 	emeralds = READUINT16(save_p);
@@ -3416,6 +3991,7 @@ void P_SaveNetGame(void)
 #endif
 		P_NetArchiveThinkers();
 		P_NetArchiveSpecials();
+		P_NetArchiveColormaps();
 	}
 #ifdef HAVE_BLUA
 	LUA_Archive();
@@ -3458,6 +4034,7 @@ boolean P_LoadNetGame(void)
 #endif
 		P_NetUnArchiveThinkers();
 		P_NetUnArchiveSpecials();
+		P_NetUnArchiveColormaps();
 		P_RelinkPointers();
 		P_FinishMobjs();
 	}
diff --git a/src/p_setup.c b/src/p_setup.c
index a5544c26b74f57e11b06c318d047461dc142808c..eb25c51c81a46844b1d83784fd482eafb3cec594 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -225,6 +225,7 @@ static void P_ClearSingleMapHeaderInfo(INT16 i)
 	mapheaderinfo[num]->unlockrequired = -1;
 	mapheaderinfo[num]->levelselect = 0;
 	mapheaderinfo[num]->bonustype = 0;
+	mapheaderinfo[num]->maxbonuslives = -1;
 	mapheaderinfo[num]->levelflags = 0;
 	mapheaderinfo[num]->menuflags = 0;
 #if 1 // equivalent to "FlickyList = DEMO"
@@ -677,6 +678,7 @@ static void P_LoadRawSectors(UINT8 *data, size_t i)
 		ss->ceilingpic = P_AddLevelFlat(ms->ceilingpic, foundflats);
 
 		ss->lightlevel = SHORT(ms->lightlevel);
+		ss->spawn_lightlevel = SHORT(ms->lightlevel);
 		ss->special = SHORT(ms->special);
 		ss->tag = SHORT(ms->tag);
 		ss->nexttag = ss->firsttag = -1;
@@ -712,12 +714,12 @@ static void P_LoadRawSectors(UINT8 *data, size_t i)
 		ss->moved = true;
 
 		ss->extra_colormap = NULL;
+		ss->spawn_extra_colormap = NULL;
 
 		ss->floor_xoffs = ss->ceiling_xoffs = ss->floor_yoffs = ss->ceiling_yoffs = 0;
 		ss->spawn_flr_xoffs = ss->spawn_ceil_xoffs = ss->spawn_flr_yoffs = ss->spawn_ceil_yoffs = 0;
 		ss->floorpic_angle = ss->ceilingpic_angle = 0;
 		ss->spawn_flrpic_angle = ss->spawn_ceilpic_angle = 0;
-		ss->bottommap = ss->midmap = ss->topmap = -1;
 		ss->gravity = NULL;
 		ss->cullheight = NULL;
 		ss->verticalflip = false;
@@ -808,7 +810,7 @@ void P_ReloadRings(void)
 	mapthing_t *hoopsToRespawn[4096];
 	mapthing_t *mt = mapthings;
 
-	// scan the thinkers to find rings/wings/hoops to unset
+	// scan the thinkers to find rings/spheres/hoops to unset
 	for (th = thinkercap.next; th != &thinkercap; th = th->next)
 	{
 		if (th->function.acp1 != (actionf_p1)P_MobjThinker)
@@ -826,7 +828,9 @@ void P_ReloadRings(void)
 			}
 			continue;
 		}
-		if (!(mo->type == MT_RING || mo->type == MT_NIGHTSWING || mo->type == MT_COIN || mo->type == MT_BLUEBALL))
+		if (!(mo->type == MT_RING || mo->type == MT_COIN
+			|| mo->type == MT_BLUESPHERE || mo->type == MT_BOMBSPHERE
+			|| mo->type == MT_NIGHTSCHIP || mo->type == MT_NIGHTSSTAR))
 			continue;
 
 		// Don't auto-disintegrate things being pulled to us
@@ -840,9 +844,10 @@ void P_ReloadRings(void)
 	for (i = 0; i < nummapthings; i++, mt++)
 	{
 		// Notice an omission? We handle hoops differently.
-		if (mt->type == 300 || mt->type == 308 || mt->type == 309
-		 || mt->type == 1706 || (mt->type >= 600 && mt->type <= 609)
-		 || mt->type == 1800)
+		if (mt->type == mobjinfo[MT_RING].doomednum || mt->type == mobjinfo[MT_COIN].doomednum
+		 || mt->type == mobjinfo[MT_REDTEAMRING].doomednum || mt->type == mobjinfo[MT_BLUETEAMRING].doomednum
+		 || mt->type == mobjinfo[MT_BLUESPHERE].doomednum || mt->type == mobjinfo[MT_BOMBSPHERE].doomednum
+		 || (mt->type >= 600 && mt->type <= 609)) // circles and diagonals
 		{
 			mt->mobj = NULL;
 
@@ -850,12 +855,46 @@ void P_ReloadRings(void)
 			mt->z = (INT16)(R_PointInSubsector(mt->x << FRACBITS, mt->y << FRACBITS)
 				->sector->floorheight>>FRACBITS);
 
-			P_SpawnHoopsAndRings (mt);
+			P_SpawnHoopsAndRings(mt,
+#ifdef MANIASPHERES
+				true);
+#else
+				!G_IsSpecialStage(gamemap)); // prevent flashing spheres in special stages
+#endif
 		}
 	}
 	for (i = 0; i < numHoops; i++)
 	{
-		P_SpawnHoopsAndRings(hoopsToRespawn[i]);
+		P_SpawnHoopsAndRings(hoopsToRespawn[i], false);
+	}
+}
+
+void P_SwitchSpheresBonusMode(boolean bonustime)
+{
+	mobj_t *mo;
+	thinker_t *th;
+
+#ifndef MANIASPHERES
+	if (G_IsSpecialStage(gamemap)) // prevent flashing spheres in special stages
+		return;
+#endif
+
+	// scan the thinkers to find spheres to switch
+	for (th = thinkercap.next; th != &thinkercap; th = th->next)
+	{
+		if (th->function.acp1 != (actionf_p1)P_MobjThinker)
+			continue;
+
+		mo = (mobj_t *)th;
+
+		if (mo->type != MT_BLUESPHERE && mo->type != MT_NIGHTSCHIP
+			&& mo->type != MT_FLINGBLUESPHERE && mo->type != MT_FLINGNIGHTSCHIP)
+			continue;
+
+		if (!mo->health)
+			continue;
+
+		P_SetMobjState(mo, ((bonustime) ? mo->info->raisestate : mo->info->spawnstate));
 	}
 }
 
@@ -1025,20 +1064,22 @@ static void P_LoadThings(void)
 		}
 
 		//decrement spawn values to the actual number because zero is valid.
-		if (emer1)
-			P_SpawnMobj(huntemeralds[emer1 - 1]->x<<FRACBITS,
-				huntemeralds[emer1 - 1]->y<<FRACBITS,
-				huntemeralds[emer1 - 1]->z<<FRACBITS, MT_EMERHUNT);
+		if (emer1--)
+			P_SpawnMobj(huntemeralds[emer1]->x<<FRACBITS,
+				huntemeralds[emer1]->y<<FRACBITS,
+				huntemeralds[emer1]->z<<FRACBITS, MT_EMERHUNT);
 
-		if (emer2)
-			P_SpawnMobj(huntemeralds[emer2 - 1]->x<<FRACBITS,
-				huntemeralds[emer2 - 1]->y<<FRACBITS,
-				huntemeralds[emer2 - 1]->z<<FRACBITS, MT_EMERHUNT);
+		if (emer2--)
+			P_SetMobjStateNF(P_SpawnMobj(huntemeralds[emer2]->x<<FRACBITS,
+				huntemeralds[emer2]->y<<FRACBITS,
+				huntemeralds[emer2]->z<<FRACBITS, MT_EMERHUNT),
+			mobjinfo[MT_EMERHUNT].spawnstate+1);
 
-		if (emer3)
-			P_SpawnMobj(huntemeralds[emer3 - 1]->x<<FRACBITS,
-				huntemeralds[emer3 - 1]->y<<FRACBITS,
-				huntemeralds[emer3 - 1]->z<<FRACBITS, MT_EMERHUNT);
+		if (emer3--)
+			P_SetMobjStateNF(P_SpawnMobj(huntemeralds[emer3]->x<<FRACBITS,
+				huntemeralds[emer3]->y<<FRACBITS,
+				huntemeralds[emer3]->z<<FRACBITS, MT_EMERHUNT),
+			mobjinfo[MT_EMERHUNT].spawnstate+2);
 	}
 
 	if (metalrecording) // Metal Sonic gets no rings to distract him.
@@ -1048,9 +1089,11 @@ static void P_LoadThings(void)
 	mt = mapthings;
 	for (i = 0; i < nummapthings; i++, mt++)
 	{
-		if (mt->type == 300 || mt->type == 308 || mt->type == 309
-		 || mt->type == 1706 || (mt->type >= 600 && mt->type <= 609)
-		 || mt->type == 1705 || mt->type == 1713 || mt->type == 1800)
+		if (mt->type == mobjinfo[MT_RING].doomednum || mt->type == mobjinfo[MT_COIN].doomednum
+		 || mt->type == mobjinfo[MT_REDTEAMRING].doomednum || mt->type == mobjinfo[MT_BLUETEAMRING].doomednum
+		 || mt->type == mobjinfo[MT_BLUESPHERE].doomednum || mt->type == mobjinfo[MT_BOMBSPHERE].doomednum
+		 || (mt->type >= 600 && mt->type <= 609) // circles and diagonals
+		 || mt->type == 1705 || mt->type == 1713 || mt->type == 1800) // hoops
 		{
 			mt->mobj = NULL;
 
@@ -1058,7 +1101,7 @@ static void P_LoadThings(void)
 			mt->z = (INT16)(R_PointInSubsector(mt->x << FRACBITS, mt->y << FRACBITS)
 				->sector->floorheight>>FRACBITS);
 
-			P_SpawnHoopsAndRings (mt);
+			P_SpawnHoopsAndRings(mt, false);
 		}
 	}
 }
@@ -1289,7 +1332,8 @@ static void P_LoadLineDefs2(void)
 		ld->backsector  = ld->sidenum[1] != 0xffff ? sides[ld->sidenum[1]].sector : 0;
 
 		// Repeat count for midtexture
-		if ((ld->flags & ML_EFFECT5) && (ld->sidenum[1] != 0xffff))
+		if ((ld->flags & ML_EFFECT5) && (ld->sidenum[1] != 0xffff)
+			&& !(ld->special >= 300 && ld->special < 500)) // exempt linedef exec specials
 		{
 			sides[ld->sidenum[0]].repeatcnt = (INT16)(((unsigned)sides[ld->sidenum[0]].textureoffset >> FRACBITS) >> 12);
 			sides[ld->sidenum[0]].textureoffset = (((unsigned)sides[ld->sidenum[0]].textureoffset >> FRACBITS) & 2047) << FRACBITS;
@@ -1396,7 +1440,6 @@ static inline void P_LoadSideDefs(lumpnum_t lumpnum)
 static void P_LoadRawSideDefs2(void *data)
 {
 	UINT16 i;
-	INT32 num;
 
 	for (i = 0; i < numsides; i++)
 	{
@@ -1418,117 +1461,23 @@ static void P_LoadRawSideDefs2(void *data)
 			sd->sector = sec = &sectors[sector_num];
 		}
 
-		// refined to allow colormaps to work as wall textures if invalid as colormaps
-		// but valid as textures.
-
 		sd->sector = sec = &sectors[SHORT(msd->sector)];
 
+		sd->colormap_data = NULL;
+
 		// Colormaps!
 		switch (sd->special)
 		{
 			case 63: // variable colormap via 242 linedef
 			case 606: //SoM: 4/4/2000: Just colormap transfer
+			case 447: // Change colormap of tagged sectors! -- Monster Iestyn 14/06/18
+			case 455: // Fade colormaps! mazmazz 9/12/2018 (:flag_us:)
 				// SoM: R_CreateColormap will only create a colormap in software mode...
 				// Perhaps we should just call it instead of doing the calculations here.
-				if (rendermode == render_soft || rendermode == render_none)
-				{
-					if (msd->toptexture[0] == '#' || msd->bottomtexture[0] == '#')
-					{
-						sec->midmap = R_CreateColormap(msd->toptexture, msd->midtexture,
-							msd->bottomtexture);
-						sd->toptexture = sd->bottomtexture = 0;
-					}
-					else
-					{
-						if ((num = R_CheckTextureNumForName(msd->toptexture)) == -1)
-							sd->toptexture = 0;
-						else
-							sd->toptexture = num;
-						if ((num = R_CheckTextureNumForName(msd->midtexture)) == -1)
-							sd->midtexture = 0;
-						else
-							sd->midtexture = num;
-						if ((num = R_CheckTextureNumForName(msd->bottomtexture)) == -1)
-							sd->bottomtexture = 0;
-						else
-							sd->bottomtexture = num;
-					}
-					break;
-				}
-#ifdef HWRENDER
-				else
-				{
-					// for now, full support of toptexture only
-					if ((msd->toptexture[0] == '#' && msd->toptexture[1] && msd->toptexture[2] && msd->toptexture[3] && msd->toptexture[4] && msd->toptexture[5] && msd->toptexture[6])
-						|| (msd->bottomtexture[0] == '#' && msd->bottomtexture[1] && msd->bottomtexture[2] && msd->bottomtexture[3] && msd->bottomtexture[4] && msd->bottomtexture[5] && msd->bottomtexture[6]))
-					{
-						char *col;
-
-						sec->midmap = R_CreateColormap(msd->toptexture, msd->midtexture,
-							msd->bottomtexture);
-						sd->toptexture = sd->bottomtexture = 0;
-#define HEX2INT(x) (x >= '0' && x <= '9' ? x - '0' : x >= 'a' && x <= 'f' ? x - 'a' + 10 : x >= 'A' && x <= 'F' ? x - 'A' + 10 : 0)
-#define ALPHA2INT(x) (x >= 'a' && x <= 'z' ? x - 'a' : x >= 'A' && x <= 'Z' ? x - 'A' : x >= '0' && x <= '9' ? 25 : 0)
-						sec->extra_colormap = &extra_colormaps[sec->midmap];
-
-						if (msd->toptexture[0] == '#' && msd->toptexture[1] && msd->toptexture[2] && msd->toptexture[3] && msd->toptexture[4] && msd->toptexture[5] && msd->toptexture[6])
-						{
-							col = msd->toptexture;
-
-							sec->extra_colormap->rgba =
-								(HEX2INT(col[1]) << 4) + (HEX2INT(col[2]) << 0) +
-								(HEX2INT(col[3]) << 12) + (HEX2INT(col[4]) << 8) +
-								(HEX2INT(col[5]) << 20) + (HEX2INT(col[6]) << 16);
-
-							// alpha
-							if (msd->toptexture[7])
-								sec->extra_colormap->rgba += (ALPHA2INT(col[7]) << 24);
-							else
-								sec->extra_colormap->rgba += (25 << 24);
-						}
-						else
-							sec->extra_colormap->rgba = 0;
-
-						if (msd->bottomtexture[0] == '#' && msd->bottomtexture[1] && msd->bottomtexture[2] && msd->bottomtexture[3] && msd->bottomtexture[4] && msd->bottomtexture[5] && msd->bottomtexture[6])
-						{
-							col = msd->bottomtexture;
-
-							sec->extra_colormap->fadergba =
-								(HEX2INT(col[1]) << 4) + (HEX2INT(col[2]) << 0) +
-								(HEX2INT(col[3]) << 12) + (HEX2INT(col[4]) << 8) +
-								(HEX2INT(col[5]) << 20) + (HEX2INT(col[6]) << 16);
-
-							// alpha
-							if (msd->bottomtexture[7])
-								sec->extra_colormap->fadergba += (ALPHA2INT(col[7]) << 24);
-							else
-								sec->extra_colormap->fadergba += (25 << 24);
-						}
-						else
-							sec->extra_colormap->fadergba = 0x19000000; // default alpha, (25 << 24)
-#undef ALPHA2INT
-#undef HEX2INT
-					}
-					else
-					{
-						if ((num = R_CheckTextureNumForName(msd->toptexture)) == -1)
-							sd->toptexture = 0;
-						else
-							sd->toptexture = num;
-
-						if ((num = R_CheckTextureNumForName(msd->midtexture)) == -1)
-							sd->midtexture = 0;
-						else
-							sd->midtexture = num;
-
-						if ((num = R_CheckTextureNumForName(msd->bottomtexture)) == -1)
-							sd->bottomtexture = 0;
-						else
-							sd->bottomtexture = num;
-					}
-					break;
-				}
-#endif
+				sd->colormap_data = R_CreateColormap(msd->toptexture, msd->midtexture,
+					msd->bottomtexture);
+				sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
+				break;
 
 			case 413: // Change music
 			{
@@ -2298,7 +2247,7 @@ static void P_LevelInitStuff(void)
 	// circuit, race and competition stuff
 	circuitmap = false;
 	numstarposts = 0;
-	totalrings = timeinmap = 0;
+	ssspheres = timeinmap = 0;
 
 	// special stage
 	stagefailed = false;
@@ -2320,6 +2269,8 @@ static void P_LevelInitStuff(void)
 		}
 	}
 
+	countdown = countdown2 = 0;
+
 	for (i = 0; i < MAXPLAYERS; i++)
 	{
 		if (canresetlives && (netgame || multiplayer) && playeringame[i] && (gametype == GT_COMPETITION || players[i].lives <= 0))
@@ -2328,41 +2279,41 @@ static void P_LevelInitStuff(void)
 			players[i].lives = cv_startinglives.value;
 		}
 
-		players[i].realtime = countdown = countdown2 = 0;
-
+		// obliteration station...
+		players[i].rings = players[i].spheres =\
+		 players[i].xtralife = players[i].deadtimer =\
+		 players[i].numboxes = players[i].totalring =\
+		 players[i].laps = players[i].aiming =\
+		 players[i].losstime = players[i].timeshit =\
+		 players[i].marescore = players[i].lastmarescore =\
+		 players[i].maxlink = players[i].startedtime =\
+		 players[i].finishedtime = players[i].finishedspheres =\
+		 players[i].finishedrings = players[i].lastmare =\
+		 players[i].lastmarelap = players[i].lastmarebonuslap =\
+		 players[i].totalmarelap = players[i].totalmarebonuslap =\
+		 players[i].marebegunat = players[i].textvar =\
+		 players[i].texttimer = players[i].linkcount =\
+		 players[i].linktimer = players[i].flyangle =\
+		 players[i].anotherflyangle = players[i].nightstime =\
+		 players[i].mare = players[i].marelap =\
+		 players[i].marebonuslap = players[i].lapbegunat =\
+		 players[i].lapstartedtime = players[i].totalmarescore =\
+		 players[i].realtime = players[i].exiting = 0;
+
+		// i guess this could be part of the above but i feel mildly uncomfortable implicitly casting
 		players[i].gotcontinue = false;
 
-		players[i].xtralife = players[i].deadtimer = players[i].numboxes = players[i].totalring = players[i].laps = 0;
-		players[i].rings = 0;
-		players[i].aiming = 0;
-		players[i].pflags &= ~PF_GAMETYPEOVER;
-
-		players[i].losstime = 0;
-		players[i].timeshit = 0;
-
-		players[i].marescore = players[i].lastmarescore = players[i].maxlink = 0;
-		players[i].startedtime = players[i].finishedtime = players[i].finishedrings = 0;
-		players[i].lastmare = players[i].marebegunat = 0;
-
-		// Don't show anything
-		players[i].textvar = players[i].texttimer = 0;
-
-		players[i].linkcount = players[i].linktimer = 0;
-		players[i].flyangle = players[i].anotherflyangle = 0;
-		players[i].nightstime = players[i].mare = 0;
-		P_SetTarget(&players[i].capsule, NULL);
+		// aha, the first evidence this shouldn't be a memset!
 		players[i].drillmeter = 40*20;
 
-		players[i].exiting = 0;
 		P_ResetPlayer(&players[i]);
+		// hit these too
+		players[i].pflags &= ~(PF_GAMETYPEOVER|PF_TRANSFERTOCLOSEST);
 
-		players[i].mo = NULL;
-
-		// we must unset axis details too
-		players[i].axis1 = players[i].axis2 = NULL;
-
-		// and this stupid flag as a result
-		players[i].pflags &= ~PF_TRANSFERTOCLOSEST;
+		// unset ALL the pointers. P_SetTarget isn't needed here because if this
+		// function is being called we're just going to clobber the data anyways
+		players[i].mo = players[i].followmobj = players[i].awayviewmobj =\
+		players[i].capsule = players[i].axis1 = players[i].axis2 = NULL;
 	}
 }
 
@@ -2402,7 +2353,17 @@ void P_LoadThingsOnly(void)
 
 	P_LevelInitStuff();
 
-	P_PrepareThings(lastloadedmaplumpnum + ML_THINGS);
+	if (W_IsLumpWad(lastloadedmaplumpnum)) // welp it's a map wad in a pk3
+	{ // HACK: Open wad file rather quickly so we can use the things lump
+		UINT8 *wadData = W_CacheLumpNum(lastloadedmaplumpnum, PU_STATIC);
+		filelump_t *fileinfo = (filelump_t *)(wadData + ((wadinfo_t *)wadData)->infotableofs);
+		fileinfo += ML_THINGS; // we only need the THINGS lump
+		P_PrepareRawThings(wadData + fileinfo->filepos, fileinfo->size);
+		Z_Free(wadData); // we're done with this now
+	}
+	else // phew it's just a WAD
+		P_PrepareThings(lastloadedmaplumpnum + ML_THINGS);
+
 	P_LoadThings();
 
 
@@ -2883,7 +2844,6 @@ boolean P_SetupLevel(boolean skipprecip)
 		// Important: take care of the ordering of the next functions.
 		if (!loadedbm)
 			P_CreateBlockMap(); // Graue 02-29-2004
-		R_MakeColormaps();
 		P_LoadLineDefs2();
 		P_GroupLines();
 		numdmstarts = numredctfstarts = numbluectfstarts = 0;
@@ -2920,7 +2880,6 @@ boolean P_SetupLevel(boolean skipprecip)
 		// Important: take care of the ordering of the next functions.
 		if (!loadedbm)
 			P_CreateBlockMap(); // Graue 02-29-2004
-		R_MakeColormaps();
 		P_LoadLineDefs2();
 		P_GroupLines();
 		numdmstarts = numredctfstarts = numbluectfstarts = 0;
@@ -3354,7 +3313,7 @@ boolean P_AddWadFile(const char *wadfilename)
 		for (i = 0; i < numlumps; i++, lumpinfo++)
 		{
 //			lumpinfo = FindFolder("Lua/",      &luaPos, &luaNum, lumpinfo, &numlumps, &i);
-//			lumpinfo = FindFolder("SOCs/",     &socPos, &socNum, lumpinfo, &numlumps, &i);
+//			lumpinfo = FindFolder("SOC/",      &socPos, &socNum, lumpinfo, &numlumps, &i);
 			lumpinfo = FindFolder("Sounds/",   &sfxPos, &sfxNum, lumpinfo, &numlumps, &i);
 			lumpinfo = FindFolder("Music/",    &musPos, &musNum, lumpinfo, &numlumps, &i);
 //			lumpinfo = FindFolder("Sprites/",  &sprPos, &sprNum, lumpinfo, &numlumps, &i);
diff --git a/src/p_setup.h b/src/p_setup.h
index a42ac5b7605322f2d75229c6941450f331848748..569501531e06e009f711a7076837dc26bca5d30c 100644
--- a/src/p_setup.h
+++ b/src/p_setup.h
@@ -72,6 +72,7 @@ void P_DeleteFlickies(INT16 i);
 
 // Needed for NiGHTS
 void P_ReloadRings(void);
+void P_SwitchSpheresBonusMode(boolean bonustime);
 void P_DeleteGrades(INT16 i);
 void P_AddGradesForMare(INT16 i, UINT8 mare, char *gtext);
 UINT8 P_GetGrade(UINT32 pscore, INT16 map, UINT8 mare);
diff --git a/src/p_spec.c b/src/p_spec.c
index 93b5c89ca1bf26d6118bf40b5c542fb638ec3991..9f9e80eccf5507afd69871215342b1a949135324 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -103,6 +103,18 @@ static void P_SpawnFriction(void);
 static void P_SpawnPushers(void);
 static void Add_Pusher(pushertype_e type, fixed_t x_mag, fixed_t y_mag, mobj_t *source, INT32 affectee, INT32 referrer, INT32 exclusive, INT32 slider); //SoM: 3/9/2000
 static void Add_MasterDisappearer(tic_t appeartime, tic_t disappeartime, tic_t offset, INT32 line, INT32 sourceline);
+static void P_ResetFakeFloorFader(ffloor_t *rover, fade_t *data, boolean finalize);
+#define P_RemoveFakeFloorFader(l) P_ResetFakeFloorFader(l, NULL, false);
+static boolean P_FadeFakeFloor(ffloor_t *rover, INT16 sourcevalue, INT16 destvalue, INT16 speed, boolean ticbased, INT32 *timer,
+	boolean doexists, boolean dotranslucent, boolean dolighting, boolean docolormap,
+	boolean docollision, boolean doghostfade, boolean exactalpha);
+static void P_AddFakeFloorFader(ffloor_t *rover, size_t sectornum, size_t ffloornum,
+	INT16 destvalue, INT16 speed, boolean ticbased, boolean relative,
+	boolean doexists, boolean dotranslucent, boolean dolighting, boolean docolormap,
+	boolean docollision, boolean doghostfade, boolean exactalpha);
+static void P_ResetColormapFader(sector_t *sector);
+static void Add_ColormapFader(sector_t *sector, extracolormap_t *source_exc, extracolormap_t *dest_exc,
+	boolean ticbased, INT32 duration);
 static void P_AddBlockThinker(sector_t *sec, line_t *sourceline);
 static void P_AddFloatThinker(sector_t *sec, INT32 tag, line_t *sourceline);
 //static void P_AddBridgeThinker(line_t *sourceline, sector_t *sec);
@@ -1218,7 +1230,7 @@ static void PolyVisible(line_t *line)
 		po->flags |= POF_SOLID;
 
 	po->flags &= ~POF_NOSPECIALS;
-	po->flags |= POF_RENDERALL;
+	po->flags |= (po->spawnflags & POF_RENDERALL);
 }
 
 //
@@ -1242,7 +1254,94 @@ static void PolyTranslucency(line_t *line)
 	if (po->isBad)
 		return;
 
-	po->translucency = (line->frontsector->floorheight >> FRACBITS) / 100;
+	// if DONTPEGBOTTOM, specify raw translucency value in Front X Offset
+	// else, take it out of 1000. If Front X Offset is specified, use that. Else, use floorheight.
+	if (line->flags & ML_EFFECT3) // relative calc
+		po->translucency = max(min(po->translucency + ((line->flags & ML_DONTPEGBOTTOM) ?
+			(sides[line->sidenum[0]].textureoffset ?
+				max(min(sides[line->sidenum[0]].textureoffset>>FRACBITS, NUMTRANSMAPS), -NUMTRANSMAPS)
+				: max(min(line->frontsector->floorheight>>FRACBITS, NUMTRANSMAPS), -NUMTRANSMAPS))
+			: (sides[line->sidenum[0]].textureoffset ?
+				max(min(sides[line->sidenum[0]].textureoffset>>FRACBITS, 1000), -1000) / 100
+				: max(min(line->frontsector->floorheight>>FRACBITS, 1000), -1000) / 100)),
+			NUMTRANSMAPS), 0);
+	else
+		po->translucency = (line->flags & ML_DONTPEGBOTTOM) ?
+			(sides[line->sidenum[0]].textureoffset ?
+				max(min(sides[line->sidenum[0]].textureoffset>>FRACBITS, NUMTRANSMAPS), 0)
+				: max(min(line->frontsector->floorheight>>FRACBITS, NUMTRANSMAPS), 0))
+			: (sides[line->sidenum[0]].textureoffset ?
+				max(min(sides[line->sidenum[0]].textureoffset>>FRACBITS, 1000), 0) / 100
+				: max(min(line->frontsector->floorheight>>FRACBITS, 1000), 0) / 100);
+}
+
+//
+// PolyFade
+//
+// Makes a polyobject translucency fade and applies tangibility
+//
+static boolean PolyFade(line_t *line)
+{
+	INT32 polyObjNum = line->tag;
+	polyobj_t *po;
+	polyfadedata_t pfd;
+
+	if (!(po = Polyobj_GetForNum(polyObjNum)))
+	{
+		CONS_Debug(DBG_POLYOBJ, "PolyFade: bad polyobj %d\n", polyObjNum);
+		return 0;
+	}
+
+	// don't allow line actions to affect bad polyobjects
+	if (po->isBad)
+		return 0;
+
+	// Prevent continuous execs from interfering on an existing fade
+	if (!(line->flags & ML_EFFECT5)
+		&& po->thinker
+		&& po->thinker->function.acp1 == (actionf_p1)T_PolyObjFade)
+	{
+		CONS_Debug(DBG_POLYOBJ, "Line type 492 Executor: Fade PolyObject thinker already exists\n");
+		return 0;
+	}
+
+	pfd.polyObjNum = polyObjNum;
+
+	// if DONTPEGBOTTOM, specify raw translucency value in Front X Offset
+	// else, take it out of 1000. If Front X Offset is specified, use that. Else, use floorheight.
+	if (line->flags & ML_EFFECT3) // relative calc
+		pfd.destvalue = max(min(po->translucency + ((line->flags & ML_DONTPEGBOTTOM) ?
+			(sides[line->sidenum[0]].textureoffset ?
+				max(min(sides[line->sidenum[0]].textureoffset>>FRACBITS, NUMTRANSMAPS), -NUMTRANSMAPS)
+				: max(min(line->frontsector->floorheight>>FRACBITS, NUMTRANSMAPS), -NUMTRANSMAPS))
+			: (sides[line->sidenum[0]].textureoffset ?
+				max(min(sides[line->sidenum[0]].textureoffset>>FRACBITS, 1000), -1000) / 100
+				: max(min(line->frontsector->floorheight>>FRACBITS, 1000), -1000) / 100)),
+			NUMTRANSMAPS), 0);
+	else
+		pfd.destvalue = (line->flags & ML_DONTPEGBOTTOM) ?
+			(sides[line->sidenum[0]].textureoffset ?
+				max(min(sides[line->sidenum[0]].textureoffset>>FRACBITS, NUMTRANSMAPS), 0)
+				: max(min(line->frontsector->floorheight>>FRACBITS, NUMTRANSMAPS), 0))
+			: (sides[line->sidenum[0]].textureoffset ?
+				max(min(sides[line->sidenum[0]].textureoffset>>FRACBITS, 1000), 0) / 100
+				: max(min(line->frontsector->floorheight>>FRACBITS, 1000), 0) / 100);
+
+	// already equal, nothing to do
+	if (po->translucency == pfd.destvalue)
+		return 1;
+
+	pfd.docollision = !(line->flags & ML_BOUNCY);         // do not handle collision flags
+	pfd.doghostfade = (line->flags & ML_EFFECT1);         // do ghost fade (no collision flags during fade)
+	pfd.ticbased = (line->flags & ML_EFFECT4);            // Speed = Tic Duration
+
+	// allow Back Y Offset to be consistent with other fade specials
+	pfd.speed = (line->sidenum[1] != 0xFFFF && !sides[line->sidenum[0]].rowoffset) ?
+		abs(sides[line->sidenum[1]].rowoffset>>FRACBITS)
+		: abs(sides[line->sidenum[0]].rowoffset>>FRACBITS);
+
+
+	return EV_DoPolyObjFade(&pfd);
 }
 
 //
@@ -1375,6 +1474,67 @@ void P_ChangeSectorTag(UINT32 sector, INT16 newtag)
 	}
 }
 
+//
+// P_RunNightserizeExecutors
+//
+void P_RunNightserizeExecutors(mobj_t *actor)
+{
+	size_t i;
+
+	for (i = 0; i < numlines; i++)
+	{
+		if (lines[i].special == 323 || lines[i].special == 324)
+			P_RunTriggerLinedef(&lines[i], actor, NULL);
+	}
+}
+
+//
+// P_RunDeNightserizeExecutors
+//
+void P_RunDeNightserizeExecutors(mobj_t *actor)
+{
+	size_t i;
+
+	for (i = 0; i < numlines; i++)
+	{
+		if (lines[i].special == 325 || lines[i].special == 326)
+			P_RunTriggerLinedef(&lines[i], actor, NULL);
+	}
+}
+
+//
+// P_RunNightsLapExecutors
+//
+void P_RunNightsLapExecutors(mobj_t *actor)
+{
+	size_t i;
+
+	for (i = 0; i < numlines; i++)
+	{
+		if (lines[i].special == 327 || lines[i].special == 328)
+			P_RunTriggerLinedef(&lines[i], actor, NULL);
+	}
+}
+
+//
+// P_RunNightsCapsuleTouchExecutors
+//
+void P_RunNightsCapsuleTouchExecutors(mobj_t *actor, boolean entering, boolean enoughspheres)
+{
+	size_t i;
+
+	for (i = 0; i < numlines; i++)
+	{
+		if ((lines[i].special == 329 || lines[i].special == 330)
+			&& ((entering && (lines[i].flags & ML_TFERLINE))
+				|| (!entering && !(lines[i].flags & ML_TFERLINE)))
+			&& ((lines[i].flags & ML_DONTPEGTOP)
+				|| (enoughspheres && !(lines[i].flags & ML_BOUNCY))
+				|| (!enoughspheres && (lines[i].flags & ML_BOUNCY))))
+			P_RunTriggerLinedef(&lines[i], actor, NULL);
+	}
+}
+
 /** Hashes the sector tags across the sectors and linedefs.
   *
   * \sa P_FindSectorFromTag, P_ChangeSectorTag
@@ -1458,6 +1618,146 @@ static void P_AddExecutorDelay(line_t *line, mobj_t *mobj, sector_t *sector)
 	P_AddThinker(&e->thinker);
 }
 
+/** Used by P_RunTriggerLinedef to check a NiGHTS trigger linedef's conditions
+  *
+  * \param triggerline Trigger linedef to check conditions for; should NEVER be NULL.
+  * \param actor Object initiating the action; should not be NULL.
+  * \sa P_RunTriggerLinedef
+  */
+static boolean P_CheckNightsTriggerLine(line_t *triggerline, mobj_t *actor)
+{
+	INT16 specialtype = triggerline->special;
+	size_t i;
+
+	UINT8 inputmare = max(0, min(255, sides[triggerline->sidenum[0]].textureoffset>>FRACBITS));
+	UINT8 inputlap = max(0, min(255, sides[triggerline->sidenum[0]].rowoffset>>FRACBITS));
+
+	boolean ltemare = triggerline->flags & ML_NOCLIMB;
+	boolean gtemare = triggerline->flags & ML_BLOCKMONSTERS;
+	boolean ltelap = triggerline->flags & ML_EFFECT1;
+	boolean gtelap = triggerline->flags & ML_EFFECT2;
+
+	boolean lapfrombonustime = triggerline->flags & ML_EFFECT3;
+	boolean perglobalinverse = triggerline->flags & ML_DONTPEGBOTTOM;
+	boolean perglobal = !(triggerline->flags & ML_EFFECT4) && !perglobalinverse;
+
+	boolean donomares = triggerline->flags & ML_BOUNCY; // nightserize: run at end of level (no mares)
+	boolean fromnonights = triggerline->flags & ML_TFERLINE; // nightserize: from non-nights // denightserize: all players no nights
+	boolean fromnights = triggerline->flags & ML_DONTPEGTOP; // nightserize: from nights // denightserize: >0 players are nights
+
+	UINT8 currentmare = UINT8_MAX;
+	UINT8 currentlap = UINT8_MAX;
+
+	// Do early returns for Nightserize
+	if (specialtype >= 323 && specialtype <= 324)
+	{
+		// run only when no mares are found
+		if (donomares && P_FindLowestMare() != UINT8_MAX)
+			return false;
+
+		// run only if there is a mare present
+		if (!donomares && P_FindLowestMare() == UINT8_MAX)
+			return false;
+
+		// run only if player is nightserizing from non-nights
+		if (fromnonights)
+		{
+			if (!actor->player)
+				return false;
+			else if (actor->player->powers[pw_carry] == CR_NIGHTSMODE)
+				return false;
+		}
+		// run only if player is nightserizing from nights
+		else if (fromnights)
+		{
+			if (!actor->player)
+				return false;
+			else if (actor->player->powers[pw_carry] != CR_NIGHTSMODE)
+				return false;
+		}
+	}
+
+	// Get current mare and lap (and check early return for DeNightserize)
+	if (perglobal || perglobalinverse
+		|| (specialtype >= 325 && specialtype <= 326 && (fromnonights || fromnights)))
+	{
+		UINT8 playersarenights = 0;
+
+		for (i = 0; i < MAXPLAYERS; i++)
+		{
+			UINT8 lap;
+			if (!playeringame[i] || players[i].spectator)
+				continue;
+
+			// denightserize: run only if all players are not nights
+			if (specialtype >= 325 && specialtype <= 326 && fromnonights
+				&& players[i].powers[pw_carry] == CR_NIGHTSMODE)
+				return false;
+
+			// count number of nights players for denightserize return
+			if (specialtype >= 325 && specialtype <= 326 && fromnights
+				&& players[i].powers[pw_carry] == CR_NIGHTSMODE)
+				playersarenights++;
+
+			lap = lapfrombonustime ? players[i].marebonuslap : players[i].marelap;
+
+			// get highest mare/lap of players
+			if (perglobal)
+			{
+				if (players[i].mare > currentmare || currentmare == UINT8_MAX)
+				{
+					currentmare = players[i].mare;
+					currentlap = UINT8_MAX;
+				}
+				if (players[i].mare == currentmare
+					&& (lap > currentlap || currentlap == UINT8_MAX))
+					currentlap = lap;
+			}
+			// get lowest mare/lap of players
+			else if (perglobalinverse)
+			{
+				if (players[i].mare < currentmare || currentmare == UINT8_MAX)
+				{
+					currentmare = players[i].mare;
+					currentlap = UINT8_MAX;
+				}
+				if (players[i].mare == currentmare
+					&& (lap < currentlap || currentlap == UINT8_MAX))
+					currentlap = lap;
+			}
+		}
+
+		// denightserize: run only if >0 players are nights
+		if (specialtype >= 325 && specialtype <= 326 && fromnights
+			&& playersarenights < 1)
+			return false;
+	}
+	// get current mare/lap from triggering player
+	else if (!perglobal && !perglobalinverse)
+	{
+		if (!actor->player)
+			return false;
+		currentmare = actor->player->mare;
+		currentlap = lapfrombonustime ? actor->player->marebonuslap : actor->player->marelap;
+	}
+
+	if (lapfrombonustime && !currentlap)
+		return false; // special case: player->marebonuslap is 0 until passing through on bonus time. Don't trigger lines looking for inputlap 0.
+
+	// Compare current mare/lap to input mare/lap based on rules
+	if (!(specialtype >= 323 && specialtype <= 324 && donomares) // don't return false if donomares and we got this far
+		&& ((ltemare && currentmare > inputmare)
+		|| (gtemare && currentmare < inputmare)
+		|| (!ltemare && !gtemare && currentmare != inputmare)
+		|| (ltelap && currentlap > inputlap)
+		|| (gtelap && currentlap < inputlap)
+		|| (!ltelap && !gtelap && currentlap != inputlap))
+		)
+		return false;
+
+	return true;
+}
+
 /** Used by P_LinedefExecute to check a trigger linedef's conditions
   * The linedef executor specials in the trigger linedef's sector are run if all conditions are met.
   * Return false cancels P_LinedefExecute, this happens if a condition is not met.
@@ -1493,10 +1793,10 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
 				if (!playeringame[i] || players[i].spectator)
 					continue;
 
-				if (!players[i].mo || players[i].rings <= 0)
+				if (!players[i].mo || ((maptol & TOL_NIGHTS) ? players[i].spheres : players[i].rings) <= 0)
 					continue;
 
-				rings += players[i].rings;
+				rings += (maptol & TOL_NIGHTS) ? players[i].spheres : players[i].rings;
 			}
 		}
 		else
@@ -1504,7 +1804,7 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
 			if (!(actor && actor->player))
 				return false; // no player to count rings from here, sorry
 
-			rings = actor->player->rings;
+			rings = (maptol & TOL_NIGHTS) ? actor->player->spheres : actor->player->rings;
 		}
 
 		if (triggerline->flags & ML_NOCLIMB)
@@ -1668,6 +1968,18 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
 					return false;
 			}
 			break;
+		case 323: // nightserize - each time
+		case 324: // nightserize - once
+		case 325: // denightserize - each time
+		case 326: // denightserize - once
+		case 327: // nights lap - each time
+		case 328: // nights lap - once
+		case 329: // nights egg capsule touch - each time
+		case 330: // nights egg capsule touch - once
+			if (!P_CheckNightsTriggerLine(triggerline, actor))
+				return false;
+			break;
+
 		default:
 			break;
 	}
@@ -1796,6 +2108,10 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
 	 || specialtype == 318  // Unlockable trigger - Once
 	 || specialtype == 320  // Unlockable - Once
 	 || specialtype == 321 || specialtype == 322 // Trigger on X calls - Continuous + Each Time
+	 || specialtype == 324 // Nightserize - Once
+	 || specialtype == 326 // DeNightserize - Once
+	 || specialtype == 328 // Nights lap - Once
+	 || specialtype == 330 // Nights Bonus Time - Once
 	 || specialtype == 399) // Level Load
 		triggerline->special = 0; // Clear it out
 
@@ -1910,8 +2226,7 @@ void P_SwitchWeather(INT32 weathernum)
 
 		for (think = thinkercap.next; think != &thinkercap; think = think->next)
 		{
-			if ((think->function.acp1 != (actionf_p1)P_SnowThinker)
-				&& (think->function.acp1 != (actionf_p1)P_RainThinker))
+			if (think->function.acp1 != (actionf_p1)P_NullPrecipThinker)
 				continue; // not a precipmobj thinker
 
 			precipmobj = (precipmobj_t *)think;
@@ -1927,14 +2242,12 @@ void P_SwitchWeather(INT32 weathernum)
 
 		for (think = thinkercap.next; think != &thinkercap; think = think->next)
 		{
+			if (think->function.acp1 != (actionf_p1)P_NullPrecipThinker)
+				continue; // not a precipmobj thinker
+			precipmobj = (precipmobj_t *)think;
+
 			if (swap == PRECIP_RAIN) // Snow To Rain
 			{
-				if (!(think->function.acp1 == (actionf_p1)P_SnowThinker
-					|| think->function.acp1 == (actionf_p1)P_NullPrecipThinker))
-					continue; // not a precipmobj thinker
-
-				precipmobj = (precipmobj_t *)think;
-
 				precipmobj->flags = mobjinfo[MT_RAIN].flags;
 				st = &states[mobjinfo[MT_RAIN].spawnstate];
 				precipmobj->state = st;
@@ -1945,18 +2258,13 @@ void P_SwitchWeather(INT32 weathernum)
 
 				precipmobj->precipflags &= ~PCF_INVISIBLE;
 
-				think->function.acp1 = (actionf_p1)P_RainThinker;
+				precipmobj->precipflags |= PCF_RAIN;
+				//think->function.acp1 = (actionf_p1)P_RainThinker;
 			}
 			else if (swap == PRECIP_SNOW) // Rain To Snow
 			{
 				INT32 z;
 
-				if (!(think->function.acp1 == (actionf_p1)P_RainThinker
-					|| think->function.acp1 == (actionf_p1)P_NullPrecipThinker))
-					continue; // not a precipmobj thinker
-
-				precipmobj = (precipmobj_t *)think;
-
 				precipmobj->flags = mobjinfo[MT_SNOWFLAKE].flags;
 				z = M_RandomByte();
 
@@ -1974,19 +2282,13 @@ void P_SwitchWeather(INT32 weathernum)
 				precipmobj->frame = st->frame;
 				precipmobj->momz = mobjinfo[MT_SNOWFLAKE].speed;
 
-				precipmobj->precipflags &= ~PCF_INVISIBLE;
+				precipmobj->precipflags &= ~(PCF_INVISIBLE|PCF_RAIN);
 
-				think->function.acp1 = (actionf_p1)P_SnowThinker;
+				//think->function.acp1 = (actionf_p1)P_SnowThinker;
 			}
 			else if (swap == PRECIP_BLANK || swap == PRECIP_STORM_NORAIN) // Remove precip, but keep it around for reuse.
 			{
-				if (!(think->function.acp1 == (actionf_p1)P_RainThinker
-					|| think->function.acp1 == (actionf_p1)P_SnowThinker))
-					continue;
-
-				precipmobj = (precipmobj_t *)think;
-
-				think->function.acp1 = (actionf_p1)P_NullPrecipThinker;
+				//think->function.acp1 = (actionf_p1)P_NullPrecipThinker;
 
 				precipmobj->precipflags |= PCF_INVISIBLE;
 			}
@@ -2561,7 +2863,17 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 			break;
 
 		case 420: // Fade light levels in tagged sectors to new value
-			P_FadeLight(line->tag, line->frontsector->lightlevel, P_AproxDistance(line->dx, line->dy)>>FRACBITS);
+			P_FadeLight(line->tag,
+				(line->flags & ML_DONTPEGBOTTOM) ? max(sides[line->sidenum[0]].textureoffset>>FRACBITS, 0) : line->frontsector->lightlevel,
+				// failsafe: if user specifies Back Y Offset and NOT Front Y Offset, use the Back Offset
+				// to be consistent with other light and fade specials
+				(line->flags & ML_DONTPEGBOTTOM) ?
+					((line->sidenum[1] != 0xFFFF && !(sides[line->sidenum[0]].rowoffset>>FRACBITS)) ?
+						max(min(sides[line->sidenum[1]].rowoffset>>FRACBITS, 255), 0)
+						: max(min(sides[line->sidenum[0]].rowoffset>>FRACBITS, 255), 0))
+					: abs(P_AproxDistance(line->dx, line->dy))>>FRACBITS,
+				(line->flags & ML_EFFECT4),
+				(line->flags & ML_EFFECT5));
 			break;
 
 		case 421: // Stop lighting effect in tagged sectors
@@ -2765,6 +3077,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 				INT16 foftag = (INT16)(sides[line->sidenum[0]].rowoffset>>FRACBITS);
 				sector_t *sec; // Sector that the FOF is visible in
 				ffloor_t *rover; // FOF that we are going to crumble
+				boolean foundrover = false; // for debug, "Can't find a FOF" message
 
 				for (secnum = -1; (secnum = P_FindSectorFromTag(sectag, secnum)) >= 0 ;)
 				{
@@ -2779,16 +3092,18 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 					for (rover = sec->ffloors; rover; rover = rover->next)
 					{
 						if (rover->master->frontsector->tag == foftag)
-							break;
+						{
+							foundrover = true;
+
+							EV_CrumbleChain(sec, rover);
+						}
 					}
 
-					if (!rover)
+					if (!foundrover)
 					{
 						CONS_Debug(DBG_GAMELOGIC, "Line type 436 Executor: Can't find a FOF control sector with tag %d\n", foftag);
 						return;
 					}
-
-					EV_CrumbleChain(sec, rover);
 				}
 			}
 			break;
@@ -2868,7 +3183,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 					// Unlocked something?
 					if (M_UpdateUnlockablesAndExtraEmblems())
 					{
-						S_StartSound(NULL, sfx_ncitem);
+						S_StartSound(NULL, sfx_s3k68);
 						G_SaveGameData(); // only save if unlocked something
 					}
 				}
@@ -2949,6 +3264,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 				INT16 foftag = (INT16)(sides[line->sidenum[0]].rowoffset>>FRACBITS);
 				sector_t *sec; // Sector that the FOF is visible (or not visible) in
 				ffloor_t *rover; // FOF to vanish/un-vanish
+				boolean foundrover = false; // for debug, "Can't find a FOF" message
 				ffloortype_e oldflags; // store FOF's old flags
 
 				for (secnum = -1; (secnum = P_FindSectorFromTag(sectag, secnum)) >= 0 ;)
@@ -2964,26 +3280,28 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 					for (rover = sec->ffloors; rover; rover = rover->next)
 					{
 						if (rover->master->frontsector->tag == foftag)
-							break;
+						{
+							foundrover = true;
+
+							oldflags = rover->flags;
+
+							// Abracadabra!
+							if (line->flags & ML_NOCLIMB)
+								rover->flags |= FF_EXISTS;
+							else
+								rover->flags &= ~FF_EXISTS;
+
+							// if flags changed, reset sector's light list
+							if (rover->flags != oldflags)
+								sec->moved = true;
+						}
 					}
 
-					if (!rover)
+					if (!foundrover)
 					{
 						CONS_Debug(DBG_GAMELOGIC, "Line type 445 Executor: Can't find a FOF control sector with tag %d\n", foftag);
 						return;
 					}
-
-					oldflags = rover->flags;
-
-					// Abracadabra!
-					if (line->flags & ML_NOCLIMB)
-						rover->flags |= FF_EXISTS;
-					else
-						rover->flags &= ~FF_EXISTS;
-
-					// if flags changed, reset sector's light list
-					if (rover->flags != oldflags)
-						sec->moved = true;
 				}
 			}
 			break;
@@ -2994,6 +3312,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 				INT16 foftag = (INT16)(sides[line->sidenum[0]].rowoffset>>FRACBITS);
 				sector_t *sec; // Sector that the FOF is visible in
 				ffloor_t *rover; // FOF that we are going to make fall down
+				boolean foundrover = false; // for debug, "Can't find a FOF" message
 				player_t *player = NULL; // player that caused FOF to fall
 				boolean respawn = true; // should the fallen FOF respawn?
 
@@ -3016,20 +3335,82 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 					for (rover = sec->ffloors; rover; rover = rover->next)
 					{
 						if (rover->master->frontsector->tag == foftag)
-							break;
+						{
+							foundrover = true;
+
+							if (line->flags & ML_BLOCKMONSTERS) // FOF flags determine respawn ability instead?
+								respawn = !(rover->flags & FF_NORETURN) ^ !!(line->flags & ML_NOCLIMB); // no climb inverts
+
+							EV_StartCrumble(rover->master->frontsector, rover, (rover->flags & FF_FLOATBOB), player, rover->alpha, respawn);
+						}
 					}
 
-					if (!rover)
+					if (!foundrover)
 					{
 						CONS_Debug(DBG_GAMELOGIC, "Line type 446 Executor: Can't find a FOF control sector with tag %d\n", foftag);
 						return;
 					}
+				}
+			}
+			break;
 
-					if (line->flags & ML_BLOCKMONSTERS) // FOF flags determine respawn ability instead?
-						respawn = !(rover->flags & FF_NORETURN) ^ !!(line->flags & ML_NOCLIMB); // no climb inverts
+		case 447: // Change colormap of tagged sectors!
+			// Basically this special applies a colormap to the tagged sectors, just like 606 (the colormap linedef)
+			// Except it is activated by linedef executor, not level load
+			// This could even override existing colormaps I believe
+			// -- Monster Iestyn 14/06/18
+			for (secnum = -1; (secnum = P_FindSectorFromLineTag(line, secnum)) >= 0 ;)
+			{
+				P_ResetColormapFader(&sectors[secnum]);
 
-					EV_StartCrumble(rover->master->frontsector, rover, (rover->flags & FF_FLOATBOB), player, rover->alpha, respawn);
+				if (line->flags & ML_EFFECT3) // relative calc
+				{
+					extracolormap_t *exc = R_AddColormaps(
+						(line->flags & ML_TFERLINE) && line->sidenum[1] != 0xFFFF ?
+							sides[line->sidenum[1]].colormap_data : sectors[secnum].extra_colormap, // use back colormap instead of target sector
+						sides[line->sidenum[0]].colormap_data,
+						line->flags & ML_EFFECT1,  // subtract R
+						line->flags & ML_NOCLIMB,  // subtract G
+						line->flags & ML_EFFECT2,  // subtract B
+						false,                     // subtract A (no flag for this, just pass negative alpha)
+						line->flags & ML_EFFECT1,  // subtract FadeR
+						line->flags & ML_NOCLIMB,  // subtract FadeG
+						line->flags & ML_EFFECT2,  // subtract FadeB
+						false,                     // subtract FadeA (no flag for this, just pass negative alpha)
+						false,                     // subtract FadeStart (we ran out of flags)
+						false,                     // subtract FadeEnd (we ran out of flags)
+						false,                     // ignore Fog (we ran out of flags)
+						line->flags & ML_DONTPEGBOTTOM,
+						(line->flags & ML_DONTPEGBOTTOM) ? (sides[line->sidenum[0]].textureoffset >> FRACBITS) : 0,
+						(line->flags & ML_DONTPEGBOTTOM) ? (sides[line->sidenum[0]].rowoffset >> FRACBITS) : 0,
+						false);
+
+					if (!(sectors[secnum].extra_colormap = R_GetColormapFromList(exc)))
+					{
+						exc->colormap = R_CreateLightTable(exc);
+						R_AddColormapToList(exc);
+						sectors[secnum].extra_colormap = exc;
+					}
+					else
+						Z_Free(exc);
 				}
+				else if (line->flags & ML_DONTPEGBOTTOM) // alternate alpha (by texture offsets)
+				{
+					extracolormap_t *exc = R_CopyColormap(sides[line->sidenum[0]].colormap_data, false);
+					exc->rgba = R_GetRgbaRGB(exc->rgba) + R_PutRgbaA(max(min(sides[line->sidenum[0]].textureoffset >> FRACBITS, 25), 0));
+					exc->fadergba = R_GetRgbaRGB(exc->fadergba) + R_PutRgbaA(max(min(sides[line->sidenum[0]].rowoffset >> FRACBITS, 25), 0));
+
+					if (!(sectors[secnum].extra_colormap = R_GetColormapFromList(exc)))
+					{
+						exc->colormap = R_CreateLightTable(exc);
+						R_AddColormapToList(exc);
+						sectors[secnum].extra_colormap = exc;
+					}
+					else
+						Z_Free(exc);
+				}
+				else
+					sectors[secnum].extra_colormap = sides[line->sidenum[0]].colormap_data;
 			}
 			break;
 
@@ -3093,6 +3474,289 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 			break;
 		}
 
+		case 452: // Set FOF alpha
+		{
+			INT16 destvalue = line->sidenum[1] != 0xffff ?
+				(INT16)(sides[line->sidenum[1]].textureoffset>>FRACBITS) : (INT16)(P_AproxDistance(line->dx, line->dy)>>FRACBITS);
+			INT16 sectag = (INT16)(sides[line->sidenum[0]].textureoffset>>FRACBITS);
+			INT16 foftag = (INT16)(sides[line->sidenum[0]].rowoffset>>FRACBITS);
+			sector_t *sec; // Sector that the FOF is visible in
+			ffloor_t *rover; // FOF that we are going to operate
+			boolean foundrover = false; // for debug, "Can't find a FOF" message
+
+			for (secnum = -1; (secnum = P_FindSectorFromTag(sectag, secnum)) >= 0 ;)
+			{
+				sec = sectors + secnum;
+
+				if (!sec->ffloors)
+				{
+					CONS_Debug(DBG_GAMELOGIC, "Line type 452 Executor: Target sector #%d has no FOFs.\n", secnum);
+					return;
+				}
+
+				for (rover = sec->ffloors; rover; rover = rover->next)
+				{
+					if (rover->master->frontsector->tag == foftag)
+					{
+						foundrover = true;
+
+						// If fading an invisible FOF whose render flags we did not yet set,
+						// initialize its alpha to 1
+						// for relative alpha calc
+						if (!(line->flags & ML_NOCLIMB) &&      // do translucent
+							(rover->spawnflags & FF_NOSHADE) && // do not include light blocks, which don't set FF_NOSHADE
+							!(rover->spawnflags & FF_RENDERSIDES) &&
+							!(rover->spawnflags & FF_RENDERPLANES) &&
+							!(rover->flags & FF_RENDERALL))
+							rover->alpha = 1;
+
+						P_RemoveFakeFloorFader(rover);
+						P_FadeFakeFloor(rover,
+							rover->alpha,
+							max(1, min(256, (line->flags & ML_EFFECT3) ? rover->alpha + destvalue : destvalue)),
+							0,                                  // set alpha immediately
+							false, NULL,                        // tic-based logic
+							false,                              // do not handle FF_EXISTS
+							!(line->flags & ML_NOCLIMB),        // handle FF_TRANSLUCENT
+							false,                              // do not handle lighting
+							false,                              // do not handle colormap
+							false,                              // do not handle collision
+							false,                              // do not do ghost fade (no collision during fade)
+							true);                               // use exact alpha values (for opengl)
+					}
+				}
+
+				if (!foundrover)
+				{
+					CONS_Debug(DBG_GAMELOGIC, "Line type 452 Executor: Can't find a FOF control sector with tag %d\n", foftag);
+					return;
+				}
+			}
+			break;
+		}
+
+		case 453: // Fade FOF
+		{
+			INT16 destvalue = line->sidenum[1] != 0xffff ?
+				(INT16)(sides[line->sidenum[1]].textureoffset>>FRACBITS) : (INT16)(line->dx>>FRACBITS);
+			INT16 speed = line->sidenum[1] != 0xffff ?
+				(INT16)(abs(sides[line->sidenum[1]].rowoffset>>FRACBITS)) : (INT16)(abs(line->dy)>>FRACBITS);
+			INT16 sectag = (INT16)(sides[line->sidenum[0]].textureoffset>>FRACBITS);
+			INT16 foftag = (INT16)(sides[line->sidenum[0]].rowoffset>>FRACBITS);
+			sector_t *sec; // Sector that the FOF is visible in
+			ffloor_t *rover; // FOF that we are going to operate
+			boolean foundrover = false; // for debug, "Can't find a FOF" message
+			size_t j = 0; // sec->ffloors is saved as ffloor #0, ss->ffloors->next is #1, etc
+
+			for (secnum = -1; (secnum = P_FindSectorFromTag(sectag, secnum)) >= 0 ;)
+			{
+				sec = sectors + secnum;
+
+				if (!sec->ffloors)
+				{
+					CONS_Debug(DBG_GAMELOGIC, "Line type 453 Executor: Target sector #%d has no FOFs.\n", secnum);
+					return;
+				}
+
+				for (rover = sec->ffloors; rover; rover = rover->next)
+				{
+					if (rover->master->frontsector->tag == foftag)
+					{
+						foundrover = true;
+
+						// Prevent continuous execs from interfering on an existing fade
+						if (!(line->flags & ML_EFFECT5)
+							&& rover->fadingdata)
+							//&& ((fade_t*)rover->fadingdata)->timer > (ticbased ? 2 : speed*2))
+						{
+							CONS_Debug(DBG_GAMELOGIC, "Line type 453 Executor: Fade FOF thinker already exists, timer: %d\n", ((fade_t*)rover->fadingdata)->timer);
+							continue;
+						}
+
+						if (speed > 0)
+							P_AddFakeFloorFader(rover, secnum, j,
+								destvalue,
+								speed,
+								(line->flags & ML_EFFECT4),         // tic-based logic
+								(line->flags & ML_EFFECT3),         // Relative destvalue
+								!(line->flags & ML_BLOCKMONSTERS),  // do not handle FF_EXISTS
+								!(line->flags & ML_NOCLIMB),        // do not handle FF_TRANSLUCENT
+								!(line->flags & ML_EFFECT2),        // do not handle lighting
+								!(line->flags & ML_EFFECT2),        // do not handle colormap (ran out of flags)
+								!(line->flags & ML_BOUNCY),         // do not handle collision
+								(line->flags & ML_EFFECT1),         // do ghost fade (no collision during fade)
+								(line->flags & ML_TFERLINE));       // use exact alpha values (for opengl)
+						else
+						{
+							// If fading an invisible FOF whose render flags we did not yet set,
+							// initialize its alpha to 1
+							// for relative alpha calc
+							if (!(line->flags & ML_NOCLIMB) &&      // do translucent
+								(rover->spawnflags & FF_NOSHADE) && // do not include light blocks, which don't set FF_NOSHADE
+								!(rover->spawnflags & FF_RENDERSIDES) &&
+								!(rover->spawnflags & FF_RENDERPLANES) &&
+								!(rover->flags & FF_RENDERALL))
+								rover->alpha = 1;
+
+							P_RemoveFakeFloorFader(rover);
+							P_FadeFakeFloor(rover,
+								rover->alpha,
+								max(1, min(256, (line->flags & ML_EFFECT3) ? rover->alpha + destvalue : destvalue)),
+								0,                                  // set alpha immediately
+								false, NULL,                        // tic-based logic
+								!(line->flags & ML_BLOCKMONSTERS),  // do not handle FF_EXISTS
+								!(line->flags & ML_NOCLIMB),        // do not handle FF_TRANSLUCENT
+								!(line->flags & ML_EFFECT2),        // do not handle lighting
+								!(line->flags & ML_EFFECT2),        // do not handle colormap (ran out of flags)
+								!(line->flags & ML_BOUNCY),         // do not handle collision
+								(line->flags & ML_EFFECT1),         // do ghost fade (no collision during fade)
+								(line->flags & ML_TFERLINE));       // use exact alpha values (for opengl)
+						}
+					}
+					j++;
+				}
+
+				if (!foundrover)
+				{
+					CONS_Debug(DBG_GAMELOGIC, "Line type 453 Executor: Can't find a FOF control sector with tag %d\n", foftag);
+					return;
+				}
+			}
+			break;
+		}
+
+		case 454: // Stop fading FOF
+		{
+			INT16 sectag = (INT16)(sides[line->sidenum[0]].textureoffset>>FRACBITS);
+			INT16 foftag = (INT16)(sides[line->sidenum[0]].rowoffset>>FRACBITS);
+			sector_t *sec; // Sector that the FOF is visible in
+			ffloor_t *rover; // FOF that we are going to operate
+			boolean foundrover = false; // for debug, "Can't find a FOF" message
+
+			for (secnum = -1; (secnum = P_FindSectorFromTag(sectag, secnum)) >= 0 ;)
+			{
+				sec = sectors + secnum;
+
+				if (!sec->ffloors)
+				{
+					CONS_Debug(DBG_GAMELOGIC, "Line type 454 Executor: Target sector #%d has no FOFs.\n", secnum);
+					return;
+				}
+
+				for (rover = sec->ffloors; rover; rover = rover->next)
+				{
+					if (rover->master->frontsector->tag == foftag)
+					{
+						foundrover = true;
+
+						P_ResetFakeFloorFader(rover, NULL,
+							!(line->flags & ML_BLOCKMONSTERS)); // do not finalize collision flags
+					}
+				}
+
+				if (!foundrover)
+				{
+					CONS_Debug(DBG_GAMELOGIC, "Line type 454 Executor: Can't find a FOF control sector with tag %d\n", foftag);
+					return;
+				}
+			}
+			break;
+		}
+
+		case 455: // Fade colormap
+			for (secnum = -1; (secnum = P_FindSectorFromLineTag(line, secnum)) >= 0 ;)
+			{
+				extracolormap_t *source_exc, *dest_exc, *exc;
+				INT32 speed = (INT32)((line->flags & ML_DONTPEGBOTTOM) || !sides[line->sidenum[0]].rowoffset) && line->sidenum[1] != 0xFFFF ?
+					abs(sides[line->sidenum[1]].rowoffset >> FRACBITS)
+					: abs(sides[line->sidenum[0]].rowoffset >> FRACBITS);
+
+				// Prevent continuous execs from interfering on an existing fade
+				if (!(line->flags & ML_EFFECT5)
+					&& sectors[secnum].fadecolormapdata)
+					//&& ((fadecolormap_t*)sectors[secnum].fadecolormapdata)->timer > (ticbased ? 2 : speed*2))
+				{
+					CONS_Debug(DBG_GAMELOGIC, "Line type 455 Executor: Fade color thinker already exists, timer: %d\n", ((fadecolormap_t*)sectors[secnum].fadecolormapdata)->timer);
+					continue;
+				}
+
+				if (line->flags & ML_TFERLINE) // use back colormap instead of target sector
+					sectors[secnum].extra_colormap = (line->sidenum[1] != 0xFFFF) ?
+						sides[line->sidenum[1]].colormap_data : NULL;
+
+				exc = sectors[secnum].extra_colormap;
+
+				if (!(line->flags & ML_BOUNCY) // BOUNCY: Do not override fade from default rgba
+					&& !R_CheckDefaultColormap(sides[line->sidenum[0]].colormap_data, true, false, false)
+					&& R_CheckDefaultColormap(exc, true, false, false))
+				{
+					exc = R_CopyColormap(exc, false);
+					exc->rgba = R_GetRgbaRGB(sides[line->sidenum[0]].colormap_data->rgba) + R_PutRgbaA(R_GetRgbaA(exc->rgba));
+					//exc->fadergba = R_GetRgbaRGB(sides[line->sidenum[0]].colormap_data->rgba) + R_PutRgbaA(R_GetRgbaA(exc->fadergba));
+
+					if (!(source_exc = R_GetColormapFromList(exc)))
+					{
+						exc->colormap = R_CreateLightTable(exc);
+						R_AddColormapToList(exc);
+						source_exc = exc;
+					}
+					else
+						Z_Free(exc);
+
+					sectors[secnum].extra_colormap = source_exc;
+				}
+				else
+					source_exc = exc ? exc : R_GetDefaultColormap();
+
+				if (line->flags & ML_EFFECT3) // relative calc
+				{
+					exc = R_AddColormaps(
+						source_exc,
+						sides[line->sidenum[0]].colormap_data,
+						line->flags & ML_EFFECT1,  // subtract R
+						line->flags & ML_NOCLIMB,  // subtract G
+						line->flags & ML_EFFECT2,  // subtract B
+						false,                     // subtract A (no flag for this, just pass negative alpha)
+						line->flags & ML_EFFECT1,  // subtract FadeR
+						line->flags & ML_NOCLIMB,  // subtract FadeG
+						line->flags & ML_EFFECT2,  // subtract FadeB
+						false,                     // subtract FadeA (no flag for this, just pass negative alpha)
+						false,                     // subtract FadeStart (we ran out of flags)
+						false,                     // subtract FadeEnd (we ran out of flags)
+						false,                     // ignore Fog (we ran out of flags)
+						line->flags & ML_DONTPEGBOTTOM,
+						(line->flags & ML_DONTPEGBOTTOM) ? (sides[line->sidenum[0]].textureoffset >> FRACBITS) : 0,
+						(line->flags & ML_DONTPEGBOTTOM) ? (sides[line->sidenum[0]].rowoffset >> FRACBITS) : 0,
+						false);
+				}
+				else if (line->flags & ML_DONTPEGBOTTOM) // alternate alpha (by texture offsets)
+				{
+					exc = R_CopyColormap(sides[line->sidenum[0]].colormap_data, false);
+					exc->rgba = R_GetRgbaRGB(exc->rgba) + R_PutRgbaA(max(min(sides[line->sidenum[0]].textureoffset >> FRACBITS, 25), 0));
+					exc->fadergba = R_GetRgbaRGB(exc->fadergba) + R_PutRgbaA(max(min(sides[line->sidenum[0]].rowoffset >> FRACBITS, 25), 0));
+				}
+				else
+					exc = R_CopyColormap(sides[line->sidenum[0]].colormap_data, false);
+
+				if (!(dest_exc = R_GetColormapFromList(exc)))
+				{
+					exc->colormap = R_CreateLightTable(exc);
+					R_AddColormapToList(exc);
+					dest_exc = exc;
+				}
+				else
+					Z_Free(exc);
+
+				Add_ColormapFader(&sectors[secnum], source_exc, dest_exc, (line->flags & ML_EFFECT4), // tic-based timing
+					speed);
+			}
+			break;
+
+		case 456: // Stop fade colormap
+			for (secnum = -1; (secnum = P_FindSectorFromLineTag(line, secnum)) >= 0 ;)
+				P_ResetColormapFader(&sectors[secnum]);
+			break;
+
 #ifdef POLYOBJECTS
 		case 480: // Polyobj_DoorSlide
 		case 481: // Polyobj_DoorSwing
@@ -3120,6 +3784,9 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 		case 491:
 			PolyTranslucency(line);
 			break;
+		case 492:
+			PolyFade(line);
+			break;
 #endif
 
 		default:
@@ -3527,7 +4194,7 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
 			if (player->exiting || player->bot) // Don't do anything for bots or players who have just finished
 				break;
 
-			if (!(player->powers[pw_shield] || player->rings > 0)) // Don't do anything if no shield or rings anyway
+			if (!(player->powers[pw_shield] || player->spheres > 0)) // Don't do anything if no shield or spheres anyway
 				break;
 
 			P_SpecialStageDamage(player, NULL, NULL);
@@ -3775,8 +4442,8 @@ DoneSection2:
 		case 2: // Special stage GOAL sector / Exit Sector / CTF Flag Return
 			if (player->bot)
 				break;
-			if (!useNightsSS && G_IsSpecialStage(gamemap) && sstimer > 6)
-				sstimer = 6; // Just let P_Ticker take care of the rest.
+			if (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap) && player->nightstime > 6)
+				player->nightstime = 6; // Just let P_Ticker take care of the rest.
 
 			// Exit (for FOF exits; others are handled in P_PlayerThink in p_user.c)
 			{
@@ -4643,7 +5310,7 @@ static void P_RunSpecialSectorCheck(player_t *player, sector_t *sector)
 	switch(GETSECSPECIAL(sector->special, 4))
 	{
 		case 2: // Level Exit / GOAL Sector / Flag Return
-			if (!useNightsSS && G_IsSpecialStage(gamemap))
+			if (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap))
 			{
 				// Special stage GOAL sector
 				// requires touching floor.
@@ -4938,7 +5605,7 @@ static ffloor_t *P_AddFakeFloor(sector_t *sec, sector_t *sec2, line_t *master, f
 	ffloor->spawnflags = ffloor->flags = flags;
 	ffloor->master = master;
 	ffloor->norender = INFTICS;
-
+	ffloor->fadingdata = NULL;
 
 	// Scan the thinkers to check for special conditions applying to this FOF.
 	// If we have thinkers sorted by sector, just check the relevant ones;
@@ -5502,7 +6169,7 @@ void P_InitSpecials(void)
 
 	// Defaults in case levels don't have them set.
 	sstimer = 90*TICRATE + 6;
-	totalrings = 1;
+	ssspheres = 1;
 
 	CheckForBustableBlocks = CheckForBouncySector = CheckForQuicksand = CheckForMarioBlocks = CheckForFloatBob = CheckForReverseGravity = false;
 
@@ -5596,7 +6263,7 @@ void P_SpawnSpecials(INT32 fromnetsave)
 		{
 			case 10: // Time for special stage
 				sstimer = (sector->floorheight>>FRACBITS) * TICRATE + 6; // Time to finish
-				totalrings = sector->ceilingheight>>FRACBITS; // Ring count for special stage
+				ssspheres = sector->ceilingheight>>FRACBITS; // Ring count for special stage
 				break;
 
 			case 11: // Custom global gravity!
@@ -6419,6 +7086,17 @@ void P_SpawnSpecials(INT32 fromnetsave)
 				}
 				break;
 
+			// NiGHTS trigger executors
+			case 323:
+			case 324:
+			case 325:
+			case 326:
+			case 327:
+			case 328:
+			case 329:
+			case 330:
+				break;
+
 			case 399: // Linedef execute on map load
 				// This is handled in P_RunLevelLoadExecutors.
 				break;
@@ -6532,7 +7210,7 @@ void P_SpawnSpecials(INT32 fromnetsave)
 
 			case 606: // HACK! Copy colormaps. Just plain colormaps.
 				for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;)
-					sectors[s].midmap = lines[i].frontsector->midmap;
+					sectors[s].extra_colormap = sectors[s].spawn_extra_colormap = sides[lines[i].sidenum[0]].colormap_data;
 				break;
 
 #ifdef ESLOPE // Slope copy specials. Handled here for sanity.
@@ -7094,6 +7772,591 @@ void T_Disappear(disappear_t *d)
 	}
 }
 
+/** Removes fadingdata from FOF control sector
+ *
+ * \param line	line to search for target faders
+ * \param data	pointer to set new fadingdata to. Can be NULL to erase.
+ */
+static void P_ResetFakeFloorFader(ffloor_t *rover, fade_t *data, boolean finalize)
+{
+	fade_t *fadingdata = (fade_t *)rover->fadingdata;
+	// find any existing thinkers and remove them, then replace with new data
+	if (fadingdata != data)
+	{
+		if (fadingdata)
+		{
+			if (finalize)
+				P_FadeFakeFloor(rover,
+					fadingdata->sourcevalue,
+					fadingdata->alpha >= fadingdata->destvalue ?
+						fadingdata->alpha - 1 : // trigger fade-out finish
+						fadingdata->alpha + 1, // trigger fade-in finish
+					0,
+					fadingdata->ticbased,
+					&fadingdata->timer,
+					fadingdata->doexists,
+					fadingdata->dotranslucent,
+					fadingdata->dolighting,
+					fadingdata->docolormap,
+					fadingdata->docollision,
+					fadingdata->doghostfade,
+					fadingdata->exactalpha);
+			rover->alpha = fadingdata->alpha;
+
+			if (fadingdata->dolighting)
+				P_RemoveLighting(&sectors[rover->secnum]);
+
+			if (fadingdata->docolormap)
+				P_ResetColormapFader(&sectors[rover->secnum]);
+
+			P_RemoveThinker(&fadingdata->thinker);
+		}
+
+		rover->fadingdata = data;
+	}
+}
+
+static boolean P_FadeFakeFloor(ffloor_t *rover, INT16 sourcevalue, INT16 destvalue, INT16 speed, boolean ticbased, INT32 *timer,
+	boolean doexists, boolean dotranslucent, boolean dolighting, boolean docolormap,
+	boolean docollision, boolean doghostfade, boolean exactalpha)
+{
+	boolean stillfading = false;
+	INT32 alpha;
+	fade_t *fadingdata = (fade_t *)rover->fadingdata;
+	(void)docolormap; // *shrug* maybe we can use this in the future. For now, let's be consistent with our other function params
+
+	if (rover->master->special == 258) // Laser block
+		return false;
+
+	// If fading an invisible FOF whose render flags we did not yet set,
+	// initialize its alpha to 1
+	if (dotranslucent &&
+		(rover->spawnflags & FF_NOSHADE) && // do not include light blocks, which don't set FF_NOSHADE
+		!(rover->flags & FF_FOG) && // do not include fog
+		!(rover->spawnflags & FF_RENDERSIDES) &&
+		!(rover->spawnflags & FF_RENDERPLANES) &&
+		!(rover->flags & FF_RENDERALL))
+		rover->alpha = 1;
+
+	if (fadingdata)
+		alpha = fadingdata->alpha;
+	else
+		alpha = rover->alpha;
+
+	// routines specific to fade in and fade out
+	if (!ticbased && alpha == destvalue)
+		return stillfading;
+	else if (alpha > destvalue) // fade out
+	{
+		// finish fading out
+		if (speed < 1 || (!ticbased && alpha - speed <= destvalue + speed) ||
+			(ticbased && (--(*timer) <= 0 || alpha <= destvalue)))
+		{
+			alpha = destvalue;
+
+			if (docollision)
+			{
+				if (rover->spawnflags & FF_SOLID)
+					rover->flags &= ~FF_SOLID;
+				if (rover->spawnflags & FF_SWIMMABLE)
+					rover->flags &= ~FF_SWIMMABLE;
+				if (rover->spawnflags & FF_QUICKSAND)
+					rover->flags &= ~FF_QUICKSAND;
+				if (rover->spawnflags & FF_BUSTUP)
+					rover->flags &= ~FF_BUSTUP;
+				if (rover->spawnflags & FF_MARIO)
+					rover->flags &= ~FF_MARIO;
+			}
+		}
+		else // continue fading out
+		{
+			if (!ticbased)
+				alpha -= speed;
+			else
+			{
+				INT16 delta = abs(destvalue - sourcevalue);
+				fixed_t factor = min(FixedDiv(speed - (*timer), speed), 1*FRACUNIT);
+				alpha = max(min(alpha, sourcevalue - (INT16)FixedMul(delta, factor)), destvalue);
+			}
+			stillfading = true;
+		}
+	}
+	else // fade in
+	{
+		// finish fading in
+		if (speed < 1 || (!ticbased && alpha + speed >= destvalue - speed) ||
+			(ticbased && (--(*timer) <= 0 || alpha >= destvalue)))
+		{
+			alpha = destvalue;
+
+			if (docollision)
+			{
+				if (rover->spawnflags & FF_SOLID)
+					rover->flags |= FF_SOLID;
+				if (rover->spawnflags & FF_SWIMMABLE)
+					rover->flags |= FF_SWIMMABLE;
+				if (rover->spawnflags & FF_QUICKSAND)
+					rover->flags |= FF_QUICKSAND;
+				if (rover->spawnflags & FF_BUSTUP)
+					rover->flags |= FF_BUSTUP;
+				if (rover->spawnflags & FF_MARIO)
+					rover->flags |= FF_MARIO;
+			}
+		}
+		else // continue fading in
+		{
+			if (!ticbased)
+				alpha += speed;
+			else
+			{
+				INT16 delta = abs(destvalue - sourcevalue);
+				fixed_t factor = min(FixedDiv(speed - (*timer), speed), 1*FRACUNIT);
+				alpha = min(max(alpha, sourcevalue + (INT16)FixedMul(delta, factor)), destvalue);
+			}
+			stillfading = true;
+		}
+	}
+
+	// routines common to both fade in and fade out
+	if (!stillfading)
+	{
+		if (doexists && !(rover->spawnflags & FF_BUSTUP))
+		{
+			if (alpha <= 1)
+				rover->flags &= ~FF_EXISTS;
+			else
+				rover->flags |= FF_EXISTS;
+
+			// Re-render lighting at end of fade
+			if (dolighting && !(rover->spawnflags & FF_NOSHADE) && !(rover->flags & FF_EXISTS))
+				rover->target->moved = true;
+		}
+
+		if (dotranslucent && !(rover->flags & FF_FOG))
+		{
+			if (alpha >= 256)
+			{
+				if (!(rover->flags & FF_CUTSOLIDS) &&
+					(rover->spawnflags & FF_CUTSOLIDS))
+				{
+					rover->flags |= FF_CUTSOLIDS;
+					rover->target->moved = true;
+				}
+
+				rover->flags &= ~FF_TRANSLUCENT;
+			}
+			else
+			{
+				rover->flags |= FF_TRANSLUCENT;
+
+				if ((rover->flags & FF_CUTSOLIDS) &&
+					(rover->spawnflags & FF_CUTSOLIDS))
+				{
+					rover->flags &= ~FF_CUTSOLIDS;
+					rover->target->moved = true;
+				}
+			}
+
+			if ((rover->spawnflags & FF_NOSHADE) && // do not include light blocks, which don't set FF_NOSHADE
+				!(rover->spawnflags & FF_RENDERSIDES) &&
+				!(rover->spawnflags & FF_RENDERPLANES))
+			{
+				if (rover->alpha > 1)
+					rover->flags |= FF_RENDERALL;
+				else
+					rover->flags &= ~FF_RENDERALL;
+			}
+		}
+	}
+	else
+	{
+		if (doexists && !(rover->spawnflags & FF_BUSTUP))
+		{
+			// Re-render lighting if we haven't yet set FF_EXISTS (beginning of fade)
+			if (dolighting && !(rover->spawnflags & FF_NOSHADE) && !(rover->flags & FF_EXISTS))
+				rover->target->moved = true;
+
+			rover->flags |= FF_EXISTS;
+		}
+
+		if (dotranslucent && !(rover->flags & FF_FOG))
+		{
+			rover->flags |= FF_TRANSLUCENT;
+
+			if ((rover->flags & FF_CUTSOLIDS) &&
+				(rover->spawnflags & FF_CUTSOLIDS))
+			{
+				rover->flags &= ~FF_CUTSOLIDS;
+				rover->target->moved = true;
+			}
+
+			if ((rover->spawnflags & FF_NOSHADE) && // do not include light blocks, which don't set FF_NOSHADE
+				!(rover->spawnflags & FF_RENDERSIDES) &&
+				!(rover->spawnflags & FF_RENDERPLANES))
+				rover->flags |= FF_RENDERALL;
+		}
+
+		if (docollision)
+		{
+			if (doghostfade) // remove collision flags during fade
+			{
+				if (rover->spawnflags & FF_SOLID)
+					rover->flags &= ~FF_SOLID;
+				if (rover->spawnflags & FF_SWIMMABLE)
+					rover->flags &= ~FF_SWIMMABLE;
+				if (rover->spawnflags & FF_QUICKSAND)
+					rover->flags &= ~FF_QUICKSAND;
+				if (rover->spawnflags & FF_BUSTUP)
+					rover->flags &= ~FF_BUSTUP;
+				if (rover->spawnflags & FF_MARIO)
+					rover->flags &= ~FF_MARIO;
+			}
+			else // keep collision during fade
+			{
+				if (rover->spawnflags & FF_SOLID)
+					rover->flags |= FF_SOLID;
+				if (rover->spawnflags & FF_SWIMMABLE)
+					rover->flags |= FF_SWIMMABLE;
+				if (rover->spawnflags & FF_QUICKSAND)
+					rover->flags |= FF_QUICKSAND;
+				if (rover->spawnflags & FF_BUSTUP)
+					rover->flags |= FF_BUSTUP;
+				if (rover->spawnflags & FF_MARIO)
+					rover->flags |= FF_MARIO;
+			}
+		}
+	}
+
+	if (!(rover->flags & FF_FOG)) // don't set FOG alpha
+	{
+		if (!stillfading || exactalpha)
+			rover->alpha = alpha;
+		else // clamp fadingdata->alpha to software's alpha levels
+		{
+			if (alpha < 12)
+				rover->alpha = destvalue < 12 ? destvalue : 1; // Don't even draw it
+			else if (alpha < 38)
+				rover->alpha = destvalue >= 12 && destvalue < 38 ? destvalue : 25;
+			else if (alpha < 64)
+				rover->alpha = destvalue >=38 && destvalue < 64 ? destvalue : 51;
+			else if (alpha < 89)
+				rover->alpha = destvalue >= 64 && destvalue < 89 ? destvalue : 76;
+			else if (alpha < 115)
+				rover->alpha = destvalue >= 89 && destvalue < 115 ? destvalue : 102;
+			else if (alpha < 140)
+				rover->alpha = destvalue >= 115 && destvalue < 140 ? destvalue : 128;
+			else if (alpha < 166)
+				rover->alpha = destvalue >= 140 && destvalue < 166 ? destvalue : 154;
+			else if (alpha < 192)
+				rover->alpha = destvalue >= 166 && destvalue < 192 ? destvalue : 179;
+			else if (alpha < 217)
+				rover->alpha = destvalue >= 192 && destvalue < 217 ? destvalue : 204;
+			else if (alpha < 243)
+				rover->alpha = destvalue >= 217 && destvalue < 243 ? destvalue : 230;
+			else // Opaque
+				rover->alpha = destvalue >= 243 ? destvalue : 256;
+		}
+	}
+
+	if (fadingdata)
+		fadingdata->alpha = alpha;
+
+	return stillfading;
+}
+
+/** Adds fake floor fader thinker.
+  *
+  * \param destvalue    transparency value to fade to
+  * \param speed        speed to fade by
+  * \param ticbased     tic-based logic, speed = duration
+  * \param relative     Destvalue is relative to rover->alpha
+  * \param doexists	    handle FF_EXISTS
+  * \param dotranslucent handle FF_TRANSLUCENT
+  * \param dolighting  fade FOF light
+  * \param docollision handle interactive flags
+  * \param doghostfade  no interactive flags during fading
+  * \param exactalpha   use exact alpha values (opengl)
+  */
+static void P_AddFakeFloorFader(ffloor_t *rover, size_t sectornum, size_t ffloornum,
+	INT16 destvalue, INT16 speed, boolean ticbased, boolean relative,
+	boolean doexists, boolean dotranslucent, boolean dolighting, boolean docolormap,
+	boolean docollision, boolean doghostfade, boolean exactalpha)
+{
+	fade_t *d;
+
+	// If fading an invisible FOF whose render flags we did not yet set,
+	// initialize its alpha to 1
+	if (dotranslucent &&
+		(rover->spawnflags & FF_NOSHADE) && // do not include light blocks, which don't set FF_NOSHADE
+		!(rover->spawnflags & FF_RENDERSIDES) &&
+		!(rover->spawnflags & FF_RENDERPLANES) &&
+		!(rover->flags & FF_RENDERALL))
+		rover->alpha = 1;
+
+	// already equal, nothing to do
+	if (rover->alpha == max(1, min(256, relative ? rover->alpha + destvalue : destvalue)))
+		return;
+
+	d = Z_Malloc(sizeof *d, PU_LEVSPEC, NULL);
+
+	d->thinker.function.acp1 = (actionf_p1)T_Fade;
+	d->rover = rover;
+	d->sectornum = (UINT32)sectornum;
+	d->ffloornum = (UINT32)ffloornum;
+
+	d->alpha = d->sourcevalue = rover->alpha;
+	d->destvalue = max(1, min(256, relative ? rover->alpha + destvalue : destvalue)); // ffloor->alpha is 1-256
+
+	if (ticbased)
+	{
+		d->ticbased = true;
+		d->timer = d->speed = abs(speed); // use d->speed as total duration
+	}
+	else
+	{
+		d->ticbased = false;
+		d->speed = max(1, speed); // minimum speed 1/tic // if speed < 1, alpha is set immediately in thinker
+		d->timer = -1;
+	}
+
+	d->doexists = doexists;
+	d->dotranslucent = dotranslucent;
+	d->dolighting = dolighting;
+	d->docolormap = docolormap;
+	d->docollision = docollision;
+	d->doghostfade = doghostfade;
+	d->exactalpha = exactalpha;
+
+	// find any existing thinkers and remove them, then replace with new data
+	P_ResetFakeFloorFader(rover, d, false);
+
+	// Set a separate thinker for shadow fading
+	if (dolighting && !(rover->flags & FF_NOSHADE))
+	{
+		UINT16 lightdelta = abs(sectors[rover->secnum].spawn_lightlevel - rover->target->lightlevel);
+		fixed_t alphapercent = min(FixedDiv(d->destvalue, rover->spawnalpha), 1*FRACUNIT); // don't make darker than spawn_lightlevel
+		fixed_t adjustedlightdelta = FixedMul(lightdelta, alphapercent);
+
+		if (rover->target->lightlevel >= sectors[rover->secnum].spawn_lightlevel) // fading out, get lighter
+			d->destlightlevel = rover->target->lightlevel - adjustedlightdelta;
+		else // fading in, get darker
+			d->destlightlevel = rover->target->lightlevel + adjustedlightdelta;
+
+		P_FadeLightBySector(&sectors[rover->secnum],
+			d->destlightlevel,
+			ticbased ? d->timer :
+				FixedFloor(FixedDiv(abs(d->destvalue - d->alpha), d->speed))/FRACUNIT,
+			true);
+	}
+	else
+		d->destlightlevel = -1;
+
+	// Set a separate thinker for colormap fading
+	if (docolormap && !(rover->flags & FF_NOSHADE) && sectors[rover->secnum].spawn_extra_colormap)
+	{
+		extracolormap_t *dest_exc,
+			*source_exc = sectors[rover->secnum].extra_colormap ? sectors[rover->secnum].extra_colormap : R_GetDefaultColormap();
+
+		INT32 colordelta = R_GetRgbaA(sectors[rover->secnum].spawn_extra_colormap->rgba); // alpha is from 0
+		fixed_t alphapercent = min(FixedDiv(d->destvalue, rover->spawnalpha), 1*FRACUNIT); // don't make darker than spawn_lightlevel
+		fixed_t adjustedcolordelta = FixedMul(colordelta, alphapercent);
+		INT32 coloralpha;
+
+		coloralpha = adjustedcolordelta;
+
+		dest_exc = R_CopyColormap(sectors[rover->secnum].spawn_extra_colormap, false);
+		dest_exc->rgba = R_GetRgbaRGB(dest_exc->rgba) + R_PutRgbaA(coloralpha);
+
+		if (!(d->dest_exc = R_GetColormapFromList(dest_exc)))
+		{
+			dest_exc->colormap = R_CreateLightTable(dest_exc);
+			R_AddColormapToList(dest_exc);
+			d->dest_exc = dest_exc;
+		}
+		else
+			Z_Free(dest_exc);
+
+		// If fading from 0, set source_exc rgb same to dest_exc
+		if (!R_CheckDefaultColormap(d->dest_exc, true, false, false)
+			&& R_CheckDefaultColormap(source_exc, true, false, false))
+		{
+			extracolormap_t *exc = R_CopyColormap(source_exc, false);
+			exc->rgba = R_GetRgbaRGB(d->dest_exc->rgba) + R_PutRgbaA(R_GetRgbaA(source_exc->rgba));
+			exc->fadergba = R_GetRgbaRGB(d->dest_exc->rgba) + R_PutRgbaA(R_GetRgbaA(source_exc->fadergba));
+
+			if (!(source_exc = R_GetColormapFromList(exc)))
+			{
+				exc->colormap = R_CreateLightTable(exc);
+				R_AddColormapToList(exc);
+				source_exc = exc;
+			}
+			else
+				Z_Free(exc);
+		}
+
+		Add_ColormapFader(&sectors[rover->secnum], source_exc, d->dest_exc, true,
+			ticbased ? d->timer :
+				FixedFloor(FixedDiv(abs(d->destvalue - d->alpha), d->speed))/FRACUNIT);
+	}
+
+	P_AddThinker(&d->thinker);
+}
+
+/** Makes a FOF fade
+  *
+  * \param d Fade thinker.
+  * \sa P_AddFakeFloorFader
+  */
+void T_Fade(fade_t *d)
+{
+	if (d->rover && !P_FadeFakeFloor(d->rover, d->sourcevalue, d->destvalue, d->speed, d->ticbased, &d->timer,
+		d->doexists, d->dotranslucent, d->dolighting, d->docolormap, d->docollision, d->doghostfade, d->exactalpha))
+	{
+		// Finalize lighting, copypasta from P_AddFakeFloorFader
+		if (d->dolighting && !(d->rover->flags & FF_NOSHADE) && d->destlightlevel > -1)
+			sectors[d->rover->secnum].lightlevel = d->destlightlevel;
+
+		// Finalize colormap
+		if (d->docolormap && !(d->rover->flags & FF_NOSHADE) && sectors[d->rover->secnum].spawn_extra_colormap)
+			sectors[d->rover->secnum].extra_colormap = d->dest_exc;
+
+		P_RemoveFakeFloorFader(d->rover);
+	}
+}
+
+static void P_ResetColormapFader(sector_t *sector)
+{
+	if (sector->fadecolormapdata)
+	{
+		// The thinker is the first member in all the action structs,
+		// so just let the thinker get freed, and that will free the whole
+		// structure.
+		P_RemoveThinker(&((elevator_t *)sector->fadecolormapdata)->thinker);
+		sector->fadecolormapdata = NULL;
+	}
+}
+
+static void Add_ColormapFader(sector_t *sector, extracolormap_t *source_exc, extracolormap_t *dest_exc,
+	boolean ticbased, INT32 duration)
+{
+	fadecolormap_t *d;
+
+	P_ResetColormapFader(sector);
+
+	// nothing to do, set immediately
+	if (!duration || R_CheckEqualColormaps(source_exc, dest_exc, true, true, true))
+	{
+		sector->extra_colormap = dest_exc;
+		return;
+	}
+
+	d = Z_Malloc(sizeof *d, PU_LEVSPEC, NULL);
+	d->thinker.function.acp1 = (actionf_p1)T_FadeColormap;
+	d->sector = sector;
+	d->source_exc = source_exc;
+	d->dest_exc = dest_exc;
+
+	if (ticbased)
+	{
+		d->ticbased = true;
+		d->duration = d->timer = duration;
+	}
+	else
+	{
+		d->ticbased = false;
+		d->timer = 256;
+		d->duration = duration; // use as speed
+	}
+
+	sector->fadecolormapdata = d;
+	P_AddThinker(&d->thinker); // add thinker
+}
+
+void T_FadeColormap(fadecolormap_t *d)
+{
+	if ((d->ticbased && --d->timer <= 0)
+		|| (!d->ticbased && (d->timer -= d->duration) <= 0)) // d->duration used as speed decrement
+	{
+		d->sector->extra_colormap = d->dest_exc;
+		P_ResetColormapFader(d->sector);
+	}
+	else
+	{
+		extracolormap_t *exc;
+		INT32 duration = d->ticbased ? d->duration : 256;
+		fixed_t factor = min(FixedDiv(duration - d->timer, duration), 1*FRACUNIT);
+		INT16 cr, cg, cb, ca, fadestart, fadeend, fog;
+		INT32 rgba, fadergba;
+
+		// NULL failsafes (or intentionally set to signify default)
+		if (!d->sector->extra_colormap)
+			d->sector->extra_colormap = R_GetDefaultColormap();
+
+		if (!d->source_exc)
+			d->source_exc = R_GetDefaultColormap();
+
+		if (!d->dest_exc)
+			d->dest_exc = R_GetDefaultColormap();
+
+		// For each var (rgba + fadergba + params = 11 vars), we apply
+		// percentage fading: currentval = sourceval + (delta * percent of duration elapsed)
+		// delta is negative when fading out (destval is lower)
+		// max/min are used to ensure progressive calcs don't go backwards and to cap values to dest.
+
+#define APPLYFADE(dest, src, cur) (\
+(dest-src < 0) ? \
+	max(\
+		min(cur,\
+			src + (INT16)FixedMul(dest-src, factor)),\
+		dest)\
+: (dest-src > 0) ? \
+	min(\
+		max(cur,\
+			src + (INT16)FixedMul(dest-src, factor)),\
+		dest)\
+: \
+	dest\
+)
+
+		cr = APPLYFADE(R_GetRgbaR(d->dest_exc->rgba), R_GetRgbaR(d->source_exc->rgba), R_GetRgbaR(d->sector->extra_colormap->rgba));
+		cg = APPLYFADE(R_GetRgbaG(d->dest_exc->rgba), R_GetRgbaG(d->source_exc->rgba), R_GetRgbaG(d->sector->extra_colormap->rgba));
+		cb = APPLYFADE(R_GetRgbaB(d->dest_exc->rgba), R_GetRgbaB(d->source_exc->rgba), R_GetRgbaB(d->sector->extra_colormap->rgba));
+		ca = APPLYFADE(R_GetRgbaA(d->dest_exc->rgba), R_GetRgbaA(d->source_exc->rgba), R_GetRgbaA(d->sector->extra_colormap->rgba));
+
+		rgba = R_PutRgbaRGBA(cr, cg, cb, ca);
+
+		cr = APPLYFADE(R_GetRgbaR(d->dest_exc->fadergba), R_GetRgbaR(d->source_exc->fadergba), R_GetRgbaR(d->sector->extra_colormap->fadergba));
+		cg = APPLYFADE(R_GetRgbaG(d->dest_exc->fadergba), R_GetRgbaG(d->source_exc->fadergba), R_GetRgbaG(d->sector->extra_colormap->fadergba));
+		cb = APPLYFADE(R_GetRgbaB(d->dest_exc->fadergba), R_GetRgbaB(d->source_exc->fadergba), R_GetRgbaB(d->sector->extra_colormap->fadergba));
+		ca = APPLYFADE(R_GetRgbaA(d->dest_exc->fadergba), R_GetRgbaA(d->source_exc->fadergba), R_GetRgbaA(d->sector->extra_colormap->fadergba));
+
+		fadergba = R_PutRgbaRGBA(cr, cg, cb, ca);
+
+		fadestart = APPLYFADE(d->dest_exc->fadestart, d->source_exc->fadestart, d->sector->extra_colormap->fadestart);
+		fadeend = APPLYFADE(d->dest_exc->fadeend, d->source_exc->fadeend, d->sector->extra_colormap->fadeend);
+		fog = abs(factor) > FRACUNIT/2 ? d->dest_exc->fog : d->source_exc->fog; // set new fog flag halfway through fade
+
+#undef APPLYFADE
+
+		//////////////////
+		// setup new colormap
+		//////////////////
+
+		if (!(d->sector->extra_colormap = R_GetColormapFromListByValues(rgba, fadergba, fadestart, fadeend, fog)))
+		{
+			exc = R_CreateDefaultColormap(false);
+			exc->fadestart = fadestart;
+			exc->fadeend = fadeend;
+			exc->fog = (boolean)fog;
+			exc->rgba = rgba;
+			exc->fadergba = fadergba;
+			exc->colormap = R_CreateLightTable(exc);
+			R_AddColormapToList(exc);
+			d->sector->extra_colormap = exc;
+		}
+	}
+}
+
 /*
  SoM: 3/8/2000: Friction functions start.
  Add_Friction,
@@ -7683,7 +8946,6 @@ void T_Pusher(pusher_t *p)
 					thing->player->pflags |= jumped;
 
 				thing->player->pflags |= PF_SLIDING;
-				P_SetPlayerMobjState (thing, thing->info->painstate); // Whee!
 				thing->angle = R_PointToAngle2 (0, 0, xspeed<<(FRACBITS-PUSH_FACTOR), yspeed<<(FRACBITS-PUSH_FACTOR));
 
 				if (!demoplayback || P_AnalogMove(thing->player))
diff --git a/src/p_spec.h b/src/p_spec.h
index c4e05e0723485c4fd226227a463234eeab5c02f1..f04e6590f667de3dfb338a56e3030b4f0ca1ea49 100644
--- a/src/p_spec.h
+++ b/src/p_spec.h
@@ -66,6 +66,10 @@ void P_SwitchWeather(INT32 weathernum);
 boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller);
 void P_LinedefExecute(INT16 tag, mobj_t *actor, sector_t *caller);
 void P_ChangeSectorTag(UINT32 sector, INT16 newtag);
+void P_RunNightserizeExecutors(mobj_t *actor);
+void P_RunDeNightserizeExecutors(mobj_t *actor);
+void P_RunNightsLapExecutors(mobj_t *actor);
+void P_RunNightsCapsuleTouchExecutors(mobj_t *actor, boolean entering, boolean enoughspheres);
 
 ffloor_t *P_GetFFloorByID(sector_t *sec, UINT16 id);
 
@@ -130,10 +134,15 @@ typedef struct
   */
 typedef struct
 {
-	thinker_t thinker; ///< Thinker in use for the effect.
-	sector_t *sector;  ///< Sector where action is taking place.
-	INT32 destlevel;   ///< Light level we're fading to.
-	INT32 speed;       ///< Speed at which to change light level.
+	thinker_t thinker;		///< Thinker in use for the effect.
+	sector_t *sector;		///< Sector where action is taking place.
+	INT16 sourcelevel;		///< Light level we're fading from.
+	INT16 destlevel;		///< Light level we're fading to.
+
+	fixed_t fixedcurlevel;	///< Fixed point for current light level.
+	fixed_t fixedpertic;	///< Fixed point for increment per tic.
+	// The reason for those two above to be fixed point is to deal with decimal values that would otherwise get trimmed away.
+	INT32 timer;			///< Internal timer.
 } lightlevel_t;
 
 #define GLOWSPEED 8
@@ -141,6 +150,8 @@ typedef struct
 #define FASTDARK 15
 #define SLOWDARK 35
 
+void P_RemoveLighting(sector_t *sector);
+
 void T_FireFlicker(fireflicker_t *flick);
 fireflicker_t *P_SpawnAdjustableFireFlicker(sector_t *minsector, sector_t *maxsector, INT32 length);
 void T_LightningFlash(lightflash_t *flash);
@@ -152,7 +163,8 @@ strobe_t * P_SpawnAdjustableStrobeFlash(sector_t *minsector, sector_t *maxsector
 void T_Glow(glow_t *g);
 glow_t *P_SpawnAdjustableGlowingLight(sector_t *minsector, sector_t *maxsector, INT32 length);
 
-void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed);
+void P_FadeLightBySector(sector_t *sector, INT32 destvalue, INT32 speed, boolean ticbased);
+void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed, boolean ticbased, boolean force);
 void T_LightFade(lightlevel_t *ll);
 
 typedef enum
@@ -449,6 +461,47 @@ typedef struct
 
 void T_Disappear(disappear_t *d);
 
+// Model for fading FOFs
+typedef struct
+{
+	thinker_t thinker;  ///< Thinker structure for effect.
+	ffloor_t *rover;    ///< Target ffloor
+	extracolormap_t *dest_exc; ///< Colormap to fade to
+	UINT32 sectornum;    ///< Number of ffloor target sector
+	UINT32 ffloornum;    ///< Number of ffloor of target sector
+	INT32 alpha;        ///< Internal alpha counter
+	INT16 sourcevalue;  ///< Transparency value to fade from
+	INT16 destvalue;    ///< Transparency value to fade to
+	INT16 destlightlevel; ///< Light level to fade to
+	INT16 speed;        ///< Speed to fade by
+	boolean ticbased;    ///< Tic-based logic toggle
+	INT32 timer;        ///< Timer for tic-based logic
+	boolean doexists;   ///< Handle FF_EXISTS
+	boolean dotranslucent; ///< Handle FF_TRANSLUCENT
+	boolean dolighting; ///< Handle shadows and light blocks
+	boolean docolormap; ///< Handle colormaps
+	boolean docollision; ///< Handle interactive flags
+	boolean doghostfade; ///< No interactive flags during fading
+	boolean exactalpha; ///< Use exact alpha values (opengl)
+} fade_t;
+
+void T_Fade(fade_t *d);
+
+// Model for fading colormaps
+
+typedef struct
+{
+	thinker_t thinker;          ///< Thinker structure for effect.
+	sector_t *sector;           ///< Sector where action is taking place.
+	extracolormap_t *source_exc;
+	extracolormap_t *dest_exc;
+	boolean ticbased;           ///< Tic-based timing
+	INT32 duration;             ///< Total duration for tic-based logic (OR: speed increment)
+	INT32 timer;                ///< Timer for tic-based logic (OR: internal speed counter)
+} fadecolormap_t;
+
+void T_FadeColormap(fadecolormap_t *d);
+
 // Prototype functions for pushers
 void T_Pusher(pusher_t *p);
 mobj_t *P_GetPushThing(UINT32 s);
diff --git a/src/p_tick.c b/src/p_tick.c
index 071cdd4e8935cc2b3d25043d09aa1fb66aaf94ff..71ca83927fa0d824a1cae08d70b51faf7dfa02c5 100644
--- a/src/p_tick.c
+++ b/src/p_tick.c
@@ -56,12 +56,12 @@ void Command_Numthinkers_f(void)
 		CONS_Printf(M_GetText("numthinkers <#>: Count number of thinkers\n"));
 		CONS_Printf(
 			"\t1: P_MobjThinker\n"
-			"\t2: P_RainThinker\n"
-			"\t3: P_SnowThinker\n"
-			"\t4: P_NullPrecipThinker\n"
-			"\t5: T_Friction\n"
-			"\t6: T_Pusher\n"
-			"\t7: P_RemoveThinkerDelayed\n");
+			/*"\t2: P_RainThinker\n"
+			"\t3: P_SnowThinker\n"*/
+			"\t2: P_NullPrecipThinker\n"
+			"\t3: T_Friction\n"
+			"\t4: T_Pusher\n"
+			"\t5: P_RemoveThinkerDelayed\n");
 		return;
 	}
 
@@ -73,27 +73,27 @@ void Command_Numthinkers_f(void)
 			action = (actionf_p1)P_MobjThinker;
 			CONS_Printf(M_GetText("Number of %s: "), "P_MobjThinker");
 			break;
-		case 2:
+		/*case 2:
 			action = (actionf_p1)P_RainThinker;
 			CONS_Printf(M_GetText("Number of %s: "), "P_RainThinker");
 			break;
 		case 3:
 			action = (actionf_p1)P_SnowThinker;
 			CONS_Printf(M_GetText("Number of %s: "), "P_SnowThinker");
-			break;
-		case 4:
+			break;*/
+		case 2:
 			action = (actionf_p1)P_NullPrecipThinker;
 			CONS_Printf(M_GetText("Number of %s: "), "P_NullPrecipThinker");
 			break;
-		case 5:
+		case 3:
 			action = (actionf_p1)T_Friction;
 			CONS_Printf(M_GetText("Number of %s: "), "T_Friction");
 			break;
-		case 6:
+		case 4:
 			action = (actionf_p1)T_Pusher;
 			CONS_Printf(M_GetText("Number of %s: "), "T_Pusher");
 			break;
-		case 7:
+		case 5:
 			action = (actionf_p1)P_RemoveThinkerDelayed;
 			CONS_Printf(M_GetText("Number of %s: "), "P_RemoveThinkerDelayed");
 			break;
@@ -432,7 +432,7 @@ void P_DoTeamscrambling(void)
 
 static inline void P_DoSpecialStageStuff(void)
 {
-	boolean inwater = false;
+	boolean stillalive = false;
 	INT32 i;
 
 	// Can't drown in a special stage
@@ -444,68 +444,60 @@ static inline void P_DoSpecialStageStuff(void)
 		players[i].powers[pw_underwater] = players[i].powers[pw_spacetime] = 0;
 	}
 
-	if (sstimer < 15*TICRATE+6 && sstimer > 7 && (mapheaderinfo[gamemap-1]->levelflags & LF_SPEEDMUSIC))
-		S_SpeedMusic(1.4f);
-
-	if (sstimer < 7 && sstimer > 0) // The special stage time is up!
-	{
-		sstimer = 0;
-		for (i = 0; i < MAXPLAYERS; i++)
-		{
-			if (playeringame[i])
-			{
-				players[i].exiting = (14*TICRATE)/5 + 1;
-				players[i].pflags &= ~PF_GLIDING;
-			}
-
-			if (i == consoleplayer)
-				S_StartSound(NULL, sfx_lose);
-		}
-
-		if (mapheaderinfo[gamemap-1]->levelflags & LF_SPEEDMUSIC)
-			S_SpeedMusic(1.0f);
+	//if (sstimer < 15*TICRATE+6 && sstimer > 7 && (mapheaderinfo[gamemap-1]->levelflags & LF_SPEEDMUSIC))
+		//S_SpeedMusic(1.4f);
 
-		stagefailed = true;
-	}
-
-	if (sstimer > 1) // As long as time isn't up...
+	if (sstimer && !objectplacing)
 	{
-		UINT32 ssrings = 0;
+		UINT16 countspheres = 0;
 		// Count up the rings of all the players and see if
 		// they've collected the required amount.
 		for (i = 0; i < MAXPLAYERS; i++)
 			if (playeringame[i])
 			{
-				ssrings += players[i].rings;
+				tic_t oldnightstime = players[i].nightstime;
+				countspheres += players[i].spheres;
 
 				// If in water, deplete timer 6x as fast.
-				if ((players[i].mo->eflags & MFE_TOUCHWATER)
-					|| (players[i].mo->eflags & MFE_UNDERWATER))
-					inwater = true;
-			}
-
-		if (ssrings >= totalrings && totalrings > 0)
-		{
-			// Halt all the players
-			for (i = 0; i < MAXPLAYERS; i++)
-				if (playeringame[i])
+				if (players[i].mo->eflags & (MFE_TOUCHWATER|MFE_UNDERWATER))
+					players[i].nightstime -= 5;
+				if (--players[i].nightstime > 6)
+				{
+					if (P_IsLocalPlayer(&players[i]) && oldnightstime > 10*TICRATE && players[i].nightstime <= 10*TICRATE)
+						S_ChangeMusicInternal("_drown", false);
+					stillalive = true;
+				}
+				else if (!players[i].exiting)
 				{
-					players[i].mo->momx = players[i].mo->momy = 0;
 					players[i].exiting = (14*TICRATE)/5 + 1;
+					players[i].pflags &= ~(PF_GLIDING|PF_BOUNCING);
+					players[i].nightstime = 0;
+					if (P_IsLocalPlayer(&players[i]))
+						S_StartSound(NULL, sfx_s3k66);
 				}
+			}
 
-			sstimer = 0;
-
-			P_GiveEmerald(true);
+		if (stillalive)
+		{
+			if (countspheres >= ssspheres)
+			{
+				// Halt all the players
+				for (i = 0; i < MAXPLAYERS; i++)
+					if (playeringame[i])
+					{
+						players[i].mo->momx = players[i].mo->momy = 0;
+						players[i].exiting = (14*TICRATE)/5 + 1;
+					}
+
+				sstimer = 0;
+				P_GiveEmerald(true);
+				P_RestoreMusic(&players[consoleplayer]);
+			}
 		}
-
-		// Decrement the timer
-		if (!objectplacing)
+		else
 		{
-			if (inwater)
-				sstimer -= 6;
-			else
-				sstimer--;
+			sstimer = 0;
+			stagefailed = true;
 		}
 	}
 }
@@ -614,7 +606,7 @@ void P_Ticker(boolean run)
 	if (!demoplayback) // Don't increment if a demo is playing.
 		totalplaytime++;
 
-	if (!useNightsSS && G_IsSpecialStage(gamemap))
+	if (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap))
 		P_DoSpecialStageStuff();
 
 	if (runemeraldmanager)
diff --git a/src/p_user.c b/src/p_user.c
index cf8f5f0809c1884b25a30e7891cd6596505297ba..ae6b1fa88ffbec2544291e7e8fdadc6cfba385c2 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -283,22 +283,11 @@ boolean P_PlayerMoving(INT32 pnum)
 //
 UINT8 P_GetNextEmerald(void)
 {
-	if (!useNightsSS) // In order
-	{
-		if (!(emeralds & EMERALD1)) return 0;
-		if (!(emeralds & EMERALD2)) return 1;
-		if (!(emeralds & EMERALD3)) return 2;
-		if (!(emeralds & EMERALD4)) return 3;
-		if (!(emeralds & EMERALD5)) return 4;
-		if (!(emeralds & EMERALD6)) return 5;
-		return 6;
-	}
-	else // Depends on stage
-	{
-		if (gamemap < sstage_start || gamemap > sstage_end)
-			return 0;
+	if (gamemap >= sstage_start && gamemap <= sstage_end)
 		return (UINT8)(gamemap - sstage_start);
-	}
+	if (gamemap >= smpstage_start || gamemap <= smpstage_end)
+		return (UINT8)(gamemap - smpstage_start);
+	return 0;
 }
 
 //
@@ -309,20 +298,19 @@ UINT8 P_GetNextEmerald(void)
 //
 void P_GiveEmerald(boolean spawnObj)
 {
-	INT32 i;
-	UINT8 em;
+	UINT8 em = P_GetNextEmerald();
 
 	S_StartSound(NULL, sfx_cgot); // Got the emerald!
-	em = P_GetNextEmerald();
 	emeralds |= (1 << em);
 
-	if (spawnObj)
+	if (spawnObj && playeringame[consoleplayer])
 	{
-		for (i = 0; i < MAXPLAYERS; i++)
-			if (playeringame[i])
-				P_SetMobjState(P_SpawnMobj(players[i].mo->x, players[i].mo->y, players[i].mo->z + players[i].mo->info->height, MT_GOTEMERALD),
-				mobjinfo[MT_GOTEMERALD].spawnstate + em);
-
+		// The Chaos Emerald begins to orbit us!
+		// Only give it to ONE person!
+		mobj_t *emmo = P_SpawnMobjFromMobj(players[consoleplayer].mo, 0, 0, players[consoleplayer].mo->height, MT_GOTEMERALD);
+		P_SetTarget(&emmo->target, players[consoleplayer].mo);
+		P_SetMobjState(emmo, mobjinfo[MT_GOTEMERALD].meleestate + em);
+		P_SetTarget(&players[consoleplayer].mo->tracer, emmo);
 	}
 }
 
@@ -399,6 +387,8 @@ boolean P_TransferToNextMare(player_t *player)
 	CONS_Debug(DBG_NIGHTS, "Mare is %d\n", mare);
 
 	player->mare = mare;
+	player->marelap = 0;
+	player->marebonuslap = 0;
 
 	// scan the thinkers
 	// to find the closest axis point
@@ -586,6 +576,10 @@ static void P_DeNightserizePlayer(player_t *player)
 	player->climbing = 0;
 	player->mo->fuse = 0;
 	player->speed = 0;
+	player->marelap = 0;
+	player->marebonuslap = 0;
+	player->flyangle = 0;
+	player->anotherflyangle = 0;
 	P_SetTarget(&player->mo->target, NULL);
 	P_SetTarget(&player->axis1, P_SetTarget(&player->axis2, NULL));
 
@@ -602,9 +596,6 @@ static void P_DeNightserizePlayer(player_t *player)
 	else if (player == &players[secondarydisplayplayer])
 		localaiming2 = 0;
 
-	// If you screwed up, kiss your score goodbye.
-	player->marescore = 0;
-
 	P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
 
 	// If in a special stage, add some preliminary exit time.
@@ -616,6 +607,11 @@ static void P_DeNightserizePlayer(player_t *player)
 				players[i].nightstime = 1; // force everyone else to fall too.
 		player->exiting = 3*TICRATE;
 		stagefailed = true; // NIGHT OVER
+
+		// If you screwed up, kiss your score and ring bonus goodbye.
+		// But only do this in special stage (and instakill!) In regular stages, wait til we hit the ground.
+		player->marescore = player->spheres =\
+		 player->rings = 0;
 	}
 
 	// Check to see if the player should be killed.
@@ -629,13 +625,19 @@ static void P_DeNightserizePlayer(player_t *player)
 			continue;
 
 		if (mo2->flags2 & MF2_AMBUSH)
+		{
+			player->marescore = player->spheres =\
+			 player->rings = 0;
 			P_DamageMobj(player->mo, NULL, NULL, 1, DMG_INSTAKILL);
+		}
 
 		break;
 	}
 
 	// Restore from drowning music
 	P_RestoreMusic(player);
+
+	P_RunDeNightserizeExecutors(player->mo);
 }
 
 //
@@ -644,7 +646,7 @@ static void P_DeNightserizePlayer(player_t *player)
 // NiGHTS Time!
 void P_NightserizePlayer(player_t *player, INT32 nighttime)
 {
-	INT32 oldmare;
+	UINT8 oldmare, oldmarelap, oldmarebonuslap;
 
 	// Bots can't be NiGHTSerized, silly!1 :P
 	if (player->bot)
@@ -659,6 +661,8 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
 	player->speed = 0;
 	player->climbing = 0;
 	player->secondjump = 0;
+	player->flyangle = 0;
+	player->anotherflyangle = 0;
 
 	player->powers[pw_shield] = SH_NONE;
 	player->powers[pw_super] = 0;
@@ -673,7 +677,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
 		player->followitem = skins[DEFAULTNIGHTSSKIN].followitem;
 	}
 
-	player->nightstime = player->startedtime = nighttime*TICRATE;
+	player->nightstime = player->startedtime = player->lapstartedtime = nighttime*TICRATE;
 	player->bonustime = false;
 
 	P_RestoreMusic(player);
@@ -691,10 +695,13 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
 	}
 
 	oldmare = player->mare;
+	oldmarelap = player->marelap;
+	oldmarebonuslap = player->marebonuslap;
 
-	if (P_TransferToNextMare(player) == false)
+	if (!P_TransferToNextMare(player))
 	{
 		INT32 i;
+		INT32 total_spheres = 0;
 		INT32 total_rings = 0;
 
 		P_SetTarget(&player->mo->target, NULL);
@@ -703,7 +710,10 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
 		{
 			for (i = 0; i < MAXPLAYERS; i++)
 				if (playeringame[i]/* && players[i].powers[pw_carry] == CR_NIGHTSMODE*/)
+				{
+					total_spheres += players[i].spheres;
 					total_rings += players[i].rings;
+				}
 		}
 
 		for (i = 0; i < MAXPLAYERS; i++)
@@ -714,15 +724,19 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
 			players[i].texttimer = (3 * TICRATE) - 10;
 			players[i].textvar = 4; // Score and grades
 			players[i].lastmare = players[i].mare;
+			players[i].lastmarelap = players[i].marelap;
+			players[i].lastmarebonuslap = players[i].marebonuslap;
 			if (G_IsSpecialStage(gamemap))
 			{
+				players[i].finishedspheres = (INT16)total_spheres;
 				players[i].finishedrings = (INT16)total_rings;
-				P_AddPlayerScore(player, total_rings * 50);
+				P_AddPlayerScore(player, total_spheres * 50);
 			}
 			else
 			{
+				players[i].finishedspheres = (INT16)(players[i].spheres);
 				players[i].finishedrings = (INT16)(players[i].rings);
-				P_AddPlayerScore(&players[i], (players[i].rings) * 50);
+				P_AddPlayerScore(&players[i], (players[i].spheres) * 50);
 			}
 
 			// Add score to leaderboards now
@@ -730,22 +744,26 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
 				G_AddTempNightsRecords(players[i].marescore, leveltime - player->marebegunat, players[i].mare + 1);
 
 			// transfer scores anyway
+			players[i].totalmarescore += players[i].marescore;
 			players[i].lastmarescore = players[i].marescore;
 			players[i].marescore = 0;
 
-			players[i].rings = 0;
+			players[i].spheres = players[i].rings = 0;
 			P_DoPlayerExit(&players[i]);
 		}
 	}
 	else if (oldmare != player->mare)
 	{
 		/// \todo Handle multi-mare special stages.
-		// Ring bonus
-		P_AddPlayerScore(player, (player->rings) * 50);
+		// Spheres bonus
+		P_AddPlayerScore(player, (player->spheres) * 50);
 
-		player->lastmare = (UINT8)oldmare;
+		player->lastmare = oldmare;
+		player->lastmarelap = oldmarelap;
+		player->lastmarebonuslap = oldmarebonuslap;
 		player->texttimer = 4*TICRATE;
 		player->textvar = 4; // Score and grades
+		player->finishedspheres = (INT16)(player->spheres);
 		player->finishedrings = (INT16)(player->rings);
 
 		// Add score to temp leaderboards
@@ -753,11 +771,13 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
 			G_AddTempNightsRecords(player->marescore, leveltime - player->marebegunat, (UINT8)(oldmare + 1));
 
 		// Starting a new mare, transfer scores
+		player->totalmarescore += player->marescore;
 		player->lastmarescore = player->marescore;
 		player->marescore = 0;
 		player->marebegunat = leveltime;
+		player->lapbegunat = leveltime;
 
-		player->rings = 0;
+		player->spheres = player->rings = 0;
 	}
 	else
 	{
@@ -770,6 +790,16 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
 			player->texttimer = (UINT8)(110 - timeinmap);
 	}
 
+	// force NiGHTS to face forward or backward
+	if (player->mo->target)
+		player->mo->angle = R_PointToAngle2(player->mo->target->x, player->mo->target->y, player->mo->x, player->mo->y) // player->angle_pos, won't be set on first instance
+			+ ((player->mo->target->flags2 & MF2_AMBUSH) ? // if axis is invert, take the opposite right angle
+				(player->flyangle > 90 && player->flyangle < 270 ? ANGLE_90 : -ANGLE_90)
+				: (player->flyangle > 90 && player->flyangle < 270 ? -ANGLE_90 : ANGLE_90));
+
+	// Do this before setting CR_NIGHTSMODE so we can tell if player was non-NiGHTS
+	P_RunNightserizeExecutors(player->mo);
+
 	player->powers[pw_carry] = CR_NIGHTSMODE;
 }
 
@@ -857,7 +887,7 @@ void P_DoPlayerPain(player_t *player, mobj_t *source, mobj_t *inflictor)
 		}
 		else
 		{
-			ang = R_PointToAngle2(player->mo->momx, player->mo->momy, 0, 0);
+			ang = ((player->mo->momx || player->mo->momy) ? R_PointToAngle2(player->mo->momx, player->mo->momy, 0, 0) : player->drawangle);
 			fallbackspeed = FixedMul(4*FRACUNIT, player->mo->scale);
 		}
 
@@ -923,8 +953,7 @@ void P_GivePlayerRings(player_t *player, INT32 num_rings)
 
 	player->rings += num_rings;
 
-	if (!G_IsSpecialStage(gamemap) || !useNightsSS)
-		player->totalring += num_rings;
+	player->totalring += num_rings;
 
 	// Can only get up to 9999 rings, sorry!
 	if (player->rings > 9999)
@@ -933,7 +962,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;
 
@@ -956,6 +985,26 @@ void P_GivePlayerRings(player_t *player, INT32 num_rings)
 	}
 }
 
+void P_GivePlayerSpheres(player_t *player, INT32 num_spheres)
+{
+	if (!player)
+		return;
+
+	if (player->bot)
+		player = &players[consoleplayer];
+
+	if (!player->mo)
+		return;
+
+	player->spheres += num_spheres;
+
+	// Can only get up to 9999 spheres, sorry!
+	if (player->spheres > 9999)
+		player->spheres = 9999;
+	else if (player->spheres < 0)
+		player->spheres = 0;
+}
+
 //
 // P_GivePlayerLives
 //
@@ -972,7 +1021,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;
@@ -1035,12 +1084,11 @@ void P_DoSuperTransformation(player_t *player, boolean giverings)
 
 	S_StartSound(NULL, sfx_supert); //let all players hear it -mattw_cfi
 
+	player->mo->momx = player->mo->momy = player->mo->momz = player->cmomx = player->cmomy = player->rmomx = player->rmomy = 0;
+
 	// Transformation animation
 	P_SetPlayerMobjState(player->mo, S_PLAY_SUPER_TRANS1);
 
-	player->mo->momx = player->mo->momy = player->mo->momz = 0;
-	player->pflags |= PF_NOJUMPDAMAGE; // just to avoid recurling but still allow thok
-
 	if (giverings)
 		player->rings = 50;
 
@@ -3492,194 +3540,198 @@ static void P_SetWeaponDelay(player_t *player, INT32 delay)
 static void P_DoFiring(player_t *player, ticcmd_t *cmd)
 {
 	INT32 i;
+	mobj_t *mo = NULL;
 
 	I_Assert(player != NULL);
 	I_Assert(!P_MobjWasRemoved(player->mo));
 
-	if (cmd->buttons & BT_ATTACK || cmd->buttons & BT_FIRENORMAL)
+	if (!(cmd->buttons & (BT_ATTACK|BT_FIRENORMAL)))
 	{
-		if (!(player->pflags & PF_ATTACKDOWN) && (player->powers[pw_shield] & SH_STACK) == SH_FIREFLOWER && !player->climbing)
-		{
-			player->pflags |= PF_ATTACKDOWN;
-			P_SpawnPlayerMissile(player->mo, MT_FIREBALL, 0);
-			S_StartSound(player->mo, sfx_mario7);
-		}
-		else if (G_RingSlingerGametype() && (!G_TagGametype() || player->pflags & PF_TAGIT)
-		  && !player->weapondelay && !player->climbing
-		  && !(player->pflags & PF_ATTACKDOWN))
-		{
-			mobj_t *mo = NULL;
-			player->pflags |= PF_ATTACKDOWN;
+		// Not holding any firing buttons anymore.
+		// Release the grenade / whatever.
+		player->pflags &= ~PF_ATTACKDOWN;
+		return;
+	}
 
-			#define TAKE_AMMO(player, power) \
-			player->powers[power]--; \
-			if (player->rings < 1) \
-			{ \
-				if (player->powers[power] > 0) \
-					player->powers[power]--; \
-			} \
-			else \
-				player->rings--;
+	if (player->pflags & PF_ATTACKDOWN || player->climbing || (G_TagGametype() && !(player->pflags & PF_TAGIT)))
+		return;
 
-			if (cmd->buttons & BT_FIRENORMAL) // No powers, just a regular ring.
-				goto firenormal; //code repetition sucks.
-			// Bounce ring
-			else if (player->currentweapon == WEP_BOUNCE && player->powers[pw_bouncering])
-			{
-				TAKE_AMMO(player, pw_bouncering);
-				P_SetWeaponDelay(player, TICRATE/4);
+	if ((player->powers[pw_shield] & SH_STACK) == SH_FIREFLOWER)
+	{
+		player->pflags |= PF_ATTACKDOWN;
+		mo = P_SpawnPlayerMissile(player->mo, MT_FIREBALL, 0);
+		P_InstaThrust(mo, player->mo->angle, ((mo->info->speed>>FRACBITS)*player->mo->scale) + player->speed);
+		S_StartSound(player->mo, sfx_mario7);
+		return;
+	}
 
-				mo = P_SpawnPlayerMissile(player->mo, MT_THROWNBOUNCE, MF2_BOUNCERING);
+	if (!G_RingSlingerGametype() || player->weapondelay)
+		return;
 
-				if (mo)
-					mo->fuse = 3*TICRATE; // Bounce Ring time
-			}
-			// Rail ring
-			else if (player->currentweapon == WEP_RAIL && player->powers[pw_railring])
-			{
-				TAKE_AMMO(player, pw_railring);
-				P_SetWeaponDelay(player, (3*TICRATE)/2);
+	player->pflags |= PF_ATTACKDOWN;
 
-				mo = P_SpawnPlayerMissile(player->mo, MT_REDRING, MF2_RAILRING|MF2_DONTDRAW);
+#define TAKE_AMMO(player, power) \
+		player->powers[power]--; \
+		if (player->rings < 1) \
+		{ \
+			if (player->powers[power] > 0) \
+				player->powers[power]--; \
+		} \
+		else \
+			player->rings--;
 
-				// Rail has no unique thrown object, therefore its sound plays here.
-				S_StartSound(player->mo, sfx_rail1);
-			}
-			// Automatic
-			else if (player->currentweapon == WEP_AUTO && player->powers[pw_automaticring])
-			{
-				TAKE_AMMO(player, pw_automaticring);
-				player->pflags &= ~PF_ATTACKDOWN;
-				P_SetWeaponDelay(player, 2);
+	if (cmd->buttons & BT_FIRENORMAL) // No powers, just a regular ring.
+		goto firenormal; //code repetition sucks.
+	// Bounce ring
+	else if (player->currentweapon == WEP_BOUNCE && player->powers[pw_bouncering])
+	{
+		TAKE_AMMO(player, pw_bouncering);
+		P_SetWeaponDelay(player, TICRATE/4);
 
-				mo = P_SpawnPlayerMissile(player->mo, MT_THROWNAUTOMATIC, MF2_AUTOMATIC);
-			}
-			// Explosion
-			else if (player->currentweapon == WEP_EXPLODE && player->powers[pw_explosionring])
-			{
-				TAKE_AMMO(player, pw_explosionring);
-				P_SetWeaponDelay(player, (3*TICRATE)/2);
+		mo = P_SpawnPlayerMissile(player->mo, MT_THROWNBOUNCE, MF2_BOUNCERING);
 
-				mo = P_SpawnPlayerMissile(player->mo, MT_THROWNEXPLOSION, MF2_EXPLOSION);
-			}
-			// Grenade
-			else if (player->currentweapon == WEP_GRENADE && player->powers[pw_grenadering])
-			{
-				TAKE_AMMO(player, pw_grenadering);
-				P_SetWeaponDelay(player, TICRATE/3);
+	if (mo)
+		mo->fuse = 3*TICRATE; // Bounce Ring time
+	}
+	// Rail ring
+	else if (player->currentweapon == WEP_RAIL && player->powers[pw_railring])
+	{
+		TAKE_AMMO(player, pw_railring);
+		P_SetWeaponDelay(player, (3*TICRATE)/2);
 
-				mo = P_SpawnPlayerMissile(player->mo, MT_THROWNGRENADE, MF2_EXPLOSION);
+		mo = P_SpawnPlayerMissile(player->mo, MT_REDRING, MF2_RAILRING|MF2_DONTDRAW);
 
-				if (mo)
-				{
-					//P_InstaThrust(mo, player->mo->angle, FixedMul(mo->info->speed, player->mo->scale));
-					mo->fuse = mo->info->mass;
-				}
-			}
-			// Scatter
-			// Note: Ignores MF2_RAILRING
-			else if (player->currentweapon == WEP_SCATTER && player->powers[pw_scatterring])
-			{
-				fixed_t oldz = player->mo->z;
-				angle_t shotangle = player->mo->angle;
-				angle_t oldaiming = player->aiming;
+		// Rail has no unique thrown object, therefore its sound plays here.
+		S_StartSound(player->mo, sfx_rail1);
+	}
+	// Automatic
+	else if (player->currentweapon == WEP_AUTO && player->powers[pw_automaticring])
+	{
+		TAKE_AMMO(player, pw_automaticring);
+		player->pflags &= ~PF_ATTACKDOWN;
+		P_SetWeaponDelay(player, 2);
 
-				TAKE_AMMO(player, pw_scatterring);
-				P_SetWeaponDelay(player, (2*TICRATE)/3);
+		mo = P_SpawnPlayerMissile(player->mo, MT_THROWNAUTOMATIC, MF2_AUTOMATIC);
+	}
+	// Explosion
+	else if (player->currentweapon == WEP_EXPLODE && player->powers[pw_explosionring])
+	{
+		TAKE_AMMO(player, pw_explosionring);
+		P_SetWeaponDelay(player, (3*TICRATE)/2);
 
-				// Center
-				mo = P_SpawnPlayerMissile(player->mo, MT_THROWNSCATTER, MF2_SCATTER);
-				if (mo)
-					shotangle = R_PointToAngle2(player->mo->x, player->mo->y, mo->x, mo->y);
+		mo = P_SpawnPlayerMissile(player->mo, MT_THROWNEXPLOSION, MF2_EXPLOSION);
+	}
+	// Grenade
+	else if (player->currentweapon == WEP_GRENADE && player->powers[pw_grenadering])
+	{
+		TAKE_AMMO(player, pw_grenadering);
+		P_SetWeaponDelay(player, TICRATE/3);
 
-				// Left
-				mo = P_SPMAngle(player->mo, MT_THROWNSCATTER, shotangle-ANG2, true, MF2_SCATTER);
+		mo = P_SpawnPlayerMissile(player->mo, MT_THROWNGRENADE, MF2_EXPLOSION);
 
-				// Right
-				mo = P_SPMAngle(player->mo, MT_THROWNSCATTER, shotangle+ANG2, true, MF2_SCATTER);
+		if (mo)
+		{
+			//P_InstaThrust(mo, player->mo->angle, FixedMul(mo->info->speed, player->mo->scale));
+			mo->fuse = mo->info->reactiontime;
+		}
+	}
+	// Scatter
+	// Note: Ignores MF2_RAILRING
+	else if (player->currentweapon == WEP_SCATTER && player->powers[pw_scatterring])
+	{
+		fixed_t oldz = player->mo->z;
+		angle_t shotangle = player->mo->angle;
+		angle_t oldaiming = player->aiming;
 
-				// Down
-				player->mo->z += FixedMul(12*FRACUNIT, player->mo->scale);
-				player->aiming += ANG1;
-				mo = P_SPMAngle(player->mo, MT_THROWNSCATTER, shotangle, true, MF2_SCATTER);
+		TAKE_AMMO(player, pw_scatterring);
+		P_SetWeaponDelay(player, (2*TICRATE)/3);
 
-				// Up
-				player->mo->z -= FixedMul(24*FRACUNIT, player->mo->scale);
-				player->aiming -= ANG2;
-				mo = P_SPMAngle(player->mo, MT_THROWNSCATTER, shotangle, true, MF2_SCATTER);
+		// Center
+		mo = P_SpawnPlayerMissile(player->mo, MT_THROWNSCATTER, MF2_SCATTER);
+		if (mo)
+			shotangle = R_PointToAngle2(player->mo->x, player->mo->y, mo->x, mo->y);
 
-				player->mo->z = oldz;
-				player->aiming = oldaiming;
-				return;
-			}
-			// No powers, just a regular ring.
-			else
-			{
-firenormal:
-				// Infinity ring was selected.
-				// Mystic wants this ONLY to happen specifically if it's selected,
-				// and to not be able to get around it EITHER WAY with firenormal.
+		// Left
+		mo = P_SPMAngle(player->mo, MT_THROWNSCATTER, shotangle-ANG2, true, MF2_SCATTER);
 
-				// Infinity Ring
-				if (player->currentweapon == 0
-				&& player->powers[pw_infinityring])
-				{
-					P_SetWeaponDelay(player, TICRATE/4);
+		// Right
+		mo = P_SPMAngle(player->mo, MT_THROWNSCATTER, shotangle+ANG2, true, MF2_SCATTER);
 
-					mo = P_SpawnPlayerMissile(player->mo, MT_THROWNINFINITY, 0);
+		// Down
+		player->mo->z += FixedMul(12*FRACUNIT, player->mo->scale);
+		player->aiming += ANG1;
+		mo = P_SPMAngle(player->mo, MT_THROWNSCATTER, shotangle, true, MF2_SCATTER);
 
-					player->powers[pw_infinityring]--;
-				}
-				// Red Ring
-				else
-				{
-					if (player->rings <= 0)
-						return;
-					P_SetWeaponDelay(player, TICRATE/4);
+		// Up
+		player->mo->z -= FixedMul(24*FRACUNIT, player->mo->scale);
+		player->aiming -= ANG2;
+		mo = P_SPMAngle(player->mo, MT_THROWNSCATTER, shotangle, true, MF2_SCATTER);
 
-					mo = P_SpawnPlayerMissile(player->mo, MT_REDRING, 0);
+		player->mo->z = oldz;
+		player->aiming = oldaiming;
+		return;
+	}
+	// No powers, just a regular ring.
+	else
+	{
+firenormal:
+		// Infinity ring was selected.
+		// Mystic wants this ONLY to happen specifically if it's selected,
+		// and to not be able to get around it EITHER WAY with firenormal.
 
-					if (mo)
-						P_ColorTeamMissile(mo, player);
+		// Infinity Ring
+		if (player->currentweapon == 0
+		&& player->powers[pw_infinityring])
+		{
+			P_SetWeaponDelay(player, TICRATE/4);
 
-					player->rings--;
-				}
-			}
+			mo = P_SpawnPlayerMissile(player->mo, MT_THROWNINFINITY, 0);
+
+			player->powers[pw_infinityring]--;
+		}
+		// Red Ring
+		else
+		{
+			if (player->rings <= 0)
+				return;
+			P_SetWeaponDelay(player, TICRATE/4);
 
-			#undef TAKE_AMMO
+			mo = P_SpawnPlayerMissile(player->mo, MT_REDRING, 0);
 
 			if (mo)
-			{
-				if (mo->flags & MF_MISSILE && mo->flags2 & MF2_RAILRING)
-				{
-					const boolean nblockmap = !(mo->flags & MF_NOBLOCKMAP);
-					for (i = 0; i < 256; i++)
-					{
-						if (nblockmap)
-						{
-							P_UnsetThingPosition(mo);
-							mo->flags |= MF_NOBLOCKMAP;
-							P_SetThingPosition(mo);
-						}
+				P_ColorTeamMissile(mo, player);
 
-						if (i&1)
-							P_SpawnMobj(mo->x, mo->y, mo->z, MT_SPARK);
+			player->rings--;
+		}
+	}
 
-						if (P_RailThinker(mo))
-							break; // mobj was removed (missile hit a wall) or couldn't move
-					}
+	#undef TAKE_AMMO
 
-					// Other rail sound plays at contact point.
-					S_StartSound(mo, sfx_rail2);
+	if (mo)
+	{
+		if (mo->flags & MF_MISSILE && mo->flags2 & MF2_RAILRING)
+		{
+			const boolean nblockmap = !(mo->flags & MF_NOBLOCKMAP);
+			for (i = 0; i < 256; i++)
+			{
+				if (nblockmap)
+				{
+					P_UnsetThingPosition(mo);
+					mo->flags |= MF_NOBLOCKMAP;
+					P_SetThingPosition(mo);
 				}
+
+				if (i&1)
+					P_SpawnMobj(mo->x, mo->y, mo->z, MT_SPARK);
+
+				if (P_RailThinker(mo))
+					break; // mobj was removed (missile hit a wall) or couldn't move
 			}
+
+			// Other rail sound plays at contact point.
+			S_StartSound(mo, sfx_rail2);
 		}
-		return;
 	}
-
-	// Not holding any firing buttons anymore.
-	// Release the grenade / whatever.
-	player->pflags &= ~PF_ATTACKDOWN;
 }
 
 //
@@ -3801,12 +3853,15 @@ static void P_DoSuperStuff(player_t *player)
 //
 boolean P_SuperReady(player_t *player)
 {
-	if ((ALL7EMERALDS(emeralds) && player->rings >= 50) && !player->powers[pw_super] && !player->powers[pw_tailsfly]
-	&& !(player->powers[pw_shield] & SH_NOSTACK)
+	if (!player->powers[pw_super]
 	&& !player->powers[pw_invulnerability]
-	&& !(maptol & TOL_NIGHTS || (player->powers[pw_carry] == CR_NIGHTSMODE)) // don't turn 'regular super' in nights levels
-	&& player->pflags & PF_JUMPED
-	&& player->charflags & SF_SUPER)
+	&& !player->powers[pw_tailsfly]
+	&& (player->charflags & SF_SUPER)
+	&& (player->pflags & PF_JUMPED)
+	&& !(player->powers[pw_shield] & SH_NOSTACK)
+	&& !(maptol & TOL_NIGHTS)
+	&& ALL7EMERALDS(emeralds)
+	&& (player->rings >= 50))
 		return true;
 
 	return false;
@@ -4492,12 +4547,12 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
 		}
 		else if (player->pflags & PF_SLIDING || (gametype == GT_CTF && player->gotflag))
 			;
-		else if (P_SuperReady(player))
+		/*else if (P_SuperReady(player))
 		{
 			// If you can turn super and aren't already,
 			// and you don't have a shield, do it!
 			P_DoSuperTransformation(player, false);
-		}
+		}*/
 		else if (player->pflags & PF_JUMPED)
 		{
 #ifdef HAVE_BLUA
@@ -5913,6 +5968,8 @@ static void P_DoNiGHTSCapsule(player_t *player)
 {
 	INT32 i;
 
+	player->capsule->extravalue2++; // tic counter
+
 	if (abs(player->mo->x-player->capsule->x) <= 2*FRACUNIT)
 	{
 		P_UnsetThingPosition(player->mo);
@@ -5967,21 +6024,24 @@ static void P_DoNiGHTSCapsule(player_t *player)
 	if (G_IsSpecialStage(gamemap))
 	{ // In special stages, share rings. Everyone gives up theirs to the capsule player always, because we can't have any individualism here!
 		for (i = 0; i < MAXPLAYERS; i++)
-			if (playeringame[i] && (&players[i] != player) && players[i].rings > 0)
+			if (playeringame[i] && (&players[i] != player) && players[i].spheres > 0)
 			{
-				player->rings += players[i].rings;
-				players[i].rings = 0;
+				player->spheres += players[i].spheres;
+				players[i].spheres = 0;
 			}
 	}
 
+	if (player->capsule->extravalue2 <= 0 && player->capsule->health > 0)
+		P_RunNightsCapsuleTouchExecutors(player->mo, true, player->spheres >= player->capsule->health); // run capsule entrance executors
+
 	// Time to blow it up!
 	if (player->mo->x == player->capsule->x
 		&& player->mo->y == player->capsule->y
 		&& player->mo->z == player->capsule->z+(player->capsule->height/3))
 	{
-		if (player->rings > 0)
+		if (player->spheres > 0)
 		{
-			player->rings--;
+			player->spheres--;
 			player->capsule->health--;
 			player->capsule->extravalue1++;
 
@@ -5997,7 +6057,7 @@ static void P_DoNiGHTSCapsule(player_t *player)
 				player->capsule->flags &= ~MF_NOGRAVITY;
 				player->capsule->momz = 5*FRACUNIT;
 				player->capsule->reactiontime = 0;
-				player->capsule->extravalue1 = -1;
+				player->capsule->extravalue1 = player->capsule->extravalue2 = -1;
 
 				for (i = 0; i < MAXPLAYERS; i++)
 					if (playeringame[i] && !player->exiting && players[i].mare == player->mare)
@@ -6013,9 +6073,6 @@ static void P_DoNiGHTSCapsule(player_t *player)
 
 				if (G_IsSpecialStage(gamemap))
 				{
-					// The Chaos Emerald begins to orbit us!
-					mobj_t *emmo;
-					UINT8 em = P_GetNextEmerald();
 					tic_t lowest_time;
 
 					/*for (i = 0; i < MAXPLAYERS; i++)
@@ -6030,8 +6087,10 @@ static void P_DoNiGHTSCapsule(player_t *player)
 
 					if (player->powers[pw_carry] == CR_NIGHTSMODE)
 					{
+						// The Chaos Emerald begins to orbit us!
+						UINT8 em = P_GetNextEmerald();
 						// Only give it to ONE person, and THAT player has to get to the goal!
-						emmo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->info->height, MT_GOTEMERALD);
+						mobj_t *emmo = P_SpawnMobjFromMobj(player->mo, 0, 0, player->mo->height, MT_GOTEMERALD);
 						P_SetTarget(&emmo->target, player->mo);
 						P_SetMobjState(emmo, mobjinfo[MT_GOTEMERALD].meleestate + em);
 						P_SetTarget(&player->mo->tracer, emmo);
@@ -6049,18 +6108,25 @@ static void P_DoNiGHTSCapsule(player_t *player)
 				}
 				else
 				{
-					for (i = 0; i < 16; i++)
+					/*for (i = 0; i < 16; i++)
 					{
 						mobj_t *flicky = P_InternalFlickySpawn(player->capsule, 0, ((i%4) + 1)*2*FRACUNIT, true);
 						flicky->z += player->capsule->height/2;
 						flicky->angle = (i*(ANGLE_MAX/16));
 						P_InstaThrust(flicky, flicky->angle, 8*FRACUNIT);
-					}
+					}*/
+					mobj_t *idya = P_SpawnMobjFromMobj(player->mo, 0, 0, player->mo->height, MT_GOTEMERALD);
+					idya->extravalue2 = player->mare/5;
+					P_SetTarget(&idya->target, player->mo);
+					P_SetMobjState(idya, mobjinfo[MT_GOTEMERALD].missilestate + ((player->mare + 1) % 5));
+					P_SetTarget(&player->mo->tracer, idya);
 				}
 				for (i = 0; i < MAXPLAYERS; i++)
 					if (playeringame[i] && players[i].mare == player->mare)
 						P_SetTarget(&players[i].capsule, NULL); // Remove capsule from everyone now that it is dead!
 				S_StartScreamSound(player->mo, sfx_ngdone);
+				P_SwitchSpheresBonusMode(true);
+				P_RunNightsCapsuleTouchExecutors(player->mo, false, true); // run capsule exit executors, and we destroyed it
 			}
 		}
 		else
@@ -6069,7 +6135,8 @@ static void P_DoNiGHTSCapsule(player_t *player)
 			player->texttimer = 4*TICRATE;
 			player->textvar = 3; // Get more rings!
 			player->capsule->reactiontime = 0;
-			player->capsule->extravalue1 = -1;
+			player->capsule->extravalue1 = player->capsule->extravalue2 = -1;
+			P_RunNightsCapsuleTouchExecutors(player->mo, false, false); // run capsule exit executors, and we lacked rings
 		}
 	}
 	else
@@ -6155,8 +6222,7 @@ static void P_NiGHTSMovement(player_t *player)
 	}
 	else if (P_IsLocalPlayer(player) && player->nightstime == 10*TICRATE)
 //		S_StartSound(NULL, sfx_timeup); // that creepy "out of time" music from NiGHTS. Dummied out, as some on the dev team thought it wasn't Sonic-y enough (Mystic, notably). Uncomment to restore. -SH
-		S_ChangeMusicInternal("_drown",false);
-
+		S_ChangeMusicInternal((((maptol & TOL_NIGHTS) && !G_IsSpecialStage(gamemap)) ? "_ntime" : "_drown"), false);
 
 	if (player->mo->z < player->mo->floorz)
 		player->mo->z = player->mo->floorz;
@@ -6627,7 +6693,18 @@ static void P_NiGHTSMovement(player_t *player)
 			S_StartSound(player->mo, sfx_drill1);
 			player->drilltimer = 32;
 		}
-		else if (--player->drilltimer <= 0)
+		else if (player->drilltimer == 32)
+		{
+			// drill mash penalty
+			player->drilltimer = 31;
+			player->drillmeter -= TICRATE/2;
+			if (player->drillmeter <= 0)
+				player->drillmeter = TICRATE/10;
+		}
+		else if (--player->drilltimer == 11)
+			// give that drill mash penalty back (after 0.6 seconds)
+			player->drillmeter += TICRATE/2;
+		else if (player->drilltimer <= 0)
 		{
 			player->drilltimer = 10;
 			S_StartSound(player->mo, sfx_drill2);
@@ -6956,12 +7033,17 @@ static void P_MovePlayer(player_t *player)
 		if ((player->powers[pw_carry] == CR_NIGHTSMODE)
 		&& (player->exiting
 		|| !(player->mo->state >= &states[S_PLAY_NIGHTS_TRANS1]
-			&& player->mo->state < &states[S_PLAY_NIGHTS_TRANS6])))
+			&& player->mo->state < &states[S_PLAY_NIGHTS_TRANS6]))) // Note the < instead of <=
 		{
 			skin_t *skin = ((skin_t *)(player->mo->skin));
-			if (skin->flags & SF_SUPER && player->mo->color < MAXSKINCOLORS)
+			if (skin->flags & SF_SUPER)
+			{
+				player->mo->color = skin->supercolor
+					+ ((player->nightstime == player->startedtime)
+						? 4
+						: abs((((signed)leveltime >> 1) % 9) - 4)); // This is where super flashing is handled.
 				G_GhostAddColor(GHC_SUPER);
-			player->mo->color = (skin->flags & SF_SUPER) ? skin->supercolor + abs((((signed)(player->startedtime - player->nightstime) >> 1) % 9) - 4) : player->mo->color; // This is where super flashing is handled.
+			}
 		}
 
 		if (!player->capsule && !player->bonustime)
@@ -7010,8 +7092,14 @@ static void P_MovePlayer(player_t *player)
 					if (playeringame[i])
 						players[i].exiting = (14*TICRATE)/5 + 1;
 			}
-			else if (player->rings > 0)
+			else {
+				// Damage whether or not we have spheres, as player should recoil upon losing points
 				P_DamageMobj(player->mo, NULL, NULL, 1, 0);
+
+				// Now deduct our mare score!
+				player->marescore = player->spheres =\
+				 player->rings = 0;
+			}
 			player->powers[pw_carry] = CR_NONE;
 		}
 	}
@@ -7495,7 +7583,7 @@ static void P_MovePlayer(player_t *player)
 #endif
 			{
 				if (!(player->pflags & (PF_USEDOWN|PF_GLIDING|PF_SLIDING|PF_SHIELDABILITY)) // If the player is not holding down BT_USE, or having used an ability previously
-					&& (!(player->pflags & PF_THOKKED) || ((player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP && player->secondjump == UINT8_MAX))) // thokked is optional if you're bubblewrapped
+					&& (!(player->powers[pw_shield] & SH_NOSTACK) || !(player->pflags & PF_THOKKED) || ((player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP && player->secondjump == UINT8_MAX))) // thokked is optional if you're bubblewrapped/turning super
 				{
 					// Force shield activation
 					if ((player->powers[pw_shield] & ~(SH_FORCEHP|SH_STACK)) == SH_FORCE)
@@ -7508,6 +7596,11 @@ static void P_MovePlayer(player_t *player)
 					{
 						switch (player->powers[pw_shield] & SH_NOSTACK)
 						{
+							// Super!
+							case SH_NONE:
+								if (P_SuperReady(player))
+									P_DoSuperTransformation(player, false);
+								break;
 							// Whirlwind/Thundercoin shield activation
 							case SH_WHIRLWIND:
 							case SH_THUNDERCOIN:
@@ -7959,17 +8052,18 @@ static void P_DoRopeHang(player_t *player)
 
 	if (player->cmd.buttons & BT_USE && !(player->pflags & PF_STASIS)) // Drop off of the rope
 	{
-		P_SetTarget(&player->mo->tracer, NULL);
-
 		player->pflags |= P_GetJumpFlags(player);
+		P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
+
+		P_SetTarget(&player->mo->tracer, NULL);
 		player->powers[pw_carry] = CR_NONE;
 
-		if (!(player->pflags & PF_SLIDING) && (player->pflags & PF_JUMPED)
-		&& !(player->panim == PA_JUMP))
-			P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
 		return;
 	}
 
+	if (player->mo->state-states != S_PLAY_RIDE)
+		P_SetPlayerMobjState(player->mo, S_PLAY_RIDE);
+
 	// If not allowed to move, we're done here.
 	if (!speed)
 		return;
@@ -8060,10 +8154,7 @@ static void P_DoRopeHang(player_t *player)
 			if (player->mo->tracer->flags & MF_SLIDEME)
 			{
 				player->pflags |= P_GetJumpFlags(player);
-
-				if (!(player->pflags & PF_SLIDING) && (player->pflags & PF_JUMPED)
-				&& !(player->panim == PA_JUMP))
-					P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
+				P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
 			}
 
 			P_SetTarget(&player->mo->tracer, NULL);
@@ -8181,7 +8272,6 @@ mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet)
 	mobj_t *mo;
 	thinker_t *think;
 	mobj_t *closestmo = NULL;
-	const UINT32 targetmask = (MF_ENEMY|MF_BOSS|(nonenemies ? (MF_MONITOR|MF_SPRING) : 0));
 	const fixed_t maxdist = FixedMul((bullet ? RING_DIST*2 : RING_DIST), player->mo->scale);
 	const angle_t span = (bullet ? ANG30 : ANGLE_90);
 	fixed_t dist, closestdist = 0;
@@ -8192,9 +8282,7 @@ mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet)
 			continue; // not a mobj thinker
 
 		mo = (mobj_t *)think;
-		if (!(mo->flags & targetmask
-		|| mo->type == MT_FAKEMOBILE // hehehehe
-		|| mo->type == MT_EGGSHIELD))
+		if (!(mo->flags & (MF_ENEMY|MF_BOSS|MF_MONITOR|MF_SPRING)) == !(mo->flags2 & MF2_INVERTAIMABLE)) // allows if it has the flags desired XOR it has the invert aimable flag
 			continue; // not a valid target
 
 		if (mo->health <= 0) // dead
@@ -8209,6 +8297,9 @@ mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet)
 		if ((mo->flags & (MF_ENEMY|MF_BOSS)) && !(mo->flags & MF_SHOOTABLE)) // don't aim at something you can't shoot at anyway (see Egg Guard or Minus)
 			continue;
 
+		if (!nonenemies && mo->flags & (MF_MONITOR|MF_SPRING))
+			continue;
+
 		if (!bullet && mo->type == MT_DETON) // Don't be STUPID, Sonic!
 			continue;
 
@@ -8246,7 +8337,7 @@ mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet)
 		if (closestmo && dist > closestdist)
 			continue;
 
-		if ((R_PointToAngle2(player->mo->x, player->mo->y, mo->x, mo->y) - player->mo->angle + span) > span*2)
+		if ((R_PointToAngle2(player->mo->x + P_ReturnThrustX(player->mo, player->mo->angle, player->mo->radius), player->mo->y + P_ReturnThrustY(player->mo, player->mo->angle, player->mo->radius), mo->x, mo->y) - player->mo->angle + span) > span*2)
 			continue; // behind back
 
 		if (!P_CheckSight(player->mo, mo))
@@ -8354,7 +8445,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)
@@ -8381,7 +8472,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)
@@ -8696,7 +8787,13 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
 	subsector_t *newsubsec;
 	fixed_t f1, f2;
 
-	cameranoclip = (player->powers[pw_carry] == CR_NIGHTSMODE || player->pflags & PF_NOCLIP) || (player->mo->flags & (MF_NOCLIP|MF_NOCLIPHEIGHT)); // Noclipping player camera noclips too!!
+	// We probably shouldn't move the camera if there is no player or player mobj somehow
+	if (!player || !player->mo)
+		return true;
+
+	mo = player->mo;
+
+	cameranoclip = (player->powers[pw_carry] == CR_NIGHTSMODE || player->pflags & PF_NOCLIP) || (mo->flags & (MF_NOCLIP|MF_NOCLIPHEIGHT)); // Noclipping player camera noclips too!!
 
 	if (!(player->climbing || (player->powers[pw_carry] == CR_NIGHTSMODE) || player->playerstate == PST_DEAD))
 	{
@@ -8717,7 +8814,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
 		else if (player == &players[secondarydisplayplayer])
 			focusangle = localangle2;
 		else
-			focusangle = player->mo->angle;
+			focusangle = mo->angle;
 		if (thiscam == &camera)
 			camrotate = cv_cam_rotate.value;
 		else if (thiscam == &camera2)
@@ -8729,17 +8826,9 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
 		return true;
 	}
 
-	if (!player || !player->mo)
-		return true;
-
-	mo = player->mo;
-
 	thiscam->radius = FixedMul(20*FRACUNIT, mo->scale);
 	thiscam->height = FixedMul(16*FRACUNIT, mo->scale);
 
-	if (!mo)
-		return true;
-
 	// Don't run while respawning from a starpost
 	// Inu 4/8/13 Why not?!
 //	if (leveltime > 0 && timeinmap <= 0)
@@ -8747,7 +8836,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
 
 	if (player->powers[pw_carry] == CR_NIGHTSMODE)
 	{
-		focusangle = player->mo->angle;
+		focusangle = mo->angle;
 		focusaiming = 0;
 	}
 	else if (player == &players[consoleplayer])
@@ -8762,7 +8851,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
 	}
 	else
 	{
-		focusangle = player->mo->angle;
+		focusangle = mo->angle;
 		focusaiming = player->aiming;
 	}
 
@@ -8809,12 +8898,12 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
 			angle = R_PointToAngle2(player->axis1->x, player->axis1->y, player->axis2->x, player->axis2->y);
 			angle += ANGLE_90;
 		}
-		else if (player->mo->target)
+		else if (mo->target)
 		{
-			if (player->mo->target->flags2 & MF2_AMBUSH)
-				angle = R_PointToAngle2(player->mo->target->x, player->mo->target->y, player->mo->x, player->mo->y);
+			if (mo->target->flags2 & MF2_AMBUSH)
+				angle = R_PointToAngle2(mo->target->x, mo->target->y, mo->x, mo->y);
 			else
-				angle = R_PointToAngle2(player->mo->x, player->mo->y, player->mo->target->x, player->mo->target->y);
+				angle = R_PointToAngle2(mo->x, mo->y, mo->target->x, mo->target->y);
 		}
 	}
 	else if (P_AnalogMove(player)) // Analog
@@ -8903,7 +8992,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
 	if (twodlevel || (mo->flags2 & MF2_TWOD))
 	{
 		// Camera doesn't ALWAYS need to move, only when running...
-		if (abs(player->mo->momx) > 10)
+		if (abs(mo->momx) > 10)
 		{
 			// Move the camera all smooth-like, not jerk it around...
 			if (mo->momx > 0)
@@ -9221,13 +9310,13 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
 			vy = thiscam->y;
 		}
 
-		if (P_AproxDistance(vx - player->mo->x, vy - player->mo->y) < FixedMul(48*FRACUNIT, mo->scale))
-			player->mo->flags2 |= MF2_SHADOW;
+		if (P_AproxDistance(vx - mo->x, vy - mo->y) < FixedMul(48*FRACUNIT, mo->scale))
+			mo->flags2 |= MF2_SHADOW;
 		else
-			player->mo->flags2 &= ~MF2_SHADOW;
+			mo->flags2 &= ~MF2_SHADOW;
 	}
 	else
-		player->mo->flags2 &= ~MF2_SHADOW;
+		mo->flags2 &= ~MF2_SHADOW;
 
 /*	if (!resetcalled && (player->powers[pw_carry] == CR_NIGHTSMODE && player->exiting))
 	{
@@ -9581,11 +9670,8 @@ void P_PlayerThink(player_t *player)
 
 		// If 11 seconds are left on the timer,
 		// begin the drown music for countdown!
-		if (countdown == 11*TICRATE - 1)
-		{
-			if (P_IsLocalPlayer(player))
-				S_ChangeMusicInternal("_drown", false);
-		}
+		if (countdown == 11*TICRATE - 1 && P_IsLocalPlayer(player))
+			S_ChangeMusicInternal("_drown", false);
 
 		// If you've hit the countdown and you haven't made
 		//  it to the exit, you're a goner!
@@ -9686,7 +9772,7 @@ void P_PlayerThink(player_t *player)
 		if (gametype != GT_COOP)
 			player->score = 0;
 		player->mo->health = 1;
-		player->rings = 0;
+		player->rings = player->spheres = 0;
 	}
 	else if ((netgame || multiplayer) && player->lives <= 0 && gametype != GT_COOP)
 	{
@@ -9739,8 +9825,12 @@ void P_PlayerThink(player_t *player)
 
 			mo2 = (mobj_t *)th;
 
-			if (!(mo2->type == MT_NIGHTSWING || mo2->type == MT_RING || mo2->type == MT_COIN
-			   || mo2->type == MT_BLUEBALL))
+			if (!(mo2->type == MT_RING || mo2->type == MT_COIN
+				|| mo2->type == MT_BLUESPHERE || mo2->type == MT_BOMBSPHERE
+				|| mo2->type == MT_NIGHTSCHIP || mo2->type == MT_NIGHTSSTAR))
+				continue;
+
+			if (mo2->flags2 & MF2_NIGHTSPULL)
 				continue;
 
 			if (P_AproxDistance(P_AproxDistance(mo2->x - x, mo2->y - y), mo2->z - z) > FixedMul(128*FRACUNIT, player->mo->scale))
@@ -9749,6 +9839,9 @@ void P_PlayerThink(player_t *player)
 			// Yay! The thing's in reach! Pull it in!
 			mo2->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT;
 			mo2->flags2 |= MF2_NIGHTSPULL;
+			// New NiGHTS attract speed dummied out because the older behavior
+			// is exploited as a mechanic. Uncomment to enable.
+			mo2->movefactor = 0; // 40*FRACUNIT; // initialize the NightsItemChase timer
 			P_SetTarget(&mo2->tracer, player->mo);
 		}
 	}
@@ -9779,8 +9872,6 @@ void P_PlayerThink(player_t *player)
 				ticmiss++;
 
 			P_DoRopeHang(player);
-			if (player->mo->state-states != S_PLAY_RIDE)
-				P_SetPlayerMobjState(player->mo, S_PLAY_RIDE);
 			P_DoJumpStuff(player, &player->cmd);
 		}
 		else //if (player->powers[pw_carry] == CR_ZOOMTUBE)
@@ -9793,7 +9884,19 @@ void P_PlayerThink(player_t *player)
 		P_ResetScore(player);
 	}
 	else
+	{
+		if (player->bumpertime == TICRATE/2 && player->mo->hnext)
+		{
+			// Center player to NiGHTS bumper here because if you try to set player's position in
+			// P_TouchSpecialThing case MT_NIGHTSBUMPER, that position is fudged in the time
+			// between that routine in the previous tic
+			// and reaching here in the current tic
+			P_TeleportMove(player->mo, player->mo->hnext->x, player->mo->hnext->y
+				, player->mo->hnext->z + FixedMul(player->mo->hnext->height/4, player->mo->hnext->scale));
+			P_SetTarget(&player->mo->hnext, NULL);
+		}
 		P_MovePlayer(player);
+	}
 
 	if (!player->mo)
 		return; // P_MovePlayer removed player->mo.
@@ -9896,7 +9999,8 @@ void P_PlayerThink(player_t *player)
 			if (!player->powers[pw_carry]
 			&& ((player->pflags & (PF_AUTOBRAKE|PF_APPLYAUTOBRAKE)) == (PF_AUTOBRAKE|PF_APPLYAUTOBRAKE))
 			&& !(cmd->forwardmove || cmd->sidemove)
-			&& (player->rmomx || player->rmomy))
+			&& (player->rmomx || player->rmomy)
+			&& (!player->capsule || (player->capsule->reactiontime != (player-players)+1)))
 			{
 				fixed_t acceleration = (player->accelstart + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * player->acceleration) * player->thrustfactor * 20;
 				angle_t moveAngle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy);
@@ -9916,9 +10020,10 @@ void P_PlayerThink(player_t *player)
 			|| player->panim == PA_PAIN
 			|| !player->mo->health
 			|| player->climbing
-			|| player->pflags & (PF_SPINNING|PF_SLIDING))
+			|| player->pflags & (PF_SPINNING|PF_SLIDING)
+			|| player->bumpertime)
 				player->pflags &= ~PF_APPLYAUTOBRAKE;
-			else if (currentlyonground)
+			else if (currentlyonground || player->powers[pw_tailsfly])
 				player->pflags |= PF_APPLYAUTOBRAKE;
 		}
 	}
@@ -10230,7 +10335,7 @@ void P_PlayerAfterThink(player_t *player)
 		if (player->followmobj)
 		{
 			P_RemoveMobj(player->followmobj);
-			player->followmobj = NULL;
+			P_SetTarget(&player->followmobj, NULL);
 		}
 		return;
 	}
@@ -10348,7 +10453,7 @@ void P_PlayerAfterThink(player_t *player)
 	if (P_IsLocalPlayer(player) && (player->pflags & PF_WPNDOWN) && player->currentweapon != oldweapon)
 		S_StartSound(NULL, sfx_wepchg);
 
-	if (player->pflags & PF_SLIDING)
+	if ((player->pflags & PF_SLIDING) && ((player->pflags & (PF_JUMPED|PF_NOJUMPDAMAGE)) != PF_JUMPED))
 		P_SetPlayerMobjState(player->mo, player->mo->info->painstate);
 
 	/* if (player->powers[pw_carry] == CR_NONE && player->mo->tracer && !player->homing)
@@ -10513,14 +10618,14 @@ void P_PlayerAfterThink(player_t *player)
 	if (player->followmobj && (player->spectator || player->mo->health <= 0 || player->followmobj->type != player->followitem))
 	{
 		P_RemoveMobj(player->followmobj);
-		player->followmobj = NULL;
+		P_SetTarget(&player->followmobj, NULL);
 	}
 
 	if (!player->spectator && player->mo->health && player->followitem)
 	{
 		if (!player->followmobj || P_MobjWasRemoved(player->followmobj))
 		{
-			player->followmobj = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, player->followitem);
+			P_SetTarget(&player->followmobj, P_SpawnMobjFromMobj(player->mo, 0, 0, 0, player->followitem));
 			P_SetTarget(&player->followmobj->tracer, player->mo);
 			player->followmobj->flags2 |= MF2_LINKDRAW;
 		}
diff --git a/src/r_bsp.c b/src/r_bsp.c
index 183def25abf995b6783a44317401985f690f94e3..cbb012b2875cc75e9ff026d4262d20c256bbf184 100644
--- a/src/r_bsp.c
+++ b/src/r_bsp.c
@@ -234,8 +234,6 @@ static INT32 R_DoorClosed(void)
 sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, INT32 *floorlightlevel,
 	INT32 *ceilinglightlevel, boolean back)
 {
-	INT32 mapnum = -1;
-
 	if (floorlightlevel)
 		*floorlightlevel = sec->floorlightsec == -1 ?
 			sec->lightlevel : sectors[sec->floorlightsec].lightlevel;
@@ -244,10 +242,10 @@ sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, INT32 *floorlightlevel,
 		*ceilinglightlevel = sec->ceilinglightsec == -1 ?
 			sec->lightlevel : sectors[sec->ceilinglightsec].lightlevel;
 
-	// If the sector has a midmap, it's probably from 280 type
-	if (sec->midmap != -1)
-		mapnum = sec->midmap;
-	else if (sec->heightsec != -1)
+	// if (sec->midmap != -1)
+	//	mapnum = sec->midmap;
+	// In original colormap code, this block did not run if sec->midmap was set
+	if (!sec->extra_colormap && sec->heightsec != -1)
 	{
 		const sector_t *s = &sectors[sec->heightsec];
 		mobj_t *viewmobj = viewplayer->mo;
@@ -271,8 +269,6 @@ sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, INT32 *floorlightlevel,
 		tempsec->floorheight = s->floorheight;
 		tempsec->ceilingheight = s->ceilingheight;
 
-		mapnum = s->midmap;
-
 		if ((underwater && (tempsec->  floorheight = sec->floorheight,
 			tempsec->ceilingheight = s->floorheight - 1, !back)) || viewz <= s->floorheight)
 		{ // head-below-floor hack
@@ -298,7 +294,6 @@ sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, INT32 *floorlightlevel,
 					tempsec->ceiling_yoffs = s->ceiling_yoffs;
 					tempsec->ceilingpic_angle = s->ceilingpic_angle;
 				}
-				mapnum = s->bottommap;
 			}
 
 			tempsec->lightlevel = s->lightlevel;
@@ -322,8 +317,6 @@ sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, INT32 *floorlightlevel,
 			tempsec->floor_yoffs = tempsec->ceiling_yoffs = s->ceiling_yoffs;
 			tempsec->floorpic_angle = tempsec->ceilingpic_angle = s->ceilingpic_angle;
 
-			mapnum = s->topmap;
-
 			if (s->floorpic == skyflatnum) // SKYFIX?
 			{
 				tempsec->ceilingheight = tempsec->floorheight-1;
@@ -354,11 +347,6 @@ sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, INT32 *floorlightlevel,
 		sec = tempsec;
 	}
 
-	if (mapnum >= 0 && (size_t)mapnum < num_extra_colormaps)
-		sec->extra_colormap = &extra_colormaps[mapnum];
-	else
-		sec->extra_colormap = NULL;
-
 	return sec;
 }
 
@@ -937,11 +925,11 @@ static void R_Subsector(size_t num)
 		light = R_GetPlaneLight(frontsector, floorcenterz, false);
 		if (frontsector->floorlightsec == -1)
 			floorlightlevel = *frontsector->lightlist[light].lightlevel;
-		floorcolormap = frontsector->lightlist[light].extra_colormap;
+		floorcolormap = *frontsector->lightlist[light].extra_colormap;
 		light = R_GetPlaneLight(frontsector, ceilingcenterz, false);
 		if (frontsector->ceilinglightsec == -1)
 			ceilinglightlevel = *frontsector->lightlist[light].lightlevel;
-		ceilingcolormap = frontsector->lightlist[light].extra_colormap;
+		ceilingcolormap = *frontsector->lightlist[light].extra_colormap;
 	}
 
 	sub->sector->extra_colormap = frontsector->extra_colormap;
@@ -1038,7 +1026,7 @@ static void R_Subsector(size_t num)
 
 				ffloor[numffloors].plane = R_FindPlane(*rover->bottomheight, *rover->bottompic,
 					*frontsector->lightlist[light].lightlevel, *rover->bottomxoffs,
-					*rover->bottomyoffs, *rover->bottomangle, frontsector->lightlist[light].extra_colormap, rover
+					*rover->bottomyoffs, *rover->bottomangle, *frontsector->lightlist[light].extra_colormap, rover
 #ifdef POLYOBJECTS_PLANES
 					, NULL
 #endif
@@ -1084,7 +1072,7 @@ static void R_Subsector(size_t num)
 
 				ffloor[numffloors].plane = R_FindPlane(*rover->topheight, *rover->toppic,
 					*frontsector->lightlist[light].lightlevel, *rover->topxoffs, *rover->topyoffs, *rover->topangle,
-					frontsector->lightlist[light].extra_colormap, rover
+					*frontsector->lightlist[light].extra_colormap, rover
 #ifdef POLYOBJECTS_PLANES
 					, NULL
 #endif
@@ -1237,7 +1225,7 @@ void R_Prep3DFloors(sector_t *sector)
 	ffloor_t *rover;
 	ffloor_t *best;
 	fixed_t bestheight, maxheight;
-	INT32 count, i, mapnum;
+	INT32 count, i;
 	sector_t *sec;
 #ifdef ESLOPE
 	pslope_t *bestslope = NULL;
@@ -1276,7 +1264,7 @@ void R_Prep3DFloors(sector_t *sector)
 #endif
 	sector->lightlist[0].lightlevel = &sector->lightlevel;
 	sector->lightlist[0].caster = NULL;
-	sector->lightlist[0].extra_colormap = sector->extra_colormap;
+	sector->lightlist[0].extra_colormap = &sector->extra_colormap;
 	sector->lightlist[0].flags = 0;
 
 	maxheight = INT32_MAX;
@@ -1342,11 +1330,6 @@ void R_Prep3DFloors(sector_t *sector)
 		sector->lightlist[i].slope = bestslope;
 #endif
 		sec = &sectors[best->secnum];
-		mapnum = sec->midmap;
-		if (mapnum >= 0 && (size_t)mapnum < num_extra_colormaps)
-			sec->extra_colormap = &extra_colormaps[mapnum];
-		else
-			sec->extra_colormap = NULL;
 
 		if (best->flags & FF_NOSHADE)
 		{
@@ -1356,12 +1339,12 @@ void R_Prep3DFloors(sector_t *sector)
 		else if (best->flags & FF_COLORMAPONLY)
 		{
 			sector->lightlist[i].lightlevel = sector->lightlist[i-1].lightlevel;
-			sector->lightlist[i].extra_colormap = sec->extra_colormap;
+			sector->lightlist[i].extra_colormap = &sec->extra_colormap;
 		}
 		else
 		{
 			sector->lightlist[i].lightlevel = best->toplightlevel;
-			sector->lightlist[i].extra_colormap = sec->extra_colormap;
+			sector->lightlist[i].extra_colormap = &sec->extra_colormap;
 		}
 
 		if (best->flags & FF_DOUBLESHADOW)
diff --git a/src/r_data.c b/src/r_data.c
index 169b042015066ca459432293604829717d8bd27b..08e3d91b6820aea327932105fcfd4bfe5897f090 100644
--- a/src/r_data.c
+++ b/src/r_data.c
@@ -1144,6 +1144,10 @@ void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *texindex)
 	Z_Free((void *)texturesText);
 }
 
+#ifdef EXTRACOLORMAPLUMPS
+static lumplist_t *colormaplumps = NULL; ///\todo free leak
+static size_t numcolormaplumps = 0;
+
 static inline lumpnum_t R_CheckNumForNameList(const char *name, lumplist_t *list, size_t listsize)
 {
 	size_t i;
@@ -1160,9 +1164,6 @@ static inline lumpnum_t R_CheckNumForNameList(const char *name, lumplist_t *list
 	return LUMPERROR;
 }
 
-static lumplist_t *colormaplumps = NULL; ///\todo free leak
-static size_t numcolormaplumps = 0;
-
 static void R_InitExtraColormaps(void)
 {
 	lumpnum_t startnum, endnum;
@@ -1195,6 +1196,7 @@ static void R_InitExtraColormaps(void)
 	}
 	CONS_Printf(M_GetText("Number of Extra Colormaps: %s\n"), sizeu1(numcolormaplumps));
 }
+#endif
 
 // Search for flat name through all
 lumpnum_t R_GetFlatNumForName(const char *name)
@@ -1291,7 +1293,9 @@ static void R_InitColormaps(void)
 
 	// Init Boom colormaps.
 	R_ClearColormaps();
+#ifdef EXTRACOLORMAPLUMPS
 	R_InitExtraColormaps();
+#endif
 }
 
 void R_ReInitColormaps(UINT16 num)
@@ -1311,11 +1315,6 @@ void R_ReInitColormaps(UINT16 num)
 	R_ClearColormaps();
 }
 
-static lumpnum_t foundcolormaps[MAXCOLORMAPS];
-
-static char colormapFixingArray[MAXCOLORMAPS][3][9];
-static size_t carrayindex;
-
 //
 // R_ClearColormaps
 //
@@ -1323,303 +1322,343 @@ static size_t carrayindex;
 //
 void R_ClearColormaps(void)
 {
-	size_t i;
+	// Purged by PU_LEVEL, just overwrite the pointer
+	extra_colormaps = R_CreateDefaultColormap(true);
+}
 
-	num_extra_colormaps = 0;
+//
+// R_CreateDefaultColormap()
+// NOTE: The result colormap is not added to the extra_colormaps chain. You must do that yourself!
+//
+extracolormap_t *R_CreateDefaultColormap(boolean lighttable)
+{
+	extracolormap_t *exc = Z_Calloc(sizeof (*exc), PU_LEVEL, NULL);
+	exc->fadestart = 0;
+	exc->fadeend = 31;
+	exc->fog = 0;
+	exc->rgba = 0;
+	exc->fadergba = 0x19000000;
+	exc->colormap = lighttable ? R_CreateLightTable(exc) : NULL;
+#ifdef EXTRACOLORMAPLUMPS
+	exc->lump = LUMPERROR;
+	exc->lumpname[0] = 0;
+#endif
+	exc->next = exc->prev = NULL;
+	return exc;
+}
 
-	carrayindex = 0;
+//
+// R_GetDefaultColormap()
+//
+extracolormap_t *R_GetDefaultColormap(void)
+{
+#ifdef COLORMAPREVERSELIST
+	extracolormap_t *exc;
+#endif
 
-	for (i = 0; i < MAXCOLORMAPS; i++)
-		foundcolormaps[i] = LUMPERROR;
+	if (!extra_colormaps)
+		return (extra_colormaps = R_CreateDefaultColormap(true));
 
-	memset(extra_colormaps, 0, sizeof (extra_colormaps));
+#ifdef COLORMAPREVERSELIST
+	for (exc = extra_colormaps; exc->next; exc = exc->next);
+	return exc;
+#else
+	return extra_colormaps;
+#endif
 }
 
-INT32 R_ColormapNumForName(char *name)
+//
+// R_CopyColormap()
+// NOTE: The result colormap is not added to the extra_colormaps chain. You must do that yourself!
+//
+extracolormap_t *R_CopyColormap(extracolormap_t *extra_colormap, boolean lighttable)
 {
-	lumpnum_t lump, i;
-
-	if (num_extra_colormaps == MAXCOLORMAPS)
-		I_Error("R_ColormapNumForName: Too many colormaps! the limit is %d\n", MAXCOLORMAPS);
+	extracolormap_t *exc = Z_Calloc(sizeof (*exc), PU_LEVEL, NULL);
 
-	lump = R_CheckNumForNameList(name, colormaplumps, numcolormaplumps);
-	if (lump == LUMPERROR)
-		I_Error("R_ColormapNumForName: Cannot find colormap lump %.8s\n", name);
+	if (!extra_colormap)
+		extra_colormap = R_GetDefaultColormap();
 
-	for (i = 0; i < num_extra_colormaps; i++)
-		if (lump == foundcolormaps[i])
-			return i;
+	*exc = *extra_colormap;
+	exc->next = exc->prev = NULL;
 
-	foundcolormaps[num_extra_colormaps] = lump;
+#ifdef EXTRACOLORMAPLUMPS
+	strncpy(exc->lumpname, extra_colormap->lumpname, 9);
 
-	// aligned on 8 bit for asm code
-	extra_colormaps[num_extra_colormaps].colormap = Z_MallocAlign(W_LumpLength(lump), PU_LEVEL, NULL, 16);
-	W_ReadLump(lump, extra_colormaps[num_extra_colormaps].colormap);
+	if (exc->lump != LUMPERROR && lighttable)
+	{
+		// aligned on 8 bit for asm code
+		exc->colormap = Z_MallocAlign(W_LumpLength(lump), PU_LEVEL, NULL, 16);
+		W_ReadLump(lump, exc->colormap);
+	}
+	else
+#endif
+	if (lighttable)
+		exc->colormap = R_CreateLightTable(exc);
+	else
+		exc->colormap = NULL;
 
-	// We set all params of the colormap to normal because there
-	// is no real way to tell how GL should handle a colormap lump anyway..
-	extra_colormaps[num_extra_colormaps].maskcolor = 0xffff;
-	extra_colormaps[num_extra_colormaps].fadecolor = 0x0;
-	extra_colormaps[num_extra_colormaps].maskamt = 0x0;
-	extra_colormaps[num_extra_colormaps].fadestart = 0;
-	extra_colormaps[num_extra_colormaps].fadeend = 33;
-	extra_colormaps[num_extra_colormaps].fog = 0;
-
-	num_extra_colormaps++;
-	return (INT32)num_extra_colormaps - 1;
+	return exc;
 }
 
 //
-// R_CreateColormap
+// R_AddColormapToList
 //
-// This is a more GL friendly way of doing colormaps: Specify colormap
-// data in a special linedef's texture areas and use that to generate
-// custom colormaps at runtime. NOTE: For GL mode, we only need to color
-// data and not the colormap data.
+// Sets prev/next chain for extra_colormaps var
+// Copypasta from P_AddFFloorToList
 //
-static double deltas[256][3], map[256][3];
-
-static UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b);
-static int RoundUp(double number);
-
-INT32 R_CreateColormap(char *p1, char *p2, char *p3)
+void R_AddColormapToList(extracolormap_t *extra_colormap)
 {
-	double cmaskr, cmaskg, cmaskb, cdestr, cdestg, cdestb;
-	double r, g, b, cbrightness, maskamt = 0, othermask = 0;
-	int mask, fog = 0;
-	size_t mapnum = num_extra_colormaps;
-	size_t i;
-	UINT32 cr, cg, cb, maskcolor, fadecolor;
-	UINT32 fadestart = 0, fadeend = 33, fadedist = 33;
+#ifndef COLORMAPREVERSELIST
+	extracolormap_t *exc;
+#endif
 
-#define HEX2INT(x) (UINT32)(x >= '0' && x <= '9' ? x - '0' : x >= 'a' && x <= 'f' ? x - 'a' + 10 : x >= 'A' && x <= 'F' ? x - 'A' + 10 : 0)
-	if (p1[0] == '#')
+	if (!extra_colormaps)
 	{
-		cr = ((HEX2INT(p1[1]) * 16) + HEX2INT(p1[2]));
-		cmaskr = cr;
-		cg = ((HEX2INT(p1[3]) * 16) + HEX2INT(p1[4]));
-		cmaskg = cg;
-		cb = ((HEX2INT(p1[5]) * 16) + HEX2INT(p1[6]));
-		cmaskb = cb;
-		// Create a rough approximation of the color (a 16 bit color)
-		maskcolor = ((cb) >> 3) + (((cg) >> 2) << 5) + (((cr) >> 3) << 11);
-		if (p1[7] >= 'a' && p1[7] <= 'z')
-			mask = (p1[7] - 'a');
-		else if (p1[7] >= 'A' && p1[7] <= 'Z')
-			mask = (p1[7] - 'A');
-		else
-			mask = 24;
+		extra_colormaps = extra_colormap;
+		extra_colormap->next = 0;
+		extra_colormap->prev = 0;
+		return;
+	}
 
-		maskamt = (double)(mask/24.0l);
+#ifdef COLORMAPREVERSELIST
+	extra_colormaps->prev = extra_colormap;
+	extra_colormap->next = extra_colormaps;
+	extra_colormaps = extra_colormap;
+	extra_colormap->prev = 0;
+#else
+	for (exc = extra_colormaps; exc->next; exc = exc->next);
 
-		othermask = 1 - maskamt;
-		maskamt /= 0xff;
-		cmaskr *= maskamt;
-		cmaskg *= maskamt;
-		cmaskb *= maskamt;
-	}
-	else
-	{
-		cmaskr = cmaskg = cmaskb = 0xff;
-		maskamt = 0;
-		maskcolor = ((0xff) >> 3) + (((0xff) >> 2) << 5) + (((0xff) >> 3) << 11);
-	}
+	exc->next = extra_colormap;
+	extra_colormap->prev = exc;
+	extra_colormap->next = 0;
+#endif
+}
 
-#define NUMFROMCHAR(c) (c >= '0' && c <= '9' ? c - '0' : 0)
-	if (p2[0] == '#')
-	{
-		// Get parameters like fadestart, fadeend, and the fogflag
-		fadestart = NUMFROMCHAR(p2[3]) + (NUMFROMCHAR(p2[2]) * 10);
-		fadeend = NUMFROMCHAR(p2[5]) + (NUMFROMCHAR(p2[4]) * 10);
-		if (fadestart > 32)
-			fadestart = 0;
-		if (fadeend > 33 || fadeend < 1)
-			fadeend = 33;
-		fadedist = fadeend - fadestart;
-		fog = NUMFROMCHAR(p2[1]) ? 1 : 0;
-	}
-#undef getnum
+//
+// R_CheckDefaultColormapByValues()
+//
+#ifdef EXTRACOLORMAPLUMPS
+boolean R_CheckDefaultColormapByValues(boolean checkrgba, boolean checkfadergba, boolean checkparams,
+	INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 fog, lumpnum_t lump)
+#else
+boolean R_CheckDefaultColormapByValues(boolean checkrgba, boolean checkfadergba, boolean checkparams,
+	INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 fog)
+#endif
+{
+	return (
+		(!checkparams ? true :
+			(fadestart == 0
+				&& fadeend == 31
+				&& !fog)
+			)
+		&& (!checkrgba ? true : rgba == 0)
+		&& (!checkfadergba ? true : fadergba == 0x19000000)
+#ifdef EXTRACOLORMAPLUMPS
+		&& lump == LUMPERROR
+		&& extra_colormap->lumpname[0] == 0
+#endif
+		);
+}
 
-	if (p3[0] == '#')
-	{
-		cdestr = cr = ((HEX2INT(p3[1]) * 16) + HEX2INT(p3[2]));
-		cdestg = cg = ((HEX2INT(p3[3]) * 16) + HEX2INT(p3[4]));
-		cdestb = cb = ((HEX2INT(p3[5]) * 16) + HEX2INT(p3[6]));
-		fadecolor = (((cb) >> 3) + (((cg) >> 2) << 5) + (((cr) >> 3) << 11));
-	}
-	else
-		cdestr = cdestg = cdestb = fadecolor = 0;
-#undef HEX2INT
+boolean R_CheckDefaultColormap(extracolormap_t *extra_colormap, boolean checkrgba, boolean checkfadergba, boolean checkparams)
+{
+	if (!extra_colormap)
+		return true;
+
+#ifdef EXTRACOLORMAPLUMPS
+	return R_CheckDefaultColormapByValues(checkrgba, checkfadergba, checkparams, extra_colormap->rgba, extra_colormap->fadergba, extra_colormap->fadestart, extra_colormap->fadeend, extra_colormap->fog, extra_colormap->lump);
+#else
+	return R_CheckDefaultColormapByValues(checkrgba, checkfadergba, checkparams, extra_colormap->rgba, extra_colormap->fadergba, extra_colormap->fadestart, extra_colormap->fadeend, extra_colormap->fog);
+#endif
+}
+
+boolean R_CheckEqualColormaps(extracolormap_t *exc_a, extracolormap_t *exc_b, boolean checkrgba, boolean checkfadergba, boolean checkparams)
+{
+	// Treat NULL as default colormap
+	// We need this because what if one exc is a default colormap, and the other is NULL? They're really both equal.
+	if (!exc_a)
+		exc_a = R_GetDefaultColormap();
+	if (!exc_b)
+		exc_b = R_GetDefaultColormap();
+
+	if (exc_a == exc_b)
+		return true;
+
+	return (
+		(!checkparams ? true :
+			(exc_a->fadestart == exc_b->fadestart
+				&& exc_a->fadeend == exc_b->fadeend
+				&& exc_a->fog == exc_b->fog)
+			)
+		&& (!checkrgba ? true : exc_a->rgba == exc_b->rgba)
+		&& (!checkfadergba ? true : exc_a->fadergba == exc_b->fadergba)
+#ifdef EXTRACOLORMAPLUMPS
+		&& exc_a->lump == exc_b->lump
+		&& !strncmp(exc_a->lumpname, exc_b->lumpname, 9)
+#endif
+		);
+}
+
+//
+// R_GetColormapFromListByValues()
+// NOTE: Returns NULL if no match is found
+//
+#ifdef EXTRACOLORMAPLUMPS
+extracolormap_t *R_GetColormapFromListByValues(INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 fog, lumpnum_t lump)
+#else
+extracolormap_t *R_GetColormapFromListByValues(INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 fog)
+#endif
+{
+	extracolormap_t *exc;
+	UINT32 dbg_i = 0;
 
-	for (i = 0; i < num_extra_colormaps; i++)
+	for (exc = extra_colormaps; exc; exc = exc->next)
 	{
-		if (foundcolormaps[i] != LUMPERROR)
-			continue;
-		if (maskcolor == extra_colormaps[i].maskcolor
-			&& fadecolor == extra_colormaps[i].fadecolor
-			&& (float)maskamt == (float)extra_colormaps[i].maskamt
-			&& fadestart == extra_colormaps[i].fadestart
-			&& fadeend == extra_colormaps[i].fadeend
-			&& fog == extra_colormaps[i].fog)
+		if (rgba == exc->rgba
+			&& fadergba == exc->fadergba
+			&& fadestart == exc->fadestart
+			&& fadeend == exc->fadeend
+			&& fog == exc->fog
+#ifdef EXTRACOLORMAPLUMPS
+			&& (lump != LUMPERROR && lump == exc->lump)
+#endif
+		)
 		{
-			return (INT32)i;
+			CONS_Debug(DBG_RENDER, "Found Colormap %d: rgba(%d,%d,%d,%d) fadergba(%d,%d,%d,%d)\n",
+				dbg_i, R_GetRgbaR(rgba), R_GetRgbaG(rgba), R_GetRgbaB(rgba), R_GetRgbaA(rgba),
+				R_GetRgbaR(fadergba), R_GetRgbaG(fadergba), R_GetRgbaB(fadergba), R_GetRgbaA(fadergba));
+			return exc;
 		}
+		dbg_i++;
 	}
+	return NULL;
+}
 
-	if (num_extra_colormaps == MAXCOLORMAPS)
-		I_Error("R_CreateColormap: Too many colormaps! the limit is %d\n", MAXCOLORMAPS);
-
-	strncpy(colormapFixingArray[num_extra_colormaps][0], p1, 8);
-	strncpy(colormapFixingArray[num_extra_colormaps][1], p2, 8);
-	strncpy(colormapFixingArray[num_extra_colormaps][2], p3, 8);
-
-	num_extra_colormaps++;
+extracolormap_t *R_GetColormapFromList(extracolormap_t *extra_colormap)
+{
+#ifdef EXTRACOLORMAPLUMPS
+	return R_GetColormapFromListByValues(extra_colormap->rgba, extra_colormap->fadergba, extra_colormap->fadestart, extra_colormap->fadeend, extra_colormap->fog, extra_colormap->lump);
+#else
+	return R_GetColormapFromListByValues(extra_colormap->rgba, extra_colormap->fadergba, extra_colormap->fadestart, extra_colormap->fadeend, extra_colormap->fog);
+#endif
+}
 
-	if (rendermode == render_soft)
-	{
-		for (i = 0; i < 256; i++)
-		{
-			r = pMasterPalette[i].s.red;
-			g = pMasterPalette[i].s.green;
-			b = pMasterPalette[i].s.blue;
-			cbrightness = sqrt((r*r) + (g*g) + (b*b));
+#ifdef EXTRACOLORMAPLUMPS
+extracolormap_t *R_ColormapForName(char *name)
+{
+	lumpnum_t lump;
+	extracolormap_t *exc;
 
-			map[i][0] = (cbrightness * cmaskr) + (r * othermask);
-			if (map[i][0] > 255.0l)
-				map[i][0] = 255.0l;
-			deltas[i][0] = (map[i][0] - cdestr) / (double)fadedist;
+	lump = R_CheckNumForNameList(name, colormaplumps, numcolormaplumps);
+	if (lump == LUMPERROR)
+		I_Error("R_ColormapForName: Cannot find colormap lump %.8s\n", name);
 
-			map[i][1] = (cbrightness * cmaskg) + (g * othermask);
-			if (map[i][1] > 255.0l)
-				map[i][1] = 255.0l;
-			deltas[i][1] = (map[i][1] - cdestg) / (double)fadedist;
+	exc = R_GetColormapFromListByValues(0, 0x19000000, 0, 31, 0, lump);
+	if (exc)
+		return exc;
 
-			map[i][2] = (cbrightness * cmaskb) + (b * othermask);
-			if (map[i][2] > 255.0l)
-				map[i][2] = 255.0l;
-			deltas[i][2] = (map[i][2] - cdestb) / (double)fadedist;
-		}
-	}
+	exc = Z_Calloc(sizeof (*exc), PU_LEVEL, NULL);
 
-	foundcolormaps[mapnum] = LUMPERROR;
+	exc->lump = lump;
+	strncpy(exc->lumpname, name, 9);
+	exc->lumpname[8] = 0;
 
 	// aligned on 8 bit for asm code
-	extra_colormaps[mapnum].colormap = NULL;
-	extra_colormaps[mapnum].maskcolor = (UINT16)maskcolor;
-	extra_colormaps[mapnum].fadecolor = (UINT16)fadecolor;
-	extra_colormaps[mapnum].maskamt = maskamt;
-	extra_colormaps[mapnum].fadestart = (UINT16)fadestart;
-	extra_colormaps[mapnum].fadeend = (UINT16)fadeend;
-	extra_colormaps[mapnum].fog = fog;
-
-	return (INT32)mapnum;
-}
+	exc->colormap = Z_MallocAlign(W_LumpLength(lump), PU_LEVEL, NULL, 16);
+	W_ReadLump(lump, exc->colormap);
 
-void R_MakeColormaps(void)
-{
-	size_t i;
+	// We set all params of the colormap to normal because there
+	// is no real way to tell how GL should handle a colormap lump anyway..
+	exc->fadestart = 0;
+	exc->fadeend = 31;
+	exc->fog = 0;
+	exc->rgba = 0;
+	exc->fadergba = 0x19000000;
 
-	carrayindex = num_extra_colormaps;
-	num_extra_colormaps = 0;
+	R_AddColormapToList(exc);
 
-	for (i = 0; i < carrayindex; i++)
-		R_CreateColormap2(colormapFixingArray[i][0], colormapFixingArray[i][1],
-			colormapFixingArray[i][2]);
+	return exc;
 }
+#endif
+
+//
+// R_CreateColormap
+//
+// This is a more GL friendly way of doing colormaps: Specify colormap
+// data in a special linedef's texture areas and use that to generate
+// custom colormaps at runtime. NOTE: For GL mode, we only need to color
+// data and not the colormap data.
+//
+static double deltas[256][3], map[256][3];
+
+static UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b);
+static int RoundUp(double number);
 
-void R_CreateColormap2(char *p1, char *p2, char *p3)
+lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap)
 {
 	double cmaskr, cmaskg, cmaskb, cdestr, cdestg, cdestb;
-	double r, g, b, cbrightness;
 	double maskamt = 0, othermask = 0;
-	int mask, p, fog = 0;
-	size_t mapnum = num_extra_colormaps;
-	size_t i;
-	char *colormap_p;
-	UINT32 cr, cg, cb, maskcolor, fadecolor;
-	UINT32 fadestart = 0, fadeend = 33, fadedist = 33;
-
-#define HEX2INT(x) (UINT32)(x >= '0' && x <= '9' ? x - '0' : x >= 'a' && x <= 'f' ? x - 'a' + 10 : x >= 'A' && x <= 'F' ? x - 'A' + 10 : 0)
-	if (p1[0] == '#')
-	{
-		cr = ((HEX2INT(p1[1]) * 16) + HEX2INT(p1[2]));
-		cmaskr = cr;
-		cg = ((HEX2INT(p1[3]) * 16) + HEX2INT(p1[4]));
-		cmaskg = cg;
-		cb = ((HEX2INT(p1[5]) * 16) + HEX2INT(p1[6]));
-		cmaskb = cb;
-		// Create a rough approximation of the color (a 16 bit color)
-		maskcolor = ((cb) >> 3) + (((cg) >> 2) << 5) + (((cr) >> 3) << 11);
-		if (p1[7] >= 'a' && p1[7] <= 'z')
-			mask = (p1[7] - 'a');
-		else if (p1[7] >= 'A' && p1[7] <= 'Z')
-			mask = (p1[7] - 'A');
-		else
-			mask = 24;
-
-		maskamt = (double)(mask/24.0l);
-
-		othermask = 1 - maskamt;
-		maskamt /= 0xff;
-		cmaskr *= maskamt;
-		cmaskg *= maskamt;
-		cmaskb *= maskamt;
-	}
-	else
-	{
-		cmaskr = cmaskg = cmaskb = 0xff;
-		maskamt = 0;
-		maskcolor = ((0xff) >> 3) + (((0xff) >> 2) << 5) + (((0xff) >> 3) << 11);
-	}
-
-#define NUMFROMCHAR(c) (c >= '0' && c <= '9' ? c - '0' : 0)
-	if (p2[0] == '#')
-	{
-		// Get parameters like fadestart, fadeend, and the fogflag
-		fadestart = NUMFROMCHAR(p2[3]) + (NUMFROMCHAR(p2[2]) * 10);
-		fadeend = NUMFROMCHAR(p2[5]) + (NUMFROMCHAR(p2[4]) * 10);
-		if (fadestart > 32)
-			fadestart = 0;
-		if (fadeend > 33 || fadeend < 1)
-			fadeend = 33;
-		fadedist = fadeend - fadestart;
-		fog = NUMFROMCHAR(p2[1]) ? 1 : 0;
-	}
-#undef getnum
-
-	if (p3[0] == '#')
-	{
-		cdestr = cr = ((HEX2INT(p3[1]) * 16) + HEX2INT(p3[2]));
-		cdestg = cg = ((HEX2INT(p3[3]) * 16) + HEX2INT(p3[4]));
-		cdestb = cb = ((HEX2INT(p3[5]) * 16) + HEX2INT(p3[6]));
-		fadecolor = (((cb) >> 3) + (((cg) >> 2) << 5) + (((cr) >> 3) << 11));
-	}
-	else
-		cdestr = cdestg = cdestb = fadecolor = 0;
-#undef HEX2INT
 
-	for (i = 0; i < num_extra_colormaps; i++)
-	{
-		if (foundcolormaps[i] != LUMPERROR)
-			continue;
-		if (maskcolor == extra_colormaps[i].maskcolor
-			&& fadecolor == extra_colormaps[i].fadecolor
-			&& (float)maskamt == (float)extra_colormaps[i].maskamt
-			&& fadestart == extra_colormaps[i].fadestart
-			&& fadeend == extra_colormaps[i].fadeend
-			&& fog == extra_colormaps[i].fog)
-		{
-			return;
-		}
-	}
+	UINT8 cr = R_GetRgbaR(extra_colormap->rgba),
+		cg = R_GetRgbaG(extra_colormap->rgba),
+		cb = R_GetRgbaB(extra_colormap->rgba),
+		ca = R_GetRgbaA(extra_colormap->rgba),
+		cfr = R_GetRgbaR(extra_colormap->fadergba),
+		cfg = R_GetRgbaG(extra_colormap->fadergba),
+		cfb = R_GetRgbaB(extra_colormap->fadergba);
+//		cfa = R_GetRgbaA(extra_colormap->fadergba); // unused in software
 
-	if (num_extra_colormaps == MAXCOLORMAPS)
-		I_Error("R_CreateColormap: Too many colormaps! the limit is %d\n", MAXCOLORMAPS);
+	UINT8 fadestart = extra_colormap->fadestart,
+		fadedist = extra_colormap->fadeend - extra_colormap->fadestart;
 
-	num_extra_colormaps++;
+	lighttable_t *lighttable = NULL;
+	size_t i;
 
+	/////////////////////
+	// Calc the RGBA mask
+	/////////////////////
+	cmaskr = cr;
+	cmaskg = cg;
+	cmaskb = cb;
+
+	maskamt = (double)(ca/24.0l);
+	othermask = 1 - maskamt;
+	maskamt /= 0xff;
+
+	cmaskr *= maskamt;
+	cmaskg *= maskamt;
+	cmaskb *= maskamt;
+
+	/////////////////////
+	// Calc the RGBA fade mask
+	/////////////////////
+	cdestr = cfr;
+	cdestg = cfg;
+	cdestb = cfb;
+
+	// fade alpha unused in software
+	// maskamt = (double)(cfa/24.0l);
+	// othermask = 1 - maskamt;
+	// maskamt /= 0xff;
+
+	// cdestr *= maskamt;
+	// cdestg *= maskamt;
+	// cdestb *= maskamt;
+
+	/////////////////////
+	// This code creates the colormap array used by software renderer
+	/////////////////////
 	if (rendermode == render_soft)
 	{
+		double r, g, b, cbrightness;
+		int p;
+		char *colormap_p;
+
+		// Initialise the map and delta arrays
+		// map[i] stores an RGB color (as double) for index i,
+		//  which is then converted to SRB2's palette later
+		// deltas[i] stores a corresponding fade delta between the RGB color and the final fade color;
+		//  map[i]'s values are decremented by after each use
 		for (i = 0; i < 256; i++)
 		{
 			r = pMasterPalette[i].s.red;
@@ -1642,25 +1681,14 @@ void R_CreateColormap2(char *p1, char *p2, char *p3)
 				map[i][2] = 255.0l;
 			deltas[i][2] = (map[i][2] - cdestb) / (double)fadedist;
 		}
-	}
-
-	foundcolormaps[mapnum] = LUMPERROR;
-
-	// aligned on 8 bit for asm code
-	extra_colormaps[mapnum].colormap = NULL;
-	extra_colormaps[mapnum].maskcolor = (UINT16)maskcolor;
-	extra_colormaps[mapnum].fadecolor = (UINT16)fadecolor;
-	extra_colormaps[mapnum].maskamt = maskamt;
-	extra_colormaps[mapnum].fadestart = (UINT16)fadestart;
-	extra_colormaps[mapnum].fadeend = (UINT16)fadeend;
-	extra_colormaps[mapnum].fog = fog;
 
-#define ABS2(x) ((x) < 0 ? -(x) : (x))
-	if (rendermode == render_soft)
-	{
+		// Now allocate memory for the actual colormap array itself!
+		// aligned on 8 bit for asm code
 		colormap_p = Z_MallocAlign((256 * 34) + 10, PU_LEVEL, NULL, 8);
-		extra_colormaps[mapnum].colormap = (UINT8 *)colormap_p;
+		lighttable = (UINT8 *)colormap_p;
 
+		// Calculate the palette index for each palette index, for each light level
+		// (as well as the two unused colormap lines we inherited from Doom)
 		for (p = 0; p < 34; p++)
 		{
 			for (i = 0; i < 256; i++)
@@ -1672,7 +1700,7 @@ void R_CreateColormap2(char *p1, char *p2, char *p3)
 
 				if ((UINT32)p < fadestart)
 					continue;
-
+#define ABS2(x) ((x) < 0 ? -(x) : (x))
 				if (ABS2(map[i][0] - cdestr) > ABS2(deltas[i][0]))
 					map[i][0] -= deltas[i][0];
 				else
@@ -1687,12 +1715,290 @@ void R_CreateColormap2(char *p1, char *p2, char *p3)
 					map[i][2] -= deltas[i][2];
 				else
 					map[i][2] = cdestb;
+#undef ABS2
 			}
 		}
 	}
-#undef ABS2
 
-	return;
+	return lighttable;
+}
+
+extracolormap_t *R_CreateColormap(char *p1, char *p2, char *p3)
+{
+	extracolormap_t *extra_colormap, *exc;
+
+	// default values
+	UINT8 cr = 0, cg = 0, cb = 0, ca = 0, cfr = 0, cfg = 0, cfb = 0, cfa = 25;
+	UINT32 fadestart = 0, fadeend = 31;
+	UINT8 fog = 0;
+	INT32 rgba = 0, fadergba = 0x19000000;
+
+#define HEX2INT(x) (UINT32)(x >= '0' && x <= '9' ? x - '0' : x >= 'a' && x <= 'f' ? x - 'a' + 10 : x >= 'A' && x <= 'F' ? x - 'A' + 10 : 0)
+#define ALPHA2INT(x) (x >= 'a' && x <= 'z' ? x - 'a' : x >= 'A' && x <= 'Z' ? x - 'A' : x >= '0' && x <= '9' ? 25 : 0)
+
+	// Get base colormap value
+	// First alpha-only, then full value
+	if (p1[0] >= 'a' && p1[0] <= 'z' && !p1[1])
+		ca = (p1[0] - 'a');
+	else if (p1[0] == '#' && p1[1] >= 'a' && p1[1] <= 'z' && !p1[2])
+		ca = (p1[1] - 'a');
+	else if (p1[0] >= 'A' && p1[0] <= 'Z' && !p1[1])
+		ca = (p1[0] - 'A');
+	else if (p1[0] == '#' && p1[1] >= 'A' && p1[1] <= 'Z' && !p1[2])
+		ca = (p1[1] - 'A');
+	else if (p1[0] == '#')
+	{
+		// For each subsequent value, the value before it must exist
+		// If we don't get every value, then set alpha to max
+		if (p1[1] && p1[2])
+		{
+			cr = ((HEX2INT(p1[1]) * 16) + HEX2INT(p1[2]));
+			if (p1[3] && p1[4])
+			{
+				cg = ((HEX2INT(p1[3]) * 16) + HEX2INT(p1[4]));
+				if (p1[5] && p1[6])
+				{
+					cb = ((HEX2INT(p1[5]) * 16) + HEX2INT(p1[6]));
+
+					if (p1[7] >= 'a' && p1[7] <= 'z')
+						ca = (p1[7] - 'a');
+					else if (p1[7] >= 'A' && p1[7] <= 'Z')
+						ca = (p1[7] - 'A');
+					else
+						ca = 25;
+				}
+				else
+					ca = 25;
+			}
+			else
+				ca = 25;
+		}
+		else
+			ca = 25;
+	}
+
+#define NUMFROMCHAR(c) (c >= '0' && c <= '9' ? c - '0' : 0)
+
+	// Get parameters like fadestart, fadeend, and the fogflag
+	if (p2[0] == '#')
+	{
+		if (p2[1])
+		{
+			fog = NUMFROMCHAR(p2[1]);
+			if (p2[2] && p2[3])
+			{
+				fadestart = NUMFROMCHAR(p2[3]) + (NUMFROMCHAR(p2[2]) * 10);
+				if (p2[4] && p2[5])
+					fadeend = NUMFROMCHAR(p2[5]) + (NUMFROMCHAR(p2[4]) * 10);
+			}
+		}
+
+		if (fadestart > 30)
+			fadestart = 0;
+		if (fadeend > 31 || fadeend < 1)
+			fadeend = 31;
+	}
+
+#undef NUMFROMCHAR
+
+	// Get fade (dark) colormap value
+	// First alpha-only, then full value
+	if (p3[0] >= 'a' && p3[0] <= 'z' && !p3[1])
+		cfa = (p3[0] - 'a');
+	else if (p3[0] == '#' && p3[1] >= 'a' && p3[1] <= 'z' && !p3[2])
+		cfa = (p3[1] - 'a');
+	else if (p3[0] >= 'A' && p3[0] <= 'Z' && !p3[1])
+		cfa = (p3[0] - 'A');
+	else if (p3[0] == '#' && p3[1] >= 'A' && p3[1] <= 'Z' && !p3[2])
+		cfa = (p3[1] - 'A');
+	else if (p3[0] == '#')
+	{
+		// For each subsequent value, the value before it must exist
+		// If we don't get every value, then set alpha to max
+		if (p3[1] && p3[2])
+		{
+			cfr = ((HEX2INT(p3[1]) * 16) + HEX2INT(p3[2]));
+			if (p3[3] && p3[4])
+			{
+				cfg = ((HEX2INT(p3[3]) * 16) + HEX2INT(p3[4]));
+				if (p3[5] && p3[6])
+				{
+					cfb = ((HEX2INT(p3[5]) * 16) + HEX2INT(p3[6]));
+
+					if (p3[7] >= 'a' && p3[7] <= 'z')
+						cfa = (p3[7] - 'a');
+					else if (p3[7] >= 'A' && p3[7] <= 'Z')
+						cfa = (p3[7] - 'A');
+					else
+						cfa = 25;
+				}
+				else
+					cfa = 25;
+			}
+			else
+				cfa = 25;
+		}
+		else
+			cfa = 25;
+	}
+#undef ALPHA2INT
+#undef HEX2INT
+
+	// Pack rgba values into combined var
+	// OpenGL also uses this instead of lighttables for rendering
+	rgba = R_PutRgbaRGBA(cr, cg, cb, ca);
+	fadergba = R_PutRgbaRGBA(cfr, cfg, cfb, cfa);
+
+	// Did we just make a default colormap?
+#ifdef EXTRACOLORMAPLUMPS
+	if (R_CheckDefaultColormapByValues(true, true, true, rgba, fadergba, fadestart, fadeend, fog, LUMPERROR))
+		return NULL;
+#else
+	if (R_CheckDefaultColormapByValues(true, true, true, rgba, fadergba, fadestart, fadeend, fog))
+		return NULL;
+#endif
+
+	// Look for existing colormaps
+#ifdef EXTRACOLORMAPLUMPS
+	exc = R_GetColormapFromListByValues(rgba, fadergba, fadestart, fadeend, fog, LUMPERROR);
+#else
+	exc = R_GetColormapFromListByValues(rgba, fadergba, fadestart, fadeend, fog);
+#endif
+	if (exc)
+		return exc;
+
+	CONS_Debug(DBG_RENDER, "Creating Colormap: rgba(%d,%d,%d,%d) fadergba(%d,%d,%d,%d)\n",
+		cr, cg, cb, ca, cfr, cfg, cfb, cfa);
+
+	extra_colormap = Z_Calloc(sizeof (*extra_colormap), PU_LEVEL, NULL);
+
+	extra_colormap->fadestart = (UINT16)fadestart;
+	extra_colormap->fadeend = (UINT16)fadeend;
+	extra_colormap->fog = fog;
+
+	extra_colormap->rgba = rgba;
+	extra_colormap->fadergba = fadergba;
+
+#ifdef EXTRACOLORMAPLUMPS
+	extra_colormap->lump = LUMPERROR;
+	extra_colormap->lumpname[0] = 0;
+#endif
+
+	// Having lighttables for alpha-only entries is kind of pointless,
+	// but if there happens to be a matching rgba entry that is NOT alpha-only (but has same rgb values),
+	// then it needs this lighttable because we share matching entries.
+	extra_colormap->colormap = R_CreateLightTable(extra_colormap);
+
+	R_AddColormapToList(extra_colormap);
+
+	return extra_colormap;
+}
+
+//
+// R_AddColormaps()
+// NOTE: The result colormap is not added to the extra_colormaps chain. You must do that yourself!
+//
+extracolormap_t *R_AddColormaps(extracolormap_t *exc_augend, extracolormap_t *exc_addend,
+	boolean subR, boolean subG, boolean subB, boolean subA,
+	boolean subFadeR, boolean subFadeG, boolean subFadeB, boolean subFadeA,
+	boolean subFadeStart, boolean subFadeEnd, boolean ignoreFog,
+	boolean useAltAlpha, INT16 altAlpha, INT16 altFadeAlpha,
+	boolean lighttable)
+{
+	INT16 red, green, blue, alpha;
+
+	// exc_augend is added (or subtracted) onto by exc_addend
+	// In Rennaisance times, the first number was considered the augend, the second number the addend
+	// But since the commutative property was discovered, today they're both called addends!
+	// So let's be Olde English for a hot second.
+
+	exc_augend = R_CopyColormap(exc_augend, false);
+	if(!exc_addend)
+		exc_addend = R_GetDefaultColormap();
+
+	///////////////////
+	// base rgba
+	///////////////////
+
+	red = max(min(
+		R_GetRgbaR(exc_augend->rgba)
+			+ (subR ? -1 : 1) // subtract R
+			* R_GetRgbaR(exc_addend->rgba)
+		, 255), 0);
+
+	green = max(min(
+		R_GetRgbaG(exc_augend->rgba)
+			+ (subG ? -1 : 1) // subtract G
+			* R_GetRgbaG(exc_addend->rgba)
+		, 255), 0);
+
+	blue = max(min(
+		R_GetRgbaB(exc_augend->rgba)
+			+ (subB ? -1 : 1) // subtract B
+			* R_GetRgbaB(exc_addend->rgba)
+		, 255), 0);
+
+	alpha = useAltAlpha ? altAlpha : R_GetRgbaA(exc_addend->rgba);
+	alpha = max(min(R_GetRgbaA(exc_augend->rgba) + (subA ? -1 : 1) * alpha, 25), 0);
+
+	exc_augend->rgba = R_PutRgbaRGBA(red, green, blue, alpha);
+
+	///////////////////
+	// fade/dark rgba
+	///////////////////
+
+	red = max(min(
+		R_GetRgbaR(exc_augend->fadergba)
+			+ (subFadeR ? -1 : 1) // subtract R
+			* R_GetRgbaR(exc_addend->fadergba)
+		, 255), 0);
+
+	green = max(min(
+		R_GetRgbaG(exc_augend->fadergba)
+			+ (subFadeG ? -1 : 1) // subtract G
+			* R_GetRgbaG(exc_addend->fadergba)
+		, 255), 0);
+
+	blue = max(min(
+		R_GetRgbaB(exc_augend->fadergba)
+			+ (subFadeB ? -1 : 1) // subtract B
+			* R_GetRgbaB(exc_addend->fadergba)
+		, 255), 0);
+
+	alpha = useAltAlpha ? altFadeAlpha : R_GetRgbaA(exc_addend->fadergba);
+	if (alpha == 25 && !useAltAlpha && !R_GetRgbaRGB(exc_addend->fadergba))
+		alpha = 0; // HACK: fadergba A defaults at 25, so don't add anything in this case
+	alpha = max(min(R_GetRgbaA(exc_augend->fadergba) + (subFadeA ? -1 : 1) * alpha, 25), 0);
+
+	exc_augend->fadergba = R_PutRgbaRGBA(red, green, blue, alpha);
+
+	///////////////////
+	// parameters
+	///////////////////
+
+	exc_augend->fadestart = max(min(
+		exc_augend->fadestart
+			+ (subFadeStart ? -1 : 1) // subtract fadestart
+			* exc_addend->fadestart
+		, 31), 0);
+
+	exc_augend->fadeend = max(min(
+		exc_augend->fadeend
+			+ (subFadeEnd ? -1 : 1) // subtract fadeend
+			* (exc_addend->fadeend == 31 && !exc_addend->fadestart ? 0 : exc_addend->fadeend)
+				// HACK: fadeend defaults to 31, so don't add anything in this case
+		, 31), 0);
+
+	if (!ignoreFog) // overwrite fog with new value
+		exc_augend->fog = exc_addend->fog;
+
+	///////////////////
+	// put it together
+	///////////////////
+
+	exc_augend->colormap = lighttable ? R_CreateLightTable(exc_augend) : NULL;
+	exc_augend->next = exc_augend->prev = NULL;
+	return exc_augend;
 }
 
 // Thanks to quake2 source!
@@ -1735,20 +2041,18 @@ static int RoundUp(double number)
 	return (int)number;
 }
 
-const char *R_ColormapNameForNum(INT32 num)
+#ifdef EXTRACOLORMAPLUMPS
+const char *R_NameForColormap(extracolormap_t *extra_colormap)
 {
-	if (num == -1)
+	if (!extra_colormap)
 		return "NONE";
 
-	if (num < 0 || num > MAXCOLORMAPS)
-		I_Error("R_ColormapNameForNum: num %d is invalid!\n", num);
-
-	if (foundcolormaps[num] == LUMPERROR)
+	if (extra_colormap->lump == LUMPERROR)
 		return "INLEVEL";
 
-	return W_CheckNameForNum(foundcolormaps[num]);
+	return extra_colormap->lumpname;
 }
-
+#endif
 
 //
 // build a table for quick conversion from 8bpp to 15bpp
diff --git a/src/r_data.h b/src/r_data.h
index 53bf27835863e52dde02098321fc115863ebad3b..54857661abf829f6f6f48d73f63ef36d6c2e45de 100644
--- a/src/r_data.h
+++ b/src/r_data.h
@@ -96,13 +96,57 @@ void R_ClearTextureNumCache(boolean btell);
 INT32 R_TextureNumForName(const char *name);
 INT32 R_CheckTextureNumForName(const char *name);
 
+// Extra Colormap lumps (C_START/C_END) are not used anywhere
+// Uncomment to enable
+//#define EXTRACOLORMAPLUMPS
+
+// Uncomment to make extra_colormaps order Newest -> Oldest
+//#define COLORMAPREVERSELIST
+
 void R_ReInitColormaps(UINT16 num);
 void R_ClearColormaps(void);
-INT32 R_ColormapNumForName(char *name);
-INT32 R_CreateColormap(char *p1, char *p2, char *p3);
-void R_CreateColormap2(char *p1, char *p2, char *p3);
-void R_MakeColormaps(void);
-const char *R_ColormapNameForNum(INT32 num);
+extracolormap_t *R_CreateDefaultColormap(boolean lighttable);
+extracolormap_t *R_GetDefaultColormap(void);
+extracolormap_t *R_CopyColormap(extracolormap_t *extra_colormap, boolean lighttable);
+void R_AddColormapToList(extracolormap_t *extra_colormap);
+
+#ifdef EXTRACOLORMAPLUMPS
+boolean R_CheckDefaultColormapByValues(boolean checkrgba, boolean checkfadergba, boolean checkparams,
+	INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 fog, lumpnum_t lump);
+extracolormap_t *R_GetColormapFromListByValues(INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 fog, lumpnum_t lump);
+#else
+boolean R_CheckDefaultColormapByValues(boolean checkrgba, boolean checkfadergba, boolean checkparams,
+	INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 fog);
+extracolormap_t *R_GetColormapFromListByValues(INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 fog);
+#endif
+boolean R_CheckDefaultColormap(extracolormap_t *extra_colormap, boolean checkrgba, boolean checkfadergba, boolean checkparams);
+boolean R_CheckEqualColormaps(extracolormap_t *exc_a, extracolormap_t *exc_b, boolean checkrgba, boolean checkfadergba, boolean checkparams);
+extracolormap_t *R_GetColormapFromList(extracolormap_t *extra_colormap);
+
+lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap);
+extracolormap_t *R_CreateColormap(char *p1, char *p2, char *p3);
+extracolormap_t *R_AddColormaps(extracolormap_t *exc_augend, extracolormap_t *exc_addend,
+	boolean subR, boolean subG, boolean subB, boolean subA,
+	boolean subFadeR, boolean subFadeG, boolean subFadeB, boolean subFadeA,
+	boolean subFadeStart, boolean subFadeEnd, boolean ignoreFog,
+	boolean useAltAlpha, INT16 altAlpha, INT16 altFadeAlpha,
+	boolean lighttable);
+#ifdef EXTRACOLORMAPLUMPS
+extracolormap_t *R_ColormapForName(char *name);
+const char *R_NameForColormap(extracolormap_t *extra_colormap);
+#endif
+
+#define R_GetRgbaR(rgba) (rgba & 0xFF)
+#define R_GetRgbaG(rgba) ((rgba >> 8) & 0xFF)
+#define R_GetRgbaB(rgba) ((rgba >> 16) & 0xFF)
+#define R_GetRgbaA(rgba) ((rgba >> 24) & 0xFF)
+#define R_GetRgbaRGB(rgba) (rgba & 0xFFFFFF)
+#define R_PutRgbaR(r) (r)
+#define R_PutRgbaG(g) (g << 8)
+#define R_PutRgbaB(b) (b << 16)
+#define R_PutRgbaA(a) (a << 24)
+#define R_PutRgbaRGB(r, g, b) (R_PutRgbaR(r) + R_PutRgbaG(g) + R_PutRgbaB(b))
+#define R_PutRgbaRGBA(r, g, b, a) (R_PutRgbaRGB(r, g, b) + R_PutRgbaA(a))
 
 extern INT32 numtextures;
 
diff --git a/src/r_defs.h b/src/r_defs.h
index 7c8f2a73fc29511268fc8728a6d9700fcfa55447..0e63eec64fef643a4fae69fb88e720fe2bdca1d5 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -50,18 +50,25 @@ typedef struct
 typedef UINT8 lighttable_t;
 
 // ExtraColormap type. Use for extra_colormaps from now on.
-typedef struct
+typedef struct extracolormap_s
 {
-	UINT16 maskcolor, fadecolor;
-	double maskamt;
-	UINT16 fadestart, fadeend;
-	INT32 fog;
+	UINT8 fadestart, fadeend;
+	UINT8 fog; // categorical value, not boolean
 
-	// rgba is used in hw mode for colored sector lighting
+	// store rgba values in combined bitwise
+	// also used in OpenGL instead lighttables
 	INT32 rgba; // similar to maskcolor in sw mode
 	INT32 fadergba; // The colour the colourmaps fade to
 
 	lighttable_t *colormap;
+
+#ifdef EXTRACOLORMAPLUMPS
+	lumpnum_t lump; // for colormap lump matching, init to LUMPERROR
+	char lumpname[9]; // for netsyncing
+#endif
+
+	struct extracolormap_s *next;
+	struct extracolormap_s *prev;
 } extracolormap_t;
 
 //
@@ -177,6 +184,8 @@ typedef struct ffloor_s
 	// these are saved for netgames, so do not let Lua touch these!
 	ffloortype_e spawnflags; // flags the 3D floor spawned with
 	INT32 spawnalpha; // alpha the 3D floor spawned with
+
+	void *fadingdata; // fading FOF thinker
 } ffloor_t;
 
 
@@ -187,7 +196,7 @@ typedef struct lightlist_s
 {
 	fixed_t height;
 	INT16 *lightlevel;
-	extracolormap_t *extra_colormap;
+	extracolormap_t **extra_colormap; // pointer-to-a-pointer, so we can react to colormap changes
 	INT32 flags;
 	ffloor_t *caster;
 #ifdef ESLOPE
@@ -308,6 +317,7 @@ typedef struct sector_s
 	void *floordata; // floor move thinker
 	void *ceilingdata; // ceiling move thinker
 	void *lightingdata; // lighting change thinker
+	void *fadecolormapdata; // fade colormap thinker
 
 	// floor and ceiling texture offsets
 	fixed_t floor_xoffs, floor_yoffs;
@@ -323,8 +333,6 @@ typedef struct sector_s
 	INT32 floorlightsec, ceilinglightsec;
 	INT32 crumblestate; // used for crumbling and bobbing
 
-	INT32 bottommap, midmap, topmap; // dynamic colormaps
-
 	// list of mobjs that are at least partially in the sector
 	// thinglist is a subset of touching_thinglist
 	struct msecnode_s *touching_thinglist;
@@ -383,6 +391,9 @@ typedef struct sector_s
 	boolean hasslope; // The sector, or one of its visible FOFs, contains a slope
 #endif
 
+	// for fade thinker
+	INT16 spawn_lightlevel;
+
 	// these are saved for netgames, so do not let Lua touch these!
 	INT32 spawn_nexttag, spawn_firsttag; // the actual nexttag/firsttag values may differ if the sector's tag was changed
 
@@ -393,6 +404,9 @@ typedef struct sector_s
 	// flag angles sector spawned with (via linedef type 7)
 	angle_t spawn_flrpic_angle;
 	angle_t spawn_ceilpic_angle;
+
+	// colormap structure
+	extracolormap_t *spawn_extra_colormap;
 } sector_t;
 
 //
@@ -468,6 +482,8 @@ typedef struct
 	INT16 repeatcnt; // # of times to repeat midtexture
 
 	char *text; // a concatination of all top, bottom, and mid texture names, for linedef specials that require a string.
+
+	extracolormap_t *colormap_data; // storage for colormaps; not applied to sectors.
 } side_t;
 
 //
diff --git a/src/r_draw.c b/src/r_draw.c
index e06d43f676117f013feb9dfc93b4b080fef7902c..40945f4e0e5c953a64fec90fdde4af982ce39985 100644
--- a/src/r_draw.c
+++ b/src/r_draw.c
@@ -257,7 +257,7 @@ const UINT8 Color_Index[MAXTRANSLATIONS-1][16] = {
 	{0x00, 0x50, 0x50, 0x51, 0x51, 0x52, 0x52, 0x52, 0x54, 0x54, 0x54, 0x54, 0x55, 0x56, 0x57, 0xf5}, // SKINCOLOR_SUPERTAN2
 	{0x50, 0x51, 0x51, 0x52, 0x52, 0x52, 0x54, 0x54, 0x54, 0x54, 0x55, 0x56, 0x57, 0xf5, 0xf7, 0xf9}, // SKINCOLOR_SUPERTAN3
 	{0x51, 0x52, 0x52, 0x52, 0x52, 0x54, 0x54, 0x54, 0x55, 0x56, 0x57, 0xf5, 0xf7, 0xf9, 0xfb, 0xed}, // SKINCOLOR_SUPERTAN4
-	{0x52, 0x52, 0x54, 0x54, 0x54, 0x55, 0x56, 0x57, 0xf5, 0xf7, 0xf9, 0xfb, 0xed, 0xee, 0xef, 0xef} // SKINCOLOR_SUPERTAN5
+	{0x52, 0x52, 0x54, 0x54, 0x54, 0x55, 0x56, 0x57, 0xf5, 0xf7, 0xf9, 0xfb, 0xed, 0xee, 0xef, 0xef}  // SKINCOLOR_SUPERTAN5
 };
 
 // See also the enum skincolors_t
diff --git a/src/r_draw8.c b/src/r_draw8.c
index 9340b61c6135059228a8280ccfe58237b9deb6a9..bda146546457bffd158990726871236582a1625a 100644
--- a/src/r_draw8.c
+++ b/src/r_draw8.c
@@ -261,7 +261,7 @@ void R_Draw2sMultiPatchTranslucentColumn_8(void)
 				val = source[frac>>FRACBITS];
 
 				if (val != TRANSPARENTPIXEL)
-					*dest = colormap[*(transmap + (val<<8) + (*dest))];
+					*dest = *(transmap + (colormap[val]<<8) + (*dest));
 
 				dest += vid.width;
 
@@ -281,12 +281,12 @@ void R_Draw2sMultiPatchTranslucentColumn_8(void)
 			{
 				val = source[(frac>>FRACBITS) & heightmask];
 				if (val != TRANSPARENTPIXEL)
-					*dest = colormap[*(transmap + (val<<8) + (*dest))];
+					*dest = *(transmap + (colormap[val]<<8) + (*dest));
 				dest += vid.width;
 				frac += fracstep;
 				val = source[(frac>>FRACBITS) & heightmask];
 				if (val != TRANSPARENTPIXEL)
-					*dest = colormap[*(transmap + (val<<8) + (*dest))];
+					*dest = *(transmap + (colormap[val]<<8) + (*dest));
 				dest += vid.width;
 				frac += fracstep;
 			}
@@ -294,7 +294,7 @@ void R_Draw2sMultiPatchTranslucentColumn_8(void)
 			{
 				val = source[(frac>>FRACBITS) & heightmask];
 				if (val != TRANSPARENTPIXEL)
-					*dest = colormap[*(transmap + (val<<8) + (*dest))];
+					*dest = *(transmap + (colormap[val]<<8) + (*dest));
 			}
 		}
 	}
@@ -394,7 +394,7 @@ void R_DrawTranslucentColumn_8(void)
 				// Re-map color indices from wall texture column
 				// using a lighting/special effects LUT.
 				// heightmask is the Tutti-Frutti fix
-				*dest = colormap[*(transmap + (source[frac>>FRACBITS]<<8) + (*dest))];
+				*dest = *(transmap + (colormap[source[frac>>FRACBITS]]<<8) + (*dest));
 				dest += vid.width;
 				if ((frac += fracstep) >= heightmask)
 					frac -= heightmask;
@@ -405,15 +405,15 @@ void R_DrawTranslucentColumn_8(void)
 		{
 			while ((count -= 2) >= 0) // texture height is a power of 2
 			{
-				*dest = colormap[*(transmap + ((source[(frac>>FRACBITS)&heightmask]<<8)) + (*dest))];
+				*dest = *(transmap + (colormap[source[(frac>>FRACBITS)&heightmask]]<<8) + (*dest));
 				dest += vid.width;
 				frac += fracstep;
-				*dest = colormap[*(transmap + ((source[(frac>>FRACBITS)&heightmask]<<8)) + (*dest))];
+				*dest = *(transmap + (colormap[source[(frac>>FRACBITS)&heightmask]]<<8) + (*dest));
 				dest += vid.width;
 				frac += fracstep;
 			}
 			if (count & 1)
-				*dest = colormap[*(transmap + ((source[(frac>>FRACBITS)&heightmask]<<8)) + (*dest))];
+				*dest = *(transmap + (colormap[source[(frac>>FRACBITS)&heightmask]]<<8) + (*dest));
 		}
 	}
 }
@@ -464,8 +464,7 @@ void R_DrawTranslatedTranslucentColumn_8(void)
 				//  using a lighting/special effects LUT.
 				// heightmask is the Tutti-Frutti fix
 
-				*dest = dc_colormap[*(dc_transmap
-					+ (dc_colormap[dc_translation[dc_source[frac>>FRACBITS]]]<<8) + (*dest))];
+				*dest = *(dc_transmap + (dc_colormap[dc_translation[dc_source[frac>>FRACBITS]]]<<8) + (*dest));
 
 				dest += vid.width;
 				if ((frac += fracstep) >= heightmask)
@@ -477,17 +476,15 @@ void R_DrawTranslatedTranslucentColumn_8(void)
 		{
 			while ((count -= 2) >= 0) // texture height is a power of 2
 			{
-				*dest = dc_colormap[*(dc_transmap
-					+ (dc_colormap[dc_translation[dc_source[frac>>FRACBITS]]]<<8) + (*dest))];
+				*dest = *(dc_transmap + (dc_colormap[dc_translation[dc_source[(frac>>FRACBITS)&heightmask]]]<<8) + (*dest));
 				dest += vid.width;
 				frac += fracstep;
-				*dest = dc_colormap[*(dc_transmap
-					+ (dc_colormap[dc_translation[dc_source[frac>>FRACBITS]]]<<8) + (*dest))];
+				*dest = *(dc_transmap + (dc_colormap[dc_translation[dc_source[(frac>>FRACBITS)&heightmask]]]<<8) + (*dest));
 				dest += vid.width;
 				frac += fracstep;
 			}
 			if (count & 1)
-				*dest = dc_colormap[*(dc_transmap + (dc_colormap[dc_translation[dc_source[frac>>FRACBITS]]] <<8) + (*dest))];
+				*dest = *(dc_transmap + (dc_colormap[dc_translation[dc_source[(frac>>FRACBITS)&heightmask]]]<<8) + (*dest));
 		}
 	}
 }
@@ -835,8 +832,7 @@ void R_DrawTiltedTranslucentSpan_8(void)
 		v = (INT64)(vz*z) + viewy;
 
 		colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
-
-		*dest = colormap[*(ds_transmap + (source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)] << 8) + dest[0])];
+		*dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest);
 		dest++;
 		iz += ds_sz.x;
 		uz += ds_su.x;
@@ -873,7 +869,7 @@ void R_DrawTiltedTranslucentSpan_8(void)
 		for (i = SPANSIZE-1; i >= 0; i--)
 		{
 			colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
-			*dest = colormap[*(ds_transmap + (source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)] << 8) + dest[0])];
+			*dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest);
 			dest++;
 			u += stepu;
 			v += stepv;
@@ -889,7 +885,7 @@ void R_DrawTiltedTranslucentSpan_8(void)
 			u = (INT64)(startu);
 			v = (INT64)(startv);
 			colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
-			*dest = colormap[*(ds_transmap + (source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)] << 8) + dest[0])];
+			*dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest);
 		}
 		else
 		{
@@ -910,7 +906,7 @@ void R_DrawTiltedTranslucentSpan_8(void)
 			for (; width != 0; width--)
 			{
 				colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
-				*dest = colormap[*(ds_transmap + (source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)] << 8) + dest[0])];
+				*dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest);
 				dest++;
 				u += stepu;
 				v += stepv;
@@ -1221,49 +1217,49 @@ void R_DrawTranslucentSplat_8 (void)
 		// need!
 		val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)];
 		if (val != TRANSPARENTPIXEL)
-			dest[0] = colormap[*(ds_transmap + (val << 8) + dest[0])];
+			dest[0] = *(ds_transmap + (colormap[val] << 8) + dest[0]);
 		xposition += xstep;
 		yposition += ystep;
 
 		val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)];
 		if (val != TRANSPARENTPIXEL)
-			dest[1] = colormap[*(ds_transmap + (val << 8) + dest[1])];
+			dest[1] = *(ds_transmap + (colormap[val] << 8) + dest[1]);
 		xposition += xstep;
 		yposition += ystep;
 
 		val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)];
 		if (val != TRANSPARENTPIXEL)
-			dest[2] = colormap[*(ds_transmap + (val << 8) + dest[2])];
+			dest[2] = *(ds_transmap + (colormap[val] << 8) + dest[2]);
 		xposition += xstep;
 		yposition += ystep;
 
 		val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)];
 		if (val != TRANSPARENTPIXEL)
-			dest[3] = colormap[*(ds_transmap + (val << 8) + dest[3])];
+			dest[3] = *(ds_transmap + (colormap[val] << 8) + dest[3]);
 		xposition += xstep;
 		yposition += ystep;
 
 		val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)];
 		if (val != TRANSPARENTPIXEL)
-			dest[4] = colormap[*(ds_transmap + (val << 8) + dest[4])];
+			dest[4] = *(ds_transmap + (colormap[val] << 8) + dest[4]);
 		xposition += xstep;
 		yposition += ystep;
 
 		val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)];
 		if (val != TRANSPARENTPIXEL)
-			dest[5] = colormap[*(ds_transmap + (val << 8) + dest[5])];
+			dest[5] = *(ds_transmap + (colormap[val] << 8) + dest[5]);
 		xposition += xstep;
 		yposition += ystep;
 
 		val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)];
 		if (val != TRANSPARENTPIXEL)
-			dest[6] = colormap[*(ds_transmap + (val << 8) + dest[6])];
+			dest[6] = *(ds_transmap + (colormap[val] << 8) + dest[6]);
 		xposition += xstep;
 		yposition += ystep;
 
 		val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)];
 		if (val != TRANSPARENTPIXEL)
-			dest[7] = colormap[*(ds_transmap + (val << 8) + dest[7])];
+			dest[7] = *(ds_transmap + (colormap[val] << 8) + dest[7]);
 		xposition += xstep;
 		yposition += ystep;
 
@@ -1274,7 +1270,7 @@ void R_DrawTranslucentSplat_8 (void)
 	{
 		val = source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)];
 		if (val != TRANSPARENTPIXEL)
-			*dest = colormap[*(ds_transmap + (val << 8) + *dest)];
+			*dest = *(ds_transmap + (colormap[val] << 8) + *dest);
 
 		dest++;
 		xposition += xstep;
@@ -1317,35 +1313,35 @@ void R_DrawTranslucentSpan_8 (void)
 		// SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't
 		// have the uber complicated math to calculate it now, so that was a memory write we didn't
 		// need!
-		dest[0] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + dest[0])];
+		dest[0] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[0]);
 		xposition += xstep;
 		yposition += ystep;
 
-		dest[1] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + dest[1])];
+		dest[1] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[1]);
 		xposition += xstep;
 		yposition += ystep;
 
-		dest[2] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + dest[2])];
+		dest[2] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[2]);
 		xposition += xstep;
 		yposition += ystep;
 
-		dest[3] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + dest[3])];
+		dest[3] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[3]);
 		xposition += xstep;
 		yposition += ystep;
 
-		dest[4] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + dest[4])];
+		dest[4] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[4]);
 		xposition += xstep;
 		yposition += ystep;
 
-		dest[5] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + dest[5])];
+		dest[5] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[5]);
 		xposition += xstep;
 		yposition += ystep;
 
-		dest[6] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + dest[6])];
+		dest[6] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[6]);
 		xposition += xstep;
 		yposition += ystep;
 
-		dest[7] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + dest[7])];
+		dest[7] = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + dest[7]);
 		xposition += xstep;
 		yposition += ystep;
 
@@ -1354,7 +1350,7 @@ void R_DrawTranslucentSpan_8 (void)
 	}
 	while (count--)
 	{
-		*dest = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dest)];
+		*dest = *(ds_transmap + (colormap[source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)]] << 8) + *dest);
 		dest++;
 		xposition += xstep;
 		yposition += ystep;
diff --git a/src/r_main.c b/src/r_main.c
index 9d0a8fe6059d5f3c73101849b59afa551a0f0828..bfca180d0011ed9dc34d68bf9948b448b12827b8 100644
--- a/src/r_main.c
+++ b/src/r_main.c
@@ -118,8 +118,7 @@ lighttable_t *scalelightfixed[MAXLIGHTSCALE];
 lighttable_t *zlight[LIGHTLEVELS][MAXLIGHTZ];
 
 // Hack to support extra boom colormaps.
-size_t num_extra_colormaps;
-extracolormap_t extra_colormaps[MAXCOLORMAPS];
+extracolormap_t *extra_colormaps = NULL;
 
 static CV_PossibleValue_t drawdist_cons_t[] = {
 	{256, "256"},	{512, "512"},	{768, "768"},
@@ -1146,9 +1145,9 @@ void R_RenderPlayerView(player_t *player)
 	if (cv_homremoval.value && player == &players[displayplayer]) // if this is display player 1
 	{
 		if (cv_homremoval.value == 1)
-			V_DrawFill(0, 0, vid.width, vid.height, 31); // No HOM effect!
+			V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); // No HOM effect!
 		else //'development' HOM removal -- makes it blindingly obvious if HOM is spotted.
-			V_DrawFill(0, 0, vid.width, vid.height, 32+(timeinmap&15));
+			V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 32+(timeinmap&15));
 	}
 
 	// load previous saved value of skyVisible for the player
diff --git a/src/r_plane.c b/src/r_plane.c
index bc510bbb5ab453274f1b856d279475003f8f1ee4..620ef90d1fbb5d3956a9651e5ad37763c605f4cd 100644
--- a/src/r_plane.c
+++ b/src/r_plane.c
@@ -37,6 +37,9 @@
 // Quincunx antialiasing of flats!
 //#define QUINCUNX
 
+// good night sweet prince
+#define SHITPLANESPARENCY
+
 //SoM: 3/23/2000: Use Boom visplane hashing.
 #define MAXVISPLANES 512
 
@@ -813,7 +816,11 @@ void R_DrawSinglePlane(visplane_t *pl)
 		else // Opaque, but allow transparent flat pixels
 			spanfunc = splatfunc;
 
-		if (pl->extra_colormap && pl->extra_colormap->fog)
+#ifdef SHITPLANESPARENCY
+		if (spanfunc == splatfunc || (pl->extra_colormap && pl->extra_colormap->fog))
+#else
+		if (!pl->extra_colormap || !(pl->extra_colormap->fog & 2))
+#endif
 			light = (pl->lightlevel >> LIGHTSEGSHIFT);
 		else
 			light = LIGHTLEVELS-1;
@@ -867,7 +874,11 @@ void R_DrawSinglePlane(visplane_t *pl)
 			else // Opaque, but allow transparent flat pixels
 				spanfunc = splatfunc;
 
-			if (pl->extra_colormap && pl->extra_colormap->fog)
+#ifdef SHITPLANESPARENCY
+			if (spanfunc == splatfunc || (pl->extra_colormap && pl->extra_colormap->fog))
+#else
+			if (!pl->extra_colormap || !(pl->extra_colormap->fog & 2))
+#endif
 				light = (pl->lightlevel >> LIGHTSEGSHIFT);
 			else
 				light = LIGHTLEVELS-1;
diff --git a/src/r_segs.c b/src/r_segs.c
index 5395f414a043b2fa54ccaaf28043d3d0668a1d30..cde019f66de508f2f4031e720e2d705a3801816a 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -413,7 +413,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
 #endif
 			rlight->startheight = rlight->height; // keep starting value here to reset for each repeat
 			rlight->lightlevel = *light->lightlevel;
-			rlight->extra_colormap = light->extra_colormap;
+			rlight->extra_colormap = *light->extra_colormap;
 			rlight->flags = light->flags;
 
 			if (rlight->flags & FF_FOG || (rlight->extra_colormap && rlight->extra_colormap->fog))
@@ -944,7 +944,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 			}
 
 			rlight->lightlevel = *light->lightlevel;
-			rlight->extra_colormap = light->extra_colormap;
+			rlight->extra_colormap = *light->extra_colormap;
 
 			// Check if the current light effects the colormap/lightlevel
 			if (pfloor->flags & FF_FOG)
@@ -1443,7 +1443,8 @@ static void R_RenderSegLoop (void)
 #ifdef POLYOBJECTS_PLANES
 					// Polyobject-specific hack to fix plane leaking -Red
 					if (ffloor[i].polyobj && top_w >= bottom_w) {
-						ffloor[i].plane->top[rw_x] = ffloor[i].plane->bottom[rw_x] = 0xFFFF;
+						ffloor[i].plane->top[rw_x] = 0xFFFF;
+						ffloor[i].plane->bottom[rw_x] = 0x0000; // fix for sky plane drawing crashes - Monster Iestyn 25/05/18
 					} else
 #endif
 
@@ -1467,7 +1468,8 @@ static void R_RenderSegLoop (void)
 #ifdef POLYOBJECTS_PLANES
 					// Polyobject-specific hack to fix plane leaking -Red
 					if (ffloor[i].polyobj && top_w >= bottom_w) {
-						ffloor[i].plane->top[rw_x] = ffloor[i].plane->bottom[rw_x] = 0xFFFF;
+						ffloor[i].plane->top[rw_x] = 0xFFFF;
+						ffloor[i].plane->bottom[rw_x] = 0x0000; // fix for sky plane drawing crashes - Monster Iestyn 25/05/18
 					} else
 #endif
 
@@ -2693,6 +2695,7 @@ void R_StoreWallRange(INT32 start, INT32 stop)
 	if (linedef->special == 41) { // HORIZON LINES
 		topstep = bottomstep = 0;
 		topfrac = bottomfrac = (centeryfrac>>4);
+		topfrac++; // Prevent 1px HOM
 	} else {
 		topstep = -FixedMul (rw_scalestep, worldtop);
 		topfrac = (centeryfrac>>4) - FixedMul (worldtop, rw_scale);
@@ -2805,7 +2808,7 @@ void R_StoreWallRange(INT32 start, INT32 stop)
 			}
 
 			rlight->lightlevel = *light->lightlevel;
-			rlight->extra_colormap = light->extra_colormap;
+			rlight->extra_colormap = *light->extra_colormap;
 			p++;
 		}
 
diff --git a/src/r_state.h b/src/r_state.h
index ac3e1fa42576a050c4edfdf177b560defc08eead..91c2092e98f140035b0efc332594ec60e4ccaf0e 100644
--- a/src/r_state.h
+++ b/src/r_state.h
@@ -40,11 +40,7 @@ extern sprcache_t *spritecachedinfo;
 extern lighttable_t *colormaps;
 
 // Boom colormaps.
-// Had to put a limit on colormaps :(
-#define MAXCOLORMAPS 60
-
-extern size_t num_extra_colormaps;
-extern extracolormap_t extra_colormaps[MAXCOLORMAPS];
+extern extracolormap_t *extra_colormaps;
 
 // for global animation
 extern INT32 *texturetranslation;
diff --git a/src/r_things.c b/src/r_things.c
index fad47d26b12472876a5b97dd9fe304eb0dfc84a6..a6a7d9877c977b60eb2008126f9d87080fd96cf6 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -717,7 +717,7 @@ static void R_DrawVisSprite(vissprite_t *vis)
 
 	colfunc = basecolfunc; // hack: this isn't resetting properly somewhere.
 	dc_colormap = vis->colormap;
-	if (!(vis->cut & SC_PRECIP) && (vis->mobj->flags & MF_BOSS) && (vis->mobj->flags2 & MF2_FRET) && (leveltime & 1)) // Bosses "flash"
+	if (!(vis->cut & SC_PRECIP) && (vis->mobj->flags & (MF_ENEMY|MF_BOSS)) && (vis->mobj->flags2 & MF2_FRET) && (leveltime & 1)) // Bosses "flash"
 	{
 		// translate certain pixels to white
 		colfunc = transcolfunc;
@@ -981,9 +981,10 @@ static void R_SplitSprite(vissprite_t *sprite)
 			else
 				spritelights = scalelight[lightnum];
 
-			newsprite->extra_colormap = sector->lightlist[i].extra_colormap;
+			newsprite->extra_colormap = *sector->lightlist[i].extra_colormap;
 
-			if (!((newsprite->cut & SC_FULLBRIGHT) && (!newsprite->extra_colormap || !newsprite->extra_colormap->fog)))
+			if (!((newsprite->cut & SC_FULLBRIGHT)
+				&& (!newsprite->extra_colormap || !(newsprite->extra_colormap->fog & 1))))
 			{
 				lindex = FixedMul(sprite->xscale, FixedDiv(640, vid.width))>>(LIGHTSCALESHIFT);
 
@@ -1359,7 +1360,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	vis->sz = (INT16)((centeryfrac - FixedMul(vis->gz - viewz, sortscale))>>FRACBITS);
 	vis->cut = cut;
 	if (thing->subsector->sector->numlights)
-		vis->extra_colormap = thing->subsector->sector->lightlist[light].extra_colormap;
+		vis->extra_colormap = *thing->subsector->sector->lightlist[light].extra_colormap;
 	else
 		vis->extra_colormap = thing->subsector->sector->extra_colormap;
 
@@ -1403,7 +1404,7 @@ static void R_ProjectSprite(mobj_t *thing)
 		vis->cut |= SC_FULLBRIGHT;
 
 	if (vis->cut & SC_FULLBRIGHT
-		&& (!vis->extra_colormap || !vis->extra_colormap->fog))
+		&& (!vis->extra_colormap || !(vis->extra_colormap->fog & 1)))
 	{
 		// full bright: goggles
 		vis->colormap = colormaps;
@@ -1524,6 +1525,17 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing)
 			return;
 	}
 
+	// okay, we can't return now except for vertical clipping... this is a hack, but weather isn't networked, so it should be ok
+	if (!(thing->precipflags & PCF_THUNK))
+	{
+		if (thing->precipflags & PCF_RAIN)
+			P_RainThinker(thing);
+		else
+			P_SnowThinker(thing);
+		thing->precipflags |= PCF_THUNK;
+	}
+
+
 	//SoM: 3/17/2000: Disregard sprites that are out of view..
 	gzt = thing->z + spritecachedinfo[lump].topoffset;
 	gz = gzt - spritecachedinfo[lump].height;
@@ -1641,8 +1653,10 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel)
 
 			approx_dist = P_AproxDistance(viewx-thing->x, viewy-thing->y);
 
-			if (approx_dist <= limit_dist)
-				R_ProjectSprite(thing);
+			if (approx_dist > limit_dist)
+				continue;
+
+			R_ProjectSprite(thing);
 		}
 	}
 	else
@@ -1663,8 +1677,10 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel)
 
 			approx_dist = P_AproxDistance(viewx-precipthing->x, viewy-precipthing->y);
 
-			if (approx_dist <= limit_dist)
-				R_ProjectPrecipitationSprite(precipthing);
+			if (approx_dist > limit_dist)
+				continue;
+
+			R_ProjectPrecipitationSprite(precipthing);
 		}
 	}
 	else
@@ -2673,7 +2689,7 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum)
 		if (player->followmobj)
 		{
 			P_RemoveMobj(player->followmobj);
-			player->followmobj = NULL;
+			P_SetTarget(&player->followmobj, NULL);
 		}
 
 		if (player->mo)
diff --git a/src/s_sound.c b/src/s_sound.c
index 0ac3a2c76a3516eb4eb45c1fab50e6a12632816b..1c7b00e2c0b4d5d3b51eef7d404d27963c9576ae 100644
--- a/src/s_sound.c
+++ b/src/s_sound.c
@@ -75,7 +75,7 @@ consvar_t stereoreverse = {"stereoreverse", "Off", CV_SAVE, CV_OnOff, NULL, 0, N
 static consvar_t precachesound = {"precachesound", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
 
 // actual general (maximum) sound & music volume, saved into the config
-consvar_t cv_soundvolume = {"soundvolume", "31", CV_SAVE, soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_soundvolume = {"soundvolume", "18", CV_SAVE, soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
 consvar_t cv_digmusicvolume = {"digmusicvolume", "18", CV_SAVE, soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
 consvar_t cv_midimusicvolume = {"midimusicvolume", "18", CV_SAVE, soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
 
@@ -230,7 +230,7 @@ void S_RegisterSoundStuff(void)
 {
 	if (dedicated)
 	{
-		nosound = true;
+		sound_disabled = true;
 		return;
 	}
 
@@ -494,7 +494,7 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume)
 	mobj_t *listenmobj = players[displayplayer].mo;
 	mobj_t *listenmobj2 = NULL;
 
-	if (sound_disabled || !sound_started || nosound)
+	if (sound_disabled || !sound_started)
 		return;
 
 	// Don't want a sound? Okay then...
@@ -626,7 +626,7 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume)
 
 		// Assigns the handle to one of the channels in the
 		// mix/output buffer.
-		channels[cnum].handle = I_StartSound(sfx_id, volume, sep, pitch, priority);
+		channels[cnum].handle = I_StartSound(sfx_id, volume, sep, pitch, priority, cnum);
 	}
 
 dontplay:
@@ -679,7 +679,7 @@ dontplay:
 
 	// Assigns the handle to one of the channels in the
 	// mix/output buffer.
-	channels[cnum].handle = I_StartSound(sfx_id, volume, sep, pitch, priority);
+	channels[cnum].handle = I_StartSound(sfx_id, volume, sep, pitch, priority, cnum);
 }
 
 void S_StartSound(const void *origin, sfxenum_t sfx_id)
@@ -824,7 +824,7 @@ void S_UpdateSounds(void)
 		goto notinlevel;
 	}
 
-	if (dedicated || nosound)
+	if (dedicated || sound_disabled)
 		return;
 
 	if (players[displayplayer].awayviewtics)
@@ -1283,6 +1283,43 @@ void S_StartSoundName(void *mo, const char *soundname)
 	S_StartSound(mo, soundnum);
 }
 
+//
+// Initializes sound stuff, including volume
+// Sets channels, SFX volume,
+//  allocates channel buffer, sets S_sfx lookup.
+//
+void S_InitSfxChannels(INT32 sfxVolume)
+{
+	INT32 i;
+
+	if (dedicated)
+		return;
+
+	S_SetSfxVolume(sfxVolume);
+
+	SetChannelsNum();
+
+	// Note that sounds have not been cached (yet).
+	for (i = 1; i < NUMSFX; i++)
+	{
+		S_sfx[i].usefulness = -1; // for I_GetSfx()
+		S_sfx[i].lumpnum = LUMPERROR;
+	}
+
+	// precache sounds if requested by cmdline, or precachesound var true
+	if (!sound_disabled && (M_CheckParm("-precachesound") || precachesound.value))
+	{
+		// Initialize external data (all sounds) at start, keep static.
+		CONS_Printf(M_GetText("Loading sounds... "));
+
+		for (i = 1; i < NUMSFX; i++)
+			if (S_sfx[i].name)
+				S_sfx[i].data = I_GetSfx(&S_sfx[i]);
+
+		CONS_Printf(M_GetText(" pre-cached all sound data\n"));
+	}
+}
+
 /// ------------------------
 /// Music
 /// ------------------------
@@ -1309,31 +1346,109 @@ const char *compat_special_music_slots[16] =
 };
 #endif
 
-#define music_playing (music_name[0]) // String is empty if no music is playing
-
 static char      music_name[7]; // up to 6-character name
-static lumpnum_t music_lumpnum; // lump number of music (used??)
-static void     *music_data;    // music raw data
-static INT32     music_handle;  // once registered, the handle for the music
+static void      *music_data;
+static UINT16    music_flags;
+static boolean   music_looping;
+
+/// ------------------------
+/// Music Status
+/// ------------------------
+
+boolean S_DigMusicDisabled(void)
+{
+	return digital_disabled;
+}
+
+boolean S_MIDIMusicDisabled(void)
+{
+	return midi_disabled;
+}
+
+boolean S_MusicDisabled(void)
+{
+	return (midi_disabled && digital_disabled);
+}
+
+boolean S_MusicPlaying(void)
+{
+	return I_SongPlaying();
+}
+
+boolean S_MusicPaused(void)
+{
+	return I_SongPaused();
+}
 
-static boolean mus_paused     = 0;  // whether songs are mus_paused
+musictype_t S_MusicType(void)
+{
+	return I_SongType();
+}
+
+boolean S_MusicInfo(char *mname, UINT16 *mflags, boolean *looping)
+{
+	if (!I_SongPlaying())
+		return false;
+
+	strncpy(mname, music_name, 7);
+	mname[6] = 0;
+	*mflags = music_flags;
+	*looping = music_looping;
+
+	return (boolean)mname[0];
+}
 
-static boolean S_MIDIMusic(const char *mname, boolean looping)
+boolean S_MusicExists(const char *mname, boolean checkMIDI, boolean checkDigi)
+{
+	return (
+		(checkDigi ? W_CheckNumForName(va("O_%s", mname)) != LUMPERROR : false)
+		|| (checkMIDI ? W_CheckNumForName(va("D_%s", mname)) != LUMPERROR : false)
+	);
+}
+
+/// ------------------------
+/// Music Effects
+/// ------------------------
+
+boolean S_SpeedMusic(float speed)
+{
+	return I_SetSongSpeed(speed);
+}
+
+/// ------------------------
+/// Music Playback
+/// ------------------------
+
+static boolean S_LoadMusic(const char *mname)
 {
 	lumpnum_t mlumpnum;
 	void *mdata;
-	INT32 mhandle;
 
-	if (nomidimusic || music_disabled)
-		return false; // didn't search.
+	if (S_MusicDisabled())
+		return false;
 
-	if (W_CheckNumForName(va("d_%s", mname)) == LUMPERROR)
+	if (!S_DigMusicDisabled() && S_DigExists(mname))
+		mlumpnum = W_GetNumForName(va("o_%s", mname));
+	else if (!S_MIDIMusicDisabled() && S_MIDIExists(mname))
+		mlumpnum = W_GetNumForName(va("d_%s", mname));
+	else if (S_DigMusicDisabled() && S_DigExists(mname))
+	{
+		CONS_Alert(CONS_NOTICE, "Digital music is disabled!\n");
+		return false;
+	}
+	else if (S_MIDIMusicDisabled() && S_MIDIExists(mname))
+	{
+		CONS_Alert(CONS_NOTICE, "MIDI music is disabled!\n");
 		return false;
-	mlumpnum = W_GetNumForName(va("d_%s", mname));
+	}
+	else
+	{
+		CONS_Alert(CONS_ERROR, M_GetText("Music lump %.6s not found!\n"), mname);
+		return false;
+	}
 
 	// load & register it
 	mdata = W_CacheLumpNum(mlumpnum, PU_MUSIC);
-	mhandle = I_RegisterSong(mdata, W_LumpLength(mlumpnum));
 
 #ifdef MUSSERV
 	if (msg_id != -1)
@@ -1347,37 +1462,49 @@ static boolean S_MIDIMusic(const char *mname, boolean looping)
 	}
 #endif
 
-	// play it
-	if (!I_PlaySong(mhandle, looping))
+	if (I_LoadSong(mdata, W_LumpLength(mlumpnum)))
+	{
+		strncpy(music_name, mname, 7);
+		music_name[6] = 0;
+		music_data = mdata;
+		return true;
+	}
+	else
 		return false;
+}
 
-	strncpy(music_name, mname, 7);
-	music_name[6] = 0;
-	music_lumpnum = mlumpnum;
-	music_data = mdata;
-	music_handle = mhandle;
-	return true;
+static void S_UnloadMusic(void)
+{
+	I_UnloadSong();
+
+#ifndef HAVE_SDL //SDL uses RWOPS
+	Z_ChangeTag(music_data, PU_CACHE);
+#endif
+	music_data = NULL;
+
+	music_name[0] = 0;
+	music_flags = 0;
+	music_looping = false;
 }
 
-static boolean S_DigMusic(const char *mname, boolean looping)
+static boolean S_PlayMusic(boolean looping)
 {
-	if (nodigimusic || digital_disabled)
-		return false; // try midi
+	if (S_MusicDisabled())
+		return false;
 
-	if (!I_StartDigSong(mname, looping))
+	if (!I_PlaySong(looping))
+	{
+		S_UnloadMusic();
 		return false;
+	}
 
-	strncpy(music_name, mname, 7);
-	music_name[6] = 0;
-	music_lumpnum = LUMPERROR;
-	music_data = NULL;
-	music_handle = 0;
+	S_InitMusicVolume(); // switch between digi and sequence volume
 	return true;
 }
 
 void S_ChangeMusic(const char *mmusic, UINT16 mflags, boolean looping)
 {
-	if ((nomidimusic || music_disabled) && (nodigimusic || digital_disabled))
+	if (S_MusicDisabled())
 		return;
 
 	// No Music (empty string)
@@ -1387,44 +1514,39 @@ void S_ChangeMusic(const char *mmusic, UINT16 mflags, boolean looping)
 		return;
 	}
 
-	if (strncmp(music_name, mmusic, 6))
+	if (strnicmp(music_name, mmusic, 6))
 	{
 		S_StopMusic(); // shutdown old music
-		if (!S_DigMusic(mmusic, looping) && !S_MIDIMusic(mmusic, looping))
+
+		if (!S_LoadMusic(mmusic))
+		{
+			CONS_Alert(CONS_ERROR, "Music %.6s could not be loaded!\n", mmusic);
+			return;
+		}
+
+		music_flags = mflags;
+		music_looping = looping;
+
+		if (!S_PlayMusic(looping))
 		{
-			CONS_Alert(CONS_ERROR, M_GetText("Music lump %.6s not found!\n"), mmusic);
+			CONS_Alert(CONS_ERROR, "Music %.6s could not be played!\n", mmusic);
 			return;
 		}
 	}
 	I_SetSongTrack(mflags & MUSIC_TRACKMASK);
 }
 
-boolean S_SpeedMusic(float speed)
-{
-	return I_SetSongSpeed(speed);
-}
-
 void S_StopMusic(void)
 {
-	if (!music_playing)
+	if (!I_SongPlaying())
 		return;
 
-	if (mus_paused)
-		I_ResumeSong(music_handle);
-
-	if (!nodigimusic)
-		I_StopDigSong();
+	if (I_SongPaused())
+		I_ResumeSong();
 
 	S_SpeedMusic(1.0f);
-	I_StopSong(music_handle);
-	I_UnRegisterSong(music_handle);
-
-#ifndef HAVE_SDL //SDL uses RWOPS
-	Z_ChangeTag(music_data, PU_CACHE);
-#endif
-
-	music_data = NULL;
-	music_name[0] = 0;
+	I_StopSong();
+	S_UnloadMusic(); // for now, stopping also means you unload the song
 
 	if (cv_closedcaptioning.value)
 	{
@@ -1433,81 +1555,70 @@ void S_StopMusic(void)
 	}
 }
 
-void S_SetDigMusicVolume(INT32 volume)
+//
+// Stop and resume music, during game PAUSE.
+//
+void S_PauseAudio(void)
 {
-	if (volume < 0 || volume > 31)
-		CONS_Alert(CONS_WARNING, "musicvolume should be between 0-31\n");
+	if (I_SongPlaying() && !I_SongPaused())
+		I_PauseSong();
 
-	CV_SetValue(&cv_digmusicvolume, volume&31);
-	actualdigmusicvolume = cv_digmusicvolume.value;   //check for change of var
-
-#ifdef DJGPPDOS
-	I_SetDigMusicVolume(31); // Trick for buggy dos drivers. Win32 doesn't need this.
+	// pause cd music
+#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL)
+	I_PauseCD();
+#else
+	I_StopCD();
 #endif
-	I_SetDigMusicVolume(volume&31);
 }
 
-void S_SetMIDIMusicVolume(INT32 volume)
+void S_ResumeAudio(void)
 {
-	if (volume < 0 || volume > 31)
-		CONS_Alert(CONS_WARNING, "musicvolume should be between 0-31\n");
+	if (I_SongPlaying() && I_SongPaused())
+		I_ResumeSong();
 
-	CV_SetValue(&cv_midimusicvolume, volume&0x1f);
-	actualmidimusicvolume = cv_midimusicvolume.value;   //check for change of var
-
-#ifdef DJGPPDOS
-	I_SetMIDIMusicVolume(31); // Trick for buggy dos drivers. Win32 doesn't need this.
-#endif
-	I_SetMIDIMusicVolume(volume&0x1f);
+	// resume cd music
+	I_ResumeCD();
 }
 
-/// ------------------------
-/// Init & Others
-/// ------------------------
-
-//
-// Initializes sound stuff, including volume
-// Sets channels, SFX and music volume,
-//  allocates channel buffer, sets S_sfx lookup.
-//
-void S_Init(INT32 sfxVolume, INT32 digMusicVolume, INT32 midiMusicVolume)
+void S_SetMusicVolume(INT32 digvolume, INT32 seqvolume)
 {
-	INT32 i;
-
-	if (dedicated)
-		return;
-
-	S_SetSfxVolume(sfxVolume);
-	S_SetDigMusicVolume(digMusicVolume);
-	S_SetMIDIMusicVolume(midiMusicVolume);
-
-	SetChannelsNum();
+	if (digvolume < 0)
+		digvolume = cv_digmusicvolume.value;
+	if (seqvolume < 0)
+		seqvolume = cv_midimusicvolume.value;
+
+	if (digvolume < 0 || digvolume > 31)
+		CONS_Alert(CONS_WARNING, "digmusicvolume should be between 0-31\n");
+	CV_SetValue(&cv_digmusicvolume, digvolume&31);
+	actualdigmusicvolume = cv_digmusicvolume.value;   //check for change of var
 
-	// no sounds are playing, and they are not mus_paused
-	mus_paused = 0;
+	if (seqvolume < 0 || seqvolume > 31)
+		CONS_Alert(CONS_WARNING, "midimusicvolume should be between 0-31\n");
+	CV_SetValue(&cv_midimusicvolume, seqvolume&31);
+	actualmidimusicvolume = cv_midimusicvolume.value;   //check for change of var
 
-	// Note that sounds have not been cached (yet).
-	for (i = 1; i < NUMSFX; i++)
-	{
-		S_sfx[i].usefulness = -1; // for I_GetSfx()
-		S_sfx[i].lumpnum = LUMPERROR;
-	}
+#ifdef DJGPPDOS
+	digvolume = seqvolume = 31;
+#endif
 
-	// precache sounds if requested by cmdline, or precachesound var true
-	if (!nosound && (M_CheckParm("-precachesound") || precachesound.value))
+	switch(I_SongType())
 	{
-		// Initialize external data (all sounds) at start, keep static.
-		CONS_Printf(M_GetText("Loading sounds... "));
-
-		for (i = 1; i < NUMSFX; i++)
-			if (S_sfx[i].name)
-				S_sfx[i].data = I_GetSfx(&S_sfx[i]);
-
-		CONS_Printf(M_GetText(" pre-cached all sound data\n"));
+		case MU_MID:
+		//case MU_MOD:
+		//case MU_GME:
+			I_SetMusicVolume(seqvolume&31);
+			break;
+		default:
+			I_SetMusicVolume(digvolume&31);
+			break;
 	}
 }
 
 
+/// ------------------------
+/// Init & Others
+/// ------------------------
+
 //
 // Per level startup code.
 // Kills playing sounds at start of level,
@@ -1522,46 +1633,7 @@ void S_Start(void)
 		mapmusflags = (mapheaderinfo[gamemap-1]->mustrack & MUSIC_TRACKMASK);
 	}
 
-	mus_paused = 0;
-
 	if (cv_resetmusic.value)
 		S_StopMusic();
 	S_ChangeMusic(mapmusname, mapmusflags, true);
 }
-
-//
-// Stop and resume music, during game PAUSE.
-//
-void S_PauseAudio(void)
-{
-	if (!nodigimusic)
-		I_PauseSong(0);
-
-	if (music_playing && !mus_paused)
-	{
-		I_PauseSong(music_handle);
-		mus_paused = true;
-	}
-
-	// pause cd music
-#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL)
-	I_PauseCD();
-#else
-	I_StopCD();
-#endif
-}
-
-void S_ResumeAudio(void)
-{
-	if (!nodigimusic)
-		I_ResumeSong(0);
-	else
-	if (music_playing && mus_paused)
-	{
-		I_ResumeSong(music_handle);
-		mus_paused = false;
-	}
-
-	// resume cd music
-	I_ResumeCD();
-}
diff --git a/src/s_sound.h b/src/s_sound.h
index 4b9735480b7ea1cd666431e5f6dac592d2bc6ec5..c94ae6652ff12075e852f232f2b4baaa9b43b6ef 100644
--- a/src/s_sound.h
+++ b/src/s_sound.h
@@ -14,6 +14,7 @@
 #ifndef __S_SOUND__
 #define __S_SOUND__
 
+#include "i_sound.h" // musictype_t
 #include "sounds.h"
 #include "m_fixed.h"
 #include "command.h"
@@ -97,9 +98,9 @@ void S_RegisterSoundStuff(void);
 
 //
 // Initializes sound stuff, including volume
-// Sets channels, SFX and music volume, allocates channel buffer, sets S_sfx lookup.
+// Sets channels, SFX, allocates channel buffer, sets S_sfx lookup.
 //
-void S_Init(INT32 sfxVolume, INT32 digMusicVolume, INT32 midiMusicVolume);
+void S_InitSfxChannels(INT32 sfxVolume);
 
 //
 // Per level startup code.
@@ -125,6 +126,33 @@ void S_StartSoundAtVolume(const void *origin, sfxenum_t sound_id, INT32 volume);
 // Stop sound for thing at <origin>
 void S_StopSound(void *origin);
 
+//
+// Music Status
+//
+
+boolean S_DigMusicDisabled(void);
+boolean S_MIDIMusicDisabled(void);
+boolean S_MusicDisabled(void);
+boolean S_MusicPlaying(void);
+boolean S_MusicPaused(void);
+musictype_t S_MusicType(void);
+boolean S_MusicInfo(char *mname, UINT16 *mflags, boolean *looping);
+boolean S_MusicExists(const char *mname, boolean checkMIDI, boolean checkDigi);
+#define S_DigExists(a) S_MusicExists(a, false, true)
+#define S_MIDIExists(a) S_MusicExists(a, true, false)
+
+
+//
+// Music Properties
+//
+
+// Set Speed of Music
+boolean S_SpeedMusic(float speed);
+
+//
+// Music Routines
+//
+
 // Start music track, arbitrary, given its name, and set whether looping
 // note: music flags 12 bits for tracknum (gme, other formats with more than one track)
 //       13-15 aren't used yet
@@ -132,9 +160,6 @@ void S_StopSound(void *origin);
 #define S_ChangeMusicInternal(a,b) S_ChangeMusic(a,0,b)
 void S_ChangeMusic(const char *mmusic, UINT16 mflags, boolean looping);
 
-// Set Speed of Music
-boolean S_SpeedMusic(float speed);
-
 // Stops the music.
 void S_StopMusic(void);
 
@@ -149,9 +174,11 @@ void S_UpdateSounds(void);
 
 FUNCMATH fixed_t S_CalculateSoundDistance(fixed_t px1, fixed_t py1, fixed_t pz1, fixed_t px2, fixed_t py2, fixed_t pz2);
 
-void S_SetDigMusicVolume(INT32 volume);
-void S_SetMIDIMusicVolume(INT32 volume);
 void S_SetSfxVolume(INT32 volume);
+void S_SetMusicVolume(INT32 digvolume, INT32 seqvolume);
+#define S_SetDigMusicVolume(a) S_SetMusicVolume(a,-1)
+#define S_SetMIDIMusicVolume(a) S_SetMusicVolume(-1,a)
+#define S_InitMusicVolume() S_SetMusicVolume(-1,-1)
 
 INT32 S_OriginPlaying(void *origin);
 INT32 S_IdPlaying(sfxenum_t id);
diff --git a/src/screen.c b/src/screen.c
index 00307af37876cb663bb7bc49b3e7c20fe6a24045..39493d0f734175fe2b705dadf530fcfc8a692360 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -446,10 +446,11 @@ void SCR_ClosedCaptions(void)
 	{
 		if (splitscreen)
 			basey -= 8;
-		else if (((maptol & TOL_NIGHTS) && (modeattacking == ATTACKING_NIGHTS))
-		|| (cv_powerupdisplay.value == 2)
+		else if ((modeattacking == ATTACKING_NIGHTS)
+		|| (!(maptol & TOL_NIGHTS)
+		&& ((cv_powerupdisplay.value == 2)
 		|| (cv_powerupdisplay.value == 1 && ((stplyr == &players[displayplayer] && !camera.chase)
-		|| ((splitscreen && stplyr == &players[secondarydisplayplayer]) && !camera2.chase))))
+		|| ((splitscreen && stplyr == &players[secondarydisplayplayer]) && !camera2.chase))))))
 			basey -= 16;
 	}
 
diff --git a/src/sdl/IMG_xpm.c b/src/sdl/IMG_xpm.c
index e08736d66515b89367f29022afd4ed663e570497..8adfd3434f1fa72a2afc59ee3a5059483c5c417c 100644
--- a/src/sdl/IMG_xpm.c
+++ b/src/sdl/IMG_xpm.c
@@ -1,27 +1,24 @@
 /*
-    SDL_image:  An example image loading library for use with SDL
-    Copyright (C) 1999-2004 Sam Lantinga
+  SDL_image:  An example image loading library for use with SDL
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
 
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Library General Public
-    License as published by the Free Software Foundation; either
-    version 2 of the License, or (at your option) any later version.
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
 
-    This library is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-    Library General Public License for more details.
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
 
-    You should have received a copy of the GNU Library General Public
-    License along with this library; if not, write to the Free
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-    Sam Lantinga
-    slouken@libsdl.org
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
 */
 
-/* $Id: IMG_xpm.c,v 1.10 2004/01/04 22:04:38 slouken Exp $ */
-
 /*
  * XPM (X PixMap) image loader:
  *
@@ -45,98 +42,110 @@
  * requires about 13K in binary form.
  */
 
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-
-//#include "SDL_image.h"
-
+#if 0
+#include "SDL_image.h"
+#else
+extern SDLCALL int SDLCALL IMG_isXPM(SDL_RWops *src);
+extern SDLCALL SDL_Surface *IMG_LoadXPM_RW(SDL_RWops *src);
+extern SDLCALL SDL_Surface * SDLCALL IMG_ReadXPMFromArray(const char **xpm);
+#define IMG_SetError    SDL_SetError
+#define IMG_GetError    SDL_GetError
+#endif
 
 #ifdef LOAD_XPM
 
 /* See if an image is contained in a data source */
-#if 0
 int IMG_isXPM(SDL_RWops *src)
 {
-        char magic[9];
+    Sint64 start;
+    int is_XPM;
+    char magic[9];
 
-        return (SDL_RWread(src, magic, sizeof (magic), 1)
-                && memcmp(magic, "/* XPM */", 9) == 0);
+    if ( !src )
+        return 0;
+    start = SDL_RWtell(src);
+    is_XPM = 0;
+    if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
+        if ( SDL_memcmp(magic, "/* XPM */", sizeof(magic)) == 0 ) {
+            is_XPM = 1;
+        }
+    }
+    SDL_RWseek(src, start, RW_SEEK_SET);
+    return(is_XPM);
 }
-#endif
 
 /* Hash table to look up colors from pixel strings */
 #define STARTING_HASH_SIZE 256
 
 struct hash_entry {
-        char *key;
-        Uint32 color;
-        struct hash_entry *next;
+    const char *key;
+    Uint32 color;
+    struct hash_entry *next;
 };
 
 struct color_hash {
-        struct hash_entry **table;
-        struct hash_entry *entries; /* array of all entries */
-        struct hash_entry *next_free;
-        size_t size;
-        int maxnum;
+    struct hash_entry **table;
+    struct hash_entry *entries; /* array of all entries */
+    struct hash_entry *next_free;
+    int size;
+    int maxnum;
 };
 
-static int hash_key(const char *key, int cpp, size_t size)
+static int hash_key(const char *key, int cpp, int size)
 {
-        int hash;
+    int hash;
 
-        hash = 0;
-        while ( cpp-- > 0 ) {
-                hash = hash * 33 + *key++;
-        }
-        return (int)(hash & (size - 1));
+    hash = 0;
+    while ( cpp-- > 0 ) {
+        hash = hash * 33 + *key++;
+    }
+    return hash & (size - 1);
 }
 
 static struct color_hash *create_colorhash(int maxnum)
 {
-        size_t bytes;
-		int s;
-        struct color_hash *hash;
-
-        /* we know how many entries we need, so we can allocate
-           everything here */
-        hash = malloc(sizeof *hash);
-        if (!hash)
-                return NULL;
+    int bytes, s;
+    struct color_hash *hash;
 
-        /* use power-of-2 sized hash table for decoding speed */
-        for (s = STARTING_HASH_SIZE; s < maxnum; s <<= 1)
-                ;
-        hash->size = s;
-        hash->maxnum = maxnum;
-        bytes = hash->size * sizeof (struct hash_entry **);
-        hash->entries = NULL;        /* in case malloc fails */
-        hash->table = malloc(bytes);
-        if (!hash->table)
-                return NULL;
-        memset(hash->table, 0, bytes);
-        hash->entries = malloc(maxnum * sizeof (struct hash_entry));
-        if (!hash->entries)
-        {
-                free(hash->table);
-                return NULL;
-        }
-        hash->next_free = hash->entries;
-        return hash;
+    /* we know how many entries we need, so we can allocate
+       everything here */
+    hash = (struct color_hash *)SDL_malloc(sizeof *hash);
+    if (!hash)
+        return NULL;
+
+    /* use power-of-2 sized hash table for decoding speed */
+    for (s = STARTING_HASH_SIZE; s < maxnum; s <<= 1)
+        ;
+    hash->size = s;
+    hash->maxnum = maxnum;
+    bytes = hash->size * sizeof(struct hash_entry **);
+    hash->entries = NULL;   /* in case malloc fails */
+    hash->table = (struct hash_entry **)SDL_malloc(bytes);
+    if (!hash->table) {
+        SDL_free(hash);
+        return NULL;
+    }
+    SDL_memset(hash->table, 0, bytes);
+    hash->entries = (struct hash_entry *)SDL_malloc(maxnum * sizeof(struct hash_entry));
+    if (!hash->entries) {
+        SDL_free(hash->table);
+        SDL_free(hash);
+        return NULL;
+    }
+    hash->next_free = hash->entries;
+    return hash;
 }
 
 static int add_colorhash(struct color_hash *hash,
                          char *key, int cpp, Uint32 color)
 {
-        const int indexkey = hash_key(key, cpp, hash->size);
-        struct hash_entry *e = hash->next_free++;
-        e->color = color;
-        e->key = key;
-        e->next = hash->table[indexkey];
-        hash->table[indexkey] = e;
-        return 1;
+    int index = hash_key(key, cpp, hash->size);
+    struct hash_entry *e = hash->next_free++;
+    e->color = color;
+    e->key = key;
+    e->next = hash->table[index];
+    hash->table[index] = e;
+    return 1;
 }
 
 /* fast lookup that works if cpp == 1 */
@@ -144,88 +153,756 @@ static int add_colorhash(struct color_hash *hash,
 
 static Uint32 get_colorhash(struct color_hash *hash, const char *key, int cpp)
 {
-        struct hash_entry *entry = hash->table[hash_key(key, cpp, hash->size)];
-        while (entry) {
-                if (memcmp(key, entry->key, cpp) == 0)
-                        return entry->color;
-                entry = entry->next;
-        }
-        return 0;                /* garbage in - garbage out */
+    struct hash_entry *entry = hash->table[hash_key(key, cpp, hash->size)];
+    while (entry) {
+        if (SDL_memcmp(key, entry->key, cpp) == 0)
+            return entry->color;
+        entry = entry->next;
+    }
+    return 0;       /* garbage in - garbage out */
 }
 
 static void free_colorhash(struct color_hash *hash)
 {
-        if (hash && hash->table) {
-                free(hash->table);
-                free(hash->entries);
-                free(hash);
-        }
-}
-
-/* portable case-insensitive string comparison */
-static int string_equal(const char *a, const char *b, size_t n)
-{
-        while (*a && *b && n) {
-                if (toupper((unsigned char)*a) != toupper((unsigned char)*b))
-                        return 0;
-                a++;
-                b++;
-                n--;
-        }
-        return *a == *b;
+    if (hash) {
+        if (hash->table)
+            SDL_free(hash->table);
+        if (hash->entries)
+            SDL_free(hash->entries);
+        SDL_free(hash);
+    }
 }
 
-#undef ARRAYSIZE
-#define ARRAYSIZE(a) (int)(sizeof (a) / sizeof ((a)[0]))
-
 /*
  * convert colour spec to RGB (in 0xrrggbb format).
  * return 1 if successful.
  */
-static int color_to_rgb(const char *spec, size_t speclen, Uint32 *rgb)
+static int color_to_rgb(const char *spec, int speclen, Uint32 *rgb)
 {
-        /* poor man's rgb.txt */
-        static struct { const char *name; Uint32 rgb; } known[] = {
-                {"none",  0xffffffff},
-                {"black", 0x00000000},
-                {"white", 0x00ffffff},
-                {"red",   0x00ff0000},
-                {"green", 0x0000ff00},
-                {"blue",  0x000000ff}
-        };
-
-        if (spec[0] == '#') {
-                char buf[7];
-                switch (speclen) {
-                case 4:
-                        buf[0] = buf[1] = spec[1];
-                        buf[2] = buf[3] = spec[2];
-                        buf[4] = buf[5] = spec[3];
-                        break;
-                case 7:
-                        memcpy(buf, spec + 1, 6);
-                        break;
-                case 13:
-                        buf[0] = spec[1];
-                        buf[1] = spec[2];
-                        buf[2] = spec[5];
-                        buf[3] = spec[6];
-                        buf[4] = spec[9];
-                        buf[5] = spec[10];
-                        break;
-                }
-                buf[6] = '\0';
-                *rgb = (Uint32)strtol(buf, NULL, 16);
+    /* poor man's rgb.txt */
+    static struct { const char *name; Uint32 rgb; } known[] = {
+        { "none",                 0xFFFFFFFF },
+        { "black",                0x000000 },
+        { "white",                0xFFFFFF },
+        { "red",                  0xFF0000 },
+        { "green",                0x00FF00 },
+        { "blue",                 0x0000FF },
+/* This table increases the size of the library by 40K, so it's disabled by default */
+#ifdef EXTENDED_XPM_COLORS
+        { "aliceblue",            0xf0f8ff },
+        { "antiquewhite",         0xfaebd7 },
+        { "antiquewhite1",        0xffefdb },
+        { "antiquewhite2",        0xeedfcc },
+        { "antiquewhite3",        0xcdc0b0 },
+        { "antiquewhite4",        0x8b8378 },
+        { "aqua",                 0x00ffff },
+        { "aquamarine",           0x7fffd4 },
+        { "aquamarine1",          0x7fffd4 },
+        { "aquamarine2",          0x76eec6 },
+        { "aquamarine3",          0x66cdaa },
+        { "aquamarine4",          0x458b74 },
+        { "azure",                0xf0ffff },
+        { "azure1",               0xf0ffff },
+        { "azure2",               0xe0eeee },
+        { "azure3",               0xc1cdcd },
+        { "azure4",               0x838b8b },
+        { "beige",                0xf5f5dc },
+        { "bisque",               0xffe4c4 },
+        { "bisque1",              0xffe4c4 },
+        { "bisque2",              0xeed5b7 },
+        { "bisque3",              0xcdb79e },
+        { "bisque4",              0x8b7d6b },
+        { "black",                0x000000 },
+        { "blanchedalmond",       0xffebcd },
+        { "blue",                 0x0000ff },
+        { "blue1",                0x0000ff },
+        { "blue2",                0x0000ee },
+        { "blue3",                0x0000cd },
+        { "blue4",                0x00008B },
+        { "blueviolet",           0x8a2be2 },
+        { "brown",                0xA52A2A },
+        { "brown1",               0xFF4040 },
+        { "brown2",               0xEE3B3B },
+        { "brown3",               0xCD3333 },
+        { "brown4",               0x8B2323 },
+        { "burlywood",            0xDEB887 },
+        { "burlywood1",           0xFFD39B },
+        { "burlywood2",           0xEEC591 },
+        { "burlywood3",           0xCDAA7D },
+        { "burlywood4",           0x8B7355 },
+        { "cadetblue",            0x5F9EA0 },
+        { "cadetblue",            0x5f9ea0 },
+        { "cadetblue1",           0x98f5ff },
+        { "cadetblue2",           0x8ee5ee },
+        { "cadetblue3",           0x7ac5cd },
+        { "cadetblue4",           0x53868b },
+        { "chartreuse",           0x7FFF00 },
+        { "chartreuse1",          0x7FFF00 },
+        { "chartreuse2",          0x76EE00 },
+        { "chartreuse3",          0x66CD00 },
+        { "chartreuse4",          0x458B00 },
+        { "chocolate",            0xD2691E },
+        { "chocolate1",           0xFF7F24 },
+        { "chocolate2",           0xEE7621 },
+        { "chocolate3",           0xCD661D },
+        { "chocolate4",           0x8B4513 },
+        { "coral",                0xFF7F50 },
+        { "coral1",               0xFF7256 },
+        { "coral2",               0xEE6A50 },
+        { "coral3",               0xCD5B45 },
+        { "coral4",               0x8B3E2F },
+        { "cornflowerblue",       0x6495ed },
+        { "cornsilk",             0xFFF8DC },
+        { "cornsilk1",            0xFFF8DC },
+        { "cornsilk2",            0xEEE8CD },
+        { "cornsilk3",            0xCDC8B1 },
+        { "cornsilk4",            0x8B8878 },
+        { "crimson",              0xDC143C },
+        { "cyan",                 0x00FFFF },
+        { "cyan1",                0x00FFFF },
+        { "cyan2",                0x00EEEE },
+        { "cyan3",                0x00CDCD },
+        { "cyan4",                0x008B8B },
+        { "darkblue",             0x00008b },
+        { "darkcyan",             0x008b8b },
+        { "darkgoldenrod",        0xb8860b },
+        { "darkgoldenrod1",       0xffb90f },
+        { "darkgoldenrod2",       0xeead0e },
+        { "darkgoldenrod3",       0xcd950c },
+        { "darkgoldenrod4",       0x8b6508 },
+        { "darkgray",             0xa9a9a9 },
+        { "darkgreen",            0x006400 },
+        { "darkgrey",             0xa9a9a9 },
+        { "darkkhaki",            0xbdb76b },
+        { "darkmagenta",          0x8b008b },
+        { "darkolivegreen",       0x556b2f },
+        { "darkolivegreen1",      0xcaff70 },
+        { "darkolivegreen2",      0xbcee68 },
+        { "darkolivegreen3",      0xa2cd5a },
+        { "darkolivegreen4",      0x6e8b3d },
+        { "darkorange",           0xff8c00 },
+        { "darkorange1",          0xff7f00 },
+        { "darkorange2",          0xee7600 },
+        { "darkorange3",          0xcd6600 },
+        { "darkorange4",          0x8b4500 },
+        { "darkorchid",           0x9932cc },
+        { "darkorchid1",          0xbf3eff },
+        { "darkorchid2",          0xb23aee },
+        { "darkorchid3",          0x9a32cd },
+        { "darkorchid4",          0x68228b },
+        { "darkred",              0x8b0000 },
+        { "darksalmon",           0xe9967a },
+        { "darkseagreen",         0x8fbc8f },
+        { "darkseagreen1",        0xc1ffc1 },
+        { "darkseagreen2",        0xb4eeb4 },
+        { "darkseagreen3",        0x9bcd9b },
+        { "darkseagreen4",        0x698b69 },
+        { "darkslateblue",        0x483d8b },
+        { "darkslategray",        0x2f4f4f },
+        { "darkslategray1",       0x97ffff },
+        { "darkslategray2",       0x8deeee },
+        { "darkslategray3",       0x79cdcd },
+        { "darkslategray4",       0x528b8b },
+        { "darkslategrey",        0x2f4f4f },
+        { "darkturquoise",        0x00ced1 },
+        { "darkviolet",           0x9400D3 },
+        { "darkviolet",           0x9400d3 },
+        { "deeppink",             0xff1493 },
+        { "deeppink1",            0xff1493 },
+        { "deeppink2",            0xee1289 },
+        { "deeppink3",            0xcd1076 },
+        { "deeppink4",            0x8b0a50 },
+        { "deepskyblue",          0x00bfff },
+        { "deepskyblue1",         0x00bfff },
+        { "deepskyblue2",         0x00b2ee },
+        { "deepskyblue3",         0x009acd },
+        { "deepskyblue4",         0x00688b },
+        { "dimgray",              0x696969 },
+        { "dimgrey",              0x696969 },
+        { "dodgerblue",           0x1e90ff },
+        { "dodgerblue1",          0x1e90ff },
+        { "dodgerblue2",          0x1c86ee },
+        { "dodgerblue3",          0x1874cd },
+        { "dodgerblue4",          0x104e8b },
+        { "firebrick",            0xB22222 },
+        { "firebrick1",           0xFF3030 },
+        { "firebrick2",           0xEE2C2C },
+        { "firebrick3",           0xCD2626 },
+        { "firebrick4",           0x8B1A1A },
+        { "floralwhite",          0xfffaf0 },
+        { "forestgreen",          0x228b22 },
+        { "fractal",              0x808080 },
+        { "fuchsia",              0xFF00FF },
+        { "gainsboro",            0xDCDCDC },
+        { "ghostwhite",           0xf8f8ff },
+        { "gold",                 0xFFD700 },
+        { "gold1",                0xFFD700 },
+        { "gold2",                0xEEC900 },
+        { "gold3",                0xCDAD00 },
+        { "gold4",                0x8B7500 },
+        { "goldenrod",            0xDAA520 },
+        { "goldenrod1",           0xFFC125 },
+        { "goldenrod2",           0xEEB422 },
+        { "goldenrod3",           0xCD9B1D },
+        { "goldenrod4",           0x8B6914 },
+        { "gray",                 0x7E7E7E },
+        { "gray",                 0xBEBEBE },
+        { "gray0",                0x000000 },
+        { "gray1",                0x030303 },
+        { "gray10",               0x1A1A1A },
+        { "gray100",              0xFFFFFF },
+        { "gray11",               0x1C1C1C },
+        { "gray12",               0x1F1F1F },
+        { "gray13",               0x212121 },
+        { "gray14",               0x242424 },
+        { "gray15",               0x262626 },
+        { "gray16",               0x292929 },
+        { "gray17",               0x2B2B2B },
+        { "gray18",               0x2E2E2E },
+        { "gray19",               0x303030 },
+        { "gray2",                0x050505 },
+        { "gray20",               0x333333 },
+        { "gray21",               0x363636 },
+        { "gray22",               0x383838 },
+        { "gray23",               0x3B3B3B },
+        { "gray24",               0x3D3D3D },
+        { "gray25",               0x404040 },
+        { "gray26",               0x424242 },
+        { "gray27",               0x454545 },
+        { "gray28",               0x474747 },
+        { "gray29",               0x4A4A4A },
+        { "gray3",                0x080808 },
+        { "gray30",               0x4D4D4D },
+        { "gray31",               0x4F4F4F },
+        { "gray32",               0x525252 },
+        { "gray33",               0x545454 },
+        { "gray34",               0x575757 },
+        { "gray35",               0x595959 },
+        { "gray36",               0x5C5C5C },
+        { "gray37",               0x5E5E5E },
+        { "gray38",               0x616161 },
+        { "gray39",               0x636363 },
+        { "gray4",                0x0A0A0A },
+        { "gray40",               0x666666 },
+        { "gray41",               0x696969 },
+        { "gray42",               0x6B6B6B },
+        { "gray43",               0x6E6E6E },
+        { "gray44",               0x707070 },
+        { "gray45",               0x737373 },
+        { "gray46",               0x757575 },
+        { "gray47",               0x787878 },
+        { "gray48",               0x7A7A7A },
+        { "gray49",               0x7D7D7D },
+        { "gray5",                0x0D0D0D },
+        { "gray50",               0x7F7F7F },
+        { "gray51",               0x828282 },
+        { "gray52",               0x858585 },
+        { "gray53",               0x878787 },
+        { "gray54",               0x8A8A8A },
+        { "gray55",               0x8C8C8C },
+        { "gray56",               0x8F8F8F },
+        { "gray57",               0x919191 },
+        { "gray58",               0x949494 },
+        { "gray59",               0x969696 },
+        { "gray6",                0x0F0F0F },
+        { "gray60",               0x999999 },
+        { "gray61",               0x9C9C9C },
+        { "gray62",               0x9E9E9E },
+        { "gray63",               0xA1A1A1 },
+        { "gray64",               0xA3A3A3 },
+        { "gray65",               0xA6A6A6 },
+        { "gray66",               0xA8A8A8 },
+        { "gray67",               0xABABAB },
+        { "gray68",               0xADADAD },
+        { "gray69",               0xB0B0B0 },
+        { "gray7",                0x121212 },
+        { "gray70",               0xB3B3B3 },
+        { "gray71",               0xB5B5B5 },
+        { "gray72",               0xB8B8B8 },
+        { "gray73",               0xBABABA },
+        { "gray74",               0xBDBDBD },
+        { "gray75",               0xBFBFBF },
+        { "gray76",               0xC2C2C2 },
+        { "gray77",               0xC4C4C4 },
+        { "gray78",               0xC7C7C7 },
+        { "gray79",               0xC9C9C9 },
+        { "gray8",                0x141414 },
+        { "gray80",               0xCCCCCC },
+        { "gray81",               0xCFCFCF },
+        { "gray82",               0xD1D1D1 },
+        { "gray83",               0xD4D4D4 },
+        { "gray84",               0xD6D6D6 },
+        { "gray85",               0xD9D9D9 },
+        { "gray86",               0xDBDBDB },
+        { "gray87",               0xDEDEDE },
+        { "gray88",               0xE0E0E0 },
+        { "gray89",               0xE3E3E3 },
+        { "gray9",                0x171717 },
+        { "gray90",               0xE5E5E5 },
+        { "gray91",               0xE8E8E8 },
+        { "gray92",               0xEBEBEB },
+        { "gray93",               0xEDEDED },
+        { "gray94",               0xF0F0F0 },
+        { "gray95",               0xF2F2F2 },
+        { "gray96",               0xF5F5F5 },
+        { "gray97",               0xF7F7F7 },
+        { "gray98",               0xFAFAFA },
+        { "gray99",               0xFCFCFC },
+        { "green",                0x008000 },
+        { "green",                0x00FF00 },
+        { "green1",               0x00FF00 },
+        { "green2",               0x00EE00 },
+        { "green3",               0x00CD00 },
+        { "green4",               0x008B00 },
+        { "greenyellow",          0xadff2f },
+        { "grey",                 0xBEBEBE },
+        { "grey0",                0x000000 },
+        { "grey1",                0x030303 },
+        { "grey10",               0x1A1A1A },
+        { "grey100",              0xFFFFFF },
+        { "grey11",               0x1C1C1C },
+        { "grey12",               0x1F1F1F },
+        { "grey13",               0x212121 },
+        { "grey14",               0x242424 },
+        { "grey15",               0x262626 },
+        { "grey16",               0x292929 },
+        { "grey17",               0x2B2B2B },
+        { "grey18",               0x2E2E2E },
+        { "grey19",               0x303030 },
+        { "grey2",                0x050505 },
+        { "grey20",               0x333333 },
+        { "grey21",               0x363636 },
+        { "grey22",               0x383838 },
+        { "grey23",               0x3B3B3B },
+        { "grey24",               0x3D3D3D },
+        { "grey25",               0x404040 },
+        { "grey26",               0x424242 },
+        { "grey27",               0x454545 },
+        { "grey28",               0x474747 },
+        { "grey29",               0x4A4A4A },
+        { "grey3",                0x080808 },
+        { "grey30",               0x4D4D4D },
+        { "grey31",               0x4F4F4F },
+        { "grey32",               0x525252 },
+        { "grey33",               0x545454 },
+        { "grey34",               0x575757 },
+        { "grey35",               0x595959 },
+        { "grey36",               0x5C5C5C },
+        { "grey37",               0x5E5E5E },
+        { "grey38",               0x616161 },
+        { "grey39",               0x636363 },
+        { "grey4",                0x0A0A0A },
+        { "grey40",               0x666666 },
+        { "grey41",               0x696969 },
+        { "grey42",               0x6B6B6B },
+        { "grey43",               0x6E6E6E },
+        { "grey44",               0x707070 },
+        { "grey45",               0x737373 },
+        { "grey46",               0x757575 },
+        { "grey47",               0x787878 },
+        { "grey48",               0x7A7A7A },
+        { "grey49",               0x7D7D7D },
+        { "grey5",                0x0D0D0D },
+        { "grey50",               0x7F7F7F },
+        { "grey51",               0x828282 },
+        { "grey52",               0x858585 },
+        { "grey53",               0x878787 },
+        { "grey54",               0x8A8A8A },
+        { "grey55",               0x8C8C8C },
+        { "grey56",               0x8F8F8F },
+        { "grey57",               0x919191 },
+        { "grey58",               0x949494 },
+        { "grey59",               0x969696 },
+        { "grey6",                0x0F0F0F },
+        { "grey60",               0x999999 },
+        { "grey61",               0x9C9C9C },
+        { "grey62",               0x9E9E9E },
+        { "grey63",               0xA1A1A1 },
+        { "grey64",               0xA3A3A3 },
+        { "grey65",               0xA6A6A6 },
+        { "grey66",               0xA8A8A8 },
+        { "grey67",               0xABABAB },
+        { "grey68",               0xADADAD },
+        { "grey69",               0xB0B0B0 },
+        { "grey7",                0x121212 },
+        { "grey70",               0xB3B3B3 },
+        { "grey71",               0xB5B5B5 },
+        { "grey72",               0xB8B8B8 },
+        { "grey73",               0xBABABA },
+        { "grey74",               0xBDBDBD },
+        { "grey75",               0xBFBFBF },
+        { "grey76",               0xC2C2C2 },
+        { "grey77",               0xC4C4C4 },
+        { "grey78",               0xC7C7C7 },
+        { "grey79",               0xC9C9C9 },
+        { "grey8",                0x141414 },
+        { "grey80",               0xCCCCCC },
+        { "grey81",               0xCFCFCF },
+        { "grey82",               0xD1D1D1 },
+        { "grey83",               0xD4D4D4 },
+        { "grey84",               0xD6D6D6 },
+        { "grey85",               0xD9D9D9 },
+        { "grey86",               0xDBDBDB },
+        { "grey87",               0xDEDEDE },
+        { "grey88",               0xE0E0E0 },
+        { "grey89",               0xE3E3E3 },
+        { "grey9",                0x171717 },
+        { "grey90",               0xE5E5E5 },
+        { "grey91",               0xE8E8E8 },
+        { "grey92",               0xEBEBEB },
+        { "grey93",               0xEDEDED },
+        { "grey94",               0xF0F0F0 },
+        { "grey95",               0xF2F2F2 },
+        { "grey96",               0xF5F5F5 },
+        { "grey97",               0xF7F7F7 },
+        { "grey98",               0xFAFAFA },
+        { "grey99",               0xFCFCFC },
+        { "honeydew",             0xF0FFF0 },
+        { "honeydew1",            0xF0FFF0 },
+        { "honeydew2",            0xE0EEE0 },
+        { "honeydew3",            0xC1CDC1 },
+        { "honeydew4",            0x838B83 },
+        { "hotpink",              0xff69b4 },
+        { "hotpink1",             0xff6eb4 },
+        { "hotpink2",             0xee6aa7 },
+        { "hotpink3",             0xcd6090 },
+        { "hotpink4",             0x8b3a62 },
+        { "indianred",            0xcd5c5c },
+        { "indianred1",           0xff6a6a },
+        { "indianred2",           0xee6363 },
+        { "indianred3",           0xcd5555 },
+        { "indianred4",           0x8b3a3a },
+        { "indigo",               0x4B0082 },
+        { "ivory",                0xFFFFF0 },
+        { "ivory1",               0xFFFFF0 },
+        { "ivory2",               0xEEEEE0 },
+        { "ivory3",               0xCDCDC1 },
+        { "ivory4",               0x8B8B83 },
+        { "khaki",                0xF0E68C },
+        { "khaki1",               0xFFF68F },
+        { "khaki2",               0xEEE685 },
+        { "khaki3",               0xCDC673 },
+        { "khaki4",               0x8B864E },
+        { "lavender",             0xE6E6FA },
+        { "lavenderblush",        0xfff0f5 },
+        { "lavenderblush1",       0xfff0f5 },
+        { "lavenderblush2",       0xeee0e5 },
+        { "lavenderblush3",       0xcdc1c5 },
+        { "lavenderblush4",       0x8b8386 },
+        { "lawngreen",            0x7cfc00 },
+        { "lemonchiffon",         0xfffacd },
+        { "lemonchiffon1",        0xfffacd },
+        { "lemonchiffon2",        0xeee9bf },
+        { "lemonchiffon3",        0xcdc9a5 },
+        { "lemonchiffon4",        0x8b8970 },
+        { "lightblue",            0xadd8e6 },
+        { "lightblue1",           0xbfefff },
+        { "lightblue2",           0xb2dfee },
+        { "lightblue3",           0x9ac0cd },
+        { "lightblue4",           0x68838b },
+        { "lightcoral",           0xf08080 },
+        { "lightcyan",            0xe0ffff },
+        { "lightcyan1",           0xe0ffff },
+        { "lightcyan2",           0xd1eeee },
+        { "lightcyan3",           0xb4cdcd },
+        { "lightcyan4",           0x7a8b8b },
+        { "lightgoldenrod",       0xeedd82 },
+        { "lightgoldenrod1",      0xffec8b },
+        { "lightgoldenrod2",      0xeedc82 },
+        { "lightgoldenrod3",      0xcdbe70 },
+        { "lightgoldenrod4",      0x8b814c },
+        { "lightgoldenrodyellow", 0xfafad2 },
+        { "lightgray",            0xd3d3d3 },
+        { "lightgreen",           0x90ee90 },
+        { "lightgrey",            0xd3d3d3 },
+        { "lightpink",            0xffb6c1 },
+        { "lightpink1",           0xffaeb9 },
+        { "lightpink2",           0xeea2ad },
+        { "lightpink3",           0xcd8c95 },
+        { "lightpink4",           0x8b5f65 },
+        { "lightsalmon",          0xffa07a },
+        { "lightsalmon1",         0xffa07a },
+        { "lightsalmon2",         0xee9572 },
+        { "lightsalmon3",         0xcd8162 },
+        { "lightsalmon4",         0x8b5742 },
+        { "lightseagreen",        0x20b2aa },
+        { "lightskyblue",         0x87cefa },
+        { "lightskyblue1",        0xb0e2ff },
+        { "lightskyblue2",        0xa4d3ee },
+        { "lightskyblue3",        0x8db6cd },
+        { "lightskyblue4",        0x607b8b },
+        { "lightslateblue",       0x8470ff },
+        { "lightslategray",       0x778899 },
+        { "lightslategrey",       0x778899 },
+        { "lightsteelblue",       0xb0c4de },
+        { "lightsteelblue1",      0xcae1ff },
+        { "lightsteelblue2",      0xbcd2ee },
+        { "lightsteelblue3",      0xa2b5cd },
+        { "lightsteelblue4",      0x6e7b8b },
+        { "lightyellow",          0xffffe0 },
+        { "lightyellow1",         0xffffe0 },
+        { "lightyellow2",         0xeeeed1 },
+        { "lightyellow3",         0xcdcdb4 },
+        { "lightyellow4",         0x8b8b7a },
+        { "lime",                 0x00FF00 },
+        { "limegreen",            0x32cd32 },
+        { "linen",                0xFAF0E6 },
+        { "magenta",              0xFF00FF },
+        { "magenta1",             0xFF00FF },
+        { "magenta2",             0xEE00EE },
+        { "magenta3",             0xCD00CD },
+        { "magenta4",             0x8B008B },
+        { "maroon",               0x800000 },
+        { "maroon",               0xB03060 },
+        { "maroon1",              0xFF34B3 },
+        { "maroon2",              0xEE30A7 },
+        { "maroon3",              0xCD2990 },
+        { "maroon4",              0x8B1C62 },
+        { "mediumaquamarine",     0x66cdaa },
+        { "mediumblue",           0x0000cd },
+        { "mediumforestgreen",    0x32814b },
+        { "mediumgoldenrod",      0xd1c166 },
+        { "mediumorchid",         0xba55d3 },
+        { "mediumorchid1",        0xe066ff },
+        { "mediumorchid2",        0xd15fee },
+        { "mediumorchid3",        0xb452cd },
+        { "mediumorchid4",        0x7a378b },
+        { "mediumpurple",         0x9370db },
+        { "mediumpurple1",        0xab82ff },
+        { "mediumpurple2",        0x9f79ee },
+        { "mediumpurple3",        0x8968cd },
+        { "mediumpurple4",        0x5d478b },
+        { "mediumseagreen",       0x3cb371 },
+        { "mediumslateblue",      0x7b68ee },
+        { "mediumspringgreen",    0x00fa9a },
+        { "mediumturquoise",      0x48d1cc },
+        { "mediumvioletred",      0xc71585 },
+        { "midnightblue",         0x191970 },
+        { "mintcream",            0xf5fffa },
+        { "mistyrose",            0xffe4e1 },
+        { "mistyrose1",           0xffe4e1 },
+        { "mistyrose2",           0xeed5d2 },
+        { "mistyrose3",           0xcdb7b5 },
+        { "mistyrose4",           0x8b7d7b },
+        { "moccasin",             0xFFE4B5 },
+        { "navajowhite",          0xffdead },
+        { "navajowhite1",         0xffdead },
+        { "navajowhite2",         0xeecfa1 },
+        { "navajowhite3",         0xcdb38b },
+        { "navajowhite4",         0x8b795e },
+        { "navy",                 0x000080 },
+        { "navyblue",             0x000080 },
+        { "none",                 0x0000FF },
+        { "oldlace",              0xfdf5e6 },
+        { "olive",                0x808000 },
+        { "olivedrab",            0x6b8e23 },
+        { "olivedrab1",           0xc0ff3e },
+        { "olivedrab2",           0xb3ee3a },
+        { "olivedrab3",           0x9acd32 },
+        { "olivedrab4",           0x698b22 },
+        { "opaque",               0x000000 },
+        { "orange",               0xFFA500 },
+        { "orange1",              0xFFA500 },
+        { "orange2",              0xEE9A00 },
+        { "orange3",              0xCD8500 },
+        { "orange4",              0x8B5A00 },
+        { "orangered",            0xff4500 },
+        { "orangered1",           0xff4500 },
+        { "orangered2",           0xee4000 },
+        { "orangered3",           0xcd3700 },
+        { "orangered4",           0x8b2500 },
+        { "orchid",               0xDA70D6 },
+        { "orchid1",              0xFF83FA },
+        { "orchid2",              0xEE7AE9 },
+        { "orchid3",              0xCD69C9 },
+        { "orchid4",              0x8B4789 },
+        { "palegoldenrod",        0xeee8aa },
+        { "palegreen",            0x98fb98 },
+        { "palegreen1",           0x9aff9a },
+        { "palegreen2",           0x90ee90 },
+        { "palegreen3",           0x7ccd7c },
+        { "palegreen4",           0x548b54 },
+        { "paleturquoise",        0xafeeee },
+        { "paleturquoise1",       0xbbffff },
+        { "paleturquoise2",       0xaeeeee },
+        { "paleturquoise3",       0x96cdcd },
+        { "paleturquoise4",       0x668b8b },
+        { "palevioletred",        0xdb7093 },
+        { "palevioletred1",       0xff82ab },
+        { "palevioletred2",       0xee799f },
+        { "palevioletred3",       0xcd6889 },
+        { "palevioletred4",       0x8b475d },
+        { "papayawhip",           0xffefd5 },
+        { "peachpuff",            0xffdab9 },
+        { "peachpuff1",           0xffdab9 },
+        { "peachpuff2",           0xeecbad },
+        { "peachpuff3",           0xcdaf95 },
+        { "peachpuff4",           0x8b7765 },
+        { "peru",                 0xCD853F },
+        { "pink",                 0xFFC0CB },
+        { "pink1",                0xFFB5C5 },
+        { "pink2",                0xEEA9B8 },
+        { "pink3",                0xCD919E },
+        { "pink4",                0x8B636C },
+        { "plum",                 0xDDA0DD },
+        { "plum1",                0xFFBBFF },
+        { "plum2",                0xEEAEEE },
+        { "plum3",                0xCD96CD },
+        { "plum4",                0x8B668B },
+        { "powderblue",           0xb0e0e6 },
+        { "purple",               0x800080 },
+        { "purple",               0xA020F0 },
+        { "purple1",              0x9B30FF },
+        { "purple2",              0x912CEE },
+        { "purple3",              0x7D26CD },
+        { "purple4",              0x551A8B },
+        { "red",                  0xFF0000 },
+        { "red1",                 0xFF0000 },
+        { "red2",                 0xEE0000 },
+        { "red3",                 0xCD0000 },
+        { "red4",                 0x8B0000 },
+        { "rosybrown",            0xbc8f8f },
+        { "rosybrown1",           0xffc1c1 },
+        { "rosybrown2",           0xeeb4b4 },
+        { "rosybrown3",           0xcd9b9b },
+        { "rosybrown4",           0x8b6969 },
+        { "royalblue",            0x4169e1 },
+        { "royalblue1",           0x4876ff },
+        { "royalblue2",           0x436eee },
+        { "royalblue3",           0x3a5fcd },
+        { "royalblue4",           0x27408b },
+        { "saddlebrown",          0x8b4513 },
+        { "salmon",               0xFA8072 },
+        { "salmon1",              0xFF8C69 },
+        { "salmon2",              0xEE8262 },
+        { "salmon3",              0xCD7054 },
+        { "salmon4",              0x8B4C39 },
+        { "sandybrown",           0xf4a460 },
+        { "seagreen",             0x2e8b57 },
+        { "seagreen1",            0x54ff9f },
+        { "seagreen2",            0x4eee94 },
+        { "seagreen3",            0x43cd80 },
+        { "seagreen4",            0x2e8b57 },
+        { "seashell",             0xFFF5EE },
+        { "seashell1",            0xFFF5EE },
+        { "seashell2",            0xEEE5DE },
+        { "seashell3",            0xCDC5BF },
+        { "seashell4",            0x8B8682 },
+        { "sienna",               0xA0522D },
+        { "sienna1",              0xFF8247 },
+        { "sienna2",              0xEE7942 },
+        { "sienna3",              0xCD6839 },
+        { "sienna4",              0x8B4726 },
+        { "silver",               0xC0C0C0 },
+        { "skyblue",              0x87ceeb },
+        { "skyblue1",             0x87ceff },
+        { "skyblue2",             0x7ec0ee },
+        { "skyblue3",             0x6ca6cd },
+        { "skyblue4",             0x4a708b },
+        { "slateblue",            0x6a5acd },
+        { "slateblue1",           0x836fff },
+        { "slateblue2",           0x7a67ee },
+        { "slateblue3",           0x6959cd },
+        { "slateblue4",           0x473c8b },
+        { "slategray",            0x708090 },
+        { "slategray1",           0xc6e2ff },
+        { "slategray2",           0xb9d3ee },
+        { "slategray3",           0x9fb6cd },
+        { "slategray4",           0x6c7b8b },
+        { "slategrey",            0x708090 },
+        { "snow",                 0xFFFAFA },
+        { "snow1",                0xFFFAFA },
+        { "snow2",                0xEEE9E9 },
+        { "snow3",                0xCDC9C9 },
+        { "snow4",                0x8B8989 },
+        { "springgreen",          0x00ff7f },
+        { "springgreen1",         0x00ff7f },
+        { "springgreen2",         0x00ee76 },
+        { "springgreen3",         0x00cd66 },
+        { "springgreen4",         0x008b45 },
+        { "steelblue",            0x4682b4 },
+        { "steelblue1",           0x63b8ff },
+        { "steelblue2",           0x5cacee },
+        { "steelblue3",           0x4f94cd },
+        { "steelblue4",           0x36648b },
+        { "tan",                  0xD2B48C },
+        { "tan1",                 0xFFA54F },
+        { "tan2",                 0xEE9A49 },
+        { "tan3",                 0xCD853F },
+        { "tan4",                 0x8B5A2B },
+        { "teal",                 0x008080 },
+        { "thistle",              0xD8BFD8 },
+        { "thistle1",             0xFFE1FF },
+        { "thistle2",             0xEED2EE },
+        { "thistle3",             0xCDB5CD },
+        { "thistle4",             0x8B7B8B },
+        { "tomato",               0xFF6347 },
+        { "tomato1",              0xFF6347 },
+        { "tomato2",              0xEE5C42 },
+        { "tomato3",              0xCD4F39 },
+        { "tomato4",              0x8B3626 },
+        { "transparent",          0x0000FF },
+        { "turquoise",            0x40E0D0 },
+        { "turquoise1",           0x00F5FF },
+        { "turquoise2",           0x00E5EE },
+        { "turquoise3",           0x00C5CD },
+        { "turquoise4",           0x00868B },
+        { "violet",               0xEE82EE },
+        { "violetred",            0xd02090 },
+        { "violetred1",           0xff3e96 },
+        { "violetred2",           0xee3a8c },
+        { "violetred3",           0xcd3278 },
+        { "violetred4",           0x8b2252 },
+        { "wheat",                0xF5DEB3 },
+        { "wheat1",               0xFFE7BA },
+        { "wheat2",               0xEED8AE },
+        { "wheat3",               0xCDBA96 },
+        { "wheat4",               0x8B7E66 },
+        { "white",                0xFFFFFF },
+        { "whitesmoke",           0xf5f5f5 },
+        { "yellow",               0xFFFF00 },
+        { "yellow1",              0xFFFF00 },
+        { "yellow2",              0xEEEE00 },
+        { "yellow3",              0xCDCD00 },
+        { "yellow4",              0x8B8B00 },
+        { "yellowgreen",          0x9acd32 },
+#endif /* EXTENDED_XPM_COLORS */
+        {"none",                  0xFFFFFF}
+    };
+
+    if (spec[0] == '#') {
+        char buf[7];
+        switch(speclen) {
+        case 4:
+            buf[0] = buf[1] = spec[1];
+            buf[2] = buf[3] = spec[2];
+            buf[4] = buf[5] = spec[3];
+            break;
+        case 7:
+            SDL_memcpy(buf, spec + 1, 6);
+            break;
+        case 13:
+            buf[0] = spec[1];
+            buf[1] = spec[2];
+            buf[2] = spec[5];
+            buf[3] = spec[6];
+            buf[4] = spec[9];
+            buf[5] = spec[10];
+            break;
+        }
+        buf[6] = '\0';
+        *rgb = (Uint32)SDL_strtol(buf, NULL, 16);
+        return 1;
+    } else {
+        size_t i;
+        for (i = 0; i < SDL_arraysize(known); i++) {
+            if (SDL_strncasecmp(known[i].name, spec, speclen) == 0) {
+                *rgb = known[i].rgb;
                 return 1;
-        } else {
-                int i;
-                for (i = 0; i < ARRAYSIZE(known); i++)
-                        if (string_equal(known[i].name, spec, speclen)) {
-                                *rgb = known[i].rgb;
-                                return 1;
-                        }
-                return 0;
+            }
         }
+        return 0;
+    }
 }
 
 #ifndef MAX
@@ -243,263 +920,278 @@ static const char *error;
  */
 static const char *get_next_line(const char ***lines, SDL_RWops *src, int len)
 {
-        char *linebufnew;
-        if (lines) {
-                return *(*lines)++;
+    char *linebufnew;
+
+    if (lines) {
+        return *(*lines)++;
+    } else {
+        char c;
+        int n;
+        do {
+            if (SDL_RWread(src, &c, 1, 1) <= 0) {
+                error = "Premature end of data";
+                return NULL;
+            }
+        } while (c != '"');
+        if (len) {
+            len += 4;   /* "\",\n\0" */
+            if (len > buflen){
+                buflen = len;
+                linebufnew = (char *)SDL_realloc(linebuf, buflen);
+                if (!linebufnew) {
+                    SDL_free(linebuf);
+                    error = "Out of memory";
+                    return NULL;
+                }
+                linebuf = linebufnew;
+            }
+            if (SDL_RWread(src, linebuf, len - 1, 1) <= 0) {
+                error = "Premature end of data";
+                return NULL;
+            }
+            n = len - 2;
         } else {
-                char c;
-                int n;
-                do {
-                        if (SDL_RWread(src, &c, 1, 1) <= 0) {
-                                error = "Premature end of data";
-                                return NULL;
-                        }
-                } while (c != '"');
-                if (len) {
-                        len += 4;        /* "\",\n\0" */
-                        if (len > buflen){
-                                buflen = len;
-                                linebufnew = realloc(linebuf, buflen);
-                                if(!linebufnew) {
-                                        free(linebuf);
-                                        error = "Out of memory";
-                                        return NULL;
-                                }
-                                linebuf = linebufnew;
-                        }
-                        if (SDL_RWread(src, linebuf, len - 1, 1) <= 0) {
-                                error = "Premature end of data";
-                                return NULL;
-                        }
-                        n = len - 2;
-                } else {
-                        n = 0;
-                        do {
-                                if (n >= buflen - 1) {
-                                        if (buflen == 0)
-                                                buflen = 16;
-                                        buflen *= 2;
-                                        linebufnew = realloc(linebuf, buflen);
-                                        if(!linebufnew) {
-                                                free(linebuf);
-                                                error = "Out of memory";
-                                                return NULL;
-                                        }
-                                        linebuf = linebufnew;
-                                }
-                                if (SDL_RWread(src, linebuf + n, 1, 1) <= 0) {
-                                        error = "Premature end of data";
-                                        return NULL;
-                                }
-                        } while (linebuf[n++] != '"');
-                        n--;
+            n = 0;
+            do {
+                if (n >= buflen - 1) {
+                    if (buflen == 0)
+                        buflen = 16;
+                    buflen *= 2;
+                    linebufnew = (char *)SDL_realloc(linebuf, buflen);
+                    if (!linebufnew) {
+                        SDL_free(linebuf);
+                        error = "Out of memory";
+                        return NULL;
+                    }
+                    linebuf = linebufnew;
                 }
-                linebuf[n] = '\0';
-                return linebuf;
+                if (SDL_RWread(src, linebuf + n, 1, 1) <= 0) {
+                    error = "Premature end of data";
+                    return NULL;
+                }
+            } while (linebuf[n++] != '"');
+            n--;
         }
+        linebuf[n] = '\0';
+        return linebuf;
+    }
 }
 
-#define SKIPSPACE(p)                            \
-do {                                            \
-        while (isspace((unsigned char)*(p)))        \
-              ++(p);                                \
+#define SKIPSPACE(p)                \
+do {                        \
+    while (SDL_isspace((unsigned char)*(p))) \
+          ++(p);                \
 } while (0)
 
-#define SKIPNONSPACE(p)                                 \
-do {                                                    \
-        while (!isspace((unsigned char)*(p)) && *p)        \
-              ++(p);                                        \
+#define SKIPNONSPACE(p)                 \
+do {                            \
+    while (!SDL_isspace((unsigned char)*(p)) && *p)  \
+          ++(p);                    \
 } while (0)
 
 /* read XPM from either array or RWops */
 static SDL_Surface *load_xpm(const char **xpm, SDL_RWops *src)
 {
-        SDL_Surface *image = NULL;
-        int indexc;
-        int x, y;
-        int w, h, ncolors, cpp;
-        int indexed;
-        Uint8 *dst;
-        struct color_hash *colors = NULL;
-        SDL_Color *im_colors = NULL;
-        char *keystrings = NULL, *nextkey;
-        const char *line;
-        const char ***xpmlines = NULL;
-        int pixels_len;
-
-        error = NULL;
-        linebuf = NULL;
-        buflen = 0;
-
-        if (xpm)
-                xpmlines = &xpm;
+    Sint64 start = 0;
+    SDL_Surface *image = NULL;
+    int index;
+    int x, y;
+    int w, h, ncolors, cpp;
+    int indexed;
+    Uint8 *dst;
+    struct color_hash *colors = NULL;
+    SDL_Color *im_colors = NULL;
+    char *keystrings = NULL, *nextkey;
+    const char *line;
+    const char ***xpmlines = NULL;
+    int pixels_len;
+
+    error = NULL;
+    linebuf = NULL;
+    buflen = 0;
+
+    if (src)
+        start = SDL_RWtell(src);
+
+    if (xpm)
+        xpmlines = &xpm;
 
+    line = get_next_line(xpmlines, src, 0);
+    if (!line)
+        goto done;
+    /*
+     * The header string of an XPMv3 image has the format
+     *
+     * <width> <height> <ncolors> <cpp> [ <hotspot_x> <hotspot_y> ]
+     *
+     * where the hotspot coords are intended for mouse cursors.
+     * Right now we don't use the hotspots but it should be handled
+     * one day.
+     */
+    if (SDL_sscanf(line, "%d %d %d %d", &w, &h, &ncolors, &cpp) != 4
+       || w <= 0 || h <= 0 || ncolors <= 0 || cpp <= 0) {
+        error = "Invalid format description";
+        goto done;
+    }
+
+    keystrings = (char *)SDL_malloc(ncolors * cpp);
+    if (!keystrings) {
+        error = "Out of memory";
+        goto done;
+    }
+    nextkey = keystrings;
+
+    /* Create the new surface */
+    if (ncolors <= 256) {
+        indexed = 1;
+        image = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 8,
+                         0, 0, 0, 0);
+        im_colors = image->format->palette->colors;
+        image->format->palette->ncolors = ncolors;
+    } else {
+        indexed = 0;
+        image = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32,
+                         0xff0000, 0x00ff00, 0x0000ff, 0);
+    }
+    if (!image) {
+        /* Hmm, some SDL error (out of memory?) */
+        goto done;
+    }
+
+    /* Read the colors */
+    colors = create_colorhash(ncolors);
+    if (!colors) {
+        error = "Out of memory";
+        goto done;
+    }
+    for (index = 0; index < ncolors; ++index ) {
+        const char *p;
         line = get_next_line(xpmlines, src, 0);
         if (!line)
-                goto done;
-        /*
-         * The header string of an XPMv3 image has the format
-         *
-         * <width> <height> <ncolors> <cpp> [ <hotspot_x> <hotspot_y> ]
-         *
-         * where the hotspot coords are intended for mouse cursors.
-         * Right now we don't use the hotspots but it should be handled
-         * one day.
-         */
-        if (sscanf(line, "%d %d %d %d", &w, &h, &ncolors, &cpp) != 4
-           || w <= 0 || h <= 0 || ncolors <= 0 || cpp <= 0) {
-                error = "Invalid format description";
-                goto done;
-        }
+            goto done;
 
-        keystrings = malloc(ncolors * cpp);
-        if (!keystrings) {
-                error = "Out of memory";
-                goto done;
-        }
-        nextkey = keystrings;
-
-        /* Create the new surface */
-        if (ncolors <= 256) {
-                indexed = 1;
-                image = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 8,
-                                             0, 0, 0, 0);
-                im_colors = image->format->palette->colors;
-                image->format->palette->ncolors = ncolors;
-        } else {
-                indexed = 0;
-                image = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32,
-                                             0xff0000, 0x00ff00, 0x0000ff, 0);
-        }
-        if (!image) {
-                /* Hmm, some SDL error (out of memory?) */
-                goto done;
-        }
+        p = line + cpp + 1;
 
-        /* Read the colors */
-        colors = create_colorhash(ncolors);
-        if (!colors) {
-                error = "Out of memory";
+        /* parse a colour definition */
+        for (;;) {
+            char nametype;
+            const char *colname;
+            Uint32 rgb, pixel;
+
+            SKIPSPACE(p);
+            if (!*p) {
+                error = "colour parse error";
                 goto done;
+            }
+            nametype = *p;
+            SKIPNONSPACE(p);
+            SKIPSPACE(p);
+            colname = p;
+            SKIPNONSPACE(p);
+            if (nametype == 's')
+                continue;      /* skip symbolic colour names */
+
+            if (!color_to_rgb(colname, (int)(p - colname), &rgb))
+                continue;
+
+            SDL_memcpy(nextkey, line, cpp);
+            if (indexed) {
+                SDL_Color *c = im_colors + index;
+                c->r = (Uint8)(rgb >> 16);
+                c->g = (Uint8)(rgb >> 8);
+                c->b = (Uint8)(rgb);
+                pixel = index;
+            } else
+                pixel = rgb;
+            add_colorhash(colors, nextkey, cpp, pixel);
+            nextkey += cpp;
+            if (rgb == 0xffffffff)
+                SDL_SetColorKey(image, SDL_TRUE, pixel);
+            break;
         }
-        for (indexc = 0; indexc < ncolors; ++indexc ) {
-                const char *p;
-                line = get_next_line(xpmlines, src, 0);
-                if (!line)
-                        goto done;
-
-                p = line + cpp + 1;
-
-                /* parse a colour definition */
-                for (;;) {
-                        char nametype;
-                        const char *colname;
-                        Uint32 rgb, pixel;
-
-                        SKIPSPACE(p);
-                        if (!*p) {
-                                error = "colour parse error";
-                                goto done;
-                        }
-                        nametype = *p;
-                        SKIPNONSPACE(p);
-                        SKIPSPACE(p);
-                        colname = p;
-                        SKIPNONSPACE(p);
-                        if (nametype == 's')
-                                continue;      /* skip symbolic colour names */
-
-                        if (!color_to_rgb(colname, p - colname, &rgb))
-                                continue;
-
-                        memcpy(nextkey, line, cpp);
-                        if (indexed) {
-                                SDL_Color *c = im_colors + indexc;
-                                c->r = (Uint8)(rgb >> 16);
-                                c->g = (Uint8)(rgb >> 8);
-                                c->b = (Uint8)(rgb);
-                                pixel = indexc;
-                        } else
-                                pixel = rgb;
-                        add_colorhash(colors, nextkey, cpp, pixel);
-                        nextkey += cpp;
-                        if (rgb == 0xffffffff)
-                                SDL_SetColorKey(image, SDL_SRCCOLORKEY, pixel);
-                        break;
-                }
-        }
+    }
 
-        /* Read the pixels */
-        pixels_len = w * cpp;
-        dst = image->pixels;
-        for (y = 0; y < h; y++) {
-                line = get_next_line(xpmlines, src, pixels_len);
-                if (indexed) {
-                        /* optimization for some common cases */
-                        if (cpp == 1)
-                                for (x = 0; x < w; x++)
-                                        dst[x] = (Uint8)QUICK_COLORHASH(colors,
-                                                                 line + x);
-                        else
-                                for (x = 0; x < w; x++)
-                                        dst[x] = (Uint8)get_colorhash(colors,
-                                                               line + x * cpp,
-                                                               cpp);
-                } else {
-                        for (x = 0; x < w; x++)
-                                ((Uint32*)dst)[x] = get_colorhash(colors,
-                                                                line + x * cpp,
-                                                                  cpp);
-                }
-                dst += image->pitch;
+    /* Read the pixels */
+    pixels_len = w * cpp;
+    dst = (Uint8 *)image->pixels;
+    for (y = 0; y < h; y++) {
+        line = get_next_line(xpmlines, src, pixels_len);
+        if (!line)
+            goto done;
+
+        if (indexed) {
+            /* optimization for some common cases */
+            if (cpp == 1)
+                for (x = 0; x < w; x++)
+                    dst[x] = (Uint8)QUICK_COLORHASH(colors,
+                                 line + x);
+            else
+                for (x = 0; x < w; x++)
+                    dst[x] = (Uint8)get_colorhash(colors,
+                                   line + x * cpp,
+                                   cpp);
+        } else {
+            for (x = 0; x < w; x++)
+                ((Uint32*)dst)[x] = get_colorhash(colors,
+                                line + x * cpp,
+                                  cpp);
         }
+        dst += image->pitch;
+    }
 
 done:
-        if (error) {
-                SDL_FreeSurface(image);
-                image = NULL;
-                SDL_SetError(error);
+    if (error) {
+        if ( src )
+            SDL_RWseek(src, start, RW_SEEK_SET);
+        if ( image ) {
+            SDL_FreeSurface(image);
+            image = NULL;
         }
-        free(keystrings);
-        free_colorhash(colors);
-        free(linebuf);
-        return(image);
+        IMG_SetError("%s", error);
+    }
+    if (keystrings)
+        SDL_free(keystrings);
+    free_colorhash(colors);
+    if (linebuf)
+        SDL_free(linebuf);
+    return(image);
 }
 
 /* Load a XPM type image from an RWops datasource */
-#if 0
 SDL_Surface *IMG_LoadXPM_RW(SDL_RWops *src)
 {
-        if ( !src ) {
-                /* The error message has been set in SDL_RWFromFile */
-                return NULL;
-        }
-        return load_xpm(NULL, src);
+    if ( !src ) {
+        /* The error message has been set in SDL_RWFromFile */
+        return NULL;
+    }
+    return load_xpm(NULL, src);
 }
-#endif
 
-static inline SDL_Surface *IMG_ReadXPMFromArray(const char **xpm)
+SDL_Surface *IMG_ReadXPMFromArray(const char **xpm)
 {
-        return load_xpm(xpm, NULL);
+    if (!xpm) {
+        IMG_SetError("array is NULL");
+        return NULL;
+    }
+    return load_xpm(xpm, NULL);
 }
 
 #else  /* not LOAD_XPM */
 
 /* See if an image is contained in a data source */
-#if 0
 int IMG_isXPM(SDL_RWops *src)
 {
-        return(0);
+    return(0);
 }
 
+
 /* Load a XPM type image from an SDL datasource */
 SDL_Surface *IMG_LoadXPM_RW(SDL_RWops *src)
 {
-        return(NULL);
+    return(NULL);
 }
-#endif
 
-static inline SDL_Surface *IMG_ReadXPMFromArray(const char **xpm)
+SDL_Surface *IMG_ReadXPMFromArray(char **xpm)
 {
     return NULL;
 }
diff --git a/src/sdl/SDL_icon.xpm b/src/sdl/SDL_icon.xpm
index cf72960dfc9048d1ab23b3c59ed4d29d65f4c404..30259d55efd623ca9087209df7ae03fbca10e5dd 100644
--- a/src/sdl/SDL_icon.xpm
+++ b/src/sdl/SDL_icon.xpm
@@ -1,425 +1,213 @@
 /* XPM */
-static char * C:\Repo\srb2\src\sdl\SDL_icon_xpm[] = {
-"32 32 390 2",
-"  	c None",
-". 	c #4F4F70",
-"+ 	c #4D4D87",
-"@ 	c #4D4D84",
-"# 	c #4E4E6C",
-"$ 	c #6C6C95",
-"% 	c #5E5EB2",
-"& 	c #6B6BE7",
-"* 	c #7373F9",
-"= 	c #7C7CFF",
-"- 	c #6F70E7",
-"; 	c #494BB2",
-"> 	c #4F4FA3",
-", 	c #6464D4",
-"' 	c #7979F5",
-") 	c #5F5FCA",
-"! 	c #5D5D93",
-"~ 	c #3A3A9F",
-"{ 	c #6060AC",
-"] 	c #777793",
-"^ 	c #5C5CB3",
-"/ 	c #7373EA",
-"( 	c #7A7AFF",
-"_ 	c #7575FF",
-": 	c #7979FF",
-"< 	c #6264DD",
-"[ 	c #47478C",
-"} 	c #564567",
-"| 	c #4647D0",
-"1 	c #5C5CAE",
-"2 	c #5E5EFF",
-"3 	c #2929FF",
-"4 	c #1D1DFF",
-"5 	c #1919D1",
-"6 	c #4F4F90",
-"7 	c #1E1ECE",
-"8 	c #5858FF",
-"9 	c #6767A8",
-"0 	c #4949A0",
-"a 	c #7070FB",
-"b 	c #7D7DFF",
-"c 	c #7777FF",
-"d 	c #7373FF",
-"e 	c #7272FF",
-"f 	c #7878FF",
-"g 	c #6465D8",
-"h 	c #363886",
-"i 	c #9F7655",
-"j 	c #C89B5C",
-"k 	c #1D1CB7",
-"l 	c #3031B1",
-"m 	c #1919F4",
-"n 	c #1111FF",
-"o 	c #1818FF",
-"p 	c #1B1BFF",
-"q 	c #1C1CFF",
-"r 	c #2626B3",
-"s 	c #1E1EC8",
-"t 	c #1A1AE8",
-"u 	c #24249F",
-"v 	c #2F2FD2",
-"w 	c #7676FF",
-"x 	c #6869E2",
-"y 	c #414290",
-"z 	c #8C6751",
-"A 	c #FCBA68",
-"B 	c #E9BD7D",
-"C 	c #201EB8",
-"D 	c #090AB8",
-"E 	c #1616EB",
-"F 	c #1818FD",
-"G 	c #1414EE",
-"H 	c #1010E1",
-"I 	c #0E0EE2",
-"J 	c #0E0EF4",
-"K 	c #0606B2",
-"L 	c #7A7A89",
-"M 	c #0C0C9A",
-"N 	c #0A0AA7",
-"O 	c #2424E4",
-"P 	c #6669E6",
-"Q 	c #4F4A8F",
-"R 	c #BF853B",
-"S 	c #FFD98D",
-"T 	c #CDAB76",
-"U 	c #1717C4",
-"V 	c #0F10BA",
-"W 	c #0909B6",
-"X 	c #0505C3",
-"Y 	c #0000B6",
-"Z 	c #0000BE",
-"` 	c #0000AD",
-" .	c #1D1D83",
-"..	c #63638E",
-"+.	c #090975",
-"@.	c #1414F3",
-"#.	c #5B5BFF",
-"$.	c #7B7BFF",
-"%.	c #7070FF",
-"&.	c #6E6EFF",
-"*.	c #7172F6",
-"=.	c #625DAF",
-"-.	c #BA9E6C",
-";.	c #887167",
-">.	c #090DF2",
-",.	c #1313BE",
-"'.	c #000085",
-").	c #0000AC",
-"!.	c #0202AA",
-"~.	c #242488",
-"{.	c #1414C7",
-"].	c #1717FF",
-"^.	c #5959FF",
-"/.	c #7F7FFF",
-"(.	c #7474FF",
-"_.	c #7171FF",
-":.	c #8686FF",
-"<.	c #7574FF",
-"[.	c #797CFF",
-"}.	c #5756B8",
-"|.	c #1C19A4",
-"1.	c #1617FF",
-"2.	c #1212BD",
-"3.	c #040485",
-"4.	c #0707A4",
-"5.	c #1B1B71",
-"6.	c #373797",
-"7.	c #1616FF",
-"8.	c #5050FF",
-"9.	c #8080FF",
-"0.	c #AAAAFF",
-"a.	c #AEAEF6",
-"b.	c #8A8AEF",
-"c.	c #6969FB",
-"d.	c #2728FF",
-"e.	c #1314FF",
-"f.	c #1919FF",
-"g.	c #1313E8",
-"h.	c #1F1FF4",
-"i.	c #5454FF",
-"j.	c #6D6DF0",
-"k.	c #6868B5",
-"l.	c #0B0BB8",
-"m.	c #1212C5",
-"n.	c #1616FC",
-"o.	c #1515FF",
-"p.	c #1212FF",
-"q.	c #2323FF",
-"r.	c #3636FF",
-"s.	c #4040FF",
-"t.	c #4343F9",
-"u.	c #5D5DB8",
-"v.	c #7F7F92",
-"w.	c #878793",
-"x.	c #4B4B94",
-"y.	c #0B0CE2",
-"z.	c #1313FF",
-"A.	c #4C4CFF",
-"B.	c #8282FF",
-"C.	c #7171ED",
-"D.	c #636394",
-"E.	c #575785",
-"F.	c #A9A99C",
-"G.	c #1414BC",
-"H.	c #1414FF",
-"I.	c #0707FD",
-"J.	c #2525AA",
-"K.	c #A8A8A4",
-"L.	c #EBEBE2",
-"M.	c #F9F9F2",
-"N.	c #E1E1CC",
-"O.	c #4D4D9F",
-"P.	c #0B0BF7",
-"Q.	c #2121FF",
-"R.	c #3232FF",
-"S.	c #5555FF",
-"T.	c #6161B4",
-"U.	c #B5B5B2",
-"V.	c #FFFFF8",
-"W.	c #4F4F9A",
-"X.	c #0B0BF5",
-"Y.	c #1616C5",
-"Z.	c #A8A8A1",
-"`.	c #FFFFFC",
-" +	c #FFFFFF",
-".+	c #C0C0C4",
-"++	c #1212D4",
-"@+	c #4444FF",
-"#+	c #6464FF",
-"$+	c #8383FF",
-"%+	c #6767C3",
-"&+	c #E4E4E4",
-"*+	c #9494AE",
-"=+	c #0808DF",
-"-+	c #0D0DF2",
-";+	c #61619A",
-">+	c #F1F1E0",
-",+	c #E8E8DD",
-"'+	c #2424BB",
-")+	c #1010FF",
-"!+	c #3434FF",
-"~+	c #6161FF",
-"{+	c #6969D2",
-"]+	c #EFEFF0",
-"^+	c #C2C2BA",
-"/+	c #1010B6",
-"(+	c #0909AC",
-"_+	c #A4A49A",
-":+	c #EAEADE",
-"<+	c #2525B8",
-"[+	c #2F2FFF",
-"}+	c #3C3CB5",
-"|+	c #EEEEEE",
-"1+	c #BBBBAD",
-"2+	c #0B0B56",
-"3+	c #0B0BFC",
-"4+	c #1212EF",
-"5+	c #0C0C3E",
-"6+	c #919187",
-"7+	c #DEDED6",
-"8+	c #1F1FC0",
-"9+	c #1A1AFF",
-"0+	c #1717FA",
-"a+	c #1515F8",
-"b+	c #1111FC",
-"c+	c #494992",
-"d+	c #999998",
-"e+	c #3E3E3B",
-"f+	c #3C3C99",
-"g+	c #535397",
-"h+	c #5A5A4D",
-"i+	c #6F6F70",
-"j+	c #BFBFC9",
-"k+	c #1111D6",
-"l+	c #1515F1",
-"m+	c #0F0FE2",
-"n+	c #0D0DD9",
-"o+	c #0909CD",
-"p+	c #0808C7",
-"q+	c #0505C7",
-"r+	c #0303CB",
-"s+	c #0101C0",
-"t+	c #0202AF",
-"u+	c #0606AC",
-"v+	c #121283",
-"w+	c #BBBBBB",
-"x+	c #BEBEBE",
-"y+	c #2F2F2E",
-"z+	c #C7C8BB",
-"A+	c #D8DAD1",
-"B+	c #272828",
-"C+	c #929292",
-"D+	c #8688C7",
-"E+	c #0506F6",
-"F+	c #1616F5",
-"G+	c #0B0BD3",
-"H+	c #0202B6",
-"I+	c #0000AF",
-"J+	c #0000B4",
-"K+	c #0000BD",
-"L+	c #0000BB",
-"M+	c #00009E",
-"N+	c #2C2C7E",
-"O+	c #6A6A8B",
-"P+	c #959595",
-"Q+	c #F0F0F1",
-"R+	c #E1E1E1",
-"S+	c #8C8E90",
-"T+	c #BEBEBF",
-"U+	c #C9C7C5",
-"V+	c #939699",
-"W+	c #E7EAED",
-"X+	c #CBCBC7",
-"Y+	c #413B9B",
-"Z+	c #0607DD",
-"`+	c #0C0CE2",
-" @	c #0303B9",
-".@	c #0000A8",
-"+@	c #181888",
-"@@	c #6A6A6A",
-"#@	c #626263",
-"$@	c #4B4B4C",
-"%@	c #3E3B36",
-"&@	c #9B805C",
-"*@	c #D9B07D",
-"=@	c #C9AE89",
-"-@	c #B9AF9E",
-";@	c #C7C5C4",
-">@	c #CBCCCF",
-",@	c #C7C6C6",
-"'@	c #AEA59A",
-")@	c #B69974",
-"!@	c #D8B87F",
-"~@	c #9B8272",
-"{@	c #0E0B9B",
-"]@	c #0000B7",
-"^@	c #0000B8",
-"/@	c #000082",
-"(@	c #00007A",
-"_@	c #636379",
-":@	c #62533E",
-"<@	c #B59B6C",
-"[@	c #DEB07B",
-"}@	c #FECC90",
-"|@	c #FFCE92",
-"1@	c #FEC98C",
-"2@	c #F1BD82",
-"3@	c #D1A979",
-"4@	c #BC9E73",
-"5@	c #CCA777",
-"6@	c #EAB980",
-"7@	c #FFCD90",
-"8@	c #FFD595",
-"9@	c #FDD782",
-"0@	c #413678",
-"a@	c #0000AE",
-"b@	c #000077",
-"c@	c #010193",
-"d@	c #0C0CE4",
-"e@	c #38389E",
-"f@	c #EEC585",
-"g@	c #FFDA9D",
-"h@	c #FFC992",
-"i@	c #FFC88F",
-"j@	c #FFC990",
-"k@	c #FFCE93",
-"l@	c #FFD094",
-"m@	c #FFCC92",
-"n@	c #C9A174",
-"o@	c #EDBD88",
-"p@	c #FAD287",
-"q@	c #3A2F7F",
-"r@	c #0000BA",
-"s@	c #0000B0",
-"t@	c #0101B2",
-"u@	c #1111ED",
-"v@	c #1919C1",
-"w@	c #95887C",
-"x@	c #DCAC6E",
-"y@	c #FFD393",
-"z@	c #FFCD94",
-"A@	c #FFCA93",
-"B@	c #FFC991",
-"C@	c #FFC78E",
-"D@	c #FFCB91",
-"E@	c #E0B581",
-"F@	c #BB9A6F",
-"G@	c #FFDC97",
-"H@	c #C1A173",
-"I@	c #0E0B9A",
-"J@	c #0000B5",
-"K@	c #0101B6",
-"L@	c #1010E0",
-"M@	c #1616EC",
-"N@	c #A68156",
-"O@	c #E7AC6B",
-"P@	c #FFC582",
-"Q@	c #FFCF8F",
-"R@	c #FFD195",
-"S@	c #FFD296",
-"T@	c #FFD396",
-"U@	c #FFD193",
-"V@	c #FFD28F",
-"W@	c #D2A96B",
-"X@	c #2F2482",
-"Y@	c #0000C1",
-"Z@	c #0000C0",
-"`@	c #0000BF",
-" #	c #0101BF",
-".#	c #1212F0",
-"+#	c #767698",
-"@#	c #9C866E",
-"##	c #A9865D",
-"$#	c #C0915D",
-"%#	c #C89760",
-"&#	c #C29360",
-"*#	c #AD8A61",
-"=#	c #9D8971",
-"-#	c #7F7A7A",
-";#	c #70708F",
-">#	c #6F6F91",
-",#	c #575788",
-"'#	c #464687",
-")#	c #2F2F87",
-"!#	c #15158F",
-"~#	c #0101A8",
-"{#	c #1313FB",
-"]#	c #57579F",
-"^#	c #343487",
-"/#	c #434388",
+static const char *SDL_icon_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"32 32 175 2 ",
+"   c None",
+".  c #2E2E2E",
+"X  c #3C3C3C",
+"o  c #493939",
+"O  c #4E473F",
+"+  c #161658",
+"@  c #131369",
+"#  c #06067B",
+"$  c #111173",
+"%  c #16167F",
+"&  c #252567",
+"*  c #372B7C",
+"=  c #3D3679",
+"-  c #41414A",
+";  c #575655",
+":  c #6A5841",
+">  c #5B4B72",
+",  c #616160",
+"<  c #7B7B7B",
+"1  c #906E49",
+"2  c #89685D",
+"3  c #A67B4A",
+"4  c #AA7F50",
+"5  c #9B7560",
+"6  c #856C78",
+"7  c #997B7D",
+"8  c #B48552",
+"9  c #BA8A55",
+"0  c #A48665",
+"q  c #B98F67",
+"w  c #B9946A",
+"e  c #B7937A",
+"r  c #C8955C",
+"t  c #CA9966",
+"y  c #DAA469",
+"u  c #C9A37B",
+"i  c #D7AB7B",
+"p  c #DFB07D",
+"a  c #EBAE6A",
+"s  c #E5B27A",
+"d  c #F1B779",
+"f  c #0A0A83",
+"g  c #05058B",
+"h  c #060687",
+"j  c #101089",
+"k  c #131382",
+"l  c #040494",
+"z  c #02029D",
+"x  c #0C0B9C",
+"c  c #120F9E",
+"v  c #19199B",
+"b  c #382D84",
+"n  c #39398D",
+"m  c #222296",
+"M  c #0101A6",
+"N  c #0A0AA2",
+"B  c #0202AC",
+"V  c #1919A2",
+"C  c #1616AD",
+"Z  c #0000B5",
+"A  c #0202BC",
+"S  c #0C0CB6",
+"D  c #1313B3",
+"F  c #1011BD",
+"G  c #1B1BBE",
+"H  c #2B2BAC",
+"J  c #3737A1",
+"K  c #2A26BE",
+"L  c #2A29B4",
+"P  c #3B3BB8",
+"I  c #48478B",
+"U  c #57578A",
+"Y  c #4A499A",
+"T  c #524F95",
+"R  c #565399",
+"E  c #4C4CA8",
+"W  c #524DA7",
+"Q  c #5353A4",
+"!  c #5555A9",
+"~  c #5555B4",
+"^  c #5656B7",
+"/  c #6464A6",
+"(  c #6F67B5",
+")  c #0404C3",
+"_  c #0707CA",
+"`  c #1414CB",
+"'  c #1A1AC6",
+"]  c #0A0AD3",
+"[  c #0D0DDC",
+"{  c #1A1AD4",
+"}  c #1010DF",
+"|  c #1E1EDE",
+" . c #1817DE",
+".. c #221FCA",
+"X. c #2B2BCC",
+"o. c #2727C9",
+"O. c #3434C3",
+"+. c #3434D4",
+"@. c #0F0FE2",
+"#. c #1313E5",
+"$. c #1515ED",
+"%. c #1B1BEA",
+"&. c #1C1CE4",
+"*. c #1515F4",
+"=. c #1818F3",
+"-. c #1717FD",
+";. c #1818FF",
+":. c #2B2BE9",
+">. c #2424FF",
+",. c #2A2AFF",
+"<. c #2222F1",
+"1. c #3737FF",
+"2. c #5D5DC3",
+"3. c #5F5FC9",
+"4. c #5655C2",
+"5. c #4747D1",
+"6. c #5B5BD4",
+"7. c #6565C8",
+"8. c #6363DA",
+"9. c #4545FF",
+"0. c #4D4DFC",
+"q. c #5454FF",
+"w. c #5959FF",
+"e. c #6969E5",
+"r. c #6B6CEA",
+"t. c #6666E7",
+"y. c #6B6BFE",
+"u. c #6767F8",
+"i. c #7070F6",
+"p. c #7373FF",
+"a. c #7C7CFF",
+"s. c #91918F",
+"d. c #8F9090",
+"f. c #979797",
+"g. c #9C9C9C",
+"h. c #8585A1",
+"j. c #9C9CA7",
+"k. c #9292B6",
+"l. c #A4A4A4",
+"z. c #BDB2A4",
+"x. c #A4A4B1",
+"c. c #BFBFBD",
+"v. c #BABAB7",
+"b. c #C8AA87",
+"n. c #DAAE82",
+"m. c #DBB081",
+"M. c #EBBA85",
+"N. c #F3BF84",
+"B. c #F2BE88",
+"V. c #C2B3A3",
+"C. c #FBC386",
+"Z. c #FCC68C",
+"A. c #FFC88F",
+"S. c #F4C387",
+"D. c #FFC990",
+"F. c #C3C1BF",
+"G. c #8F8FCB",
+"H. c #BDBDC2",
+"J. c #BDBDD1",
+"K. c #8888F9",
+"L. c #A4A4FB",
+"P. c #CDCDCC",
+"I. c #CECAC6",
+"U. c #D3CFCA",
+"Y. c #D3D0CC",
+"T. c #C0C0D5",
+"R. c #D6D5D4",
+"E. c #D7D7DD",
+"W. c #E1E1DF",
+"Q. c #DEDEE1",
+"!. c #E4E4E4",
+"~. c #E8E8E8",
+"^. c #F0F0EE",
+"/. c #F5F5F2",
+"(. c #FFFFFF",
+/* pixels */
 "                                                                ",
 "                                                                ",
 "                                                                ",
-"                              . + @ #                           ",
-"                      $ % & * = - ; > , ' ) !                   ",
-"      ~ {       ] ^ / = ( _ : < [ } | 1 2 3 4 5 6               ",
-"      7 8 9   0 a b c d e f g h i j k l m n o p q r             ",
-"      s t u v _ f d d d w x y z A B C D E F G H I J K L         ",
-"      M N O _ c e d d d _ P Q R S T U V W X Y Z `  ...          ",
-"      +.@.#.$.d d d d %.&._ *.=.-.;.>.,.'.).!.~.                ",
-"      {.].^./.(.d d _.$.:._ <.[.}.|.1.2.3.4.5.                  ",
-"    6.7.7.4 8.e : w 9.0.a.b.c.2 d.e.f.g.h.i.j.k.                ",
-"    l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.o o z.A.B./.b C.D.            ",
-"    E.F.G.].o H.z.I.J.K.L.M.N.O.P.o o o Q.R.S._.b B.T.          ",
-"    U.V.W.X.f.f.7.Y.Z.`. + + +.+++].o o o.n z.q.@+#+$+%+        ",
-"    &+ +*+=+].o -+;+>+ + + + +,+'+H.o o o o o H.)+o !+~+{+      ",
-"    ]+ +^+/+H.o.(+_+ + + + + +:+<+z.o o o o o o o 7.n H.[+}+    ",
-"    |+ +1+2+3+4+5+6+ + + + + +7+8+H.o o f.9+f.9+f.F 0+a+b+o.c+  ",
-"    &+ +d+e+f+g+h+i+ + + + + +j+k+].f.9+l+m+n+o+p+q+r+s+t+u+v+  ",
-"    w+ +x+y+z+A+B+C+ + + + + +D+E+9+F+G+H+I+J+K+L+M+N+O+        ",
-"    P+Q+R+S+T+U+V+W+ + + + +X+Y+Z+`+ @I+J+Z .@+@E.              ",
-"    @@#@$@%@&@*@=@-@;@>@,@'@)@!@~@{@]@^@I+/@(@_@                ",
-"      :@<@[@}@|@1@2@3@4@5@6@7@8@9@0@L+a@b@c@d@e@                ",
-"        f@g@h@i@i@j@k@l@|@m@n@o@p@q@r@s@t@u@p v@                ",
-"        w@x@y@z@A@B@i@C@D@E@F@G@H@I@L+J@K@L@p M@                ",
-"            N@O@P@Q@R@S@T@U@V@W@X@Y@Z@Y@`@ #.#p +#              ",
-"                @###$#%#&#*#=#-#;#>#,#'#)#!#~#{#]#              ",
-"                                              ^#/#              ",
+"                              I Q T =                           ",
+"                      Q 7.e.r.i.8.E E 3.r.6.J                   ",
+"      H ~       n 4.r.p.p.p.p.8.R > 5.^ w.,.-.{ v               ",
+"      { 9.^ & P t.p.p.p.p.p.8.I 5 q K L <.;.;.;.-.'             ",
+"      { %.H +.y.p.p.p.p.p.e.Y 2 a n.K F $.*.$.@.} ] N           ",
+"      x D :.y.p.p.p.p.p.p.r.R 8 C.u ..F A ) A Z M h $           ",
+"      f =.q.p.p.p.p.p.p.p.p.i.( e 6 $.` l B M g                 ",
+"      ` ;.q.p.p.p.p.p.a.K.a.p.p.4.L -.` l N %                   ",
+"    V =.-.>.q.y.p.p.p.L.L.K.i.w.,.-.;.$.<.q.u.2.                ",
+"    D { =.-.;.>.1.1.9.( h.h.Q &.-.-.-.;.9.p.p.p.r.!             ",
+"    U j.o.-.;.-.;.-.P x.Q.^.R.~ *.-.;.;.>.1.q.y.p.i.2.          ",
+"    H./.! *.;.;.;.o.x./.(.(.(.J.| -.-.;.-.-.;.,.9.u.p.7.        ",
+"    !.(.k.#.;.-.=./ !.(.(.(.(.Q.X.-.;.;.;.;.-.-.;.;.1.w.6.      ",
+"    ~.(.H.G ;.-.D j.(.(.(.(.(.!.O.-.-.;.;.;.-.;.-.;.-.;.,.O.    ",
+"    ~.(.v.@ *.$.+ d.(.(.(.(.(.E.o.-.-.;.;.-.;.;.;.*.=.=.*.$.v   ",
+"    ~.(.l.- Y T ; < (.(.(.(.(.J.&.-.;.;.$.@.[ ] _ ) ) Z B B f   ",
+"    P.(.F.X c.I.X f.(.(.(.(.(.G.=.-.=.] A Z Z Z Z z f $         ",
+"    l.!.R.s.F.I.g.W.(.(.(.(.R.E  .[ A Z Z Z B g $               ",
+"    . , ; - 0 M.b.V.U.R.Y.z.u n.7 c Z Z B g # +                 ",
+"      : w p Z.D.A.S.p u i M.A.A.S.* Z B h z ] C                 ",
+"        s D.D.A.A.A.A.A.A.A.i B.B.b A Z Z @.-.`                 ",
+"        1 y C.D.A.A.A.A.A.M.u Z.e c A Z Z [ ;.&.                ",
+"            8 y d C.A.A.A.C.B.t * B Z Z Z A #.=.m               ",
+"                3 9 r t r 9 8 o @ $ # f j l B #.V               ",
+"                                              j k               ",
 "                                                                ",
 "                                                                ",
 "                                                                ",
-"                                                                "};
+"                                                                "
+};
diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index c8fcd080ebd42aa5f989c60875e0cd2f4bbe1aec..eb851a619f2f3d2a8f4f67003aa0303c9d785fd8 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -124,6 +124,10 @@ typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T);
 #include "macosx/mac_resources.h"
 #endif
 
+#ifndef errno
+#include <errno.h>
+#endif
+
 // Locations for searching the srb2.pk3
 #if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)
 #define DEFAULTWADLOCATION1 "/usr/local/share/games/SRB2"
@@ -1149,6 +1153,7 @@ static void I_ShutdownJoystick2(void)
 		D_PostEvent(&event);
 	}
 
+	joystick2_started = 0;
 	JoyReset(&JoyInfo2);
 	if (!joystick_started && !joystick2_started && SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK)
 	{
@@ -1678,7 +1683,7 @@ static void I_ShutdownMouse2(void)
 	EscapeCommFunction(mouse2filehandle, CLRRTS);
 
 	PurgeComm(mouse2filehandle, PURGE_TXABORT | PURGE_RXABORT |
-	          PURGE_TXCLEAR | PURGE_RXCLEAR);
+			  PURGE_TXCLEAR | PURGE_RXCLEAR);
 
 	CloseHandle(mouse2filehandle);
 
@@ -1871,11 +1876,11 @@ void I_StartupMouse2(void)
 	{
 		// COM file handle
 		mouse2filehandle = CreateFileA(cv_mouse2port.string, GENERIC_READ | GENERIC_WRITE,
-		                               0,                     // exclusive access
-		                               NULL,                  // no security attrs
-		                               OPEN_EXISTING,
-		                               FILE_ATTRIBUTE_NORMAL,
-		                               NULL);
+									   0,                     // exclusive access
+									   NULL,                  // no security attrs
+									   OPEN_EXISTING,
+									   FILE_ATTRIBUTE_NORMAL,
+									   NULL);
 		if (mouse2filehandle == INVALID_HANDLE_VALUE)
 		{
 			INT32 e = GetLastError();
@@ -1895,7 +1900,7 @@ void I_StartupMouse2(void)
 
 	// purge buffers
 	PurgeComm(mouse2filehandle, PURGE_TXABORT | PURGE_RXABORT
-	          | PURGE_TXCLEAR | PURGE_RXCLEAR);
+			  | PURGE_TXCLEAR | PURGE_RXCLEAR);
 
 	// setup port to 1200 7N1
 	dcb.DCBlength = sizeof (DCB);
@@ -2024,7 +2029,7 @@ static void I_ShutdownTimer(void)
 tic_t I_GetTime (void)
 {
 	static Uint32 basetime = 0;
-	       Uint32 ticks = SDL_GetTicks();
+		   Uint32 ticks = SDL_GetTicks();
 
 	if (!basetime)
 		basetime = ticks;
@@ -2090,7 +2095,6 @@ INT32 I_StartupSystem(void)
 	return 0;
 }
 
-
 //
 // I_Quit
 //
@@ -2369,7 +2373,7 @@ void I_GetDiskFreeSpace(INT64 *freespace)
 	{
 		DWORD SectorsPerCluster, BytesPerSector, NumberOfFreeClusters, TotalNumberOfClusters;
 		GetDiskFreeSpace(NULL, &SectorsPerCluster, &BytesPerSector,
-		                 &NumberOfFreeClusters, &TotalNumberOfClusters);
+						 &NumberOfFreeClusters, &TotalNumberOfClusters);
 		*freespace = BytesPerSector*SectorsPerCluster*NumberOfFreeClusters;
 	}
 #else // Dummy for platform independent; 1GB should be enough
@@ -2576,22 +2580,22 @@ static const char *locateWad(void)
 
 #ifdef CMAKECONFIG
 #ifndef NDEBUG
-    I_OutputMsg(","CMAKE_ASSETS_DIR);
-    strcpy(returnWadPath, CMAKE_ASSETS_DIR);
-    if (isWadPathOk(returnWadPath))
-    {
-        return returnWadPath;
-    }
+	I_OutputMsg(","CMAKE_ASSETS_DIR);
+	strcpy(returnWadPath, CMAKE_ASSETS_DIR);
+	if (isWadPathOk(returnWadPath))
+	{
+		return returnWadPath;
+	}
 #endif
 #endif
 
 #ifdef __APPLE__
-    OSX_GetResourcesPath(returnWadPath);
-    I_OutputMsg(",%s", returnWadPath);
-    if (isWadPathOk(returnWadPath))
-    {
-        return returnWadPath;
-    }
+	OSX_GetResourcesPath(returnWadPath);
+	I_OutputMsg(",%s", returnWadPath);
+	if (isWadPathOk(returnWadPath))
+	{
+		return returnWadPath;
+	}
 #endif
 
 	// examine default dirs
@@ -2696,7 +2700,30 @@ const char *I_LocateWad(void)
 #ifdef __linux__
 #define MEMINFO_FILE "/proc/meminfo"
 #define MEMTOTAL "MemTotal:"
+#define MEMAVAILABLE "MemAvailable:"
 #define MEMFREE "MemFree:"
+#define CACHED "Cached:"
+#define BUFFERS "Buffers:"
+#define SHMEM "Shmem:"
+
+/* Parse the contents of /proc/meminfo (in buf), return value of "name"
+ * (example: MemTotal) */
+static long get_entry(const char* name, const char* buf)
+{
+	long val;
+	char* hit = strstr(buf, name);
+	if (hit == NULL) {
+		return -1;
+	}
+
+	errno = 0;
+	val = strtol(hit + strlen(name), NULL, 10);
+	if (errno != 0) {
+		CONS_Alert(CONS_ERROR, M_GetText("get_entry: strtol() failed: %s\n"), strerror(errno));
+		return -1;
+	}
+	return val;
+}
 #endif
 
 // quick fix for compil
@@ -2758,6 +2785,11 @@ UINT32 I_GetFreeMem(UINT32 *total)
 	UINT32 totalKBytes;
 	INT32 n;
 	INT32 meminfo_fd = -1;
+	long Cached;
+	long MemFree;
+	long Buffers;
+	long Shmem;
+	long MemAvailable = -1;
 
 	meminfo_fd = open(MEMINFO_FILE, O_RDONLY);
 	n = read(meminfo_fd, buf, 1023);
@@ -2783,16 +2815,28 @@ UINT32 I_GetFreeMem(UINT32 *total)
 	memTag += sizeof (MEMTOTAL);
 	totalKBytes = atoi(memTag);
 
-	if ((memTag = strstr(buf, MEMFREE)) == NULL)
+	if ((memTag = strstr(buf, MEMAVAILABLE)) == NULL)
 	{
-		// Error
-		if (total)
-			*total = 0L;
-		return 0;
-	}
+		Cached = get_entry(CACHED, buf);
+		MemFree = get_entry(MEMFREE, buf);
+		Buffers = get_entry(BUFFERS, buf);
+		Shmem = get_entry(SHMEM, buf);
+		MemAvailable = Cached + MemFree + Buffers - Shmem;
 
-	memTag += sizeof (MEMFREE);
-	freeKBytes = atoi(memTag);
+		if (MemAvailable == -1)
+		{
+			// Error
+			if (total)
+				*total = 0L;
+			return 0;
+		}
+		freeKBytes = MemAvailable;
+	}
+	else
+	{
+		memTag += sizeof (MEMAVAILABLE);
+		freeKBytes = atoi(memTag);
+	}
 
 	if (total)
 		*total = totalKBytes << 10;
diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index a610c56c6b7b3e36fd087e3f0ec2a694208b87a4..c9d7e04092d4ea3d5dacbbc00aaf2214c5ff93c8 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -39,6 +39,10 @@
 
 #ifdef HAVE_IMAGE
 #include "SDL_image.h"
+#elif 1
+#define LOAD_XPM //I want XPM!
+#include "IMG_xpm.c" //Alam: I don't want to add SDL_Image.dll/so
+#define HAVE_IMAGE //I have SDL_Image, sortof
 #endif
 
 #ifdef HAVE_IMAGE
@@ -562,7 +566,7 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt)
 		// Tell game we got focus back, resume music if necessary
 		window_notinfocus = false;
 		if (!paused)
-			I_ResumeSong(0); //resume it
+			I_ResumeSong(); //resume it
 
 		if (!firsttimeonmouse)
 		{
@@ -574,7 +578,7 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt)
 	{
 		// Tell game we lost focus, pause music
 		window_notinfocus = true;
-		I_PauseSong(0);
+		I_PauseSong();
 
 		if (!disable_mouse)
 		{
diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c
index 71832459182305a2bed1cf1e9e94446508fb8dd0..4d86d7a3cefb9624a96c5ef76f84bfb546138566 100644
--- a/src/sdl/mixer_sound.c
+++ b/src/sdl/mixer_sound.c
@@ -34,14 +34,18 @@
 	(SDL_MIXER_COMPILEDVERSION >= SDL_VERSIONNUM(X, Y, Z))
 #endif
 
+// thanks alam for making the buildbots happy!
+#if SDL_MIXER_VERSION_ATLEAST(2,0,2)
+#define MUS_MP3_MAD MUS_MP3_MAD_UNUSED
+#define MUS_MODPLUG MUS_MODPLUG_UNUSED
+#endif
+
 #ifdef HAVE_LIBGME
 #include "gme/gme.h"
 #define GME_TREBLE 5.0
 #define GME_BASS 1.0
-#ifdef HAVE_PNG /// TODO: compile with zlib support without libpng
-
-#define HAVE_ZLIB
 
+#ifdef HAVE_ZLIB
 #ifndef _MSC_VER
 #ifndef _LARGEFILE64_SOURCE
 #define _LARGEFILE64_SOURCE
@@ -57,28 +61,35 @@
 #endif
 
 #include "zlib.h"
-#endif
-#endif
+#endif // HAVE_ZLIB
+#endif // HAVE_LIBGME
 
 UINT8 sound_started = false;
 
-static boolean midimode;
 static Mix_Music *music;
-static UINT8 music_volume, midi_volume, sfx_volume;
+static UINT8 music_volume, sfx_volume;
 static float loop_point;
+static boolean songpaused;
 
 #ifdef HAVE_LIBGME
 static Music_Emu *gme;
 static INT32 current_track;
 #endif
 
+/// ------------------------
+/// Audio System
+/// ------------------------
+
 void I_StartupSound(void)
 {
 	I_Assert(!sound_started);
 
 	// EE inits audio first so we're following along.
 	if (SDL_WasInit(SDL_INIT_AUDIO) == SDL_INIT_AUDIO)
-		CONS_Printf("SDL Audio already started\n");
+	{
+		CONS_Debug(DBG_DETAILED, "SDL Audio already started\n");
+		return;
+	}
 	else if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
 	{
 		CONS_Alert(CONS_ERROR, "Error initializing SDL Audio: %s\n", SDL_GetError());
@@ -86,9 +97,8 @@ void I_StartupSound(void)
 		return;
 	}
 
-	midimode = false;
 	music = NULL;
-	music_volume = midi_volume = sfx_volume = 0;
+	music_volume = sfx_volume = 0;
 
 #if SDL_MIXER_VERSION_ATLEAST(1,2,11)
 	Mix_Init(MIX_INIT_FLAC|MIX_INIT_MOD|MIX_INIT_MP3|MIX_INIT_OGG);
@@ -102,6 +112,7 @@ void I_StartupSound(void)
 	}
 
 	sound_started = true;
+	songpaused = false;
 	Mix_AllocateChannels(256);
 }
 
@@ -128,6 +139,10 @@ FUNCMATH void I_UpdateSound(void)
 {
 }
 
+/// ------------------------
+/// SFX
+/// ------------------------
+
 // this is as fast as I can possibly make it.
 // sorry. more asm needed.
 static Mix_Chunk *ds2chunk(void *stream)
@@ -244,6 +259,7 @@ void *I_GetSfx(sfxinfo_t *sfx)
 {
 	void *lump;
 	Mix_Chunk *chunk;
+	SDL_RWops *rw;
 #ifdef HAVE_LIBGME
 	Music_Emu *emu;
 	gme_info_t *info;
@@ -304,7 +320,7 @@ void *I_GetSfx(sfxinfo_t *sfx)
 					gme_track_info(emu, &info, 0);
 
 					len = (info->play_length * 441 / 10) << 2;
-					mem = Z_Malloc(len, PU_SOUND, NULL);
+					mem = malloc(len);
 					gme_play(emu, len >> 1, mem);
 					gme_delete(emu);
 
@@ -359,7 +375,7 @@ void *I_GetSfx(sfxinfo_t *sfx)
 		}
 		Z_Free(inflatedData); // GME didn't open jack, but don't let that stop us from freeing this up
 #else
-		//CONS_Alert(CONS_ERROR,"Cannot decompress VGZ; no zlib support\n");
+		return NULL; // No zlib support
 #endif
 	}
 	// Try to read it as a GME sound
@@ -376,7 +392,7 @@ void *I_GetSfx(sfxinfo_t *sfx)
 		gme_track_info(emu, &info, 0);
 
 		len = (info->play_length * 441 / 10) << 2;
-		mem = Z_Malloc(len, PU_SOUND, NULL);
+		mem = malloc(len);
 		gme_play(emu, len >> 1, mem);
 		gme_delete(emu);
 
@@ -385,21 +401,43 @@ void *I_GetSfx(sfxinfo_t *sfx)
 #endif
 
 	// Try to load it as a WAVE or OGG using Mixer.
-	return Mix_LoadWAV_RW(SDL_RWFromMem(lump, sfx->length), 1);
+	rw = SDL_RWFromMem(lump, sfx->length);
+	if (rw != NULL)
+	{
+		chunk = Mix_LoadWAV_RW(rw, 1);
+		return chunk;
+	}
+
+	return NULL; // haven't been able to get anything
 }
 
 void I_FreeSfx(sfxinfo_t *sfx)
 {
 	if (sfx->data)
+	{
+		Mix_Chunk *chunk = (Mix_Chunk*)sfx->data;
+		UINT8 *abufdata = NULL;
+		if (chunk->allocated == 0)
+		{
+			// We allocated the data in this chunk, so get the abuf from mixer, then let it free the chunk, THEN we free the data
+			// I believe this should ensure the sound is not playing when we free it
+			abufdata = chunk->abuf;
+		}
 		Mix_FreeChunk(sfx->data);
+		if (abufdata)
+		{
+			// I'm going to assume we used Z_Malloc to allocate this data.
+			Z_Free(abufdata);
+		}
+	}
 	sfx->data = NULL;
 	sfx->lumpnum = LUMPERROR;
 }
 
-INT32 I_StartSound(sfxenum_t id, UINT8 vol, UINT8 sep, UINT8 pitch, UINT8 priority)
+INT32 I_StartSound(sfxenum_t id, UINT8 vol, UINT8 sep, UINT8 pitch, UINT8 priority, INT32 channel)
 {
 	UINT8 volume = (((UINT16)vol + 1) * (UINT16)sfx_volume) / 62; // (256 * 31) / 62 == 127
-	INT32 handle = Mix_PlayChannel(-1, S_sfx[id].data, 0);
+	INT32 handle = Mix_PlayChannel(channel, S_sfx[id].data, 0);
 	Mix_Volume(handle, volume);
 	Mix_SetPanning(handle, min((UINT16)(0xff-sep)<<1, 0xff), min((UINT16)(sep)<<1, 0xff));
 	(void)pitch; // Mixer can't handle pitch
@@ -430,11 +468,10 @@ void I_SetSfxVolume(UINT8 volume)
 	sfx_volume = volume;
 }
 
-//
-// Music
-//
+/// ------------------------
+/// Music Hooks
+/// ------------------------
 
-// Music hooks
 static void music_loop(void)
 {
 	Mix_PlayMusic(music, 0);
@@ -450,7 +487,7 @@ static void mix_gme(void *udata, Uint8 *stream, int len)
 	(void)udata;
 
 	// no gme? no music.
-	if (!gme || gme_track_ended(gme))
+	if (!gme || gme_track_ended(gme) || songpaused)
 		return;
 
 	// play gme into stream
@@ -458,80 +495,105 @@ static void mix_gme(void *udata, Uint8 *stream, int len)
 
 	// apply volume to stream
 	for (i = 0, p = (short *)stream; i < len/2; i++, p++)
-		*p = ((INT32)*p) * music_volume / 31;
+		*p = ((INT32)*p) * music_volume*2 / 42;
 }
 #endif
 
+
+/// ------------------------
+/// Music System
+/// ------------------------
+
 FUNCMATH void I_InitMusic(void)
 {
 }
 
 void I_ShutdownMusic(void)
 {
-	I_ShutdownDigMusic();
-	I_ShutdownMIDIMusic();
+	I_UnloadSong();
 }
 
-void I_PauseSong(INT32 handle)
-{
-	(void)handle;
-	Mix_PauseMusic();
-}
+/// ------------------------
+/// Music Properties
+/// ------------------------
 
-void I_ResumeSong(INT32 handle)
+musictype_t I_SongType(void)
 {
-	(void)handle;
-	Mix_ResumeMusic();
+#ifdef HAVE_LIBGME
+	if (gme)
+		return MU_GME;
+	else
+#endif
+	if (!music)
+		return MU_NONE;
+	else if (Mix_GetMusicType(music) == MUS_MID)
+		return MU_MID;
+	else if (Mix_GetMusicType(music) == MUS_MOD || Mix_GetMusicType(music) == MUS_MODPLUG)
+		return MU_MOD;
+	else if (Mix_GetMusicType(music) == MUS_MP3 || Mix_GetMusicType(music) == MUS_MP3_MAD)
+		return MU_MP3;
+	else
+		return (musictype_t)Mix_GetMusicType(music);
 }
 
-//
-// Digital Music
-//
-
-void I_InitDigMusic(void)
+boolean I_SongPlaying(void)
 {
+	return (
 #ifdef HAVE_LIBGME
-	gme = NULL;
-	current_track = -1;
+		(I_SongType() == MU_GME && gme) ||
 #endif
+		(boolean)music
+	);
 }
 
-void I_ShutdownDigMusic(void)
+boolean I_SongPaused(void)
 {
-	if (midimode)
-		return;
+	return songpaused;
+}
+
+/// ------------------------
+/// Music Effects
+/// ------------------------
+
+boolean I_SetSongSpeed(float speed)
+{
+	if (speed > 250.0f)
+		speed = 250.0f; //limit speed up to 250x
 #ifdef HAVE_LIBGME
 	if (gme)
 	{
-		Mix_HookMusic(NULL, NULL);
-		gme_delete(gme);
-		gme = NULL;
+		SDL_LockAudio();
+		gme_set_tempo(gme, speed);
+		SDL_UnlockAudio();
+		return true;
 	}
+#else
+	(void)speed;
 #endif
-	if (!music)
-		return;
-	Mix_HookMusicFinished(NULL);
-	Mix_FreeMusic(music);
-	music = NULL;
+	return false;
 }
 
-boolean I_StartDigSong(const char *musicname, boolean looping)
-{
-	char *data;
-	size_t len;
-	lumpnum_t lumpnum = W_CheckNumForName(va("O_%s",musicname));
+/// ------------------------
+/// Music Playback
+/// ------------------------
 
-	I_Assert(!music);
+boolean I_LoadSong(char *data, size_t len)
+{
+	const char *key1 = "LOOP";
+	const char *key2 = "POINT=";
+	const char *key3 = "MS=";
+	const size_t key1len = strlen(key1);
+	const size_t key2len = strlen(key2);
+	const size_t key3len = strlen(key3);
+	char *p = data;
+	SDL_RWops *rw;
+
+	if (music
 #ifdef HAVE_LIBGME
-	I_Assert(!gme);
+		|| gme
 #endif
-
-	if (lumpnum == LUMPERROR)
-		return false;
-	midimode = false;
-
-	data = (char *)W_CacheLumpNum(lumpnum, PU_MUSIC);
-	len = W_LumpLength(lumpnum);
+	)
+		I_UnloadSong();
 
 #ifdef HAVE_LIBGME
 	if ((UINT8)data[0] == 0x1F
@@ -617,66 +679,95 @@ boolean I_StartDigSong(const char *musicname, boolean looping)
 		}
 		Z_Free(inflatedData); // GME didn't open jack, but don't let that stop us from freeing this up
 #else
-		//CONS_Alert(CONS_ERROR,"Cannot decompress VGZ; no zlib support\n");
+		CONS_Alert(CONS_ERROR,"Cannot decompress VGZ; no zlib support\n");
+		return true;
 #endif
 	}
 	else if (!gme_open_data(data, len, &gme, 44100))
 	{
 		gme_equalizer_t eq = {GME_TREBLE, GME_BASS, 0,0,0,0,0,0,0,0};
-		gme_start_track(gme, 0);
-		current_track = 0;
 		gme_set_equalizer(gme, &eq);
-		Mix_HookMusic(mix_gme, gme);
 		return true;
 	}
 #endif
 
-	music = Mix_LoadMUS_RW(SDL_RWFromMem(data, len), SDL_FALSE);
+	rw = SDL_RWFromMem(data, len);
+	if (rw != NULL)
+	{
+		music = Mix_LoadMUS_RW(rw, 1);
+	}
 	if (!music)
 	{
 		CONS_Alert(CONS_ERROR, "Mix_LoadMUS_RW: %s\n", Mix_GetError());
-		return true;
+		return false;
 	}
 
 	// Find the OGG loop point.
 	loop_point = 0.0f;
-	if (looping)
+
+	while ((UINT32)(p - data) < len)
 	{
-		const char *key1 = "LOOP";
-		const char *key2 = "POINT=";
-		const char *key3 = "MS=";
-		const size_t key1len = strlen(key1);
-		const size_t key2len = strlen(key2);
-		const size_t key3len = strlen(key3);
-		char *p = data;
-		while ((UINT32)(p - data) < len)
+		if (strncmp(p++, key1, key1len))
+			continue;
+		p += key1len-1; // skip OOP (the L was skipped in strncmp)
+		if (!strncmp(p, key2, key2len)) // is it LOOPPOINT=?
 		{
-			if (strncmp(p++, key1, key1len))
-				continue;
-			p += key1len-1; // skip OOP (the L was skipped in strncmp)
-			if (!strncmp(p, key2, key2len)) // is it LOOPPOINT=?
-			{
-				p += key2len; // skip POINT=
-				loop_point = (float)((44.1L+atoi(p)) / 44100.0L); // LOOPPOINT works by sample count.
-				// because SDL_Mixer is USELESS and can't even tell us
-				// something simple like the frequency of the streaming music,
-				// we are unfortunately forced to assume that ALL MUSIC is 44100hz.
-				// This means a lot of tracks that are only 22050hz for a reasonable downloadable file size will loop VERY badly.
-			}
-			else if (!strncmp(p, key3, key3len)) // is it LOOPMS=?
-			{
-				p += key3len; // skip MS=
-				loop_point = (float)(atoi(p) / 1000.0L); // LOOPMS works by real time, as miliseconds.
-				// Everything that uses LOOPMS will work perfectly with SDL_Mixer.
-			}
-			// Neither?! Continue searching.
+			p += key2len; // skip POINT=
+			loop_point = (float)((44.1L+atoi(p)) / 44100.0L); // LOOPPOINT works by sample count.
+			// because SDL_Mixer is USELESS and can't even tell us
+			// something simple like the frequency of the streaming music,
+			// we are unfortunately forced to assume that ALL MUSIC is 44100hz.
+			// This means a lot of tracks that are only 22050hz for a reasonable downloadable file size will loop VERY badly.
+		}
+		else if (!strncmp(p, key3, key3len)) // is it LOOPMS=?
+		{
+			p += key3len; // skip MS=
+			loop_point = (float)(atoi(p) / 1000.0L); // LOOPMS works by real time, as miliseconds.
+			// Everything that uses LOOPMS will work perfectly with SDL_Mixer.
 		}
+		// Neither?! Continue searching.
+	}
+
+	return true;
+}
+
+void I_UnloadSong(void)
+{
+	I_StopSong();
+
+#ifdef HAVE_LIBGME
+	if (gme)
+	{
+		gme_delete(gme);
+		gme = NULL;
 	}
+#endif
+	if (music)
+	{
+		Mix_FreeMusic(music);
+		music = NULL;
+	}
+}
+
+boolean I_PlaySong(boolean looping)
+{
+#ifdef HAVE_LIBGME
+	if (gme)
+	{
+		gme_start_track(gme, 0);
+		current_track = 0;
+		Mix_HookMusic(mix_gme, gme);
+		return true;
+	}
+	else
+#endif
+	if (!music)
+		return false;
 
 	if (Mix_PlayMusic(music, looping && loop_point == 0.0f ? -1 : 0) == -1)
 	{
 		CONS_Alert(CONS_ERROR, "Mix_PlayMusic: %s\n", Mix_GetError());
-		return true;
+		return false;
 	}
 	Mix_VolumeMusic((UINT32)music_volume*128/31);
 
@@ -685,51 +776,49 @@ boolean I_StartDigSong(const char *musicname, boolean looping)
 	return true;
 }
 
-void I_StopDigSong(void)
+void I_StopSong(void)
 {
-	if (midimode)
-		return;
 #ifdef HAVE_LIBGME
 	if (gme)
 	{
 		Mix_HookMusic(NULL, NULL);
-		gme_delete(gme);
-		gme = NULL;
 		current_track = -1;
-		return;
 	}
 #endif
-	if (!music)
-		return;
-	Mix_HookMusicFinished(NULL);
-	Mix_FreeMusic(music);
-	music = NULL;
+	if (music)
+	{
+		Mix_HookMusicFinished(NULL);
+		Mix_HaltMusic();
+	}
 }
 
-void I_SetDigMusicVolume(UINT8 volume)
+void I_PauseSong(void)
 {
-	music_volume = volume;
-	if (midimode || !music)
-		return;
-	Mix_VolumeMusic((UINT32)volume*128/31);
+	Mix_PauseMusic();
+	songpaused = true;
 }
 
-boolean I_SetSongSpeed(float speed)
+void I_ResumeSong(void)
 {
-	if (speed > 250.0f)
-		speed = 250.0f; //limit speed up to 250x
-#ifdef HAVE_LIBGME
-	if (gme)
-	{
-		SDL_LockAudio();
-		gme_set_tempo(gme, speed);
-		SDL_UnlockAudio();
-		return true;
-	}
-#else
-	(void)speed;
+	Mix_ResumeMusic();
+	songpaused = false;
+}
+
+void I_SetMusicVolume(UINT8 volume)
+{
+	if (!I_SongPlaying())
+		return;
+
+#ifdef _WIN32
+	if (I_SongType() == MU_MID)
+		// HACK: Until we stop using native MIDI,
+		// disable volume changes
+		music_volume = 31;
+	else
 #endif
-	return false;
+		music_volume = volume;
+
+	Mix_VolumeMusic((UINT32)music_volume*128/31);
 }
 
 boolean I_SetSongTrack(int track)
@@ -763,79 +852,4 @@ boolean I_SetSongTrack(int track)
 	return false;
 }
 
-//
-// MIDI Music
-//
-
-FUNCMATH void I_InitMIDIMusic(void)
-{
-}
-
-void I_ShutdownMIDIMusic(void)
-{
-	if (!midimode || !music)
-		return;
-	Mix_FreeMusic(music);
-	music = NULL;
-}
-
-void I_SetMIDIMusicVolume(UINT8 volume)
-{
-	// HACK: Until we stop using native MIDI,
-	// disable volume changes
-	(void)volume;
-	midi_volume = 31;
-	//midi_volume = volume;
-
-	if (!midimode || !music)
-		return;
-	Mix_VolumeMusic((UINT32)midi_volume*128/31);
-}
-
-INT32 I_RegisterSong(void *data, size_t len)
-{
-	music = Mix_LoadMUS_RW(SDL_RWFromMem(data, len), SDL_FALSE);
-	if (!music)
-	{
-		CONS_Alert(CONS_ERROR, "Mix_LoadMUS_RW: %s\n", Mix_GetError());
-		return -1;
-	}
-	return 1337;
-}
-
-boolean I_PlaySong(INT32 handle, boolean looping)
-{
-	(void)handle;
-
-	midimode = true;
-
-	if (Mix_PlayMusic(music, looping ? -1 : 0) == -1)
-	{
-		CONS_Alert(CONS_ERROR, "Mix_PlayMusic: %s\n", Mix_GetError());
-		return false;
-	}
-
-	Mix_VolumeMusic((UINT32)midi_volume*128/31);
-	return true;
-}
-
-void I_StopSong(INT32 handle)
-{
-	if (!midimode || !music)
-		return;
-
-	(void)handle;
-	Mix_HaltMusic();
-}
-
-void I_UnRegisterSong(INT32 handle)
-{
-	if (!midimode || !music)
-		return;
-
-	(void)handle;
-	Mix_FreeMusic(music);
-	music = NULL;
-}
-
-#endif
+#endif
\ No newline at end of file
diff --git a/src/sdl/sdl_sound.c b/src/sdl/sdl_sound.c
index 63b51c625415b043c09dcc8b3b8937af46f5be9c..477f798bdab2cbe4921db576d42720d0caf19245 100644
--- a/src/sdl/sdl_sound.c
+++ b/src/sdl/sdl_sound.c
@@ -194,8 +194,8 @@ static srb2audio_t localdata;
 static void Snd_LockAudio(void) //Alam: Lock audio data and uninstall audio callback
 {
 	if (Snd_Mutex) SDL_LockMutex(Snd_Mutex);
-	else if (nosound) return;
-	else if (nomidimusic && nodigimusic
+	else if (sound_disabled) return;
+	else if (midi_disabled && digital_disabled
 #ifdef HW3SOUND
 	         && hws_mode == HWS_DEFAULT_MODE
 #endif
@@ -208,8 +208,8 @@ static void Snd_LockAudio(void) //Alam: Lock audio data and uninstall audio call
 static void Snd_UnlockAudio(void) //Alam: Unlock audio data and reinstall audio callback
 {
 	if (Snd_Mutex) SDL_UnlockMutex(Snd_Mutex);
-	else if (nosound) return;
-	else if (nomidimusic && nodigimusic
+	else if (sound_disabled) return;
+	else if (midi_disabled && digital_disabled
 #ifdef HW3SOUND
 	         && hws_mode == HWS_DEFAULT_MODE
 #endif
@@ -493,7 +493,7 @@ static inline void I_SetChannels(void)
 
 	INT32 *steptablemid = steptable + 128;
 
-	if (nosound)
+	if (sound_disabled)
 		return;
 
 	// This table provides step widths for pitch parameters.
@@ -604,12 +604,13 @@ void I_FreeSfx(sfxinfo_t * sfx)
 // Pitching (that is, increased speed of playback)
 //  is set, but currently not used by mixing.
 //
-INT32 I_StartSound(sfxenum_t id, UINT8 vol, UINT8 sep, UINT8 pitch, UINT8 priority)
+INT32 I_StartSound(sfxenum_t id, UINT8 vol, UINT8 sep, UINT8 pitch, UINT8 priority, INT32 channel)
 {
 	(void)priority;
 	(void)pitch;
+	(void)channel;
 
-	if (nosound)
+	if (sound_disabled)
 		return 0;
 
 	if (S_sfx[id].data == NULL) return -1;
@@ -989,7 +990,7 @@ FUNCINLINE static ATTRINLINE void I_UpdateStream16M(Uint8 *stream, int len)
 	if (Snd_Mutex) SDL_UnlockMutex(Snd_Mutex);
 }
 
-#ifdef HAVE_LIBGME
+#if 0 //#ifdef HAVE_LIBGME
 static void I_UpdateSteamGME(Music_Emu *emu, INT16 *stream, int len, UINT8 looping)
 {
 	#define GME_BUFFER_LEN 44100*2048
@@ -1049,14 +1050,16 @@ static void SDLCALL I_UpdateStream(void *userdata, Uint8 *stream, int len)
 	else if (audio.channels == 2 && audio.format == AUDIO_S16SYS)
 	{
 		I_UpdateStream16S(stream, len);
-#ifdef HAVE_LIBGME
-		if (userdata)
-		{
-			srb2audio_t *sa_userdata = userdata;
-			if (!sa_userdata->gme_pause)
-				I_UpdateSteamGME(sa_userdata->gme_emu, (INT16 *)stream, len/4, sa_userdata->gme_loop);
-		}
-#endif
+
+		// Crashes! But no matter; this build doesn't play music anyway...
+// #ifdef HAVE_LIBGME
+// 		if (userdata)
+// 		{
+// 			srb2audio_t *sa_userdata = userdata;
+// 			if (!sa_userdata->gme_pause)
+// 				I_UpdateSteamGME(sa_userdata->gme_emu, (INT16 *)stream, len/4, sa_userdata->gme_loop);
+// 		}
+// #endif
 
 	}
 }
@@ -1136,7 +1139,7 @@ static INT32 Init3DSDriver(const char *soName)
 
 void I_ShutdownSound(void)
 {
-	if (nosound || !sound_started)
+	if (sound_disabled || !sound_started)
 		return;
 
 	CONS_Printf("I_ShutdownSound: ");
@@ -1150,7 +1153,7 @@ void I_ShutdownSound(void)
 	}
 #endif
 
-	if (nomidimusic && nodigimusic)
+	if (midi_disabled && digital_disabled)
 		SDL_CloseAudio();
 	CONS_Printf("%s", M_GetText("shut down\n"));
 	sound_started = false;
@@ -1170,7 +1173,7 @@ void I_StartupSound(void)
 	const char *sdrv_name = NULL;
 #endif
 #ifndef HAVE_MIXER
-	nomidimusic = nodigimusic = true;
+	midi_disabled = digital_disabled = true;
 #endif
 
 	memset(channels, 0, sizeof (channels)); //Alam: Clean it
@@ -1213,7 +1216,7 @@ void I_StartupSound(void)
 		audio.samples /= 2;
 	}
 
-	if (nosound)
+	if (sound_disabled)
 		return;
 
 #ifdef HW3SOUND
@@ -1261,7 +1264,7 @@ void I_StartupSound(void)
 		{
 			snddev_t            snddev;
 
-			//nosound = true;
+			//sound_disabled = true;
 			//I_AddExitFunc(I_ShutdownSound);
 			snddev.bps = 16;
 			snddev.sample_rate = audio.freq;
@@ -1288,7 +1291,7 @@ void I_StartupSound(void)
 	if (!musicStarted && SDL_OpenAudio(&audio, &audio) < 0)
 	{
 		CONS_Printf("%s", M_GetText(" couldn't open audio with desired format\n"));
-		nosound = true;
+		sound_disabled = true;
 		return;
 	}
 	else
@@ -1313,13 +1316,11 @@ void I_StartupSound(void)
 // MUSIC API.
 //
 
-void I_ShutdownMIDIMusic(void)
-{
-	nomidimusic = false;
-	if (nodigimusic) I_ShutdownMusic();
-}
+/// ------------------------
+//  MUSIC SYSTEM
+/// ------------------------
 
-#ifdef HAVE_LIBGME
+#if 0 //#ifdef HAVE_LIBGME
 static void I_ShutdownGMEMusic(void)
 {
 	Snd_LockAudio();
@@ -1330,390 +1331,126 @@ static void I_ShutdownGMEMusic(void)
 }
 #endif
 
-void I_ShutdownDigMusic(void)
-{
-	nodigimusic = false;
-	if (nomidimusic) I_ShutdownMusic();
-}
-
-#ifdef HAVE_MIXER
-static boolean LoadSong(void *data, size_t lumplength, size_t selectpos)
+void I_InitMusic(void)
 {
-	FILE *midfile;
-	const char *tempname;
-#ifdef USE_RWOPS
-	if (canuseRW)
-	{
-		SDL_RWops *SDLRW;
-		void *olddata = Smidi[selectpos]; //quick shortcut to set
-
-		Z_Free(olddata); //free old memory
-		Smidi[selectpos] = NULL;
-
-		if (!data)
-			return olddata != NULL; //was there old data?
-
-		SDLRW = SDL_RWFromConstMem(data, (int)lumplength); //new RWops from Z_zone
-		if (!SDLRW) //ERROR while making RWops!
-		{
-			CONS_Printf(M_GetText("Couldn't load music lump: %s\n"), SDL_GetError());
-			Z_Free(data);
-			return false;
-		}
-
-		music[selectpos] = Mix_LoadMUS_RW(SDLRW); // new Mix_Chuck from RWops
-		if (music[selectpos])
-			Smidi[selectpos] = data; //all done
-		else //ERROR while making Mix_Chuck
-		{
-			CONS_Printf(M_GetText("Couldn't load music data: %s\n"), Mix_GetError());
-			Z_Free(data);
-			SDL_RWclose(SDLRW);
-			Smidi[selectpos] = NULL;
-		}
-		return true;
-	}
-#endif
-	tempname = va("%s/%s", MIDI_PATH, fmidi[selectpos]);
-
-	if (!data)
-	{
-		if (FIL_FileExists(tempname))
-			return unlink(tempname)+1;
-#ifdef MIDI_PATH2
-		else if (FIL_FileExists(tempname = va("%s/%s", MIDI_PATH2, fmidi[selectpos])))
-			return unlink(tempname)+1;
-#endif
-		else
-			return false;
-	}
-
-	midfile = fopen(tempname, "wb");
-
-#ifdef MIDI_PATH2
-	if (!midfile)
-	{
-		tempname = va("%s/%s", MIDI_PATH2, fmidi[selectpos]);
-		midfile = fopen(tempname, "wb");
-	}
+#if 0 //#ifdef HAVE_LIBGME
+	I_AddExitFunc(I_ShutdownGMEMusic);
 #endif
+}
 
-	if (!midfile)
-	{
-		CONS_Printf(M_GetText("Couldn't open file %s to write music in\n"), tempname);
-		Z_Free(data);
-		return false;
-	}
-
-	if (fwrite(data, lumplength, 1, midfile) == 0)
-	{
-		CONS_Printf(M_GetText("Couldn't write music into file %s because %s\n"), tempname, strerror(ferror(midfile)));
-		Z_Free(data);
-		fclose(midfile);
-		return false;
-	}
-
-	fclose(midfile);
+void I_ShutdownMusic(void) { }
 
-	Z_Free(data);
+/// ------------------------
+//  MUSIC PROPERTIES
+/// ------------------------
 
-	music[selectpos] = Mix_LoadMUS(tempname);
-	if (!music[selectpos]) //ERROR while making Mix_Chuck
-	{
-		CONS_Printf(M_GetText("Couldn't load music file %s: %s\n"), tempname, Mix_GetError());
-		return false;
-	}
-	return true;
+musictype_t I_SongType(void)
+{
+	return MU_NONE;
 }
-#endif
 
+boolean I_SongPlaying(void)
+{
+	return false;
+}
 
-void I_ShutdownMusic(void)
+boolean I_SongPaused(void)
 {
-#ifdef HAVE_MIXER
-	if ((nomidimusic && nodigimusic) || !musicStarted)
-		return;
+	return false;
+}
 
-	CONS_Printf("%s", M_GetText("I_ShutdownMusic: "));
+/// ------------------------
+//  MUSIC EFFECTS
+/// ------------------------
 
-	I_UnRegisterSong(0);
-	I_StopDigSong();
-	Mix_CloseAudio();
-#ifdef MIX_INIT
-	Mix_Quit();
-#endif
-	CONS_Printf("%s", M_GetText("shut down\n"));
-	musicStarted = SDL_FALSE;
-	if (Msc_Mutex)
-		SDL_DestroyMutex(Msc_Mutex);
-	Msc_Mutex = NULL;
-#endif
+boolean I_SetSongSpeed(float speed)
+{
+	(void)speed;
+	return false;
 }
 
-void I_InitMIDIMusic(void)
+/// ------------------------
+//  MUSIC PLAYBACK
+/// ------------------------
+
+#if 0 //#ifdef HAVE_LIBGME
+static void I_StopGME(void)
 {
-	if (nodigimusic) I_InitMusic();
+	Snd_LockAudio();
+	gme_seek(localdata.gme_emu, 0);
+	Snd_UnlockAudio();
 }
 
-void I_InitDigMusic(void)
+static void I_PauseGME(void)
 {
-	if (nomidimusic) I_InitMusic();
+	localdata.gme_pause = true;
 }
 
-void I_InitMusic(void)
+static void I_ResumeGME(void)
 {
-#ifdef HAVE_MIXER
-	char ad[100];
-	SDL_version MIXcompiled;
-	const SDL_version *MIXlinked;
-#ifdef MIXER_INIT
-	const int mixstart = MIX_INIT_OGG;
-	int mixflags;
-#endif
-#endif
-#ifdef HAVE_LIBGME
-	I_AddExitFunc(I_ShutdownGMEMusic);
-#endif
-
-#ifdef HAVE_MIXER
-	MIX_VERSION(&MIXcompiled)
-	MIXlinked = Mix_Linked_Version();
-	I_OutputMsg("Compiled for SDL_mixer version: %d.%d.%d\n",
-	            MIXcompiled.major, MIXcompiled.minor, MIXcompiled.patch);
-#ifdef MIXER_POS
-	if (MIXlinked->major == 1 && MIXlinked->minor == 2 && MIXlinked->patch < 7)
-		canlooping = SDL_FALSE;
-#endif
-#ifdef USE_RWOPS
-	if (M_CheckParm("-noRW"))
-		canuseRW = SDL_FALSE;
-#endif
-	I_OutputMsg("Linked with SDL_mixer version: %d.%d.%d\n",
-	            MIXlinked->major, MIXlinked->minor, MIXlinked->patch);
-	if (audio.freq < 44100 && !M_CheckParm ("-freq")) //I want atleast 44Khz
-	{
-		audio.samples = (Uint16)(audio.samples*(INT32)(44100/audio.freq));
-		audio.freq = 44100; //Alam: to keep it around the same XX ms
-	}
-
-	if (sound_started
-#ifdef HW3SOUND
-		&& hws_mode == HWS_DEFAULT_MODE
-#endif
-		)
-	{
-		I_OutputMsg("Temp Shutdown of SDL Audio System");
-		SDL_CloseAudio();
-		I_OutputMsg(" Done\n");
-	}
-
-	CONS_Printf("%s", M_GetText("I_InitMusic:"));
-
-#ifdef MIXER_INIT
-	mixflags = Mix_Init(mixstart);
-	if ((mixstart & MIX_INIT_FLAC) != (mixflags & MIX_INIT_FLAC))
-	{
-		CONS_Printf("%s", M_GetText(" Unable to load FLAC support\n"));
-	}
-	if ((mixstart & MIX_INIT_MOD ) != (mixflags & MIX_INIT_MOD ))
-	{
-		CONS_Printf("%s", M_GetText(" Unable to load MOD support\n"));
-	}
-	if ((mixstart & MIX_INIT_MP3 ) != (mixflags & MIX_INIT_MP3 ))
-	{
-		CONS_Printf("%s", M_GetText(" Unable to load MP3 support\n"));
-	}
-	if ((mixstart & MIX_INIT_OGG ) != (mixflags & MIX_INIT_OGG ))
-	{
-		CONS_Printf("%s", M_GetText(" Unable to load OGG support\n"));
-	}
-#endif
-
-	if (Mix_OpenAudio(audio.freq, audio.format, audio.channels, audio.samples) < 0) //open_music(&audio)
-	{
-		CONS_Printf(M_GetText(" Unable to open music: %s\n"), Mix_GetError());
-		nomidimusic = nodigimusic = true;
-		if (sound_started
-#ifdef HW3SOUND
-			&& hws_mode == HWS_DEFAULT_MODE
-#endif
-			)
-		{
-			if (SDL_OpenAudio(&audio, NULL) < 0) //retry
-			{
-				CONS_Printf("%s", M_GetText(" couldn't open audio with desired format\n"));
-				nosound = true;
-				sound_started = false;
-			}
-			else
-			{
-				CONS_Printf(M_GetText(" Starting with audio driver : %s\n"), SDL_AudioDriverName(ad, (int)sizeof ad));
-			}
-		}
-		return;
-	}
-	else
-		CONS_Printf(M_GetText(" Starting up with audio driver : %s with SDL_Mixer\n"), SDL_AudioDriverName(ad, (int)sizeof ad));
-
-	samplecount = audio.samples;
-	CV_SetValue(&cv_samplerate, audio.freq);
-	if (sound_started
-#ifdef HW3SOUND
-		&& hws_mode == HWS_DEFAULT_MODE
-#endif
-		)
-		I_OutputMsg(" Reconfigured SDL Audio System");
-	else I_OutputMsg(" Configured SDL_Mixer System");
-	I_OutputMsg(" with %d samples/slice at %ikhz(%dms buffer)\n", samplecount, audio.freq/1000, (INT32) ((audio.samples * 1000.0f) / audio.freq));
-	Mix_SetPostMix(audio.callback, audio.userdata);  // after mixing music, add sound effects
-	Mix_Resume(-1);
-	CONS_Printf("%s", M_GetText("Music initialized\n"));
-	musicStarted = SDL_TRUE;
-	Msc_Mutex = SDL_CreateMutex();
-#endif
+	localdata.gme_pause = false;
 }
+#endif
 
-boolean I_PlaySong(INT32 handle, boolean looping)
+boolean I_LoadSong(char *data, size_t len)
 {
-	(void)handle;
-#ifdef HAVE_MIXER
-	if (nomidimusic || !musicStarted || !music[handle])
-		return false;
+	return false;
+}
 
-#ifdef MIXER_POS
-	if (canlooping)
-		Mix_HookMusicFinished(NULL);
-#endif
+void I_UnloadSong(void) { }
 
-	if (Mix_FadeInMusic(music[handle], looping ? -1 : 0, MIDIfade) == -1)
-		CONS_Printf(M_GetText("Couldn't play song because %s\n"), Mix_GetError());
-	else
-	{
-		Mix_VolumeMusic(musicvol);
-		return true;
-	}
-#else
+boolean I_PlaySong(boolean looping)
+{
 	(void)looping;
-#endif
 	return false;
 }
 
-static void I_PauseGME(void)
+void I_StopSong(void)
 {
-#ifdef HAVE_LIBGME
-	localdata.gme_pause = true;
+#if 0 //#ifdef HAVE_LIBGME
+	I_StopGME();
 #endif
 }
 
-void I_PauseSong(INT32 handle)
+void I_PauseSong(void)
 {
-	(void)handle;
+#if 0 //#ifdef HAVE_LIBGME
 	I_PauseGME();
-#ifdef HAVE_MIXER
-	if ((nomidimusic && nodigimusic) || !musicStarted)
-		return;
-
-	Mix_PauseMusic();
-	//I_StopSong(handle);
-#endif
-}
-
-static void I_ResumeGME(void)
-{
-#ifdef HAVE_LIBGME
-	localdata.gme_pause = false;
 #endif
 }
 
-void I_ResumeSong(INT32 handle)
+void I_ResumeSong(void)
 {
-	(void)handle;
+#if 0
 	I_ResumeGME();
-#ifdef HAVE_MIXER
-	if ((nomidimusic && nodigimusic) || !musicStarted)
-		return;
-
-	Mix_VolumeMusic(musicvol);
-	Mix_ResumeMusic();
-	//I_PlaySong(handle, true);
 #endif
 }
 
-void I_StopSong(INT32 handle)
+void I_SetMusicVolume(UINT8 volume)
 {
-	(void)handle;
-#ifdef HAVE_MIXER
-	if (nomidimusic || !musicStarted)
-		return;
-	Mix_FadeOutMusic(MIDIfade);
-#endif
-}
-
-void I_UnRegisterSong(INT32 handle)
-{
-#ifdef HAVE_MIXER
-
-	if (nomidimusic || !musicStarted)
-		return;
-
-	Mix_HaltMusic();
-	while (Mix_PlayingMusic())
-		;
-
-	if (music[handle])
-		Mix_FreeMusic(music[handle]);
-	music[handle] = NULL;
-	LoadSong(NULL, 0, handle);
-#else
-	(void)handle;
-#endif
+	(void)volume;
 }
 
-INT32 I_RegisterSong(void *data, size_t len)
+boolean I_SetSongTrack(int track)
 {
-#ifdef HAVE_MIXER
-	if (nomidimusic || !musicStarted)
-		return false;
-
-	if (!LoadSong(data, len, 0))
-		return false;
-
-	if (music[0])
-		return true;
-
-	CONS_Printf(M_GetText("Couldn't load MIDI: %s\n"), Mix_GetError());
-#else
-	(void)len;
-	(void)data;
-#endif
+	(void)track;
 	return false;
 }
 
-void I_SetMIDIMusicVolume(UINT8 volume)
-{
-#ifdef HAVE_MIXER
-	if ((nomidimusic && nodigimusic) || !musicStarted)
-		return;
-
-	if (Msc_Mutex) SDL_LockMutex(Msc_Mutex);
-	musicvol = volume * 2;
-	if (Msc_Mutex) SDL_UnlockMutex(Msc_Mutex);
-	Mix_VolumeMusic(musicvol);
-#else
-	(void)volume;
-#endif
-}
+/// ------------------------
+//  MUSIC LOADING AND CLEANUP
+//  \todo Split logic between loading and playing,
+//        then move to Playback section
+/// ------------------------
 
-#ifdef HAVE_LIBGME
+#if 0 //#ifdef HAVE_LIBGME
 static void I_CleanupGME(void *userdata)
 {
 	Z_Free(userdata);
 }
-#endif
 
 static boolean I_StartGMESong(const char *musicname, boolean looping)
 {
-#ifdef HAVE_LIBGME
 	char filename[9];
 	void *data;
 	lumpnum_t lumpnum;
@@ -1759,240 +1496,7 @@ static boolean I_StartGMESong(const char *musicname, boolean looping)
 	Snd_UnlockAudio();
 
 	return true;
-#else
-	(void)musicname;
-	(void)looping;
-#endif
-	return false;
 }
-
-boolean I_StartDigSong(const char *musicname, boolean looping)
-{
-#ifdef HAVE_MIXER
-	char filename[9];
-	void *data;
-	lumpnum_t lumpnum;
-	size_t lumplength;
 #endif
 
-	if(I_StartGMESong(musicname, looping))
-		return true;
-
-#ifdef HAVE_MIXER
-	if (nodigimusic)
-		return false;
-
-	snprintf(filename, sizeof filename, "o_%s", musicname);
-
-	lumpnum = W_CheckNumForName(filename);
-
-	I_StopDigSong();
-
-	if (lumpnum == LUMPERROR)
-	{
-		// Alam_GBC: like in win32/win_snd.c: Graue 02-29-2004: don't worry about missing music, there might still be a MIDI
-		//I_OutputMsg("Music lump %s not found!\n", filename);
-		return false; // No music found. Oh well!
-	}
-	else
-		lumplength = W_LumpLength(lumpnum);
-
-	data = W_CacheLumpNum(lumpnum, PU_MUSIC);
-
-	if (Msc_Mutex) SDL_LockMutex(Msc_Mutex);
-
-#ifdef MIXER_POS
-	if (canlooping && (loopingDig = looping) == SDL_TRUE && strcmp(data, "OggS")  == 0)
-		looping = false; // Only on looping Ogg files, will we will do our own looping
-
-	// Scan the Ogg Vorbis file for the COMMENT= field for a custom
-	// loop point
-	if (!looping && loopingDig)
-	{
-		size_t scan;
-		const char *dataum = data;
-		char looplength[64];
-		UINT32 loopstart = 0;
-		UINT8 newcount = 0;
-
-		Mix_HookMusicFinished(I_FinishMusic);
-
-		for (scan = 0; scan < lumplength; scan++)
-		{
-			if (*dataum++ == 'C'){
-			if (*dataum++ == 'O'){
-			if (*dataum++ == 'M'){
-			if (*dataum++ == 'M'){
-			if (*dataum++ == 'E'){
-			if (*dataum++ == 'N'){
-			if (*dataum++ == 'T'){
-			if (*dataum++ == '='){
-			if (*dataum++ == 'L'){
-			if (*dataum++ == 'O'){
-			if (*dataum++ == 'O'){
-			if (*dataum++ == 'P'){
-			if (*dataum++ == 'P'){
-			if (*dataum++ == 'O'){
-			if (*dataum++ == 'I'){
-			if (*dataum++ == 'N'){
-			if (*dataum++ == 'T'){
-			if (*dataum++ == '=')
-			{
-
-				while (*dataum != 1 && newcount != 63)
-					looplength[newcount++] = *dataum++;
-
-				looplength[newcount] = '\0';
-
-				loopstart = atoi(looplength);
-
-			}
-			else
-				dataum--;}
-			else
-				dataum--;}
-			else
-				dataum--;}
-			else
-				dataum--;}
-			else
-				dataum--;}
-			else
-				dataum--;}
-			else
-				dataum--;}
-			else
-				dataum--;}
-			else
-				dataum--;}
-			else
-				dataum--;}
-			else
-				dataum--;}
-			else
-				dataum--;}
-			else
-				dataum--;}
-			else
-				dataum--;}
-			else
-				dataum--;}
-			else
-				dataum--;}
-			else
-				dataum--;}
-		}
-
-		if (loopstart > 0)
-		{
-			loopstartDig = (double)((44.1l+loopstart) / 44100.0l); //8 PCM chucks off and PCM to secs
-//#ifdef PARANOIA
-			//I_OutputMsg("I_StartDigSong: setting looping point to %ul PCMs(%f seconds)\n", loopstart, loopstartDig);
-//#endif
-		}
-		else
-		{
-			looping = true; // loopingDig true, but couldn't find start loop point
-		}
-	}
-	else
-		loopstartDig = 0.0l;
-#else
-	if (looping && strcmp(data, "OggS")  == 0)
-		I_OutputMsg("I_StartDigSong: SRB2 was not compiled with looping music support(no Mix_FadeInMusicPos)\n");
-#endif
-
-	if (!LoadSong(data, lumplength, 1))
-	{
-		if (Msc_Mutex) SDL_UnlockMutex(Msc_Mutex);
-		return false;
-	}
-
-	// Note: LoadSong() frees the data. Let's make sure
-	// we don't try to use the data again.
-	data = NULL;
-
-	if (Mix_FadeInMusic(music[1], looping ? -1 : 0, Digfade) == -1)
-	{
-		if (Msc_Mutex) SDL_UnlockMutex(Msc_Mutex);
-		I_OutputMsg("I_StartDigSong: Couldn't play song %s because %s\n", musicname, Mix_GetError());
-		return false;
-	}
-	Mix_VolumeMusic(musicvol);
-
-	if (Msc_Mutex) SDL_UnlockMutex(Msc_Mutex);
-	return true;
-#else
-	(void)looping;
-	(void)musicname;
-	return false;
-#endif
-}
-
-static void I_StopGME(void)
-{
-#ifdef HAVE_LIBGME
-	Snd_LockAudio();
-	gme_seek(localdata.gme_emu, 0);
-	Snd_UnlockAudio();
-#endif
-}
-
-void I_StopDigSong(void)
-{
-	I_StopGME();
-#ifdef HAVE_MIXER
-	if (nodigimusic)
-		return;
-
-#ifdef MIXER_POS
-	if (canlooping)
-		Mix_HookMusicFinished(NULL);
-#endif
-
-	Mix_HaltMusic();
-	while (Mix_PlayingMusic())
-		;
-
-	if (music[1])
-		Mix_FreeMusic(music[1]);
-	music[1] = NULL;
-	LoadSong(NULL, 0, 1);
-#endif
-}
-
-void I_SetDigMusicVolume(UINT8 volume)
-{
-	I_SetMIDIMusicVolume(volume);
-}
-
-boolean I_SetSongSpeed(float speed)
-{
-
-	(void)speed;
-	return false;
-}
-
-boolean I_SetSongTrack(int track)
-{
-	(void)track;
-	return false;
-}
-
-#ifdef MIXER_POS
-static void SDLCALL I_FinishMusic(void)
-{
-	if (!music[1])
-		return;
-	else if (Msc_Mutex) SDL_LockMutex(Msc_Mutex);
-//		I_OutputMsg("I_FinishMusic: Loopping song to %g seconds\n", loopstartDig);
-
-	if (Mix_FadeInMusicPos(music[1], loopstartDig ? 0 : -1, Digfade, loopstartDig) == 0)
-		Mix_VolumeMusic(musicvol);
-	else
-		I_OutputMsg("I_FinishMusic: Couldn't loop song because %s\n", Mix_GetError());
-
-	if (Msc_Mutex) SDL_UnlockMutex(Msc_Mutex);
-}
-#endif
 #endif //HAVE_SDL
diff --git a/src/sounds.c b/src/sounds.c
index 293ce381d4867d33e9a8ae9d1f76c07a5d659fb6..35b21e590ca6563f5d1622e7df671e40212a5b5b 100644
--- a/src/sounds.c
+++ b/src/sounds.c
@@ -65,7 +65,7 @@ sfxinfo_t S_sfx[NUMSFX] =
   {"buzz1",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Electric zap"},
   {"buzz2",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Electric zap"},
   {"buzz3",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Wacky worksurface"},
-  {"buzz4",  false,   8,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Buzz"},
+  {"buzz4",   true,   8,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Buzz"},
   {"crumbl",  true, 127,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Crumbling"}, // Platform Crumble Tails 03-16-2001
   {"fire",   false,   8, 32, -1, NULL, 0,        -1,  -1, LUMPERROR, "Flamethrower"},
   {"grind",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Metallic grinding"},
@@ -76,6 +76,8 @@ sfxinfo_t S_sfx[NUMSFX] =
   {"steam1", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Steam jet"}, // Tails 06-19-2001
   {"steam2", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Steam jet"}, // Tails 06-19-2001
   {"wbreak", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Wood breaking"},
+  {"ambmac", false,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Machinery"},
+  {"spsmsh", false,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Heavy impact"},
 
   {"rainin",  true,  24,  4, -1, NULL, 0,        -1,  -1, LUMPERROR, "Rain"},
   {"litng1", false,  16,  2, -1, NULL, 0,        -1,  -1, LUMPERROR, "Lightning"},
@@ -139,14 +141,14 @@ sfxinfo_t S_sfx[NUMSFX] =
   {"bnce1",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Bounce"}, // Boing!
   {"bnce2",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Scatter"}, // Boing!
   {"cannon", false,  64,  8, -1, NULL, 0,        -1,  -1, LUMPERROR, "Powerful shot"},
-  {"cgot" ,   true, 120,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Got Chaos Emerald"}, // Got Emerald! Tails 09-02-2001
+  {"cgot" ,   true, 120,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Got Emerald"}, // Got Emerald! Tails 09-02-2001
   {"cybdth", false,  32,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Explosion"},
-  {"deton",   true,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Ominous beeping"},
+  {"deton",   true,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Threatening beeping"},
   {"ding",   false, 127,  8, -1, NULL, 0,        -1,  -1, LUMPERROR, "Ding"},
   {"dmpain", false,  96,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Machine damage"},
   {"drown",  false, 192,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Drowning"},
   {"fizzle", false, 127,  8, -1, NULL, 0,        -1,  -1, LUMPERROR, "Electric fizzle"},
-  {"gbeep",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Ominous beeping"}, // Grenade beep
+  {"gbeep",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Threatening beeping"}, // Grenade beep
   {"wepfir", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Firing weapon"}, // defaults to thok
   {"ghit" ,  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Goop splash"},
   {"gloop",  false,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Splash"},
@@ -177,15 +179,17 @@ sfxinfo_t S_sfx[NUMSFX] =
   {"spring", false, 112,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Spring"},
   {"statu1",  true,  64,  2, -1, NULL, 0,        -1,  -1, LUMPERROR, "Pushing a statue"},
   {"statu2",  true,  64,  2, -1, NULL, 0,        -1,  -1, LUMPERROR, "Pushing a statue"},
-  {"strpst",  true, 192,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Starpost"}, // Starpost Sound Tails 07-04-2002
+  {"strpst",  true, 192,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Starpost"},
   {"supert",  true, 127,  2, -1, NULL, 0,        -1,  -1, LUMPERROR, "Transformation"},
   {"telept", false,  32,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Dash"},
   {"tink" ,  false,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Tink"},
-  {"token" ,  true, 224,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Got Token"}, // SS token
+  {"token" ,  true, 224,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Got Token"},
   {"trfire",  true,  60,  8, -1, NULL, 0,        -1,  -1, LUMPERROR, "Laser fired"},
   {"trpowr",  true, 127,  8, -1, NULL, 0,        -1,  -1, LUMPERROR, "Powering up"},
   {"turhit", false,  40,  8, -1, NULL, 0,        -1,  -1, LUMPERROR, "Laser hit"},
   {"wdjump", false,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Whirlwind jump"},
+  {"shrpsp",  true,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Spincushion"},
+  {"shrpgo", false,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Launch"},
   {"mswarp", false,  60, 16, -1, NULL, 0,        -1,  -1, LUMPERROR, "Spinning out"},
   {"mspogo", false,  60,  8, -1, NULL, 0,        -1,  -1, LUMPERROR, "Breaking through"},
   {"boingf", false,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Bouncing"},
@@ -210,11 +214,12 @@ sfxinfo_t S_sfx[NUMSFX] =
   {"xideya", false, 127,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Success"}, // Xmas
   {"nbmper", false,  96,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Bumper"},
   {"nxbump", false,  96,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Bumper"}, // Xmas
+  {"ncchip", false, 204,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Got chip"},
   {"ncitem", false, 204,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Got special"},
   {"nxitem", false, 204,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Got special"}, // Xmas
   {"ngdone",  true, 127,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Bonus time start"},
   {"nxdone",  true, 127,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Bonus time start"}, // Xmas
-  {"drill1", false,  48,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Drill start"},
+  {"drill1", false,  48,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Drill"},
   {"drill2", false,  48,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Drill"},
   {"ncspec", false, 204,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Power-up"}, // Tails 12-15-2003
   {"nghurt", false,  96,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Hurt"},
@@ -224,19 +229,26 @@ sfxinfo_t S_sfx[NUMSFX] =
   {"hoop3",  false, 192,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Hoop++"},
   {"hidden", false, 204,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Discovery"},
   {"prloop", false, 104,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Gust of wind"},
-  {"timeup",  true, 256,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Ominous Countdown"},
+  {"ngjump", false,  96,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Jump"},
+  {"peww",   false,  96,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Pew"},
+
+  // Halloween
+  {"lntsit", false,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Cacolantern awake"},
+  {"lntdie", false,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Cacolantern death"},
+  {"pumpkn", false,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Pumpkin smash"}, // idspispopd
+  {"ghosty", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Laughter"},
 
   // Mario
   {"koopfr" , true, 127,  8, -1, NULL, 0,        -1,  -1, LUMPERROR, "Fire"},
-  {"mario1", false,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Hitting a ceiling"},
-  {"mario2", false, 127,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Koopa shell"},
+  {"mario1", false,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Hit"},
+  {"mario2", false, 127,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Bonk"},
   {"mario3", false,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Power-up"},
   {"mario4",  true,  78,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Got coin"},
-  {"mario5", false,  78,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Boot"},
+  {"mario5", false,  78,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Boot-stomp"},
   {"mario6", false,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Jump"},
   {"mario7", false,  32,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Fire"},
   {"mario8", false,  48,  8, -1, NULL, 0,        -1,  -1, LUMPERROR, "Hurt"},
-  {"mario9",  true, 120,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Emerging"},
+  {"mario9",  true, 120,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Emerging power-up"},
   {"marioa",  true, 192,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "One-up"},
   {"thwomp",  true, 127,  8, -1, NULL, 0,        -1,  -1, LUMPERROR, "Thwomp"},
 
@@ -287,7 +299,7 @@ sfxinfo_t S_sfx[NUMSFX] =
   {"s3k3d",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Pop"},
   {"s3k3e",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Flame Shield"},
   {"s3k3f",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Bubble Shield"},
-  {"s3k40",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Attraction shot"},
+  {"s3k40",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Attraction blast"},
   {"s3k41",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Lightning Shield"},
   {"s3k42",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Twinspin"},
   {"s3k43",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Flame burst"},
@@ -295,22 +307,22 @@ sfxinfo_t S_sfx[NUMSFX] =
   {"s3k45",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Lightning zap"},
   {"s3k46",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Transformation"},
   {"s3k47",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Rising dust"},
-  {"s3k48",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Metallic clink"},
-  {"s3k49",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Falling rock"},
+  {"s3k48",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Pulse"},
+  {"s3k49",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Impact"},
   {"s3k4a",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Grab"},
   {"s3k4b",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Water splash"},
   {"s3k4c",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Heavy hit"},
   {"s3k4d",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Firing bullet"},
-  {"s3k4e",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Bomb explosion"},
+  {"s3k4e",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Big explosion"},
   {"s3k4f",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Flamethrower"},
   {"s3k50",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Siren"},
-  {"s3k51",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Falling bomb"},
+  {"s3k51",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Falling hazard"},
   {"s3k52",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Spike"},
   {"s3k53",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Powering up"},
   {"s3k54",  false,  64, 64, -1, NULL, 0,        -1,  -1, LUMPERROR, "Firing"}, // MetalSonic shot fire
   {"s3k55",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Mechanical movement"},
   {"s3k56",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Heavy landing"},
-  {"s3k57",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Splash"},
+  {"s3k57",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Burst"},
   {"s3k58",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Mechanical movement"},
   {"s3k59",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Crumbling"},
   {"s3k5a",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Aiming"},
@@ -324,13 +336,13 @@ sfxinfo_t S_sfx[NUMSFX] =
   {"s3k62",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Jump"},
   {"s3k63",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Starpost"},
   {"s3k64",  false,  64,  2, -1, NULL, 0,        -1,  -1, LUMPERROR, "Clatter"},
-  {"s3k65",  false, 255,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Got blue sphere"}, // Blue Spheres
-  {"s3k66",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Special stage clear"},
+  {"s3k65",  false, 255,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Got sphere"}, // Blue Spheres
+  {"s3k66",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Special stage end"},
   {"s3k67",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Firing missile"},
-  {"s3k68",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Unknown possibilities"},
+  {"s3k68",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Discovery"},
   {"s3k69",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Switch click"},
   {"s3k6a",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Special stage clear"},
-  {"s3k6b",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
+  {"s3k6b",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Punch"},
   {"s3k6c",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Burst"},
   {"s3k6d",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
   {"s3k6e",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Mechanical damage"},
@@ -363,16 +375,16 @@ sfxinfo_t S_sfx[NUMSFX] =
   {"s3k89",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Advanced technology"},
   {"s3k8a",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Boing"},
   {"s3k8b",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Powerful hit"},
-  {"s3k8c",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
+  {"s3k8c",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Humming power"},
   {"s3k8d",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
-  {"s3k8e",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
+  {"s3k8e",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Accelerating"},
   {"s3k8f",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Opening"},
   {"s3k90",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Impact"},
   {"s3k91",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Closed"},
   {"s3k92",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Ghost"},
   {"s3k93",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Rebuilding"},
   {"s3k94",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Spike"},
-  {"s3k95",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Rising from lava"},
+  {"s3k95",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Lava burst"},
   {"s3k96",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Falling object"},
   {"s3k97",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Wind"},
   {"s3k98",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Falling spike"},
@@ -429,8 +441,8 @@ sfxinfo_t S_sfx[NUMSFX] =
   {"s3kc3l", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Levitation"}, // ditto
   {"s3kc4s", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Firing laser"},
   {"s3kc4l", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Firing laser"}, // ditto
-  {"s3kc5s", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
-  {"s3kc5l", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""}, // ditto
+  {"s3kc5s", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Revving up"},
+  {"s3kc5l", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Revving up"}, // ditto
   {"s3kc6s", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Orbiting"},
   {"s3kc6l", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Orbiting"}, // ditto
   {"s3kc7",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Aiming"},
@@ -455,7 +467,7 @@ sfxinfo_t S_sfx[NUMSFX] =
   {"s3kd1s", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
   {"s3kd1l", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""}, // ditto
   {"s3kd2s", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Turning"},
-  {"s3kd2l", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Turning"}, // ditto
+  {"s3kd2l", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Moving chain"}, // ditto
   {"s3kd3s", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
   {"s3kd3l", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""}, // ditto
   {"s3kd4s", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Engine"},
diff --git a/src/sounds.h b/src/sounds.h
index 60451b6ea8e3475ab0aac20760678c1611b648ef..5fffc7b2e4f35b93fa3eb9bf26ef7d0368428ac9 100644
--- a/src/sounds.h
+++ b/src/sounds.h
@@ -142,6 +142,8 @@ typedef enum
 	sfx_steam1,
 	sfx_steam2,
 	sfx_wbreak,
+	sfx_ambmac,
+	sfx_spsmsh,
 
 	sfx_rainin,
 	sfx_litng1,
@@ -252,6 +254,8 @@ typedef enum
 	sfx_trpowr,
 	sfx_turhit,
 	sfx_wdjump,
+	sfx_shrpsp,
+	sfx_shrpgo,
 	sfx_mswarp,
 	sfx_mspogo,
 	sfx_boingf,
@@ -276,6 +280,7 @@ typedef enum
 	sfx_xideya, // Xmas
 	sfx_nbmper,
 	sfx_nxbump, // Xmas
+	sfx_ncchip,
 	sfx_ncitem,
 	sfx_nxitem, // Xmas
 	sfx_ngdone,
@@ -290,7 +295,14 @@ typedef enum
 	sfx_hoop3,
 	sfx_hidden,
 	sfx_prloop,
-	sfx_timeup, // Was gonna be played when less than ten seconds are on the clock; uncomment uses of this to see it in-context
+	sfx_ngjump,
+	sfx_peww,
+
+	// Halloween
+	sfx_lntsit,
+	sfx_lntdie,
+	sfx_pumpkn,
+	sfx_ghosty,
 
 	// Mario
 	sfx_koopfr,
diff --git a/src/st_stuff.c b/src/st_stuff.c
index d5cc9cf29da58ddd68505ba895517a5d95b8e6d0..fa13e008a05b346766ead91d43c66a1b4af182b6 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -79,7 +79,6 @@ static patch_t *race1;
 static patch_t *race2;
 static patch_t *race3;
 static patch_t *racego;
-static patch_t *ttlnum;
 static patch_t *nightslink;
 static patch_t *curweapon;
 static patch_t *normring;
@@ -112,6 +111,8 @@ static patch_t *yelstat;
 static patch_t *nbracket;
 static patch_t *nhud[12];
 static patch_t *nsshud;
+static patch_t *nbon[12];
+static patch_t *nssbon;
 static patch_t *narrow[9];
 static patch_t *nredar[8]; // Red arrow
 static patch_t *drillbar;
@@ -309,8 +310,12 @@ void ST_LoadGraphics(void)
 	yelstat = W_CachePatchName("YELSTAT", PU_HUDGFX);
 	nbracket = W_CachePatchName("NBRACKET", PU_HUDGFX);
 	for (i = 0; i < 12; ++i)
+	{
 		nhud[i] = W_CachePatchName(va("NHUD%d", i+1), PU_HUDGFX);
+		nbon[i] = W_CachePatchName(va("NBON%d", i+1), PU_HUDGFX);
+	}
 	nsshud = W_CachePatchName("NSSHUD", PU_HUDGFX);
+	nssbon = W_CachePatchName("NSSBON", PU_HUDGFX);
 	minicaps = W_CachePatchName("MINICAPS", PU_HUDGFX);
 
 	for (i = 0; i < 8; ++i)
@@ -659,9 +664,9 @@ static void ST_drawTime(void)
 	else
 	{
 		// Counting down the hidetime?
-		if ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK) && (leveltime <= (hidetime*TICRATE)))
+		if ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK) && (stplyr->realtime <= (hidetime*TICRATE)))
 		{
-			tics = (hidetime*TICRATE - leveltime);
+			tics = (hidetime*TICRATE - stplyr->realtime);
 			if (tics < 3*TICRATE)
 				ST_drawRaceNum(tics);
 			downwards = true;
@@ -669,15 +674,15 @@ static void ST_drawTime(void)
 		else
 		{
 			// Hidetime finish!
-			if ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK) && (leveltime < ((hidetime+1)*TICRATE)))
-				ST_drawRaceNum(hidetime*TICRATE - leveltime);
+			if ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK) && (stplyr->realtime < ((hidetime+1)*TICRATE)))
+				ST_drawRaceNum(hidetime*TICRATE - stplyr->realtime);
 
 			// Time limit?
-			if (gametype != GT_RACE && gametype != GT_COMPETITION && gametype != GT_COOP && cv_timelimit.value && timelimitintics > 0)
+			if (gametype != GT_COOP && gametype != GT_RACE && gametype != GT_COMPETITION && cv_timelimit.value && timelimitintics > 0)
 			{
-				if (timelimitintics >= leveltime)
+				if (timelimitintics >= stplyr->realtime)
 				{
-					tics = (timelimitintics - leveltime);
+					tics = (timelimitintics - stplyr->realtime);
 					if (tics < 3*TICRATE)
 						ST_drawRaceNum(tics);
 				}
@@ -735,18 +740,7 @@ static inline void ST_drawRings(void)
 
 	ST_DrawPatchFromHud(HUD_RINGS, ((!stplyr->spectator && stplyr->rings <= 0 && leveltime/5 & 1) ? sboredrings : sborings), ((stplyr->spectator) ? V_HUDTRANSHALF : V_HUDTRANS));
 
-	if (objectplacing)
-		ringnum = op_currentdoomednum;
-	else if (!useNightsSS && G_IsSpecialStage(gamemap))
-	{
-		INT32 i;
-		ringnum = 0;
-		for (i = 0; i < MAXPLAYERS; i++)
-			if (playeringame[i] && players[i].mo && players[i].rings > 0)
-				ringnum += players[i].rings;
-	}
-	else
-		ringnum = max(stplyr->rings, 0);
+	ringnum = ((objectplacing) ? op_currentdoomednum : max(stplyr->rings, 0));
 
 	if (cv_timetic.value == 2) // Yes, even in modeattacking
 		ST_DrawNumFromHud(HUD_RINGSNUMTICS, ringnum, V_PERPLAYER|((stplyr->spectator) ? V_HUDTRANSHALF : V_HUDTRANS));
@@ -813,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)
@@ -832,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)
@@ -843,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
@@ -1098,7 +1092,8 @@ static void ST_drawInput(void)
 				((!stplyr->powers[pw_carry]
 				&& (stplyr->pflags & PF_APPLYAUTOBRAKE)
 				&& !(stplyr->cmd.sidemove || stplyr->cmd.forwardmove)
-				&& (stplyr->rmomx || stplyr->rmomy))
+				&& (stplyr->rmomx || stplyr->rmomy)
+				&& (!stplyr->capsule || (stplyr->capsule->reactiontime != (stplyr-players)+1)))
 				? 0 : V_GRAYMAP),
 				"AUTOBRAKE");
 			y -= 8;
@@ -1124,13 +1119,10 @@ static void ST_drawLevelTitle(void)
 	if (!(timeinmap > 2 && timeinmap-3 < 110))
 		return;
 
+	lvlttlxpos = ((BASEVIDWIDTH/2) - (V_LevelNameWidth(lvlttl)/2));
+
 	if (actnum > 0)
-	{
-		ttlnum = W_CachePatchName(va("TTL%.2d", actnum), PU_CACHE);
-		lvlttlxpos = ((BASEVIDWIDTH/2) - (V_LevelNameWidth(lvlttl)/2)) - SHORT(ttlnum->width);
-	}
-	else
-		lvlttlxpos = ((BASEVIDWIDTH/2) - (V_LevelNameWidth(lvlttl)/2));
+		lvlttlxpos -= V_LevelActNumWidth(actnum);
 
 	ttlnumxpos = lvlttlxpos + V_LevelNameWidth(lvlttl);
 	zonexpos = ttlnumxpos - V_LevelNameWidth(M_GetText("ZONE"));
@@ -1190,7 +1182,7 @@ static void ST_drawLevelTitle(void)
 #endif
 
 	if (actnum)
-		V_DrawScaledPatch(ttlnumxpos, zoney, V_PERPLAYER, ttlnum);
+		V_DrawLevelActNum(ttlnumxpos, zoney, V_PERPLAYER, actnum);
 
 	V_DrawLevelTitle(lvlttlxpos, lvlttly, V_PERPLAYER, lvlttl);
 
@@ -1213,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)
 	{
@@ -1255,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)
 	{
@@ -1272,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)
@@ -1289,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)
@@ -1305,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
 }
 
@@ -1369,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" : "RING",
-		                        (stplyr->capsule->health > 1) ? "S" : ""));
-	}
+			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!"));
 
-	// End Bonus
-	else if (stplyr->textvar == 4)
-	{
-		V_DrawString(BASEVIDWIDTH/2 - 56, 140, aflag, (G_IsSpecialStage(gamemap)) ? "SPHERES:" : "RINGS:");
-		V_DrawString(BASEVIDWIDTH/2 - 56, 148, aflag, "BONUS:");
-		V_DrawRightAlignedString(BASEVIDWIDTH/2 + 56, 140, V_ORANGEMAP|aflag, va("%d", stplyr->finishedrings));
-		V_DrawRightAlignedString(BASEVIDWIDTH/2 + 56, 140, V_ORANGEMAP|aflag, va("%d", stplyr->finishedrings * 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 (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;
 	}
 }
 
@@ -1458,19 +1472,59 @@ 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_drawNiGHTSHUD(void)
+static void ST_drawNiGHTSLink(void)
 {
-	INT32 origamount;
-	INT32 minlink = 1;
-	INT32 total_ringcount;
+	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 < nightslinktics/3)
+	? (9 - 9*stplyr->linktimer/(nightslinktics/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));
 
-	// When debugging, show "0 Link".
+	// Show remaining link time left in debug
 	if (cv_debug & DBG_NIGHTSBASIC)
-		minlink = 0;
+		V_DrawCenteredString(BASEVIDWIDTH/2, 180, V_SNAPTOBOTTOM, va("End in %d.%02d", stplyr->linktimer/TICRATE, G_TicsToCentiseconds(stplyr->linktimer)));
+}
+
 
-	// Cheap hack: don't display when the score is showing (it popping up for a split second when exiting a map is intentional)
-	if (stplyr->texttimer && stplyr->textvar == 4)
-		minlink = INT32_MAX;
+static void ST_drawNiGHTSHUD(void)
+{
+	INT32 origamount;
+	INT32 total_spherecount;
+	const boolean oldspecialstage = (G_IsSpecialStage(gamemap) && !(maptol & TOL_NIGHTS));
 
 	// Drill meter
 	if (
@@ -1516,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)
@@ -1576,25 +1590,37 @@ static void ST_drawNiGHTSHUD(void)
 
 	// Begin drawing brackets/chip display
 #ifdef HAVE_BLUA
-	if (LUA_HudEnabled(hud_nightsrings))
+	if (LUA_HudEnabled(hud_nightsspheres))
 	{
 #endif
 	ST_DrawTopLeftOverlayPatch(16, 8, nbracket);
 	if (G_IsSpecialStage(gamemap))
-		ST_DrawTopLeftOverlayPatch(24, 16, nsshud);
+		ST_DrawTopLeftOverlayPatch(24, 16, (
+#ifdef MANIASPHERES
+			(stplyr->bonustime && (leveltime & 4)) ? nssbon :
+#endif
+			nsshud));
 	else
-		ST_DrawTopLeftOverlayPatch(24, 16, nhud[(leveltime/2)%12]);
+		ST_DrawTopLeftOverlayPatch(24, 16, *(((stplyr->bonustime) ? nbon : nhud)+((leveltime/2)%12)));
 
 	if (G_IsSpecialStage(gamemap))
 	{
 		INT32 i;
-		total_ringcount = 0;
+		total_spherecount = 0;
 		for (i = 0; i < MAXPLAYERS; i++)
-			if (playeringame[i] /*&& players[i].powers[pw_carry] == CR_NIGHTSMODE*/ && players[i].rings)
-				total_ringcount += players[i].rings;
+			if (playeringame[i] /*&& players[i].powers[pw_carry] == CR_NIGHTSMODE*/ && players[i].spheres)
+				total_spherecount += players[i].spheres;
 	}
 	else
-		total_ringcount = stplyr->rings;
+		total_spherecount = stplyr->spheres;
+
+	/*if (oldspecialstage)
+	{
+		if (total_spherecount < ssspheres)
+			total_spherecount = ssspheres - total_spherecount;
+		else
+			total_spherecount = 0;
+	}*/
 
 	if (stplyr->capsule)
 	{
@@ -1650,28 +1676,48 @@ static void ST_drawNiGHTSHUD(void)
 			amount = (origamount - stplyr->capsule->health);
 			amount = (amount * length)/origamount;
 
-			for (cfill = 0; cfill < amount && cfill < 88; ++cfill)
+			for (cfill = 0; cfill < amount && cfill < length; ++cfill)
 				V_DrawScaledPatch(15 + cfill + 1, 8 + 35, V_PERPLAYER|V_SNAPTOLEFT|V_SNAPTOTOP|V_HUDTRANS, capsulefill);
 		}
 
-		if (total_ringcount >= stplyr->capsule->health)
-			ST_DrawTopLeftOverlayPatch(40, 8 + 5, nredar[leveltime%8]);
+		if (total_spherecount >= stplyr->capsule->health)
+			ST_DrawTopLeftOverlayPatch(40, 8 + 5, nredar[leveltime&7]);
 		else
-			ST_DrawTopLeftOverlayPatch(40, 8 + 5, narrow[(leveltime/2)%8]);
+			ST_DrawTopLeftOverlayPatch(40, 8 + 5, narrow[(leveltime/2)&7]);
+	}
+	else if (oldspecialstage && total_spherecount < (INT32)ssspheres)
+	{
+		INT32 cfill, amount;
+		const INT32 length = 88;
+		UINT8 em = P_GetNextEmerald();
+		ST_DrawTopLeftOverlayPatch(72, 8, nbracket);
+
+		if (em <= 7)
+			ST_DrawTopLeftOverlayPatch(80, 8 + 8, emeraldpics[0][em]);
+
+		ST_DrawTopLeftOverlayPatch(40, 8 + 5, narrow[(leveltime/2)&7]);
+
+		// Lil' white box!
+		V_DrawScaledPatch(15, 8 + 34, V_PERPLAYER|V_SNAPTOLEFT|V_SNAPTOTOP|V_HUDTRANS, capsulebar);
+
+		amount = (total_spherecount * length)/ssspheres;
+
+		for (cfill = 0; cfill < amount && cfill < length; ++cfill)
+			V_DrawScaledPatch(15 + cfill + 1, 8 + 35, V_PERPLAYER|V_SNAPTOLEFT|V_SNAPTOTOP|V_HUDTRANS, capsulefill);
 	}
 	else
 		ST_DrawTopLeftOverlayPatch(40, 8 + 5, narrow[8]);
 
-	if (total_ringcount >= 100)
-		V_DrawTallNum((total_ringcount >= 1000) ? 76 : 72, 8 + 11, V_PERPLAYER|V_SNAPTOTOP|V_SNAPTOLEFT|V_HUDTRANS, total_ringcount);
+	if (total_spherecount >= 100)
+		V_DrawTallNum((total_spherecount >= 1000) ? 76 : 72, 8 + 11, V_PERPLAYER|V_SNAPTOTOP|V_SNAPTOLEFT|V_HUDTRANS, total_spherecount);
 	else
-		V_DrawTallNum(68, 8 + 11, V_PERPLAYER|V_SNAPTOTOP|V_SNAPTOLEFT|V_HUDTRANS, total_ringcount);
+		V_DrawTallNum(68, 8 + 11, V_PERPLAYER|V_SNAPTOTOP|V_SNAPTOLEFT|V_HUDTRANS, total_spherecount);
 #ifdef HAVE_BLUA
 	}
 #endif
 
 	// Score
-	if (!stplyr->exiting
+	if (!stplyr->exiting && !oldspecialstage
 #ifdef HAVE_BLUA
 	&& LUA_HudEnabled(hud_nightsscore)
 #endif
@@ -1714,6 +1760,7 @@ static void ST_drawNiGHTSHUD(void)
 	{
 		INT32 realnightstime = stplyr->nightstime/TICRATE;
 		INT32 numbersize;
+		UINT8 col = ((realnightstime < 10) ? SKINCOLOR_RED : SKINCOLOR_SUPERGOLD4);
 
 		if (G_IsSpecialStage(gamemap))
 		{
@@ -1744,46 +1791,66 @@ static void ST_drawNiGHTSHUD(void)
 		else
 			numbersize = 48/2;
 
-		ST_DrawNightsOverlayNum((160 + numbersize)<<FRACBITS, 14<<FRACBITS, FRACUNIT, V_PERPLAYER|V_SNAPTOTOP, realnightstime, nightsnum,
-			((realnightstime < 10) ? SKINCOLOR_RED : SKINCOLOR_SUPERGOLD4));
+		if ((oldspecialstage && leveltime & 2)
+			&& (stplyr->mo->eflags & (MFE_TOUCHWATER|MFE_UNDERWATER)))
+			col = SKINCOLOR_ORANGE;
+
+		ST_DrawNightsOverlayNum((160 + numbersize)<<FRACBITS, 14<<FRACBITS, FRACUNIT, V_PERPLAYER|V_SNAPTOTOP, realnightstime, nightsnum, col);
 
 		// Show exact time in debug
 		if (cv_debug & DBG_NIGHTSBASIC)
 			V_DrawString(160 + numbersize + 8, 24, V_SNAPTOTOP|((realnightstime < 10) ? V_REDMAP : V_YELLOWMAP), va("%02d", G_TicsToCentiseconds(stplyr->nightstime)));
 	}
 
-	// Show pickup durations
-	if (cv_debug & DBG_NIGHTSBASIC)
+	if (oldspecialstage)
 	{
-		UINT16 pwr;
-
-		if (stplyr->powers[pw_nights_superloop])
+		if (leveltime < 5*TICRATE)
 		{
-			pwr = stplyr->powers[pw_nights_superloop];
-			V_DrawSmallScaledPatch(110, 44, 0, W_CachePatchName("NPRUA0",PU_CACHE));
-			V_DrawThinString(106, 52, V_MONOSPACE, va("%2d.%02d", pwr/TICRATE, G_TicsToCentiseconds(pwr)));
+			INT32 aflag = V_PERPLAYER;
+			tic_t drawtime = (5*TICRATE) - leveltime;
+			if (drawtime < TICRATE/2)
+				aflag |= (9 - 9*drawtime/(TICRATE/2)) << V_ALPHASHIFT;
+			// This one, not quite as much so.
+			V_DrawCenteredString(BASEVIDWIDTH/2, 60, aflag,
+		                     va(M_GetText("\x80GET\x82 %d\x80 SPHERE%s!"), ssspheres,
+		                        (ssspheres > 1) ? "S" : ""));
 		}
-
-		if (stplyr->powers[pw_nights_helper])
+	}
+	else
+	{
+		// Show pickup durations
+		if (cv_debug & DBG_NIGHTSBASIC)
 		{
-			pwr = stplyr->powers[pw_nights_helper];
-			V_DrawSmallScaledPatch(150, 44, 0, W_CachePatchName("NPRUC0",PU_CACHE));
-			V_DrawThinString(146, 52, V_MONOSPACE, va("%2d.%02d", pwr/TICRATE, G_TicsToCentiseconds(pwr)));
-		}
+			UINT16 pwr;
 
-		if (stplyr->powers[pw_nights_linkfreeze])
-		{
-			pwr = stplyr->powers[pw_nights_linkfreeze];
-			V_DrawSmallScaledPatch(190, 44, 0, W_CachePatchName("NPRUE0",PU_CACHE));
-			V_DrawThinString(186, 52, V_MONOSPACE, va("%2d.%02d", pwr/TICRATE, G_TicsToCentiseconds(pwr)));
+			if (stplyr->powers[pw_nights_superloop])
+			{
+				pwr = stplyr->powers[pw_nights_superloop];
+				V_DrawSmallScaledPatch(110, 44, 0, W_CachePatchName("NPRUA0",PU_CACHE));
+				V_DrawThinString(106, 52, V_MONOSPACE, va("%2d.%02d", pwr/TICRATE, G_TicsToCentiseconds(pwr)));
+			}
+
+			if (stplyr->powers[pw_nights_helper])
+			{
+				pwr = stplyr->powers[pw_nights_helper];
+				V_DrawSmallScaledPatch(150, 44, 0, W_CachePatchName("NPRUC0",PU_CACHE));
+				V_DrawThinString(146, 52, V_MONOSPACE, va("%2d.%02d", pwr/TICRATE, G_TicsToCentiseconds(pwr)));
+			}
+
+			if (stplyr->powers[pw_nights_linkfreeze])
+			{
+				pwr = stplyr->powers[pw_nights_linkfreeze];
+				V_DrawSmallScaledPatch(190, 44, 0, W_CachePatchName("NPRUE0",PU_CACHE));
+				V_DrawThinString(186, 52, V_MONOSPACE, va("%2d.%02d", pwr/TICRATE, G_TicsToCentiseconds(pwr)));
+			}
 		}
-	}
 
-	// Records/extra text
+		// Records/extra text
 #ifdef HAVE_BLUA
-	if (LUA_HudEnabled(hud_nightsrecords))
+		if (LUA_HudEnabled(hud_nightsrecords))
 #endif
-	ST_drawNightsRecords();
+		ST_drawNightsRecords();
+	}
 }
 
 static inline void ST_drawWeaponSelect(INT32 xoffs, INT32 y)
@@ -1952,7 +2019,7 @@ static void ST_drawTextHUD(void)
 	}
 	else if (stplyr->spectator && (gametype != GT_COOP || stplyr->playerstate == PST_LIVE))
 	{
-		if (G_IsSpecialStage(gamemap) && useNightsSS)
+		if (G_IsSpecialStage(gamemap) && (maptol & TOL_NIGHTS))
 			textHUDdraw(M_GetText("\x82""Wait for the stage to end..."))
 		else if (gametype == GT_COOP)
 		{
@@ -2070,22 +2137,22 @@ num:
 #undef SEP
 }
 
-static void ST_drawSpecialStageHUD(void)
+/*static void ST_drawSpecialStageHUD(void)
 {
-	if (totalrings > 0)
+	if (ssspheres > 0)
 	{
 		if (hudinfo[HUD_SS_TOTALRINGS].x)
-			ST_DrawNumFromHud(HUD_SS_TOTALRINGS, totalrings, V_HUDTRANS);
+			ST_DrawNumFromHud(HUD_SS_TOTALRINGS, ssspheres, V_HUDTRANS);
 		else if (cv_timetic.value == 2)
-			V_DrawTallNum(hudinfo[HUD_RINGSNUMTICS].x, hudinfo[HUD_SS_TOTALRINGS].y, hudinfo[HUD_RINGSNUMTICS].f|V_PERPLAYER|V_HUDTRANS, totalrings);
+			V_DrawTallNum(hudinfo[HUD_RINGSNUMTICS].x, hudinfo[HUD_SS_TOTALRINGS].y, hudinfo[HUD_RINGSNUMTICS].f|V_PERPLAYER|V_HUDTRANS, ssspheres);
 		else
-			V_DrawTallNum(hudinfo[HUD_RINGSNUM].x, hudinfo[HUD_SS_TOTALRINGS].y, hudinfo[HUD_RINGSNUM].f|V_PERPLAYER|V_HUDTRANS, totalrings);
+			V_DrawTallNum(hudinfo[HUD_RINGSNUM].x, hudinfo[HUD_SS_TOTALRINGS].y, hudinfo[HUD_RINGSNUM].f|V_PERPLAYER|V_HUDTRANS, ssspheres);
 	}
 
-	if (leveltime < 5*TICRATE && totalrings > 0)
+	if (leveltime < 5*TICRATE && ssspheres > 0)
 	{
 		ST_DrawPatchFromHud(HUD_GETRINGS, getall, V_HUDTRANS);
-		ST_DrawNumFromHud(HUD_GETRINGSNUM, totalrings, V_HUDTRANS);
+		ST_DrawNumFromHud(HUD_GETRINGSNUM, ssspheres, V_HUDTRANS);
 	}
 
 	if (sstimer)
@@ -2095,7 +2162,7 @@ static void ST_drawSpecialStageHUD(void)
 	}
 	else
 		ST_DrawPatchFromHud(HUD_TIMEUP, timeup, V_HUDTRANS);
-}
+}*/
 
 static INT32 ST_drawEmeraldHuntIcon(mobj_t *hunt, patch_t **patches, INT32 offset)
 {
@@ -2231,7 +2298,7 @@ static void ST_overlayDrawer(void)
 	//hu_showscores = auto hide score/time/rings when tab rankings are shown
 	if (!(hu_showscores && (netgame || multiplayer)))
 	{
-		if (maptol & TOL_NIGHTS)
+		if (maptol & TOL_NIGHTS || G_IsSpecialStage(gamemap))
 			ST_drawNiGHTSHUD();
 		else
 		{
@@ -2340,10 +2407,6 @@ static void ST_overlayDrawer(void)
 		if (gametype == GT_RACE || gametype == GT_COMPETITION)
 			ST_drawRaceHUD();
 
-		// Special Stage HUD
-		if (!useNightsSS && G_IsSpecialStage(gamemap) && stplyr == &players[displayplayer])
-			ST_drawSpecialStageHUD();
-
 		// Emerald Hunt Indicators
 		if (cv_itemfinder.value && M_SecretUnlocked(SECRET_ITEMFINDER))
 			ST_doItemFinderIconsAndSound();
@@ -2362,15 +2425,18 @@ static void ST_overlayDrawer(void)
 		}
 
 		// This is where we draw all the fun cheese if you have the chasecam off!
-		if ((stplyr == &players[displayplayer] && !camera.chase)
-		|| ((splitscreen && stplyr == &players[secondarydisplayplayer]) && !camera2.chase))
+		if (!(maptol & TOL_NIGHTS))
 		{
-			ST_drawFirstPersonHUD();
-			if (cv_powerupdisplay.value)
+			if ((stplyr == &players[displayplayer] && !camera.chase)
+			|| ((splitscreen && stplyr == &players[secondarydisplayplayer]) && !camera2.chase))
+			{
+				ST_drawFirstPersonHUD();
+				if (cv_powerupdisplay.value)
+					ST_drawPowerupHUD();
+			}
+			else if (cv_powerupdisplay.value == 2)
 				ST_drawPowerupHUD();
 		}
-		else if (cv_powerupdisplay.value == 2)
-			ST_drawPowerupHUD();
 	}
 
 #ifdef HAVE_BLUA
@@ -2404,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/v_video.c b/src/v_video.c
index 67ec78af95eaaa993d7c03b596a1d54fdc335075..b70d38a9346495ecf43a8d6504a7b5c028a2622b 100644
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -622,7 +622,7 @@ void V_DrawFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_t
 		if (scrn & V_FLIP)
 		{
 			flip = true;
-			x -= FixedMul((SHORT(patch->width) - SHORT(patch->leftoffset))<<FRACBITS, pscale);
+			x -= FixedMul((SHORT(patch->width) - SHORT(patch->leftoffset))<<FRACBITS, pscale) + 1;
 		}
 		else
 			x -= FixedMul(SHORT(patch->leftoffset)<<FRACBITS, pscale);
@@ -1045,7 +1045,7 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_
 //
 void V_DrawContinueIcon(INT32 x, INT32 y, INT32 flags, INT32 skinnum, UINT8 skincolor)
 {
-	if (skins[skinnum].flags & SF_HIRES)
+	if (skinnum < 0 || skinnum >= numskins || (skins[skinnum].flags & SF_HIRES))
 		V_DrawScaledPatch(x - 10, y - 14, flags, W_CachePatchName("CONTINS", PU_CACHE));
 	else
 	{
@@ -1236,7 +1236,7 @@ void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c)
 
 		if (x == 0 && y == 0 && w == BASEVIDWIDTH && h == BASEVIDHEIGHT)
 		{ // Clear the entire screen, from dest to deststop. Yes, this really works.
-			memset(screens[0], (UINT8)(c&255), vid.width * vid.height * vid.bpp);
+			memset(screens[0], (c&255), vid.width * vid.height * vid.bpp);
 			return;
 		}
 
@@ -1299,7 +1299,7 @@ void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c)
 	c &= 255;
 
 	for (;(--h >= 0) && dest < deststop; dest += vid.width)
-		memset(dest, (UINT8)(c&255), w * vid.bpp);
+		memset(dest, c, w * vid.bpp);
 }
 
 //
@@ -1624,7 +1624,7 @@ void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string)
 {
 	INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth, center = 0, left = 0;
 	const char *ch = string;
-	INT32 charflags = 0;
+	INT32 charflags = (option & V_CHARCOLORMASK);
 	const UINT8 *colormap = NULL;
 	INT32 spacewidth = 4, charwidth = 0;
 
@@ -1642,10 +1642,9 @@ void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string)
 		dupx = dupy = 1;
 		scrwidth = vid.width/vid.dupx;
 		left = (scrwidth - BASEVIDWIDTH)/2;
+		scrwidth -= left;
 	}
 
-	charflags = (option & V_CHARCOLORMASK);
-
 	switch (option & V_SPACINGMASK)
 	{
 		case V_MONOSPACE:
@@ -1703,7 +1702,7 @@ void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string)
 		else
 			w = SHORT(hu_font[c]->width) * dupx;
 
-		if (cx+left > scrwidth)
+		if (cx > scrwidth)
 			break;
 		if (cx+left + w < 0) //left boundary check
 		{
@@ -1756,6 +1755,7 @@ void V_DrawSmallString(INT32 x, INT32 y, INT32 option, const char *string)
 		dupx = dupy = 1;
 		scrwidth = vid.width/vid.dupx;
 		left = (scrwidth - BASEVIDWIDTH)/2;
+		scrwidth -= left;
 	}
 
 	charflags = (option & V_CHARCOLORMASK);
@@ -1815,7 +1815,8 @@ void V_DrawSmallString(INT32 x, INT32 y, INT32 option, const char *string)
 		}
 		else
 			w = SHORT(hu_font[c]->width) * dupx / 2;
-		if (cx+left > scrwidth)
+
+		if (cx > scrwidth)
 			break;
 		if (cx+left + w < 0) //left boundary check
 		{
@@ -1862,6 +1863,7 @@ void V_DrawThinString(INT32 x, INT32 y, INT32 option, const char *string)
 		dupx = dupy = 1;
 		scrwidth = vid.width/vid.dupx;
 		left = (scrwidth - BASEVIDWIDTH)/2;
+		scrwidth -= left;
 	}
 
 	charflags = (option & V_CHARCOLORMASK);
@@ -1919,7 +1921,7 @@ void V_DrawThinString(INT32 x, INT32 y, INT32 option, const char *string)
 		else
 			w = (SHORT(tny_font[c]->width) * dupx);
 
-		if (cx+left > scrwidth)
+		if (cx > scrwidth)
 			break;
 		if (cx+left + w < 0) //left boundary check
 		{
@@ -1962,6 +1964,7 @@ void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string)
 		dupx = dupy = 1;
 		scrwidth = vid.width/vid.dupx;
 		left = (scrwidth - BASEVIDWIDTH)/2;
+		scrwidth -= left;
 	}
 
 	switch (option & V_SPACINGMASK)
@@ -2016,9 +2019,9 @@ void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string)
 		else
 			w = SHORT(hu_font[c]->width) * dupx;
 
-		if ((cx>>FRACBITS)+left > scrwidth)
+		if ((cx>>FRACBITS) > scrwidth)
 			break;
-		if (cx+left + w < 0) //left boundary check
+		if ((cx>>FRACBITS)+left + w < 0) //left boundary check
 		{
 			cx += w<<FRACBITS;
 			continue;
@@ -2076,6 +2079,16 @@ void V_DrawPaddedTallNum(INT32 x, INT32 y, INT32 flags, INT32 num, INT32 digits)
 	} while (--digits);
 }
 
+// Draw an act number for a level title
+// Todo: actually draw two-digit numbers as two act num patches
+void V_DrawLevelActNum(INT32 x, INT32 y, INT32 flags, INT32 num)
+{
+	if (num < 0 || num > 19)
+		return; // not supported
+
+	V_DrawScaledPatch(x, y, flags, ttlnum[num]);
+}
+
 // Write a string using the credit font
 // NOTE: the text is centered for screens larger than the base width
 //
@@ -2118,7 +2131,7 @@ void V_DrawCreditString(fixed_t x, fixed_t y, INT32 option, const char *string)
 		}
 
 		w = SHORT(cred_font[c]->width) * dupx;
-		if ((cx>>FRACBITS) + w > scrwidth)
+		if ((cx>>FRACBITS) > scrwidth)
 			break;
 
 		V_DrawSciencePatch(cx, cy, option, cred_font[c], FRACUNIT);
@@ -2154,8 +2167,10 @@ INT32 V_CreditStringWidth(const char *string)
 //
 void V_DrawLevelTitle(INT32 x, INT32 y, INT32 option, const char *string)
 {
-	INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth = BASEVIDWIDTH;
+	INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth, left = 0;
 	const char *ch = string;
+	INT32 charflags = (option & V_CHARCOLORMASK);
+	const UINT8 *colormap = NULL;
 
 	if (option & V_NOSCALESTART)
 	{
@@ -2164,21 +2179,32 @@ void V_DrawLevelTitle(INT32 x, INT32 y, INT32 option, const char *string)
 		scrwidth = vid.width;
 	}
 	else
+	{
 		dupx = dupy = 1;
+		scrwidth = vid.width/vid.dupx;
+		left = (scrwidth - BASEVIDWIDTH)/2;
+		scrwidth -= left;
+	}
 
-	for (;;)
+	for (;;ch++)
 	{
-		c = *ch++;
-		if (!c)
+		if (!*ch)
 			break;
-		if (c == '\n')
+		if (*ch & 0x80) //color parsing -x 2.16.09
+		{
+			// manually set flags override color codes
+			if (!(option & V_CHARCOLORMASK))
+				charflags = ((*ch & 0x7f) << V_CHARCOLORSHIFT) & V_CHARCOLORMASK;
+			continue;
+		}
+		if (*ch == '\n')
 		{
 			cx = x;
 			cy += 12*dupy;
 			continue;
 		}
 
-		c = toupper(c) - LT_FONTSTART;
+		c = toupper(*ch) - LT_FONTSTART;
 		if (c < 0 || c >= LT_FONTSIZE || !lt_font[c])
 		{
 			cx += 16*dupx;
@@ -2186,17 +2212,18 @@ void V_DrawLevelTitle(INT32 x, INT32 y, INT32 option, const char *string)
 		}
 
 		w = SHORT(lt_font[c]->width) * dupx;
-		if (cx + w > scrwidth)
-			break;
 
-		//left boundary check
-		if (cx < 0)
+		if (cx > scrwidth)
+			break;
+		if (cx+left + w < 0) //left boundary check
 		{
 			cx += w;
 			continue;
 		}
 
-		V_DrawScaledPatch(cx, cy, option, lt_font[c]);
+		colormap = V_GetStringColormap(charflags);
+		V_DrawFixedPatch(cx<<FRACBITS, cy<<FRACBITS, FRACUNIT, option, lt_font[c], colormap);
+
 		cx += w;
 	}
 }
@@ -2210,6 +2237,8 @@ INT32 V_LevelNameWidth(const char *string)
 
 	for (i = 0; i < strlen(string); i++)
 	{
+		if (string[i] & 0x80)
+			continue;
 		c = toupper(string[i]) - LT_FONTSTART;
 		if (c < 0 || c >= LT_FONTSIZE || !lt_font[c])
 			w += 16;
@@ -2240,6 +2269,16 @@ INT32 V_LevelNameHeight(const char *string)
 	return w;
 }
 
+// For ST_drawLevelTitle
+// Returns the width of the act num patch
+INT32 V_LevelActNumWidth(INT32 num)
+{
+	if (num < 0 || num > 19)
+		return 0; // not a valid number
+
+	return SHORT(ttlnum[num]->width);
+}
+
 //
 // Find string width from hu_font chars
 //
@@ -2265,11 +2304,9 @@ INT32 V_StringWidth(const char *string, INT32 option)
 
 	for (i = 0; i < strlen(string); i++)
 	{
-		c = string[i];
-		if ((UINT8)c >= 0x80 && (UINT8)c <= 0x89) //color parsing! -Inuyasha 2.16.09
+		if (string[i] & 0x80)
 			continue;
-
-		c = toupper(c) - HU_FONTSTART;
+		c = toupper(string[i]) - HU_FONTSTART;
 		if (c < 0 || c >= HU_FONTSIZE || !hu_font[c])
 			w += spacewidth;
 		else
@@ -2307,11 +2344,9 @@ INT32 V_SmallStringWidth(const char *string, INT32 option)
 
 	for (i = 0; i < strlen(string); i++)
 	{
-		c = string[i];
-		if ((UINT8)c >= 0x80 && (UINT8)c <= 0x89) //color parsing! -Inuyasha 2.16.09
+		if (string[i] & 0x80)
 			continue;
-
-		c = toupper(c) - HU_FONTSTART;
+		c = toupper(string[i]) - HU_FONTSTART;
 		if (c < 0 || c >= HU_FONTSIZE || !hu_font[c])
 			w += spacewidth;
 		else
@@ -2346,11 +2381,9 @@ INT32 V_ThinStringWidth(const char *string, INT32 option)
 
 	for (i = 0; i < strlen(string); i++)
 	{
-		c = string[i];
-		if ((UINT8)c >= 0x80 && (UINT8)c <= 0x89) //color parsing! -Inuyasha 2.16.09
+		if (string[i] & 0x80)
 			continue;
-
-		c = toupper(c) - HU_FONTSTART;
+		c = toupper(string[i]) - HU_FONTSTART;
 		if (c < 0 || c >= HU_FONTSIZE || !tny_font[c])
 			w += spacewidth;
 		else
diff --git a/src/v_video.h b/src/v_video.h
index 5645ed2ce1812ae21eb91f4304ccf91b01b0bc2a..6113d08ecdc9f1ad1c46868fc24149bfb80c6213 100644
--- a/src/v_video.h
+++ b/src/v_video.h
@@ -185,10 +185,12 @@ void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string)
 // Draw tall nums, used for menu, HUD, intermission
 void V_DrawTallNum(INT32 x, INT32 y, INT32 flags, INT32 num);
 void V_DrawPaddedTallNum(INT32 x, INT32 y, INT32 flags, INT32 num, INT32 digits);
+void V_DrawLevelActNum(INT32 x, INT32 y, INT32 flags, INT32 num);
 
 // Find string width from lt_font chars
 INT32 V_LevelNameWidth(const char *string);
 INT32 V_LevelNameHeight(const char *string);
+INT32 V_LevelActNumWidth(INT32 num); // act number width
 
 void V_DrawCreditString(fixed_t x, fixed_t y, INT32 option, const char *string);
 INT32 V_CreditStringWidth(const char *string);
diff --git a/src/w_wad.c b/src/w_wad.c
index 1b0e501a63dd6f3dd1c6b4b9c913a5044825b3cc..3112e02a0f182e4cebc67122790163812267bbaf 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -194,11 +194,11 @@ static inline void W_LoadDehackedLumpsPK3(UINT16 wadnum)
 		for (; posStart < posEnd; posStart++)
 			LUA_LoadLump(wadnum, posStart);
 	}
-	posStart = W_CheckNumForFolderStartPK3("SOCs/", wadnum, 0);
+	posStart = W_CheckNumForFolderStartPK3("SOC/", wadnum, 0);
 	if (posStart != INT16_MAX)
 	{
-		posEnd = W_CheckNumForFolderEndPK3("SOCs/", wadnum, posStart);
-		posStart++; // first "lump" will be "SOCs/" folder itself, so ignore it
+		posEnd = W_CheckNumForFolderEndPK3("SOC/", wadnum, posStart);
+		posStart++; // first "lump" will be "SOC/" folder itself, so ignore it
 		for(; posStart < posEnd; posStart++)
 		{
 			lumpinfo_t *lump_p = &wadfiles[wadnum]->lumpinfo[posStart];
@@ -234,10 +234,10 @@ static inline void W_LoadDehackedLumps(UINT16 wadnum)
 		for (lump = 0; lump < wadfiles[wadnum]->numlumps; lump++, lump_p++)
 			if (memcmp(lump_p->name,"SOC_",4)==0) // Check for generic SOC lump
 			{	// shameless copy+paste of code from LUA_LoadLump
-				size_t length = strlen(wadfiles[wadnum]->filename) + 1 + strlen(lump_p->name2); // length of file name, '|', and lump name
-				char *name = malloc(length + 1);
+				size_t len = strlen(wadfiles[wadnum]->filename) + 1 + strlen(lump_p->name2); // length of file name, '|', and lump name
+				char *name = malloc(len+1);
 				sprintf(name, "%s|%s", wadfiles[wadnum]->filename, lump_p->name2);
-				name[length] = '\0';
+				name[len] = '\0';
 
 				CONS_Printf(M_GetText("Loading SOC from %s\n"), name);
 				DEH_LoadDehackedLumpPwad(wadnum, lump);
diff --git a/src/win32/win_cd.c b/src/win32/win_cd.c
index 4ac1506e5993ac139455a57c0a162d7a2e393fc7..2586b84405649d431e673d1f7c8e4bb3a4d27a9c 100644
--- a/src/win32/win_cd.c
+++ b/src/win32/win_cd.c
@@ -161,7 +161,7 @@ static BOOL wasPlaying;
 //static INT     cdVolume = 0;          // current cd volume (0-31)
 
 // 0-31 like Music & Sfx, though CD hardware volume is 0-255.
-consvar_t cd_volume = {"cd_volume","31",CV_SAVE,soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cd_volume = {"cd_volume","18",CV_SAVE,soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
 
 // allow Update for next/loop track
 // some crap cd drivers take up to
@@ -471,7 +471,7 @@ void I_PlayCD(UINT8 nTrack, UINT8 bLooping)
 	//faB: stop MIDI music, MIDI music will restart if volume is upped later
 	cv_digmusicvolume.value = 0;
 	cv_midimusicvolume.value = 0;
-	I_StopSong (0);
+	I_StopSong();
 
 	//faB: I don't use the notify message, I'm trying to minimize the delay
 	mciPlay.dwCallback = (DWORD)((size_t)hWndMain);
diff --git a/src/win32/win_main.c b/src/win32/win_main.c
index 6c774f5576dad667e64f990b2082df815049b563..bfe620a4343e88a45d8389005ba10df86aa409f3 100644
--- a/src/win32/win_main.c
+++ b/src/win32/win_main.c
@@ -110,9 +110,9 @@ static LRESULT CALLBACK MainWndproc(HWND hWnd, UINT message, WPARAM wParam, LPAR
 
 			// pause music when alt-tab
 			if (appActive  && !paused)
-				I_ResumeSong(0);
+				I_ResumeSong();
 			else if (!paused)
-				I_PauseSong(0);
+				I_PauseSong();
 			{
 				HANDLE ci = GetStdHandle(STD_INPUT_HANDLE);
 				DWORD mode;
diff --git a/src/win32/win_snd.c b/src/win32/win_snd.c
index 88f34abf8a86ef2d2f1c009e8ddceb8cc47d1f2f..1e1b062f8a14fd9ad00a87b146d3f1402367575e 100644
--- a/src/win32/win_snd.c
+++ b/src/win32/win_snd.c
@@ -17,10 +17,8 @@
 #include "gme/gme.h"
 #define GME_TREBLE 5.0
 #define GME_BASS 1.0
-#ifdef HAVE_PNG /// TODO: compile with zlib support without libpng
-
-#define HAVE_ZLIB
 
+#ifdef HAVE_ZLIB
 #ifndef _MSC_VER
 #ifndef _LARGEFILE64_SOURCE
 #define _LARGEFILE64_SOURCE
@@ -36,13 +34,12 @@
 #endif
 
 #include "zlib.h"
-#endif
-#endif
+#endif // HAVE_ZLIB
+#endif // HAVE_LIBGME
 
 static FMOD_SYSTEM *fsys;
 static FMOD_SOUND *music_stream;
 static FMOD_CHANNEL *music_channel;
-static boolean midimode;
 
 static UINT8 music_volume, midi_volume, sfx_volume;
 static INT32 current_track;
@@ -355,12 +352,13 @@ void I_FreeSfx(sfxinfo_t *sfx)
 	sfx->data = NULL;
 }
 
-INT32 I_StartSound(sfxenum_t id, UINT8 vol, UINT8 sep, UINT8 pitch, UINT8 priority)
+INT32 I_StartSound(sfxenum_t id, UINT8 vol, UINT8 sep, UINT8 pitch, UINT8 priority, INT32 channel)
 {
 	FMOD_SOUND *sound;
 	FMOD_CHANNEL *chan;
 	INT32 i;
 	float frequency;
+	(void)channel;
 
 	sound = (FMOD_SOUND *)S_sfx[id].data;
 	I_Assert(sound != NULL);
@@ -440,9 +438,9 @@ void I_SetSfxVolume(UINT8 volume)
 	sfx_volume = volume;
 }
 
-//
-// MUSIC
-//
+/// ------------------------
+//  MUSIC SYSTEM
+/// ------------------------
 
 void I_InitMusic(void)
 {
@@ -450,53 +448,111 @@ void I_InitMusic(void)
 
 void I_ShutdownMusic(void)
 {
-	I_ShutdownDigMusic();
-	I_ShutdownMIDIMusic();
+	I_StopSong();
 }
 
-void I_PauseSong(INT32 handle)
-{
-	UNREFERENCED_PARAMETER(handle);
-	if (music_stream)
-		FMR_MUSIC(FMOD_Channel_SetPaused(music_channel, true));
-}
+/// ------------------------
+//  MUSIC PROPERTIES
+/// ------------------------
 
-void I_ResumeSong(INT32 handle)
+musictype_t I_SongType(void)
 {
-	UNREFERENCED_PARAMETER(handle);
-	if (music_stream)
-		FMR_MUSIC(FMOD_Channel_SetPaused(music_channel, false));
+#ifdef HAVE_LIBGME
+	if (gme)
+		return MU_GME;
+#endif
+
+	if (!music_stream)
+		return MU_NONE;
+
+	FMOD_SOUND_TYPE type;
+	if (FMOD_Sound_GetFormat(music_stream, &type, NULL, NULL, NULL) == FMOD_OK)
+	{
+		switch(type)
+		{
+			case FMOD_SOUND_TYPE_WAV:
+				return MU_WAV;
+			case FMOD_SOUND_TYPE_MOD:
+				return MU_MOD;
+			case FMOD_SOUND_TYPE_MIDI:
+				return MU_MID;
+			case FMOD_SOUND_TYPE_OGGVORBIS:
+				return MU_OGG;
+			case FMOD_SOUND_TYPE_MPEG:
+				return MU_MP3;
+			case FMOD_SOUND_TYPE_FLAC:
+				return MU_FLAC;
+			default:
+				return MU_NONE;
+		}
+	}
+	else
+		return MU_NONE;
 }
 
-void I_InitDigMusic(void)
+boolean I_SongPlaying(void)
 {
+	return (boolean)music_stream;
 }
 
-void I_ShutdownDigMusic(void)
+boolean I_SongPaused(void)
 {
-	if (!midimode)
-		I_StopDigSong();
+	boolean fmpaused = false;
+	if (music_stream)
+		FMOD_Channel_GetPaused(music_channel, &fmpaused);
+	return fmpaused;
 }
 
-boolean I_StartDigSong(const char *musicname, boolean looping)
+/// ------------------------
+//  MUSIC EFFECTS
+/// ------------------------
+
+boolean I_SetSongSpeed(float speed)
 {
-	char *data;
-	size_t len;
-	FMOD_CREATESOUNDEXINFO fmt;
-	lumpnum_t lumpnum = W_CheckNumForName(va("O_%s",musicname));
+	FMOD_RESULT e;
+	float frequency;
+	if (!music_stream)
+		return false;
+	if (speed > 250.0f)
+		speed = 250.0f; //limit speed up to 250x
 
-	if (lumpnum == LUMPERROR)
+#ifdef HAVE_LIBGME
+	// Try to set GME speed
+	if (gme)
 	{
-		lumpnum = W_CheckNumForName(va("D_%s",musicname));
-		if (lumpnum == LUMPERROR)
-			return false;
-		midimode = true;
+		gme_set_tempo(gme, speed);
+		return true;
+	}
+#endif
+
+	// Try to set Mod/Midi speed
+	e = FMOD_Sound_SetMusicSpeed(music_stream, speed);
+
+	if (e == FMOD_ERR_FORMAT)
+	{
+		// Just change pitch instead for Ogg/etc.
+		FMR(FMOD_Sound_GetDefaults(music_stream, &frequency, NULL, NULL, NULL));
+		FMR_MUSIC(FMOD_Channel_SetFrequency(music_channel, speed*frequency));
 	}
 	else
-		midimode = false;
+		FMR_MUSIC(e);
+
+	return true;
+}
+
+/// ------------------------
+//  MUSIC PLAYBACK
+/// ------------------------
+
+boolean I_LoadSong(char *data, size_t len)
+{
+	FMOD_CREATESOUNDEXINFO fmt;
+	FMOD_RESULT e;
+	FMOD_TAG tag;
+	unsigned int loopstart, loopend;
 
-	data = (char *)W_CacheLumpNum(lumpnum, PU_MUSIC);
-	len = W_LumpLength(lumpnum);
+	if (gme || music_stream)
+		I_UnloadSong();
 
 	memset(&fmt, 0, sizeof(FMOD_CREATESOUNDEXINFO));
 	fmt.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
@@ -531,8 +587,6 @@ boolean I_StartDigSong(const char *musicname, boolean looping)
 					gme_equalizer_t gmeq = {GME_TREBLE, GME_BASS, 0,0,0,0,0,0,0,0};
 					Z_Free(inflatedData); // GME supposedly makes a copy for itself, so we don't need this lying around
 					Z_Free(data); // We don't need this, either.
-					gme_start_track(gme, 0);
-					current_track = 0;
 					gme_set_equalizer(gme,&gmeq);
 					fmt.format = FMOD_SOUND_FORMAT_PCM16;
 					fmt.defaultfrequency = 44100;
@@ -541,10 +595,7 @@ boolean I_StartDigSong(const char *musicname, boolean looping)
 					fmt.decodebuffersize = (44100 * 2) / 35;
 					fmt.pcmreadcallback = GMEReadCallback;
 					fmt.userdata = gme;
-					FMR(FMOD_System_CreateStream(fsys, NULL, FMOD_OPENUSER | (looping ? FMOD_LOOP_NORMAL : 0), &fmt, &music_stream));
-					FMR(FMOD_System_PlaySound(fsys, FMOD_CHANNEL_FREE, music_stream, false, &music_channel));
-					FMR(FMOD_Channel_SetVolume(music_channel, music_volume / 31.0));
-					FMR(FMOD_Channel_SetPriority(music_channel, 0));
+					FMR(FMOD_System_CreateStream(fsys, NULL, FMOD_OPENUSER, &fmt, &music_stream));
 					return true;
 				}
 			}
@@ -603,8 +654,6 @@ boolean I_StartDigSong(const char *musicname, boolean looping)
 	{
 		gme_equalizer_t gmeq = {GME_TREBLE, GME_BASS, 0,0,0,0,0,0,0,0};
 		Z_Free(data); // We don't need this anymore.
-		gme_start_track(gme, 0);
-		current_track = 0;
 		gme_set_equalizer(gme,&gmeq);
 		fmt.format = FMOD_SOUND_FORMAT_PCM16;
 		fmt.defaultfrequency = 44100;
@@ -613,149 +662,153 @@ boolean I_StartDigSong(const char *musicname, boolean looping)
 		fmt.decodebuffersize = (44100 * 2) / 35;
 		fmt.pcmreadcallback = GMEReadCallback;
 		fmt.userdata = gme;
-		FMR(FMOD_System_CreateStream(fsys, NULL, FMOD_OPENUSER | (looping ? FMOD_LOOP_NORMAL : 0), &fmt, &music_stream));
-		FMR(FMOD_System_PlaySound(fsys, FMOD_CHANNEL_FREE, music_stream, false, &music_channel));
-		FMR(FMOD_Channel_SetVolume(music_channel, music_volume / 31.0));
-		FMR(FMOD_Channel_SetPriority(music_channel, 0));
+		FMR(FMOD_System_CreateStream(fsys, NULL, FMOD_OPENUSER, &fmt, &music_stream));
 		return true;
 	}
 #endif
 
 	fmt.length = len;
+
+	e = FMOD_System_CreateStream(fsys, data, FMOD_OPENMEMORY_POINT, &fmt, &music_stream);
+	if (e != FMOD_OK)
 	{
-		FMOD_RESULT e = FMOD_System_CreateStream(fsys, data, FMOD_OPENMEMORY_POINT|(looping ? FMOD_LOOP_NORMAL : 0), &fmt, &music_stream);
-		if (e != FMOD_OK)
-		{
-			if (e == FMOD_ERR_FORMAT)
-				CONS_Alert(CONS_WARNING, "Failed to play music lump %s due to invalid format.\n", W_CheckNameForNum(lumpnum));
-			else
-				FMR(e);
-			return false;
-		}
+		if (e == FMOD_ERR_FORMAT)
+			CONS_Alert(CONS_WARNING, "Failed to play music lump due to invalid format.\n");
+		else
+			FMR(e);
+		return false;
 	}
-	FMR(FMOD_System_PlaySound(fsys, FMOD_CHANNEL_FREE, music_stream, false, &music_channel));
-	if (midimode)
-		FMR(FMOD_Channel_SetVolume(music_channel, midi_volume / 31.0));
-	else
-		FMR(FMOD_Channel_SetVolume(music_channel, music_volume / 31.0));
-	FMR(FMOD_Channel_SetPriority(music_channel, 0));
-	current_track = 0;
 
 	// Try to find a loop point in streaming music formats (ogg, mp3)
-	if (looping)
+
+	// A proper LOOPPOINT is its own tag, stupid.
+	e = FMOD_Sound_GetTag(music_stream, "LOOPPOINT", 0, &tag);
+	if (e != FMOD_ERR_TAGNOTFOUND)
 	{
-		FMOD_RESULT e;
-		FMOD_TAG tag;
-		unsigned int loopstart, loopend;
+		FMR(e);
+		loopstart = atoi((char *)tag.data); // assumed to be a string data tag.
+		FMR(FMOD_Sound_GetLoopPoints(music_stream, NULL, FMOD_TIMEUNIT_PCM, &loopend, FMOD_TIMEUNIT_PCM));
+		if (loopstart > 0)
+			FMR(FMOD_Sound_SetLoopPoints(music_stream, loopstart, FMOD_TIMEUNIT_PCM, loopend, FMOD_TIMEUNIT_PCM));
+		return true;
+	}
 
-		// A proper LOOPPOINT is its own tag, stupid.
-		e = FMOD_Sound_GetTag(music_stream, "LOOPPOINT", 0, &tag);
-		if (e != FMOD_ERR_TAGNOTFOUND)
+	// Use LOOPMS for time in miliseconds.
+	e = FMOD_Sound_GetTag(music_stream, "LOOPMS", 0, &tag);
+	if (e != FMOD_ERR_TAGNOTFOUND)
+	{
+		FMR(e);
+		loopstart = atoi((char *)tag.data); // assumed to be a string data tag.
+		FMR(FMOD_Sound_GetLoopPoints(music_stream, NULL, FMOD_TIMEUNIT_MS, &loopend, FMOD_TIMEUNIT_PCM));
+		if (loopstart > 0)
+			FMR(FMOD_Sound_SetLoopPoints(music_stream, loopstart, FMOD_TIMEUNIT_MS, loopend, FMOD_TIMEUNIT_PCM));
+		return true;
+	}
+
+	// Try to fetch it from the COMMENT tag, like A.J. Freda
+	e = FMOD_Sound_GetTag(music_stream, "COMMENT", 0, &tag);
+	if (e != FMOD_ERR_TAGNOTFOUND)
+	{
+		char *loopText;
+		// Handle any errors that arose, first
+		FMR(e);
+		// Figure out where the number starts
+		loopText = strstr((char *)tag.data,"LOOPPOINT=");
+		if (loopText != NULL)
 		{
-			FMR(e);
-			loopstart = atoi((char *)tag.data); // assumed to be a string data tag.
+			// Skip the "LOOPPOINT=" part.
+			loopText += 10;
+			// Convert it to our looppoint
+			// FMOD seems to ensure the tag is properly NULL-terminated.
+			// atoi will stop when it reaches anything that's not a number.
+			loopstart = atoi(loopText);
+			// Now do the rest like above
 			FMR(FMOD_Sound_GetLoopPoints(music_stream, NULL, FMOD_TIMEUNIT_PCM, &loopend, FMOD_TIMEUNIT_PCM));
 			if (loopstart > 0)
 				FMR(FMOD_Sound_SetLoopPoints(music_stream, loopstart, FMOD_TIMEUNIT_PCM, loopend, FMOD_TIMEUNIT_PCM));
-			return true;
-		}
-
-		// Use LOOPMS for time in miliseconds.
-		e = FMOD_Sound_GetTag(music_stream, "LOOPMS", 0, &tag);
-		if (e != FMOD_ERR_TAGNOTFOUND)
-		{
-			FMR(e);
-			loopstart = atoi((char *)tag.data); // assumed to be a string data tag.
-			FMR(FMOD_Sound_GetLoopPoints(music_stream, NULL, FMOD_TIMEUNIT_MS, &loopend, FMOD_TIMEUNIT_PCM));
-			if (loopstart > 0)
-				FMR(FMOD_Sound_SetLoopPoints(music_stream, loopstart, FMOD_TIMEUNIT_MS, loopend, FMOD_TIMEUNIT_PCM));
-			return true;
-		}
-
-		// Try to fetch it from the COMMENT tag, like A.J. Freda
-		e = FMOD_Sound_GetTag(music_stream, "COMMENT", 0, &tag);
-		if (e != FMOD_ERR_TAGNOTFOUND)
-		{
-			char *loopText;
-			// Handle any errors that arose, first
-			FMR(e);
-			// Figure out where the number starts
-			loopText = strstr((char *)tag.data,"LOOPPOINT=");
-			if (loopText != NULL)
-			{
-				// Skip the "LOOPPOINT=" part.
-				loopText += 10;
-				// Convert it to our looppoint
-				// FMOD seems to ensure the tag is properly NULL-terminated.
-				// atoi will stop when it reaches anything that's not a number.
-				loopstart = atoi(loopText);
-				// Now do the rest like above
-				FMR(FMOD_Sound_GetLoopPoints(music_stream, NULL, FMOD_TIMEUNIT_PCM, &loopend, FMOD_TIMEUNIT_PCM));
-				if (loopstart > 0)
-					FMR(FMOD_Sound_SetLoopPoints(music_stream, loopstart, FMOD_TIMEUNIT_PCM, loopend, FMOD_TIMEUNIT_PCM));
-			}
-			return true;
 		}
+		return true;
 	}
 
-	// No special loop point, but we're playing so it's all good.
+	// No special loop point
 	return true;
 }
 
-void I_StopDigSong(void)
+void I_UnloadSong(void)
 {
-	if (music_stream)
-		FMR(FMOD_Sound_Release(music_stream));
-	music_stream = NULL;
+	I_StopSong();
 #ifdef HAVE_LIBGME
 	if (gme)
+	{
 		gme_delete(gme);
-	gme = NULL;
+		gme = NULL;
+	}
 #endif
-	current_track = -1;
-}
-
-void I_SetDigMusicVolume(UINT8 volume)
-{
-	// volume is 0 to 31.
-	music_volume = volume;
-	if (!midimode && music_stream)
-		FMR_MUSIC(FMOD_Channel_SetVolume(music_channel, volume / 31.0));
+	if (music_stream)
+	{
+		FMR(FMOD_Sound_Release(music_stream));
+		music_stream = NULL;
+	}
 }
 
-boolean I_SetSongSpeed(float speed)
+boolean I_PlaySong(boolean looping)
 {
-	FMOD_RESULT e;
-	float frequency;
-	if (!music_stream)
-		return false;
-	if (speed > 250.0f)
-		speed = 250.0f; //limit speed up to 250x
-
 #ifdef HAVE_LIBGME
-	// Try to set GME speed
 	if (gme)
 	{
-		gme_set_tempo(gme, speed);
+		gme_start_track(gme, 0);
+		current_track = 0;
+		FMR(FMOD_System_PlaySound(fsys, FMOD_CHANNEL_FREE, music_stream, false, &music_channel));
+		FMR(FMOD_Channel_SetVolume(music_channel, music_volume / 31.0));
+		FMR(FMOD_Channel_SetPriority(music_channel, 0));
 		return true;
 	}
 #endif
 
-	// Try to set Mod/Midi speed
-	e = FMOD_Sound_SetMusicSpeed(music_stream, speed);
-
-	if (e == FMOD_ERR_FORMAT)
-	{
-		// Just change pitch instead for Ogg/etc.
-		FMR(FMOD_Sound_GetDefaults(music_stream, &frequency, NULL, NULL, NULL));
-		FMR_MUSIC(FMOD_Channel_SetFrequency(music_channel, speed*frequency));
-	}
+	FMR(FMOD_Sound_SetMode(music_stream, (looping ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF)));
+	FMR(FMOD_System_PlaySound(fsys, FMOD_CHANNEL_FREE, music_stream, false, &music_channel));
+	if (I_SongType() != MU_MID)
+		FMR(FMOD_Channel_SetVolume(music_channel, midi_volume / 31.0));
 	else
-		FMR_MUSIC(e);
+		FMR(FMOD_Channel_SetVolume(music_channel, music_volume / 31.0));
+	FMR(FMOD_Channel_SetPriority(music_channel, 0));
+	current_track = 0;
 
 	return true;
 }
 
+void I_StopSong(void)
+{
+	if (music_channel)
+		FMR_MUSIC(FMOD_Channel_Stop(music_channel));
+}
+
+void I_PauseSong(void)
+{
+	if (music_channel)
+		FMR_MUSIC(FMOD_Channel_SetPaused(music_channel, true));
+}
+
+void I_ResumeSong(void)
+{
+	if (music_channel)
+		FMR_MUSIC(FMOD_Channel_SetPaused(music_channel, false));
+}
+
+void I_SetMusicVolume(UINT8 volume)
+{
+	if (!music_channel)
+		return;
+
+	// volume is 0 to 31.
+	if (I_SongType() == MU_MID)
+		music_volume = 31; // windows bug hack
+	else
+		music_volume = volume;
+
+	FMR_MUSIC(FMOD_Channel_SetVolume(music_channel, music_volume / 31.0));
+}
+
 boolean I_SetSongTrack(INT32 track)
 {
 	if (track != current_track) // If the track's already playing, then why bother?
@@ -800,62 +853,3 @@ boolean I_SetSongTrack(INT32 track)
 	}
 	return false;
 }
-
-//
-// Fuck MIDI. ... Okay fine, you can have your silly D_-only mode.
-//
-
-void I_InitMIDIMusic(void)
-{
-}
-
-void I_ShutdownMIDIMusic(void)
-{
-	if (midimode)
-		I_StopSong(0);
-}
-
-void I_SetMIDIMusicVolume(UINT8 volume)
-{
-	// volume is 0 to 31.
-	midi_volume = volume;
-	if (midimode && music_stream)
-		FMR_MUSIC(FMOD_Channel_SetVolume(music_channel, volume / 31.0));
-}
-
-INT32 I_RegisterSong(void *data, size_t len)
-{
-	FMOD_CREATESOUNDEXINFO fmt;
-	memset(&fmt, 0, sizeof(FMOD_CREATESOUNDEXINFO));
-	fmt.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
-	fmt.length = len;
-	FMR(FMOD_System_CreateStream(fsys, (char *)data, FMOD_OPENMEMORY_POINT, &fmt, &music_stream));
-	return 1337;
-}
-
-boolean I_PlaySong(INT32 handle, boolean looping)
-{
-	if (1337 == handle)
-	{
-		midimode = true;
-		if (looping)
-			FMR(FMOD_Sound_SetMode(music_stream, FMOD_LOOP_NORMAL));
-		FMR(FMOD_System_PlaySound(fsys, FMOD_CHANNEL_FREE, music_stream, false, &music_channel));
-		FMR_MUSIC(FMOD_Channel_SetVolume(music_channel, midi_volume / 31.0));
-		FMR_MUSIC(FMOD_Channel_SetPriority(music_channel, 0));
-	}
-	return true;
-}
-
-void I_StopSong(INT32 handle)
-{
-	I_UnRegisterSong(handle);
-}
-
-void I_UnRegisterSong(INT32 handle)
-{
-	UNREFERENCED_PARAMETER(handle);
-	if (music_stream)
-		FMR(FMOD_Sound_Release(music_stream));
-	music_stream = NULL;
-}
diff --git a/src/y_inter.c b/src/y_inter.c
index ed9cc4185bae4e04fb4efc100b0502365b7ff303..ec7aa30ca66659b13c6346087ebad31cd0fc99b1 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -70,15 +70,15 @@ typedef union
 		UINT32 score, total; // fake score, total
 		UINT32 tics; // time
 
-		patch_t *ttlnum; // act number being displayed
+		INT32 actnum; // act number being displayed
 		patch_t *ptotal; // TOTAL
 		UINT8 gotlife; // Number of extra lives obtained
 	} coop;
 
 	struct
 	{
-		char passed1[29]; // KNUCKLES GOT    / CRAWLA HONCHO
-		char passed2[17];             // A CHAOS EMERALD / GOT THEM ALL!
+		char passed1[29];             // KNUCKLES GOT     / CRAWLA HONCHO
+		char passed2[17];             // A CHAOS EMERALD? / GOT THEM ALL!
 		char passed3[15];             //                   CAN NOW BECOME
 		char passed4[SKINNAMESIZE+7]; //                   SUPER CRAWLA HONCHO
 		INT32 passedx1;
@@ -288,8 +288,8 @@ void Y_IntermissionDrawer(void)
 		V_DrawLevelTitle(data.coop.passedx1, 49, 0, data.coop.passed1);
 		V_DrawLevelTitle(data.coop.passedx2, 49+V_LevelNameHeight(data.coop.passed2)+2, 0, data.coop.passed2);
 
-		if (mapheaderinfo[gamemap-1]->actnum)
-			V_DrawScaledPatch(244, 57, 0, data.coop.ttlnum);
+		if (data.coop.actnum)
+			V_DrawLevelActNum(244, 57, 0, data.coop.actnum);
 
 		bonusy = 150;
 		// Total
@@ -315,6 +315,9 @@ void Y_IntermissionDrawer(void)
 		INT32 xoffset1 = 0; // Line 1 x offset
 		INT32 xoffset2 = 0; // Line 2 x offset
 		INT32 xoffset3 = 0; // Line 3 x offset
+		INT32 xoffset4 = 0; // Line 4 x offset
+		INT32 xoffset5 = 0; // Line 5 x offset
+		INT32 xoffset6 = 0; // Line 6 x offset
 		UINT8 drawsection = 0;
 
 		if (gottoken) // first to be behind everything else
@@ -324,71 +327,130 @@ void Y_IntermissionDrawer(void)
 		if (intertic <= 2*TICRATE)
 			animatetic = 0;
 		else if (!animatetic && data.spec.bonus.points == 0 && data.spec.passed3[0] != '\0')
-			animatetic = intertic;
+			animatetic = intertic + TICRATE;
 
-		if (animatetic)
+		if (animatetic && (tic_t)intertic >= animatetic)
 		{
+			const INT32 scradjust = (vid.width/vid.dupx)>>3; // 40 for BASEVIDWIDTH
 			INT32 animatetimer = (intertic - animatetic);
-			if (animatetimer <= 8)
+			if (animatetimer <= 14)
 			{
-				xoffset1 = -(animatetimer     * 40);
-				xoffset2 = -((animatetimer-2) * 40);
+				xoffset1 = -(animatetimer     * scradjust);
+				xoffset2 = -((animatetimer-2) * scradjust);
+				xoffset3 = -((animatetimer-4) * scradjust);
+				xoffset4 = -((animatetimer-6) * scradjust);
+				xoffset5 = -((animatetimer-8) * scradjust);
 				if (xoffset2 > 0) xoffset2 = 0;
+				if (xoffset3 > 0) xoffset3 = 0;
+				if (xoffset4 > 0) xoffset4 = 0;
+				if (xoffset5 > 0) xoffset5 = 0;
 			}
-			else if (animatetimer <= 19)
+			else if (animatetimer < 32)
 			{
 				drawsection = 1;
-				xoffset1 = (16-animatetimer) * 40;
-				xoffset2 = (18-animatetimer) * 40;
-				xoffset3 = (20-animatetimer) * 40;
+				xoffset1 = (22-animatetimer) * scradjust;
+				xoffset2 = (24-animatetimer) * scradjust;
+				xoffset3 = (26-animatetimer) * scradjust;
+				xoffset4 = (28-animatetimer) * scradjust;
+				xoffset5 = (30-animatetimer) * scradjust;
+				xoffset6 = (32-animatetimer) * scradjust;
 				if (xoffset1 < 0) xoffset1 = 0;
 				if (xoffset2 < 0) xoffset2 = 0;
+				if (xoffset3 < 0) xoffset3 = 0;
+				if (xoffset4 < 0) xoffset4 = 0;
+				if (xoffset5 < 0) xoffset5 = 0;
 			}
 			else
+			{
 				drawsection = 1;
+				if (animatetimer == 32)
+					S_StartSound(NULL, sfx_s3k68);
+			}
 		}
 
 		if (drawsection == 1)
 		{
+			const char *ringtext = "\x86" "50 RINGS, NO SHIELD";
+			const char *tut1text = "\x86" "PRESS " "\x82" "SPIN";
+			const char *tut2text = "\x86" "MID-" "\x82" "JUMP";
 			ttheight = 16;
 			V_DrawLevelTitle(data.spec.passedx1 + xoffset1, ttheight, 0, data.spec.passed1);
 			ttheight += V_LevelNameHeight(data.spec.passed3) + 2;
 			V_DrawLevelTitle(data.spec.passedx3 + xoffset2, ttheight, 0, data.spec.passed3);
 			ttheight += V_LevelNameHeight(data.spec.passed4) + 2;
 			V_DrawLevelTitle(data.spec.passedx4 + xoffset3, ttheight, 0, data.spec.passed4);
-		}
-		else if (data.spec.passed1[0] != '\0')
-		{
-			ttheight = 24;
-			V_DrawLevelTitle(data.spec.passedx1 + xoffset1, ttheight, 0, data.spec.passed1);
-			ttheight += V_LevelNameHeight(data.spec.passed2) + 2;
-			V_DrawLevelTitle(data.spec.passedx2 + xoffset2, ttheight, 0, data.spec.passed2);
+
+			ttheight = 108;
+			V_DrawLevelTitle(BASEVIDWIDTH/2 + xoffset4 - (V_LevelNameWidth(ringtext)/2), ttheight, 0, ringtext);
+			ttheight += V_LevelNameHeight(ringtext) + 2;
+			V_DrawLevelTitle(BASEVIDWIDTH/2 + xoffset5 - (V_LevelNameWidth(tut1text)/2), ttheight, 0, tut1text);
+			ttheight += V_LevelNameHeight(tut1text) + 2;
+			V_DrawLevelTitle(BASEVIDWIDTH/2 + xoffset6 - (V_LevelNameWidth(tut2text)/2), ttheight, 0, tut2text);
 		}
 		else
 		{
-			ttheight = 24 + (V_LevelNameHeight(data.spec.passed2)/2) + 2;
-			V_DrawLevelTitle(data.spec.passedx2 + xoffset1, ttheight, 0, data.spec.passed2);
+			if (data.spec.passed1[0] != '\0')
+			{
+				ttheight = 24;
+				V_DrawLevelTitle(data.spec.passedx1 + xoffset1, ttheight, 0, data.spec.passed1);
+				ttheight += V_LevelNameHeight(data.spec.passed2) + 2;
+				V_DrawLevelTitle(data.spec.passedx2 + xoffset2, ttheight, 0, data.spec.passed2);
+			}
+			else
+			{
+				ttheight = 24 + (V_LevelNameHeight(data.spec.passed2)/2) + 2;
+				V_DrawLevelTitle(data.spec.passedx2 + xoffset1, ttheight, 0, data.spec.passed2);
+			}
+
+			V_DrawScaledPatch(152 + xoffset3, 108, 0, data.spec.bonuspatch);
+			V_DrawTallNum(BASEVIDWIDTH + xoffset3 - 68, 109, 0, data.spec.bonus.points);
+			V_DrawScaledPatch(152 + xoffset4, 124, 0, data.spec.pscore);
+			V_DrawTallNum(BASEVIDWIDTH + xoffset4 - 68, 125, 0, data.spec.score);
+
+			// Draw continues!
+			if (!multiplayer /* && (data.spec.continues & 0x80) */) // Always draw outside of netplay
+			{
+				UINT8 continues = data.spec.continues & 0x7F;
+
+				V_DrawScaledPatch(152 + xoffset5, 150, 0, data.spec.pcontinues);
+				for (i = 0; i < continues; ++i)
+				{
+					if ((data.spec.continues & 0x80) && i == continues-1 && (endtic < 0 || intertic%20 < 10))
+						break;
+					V_DrawContinueIcon(246 + xoffset5 - (i*12), 162, 0, *data.spec.playerchar, *data.spec.playercolor);
+				}
+			}
 		}
 
 		// draw the emeralds
 		//if (intertic & 1)
 		{
+			boolean drawthistic = !(ALL7EMERALDS(emeralds) && (intertic & 1));
 			INT32 emeraldx = 152 - 3*28;
-			INT32 em = (gamemap - sstage_start);
+			INT32 em = P_GetNextEmerald();
 
-			for (i = 0; i < 7; ++i)
+			if (em == 7)
 			{
-				if ((i != em) && !(intertic & 1) && (emeralds & (1 << i)))
-					V_DrawScaledPatch(emeraldx, 74, 0, emeraldpics[0][i]);
-				emeraldx += 28;
+				if (!stagefailed)
+				{
+					fixed_t adjust = 2*(FINESINE(FixedAngle((intertic + 1)<<(FRACBITS-4)) & FINEMASK));
+					V_DrawFixedPatch(152<<FRACBITS, (74<<FRACBITS) - adjust, FRACUNIT, 0, emeraldpics[0][em], NULL);
+				}
 			}
-
-			if (em < 7)
+			else if (em < 7)
 			{
 				static UINT8 emeraldbounces = 0;
 				static INT32 emeraldmomy = 20;
 				static INT32 emeraldy = -40;
 
+				if (drawthistic)
+					for (i = 0; i < 7; ++i)
+					{
+						if ((i != em) && (emeralds & (1 << i)))
+							V_DrawScaledPatch(emeraldx, 74, 0, emeraldpics[0][i]);
+						emeraldx += 28;
+					}
+
 				emeraldx = 152 + (em-3)*28;
 
 				if (intertic <= 1)
@@ -399,7 +461,7 @@ void Y_IntermissionDrawer(void)
 				}
 				else
 				{
-					if (emeralds & (1 << em))
+					if (!stagefailed)
 					{
 						if (emeraldbounces < 3)
 						{
@@ -427,29 +489,11 @@ void Y_IntermissionDrawer(void)
 							emeraldy = 74;
 						}
 					}
-					V_DrawScaledPatch(emeraldx, emeraldy, 0, emeraldpics[0][em]);
+					if (drawthistic)
+						V_DrawScaledPatch(emeraldx, emeraldy, 0, emeraldpics[0][em]);
 				}
 			}
 		}
-
-		V_DrawScaledPatch(152, 108, 0, data.spec.bonuspatch);
-		V_DrawTallNum(BASEVIDWIDTH - 68, 109, 0, data.spec.bonus.points);
-		V_DrawScaledPatch(152, 124, 0, data.spec.pscore);
-		V_DrawTallNum(BASEVIDWIDTH - 68, 125, 0, data.spec.score);
-
-		// Draw continues!
-		if (!multiplayer /* && (data.spec.continues & 0x80) */) // Always draw outside of netplay
-		{
-			UINT8 continues = data.spec.continues & 0x7F;
-
-			V_DrawScaledPatch(152, 150, 0, data.spec.pcontinues);
-			for (i = 0; i < continues; ++i)
-			{
-				if ((data.spec.continues & 0x80) && i == continues-1 && (endtic < 0 || intertic%20 < 10))
-					break;
-				V_DrawContinueIcon(246 - (i*12), 162, 0, *data.spec.playerchar, *data.spec.playercolor);
-			}
-		}
 	}
 	else if (intertype == int_match || intertype == int_race)
 	{
@@ -831,7 +875,7 @@ void Y_Ticker(void)
 			if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && !demoplayback)
 			{
 				if (M_UpdateUnlockablesAndExtraEmblems())
-					S_StartSound(NULL, sfx_ncitem);
+					S_StartSound(NULL, sfx_s3k68);
 
 				G_SaveGameData();
 			}
@@ -850,7 +894,7 @@ void Y_Ticker(void)
 	{
 		INT32 i;
 		UINT32 oldscore = data.spec.score;
-		boolean skip = false;
+		boolean skip = false, super = false;
 
 		if (!intertic) // first time only
 		{
@@ -862,15 +906,22 @@ void Y_Ticker(void)
 			return;
 
 		for (i = 0; i < MAXPLAYERS; i++)
-			if (playeringame[i] && (players[i].cmd.buttons & BT_USE))
-				skip = true;
+			if (playeringame[i])
+			{
+				if (players[i].cmd.buttons & BT_USE)
+					skip = true;
+				if (players[i].charflags & SF_SUPER)
+					super = true;
+			}
 
-		if ((data.spec.continues & 0x80) && tallydonetic != -1)
+		if (tallydonetic != -1 && ((data.spec.continues & 0x80) || (super && ALL7EMERALDS(emeralds))))
 		{
 			if ((intertic - tallydonetic) > (3*TICRATE)/2)
 			{
 				endtic = intertic + 4*TICRATE; // 4 second pause after end of tally
-				S_StartSound(NULL, sfx_s3kac); // bingly-bingly-bing!
+				if (data.spec.continues & 0x80)
+					S_StartSound(NULL, sfx_s3kac); // bingly-bingly-bing!
+
 			}
 			return;
 		}
@@ -887,7 +938,7 @@ void Y_Ticker(void)
 		if (!data.spec.bonus.points)
 		{
 			tallydonetic = intertic;
-			if (!(data.spec.continues & 0x80)) // don't set endtic yet!
+			if (!((data.spec.continues & 0x80) || (super && ALL7EMERALDS(emeralds)))) // don't set endtic yet!
 				endtic = intertic + 4*TICRATE; // 4 second pause after end of tally
 
 			S_StartSound(NULL, (gottoken ? sfx_token : sfx_chchng)); // cha-ching!
@@ -896,7 +947,7 @@ void Y_Ticker(void)
 			if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && !demoplayback)
 			{
 				if (M_UpdateUnlockablesAndExtraEmblems())
-					S_StartSound(NULL, sfx_ncitem);
+					S_StartSound(NULL, sfx_s3k68);
 
 				G_SaveGameData();
 			}
@@ -1132,11 +1183,7 @@ void Y_StartIntermission(void)
 			data.coop.ptotal = W_CachePatchName("YB_TOTAL", PU_STATIC);
 
 			// get act number
-			if (mapheaderinfo[prevmap]->actnum)
-				data.coop.ttlnum = W_CachePatchName(va("TTL%.2d", mapheaderinfo[prevmap]->actnum),
-					PU_STATIC);
-			else
-				data.coop.ttlnum = W_CachePatchName("TTL01", PU_STATIC);
+			data.coop.actnum = mapheaderinfo[gamemap-1]->actnum;
 
 			// get background patches
 			widebgpatch = W_CachePatchName("INTERSCW", PU_STATIC);
@@ -1286,7 +1333,7 @@ void Y_StartIntermission(void)
 				data.spec.passed1[sizeof data.spec.passed1 - 1] = '\0';
 				strcpy(data.spec.passed2, "GOT THEM ALL!");
 
-				if (skins[players[consoleplayer].skin].flags & SF_SUPER)
+				if (players[consoleplayer].charflags & SF_SUPER)
 				{
 					strcpy(data.spec.passed3, "CAN NOW BECOME");
 					snprintf(data.spec.passed4,
@@ -1307,6 +1354,11 @@ void Y_StartIntermission(void)
 				else
 					strcpy(data.spec.passed1, "YOU GOT");
 				strcpy(data.spec.passed2, "A CHAOS EMERALD");
+				if (P_GetNextEmerald() > 6)
+				{
+					data.spec.passed2[15] = '?';
+					data.spec.passed2[16] = '\0';
+				}
 			}
 			data.spec.passedx1 = (BASEVIDWIDTH - V_LevelNameWidth(data.spec.passed1))/2;
 			data.spec.passedx2 = (BASEVIDWIDTH - V_LevelNameWidth(data.spec.passed2))/2;
@@ -1698,6 +1750,26 @@ static void Y_SetRingBonus(player_t *player, y_bonus_t *bstruct)
 	bstruct->points = max(0, (player->rings) * 100);
 }
 
+//
+// Y_SetNightsBonus
+//
+static void Y_SetNightsBonus(player_t *player, y_bonus_t *bstruct)
+{
+	strncpy(bstruct->patch, "YB_NIGHT", sizeof(bstruct->patch));
+	bstruct->display = true;
+	bstruct->points = player->totalmarescore;
+}
+
+//
+// Y_SetLapBonus
+//
+static void Y_SetLapBonus(player_t *player, y_bonus_t *bstruct)
+{
+	strncpy(bstruct->patch, "YB_LAP", sizeof(bstruct->patch));
+	bstruct->display = true;
+	bstruct->points = max(0, player->totalmarebonuslap * 1000);
+}
+
 //
 // Y_SetLinkBonus
 //
@@ -1745,7 +1817,7 @@ static void Y_SetPerfectBonus(player_t *player, y_bonus_t *bstruct)
 			if (!playeringame[i]) continue;
 			sharedringtotal += players[i].rings;
 		}
-		if (!sharedringtotal || sharedringtotal < nummaprings)
+		if (!sharedringtotal || nummaprings == -1 || sharedringtotal < nummaprings)
 			data.coop.gotperfbonus = 0;
 		else
 			data.coop.gotperfbonus = 1;
@@ -1759,7 +1831,7 @@ static void Y_SetPerfectBonus(player_t *player, y_bonus_t *bstruct)
 
 // This list can be extended in the future with SOC/Lua, perhaps.
 typedef void (*bonus_f)(player_t *, y_bonus_t *);
-bonus_f bonuses_list[4][4] = {
+bonus_f bonuses_list[6][4] = {
 	{
 		Y_SetNullBonus,
 		Y_SetNullBonus,
@@ -1784,6 +1856,18 @@ bonus_f bonuses_list[4][4] = {
 		Y_SetRingBonus,
 		Y_SetPerfectBonus,
 	},
+	{
+		Y_SetNullBonus,
+		Y_SetNightsBonus,
+		Y_SetLapBonus,
+		Y_SetNullBonus,
+	},
+	{
+		Y_SetNullBonus,
+		Y_SetLinkBonus,
+		Y_SetLapBonus,
+		Y_SetNullBonus,
+	},
 };
 
 
@@ -1822,7 +1906,9 @@ static void Y_AwardCoopBonuses(void)
 				players[i].score = MAXSCORE;
 		}
 
-		ptlives = (!ultimatemode && !modeattacking && players[i].lives != 0x7f) ? max((players[i].score/50000) - (oldscore/50000), 0) : 0;
+		ptlives = min(
+			((!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);
 
@@ -1857,8 +1943,8 @@ static void Y_AwardSpecialStageBonus(void)
 
 		if (!playeringame[i] || players[i].lives < 1) // not active or game over
 			Y_SetNullBonus(&players[i], &localbonus);
-		else if (useNightsSS) // Link instead of Score
-			Y_SetLinkBonus(&players[i], &localbonus);
+		else if (maptol & TOL_NIGHTS) // Mare score instead of Rings
+			Y_SetNightsBonus(&players[i], &localbonus);
 		else
 			Y_SetRingBonus(&players[i], &localbonus);
 		players[i].score += localbonus.points;
@@ -1866,7 +1952,9 @@ static void Y_AwardSpecialStageBonus(void)
 			players[i].score = MAXSCORE;
 
 		// grant extra lives right away since tally is faked
-		ptlives = (!ultimatemode && !modeattacking && players[i].lives != 0x7f) ? max((players[i].score/50000) - (oldscore/50000), 0) : 0;
+		ptlives = min(
+			((!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);
 
@@ -1938,7 +2026,6 @@ static void Y_UnloadData(void)
 	{
 		case int_coop:
 			// unload the coop and single player patches
-			UNLOAD(data.coop.ttlnum);
 			UNLOAD(data.coop.bonuspatches[3]);
 			UNLOAD(data.coop.bonuspatches[2]);
 			UNLOAD(data.coop.bonuspatches[1]);