diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 0cb1d4497f1bc26458c97c0f94ef51ce44879e67..dd1234f0aefeda321aca5d0366f6699695dff33a 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -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);
@@ -822,7 +823,6 @@ void D_RegisterClientCommands(void)
 	COM_AddCommand("getallemeralds", Command_Getallemeralds_f);
 	COM_AddCommand("resetemeralds", Command_Resetemeralds_f);
 	COM_AddCommand("setrings", Command_Setrings_f);
-	COM_AddCommand("setspheres", Command_Setspheres_f);
 	COM_AddCommand("setlives", Command_Setlives_f);
 	COM_AddCommand("setcontinues", Command_Setcontinues_f);
 	COM_AddCommand("devmode", Command_Devmode_f);
diff --git a/src/d_player.h b/src/d_player.h
index 59f9ba42ac67070cc83bf086448255a29082d77b..dd0643bd41bc4a95cb5c257ed8b2774721abd850 100644
--- a/src/d_player.h
+++ b/src/d_player.h
@@ -456,16 +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)
+	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
diff --git a/src/dehacked.c b/src/dehacked.c
index 127b367c803aa5c06676414ba10e1fff16a00d47..9b2cf7fc0bd57adfc4a2c370ae173f8e5c43b4c7 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -1203,6 +1203,8 @@ static void readlevelheader(MYFILE *f, INT32 num)
 					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"))
@@ -7316,6 +7318,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!)
diff --git a/src/doomstat.h b/src/doomstat.h
index 24b9e5753d644c6c0cb7d3cc4d5f95ce143f0848..651540ecc833afc3ac975231f4ee0c63a781072d 100644
--- a/src/doomstat.h
+++ b/src/doomstat.h
@@ -244,6 +244,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
diff --git a/src/g_game.c b/src/g_game.c
index 52358a8b9cb2c0f923902b40d2313ecbb8ffe003..819d5e938051fa05a072a1b50710f59be45f5a8f 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -2230,6 +2230,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())
 	{
@@ -4664,6 +4666,7 @@ void G_GhostTicker(void)
 				p->next = g->next;
 			else
 				ghosts = g->next;
+			Z_Free(g);
 			continue;
 		}
 		p = g;
@@ -5664,29 +5667,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;
@@ -5885,8 +5887,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/lua_maplib.c b/src/lua_maplib.c
index b929fc0bc643c6ac72c581baf79eff67b0428b27..28fe8c75f7c0f955d51f18e02a60e92109a83072 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -1797,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_playerlib.c b/src/lua_playerlib.c
index ff62f2459d514406dd80f0087257179603a0f013..f973061f1d6ca7c928b303129208ad279fb9048d 100644
--- a/src/lua_playerlib.c
+++ b/src/lua_playerlib.c
@@ -290,12 +290,20 @@ 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"))
@@ -304,8 +312,18 @@ static int player_get(lua_State *L)
 		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"))
@@ -570,12 +588,20 @@ 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"))
@@ -584,8 +610,18 @@ static int player_set(lua_State *L)
 		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/m_cheat.c b/src/m_cheat.c
index 56bef02a35042f9066ac0724131443e20c98d9a2..9e64c9ab87fbfeb9ae47e4b44d786a35cbda57eb 100644
--- a/src/m_cheat.c
+++ b/src/m_cheat.c
@@ -880,28 +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) || !(maptol & TOL_NIGHTS))
+		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
-
-		G_SetGameModified(multiplayer);
-	}
-}
-
-void Command_Setspheres_f(void)
-{
-	REQUIRE_INLEVEL;
-	REQUIRE_SINGLEPLAYER;
-	REQUIRE_NOULTIMATE;
-	REQUIRE_PANDORA;
-
-	if (COM_Argc() > 1)
-	{
-		// P_GivePlayerRings does value clamping
-		players[consoleplayer].spheres = 0;
-		P_GivePlayerSpheres(&players[consoleplayer], atoi(COM_Argv(1)));
+		}
+		else
+		{
+			players[consoleplayer].spheres = 0;
+			P_GivePlayerSpheres(&player[consoleplayer], atoi(COM_Argv(1)));
+			// no totalsphere addition to revert
+		}
 
 		G_SetGameModified(multiplayer);
 	}
@@ -957,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
@@ -1164,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)));
@@ -1185,7 +1171,7 @@ 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, false);
@@ -1194,11 +1180,52 @@ void OP_NightsObjectplace(player_t *player)
 	// 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);
 	}
 
diff --git a/src/m_cheat.h b/src/m_cheat.h
index 31f650b3fdeff5f2aaa24f4a346ac81106f63d92..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;
@@ -51,7 +51,6 @@ void Command_Savecheckpoint_f(void);
 void Command_Getallemeralds_f(void);
 void Command_Resetemeralds_f(void);
 void Command_Setrings_f(void);
-void Command_Setspheres_f(void);
 void Command_Setlives_f(void);
 void Command_Setcontinues_f(void);
 void Command_Devmode_f(void);
diff --git a/src/p_inter.c b/src/p_inter.c
index a4493c466f80bebad51248ef41c034f6a80a7573..57b47f1c6ae74bdb70d687b723e57a587632f2dd 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -1005,13 +1005,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
 				{
@@ -1134,6 +1128,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
@@ -1143,6 +1138,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)
diff --git a/src/p_map.c b/src/p_map.c
index c38f5471b2c94469673105640f34fc18c5e37d10..6e5e03a8d3e85c703427984cd40fcb68dc467a40 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -1097,24 +1097,35 @@ 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 && (
+		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();
+			}
 		}
 		droneobj->extravalue1 = pl->flyangle;
 		droneobj->extravalue2 = (INT32)leveltime + TICRATE;
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 7cf43738463bce64297f60f54aa09a5d3687de03..22d43f358aa33133bd98f92d052325c9c61a2d49 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -198,15 +198,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);
@@ -387,15 +396,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);
diff --git a/src/p_setup.c b/src/p_setup.c
index c62f281b3ee461eed4ccce49121c6a23d988bbb8..2361d1efd9dc63a2786199668154d761dd3fbc87 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"
@@ -2381,12 +2382,16 @@ static void P_LevelInitStuff(void)
 		 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].realtime =\
-		 players[i].exiting = 0;
+		 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;
diff --git a/src/p_spec.c b/src/p_spec.c
index 103312b528e0a6c900ca5820604e9ccc6b0c7e07..1d842a0bbdf0b363bedcace484af32bcf8f98947 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -1493,10 +1493,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 +1504,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)
diff --git a/src/p_user.c b/src/p_user.c
index 100663d91c17fcbafc397c37f4c2c3a24ea97b61..e7f5f3248e8ed219d2efece775eaeb70cc79acfa 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -387,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
@@ -574,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));
 
@@ -633,7 +639,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)
@@ -648,6 +654,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;
@@ -662,7 +670,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);
@@ -680,6 +688,8 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
 	}
 
 	oldmare = player->mare;
+	oldmarelap = player->marelap;
+	oldmarebonuslap = player->marebonuslap;
 
 	if (!P_TransferToNextMare(player))
 	{
@@ -707,6 +717,8 @@ 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;
@@ -725,6 +737,7 @@ 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;
 
@@ -738,19 +751,24 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
 		// 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
 		if (!(netgame||multiplayer) && P_IsLocalPlayer(player))
 			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->spheres = player->rings = 0;
 	}
@@ -765,6 +783,13 @@ 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));
+
 	player->powers[pw_carry] = CR_NIGHTSMODE;
 }
 
@@ -6174,7 +6199,6 @@ static void P_NiGHTSMovement(player_t *player)
 //		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);
 
-
 	if (player->mo->z < player->mo->floorz)
 		player->mo->z = player->mo->floorz;
 
@@ -9808,7 +9832,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.
@@ -9932,7 +9968,8 @@ 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 || player->powers[pw_tailsfly])
 				player->pflags |= PF_APPLYAUTOBRAKE;
diff --git a/src/r_data.c b/src/r_data.c
index 169b042015066ca459432293604829717d8bd27b..6aede0396fedcbe42bc9de3da05cc78e5f5dafc9 100644
--- a/src/r_data.c
+++ b/src/r_data.c
@@ -1362,7 +1362,7 @@ INT32 R_ColormapNumForName(char *name)
 	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].fadeend = 31;
 	extra_colormaps[num_extra_colormaps].fog = 0;
 
 	num_extra_colormaps++;
@@ -1390,7 +1390,7 @@ INT32 R_CreateColormap(char *p1, char *p2, char *p3)
 	size_t mapnum = num_extra_colormaps;
 	size_t i;
 	UINT32 cr, cg, cb, maskcolor, fadecolor;
-	UINT32 fadestart = 0, fadeend = 33, fadedist = 33;
+	UINT32 fadestart = 0, fadeend = 31, fadedist = 31;
 
 #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] == '#')
@@ -1431,12 +1431,12 @@ INT32 R_CreateColormap(char *p1, char *p2, char *p3)
 		// 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)
+		if (fadestart > 30)
 			fadestart = 0;
-		if (fadeend > 33 || fadeend < 1)
-			fadeend = 33;
+		if (fadeend > 31 || fadeend < 1)
+			fadeend = 31;
 		fadedist = fadeend - fadestart;
-		fog = NUMFROMCHAR(p2[1]) ? 1 : 0;
+		fog = NUMFROMCHAR(p2[1]);
 	}
 #undef getnum
 
@@ -1537,7 +1537,7 @@ void R_CreateColormap2(char *p1, char *p2, char *p3)
 	size_t i;
 	char *colormap_p;
 	UINT32 cr, cg, cb, maskcolor, fadecolor;
-	UINT32 fadestart = 0, fadeend = 33, fadedist = 33;
+	UINT32 fadestart = 0, fadeend = 31, fadedist = 31;
 
 #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] == '#')
@@ -1578,12 +1578,12 @@ void R_CreateColormap2(char *p1, char *p2, char *p3)
 		// 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)
+		if (fadestart > 30)
 			fadestart = 0;
-		if (fadeend > 33 || fadeend < 1)
-			fadeend = 33;
+		if (fadeend > 31 || fadeend < 1)
+			fadeend = 31;
 		fadedist = fadeend - fadestart;
-		fog = NUMFROMCHAR(p2[1]) ? 1 : 0;
+		fog = NUMFROMCHAR(p2[1]);
 	}
 #undef getnum
 
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_things.c b/src/r_things.c
index 9e858aba9b6f9aa4ec6cd4523ad9199384469f19..fb4664d905c86fdaa11420065d432e81525f624b 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -983,7 +983,8 @@ static void R_SplitSprite(vissprite_t *sprite)
 
 			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);
 
@@ -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;
diff --git a/src/v_video.c b/src/v_video.c
index d93109bafd3f03542a258082d531bff90fc3fcef..765965ffdc83f5bc3845d6a875a8184bca8ea348 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);
@@ -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);
 }
 
 //
@@ -1642,6 +1642,7 @@ 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;
 	}
 
 	switch (option & V_SPACINGMASK)
@@ -1701,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
 		{
@@ -1754,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);
@@ -1813,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
 		{
@@ -1860,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);
@@ -1917,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
 		{
@@ -1960,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)
@@ -2014,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;
@@ -2126,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);
@@ -2178,6 +2183,7 @@ void V_DrawLevelTitle(INT32 x, INT32 y, INT32 option, const char *string)
 		dupx = dupy = 1;
 		scrwidth = vid.width/vid.dupx;
 		left = (scrwidth - BASEVIDWIDTH)/2;
+		scrwidth -= left;
 	}
 
 	for (;;ch++)
@@ -2207,10 +2213,9 @@ void V_DrawLevelTitle(INT32 x, INT32 y, INT32 option, const char *string)
 
 		w = SHORT(lt_font[c]->width) * dupx;
 
-		if (cx+left > scrwidth)
+		if (cx > scrwidth)
 			break;
-		//left boundary check
-		if (cx+left + w < 0)
+		if (cx+left + w < 0) //left boundary check
 		{
 			cx += w;
 			continue;
diff --git a/src/y_inter.c b/src/y_inter.c
index fff6d2ebffbd7cb466be12cc309478e0059def04..598f3c8ab9c4bec44b395aed28ccc2fc321e7b20 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -1874,7 +1874,9 @@ static void Y_AwardCoopBonuses(void)
 				players[i].score = MAXSCORE;
 		}
 
-		ptlives = (!ultimatemode && !modeattacking && players[i].lives != INFLIVES) ? 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);
 
@@ -1918,7 +1920,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 != INFLIVES) ? 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);