diff --git a/appveyor.yml b/appveyor.yml
index b9f84f395a5afccc741f0999b64e766695fce7d2..348b727b1d8a8f31dc202b6c822d1420325e7ddc 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,4 +1,4 @@
-version: 2.2.9.{branch}-{build}
+version: 2.2.10.{branch}-{build}
 os: MinGW
 
 environment:
diff --git a/assets/CMakeLists.txt b/assets/CMakeLists.txt
index 3ea7c28dfb0b950b34b46d2866001ce2d2017380..ef228759c2f00721047884590d1f0443b6034463 100644
--- a/assets/CMakeLists.txt
+++ b/assets/CMakeLists.txt
@@ -22,8 +22,7 @@ set(SRB2_ASSET_INSTALL ON
 set(SRB2_ASSET_HASHED
 "srb2.pk3;\
 player.dta;\
-zones.pk3;\
-patch.pk3"
+zones.pk3"
 	CACHE STRING "Asset filenames to apply MD5 checks. No spaces between entries!"
 )
 
diff --git a/extras/conf/SRB2-22.cfg b/extras/conf/SRB2-22.cfg
index b780ff75f02b2eab69886c10575428cdb661dc97..5580140737716a175c69bba08b8a51f3422b544c 100644
--- a/extras/conf/SRB2-22.cfg
+++ b/extras/conf/SRB2-22.cfg
@@ -4037,16 +4037,16 @@ thingtypes
 		{
 			title = "Egg Mobile";
 			sprite = "EGGMA1";
-			width = 24;
-			height = 76;
+			width = 36;
+			height = 84;
 			flags4text = "[4] End level on death";
 		}
 		201
 		{
 			title = "Egg Slimer";
 			sprite = "EGGNA1";
-			width = 24;
-			height = 76;
+			width = 36;
+			height = 84;
 			flags4text = "[4] End level on death";
 			flags8text = "[8] Speed up when hit";
 		}
@@ -4054,7 +4054,7 @@ thingtypes
 		{
 			title = "Sea Egg";
 			sprite = "EGGOA1";
-			width = 32;
+			width = 36;
 			height = 116;
 			flags4text = "[4] End level on death";
 		}
@@ -4062,8 +4062,8 @@ thingtypes
 		{
 			title = "Egg Colosseum";
 			sprite = "EGGPA1";
-			width = 24;
-			height = 76;
+			width = 36;
+			height = 84;
 			flags4text = "[4] End level on death";
 		}
 		204
@@ -6298,10 +6298,10 @@ thingtypes
 		}
 		1602
 		{
-			title = "Pian";
-			sprite = "NTPNALAR";
+			title = "Nightopian";
+			sprite = "NTPNA1";
 			width = 16;
-			height = 32;
+			height = 40;
 		}
 	}
 
@@ -6880,6 +6880,20 @@ thingtypes
 		}
 	}
 
+	tutorial
+	{
+		color = 10; // Green
+		title = "Tutorial";
+		
+		799
+		{
+			title = "Tutorial Plant";
+			sprite = "TUPFH0";
+			width = 40;
+			height = 144;
+			parametertext = "Start frame";
+		}
+
 	flickies
 	{
 		color = 10; // Green
diff --git a/extras/conf/udb/Includes/SRB222_things.cfg b/extras/conf/udb/Includes/SRB222_things.cfg
index 113c1a4c26eb08a7109da2afeba747dfb1cdda14..0407741fc9871c421340e19aa4a1b8ecd0a76476 100644
--- a/extras/conf/udb/Includes/SRB222_things.cfg
+++ b/extras/conf/udb/Includes/SRB222_things.cfg
@@ -2528,10 +2528,10 @@ dreamhill
 	}
 	1602
 	{
-		title = "Pian";
-		sprite = "NTPNALAR";
+		title = "Nightopian";
+		sprite = "NTPNA1";
 		width = 16;
-		height = 32;
+		height = 40;
 	}
 }
 
diff --git a/src/Makefile b/src/Makefile
index 9659a4994c1dce6e94981ff090b0b4f1e9a171bf..c1aa3574283131db2b8d6bf2ff59efe2bb52211b 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -2,8 +2,8 @@
 # the poly3 Makefile adapted over and over...
 #
 # Copyright 1998-2000 DooM Legacy Team.
-# Copyright 2020-2021 James R.
-# Copyright 2003-2021 Sonic Team Junior.
+# Copyright 2020-2022 James R.
+# Copyright 2003-2022 Sonic Team Junior.
 #
 # This program is free software distributed under the
 # terms of the GNU General Public License, version 2.
diff --git a/src/am_map.c b/src/am_map.c
index 24379e2f13d91822c8aed096ae3f972a95a0bfee..65a57c09e2f0f85655f7ce50b7c2195e418018f1 100644
--- a/src/am_map.c
+++ b/src/am_map.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/am_map.h b/src/am_map.h
index 022a7208b3fdf5a94f38101f5f1daccb2ff802ed..89c4ad9fab22538aae10b051c579678b7d9b5b15 100644
--- a/src/am_map.h
+++ b/src/am_map.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/apng.c b/src/apng.c
index 36b205c60998099009c21ce6a49fe97a71d44faa..f4c08d979faef761ac3ceed6fed93489162eee68 100644
--- a/src/apng.c
+++ b/src/apng.c
@@ -1,5 +1,5 @@
 /*
-Copyright 2019-2021, James R.
+Copyright 2019-2022, James R.
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
diff --git a/src/apng.h b/src/apng.h
index 893b523cbcacb8fd9f58a5be59b8dac3ef787596..6b934742486461951377998ae02e687cc0a1e1ac 100644
--- a/src/apng.h
+++ b/src/apng.h
@@ -1,5 +1,5 @@
 /*
-Copyright 2019-2021, James R.
+Copyright 2019-2022, James R.
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
diff --git a/src/asm_defs.inc b/src/asm_defs.inc
index 9074f20f86d53523bf19030f01fb9e4e63e6283d..a8c60f19ea9be18916ac626caf384ffc77a7436c 100644
--- a/src/asm_defs.inc
+++ b/src/asm_defs.inc
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/b_bot.c b/src/b_bot.c
index bf2dbbb68586aab8390557568622d2e9e6f89e0a..775a13e294cf31e8070a9f6fc51894f91317496c 100644
--- a/src/b_bot.c
+++ b/src/b_bot.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2007-2016 by John "JTE" Muniz.
-// Copyright (C) 2011-2021 by Sonic Team Junior.
+// Copyright (C) 2011-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -17,6 +17,7 @@
 #include "p_local.h"
 #include "b_bot.h"
 #include "lua_hook.h"
+#include "i_system.h" // I_BaseTiccmd
 
 void B_UpdateBotleader(player_t *player)
 {
@@ -132,17 +133,17 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 	// Update catchup_tics
 	if (mem->thinkstate == AI_SPINFOLLOW)
 	{
-		mem-> catchup_tics = 0;
+		mem->catchup_tics = 0;
 	}
 	else if (dist > followmax || zdist > comfortheight || stalled)
 	{
-		mem-> catchup_tics = min(mem-> catchup_tics + 2, 70);
-		if (mem-> catchup_tics >= 70)
+		mem->catchup_tics = min(mem->catchup_tics + 2, 70);
+		if (mem->catchup_tics >= 70)
 			mem->thinkstate = AI_CATCHUP;
 	}
 	else
 	{
-		mem-> catchup_tics = max(mem-> catchup_tics - 1, 0);
+		mem->catchup_tics = max(mem->catchup_tics - 1, 0);
 		if (mem->thinkstate == AI_CATCHUP)
 			mem->thinkstate = AI_FOLLOW;
 	}
@@ -317,7 +318,6 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 		{
 			// Copy inputs
 			cmd->angleturn = (sonic->angle) >> 16; // NOT FRACBITS DAMNIT
-			bot->drawangle = ang;
 			cmd->forwardmove = 8 * pcmd->forwardmove / 10;
 			cmd->sidemove = 8 * pcmd->sidemove / 10;
 		}
@@ -344,7 +344,7 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 		else if (!jump_last && !(bot->pflags & PF_JUMPED) //&& !(player->pflags & PF_SPINNING)
 			&& ((zdist > 32*scale && player->pflags & PF_JUMPED) // Following
 				|| (zdist > 64*scale && mem->thinkstate == AI_CATCHUP) // Vertical catch-up
-				|| (stalled && mem-> catchup_tics > 20 && bot->powers[pw_carry] == CR_NONE)
+				|| (stalled && mem->catchup_tics > 20 && bot->powers[pw_carry] == CR_NONE)
 				//|| (bmom < scale>>3 && dist > followthres && !(bot->powers[pw_carry])) // Stopped & not in carry state
 				|| (bot->pflags & PF_SPINNING && !(bot->pflags & PF_JUMPED)))) // Spinning
 					jump = true;
@@ -371,6 +371,8 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 
 void B_BuildTiccmd(player_t *player, ticcmd_t *cmd)
 {
+	G_CopyTiccmd(cmd, I_BaseTiccmd(), 1); // empty, or external driver
+
 	// Can't build a ticcmd if we aren't spawned...
 	if (!player->mo)
 		return;
@@ -390,7 +392,7 @@ void B_BuildTiccmd(player_t *player, ticcmd_t *cmd)
 		return;
 
 	// Make sure we have a valid main character to follow
-	 B_UpdateBotleader(player);
+	B_UpdateBotleader(player);
 	if (!player->botleader)
 		return;
 
@@ -403,7 +405,7 @@ void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward
 {
 	player_t *player = mo->player;
 	// don't try to do stuff if your sonic is in a minecart or something
-	if (&player->botleader && player->botleader->powers[pw_carry] && player->botleader->powers[pw_carry] != CR_PLAYER)
+	if (player->botleader && player->botleader->powers[pw_carry] && player->botleader->powers[pw_carry] != CR_PLAYER)
 		return;
 	// Turn the virtual keypresses into ticcmd_t.
 	if (twodlevel || mo->flags2 & MF2_TWOD) {
@@ -593,25 +595,43 @@ void B_HandleFlightIndicator(player_t *player)
 {
 	mobj_t *tails = player->mo;
 	botmem_t *mem = &player->botmem;
+	boolean shouldExist;
+
 	if (!tails)
 		return;
 
-	if (mem->thinkstate == AI_THINKFLY && player->bot == BOT_2PAI && tails->health)
+	shouldExist = (mem->thinkstate == AI_THINKFLY) && player->botleader
+		&& player->bot == BOT_2PAI && player->playerstate == PST_LIVE;
+
+	// check whether the indicator doesn't exist
+	if (P_MobjWasRemoved(tails->hnext))
 	{
-		if (!tails->hnext)
-		{
-			P_SetTarget(&tails->hnext, P_SpawnMobjFromMobj(tails, 0, 0, 0, MT_OVERLAY));
-			if (tails->hnext)
-			{
-				P_SetTarget(&tails->hnext->target, tails);
-				P_SetTarget(&tails->hnext->hprev, tails);
-				P_SetMobjState(tails->hnext, S_FLIGHTINDICATOR);
-			}
-		}
+		// if it shouldn't exist, everything is fine
+		if (!shouldExist)
+			return;
+
+		// otherwise, spawn it
+		P_SetTarget(&tails->hnext, P_SpawnMobjFromMobj(tails, 0, 0, 0, MT_OVERLAY));
+		P_SetTarget(&tails->hnext->target, tails);
+		P_SetTarget(&tails->hnext->hprev, tails);
+		P_SetMobjState(tails->hnext, S_FLIGHTINDICATOR);
 	}
-	else if (tails->hnext && tails->hnext->type == MT_OVERLAY && tails->hnext->state == states+S_FLIGHTINDICATOR)
+
+	// if the mobj isn't a flight indicator, let's not mess with it
+	if (tails->hnext->type != MT_OVERLAY || (tails->hnext->state != states+S_FLIGHTINDICATOR))
+		return;
+
+	// if it shouldn't exist, remove it
+	if (!shouldExist)
 	{
 		P_RemoveMobj(tails->hnext);
 		P_SetTarget(&tails->hnext, NULL);
+		return;
 	}
+
+	// otherwise, update its visibility
+	if (P_IsLocalPlayer(player->botleader))
+		tails->hnext->flags2 &= ~MF2_DONTDRAW;
+	else
+		tails->hnext->flags2 |= MF2_DONTDRAW;
 }
diff --git a/src/b_bot.h b/src/b_bot.h
index a89cfab19535477180971b0ba428f248b678b455..c29974c505bbb7c865665beadbd42306e3c61cbe 100644
--- a/src/b_bot.h
+++ b/src/b_bot.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2007-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2021 by Sonic Team Junior.
+// Copyright (C) 2012-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/byteptr.h b/src/byteptr.h
index 4c8414fae29c7d3498a9a085f607243d261c3af9..33c2c8a4b69fb0e986ad928981995c1ea58f7a95 100644
--- a/src/byteptr.h
+++ b/src/byteptr.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -150,26 +150,78 @@ FUNCINLINE static ATTRINLINE UINT32 readulong(void *ptr)
 
 #undef DEALIGNED
 
-#define WRITESTRINGN(p,s,n) do { size_t tmp_i = 0; for (; tmp_i < n && s[tmp_i] != '\0'; tmp_i++) WRITECHAR(p, s[tmp_i]); if (tmp_i < n) WRITECHAR(p, '\0');} while (0)
-#define WRITESTRING(p,s)    do { size_t tmp_i = 0; for (;              s[tmp_i] != '\0'; tmp_i++) WRITECHAR(p, s[tmp_i]); WRITECHAR(p, '\0');} while (0)
-#define WRITEMEM(p,s,n)     do { memcpy(p, s, n); p += n; } while (0)
-
-#define SKIPSTRING(p)       while (READCHAR(p) != '\0')
-
-#define READSTRINGN(p,s,n)  ({ size_t tmp_i = 0; for (; tmp_i < n && (s[tmp_i] = READCHAR(p)) != '\0'; tmp_i++); s[tmp_i] = '\0';})
-#define READSTRING(p,s)     ({ size_t tmp_i = 0; for (;              (s[tmp_i] = READCHAR(p)) != '\0'; tmp_i++); s[tmp_i] = '\0';})
-#define READMEM(p,s,n)      ({ memcpy(s, p, n); p += n; })
-
-#if 0 // old names
-#define WRITEBYTE(p,b)      WRITEUINT8(p,b)
-#define WRITESHORT(p,b)     WRITEINT16(p,b)
-#define WRITEUSHORT(p,b)    WRITEUINT16(p,b)
-#define WRITELONG(p,b)      WRITEINT32(p,b)
-#define WRITEULONG(p,b)     WRITEUINT32(p,b)
-
-#define READBYTE(p)         READUINT8(p)
-#define READSHORT(p)        READINT16(p)
-#define READUSHORT(p)       READUINT16(p)
-#define READLONG(p)         READINT32(p)
-#define READULONG(p)        READUINT32(p)
-#endif
+#define WRITESTRINGN(p, s, n) ({                            \
+	size_t tmp_i;                                           \
+                                                            \
+	for (tmp_i = 0; tmp_i < n && s[tmp_i] != '\0'; tmp_i++) \
+		WRITECHAR(p, s[tmp_i]);                             \
+                                                            \
+	if (tmp_i < n)                                          \
+		WRITECHAR(p, '\0');                                 \
+})
+
+#define WRITESTRINGL(p, s, n) ({                                \
+	size_t tmp_i;                                               \
+                                                                \
+	for (tmp_i = 0; tmp_i < n - 1 && s[tmp_i] != '\0'; tmp_i++) \
+		WRITECHAR(p, s[tmp_i]);                                 \
+                                                                \
+	WRITECHAR(p, '\0');                                         \
+})
+
+#define WRITESTRING(p, s) ({                   \
+	size_t tmp_i;                              \
+                                               \
+	for (tmp_i = 0; s[tmp_i] != '\0'; tmp_i++) \
+		WRITECHAR(p, s[tmp_i]);                \
+                                               \
+	WRITECHAR(p, '\0');                        \
+})
+
+#define WRITEMEM(p, s, n) ({ \
+	memcpy(p, s, n);         \
+	p += n;                  \
+})
+
+#define SKIPSTRING(p) while (READCHAR(p) != '\0')
+
+#define SKIPSTRINGN(p, n) ({                 \
+	size_t tmp_i = 0;                        \
+                                             \
+	while (tmp_i < n && READCHAR(p) != '\0') \
+		tmp_i++;                             \
+})
+
+#define SKIPSTRINGL(p, n) SKIPSTRINGN(p, n)
+
+#define READSTRINGN(p, s, n) ({                           \
+	size_t tmp_i = 0;                                     \
+                                                          \
+	while (tmp_i < n && (s[tmp_i] = READCHAR(p)) != '\0') \
+		tmp_i++;                                          \
+                                                          \
+	s[tmp_i] = '\0';                                      \
+})
+
+#define READSTRINGL(p, s, n) ({                               \
+	size_t tmp_i = 0;                                         \
+                                                              \
+	while (tmp_i < n - 1 && (s[tmp_i] = READCHAR(p)) != '\0') \
+		tmp_i++;                                              \
+                                                              \
+	s[tmp_i] = '\0';                                          \
+})
+
+#define READSTRING(p, s) ({                  \
+	size_t tmp_i = 0;                        \
+                                             \
+	while ((s[tmp_i] = READCHAR(p)) != '\0') \
+		tmp_i++;                             \
+                                             \
+	s[tmp_i] = '\0';                         \
+})
+
+#define READMEM(p, s, n) ({ \
+	memcpy(s, p, n);        \
+	p += n;                 \
+})
diff --git a/src/command.c b/src/command.c
index ae4a7178e437c9039ae4717defb377c091fad215..50310f11255b57d30228a65cfe26b676c5c89497 100644
--- a/src/command.c
+++ b/src/command.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/command.h b/src/command.h
index 34fd15963262d6ec4e4416ac8cbb514301f72d6f..30d7e5bbe6af641b67c60acadf81bd1054b1c1e4 100644
--- a/src/command.h
+++ b/src/command.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/config.h.in b/src/config.h.in
index db794cccc82a59eb378f53f9adaa4d5d1bd3cb20..587a881c73bfb7fa7b55e38920501c0a49e7643c 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -35,10 +35,11 @@
  * Last updated 2020 / 09 / 27 - v2.2.7 - patch.pk3
  * Last updated 2020 / 10 / 02 - v2.2.8 - patch.pk3
  * Last updated 2021 / 05 / 06 - v2.2.9 - patch.pk3 & zones.pk3
+ * Last updated 2022 / 03 / 06 - v2.2.10 - main assets
  */
-#define ASSET_HASH_SRB2_PK3   "0277c9416756627004e83cbb5b2e3e28"
-#define ASSET_HASH_ZONES_PK3  "f8f3e2b5deacf40f14e36686a07d44bb"
-#define ASSET_HASH_PLAYER_DTA "49dad7b24634c89728cc3e0b689e12bb"
+#define ASSET_HASH_SRB2_PK3   "ad911f29a28a18968ee5b2d11c2acb39"
+#define ASSET_HASH_ZONES_PK3  "86ae55cae4e0a93ceda868635706a093"
+#define ASSET_HASH_PLAYER_DTA "2e7aaae8a6b1b77d90ffe7606ceadb6c"
 #ifdef USE_PATCH_DTA
 #define ASSET_HASH_PATCH_PK3  "7d467a883f7887b3c311798ee2f56b6a"
 #endif
diff --git a/src/console.c b/src/console.c
index 6f21aeb3dd4d022c51e37c1781ec28aa029a652c..40fb43121f05adfb54f94403b36bca60caba5062 100644
--- a/src/console.c
+++ b/src/console.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/console.h b/src/console.h
index accf89d960faf6975ecd02345bba66d6ef3fd264..1cd032ac10f6cab0fcddc65c39d035450ff83525 100644
--- a/src/console.h
+++ b/src/console.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 78a3ebe6cb961cff2fb22e5e0f4520559078865a..ac8bba608288063b7b34dff533f9d2ef0e9c84e2 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -2077,6 +2077,7 @@ static boolean CL_FinishedFileList(void)
 	return true;
 }
 
+#ifndef NONET
 static const char * InvalidServerReason (serverinfo_pak *info)
 {
 #define EOT "\nPress ESC\n"
@@ -2140,6 +2141,7 @@ static const char * InvalidServerReason (serverinfo_pak *info)
 
 #undef EOT
 }
+#endif // ifndef NONET
 
 /** Called by CL_ServerConnectionTicker
   *
diff --git a/src/d_clisrv.h b/src/d_clisrv.h
index 8e75fb963c860d64e91557bc47b17daea22af7f4..bf3f0b64f5126eca92510a07686e10afbd07cba7 100644
--- a/src/d_clisrv.h
+++ b/src/d_clisrv.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/d_event.h b/src/d_event.h
index c30a8ced2b09cd7887446211cbc2b96622c0aba6..c0b9cef773b0453b3acc67d1eeafc6b8982bc7ad 100644
--- a/src/d_event.h
+++ b/src/d_event.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/d_main.c b/src/d_main.c
index 83419d266c84703694d06644a28a953e3550f6b1..fa9e21337ced42286e27b75a7bd2987ee62538e5 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -1102,10 +1102,7 @@ static void IdentifyVersion(void)
 		}
 
 		MUSICTEST("music.dta")
-		MUSICTEST("patch_music.pk3")
-#ifdef DEVELOP // remove when music_new.dta is merged into music.dta
-		MUSICTEST("music_new.dta")
-#endif
+		//MUSICTEST("patch_music.pk3")
 	}
 #endif
 }
@@ -1141,7 +1138,7 @@ void D_SRB2Main(void)
 	// Print GPL notice for our console users (Linux)
 	CONS_Printf(
 	"\n\nSonic Robo Blast 2\n"
-	"Copyright (C) 1998-2021 by Sonic Team Junior\n\n"
+	"Copyright (C) 1998-2022 by Sonic Team Junior\n\n"
 	"This program comes with ABSOLUTELY NO WARRANTY.\n\n"
 	"This is free software, and you are welcome to redistribute it\n"
 	"and/or modify it under the terms of the GNU General Public License\n"
diff --git a/src/d_main.h b/src/d_main.h
index e282906d9577fa9f869018dbc57b5e7899634d60..8189a9f2b39f277ba6d5c854551a8e9d21bd35c1 100644
--- a/src/d_main.h
+++ b/src/d_main.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/d_net.c b/src/d_net.c
index 3a4746002eb87efe8dd57e45729cefc96943bdca..5e5c10889c2a3f2a0383f005107d0ce277e7082c 100644
--- a/src/d_net.c
+++ b/src/d_net.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -1144,8 +1144,9 @@ boolean HGetPacket(void)
 		if (netbuffer->checksum != NetbufferChecksum())
 		{
 			DEBFILE("Bad packet checksum\n");
-			//Net_CloseConnection(nodejustjoined ? (doomcom->remotenode | FORCECLOSE) : doomcom->remotenode);
-			Net_CloseConnection(doomcom->remotenode);
+			// Do not disconnect or anything, just ignore the packet.
+			// Bad checksums with UDP tend to happen very scarcely
+			// so they are not normally an issue.
 			continue;
 		}
 
diff --git a/src/d_net.h b/src/d_net.h
index dbc6d8ba5ab6288ad76e2003cf67fdd2d6aa43b1..5baa593a0573d53ebbe66aac193ce217fc5089f9 100644
--- a/src/d_net.h
+++ b/src/d_net.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index fe7e7678fe447cfc186b9eaf4134cb6eeeb6db27..d9080d342a432a87e44d7c1b4e54a981111eb4b5 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/d_netcmd.h b/src/d_netcmd.h
index 7bb7eab03a1345190c670b3d9309c159cc3d3c07..0beeae15491ba555122e0ae0b5760f346d5b8791 100644
--- a/src/d_netcmd.h
+++ b/src/d_netcmd.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/d_netfil.c b/src/d_netfil.c
index fdc0026a8bbf2a87b12fa0444fbfa2156e40c5c1..37fb7265f8abe714d90dab7c0882f55d73bc1cb0 100644
--- a/src/d_netfil.c
+++ b/src/d_netfil.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/d_netfil.h b/src/d_netfil.h
index 3d713c150fad6f520a618d8e158f96599f081323..f778a518ff2eee3076170885ac9bdded15cd9f8f 100644
--- a/src/d_netfil.h
+++ b/src/d_netfil.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/d_player.h b/src/d_player.h
index a0db1402df153beb928c54d743bc4388f3bc2122..755926480ae9c2b2b90383659ec079230e48a881 100644
--- a/src/d_player.h
+++ b/src/d_player.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/d_think.h b/src/d_think.h
index c3f91edc4392238c92499146ea6aefd869e7848b..90a58ab6875f53b28e6d5b5b0590f50c2790c9a6 100644
--- a/src/d_think.h
+++ b/src/d_think.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h
index 182b30e6aef84b9e4148157594918be325c75095..e632a74a8d33244f189d8a9668bf1166973a5fd1 100644
--- a/src/d_ticcmd.h
+++ b/src/d_ticcmd.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/deh_lua.c b/src/deh_lua.c
index a2ffca95b0622826dd69d350c60dba52e221b0f9..1f4d22dcae5a316e1504faf3d227957690fa8c0d 100644
--- a/src/deh_lua.c
+++ b/src/deh_lua.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/deh_lua.h b/src/deh_lua.h
index 9df4028bdcf9619a14fe267d202aed2343125e2f..657e66b6ecf2fbeeac0392da978334a9aaeed055 100644
--- a/src/deh_lua.h
+++ b/src/deh_lua.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/deh_soc.c b/src/deh_soc.c
index 3a611f3ba18daaacd3c9f5154505176f3a1ad4cd..9e3c2f2428ebe5c3eb13ab51401ca3c6b3a45520 100644
--- a/src/deh_soc.c
+++ b/src/deh_soc.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/deh_soc.h b/src/deh_soc.h
index 28e3c9512336b91700a648f35f0fe3e922356d69..f972ec26ecbb7732098e131d427ebd34b5f621ef 100644
--- a/src/deh_soc.h
+++ b/src/deh_soc.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/deh_tables.c b/src/deh_tables.c
index cfc98f631e9d475f403e104425eae189949157bf..4412206f8e696cf669556db9de3b7fddc7382169 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -224,6 +224,8 @@ actionpointer_t actionpointers[] =
 	{{A_SetObjectFlags2},        "A_SETOBJECTFLAGS2"},
 	{{A_RandomState},            "A_RANDOMSTATE"},
 	{{A_RandomStateRange},       "A_RANDOMSTATERANGE"},
+	{{A_StateRangeByAngle},      "A_STATERANGEBYANGLE"},
+	{{A_StateRangeByParameter},  "A_STATERANGEBYPARAMETER"},
 	{{A_DualAction},             "A_DUALACTION"},
 	{{A_RemoteAction},           "A_REMOTEACTION"},
 	{{A_ToggleFlameJet},         "A_TOGGLEFLAMEJET"},
@@ -1751,6 +1753,56 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
 	// The letter
 	"S_LETTER",
 
+	// Tutorial Scenery
+	"S_TUTORIALLEAF1",
+	"S_TUTORIALLEAF2",
+	"S_TUTORIALLEAF3",
+	"S_TUTORIALLEAF4",
+	"S_TUTORIALLEAF5",
+	"S_TUTORIALLEAF6",
+	"S_TUTORIALLEAF7",
+	"S_TUTORIALLEAF8",
+	"S_TUTORIALLEAF9",
+	"S_TUTORIALLEAF10",
+	"S_TUTORIALLEAF11",
+	"S_TUTORIALLEAF12",
+	"S_TUTORIALLEAF13",
+	"S_TUTORIALLEAF14",
+	"S_TUTORIALLEAF15",
+	"S_TUTORIALLEAF16",
+	"S_TUTORIALFLOWER1",
+	"S_TUTORIALFLOWER2",
+	"S_TUTORIALFLOWER3",
+	"S_TUTORIALFLOWER4",
+	"S_TUTORIALFLOWER5",
+	"S_TUTORIALFLOWER6",
+	"S_TUTORIALFLOWER7",
+	"S_TUTORIALFLOWER8",
+	"S_TUTORIALFLOWER9",
+	"S_TUTORIALFLOWER10",
+	"S_TUTORIALFLOWER11",
+	"S_TUTORIALFLOWER12",
+	"S_TUTORIALFLOWER13",
+	"S_TUTORIALFLOWER14",
+	"S_TUTORIALFLOWER15",
+	"S_TUTORIALFLOWER16",
+	"S_TUTORIALFLOWERF1",
+	"S_TUTORIALFLOWERF2",
+	"S_TUTORIALFLOWERF3",
+	"S_TUTORIALFLOWERF4",
+	"S_TUTORIALFLOWERF5",
+	"S_TUTORIALFLOWERF6",
+	"S_TUTORIALFLOWERF7",
+	"S_TUTORIALFLOWERF8",
+	"S_TUTORIALFLOWERF9",
+	"S_TUTORIALFLOWERF10",
+	"S_TUTORIALFLOWERF11",
+	"S_TUTORIALFLOWERF12",
+	"S_TUTORIALFLOWERF13",
+	"S_TUTORIALFLOWERF14",
+	"S_TUTORIALFLOWERF15",
+	"S_TUTORIALFLOWERF16",
+
 	// GFZ flowers
 	"S_GFZFLOWERA",
 	"S_GFZFLOWERB",
@@ -3293,14 +3345,13 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
 	"S_NIGHTOPIANHELPER9",
 
 	// Nightopian
-	"S_PIAN0",
-	"S_PIAN1",
-	"S_PIAN2",
-	"S_PIAN3",
-	"S_PIAN4",
-	"S_PIAN5",
-	"S_PIAN6",
-	"S_PIANSING",
+	"S_PIAN_LOOK1",
+	"S_PIAN_LOOK2",
+	"S_PIAN_LOOK3",
+	"S_PIAN_FLY1",
+	"S_PIAN_FLY2",
+	"S_PIAN_FLY3",
+	"S_PIAN_SING",
 
 	// Shleep
 	"S_SHLEEP1",
@@ -3763,6 +3814,12 @@ const char *const MOBJTYPE_LIST[] = {  // array length left dynamic for sanity t
 	// The letter
 	"MT_LETTER",
 
+	// Tutorial Scenery
+	"MT_TUTORIALPLANT",
+	"MT_TUTORIALLEAF",
+	"MT_TUTORIALFLOWER",
+	"MT_TUTORIALFLOWERF",
+
 	// Greenflower Scenery
 	"MT_GFZFLOWER1",
 	"MT_GFZFLOWER2",
@@ -4269,6 +4326,7 @@ const char *const MOBJTYPE_LIST[] = {  // array length left dynamic for sanity t
 	"MT_YELLOWBRICKDEBRIS",
 
 	"MT_NAMECHECK",
+	"MT_RAY",
 };
 
 const char *const MOBJFLAG_LIST[] = {
diff --git a/src/deh_tables.h b/src/deh_tables.h
index 1f265cc9992da1c3d5c6e53781f6151710bc798f..972b08838ea417b210ab7f8b14de39768e5b7cf3 100644
--- a/src/deh_tables.h
+++ b/src/deh_tables.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/dehacked.c b/src/dehacked.c
index da8c81c351f845a98b59e3cd494e8f29c2a6b7d1..3f339e4778be72a2a2554723f378d2db27fb12f9 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/dehacked.h b/src/dehacked.h
index 1b200e2466f58994bb4db317db2dd9908565d5e5..b4651c66ae11dc6d2d6ee622d529435405a71b9b 100644
--- a/src/dehacked.h
+++ b/src/dehacked.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/doomdata.h b/src/doomdata.h
index e317fec1b352e21ab571810d2911ffbf4c03f7f5..009af00fa4e200f183a059ec5bcb98a7a008c5aa 100644
--- a/src/doomdata.h
+++ b/src/doomdata.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/doomdef.h b/src/doomdef.h
index e1a7a247a88168a63a01392d9555ddb578f2e215..42ae03c3e11c2acbb2a61267c0c297241600b2ae 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -150,7 +150,7 @@ extern char logfilename[1024];
 
 // Does this version require an added patch file?
 // Comment or uncomment this as necessary.
-#define USE_PATCH_DTA
+// #define USE_PATCH_DTA
 
 // Enforce a limit of loaded WAD files.
 //#define ENFORCE_WAD_LIMIT
diff --git a/src/doomstat.h b/src/doomstat.h
index 32669b68bdecbda94eea9cebd7564907149b4d14..bce43416b840e7704a870857708ae1e019062868 100644
--- a/src/doomstat.h
+++ b/src/doomstat.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/doomtype.h b/src/doomtype.h
index 3a57d90e81f25998e80fc7181cd1aa14df50bde3..5ddd9ae44e189deca59be07b6fccf653f6489551 100644
--- a/src/doomtype.h
+++ b/src/doomtype.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/endian.h b/src/endian.h
index e78204e723a43715cd7182b6aaebf59e61ee4358..86297f0cb6b6e604edc2df2e29811d35e6077ea6 100644
--- a/src/endian.h
+++ b/src/endian.h
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2014-2021 by Sonic Team Junior.
+// Copyright (C) 2014-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/f_finale.c b/src/f_finale.c
index 8dd03d44f5d068f2b5762dbaea7e51dbd408e5b7..b5715b863f8dcde6ee8d9f5cc621ab9ea7f72ccb 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/f_finale.h b/src/f_finale.h
index 4aa2c3f05b121a17c4fcfe7c4163e4b1db274415..efdc9d4ada3f88ce4de8780c70ad41a34e0897b5 100644
--- a/src/f_finale.h
+++ b/src/f_finale.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/f_wipe.c b/src/f_wipe.c
index 7526aeca36f6bd94cd8179c79fc7be299d395e40..43b7180b754408faf39387ef259d54aae590bf9f 100644
--- a/src/f_wipe.c
+++ b/src/f_wipe.c
@@ -3,7 +3,7 @@
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
 // Copyright (C) 2013-2016 by Matthew "Kaito Sinclaire" Walsh.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/g_demo.c b/src/g_demo.c
index c97dbcf9ee0559e23e6635e4a62f2de1cb843af2..e293ad9dc5e76a1af37e3b384763383e23326c70 100644
--- a/src/g_demo.c
+++ b/src/g_demo.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -94,7 +94,7 @@ demoghost *ghosts = NULL;
 // DEMO RECORDING
 //
 
-#define DEMOVERSION 0x000e
+#define DEMOVERSION 0x000f
 #define DEMOHEADER  "\xF0" "SRB2Replay" "\x0F"
 
 #define DF_GHOST        0x01 // This demo contains ghost data too!
@@ -1029,7 +1029,11 @@ void G_ReadMetalTic(mobj_t *metal)
 	if (ziptic & GZT_ANGLE)
 		metal->angle = READUINT8(metal_p)<<24;
 	if (ziptic & GZT_FRAME)
+	{
 		oldmetal.frame = READUINT32(metal_p);
+		if (metalversion < 0x000f)
+			oldmetal.frame = G_ConvertOldFrameFlags(oldmetal.frame);
+	}
 	if (ziptic & GZT_SPR2)
 		oldmetal.sprite2 = READUINT8(metal_p);
 
@@ -1169,6 +1173,8 @@ void G_ReadMetalTic(mobj_t *metal)
 					follow->sprite2 = 0;
 				follow->sprite = READUINT16(metal_p);
 				follow->frame = READUINT32(metal_p); // NOT & FF_FRAMEMASK here, so 32 bits
+				if (metalversion < 0x000f)
+					follow->frame = G_ConvertOldFrameFlags(follow->frame);
 				follow->angle = metal->angle;
 				follow->color = (metalversion==0x000c) ? READUINT8(metal_p) : READUINT16(metal_p);
 
@@ -1680,8 +1686,9 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname)
 	switch(oldversion) // demoversion
 	{
 	case DEMOVERSION: // latest always supported
-	case 0x000d: // The previous demoversion also supported
-	case 0x000c: // all that changed between then and now was longer color name
+	case 0x000e: // The previous demoversions also supported
+	case 0x000d: // all that changed between then and now was longer color name
+	case 0x000c:
 		break;
 	// too old, cannot support.
 	default:
@@ -1824,6 +1831,7 @@ void G_DoPlayDemo(char *defdemoname)
 	switch(demoversion)
 	{
 	case 0x000d:
+	case 0x000e:
 	case DEMOVERSION: // latest always supported
 		cnamelen = MAXCOLORNAME;
 		break;
@@ -2077,6 +2085,7 @@ void G_AddGhost(char *defdemoname)
 	switch(ghostversion)
 	{
 	case 0x000d:
+	case 0x000e:
 	case DEMOVERSION: // latest always supported
 		cnamelen = MAXCOLORNAME;
 		break;
@@ -2337,8 +2346,9 @@ void G_DoPlayMetal(void)
 	switch(metalversion)
 	{
 	case DEMOVERSION: // latest always supported
-	case 0x000d: // There are checks wheter the momentum is from older demo versions or not
-	case 0x000c: // all that changed between then and now was longer color name
+	case 0x000e: // There are checks wheter the momentum is from older demo versions or not
+	case 0x000d: // all that changed between then and now was longer color name
+	case 0x000c:
 		break;
 	// too old, cannot support.
 	default:
@@ -2554,3 +2564,45 @@ boolean G_CheckDemoStatus(void)
 
 	return false;
 }
+
+// 2.2.10 shifted some frame flags around, this function converts frame flags from older versions to their 2.2.10 equivalents.
+INT32 G_ConvertOldFrameFlags(INT32 frame)
+{
+	if (frame & 0x01000000) // was FF_ANIMATE, is now FF_VERTICALFLIP
+	{
+		frame &= ~0x01000000;
+		frame |= FF_ANIMATE;
+	}
+
+	if (frame & 0x02000000) // was FF_RANDOMANIM, is now FF_HORIZONTALFLIP
+	{
+		frame &= ~0x02000000;
+		frame |= FF_RANDOMANIM;
+	}
+
+	if (frame & 0x04000000) // was FF_GLOBALANIM, is now empty
+	{
+		frame &= ~0x04000000;
+		frame |= FF_GLOBALANIM;
+	}
+
+	if (frame & 0x00200000) // was FF_VERTICALFLIP, is now FF_FULLDARK
+	{
+		frame &= ~0x00200000;
+		frame |= FF_VERTICALFLIP;
+	}
+
+	if (frame & 0x00400000) // was FF_HORIZONTALFLIP, is now FF_PAPERSPRITE
+	{
+		frame &= ~0x00400000;
+		frame |= FF_HORIZONTALFLIP;
+	}
+
+	if (frame & 0x00800000) // was FF_PAPERSPRITE, is now FF_FLOORSPRITE
+	{
+		frame &= ~0x00800000;
+		frame |= FF_PAPERSPRITE;
+	}
+
+	return frame;
+}
diff --git a/src/g_demo.h b/src/g_demo.h
index 73cf273582ff1baeffcb2638ebe81654e6b8d70d..37664dc71fb7ce74a394b54b28f4b663f3a596d0 100644
--- a/src/g_demo.h
+++ b/src/g_demo.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -82,5 +82,6 @@ void G_StopMetalDemo(void);
 ATTRNORETURN void FUNCNORETURN G_StopMetalRecording(boolean kill);
 void G_StopDemo(void);
 boolean G_CheckDemoStatus(void);
+INT32 G_ConvertOldFrameFlags(INT32 frame);
 
 #endif // __G_DEMO__
diff --git a/src/g_game.c b/src/g_game.c
index 44860bbbc5a19190e5ef705dff024d70dc6d1445..39d0030565c468823819c0a35d2653d2a773db01 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -1411,7 +1411,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
 			newtarget = P_SpawnMobj(ticcmd_ztargetfocus[forplayer]->x, ticcmd_ztargetfocus[forplayer]->y, ticcmd_ztargetfocus[forplayer]->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker
 			P_SetTarget(&newtarget->target, ticcmd_ztargetfocus[forplayer]);
 
-			if (P_AproxDistance(
+			if (player->mo && P_AproxDistance(
 				player->mo->x - ticcmd_ztargetfocus[forplayer]->x,
 				player->mo->y - ticcmd_ztargetfocus[forplayer]->y
 			) > 50*player->mo->scale)
@@ -1547,12 +1547,17 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
 	cmd->sidemove = (SINT8)(cmd->sidemove + side);
 
 	// Note: Majority of botstuffs are handled in G_Ticker now.
-	if (player->bot == BOT_2PHUMAN) //Player-controlled bot
+	if (player->bot == BOT_2PAI
+		&& !player->powers[pw_tailsfly]
+		&& (cmd->forwardmove || cmd->sidemove || cmd->buttons))
 	{
-		// Fix offset angle for P2-controlled Tailsbot when P2's controls are set to non-Strafe
-		cmd->angleturn = (INT16)((localangle - *myangle) >> 16);
+		player->bot = BOT_2PHUMAN; // A player-controlled bot. Returns to AI when it respawns.
+		CV_SetValue(&cv_analog[1], true);
 	}
 
+	if (player->bot == BOT_2PHUMAN)
+		cmd->angleturn = (INT16)((localangle - *myangle) >> 16);
+
 	*myangle += (cmd->angleturn<<16);
 
 	if (controlstyle == CS_LMAOGALOG) {
@@ -2307,65 +2312,44 @@ void G_Ticker(boolean run)
 
 	buf = gametic % BACKUPTICS;
 
+	// Generate ticcmds for bots FIRST, then copy received ticcmds for players.
+	// This emulates pre-2.2.10 behaviour where the bot referenced their leader's last copied ticcmd,
+	// which is desirable because P_PlayerThink can override inputs (e.g. while PF_STASIS is applied or in a waterslide),
+	// and the bot AI needs to respect that.
+#define ISHUMAN (players[i].bot == BOT_NONE || players[i].bot == BOT_2PHUMAN)
 	for (i = 0; i < MAXPLAYERS; i++)
 	{
-		if (playeringame[i])
+		if (playeringame[i] && !ISHUMAN) // Less work is required if we're building a bot ticcmd.
 		{
-			INT16 received;
-			// Save last frame's button readings
-			players[i].lastbuttons = players[i].cmd.buttons;
+			players[i].lastbuttons = players[i].cmd.buttons; // Save last frame's button readings
+			B_BuildTiccmd(&players[i], &players[i].cmd);
+
+			// Since bot TicCmd is pre-determined for both the client and server, the latency and packet checks are simplified.
+			players[i].cmd.latency = 0;
+			P_SetPlayerAngle(&players[i], players[i].cmd.angleturn << 16);
+		}
+	}
 
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		if (playeringame[i] && ISHUMAN)
+		{
+			players[i].lastbuttons = players[i].cmd.buttons; // Save last frame's button readings
 			G_CopyTiccmd(&players[i].cmd, &netcmds[buf][i], 1);
-			// Bot ticcmd handling
-			// Yes, ordinarily this would be handled in G_BuildTiccmd...
-			// ...however, bot players won't have a corresponding consoleplayer or splitscreen player 2 to send that information.
-			// Therefore, this has to be done after ticcmd sends are received.
-			if (players[i].bot == BOT_2PAI) { // Tailsbot for P2
-				if (!players[i].powers[pw_tailsfly] && (players[i].cmd.forwardmove || players[i].cmd.sidemove || players[i].cmd.buttons))
-				{
-					players[i].bot = BOT_2PHUMAN; // A player-controlled bot. Returns to AI when it respawns.
-					CV_SetValue(&cv_analog[1], true);
-				}
-				else
-				{
-					B_BuildTiccmd(&players[i], &players[i].cmd);
-				}
-				B_HandleFlightIndicator(&players[i]);
-			}
-			else if (players[i].bot == BOT_MPAI) {
-				B_BuildTiccmd(&players[i], &players[i].cmd);
-			}
+
+			// Use the leveltime sent in the player's ticcmd to determine control lag
+			players[i].cmd.latency = min(((leveltime & 0xFF) - players[i].cmd.latency) & 0xFF, MAXPREDICTTICS-1);
 
 			// Do angle adjustments.
-			if (players[i].bot == BOT_NONE || players[i].bot == BOT_2PHUMAN)
-			{
-				received = (players[i].cmd.angleturn & TICCMD_RECEIVED);
-				players[i].angleturn += players[i].cmd.angleturn - players[i].oldrelangleturn;
-				players[i].oldrelangleturn = players[i].cmd.angleturn;
-				if (P_ControlStyle(&players[i]) == CS_LMAOGALOG)
-					P_ForceLocalAngle(&players[i], players[i].angleturn << 16);
-				else
-					players[i].cmd.angleturn = players[i].angleturn;
-    			if (P_ControlStyle(&players[i]) == CS_LMAOGALOG)
-    				P_ForceLocalAngle(&players[i], players[i].angleturn << 16);
-    			else
-    				players[i].cmd.angleturn = players[i].angleturn;
-
-    			players[i].cmd.angleturn &= ~TICCMD_RECEIVED;
-				// Use the leveltime sent in the player's ticcmd to determine control lag
-    			players[i].cmd.latency = min(((leveltime & 0xFF) - players[i].cmd.latency) & 0xFF, MAXPREDICTTICS-1);
-			}
-			else // Less work is required if we're building a bot ticcmd.
-			{
-    			// Since bot TicCmd is pre-determined for both the client and server, the latency and packet checks are simplified.
-    			received = 1;
-    			players[i].cmd.latency = 0;
-				players[i].angleturn = players[i].cmd.angleturn;
-				players[i].oldrelangleturn = players[i].cmd.angleturn;
-			}
-			players[i].cmd.angleturn |= received;
+			players[i].angleturn += players[i].cmd.angleturn - players[i].oldrelangleturn;
+			players[i].oldrelangleturn = players[i].cmd.angleturn;
+			if (P_ControlStyle(&players[i]) == CS_LMAOGALOG)
+				P_ForceLocalAngle(&players[i], players[i].angleturn << 16);
+			else
+				players[i].cmd.angleturn = (players[i].angleturn & ~TICCMD_RECEIVED) | (players[i].cmd.angleturn & TICCMD_RECEIVED);
 		}
 	}
+#undef ISHUMAN
 
 	// do main actions
 	switch (gamestate)
diff --git a/src/g_game.h b/src/g_game.h
index f98269fcec2280ceac908c310236c88bb01ee73f..dca043f2e0fae899ec86ac55255f2613dfdde0eb 100644
--- a/src/g_game.h
+++ b/src/g_game.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/g_input.c b/src/g_input.c
index 250a2477223659fe3bc9988aa9fa4010deb14f87..7bb2e799da1379b2ea06238aa273a0d6e1937608 100644
--- a/src/g_input.c
+++ b/src/g_input.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/g_input.h b/src/g_input.h
index 2e9f53dcf4e4ff57aa9deccbffc7537ebaa6c261..bf6ad39b3d24e8c941f95330a7d0c07a6cf06d09 100644
--- a/src/g_input.h
+++ b/src/g_input.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/g_state.h b/src/g_state.h
index 589dc6361705747ad0da81fddf79cbf327849228..a6ac1970d96308ef08c70ae2a61b5e08c382726d 100644
--- a/src/g_state.h
+++ b/src/g_state.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/hardware/hw_batching.c b/src/hardware/hw_batching.c
index da0319bccfecbd70901fe05b1658a49c54c24998..f9c6542ae631b07a44356273aafafce3307fef92 100644
--- a/src/hardware/hw_batching.c
+++ b/src/hardware/hw_batching.c
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2020-2021 by Sonic Team Junior.
+// Copyright (C) 2020-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/hardware/hw_batching.h b/src/hardware/hw_batching.h
index 9ccc7de3df503a12211b41d871c2734af70b0bf6..df5c478a323397fb799ff295f98459abe8e6e0a5 100644
--- a/src/hardware/hw_batching.h
+++ b/src/hardware/hw_batching.h
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2020-2021 by Sonic Team Junior.
+// Copyright (C) 2020-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c
index 317efd320e749fb4c60e2d5ab36a044d3543a699..fe0b65c5021c0a8ef7db1ae189ad37709f76d33d 100644
--- a/src/hardware/hw_cache.c
+++ b/src/hardware/hw_cache.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/hardware/hw_data.h b/src/hardware/hw_data.h
index 5aba6a2a9b14e27d98fa7f57c6847826c8e791eb..ceefe9abdd465913078173548da2e0c0bc279731 100644
--- a/src/hardware/hw_data.h
+++ b/src/hardware/hw_data.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/hardware/hw_defs.h b/src/hardware/hw_defs.h
index 8df9b8916b2e563d7c1eebb16511b222a636a312..fca9b80a3006340bb053997f8e68a51de89bdf6d 100644
--- a/src/hardware/hw_defs.h
+++ b/src/hardware/hw_defs.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c
index e02dbea5b6fbab58944c13cba4d232f95d5271ea..691e3cd3f0c70d76b0a015f3f3ee36196361a436 100644
--- a/src/hardware/hw_draw.c
+++ b/src/hardware/hw_draw.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -347,19 +347,16 @@ void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t p
 	v[2].t = v[3].t = hwrPatch->max_t;
 
 	// clip it since it is used for bunny scroll in doom I
-	if (blendmode)
-		flags = HWR_GetBlendModeFlag(blendmode+1)|PF_NoDepthTest;
-	else
-		flags = PF_Translucent|PF_NoDepthTest;
+	flags = HWR_GetBlendModeFlag(blendmode+1)|PF_NoDepthTest;
 
 	if (alphalevel)
 	{
 		FSurfaceInfo Surf;
 		Surf.PolyColor.s.red = Surf.PolyColor.s.green = Surf.PolyColor.s.blue = 0xff;
 
-		if (alphalevel == 10) Surf.PolyColor.s.alpha = softwaretranstogl_lo[st_translucency];
-		else if (alphalevel == 11) Surf.PolyColor.s.alpha = softwaretranstogl[st_translucency];
-		else if (alphalevel == 12) Surf.PolyColor.s.alpha = softwaretranstogl_hi[st_translucency];
+		if (alphalevel == 10) Surf.PolyColor.s.alpha = softwaretranstogl_lo[st_translucency]; // V_HUDTRANSHALF
+		else if (alphalevel == 11) Surf.PolyColor.s.alpha = softwaretranstogl[st_translucency]; // V_HUDTRANS
+		else if (alphalevel == 12) Surf.PolyColor.s.alpha = softwaretranstogl_hi[st_translucency]; // V_HUDTRANSDOUBLE
 		else Surf.PolyColor.s.alpha = softwaretranstogl[10-alphalevel];
 		flags |= PF_Modulated;
 		HWD.pfnDrawPolygon(&Surf, v, 4, flags);
@@ -644,19 +641,16 @@ void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale,
 	}
 
 	// clip it since it is used for bunny scroll in doom I
-	if (blendmode)
-		flags = HWR_GetBlendModeFlag(blendmode+1)|PF_NoDepthTest;
-	else
-		flags = PF_Translucent|PF_NoDepthTest;
+	flags = HWR_GetBlendModeFlag(blendmode+1)|PF_NoDepthTest;
 
 	if (alphalevel)
 	{
 		FSurfaceInfo Surf;
 		Surf.PolyColor.s.red = Surf.PolyColor.s.green = Surf.PolyColor.s.blue = 0xff;
 
-		if (alphalevel == 10) Surf.PolyColor.s.alpha = softwaretranstogl_lo[st_translucency];
-		else if (alphalevel == 11) Surf.PolyColor.s.alpha = softwaretranstogl[st_translucency];
-		else if (alphalevel == 12) Surf.PolyColor.s.alpha = softwaretranstogl_hi[st_translucency];
+		if (alphalevel == 10) Surf.PolyColor.s.alpha = softwaretranstogl_lo[st_translucency]; // V_HUDTRANSHALF
+		else if (alphalevel == 11) Surf.PolyColor.s.alpha = softwaretranstogl[st_translucency]; // V_HUDTRANS
+		else if (alphalevel == 12) Surf.PolyColor.s.alpha = softwaretranstogl_hi[st_translucency]; // V_HUDTRANSDOUBLE
 		else Surf.PolyColor.s.alpha = softwaretranstogl[10-alphalevel];
 
 		flags |= PF_Modulated;
diff --git a/src/hardware/hw_drv.h b/src/hardware/hw_drv.h
index d4a586d418de9f3da9a16d432690608b99961c8f..718774773c1dead0dd452617fbb8a5065433fbe7 100644
--- a/src/hardware/hw_drv.h
+++ b/src/hardware/hw_drv.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/hardware/hw_glob.h b/src/hardware/hw_glob.h
index 37d77b467306b823cd36a9a1bdcb57500723cc2e..8b30a346832987304e307800eb222cf6d3ac0908 100644
--- a/src/hardware/hw_glob.h
+++ b/src/hardware/hw_glob.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/hardware/hw_light.c b/src/hardware/hw_light.c
index e83d9a6ec025143150754ab6910afed140b4fdcd..eb3c9bbbb04d8b4658063a705e65f50b3ac4242e 100644
--- a/src/hardware/hw_light.c
+++ b/src/hardware/hw_light.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/hardware/hw_light.h b/src/hardware/hw_light.h
index 244cc921f567e63422868e64259b07f1cb5c0ac4..a0a9e93ad2ca4fa6693fbc6a4b1f0b41b7a64301 100644
--- a/src/hardware/hw_light.h
+++ b/src/hardware/hw_light.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index d2982afe44eb1df1812e82a79eab25de87f0b31e..92076e64407fdfe7b938141627414274bae3d3ca 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h
index 3f90f0ae17a58070948148f168c48f248510ce61..cd822c0c153bedece9dcd9c92665944b1e5504c8 100644
--- a/src/hardware/hw_main.h
+++ b/src/hardware/hw_main.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index b66f91e1962579788ebaae5bdc5baec73cdff325..d546e2f7fc6fa810c17f54638bc562ae28157275 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/hardware/hw_md2.h b/src/hardware/hw_md2.h
index 9249c034c2b1394f289681e1d33cc8726a9f161a..966ed016b898fdd9a698c0ec7a6dffa146da6353 100644
--- a/src/hardware/hw_md2.h
+++ b/src/hardware/hw_md2.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c
index de0e8c6a65aab0c1cbdc8e6e7f77cccd9118c6fd..7ec7ee2702f4bdaadbef776a0b281b7c3506f6a9 100644
--- a/src/hardware/r_opengl/r_opengl.c
+++ b/src/hardware/r_opengl/r_opengl.c
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 1998-2021 by Sonic Team Junior.
+// Copyright (C) 1998-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/http-mserv.c b/src/http-mserv.c
index f9134ba5008b0ba5f27e0b6ae48feee7ec95feef..b0ef37fa169bf8857e70b4497fa769eafb652ddf 100644
--- a/src/http-mserv.c
+++ b/src/http-mserv.c
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2020-2021 by James R.
+// Copyright (C) 2020-2022 by James R.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index cf7118fbe0694c206a2d26497196f2cc81d5787f..5d893a5515fcf21e82f72959b9aa0cc8773ad96c 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -76,7 +76,7 @@ patch_t *nto_font[NT_FONTSIZE];
 
 static player_t *plr;
 boolean chat_on; // entering a chat message?
-static char w_chat[HU_MAXMSGLEN];
+static char w_chat[HU_MAXMSGLEN + 1];
 static size_t c_input = 0; // let's try to make the chat input less shitty.
 static boolean headsupactive = false;
 boolean hu_showscores; // draw rankings
@@ -461,7 +461,7 @@ void HU_AddChatText(const char *text, boolean playsound)
 
 static void DoSayCommand(SINT8 target, size_t usedargs, UINT8 flags)
 {
-	char buf[254];
+	char buf[2 + HU_MAXMSGLEN + 1];
 	size_t numwords, ix;
 	char *msg = &buf[2];
 	const size_t msgspace = sizeof buf - 2;
@@ -537,7 +537,7 @@ static void DoSayCommand(SINT8 target, size_t usedargs, UINT8 flags)
 		}
 		buf[0] = target;
 		newmsg = msg+5+spc;
-		strlcpy(msg, newmsg, 252);
+		strlcpy(msg, newmsg, HU_MAXMSGLEN + 1);
 	}
 
 	SendNetXCmd(XD_SAY, buf, strlen(msg) + 1 + msg-buf);
@@ -644,7 +644,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
 	target = READSINT8(*p);
 	flags = READUINT8(*p);
 	msg = (char *)*p;
-	SKIPSTRING(*p);
+	SKIPSTRINGL(*p, HU_MAXMSGLEN + 1);
 
 	if ((cv_mute.value || flags & (HU_CSAY|HU_SERVER_SAY)) && playernum != serverplayer && !(IsPlayerAdmin(playernum)))
 	{
@@ -858,72 +858,6 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
 #endif
 }
 
-// Handles key input and string input
-//
-static inline boolean HU_keyInChatString(char *s, char ch)
-{
-	size_t l;
-
-	if ((ch >= HU_FONTSTART && ch <= HU_FONTEND && hu_font[ch-HU_FONTSTART])
-	  || ch == ' ') // Allow spaces, of course
-	{
-		l = strlen(s);
-		if (l < HU_MAXMSGLEN - 1)
-		{
-			if (c_input >= strlen(s)) // don't do anything complicated
-			{
-				s[l++] = ch;
-				s[l]=0;
-			}
-			else
-			{
-
-				// move everything past c_input for new characters:
-				size_t m = HU_MAXMSGLEN-1;
-				while (m>=c_input)
-				{
-					if (s[m])
-						s[m+1] = (s[m]);
-					if (m == 0) // prevent overflow
-						break;
-					m--;
-				}
-				s[c_input] = ch; // and replace this.
-			}
-			c_input++;
-			return true;
-		}
-		return false;
-	}
-	else if (ch == KEY_BACKSPACE)
-	{
-		size_t i = c_input;
-
-		if (c_input <= 0)
-			return false;
-
-		if (!s[i-1])
-			return false;
-
-		if (i >= strlen(s)-1)
-		{
-			s[strlen(s)-1] = 0;
-			c_input--;
-			return false;
-		}
-
-		for (; (i < HU_MAXMSGLEN); i++)
-		{
-			s[i-1] = s[i];
-		}
-		c_input--;
-	}
-	else if (ch != KEY_ENTER)
-		return false; // did not eat key
-
-	return true; // ate the key
-}
-
 #endif
 
 //
@@ -945,151 +879,123 @@ void HU_Ticker(void)
 #ifndef NONET
 
 static boolean teamtalk = false;
+static boolean justscrolleddown;
+static boolean justscrolledup;
+static INT16 typelines = 1; // number of drawfill lines we need when drawing the chat. it's some weird hack and might be one frame off but I'm lazy to make another loop.
+// It's up here since it has to be reset when we open the chat.
 
-// Clear spaces so we don't end up with messages only made out of emptiness
-static boolean HU_clearChatSpaces(void)
+static boolean HU_chatboxContainsOnlySpaces(void)
 {
-	size_t i = 0; // Used to just check our message
-	char c; // current character we're iterating.
-	boolean nothingbutspaces = true;
+	size_t i;
 
-	for (; i < strlen(w_chat); i++) // iterate through message and eradicate all spaces that don't belong.
-	{
-		c = w_chat[i];
-		if (!c)
-			break; // if there's nothing, it's safe to assume our message has ended, so let's not waste any more time here.
+	for (i = 0; w_chat[i]; i++)
+		if (w_chat[i] != ' ')
+			return false;
 
-		if (c != ' ') // Isn't a space
-		{
-			nothingbutspaces = false;
-		}
-	}
-	return nothingbutspaces;
+	return true;
 }
 
-//
-//
-static void HU_queueChatChar(char c)
+static void HU_sendChatMessage(void)
 {
-	// send automaticly the message (no more chat char)
-	if (c == KEY_ENTER)
+	char buf[2 + HU_MAXMSGLEN + 1];
+	char *msg = &buf[2];
+	size_t ci;
+	INT32 target = 0;
+
+	// if our message was nothing but spaces, don't send it.
+	if (HU_chatboxContainsOnlySpaces())
+		return;
+
+	// copy printable characters and terminating '\0' only.
+	for (ci = 2; w_chat[ci-2]; ci++)
 	{
-		char buf[2+256];
-		char *msg = &buf[2];
-		size_t i = 0;
-		size_t ci = 2;
-		INT32 target = 0;
+		char c = w_chat[ci-2];
+		if (c >= ' ' && !(c & 0x80))
+			buf[ci] = c;
+	};
+	buf[ci] = '\0';
 
-		if (HU_clearChatSpaces()) // Avoids being able to send empty messages, or something.
-			return; // If this returns true, that means our message was NOTHING but spaces, so don't send it period.
+	memset(w_chat, '\0', sizeof(w_chat));
+	c_input = 0;
 
-		do {
-			c = w_chat[-2+ci++];
-			if (!c || (c >= ' ' && !(c & 0x80))) // copy printable characters and terminating '\0' only.
-				buf[ci-1]=c;
-		} while (c);
+	// last minute mute check
+	if (CHAT_MUTE)
+	{
+		HU_AddChatText(va("%s>ERROR: The chat is muted. You can't say anything.", "\x85"), false);
+		return;
+	}
 
-		for (;(i<HU_MAXMSGLEN);i++)
-			w_chat[i] = 0; // reset this.
+	if (strlen(msg) > 4 && strnicmp(msg, "/pm", 3) == 0) // used /pm
+	{
+		INT32 spc = 1; // used if playernum[1] is a space.
+		char playernum[3];
+		const char *newmsg;
 
-		c_input = 0;
+		// what we're gonna do now is check if the player exists
+		// with that logic, characters 4 and 5 are our numbers:
 
-		// last minute mute check
-		if (CHAT_MUTE)
+		// teamtalk can't send PMs, just don't send it, else everyone would be able to see it, and no one wants to see your sex RP sicko.
+		if (teamtalk)
 		{
-			HU_AddChatText(va("%s>ERROR: The chat is muted. You can't say anything.", "\x85"), false);
+			HU_AddChatText(va("%sCannot send sayto in Say-Team.", "\x85"), false);
 			return;
 		}
 
-		if (strlen(msg) > 4 && strnicmp(msg, "/pm", 3) == 0) // used /pm
+		strncpy(playernum, msg+3, 3);
+		// check for undesirable characters in our "number"
+		if (!(isdigit(playernum[0]) && isdigit(playernum[1])))
 		{
-			INT32 spc = 1; // used if playernum[1] is a space.
-			char playernum[3];
-			const char *newmsg;
-
-			// what we're gonna do now is check if the player exists
-			// with that logic, characters 4 and 5 are our numbers:
-
-			// teamtalk can't send PMs, just don't send it, else everyone would be able to see it, and no one wants to see your sex RP sicko.
-			if (teamtalk)
-			{
-				HU_AddChatText(va("%sCannot send sayto in Say-Team.", "\x85"), false);
-				return;
-			}
-
-			strncpy(playernum, msg+3, 3);
-			// check for undesirable characters in our "number"
-			if (((playernum[0] < '0') || (playernum[0] > '9')) || ((playernum[1] < '0') || (playernum[1] > '9')))
-			{
-				// check if playernum[1] is a space
-				if (playernum[1] == ' ')
-					spc = 0;
-					// let it slide
-				else
-				{
-					HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm<player num> \'.", false);
-					return;
-				}
-			}
-			// I'm very bad at C, I swear I am, additional checks eww!
-			if (spc != 0)
-			{
-				if (msg[5] != ' ')
-				{
-					HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm<player num> \'.", false);
-					return;
-				}
-			}
-
-			target = atoi(playernum); // turn that into a number
-			//CONS_Printf("%d\n", target);
-
-			// check for target player, if it doesn't exist then we can't send the message!
-			if (target < MAXPLAYERS && playeringame[target]) // player exists
-				target++; // even though playernums are from 0 to 31, target is 1 to 32, so up that by 1 to have it work!
+			// check if playernum[1] is a space
+			if (playernum[1] == ' ')
+				spc = 0;
+				// let it slide
 			else
 			{
-				HU_AddChatText(va("\x82NOTICE: \x80Player %d does not exist.", target), false); // same
+				HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm<player num> \'.", false);
 				return;
 			}
-
-			// we need to get rid of the /pm<player num>
-			newmsg = msg+5+spc;
-			strlcpy(msg, newmsg, 255);
 		}
-		if (ci > 3) // don't send target+flags+empty message.
+		// I'm very bad at C, I swear I am, additional checks eww!
+		if (spc != 0 && msg[5] != ' ')
 		{
-			if (teamtalk)
-				buf[0] = -1; // target
-			else
-				buf[0] = target;
+			HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm<player num> \'.", false);
+			return;
+		}
 
-			buf[1] = 0; // flags
-			SendNetXCmd(XD_SAY, buf, 2 + strlen(&buf[2]) + 1);
+		target = atoi(playernum); // turn that into a number
+
+		// check for target player, if it doesn't exist then we can't send the message!
+		if (target < MAXPLAYERS && playeringame[target]) // player exists
+			target++; // even though playernums are from 0 to 31, target is 1 to 32, so up that by 1 to have it work!
+		else
+		{
+			HU_AddChatText(va("\x82NOTICE: \x80Player %d does not exist.", target), false); // same
+			return;
 		}
-		return;
+
+		// we need to get rid of the /pm<player num>
+		newmsg = msg+5+spc;
+		strlcpy(msg, newmsg, HU_MAXMSGLEN + 1);
+	}
+	if (ci > 2) // don't send target+flags+empty message.
+	{
+		buf[0] = teamtalk ? -1 : target; // target
+		buf[1] = 0; // flags
+		SendNetXCmd(XD_SAY, buf, 2 + strlen(&buf[2]) + 1);
 	}
 }
+
 #endif
 
 void HU_clearChatChars(void)
 {
-	size_t i = 0;
-	for (;i<HU_MAXMSGLEN;i++)
-		w_chat[i] = 0; // reset this.
+	memset(w_chat, '\0', sizeof(w_chat));
 	chat_on = false;
 	c_input = 0;
 
 	I_UpdateMouseGrab();
 }
 
-#ifndef NONET
-static boolean justscrolleddown;
-static boolean justscrolledup;
-static INT16 typelines = 1; // number of drawfill lines we need when drawing the chat. it's some weird hack and might be one frame off but I'm lazy to make another loop.
-// It's up here since it has to be reset when we open the chat.
-#endif
-
 //
 // Returns true if key eaten
 //
@@ -1171,21 +1077,23 @@ boolean HU_Responder(event_t *ev)
 			if (shiftdown ^ capslock)
 				c = shiftxform[c];
 		}
-		else	// if we're holding shift we should still shift non letter symbols
+		else // if we're holding shift we should still shift non letter symbols
 		{
 			if (shiftdown)
 				c = shiftxform[c];
 		}
 
 		// pasting. pasting is cool. chat is a bit limited, though :(
-		if (((c == 'v' || c == 'V') && ctrldown) && !CHAT_MUTE)
+		if ((c == 'v' || c == 'V') && ctrldown)
 		{
-			const char *paste = I_ClipboardPaste();
+			const char *paste;
 			size_t chatlen;
 			size_t pastelen;
 
-			// create a dummy string real quickly
+			if (CHAT_MUTE)
+				return true;
 
+			paste = I_ClipboardPaste();
 			if (paste == NULL)
 				return true;
 
@@ -1194,40 +1102,16 @@ boolean HU_Responder(event_t *ev)
 			if (chatlen+pastelen > HU_MAXMSGLEN)
 				return true; // we can't paste this!!
 
-			if (c_input >= strlen(w_chat)) // add it at the end of the string.
-			{
-				memcpy(&w_chat[chatlen], paste, pastelen); // copy all of that.
-				c_input += pastelen;
-				/*size_t i = 0;
-				for (;i<pastelen;i++)
-				{
-					HU_queueChatChar(paste[i]); // queue it so that it's actually sent. (this chat write thing is REALLY messy.)
-				}*/
-				return true;
-			}
-			else	// otherwise, we need to shift everything and make space, etc etc
-			{
-				size_t i = HU_MAXMSGLEN-1;
-				while (i >= c_input)
-				{
-					if (w_chat[i])
-						w_chat[i+pastelen] = w_chat[i];
-					if (i == 0) // prevent overflow
-						break;
-					i--;
-				}
-				memcpy(&w_chat[c_input], paste, pastelen); // copy all of that.
-				c_input += pastelen;
-				return true;
-			}
-		}
-
-		if (!CHAT_MUTE && HU_keyInChatString(w_chat,c))
-		{
-			HU_queueChatChar(c);
+			memmove(&w_chat[c_input + pastelen], &w_chat[c_input], pastelen);
+			memcpy(&w_chat[c_input], paste, pastelen); // copy all of that.
+			c_input += pastelen;
+			return true;
 		}
-		if (c == KEY_ENTER)
+		else if (c == KEY_ENTER)
 		{
+			if (!CHAT_MUTE)
+				HU_sendChatMessage();
+
 			chat_on = false;
 			c_input = 0; // reset input cursor
 			chat_scrollmedown = true; // you hit enter, so you might wanna autoscroll to see what you just sent. :)
@@ -1268,6 +1152,32 @@ boolean HU_Responder(event_t *ev)
 			else
 				c_input++;
 		}
+		else if ((c >= HU_FONTSTART && c <= HU_FONTEND && hu_font[c-HU_FONTSTART])
+			|| c == ' ') // Allow spaces, of course
+		{
+			if (CHAT_MUTE || strlen(w_chat) >= HU_MAXMSGLEN)
+				return true;
+
+			memmove(&w_chat[c_input + 1], &w_chat[c_input], strlen(w_chat) - c_input + 1);
+			w_chat[c_input] = c;
+			c_input++;
+		}
+		else if (c == KEY_BACKSPACE)
+		{
+			if (CHAT_MUTE || c_input <= 0)
+				return true;
+
+			memmove(&w_chat[c_input - 1], &w_chat[c_input], strlen(w_chat) - c_input + 1);
+			c_input--;
+		}
+		else if (c == KEY_DEL)
+		{
+			if (CHAT_MUTE || c_input >= strlen(w_chat))
+				return true;
+
+			memmove(&w_chat[c_input], &w_chat[c_input + 1], strlen(w_chat) - c_input);
+		}
+
 		return true;
 	}
 #endif
@@ -1863,64 +1773,25 @@ static void HU_DrawChat_Old(void)
 }
 #endif
 
-// draw the Crosshair, at the exact center of the view.
-//
+// Draw crosshairs at the exact center of the view.
+// In splitscreen, crosshairs are stretched vertically to compensate for V_PERPLAYER squishing them.
 // Crosshairs are pre-cached at HU_Init
 
-static inline void HU_DrawCrosshair(void)
+static inline void HU_DrawCrosshairs(void)
 {
-	INT32 i, y, dupz;
+	INT32 cross1 = cv_crosshair.value & 3;
+	INT32 cross2 = cv_crosshair2.value & 3;
 
-	i = cv_crosshair.value & 3;
-	if (!i)
+	if (automapactive || demoplayback)
 		return;
 
-	if ((netgame || multiplayer) && players[displayplayer].spectator)
-		return;
-
-#ifdef HWRENDER
-	if (rendermode != render_soft)
-		y = (INT32)gl_basewindowcentery;
-	else
-#endif
-		y = viewwindowy + (viewheight>>1);
+	stplyr = ((stplyr == &players[displayplayer]) ? &players[secondarydisplayplayer] : &players[displayplayer]);
+	if (!players[displayplayer].spectator && (!camera.chase || ticcmd_ztargetfocus[0]) && cross1)
+		V_DrawStretchyFixedPatch((BASEVIDWIDTH/2)<<FRACBITS, (BASEVIDHEIGHT/2)<<FRACBITS, FRACUNIT, splitscreen ? 2*FRACUNIT : FRACUNIT, V_TRANSLUCENT|V_PERPLAYER, crosshair[cross1 - 1], NULL);
 
-	dupz = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy);
-
-	V_DrawFixedPatch(vid.width<<(FRACBITS-1), y<<FRACBITS, FRACUNIT/dupz, V_TRANSLUCENT, crosshair[i - 1], NULL);
-}
-
-static inline void HU_DrawCrosshair2(void)
-{
-	INT32 i, y, dupz;
-
-	i = cv_crosshair2.value & 3;
-	if (!i)
-		return;
-
-	if ((netgame || multiplayer) && players[secondarydisplayplayer].spectator)
-		return;
-
-#ifdef HWRENDER
-	if (rendermode != render_soft)
-		y = (INT32)gl_basewindowcentery;
-	else
-#endif
-		y = viewwindowy + (viewheight>>1);
-
-	if (!splitscreen)
-		return;
-
-	#ifdef HWRENDER
-		if (rendermode != render_soft)
-			y += (INT32)gl_viewheight;
-		else
-	#endif
-			y += viewheight;
-
-	dupz = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy);
-
-	V_DrawFixedPatch(vid.width<<(FRACBITS-1), y<<FRACBITS, FRACUNIT/dupz, V_TRANSLUCENT, crosshair[i - 1], NULL);
+	stplyr = ((stplyr == &players[displayplayer]) ? &players[secondarydisplayplayer] : &players[displayplayer]);
+	if (!players[secondarydisplayplayer].spectator && (!camera2.chase || ticcmd_ztargetfocus[1]) && cross2 && splitscreen)
+		V_DrawStretchyFixedPatch((BASEVIDWIDTH/2)<<FRACBITS, (BASEVIDHEIGHT/2)<<FRACBITS, FRACUNIT, 2*FRACUNIT, V_TRANSLUCENT|V_PERPLAYER, crosshair[cross2 - 1], NULL);
 }
 
 static void HU_DrawCEcho(void)
@@ -2114,19 +1985,9 @@ void HU_Drawer(void)
 	if (gamestate != GS_LEVEL)
 		return;
 
-	// draw the crosshair, not when viewing demos nor with chasecam
+	// draw the crosshair
 	if (LUA_HudEnabled(hud_crosshair))
-	{
-		if (!automapactive && cv_crosshair.value && !demoplayback &&
-			(!camera.chase || ticcmd_ztargetfocus[0])
-		&& !players[displayplayer].spectator)
-			HU_DrawCrosshair();
-
-		if (!automapactive && cv_crosshair2.value && !demoplayback &&
-			(!camera2.chase || ticcmd_ztargetfocus[1])
-		&& !players[secondarydisplayplayer].spectator)
-			HU_DrawCrosshair2();
-	}
+		HU_DrawCrosshairs();
 
 	// draw desynch text
 	if (hu_redownloadinggamestate)
diff --git a/src/hu_stuff.h b/src/hu_stuff.h
index 9b7cee2d3053cb63138a08d32dcfb75565ee537e..11048637853e8a84cc19a9c547baa19556835418 100644
--- a/src/hu_stuff.h
+++ b/src/hu_stuff.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -62,7 +62,7 @@ typedef struct
 //------------------------------------
 //           chat stuff
 //------------------------------------
-#define HU_MAXMSGLEN 224
+#define HU_MAXMSGLEN 223
 #define CHAT_BUFSIZE 64		// that's enough messages, right? We'll delete the older ones when that gets out of hand.
 #ifdef NETSPLITSCREEN
 #define OLDCHAT (cv_consolechat.value == 1 || dedicated || vid.width < 640)
diff --git a/src/i_addrinfo.c b/src/i_addrinfo.c
index 5dcea100299805644612e5773392062ebca3c3cd..49aadf27d203e20b58a76b757c1097f69bbb9e2c 100644
--- a/src/i_addrinfo.c
+++ b/src/i_addrinfo.c
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2011-2021 by Sonic Team Junior.
+// Copyright (C) 2011-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/i_addrinfo.h b/src/i_addrinfo.h
index 397a1969d94c866a10e4030abb74df2d73e4e5b4..592e693f4c9d57b2aeba9b5a86f84f0167590a6b 100644
--- a/src/i_addrinfo.h
+++ b/src/i_addrinfo.h
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2011-2021 by Sonic Team Junior.
+// Copyright (C) 2011-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/i_joy.h b/src/i_joy.h
index 0c7c8dd3f45003909c956c536653bcdfb247f333..27584cea6ed818a8a01348e7a4d73e3510a64c91 100644
--- a/src/i_joy.h
+++ b/src/i_joy.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/i_net.h b/src/i_net.h
index dbc82db65cd94480277e03e23222741741524d48..62b7528d59f0bcf6877f55ab467687fc7a55dad5 100644
--- a/src/i_net.h
+++ b/src/i_net.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/i_sound.h b/src/i_sound.h
index e38a17626b95dac976295a27c53e5583ce4043fb..6358fbefb9edb99a956d808428486a04b08334df 100644
--- a/src/i_sound.h
+++ b/src/i_sound.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/i_system.h b/src/i_system.h
index a2dd81cca3ef815ec6121d09b64308f54c6adc3e..27fcdeb3f21d247e1001d12d7e8b1bee028e62a8 100644
--- a/src/i_system.h
+++ b/src/i_system.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/i_tcp.c b/src/i_tcp.c
index cae97a7d1039349aa570272fe5443d057e4be949..8838ba7257efb99adef6379935f36ca57cfd3876 100644
--- a/src/i_tcp.c
+++ b/src/i_tcp.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/i_tcp.h b/src/i_tcp.h
index 7857344156448712b934b0989b242e2e6fc554da..b6e5b92351d211f31c36c188598d99643443ee94 100644
--- a/src/i_tcp.h
+++ b/src/i_tcp.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/i_threads.h b/src/i_threads.h
index bc752181f521c447d736368cb0c17b09ae998572..c7b71d26cfe09c0a018713e08d3f3a4163a2405c 100644
--- a/src/i_threads.h
+++ b/src/i_threads.h
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2020-2021 by James R.
+// Copyright (C) 2020-2022 by James R.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/i_video.h b/src/i_video.h
index 2d07fcf10700973e4f3aad0b15e9566101ef9ec3..638fcb6689f026722ffbb4e777111260ec54e97c 100644
--- a/src/i_video.h
+++ b/src/i_video.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/info.c b/src/info.c
index 331e114b6381cf2161fb83d121ec2b77c9b7853b..3f0f5361f376eb8e74cbca1561f37741e7ad5d75 100644
--- a/src/info.c
+++ b/src/info.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -203,6 +203,10 @@ char sprnames[NUMSPRITES + 1][5] =
 	// The letter
 	"LETR",
 
+	// Tutorial Scenery
+	"TUPL",
+	"TUPF",
+
 	// Greenflower Scenery
 	"FWR1",
 	"FWR2", // GFZ Sunflower
@@ -2117,6 +2121,56 @@ state_t states[NUMSTATES] =
 
 	{SPR_LETR, FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_LETTER
 
+	// Tutorial scenery
+	{SPR_TUPL, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|0, 3, {NULL}, 0, 0, S_TUTORIALLEAF2}, // S_TUTORIALLEAF1
+	{SPR_TUPL, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|1, 3, {NULL}, 0, 0, S_TUTORIALLEAF3}, // S_TUTORIALLEAF2
+	{SPR_TUPL, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|2, 3, {NULL}, 0, 0, S_TUTORIALLEAF4}, // S_TUTORIALLEAF3
+	{SPR_TUPL, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|3, 3, {NULL}, 0, 0, S_TUTORIALLEAF5}, // S_TUTORIALLEAF4
+	{SPR_TUPL, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|4, 3, {NULL}, 0, 0, S_TUTORIALLEAF6}, // S_TUTORIALLEAF5
+	{SPR_TUPL, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|5, 3, {NULL}, 0, 0, S_TUTORIALLEAF7}, // S_TUTORIALLEAF6
+	{SPR_TUPL, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|6, 3, {NULL}, 0, 0, S_TUTORIALLEAF8}, // S_TUTORIALLEAF7
+	{SPR_TUPL, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|7, 3, {NULL}, 0, 0, S_TUTORIALLEAF9}, // S_TUTORIALLEAF8
+	{SPR_TUPL, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|7, 3, {NULL}, 0, 0, S_TUTORIALLEAF10}, // S_TUTORIALLEAF9
+	{SPR_TUPL, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|6, 3, {NULL}, 0, 0, S_TUTORIALLEAF11}, // S_TUTORIALLEAF10
+	{SPR_TUPL, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|5, 3, {NULL}, 0, 0, S_TUTORIALLEAF12}, // S_TUTORIALLEAF11
+	{SPR_TUPL, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|4, 3, {NULL}, 0, 0, S_TUTORIALLEAF13}, // S_TUTORIALLEAF12
+	{SPR_TUPL, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|3, 3, {NULL}, 0, 0, S_TUTORIALLEAF14}, // S_TUTORIALLEAF13
+	{SPR_TUPL, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|2, 3, {NULL}, 0, 0, S_TUTORIALLEAF15}, // S_TUTORIALLEAF14
+	{SPR_TUPL, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|1, 3, {NULL}, 0, 0, S_TUTORIALLEAF16}, // S_TUTORIALLEAF15
+	{SPR_TUPL, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|0, 3, {NULL}, 0, 0, S_TUTORIALLEAF1}, // S_TUTORIALLEAF16
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|0, 3, {NULL}, 0, 0, S_TUTORIALFLOWER2}, // S_TUTORIALFLOWER1
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|1, 3, {NULL}, 0, 0, S_TUTORIALFLOWER3}, // S_TUTORIALFLOWER2
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|2, 3, {NULL}, 0, 0, S_TUTORIALFLOWER4}, // S_TUTORIALFLOWER3
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|3, 3, {NULL}, 0, 0, S_TUTORIALFLOWER5}, // S_TUTORIALFLOWER4
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|4, 3, {NULL}, 0, 0, S_TUTORIALFLOWER6}, // S_TUTORIALFLOWER5
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|5, 3, {NULL}, 0, 0, S_TUTORIALFLOWER7}, // S_TUTORIALFLOWER6
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|6, 3, {NULL}, 0, 0, S_TUTORIALFLOWER8}, // S_TUTORIALFLOWER7
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|7, 3, {NULL}, 0, 0, S_TUTORIALFLOWER9}, // S_TUTORIALFLOWER8
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|7, 3, {NULL}, 0, 0, S_TUTORIALFLOWER10}, // S_TUTORIALFLOWER9
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|6, 3, {NULL}, 0, 0, S_TUTORIALFLOWER11}, // S_TUTORIALFLOWER10
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|5, 3, {NULL}, 0, 0, S_TUTORIALFLOWER12}, // S_TUTORIALFLOWER11
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|4, 3, {NULL}, 0, 0, S_TUTORIALFLOWER13}, // S_TUTORIALFLOWER12
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|3, 3, {NULL}, 0, 0, S_TUTORIALFLOWER14}, // S_TUTORIALFLOWER13
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|2, 3, {NULL}, 0, 0, S_TUTORIALFLOWER15}, // S_TUTORIALFLOWER14
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|1, 3, {NULL}, 0, 0, S_TUTORIALFLOWER16}, // S_TUTORIALFLOWER15
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_PAPERSPRITE|0, 3, {NULL}, 0, 0, S_TUTORIALFLOWER1}, // S_TUTORIALFLOWER16
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_FLOORSPRITE|0, 3, {NULL}, 0, 0, S_TUTORIALFLOWERF2}, // S_TUTORIALFLOWERF1
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_FLOORSPRITE|1, 3, {NULL}, 0, 0, S_TUTORIALFLOWERF3}, // S_TUTORIALFLOWERF2
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_FLOORSPRITE|2, 3, {NULL}, 0, 0, S_TUTORIALFLOWERF4}, // S_TUTORIALFLOWERF3
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_FLOORSPRITE|3, 3, {NULL}, 0, 0, S_TUTORIALFLOWERF5}, // S_TUTORIALFLOWERF4
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_FLOORSPRITE|4, 3, {NULL}, 0, 0, S_TUTORIALFLOWERF6}, // S_TUTORIALFLOWERF5
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_FLOORSPRITE|5, 3, {NULL}, 0, 0, S_TUTORIALFLOWERF7}, // S_TUTORIALFLOWERF6
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_FLOORSPRITE|6, 3, {NULL}, 0, 0, S_TUTORIALFLOWERF8}, // S_TUTORIALFLOWERF7
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_FLOORSPRITE|7, 3, {NULL}, 0, 0, S_TUTORIALFLOWERF9}, // S_TUTORIALFLOWERF8
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_FLOORSPRITE|7, 3, {NULL}, 0, 0, S_TUTORIALFLOWERF10}, // S_TUTORIALFLOWERF9
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_FLOORSPRITE|6, 3, {NULL}, 0, 0, S_TUTORIALFLOWERF11}, // S_TUTORIALFLOWERF10
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_FLOORSPRITE|5, 3, {NULL}, 0, 0, S_TUTORIALFLOWERF12}, // S_TUTORIALFLOWERF11
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_FLOORSPRITE|4, 3, {NULL}, 0, 0, S_TUTORIALFLOWERF13}, // S_TUTORIALFLOWERF12
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_FLOORSPRITE|3, 3, {NULL}, 0, 0, S_TUTORIALFLOWERF14}, // S_TUTORIALFLOWERF13
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_FLOORSPRITE|2, 3, {NULL}, 0, 0, S_TUTORIALFLOWERF15}, // S_TUTORIALFLOWERF14
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_FLOORSPRITE|1, 3, {NULL}, 0, 0, S_TUTORIALFLOWERF16}, // S_TUTORIALFLOWERF15
+	{SPR_TUPF, FF_SEMIBRIGHT|FF_ADD|FF_FLOORSPRITE|0, 3, {NULL}, 0, 0, S_TUTORIALFLOWERF1}, // S_TUTORIALFLOWERF16
+
 	// 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
@@ -2168,7 +2222,7 @@ state_t states[NUMSTATES] =
 	{SPR_GARG, 1, -1, {NULL}, 0, 0, S_NULL},  // S_BIGGARGOYLE
 
 	// DSZ Seaweed
-	{SPR_SEWE, 0, -1, {NULL}, 0, 0, S_SEAWEED2}, // S_SEAWEED1
+	{SPR_SEWE, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 26, 3, S_SEAWEED1}, // S_SEAWEED1
 	{SPR_SEWE, 1, 5, {NULL}, 0, 0, S_SEAWEED3}, // S_SEAWEED2
 	{SPR_SEWE, 2, 5, {NULL}, 0, 0, S_SEAWEED4}, // S_SEAWEED3
 	{SPR_SEWE, 3, 5, {NULL}, 0, 0, S_SEAWEED5}, // S_SEAWEED4
@@ -3736,14 +3790,13 @@ state_t states[NUMSTATES] =
 	{SPR_FL01, 3, 1, {A_OrbitNights}, ANG2*2, 180 | 0x10000, S_NIGHTOPIANHELPER1}, // S_NIGHTOPIANHELPER9
 
 	// 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
+	{SPR_NTPN, 0, 2, {A_Look}, 1, 1, S_PIAN_LOOK2}, // S_PIAN_LOOK1
+	{SPR_NTPN, 1, 2, {A_Look}, 1, 1, S_PIAN_LOOK3}, // S_PIAN_LOOK2
+	{SPR_NTPN, 2, 2, {A_Look}, 1, 1, S_PIAN_LOOK1}, // S_PIAN_LOOK3
+	{SPR_NTPN, 0, 2, {A_JetgThink}, 0, 0, S_PIAN_FLY2}, // S_PIAN_FLY1
+	{SPR_NTPN, 1, 2, {NULL}, 0, 0, S_PIAN_FLY3}, // S_PIAN_FLY2
+	{SPR_NTPN, 2, 2, {NULL}, 0, 0, S_PIAN_FLY1}, // S_PIAN_FLY3
+	{SPR_NTPN, 3|FF_ANIMATE, 24, {NULL}, 2, 2, S_PIAN_FLY1}, // S_PIAN_SING
 
 	// Shleep
 	{SPR_SHLP, 0, 15, {NULL}, 0, 0, S_SHLEEP2}, // S_SHLEEP1
@@ -5601,8 +5654,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_EGGMOBILE_FLEE1, // xdeathstate
 		sfx_s3kb4,         // deathsound
 		4,                 // speed
-		24*FRACUNIT,       // radius
-		76*FRACUNIT,       // height
+		36*FRACUNIT,       // radius
+		84*FRACUNIT,       // height
 		0,                 // display offset
 		sfx_None,          // mass
 		3,                 // damage
@@ -5736,8 +5789,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_EGGMOBILE2_FLEE1,// xdeathstate
 		sfx_s3kb4,         // deathsound
 		2*FRACUNIT,        // speed
-		24*FRACUNIT,       // radius
-		76*FRACUNIT,       // height
+		36*FRACUNIT,       // radius
+		84*FRACUNIT,       // height
 		0,                 // display offset
 		0,                 // mass
 		3,                 // damage
@@ -5844,7 +5897,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_EGGMOBILE3_FLEE1, // xdeathstate
 		sfx_s3kb4,          // deathsound
 		8*FRACUNIT,         // speed
-		32*FRACUNIT,        // radius
+		36*FRACUNIT,        // radius
 		116*FRACUNIT,       // height
 		0,                  // display offset
 		MT_FAKEMOBILE,      // mass
@@ -5871,7 +5924,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,             // xdeathstate
 		sfx_mswarp,         // deathsound
 		8*FRACUNIT,         // speed
-		32*FRACUNIT,        // radius
+		36*FRACUNIT,        // radius
 		116*FRACUNIT,       // height
 		0,                  // display offset
 		0,                  // mass
@@ -5925,8 +5978,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_EGGMOBILE4_FLEE1,// xdeathstate
 		sfx_s3kb4,         // deathsound
 		0,                 // speed
-		24*FRACUNIT,       // radius
-		76*FRACUNIT,       // height
+		36*FRACUNIT,       // radius
+		84*FRACUNIT,       // height
 		0,                 // display offset
 		0,                 // mass
 		3,                 // damage
@@ -7977,7 +8030,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		DMG_SPIKE,      // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_SOLID|MF_SCENERY,  // flags
+		MF_NOBLOCKMAP|MF_SCENERY|MF_NOCLIPHEIGHT,  // flags
 		S_NULL          // raisestate
 	},
 
@@ -8004,7 +8057,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		DMG_SPIKE,      // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_SOLID|MF_NOGRAVITY|MF_SCENERY|MF_PAPERCOLLISION,  // flags
+		MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SCENERY|MF_NOCLIPHEIGHT|MF_PAPERCOLLISION,  // flags
 		S_NULL          // raisestate
 	},
 
@@ -8031,7 +8084,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		4,              // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SCENERY|MF_NOCLIP|MF_NOCLIPTHING,  // flags
+		MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPTHING,  // flags
 		S_NULL          // raisestate
 	},
 
@@ -9776,8 +9829,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_MINE_BOOM1,   // deathstate
-		S_MINE_BOOM1,   // xdeathstate
+		S_XPLD1,        // deathstate
+		S_XPLD1,        // xdeathstate
 		sfx_cybdth,     // deathsound
 		0,              // speed
 		8*FRACUNIT,     // radius
@@ -9979,6 +10032,114 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
+	{           // MT_TUTORIALPLANT
+		799,            // doomednum
+		S_NULL,         // 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
+		32*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_TUTORIALLEAF
+		-1,            // doomednum
+		S_TUTORIALLEAF1,   // 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
+		32*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_TUTORIALFLOWER
+		-1,            // doomednum
+		S_TUTORIALFLOWER1,   // 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
+		32*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_TUTORIALFLOWERF
+		-1,            // doomednum
+		S_TUTORIALFLOWERF1,   // 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
+		32*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags
+		S_NULL          // raisestate
+	},
+
 	{           // MT_GFZFLOWER1
 		800,            // doomednum
 		S_GFZFLOWERA,   // spawnstate
@@ -20119,28 +20280,28 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_PIAN
 		1602,           // doomednum
-		S_PIAN0,        // spawnstate
+		S_PIAN_LOOK1,   // spawnstate
 		1000,           // spawnhealth
-		S_PIAN1,        // seestate
+		S_PIAN_FLY1,    // seestate
 		sfx_None,       // seesound
 		0,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
 		200,            // painchance
 		sfx_None,       // painsound
-		S_PIANSING,     // meleestate
-		S_NULL,         // missilestate
+		S_NULL,         // meleestate
+		S_PIAN_SING,    // missilestate
 		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
 		FRACUNIT,       // speed
 		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		40*FRACUNIT,    // height
 		0,              // display offset
 		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_SLIDEME|MF_ENEMY|MF_SPECIAL|MF_SHOOTABLE|MF_NOGRAVITY, // flags
+		MF_SLIDEME|MF_NOGRAVITY, // flags
 		S_NULL          // raisestate
 	},
 
@@ -21685,6 +21846,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY|MF_NOSECTOR, // flags
 		S_NULL          // raisestate
 	},
+
+	{           // MT_RAY
+		-1,             // doomednum
+		S_NULL,         // 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_NOBLOCKMAP|MF_NOSECTOR|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
+		S_NULL          // raisestate
+	},
 };
 
 skincolor_t skincolors[MAXSKINCOLORS] = {
diff --git a/src/info.h b/src/info.h
index 031a08b4316a00d135cf45a45827ff117181373e..9aa26a97a922e8673652ce0096e9f2913cd0294f 100644
--- a/src/info.h
+++ b/src/info.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -177,6 +177,8 @@ enum actionnum
 	A_SETOBJECTFLAGS2,
 	A_RANDOMSTATE,
 	A_RANDOMSTATERANGE,
+	A_STATERANGEBYANGLE,
+	A_STATERANGEBYPARAMETER,
 	A_DUALACTION,
 	A_REMOTEACTION,
 	A_TOGGLEFLAMEJET,
@@ -443,6 +445,8 @@ void A_SetObjectFlags();
 void A_SetObjectFlags2();
 void A_RandomState();
 void A_RandomStateRange();
+void A_StateRangeByAngle();
+void A_StateRangeByParameter();
 void A_DualAction();
 void A_RemoteAction();
 void A_ToggleFlameJet();
@@ -737,6 +741,10 @@ typedef enum sprite
 	// The letter
 	SPR_LETR,
 
+	// Tutorial scenery
+	SPR_TUPL,
+	SPR_TUPF,
+
 	// Greenflower Scenery
 	SPR_FWR1,
 	SPR_FWR2, // GFZ Sunflower
@@ -2551,6 +2559,56 @@ typedef enum state
 	// The letter
 	S_LETTER,
 
+	// Tutorial scenery
+	S_TUTORIALLEAF1,
+	S_TUTORIALLEAF2,
+	S_TUTORIALLEAF3,
+	S_TUTORIALLEAF4,
+	S_TUTORIALLEAF5,
+	S_TUTORIALLEAF6,
+	S_TUTORIALLEAF7,
+	S_TUTORIALLEAF8,
+	S_TUTORIALLEAF9,
+	S_TUTORIALLEAF10,
+	S_TUTORIALLEAF11,
+	S_TUTORIALLEAF12,
+	S_TUTORIALLEAF13,
+	S_TUTORIALLEAF14,
+	S_TUTORIALLEAF15,
+	S_TUTORIALLEAF16,
+	S_TUTORIALFLOWER1,
+	S_TUTORIALFLOWER2,
+	S_TUTORIALFLOWER3,
+	S_TUTORIALFLOWER4,
+	S_TUTORIALFLOWER5,
+	S_TUTORIALFLOWER6,
+	S_TUTORIALFLOWER7,
+	S_TUTORIALFLOWER8,
+	S_TUTORIALFLOWER9,
+	S_TUTORIALFLOWER10,
+	S_TUTORIALFLOWER11,
+	S_TUTORIALFLOWER12,
+	S_TUTORIALFLOWER13,
+	S_TUTORIALFLOWER14,
+	S_TUTORIALFLOWER15,
+	S_TUTORIALFLOWER16,
+	S_TUTORIALFLOWERF1,
+	S_TUTORIALFLOWERF2,
+	S_TUTORIALFLOWERF3,
+	S_TUTORIALFLOWERF4,
+	S_TUTORIALFLOWERF5,
+	S_TUTORIALFLOWERF6,
+	S_TUTORIALFLOWERF7,
+	S_TUTORIALFLOWERF8,
+	S_TUTORIALFLOWERF9,
+	S_TUTORIALFLOWERF10,
+	S_TUTORIALFLOWERF11,
+	S_TUTORIALFLOWERF12,
+	S_TUTORIALFLOWERF13,
+	S_TUTORIALFLOWERF14,
+	S_TUTORIALFLOWERF15,
+	S_TUTORIALFLOWERF16,
+
 	// GFZ flowers
 	S_GFZFLOWERA,
 	S_GFZFLOWERB,
@@ -4093,14 +4151,13 @@ typedef enum state
 	S_NIGHTOPIANHELPER9,
 
 	// Nightopian
-	S_PIAN0,
-	S_PIAN1,
-	S_PIAN2,
-	S_PIAN3,
-	S_PIAN4,
-	S_PIAN5,
-	S_PIAN6,
-	S_PIANSING,
+	S_PIAN_LOOK1,
+	S_PIAN_LOOK2,
+	S_PIAN_LOOK3,
+	S_PIAN_FLY1,
+	S_PIAN_FLY2,
+	S_PIAN_FLY3,
+	S_PIAN_SING,
 
 	// Shleep
 	S_SHLEEP1,
@@ -4583,6 +4640,12 @@ typedef enum mobj_type
 	// The letter
 	MT_LETTER,
 
+	// Tutorial Scenery
+	MT_TUTORIALPLANT,
+	MT_TUTORIALLEAF,
+	MT_TUTORIALFLOWER,
+	MT_TUTORIALFLOWERF,
+
 	// Greenflower Scenery
 	MT_GFZFLOWER1,
 	MT_GFZFLOWER2,
@@ -5089,6 +5152,7 @@ typedef enum mobj_type
 	MT_YELLOWBRICKDEBRIS, // for CEZ3
 
 	MT_NAMECHECK,
+	MT_RAY, // General purpose mobj
 
 	MT_FIRSTFREESLOT,
 	MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1,
diff --git a/src/keys.h b/src/keys.h
index b19259320e59574effaa3b48ba7a713ac7af0c86..df12c95aefa58ee65abec831e8eec1bdd5de6b85 100644
--- a/src/keys.h
+++ b/src/keys.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index 12ad4fee0549bbaad9d5ea2dd3106c9bd9b8f955..120ab671e92e1fafbfba8eaf520ee5133fdad691 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2021 by Sonic Team Junior.
+// Copyright (C) 2012-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -3455,7 +3455,7 @@ static int lib_gAddPlayer(lua_State *L)
 		lua_pushnil(L);
 		return 1;
 	}
-	
+
 
 	newplayernum = i;
 
@@ -3486,15 +3486,15 @@ static int lib_gAddPlayer(lua_State *L)
 
 	// Read the bot name, if given
 	if (!lua_isnoneornil(L, 3))
-		strcpy(player_names[newplayernum], luaL_checkstring(L, 3));
-	
+		strlcpy(player_names[newplayernum], luaL_checkstring(L, 3), sizeof(*player_names));
+
 	bot = luaL_optinteger(L, 4, 3);
 	newplayer->bot = (bot >= BOT_NONE && bot <= BOT_MPAI) ? bot : BOT_MPAI;
-	
+
 	// If our bot is a 2P type, we'll need to set its leader so it can spawn
 	if (newplayer->bot == BOT_2PAI || newplayer->bot == BOT_2PHUMAN)
 		B_UpdateBotleader(newplayer);
-	
+
 	// Set the skin (can't do this until AFTER bot type is set!)
 	SetPlayerSkinByNum(newplayernum, skinnum);
 
@@ -3507,7 +3507,7 @@ static int lib_gAddPlayer(lua_State *L)
 		strcpy(joinmsg, va(joinmsg, player_names[newplayernum], newplayernum));
 		HU_AddChatText(joinmsg, false);
 	}
-	
+
 	LUA_PushUserdata(L, newplayer, META_PLAYER);
 	return 1;
 }
diff --git a/src/lua_blockmaplib.c b/src/lua_blockmaplib.c
index 9089d19b6a02c5126aeefde5e74aa1ec3ff8511e..8c63a9d6d2adaf3a15f0fda6cff79913aad0dc17 100644
--- a/src/lua_blockmaplib.c
+++ b/src/lua_blockmaplib.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2016-2021 by Iestyn "Monster Iestyn" Jealous.
-// Copyright (C) 2016-2021 by Sonic Team Junior.
+// Copyright (C) 2016-2022 by Iestyn "Monster Iestyn" Jealous.
+// Copyright (C) 2016-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/lua_consolelib.c b/src/lua_consolelib.c
index 2b8cad69b8b5fb920294bdc4e1c0475fa6a59da0..c8e914e6de3e9299725ad26a26064c2c7d7fc04b 100644
--- a/src/lua_consolelib.c
+++ b/src/lua_consolelib.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2021 by Sonic Team Junior.
+// Copyright (C) 2012-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/lua_hook.h b/src/lua_hook.h
index 531d16288ab507e19dc6f47e92e0157772306aa8..fc6a5f4ee4e4c4aa92683126c8b939c5ee36d2c3 100644
--- a/src/lua_hook.h
+++ b/src/lua_hook.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2021 by Sonic Team Junior.
+// Copyright (C) 2012-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c
index 287a185bc36b06b5d04cc18d5667b9e869047452..81f863e0343ff6ef70594a0c98a7947e948db194 100644
--- a/src/lua_hooklib.c
+++ b/src/lua_hooklib.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2021 by Sonic Team Junior.
+// Copyright (C) 2012-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -347,6 +347,10 @@ static boolean prepare_mobj_hook
 		int          hook_type,
 		mobjtype_t   mobj_type
 ){
+#ifdef PARANOIA
+	if (mobj_type == MT_NULL)
+		I_Error("MT_NULL has been passed to a mobj hook\n");
+#endif
 	return init_hook_type(hook, default_status,
 			hook_type, mobj_type, NULL,
 			mobj_hook_available(hook_type, mobj_type));
diff --git a/src/lua_hud.h b/src/lua_hud.h
index c1d2d164b97d6952612e088d80f0bd256a117d4b..ad2b51d3ef7d58b4df71ace0c18a9560962e947e 100644
--- a/src/lua_hud.h
+++ b/src/lua_hud.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2014-2016 by John "JTE" Muniz.
-// Copyright (C) 2014-2021 by Sonic Team Junior.
+// Copyright (C) 2014-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c
index 0dd951efd964e28a04074d311215d059564d88a7..c7f2bbc28ec31d6266aa9eb435248663a4418232 100644
--- a/src/lua_hudlib.c
+++ b/src/lua_hudlib.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2014-2016 by John "JTE" Muniz.
-// Copyright (C) 2014-2021 by Sonic Team Junior.
+// Copyright (C) 2014-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/lua_infolib.c b/src/lua_infolib.c
index af2d99a0c015cf05001ccaa31b3c54fffa09ea04..ac41de419ee31d37a3a655249694925778602afc 100644
--- a/src/lua_infolib.c
+++ b/src/lua_infolib.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2021 by Sonic Team Junior.
+// Copyright (C) 2012-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/lua_inputlib.c b/src/lua_inputlib.c
index 661d9364166f4e2eec28d281dd0711dfbd45480a..1710b035522755526a1450cf53e00649fc2622ef 100644
--- a/src/lua_inputlib.c
+++ b/src/lua_inputlib.c
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2021 by Sonic Team Junior.
+// Copyright (C) 2021-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/lua_libs.h b/src/lua_libs.h
index 8903834e861c32c72dc9eaf2f22c864cefc218a7..b4a891edb83d68f77075e8671cc13c76b1df0de3 100644
--- a/src/lua_libs.h
+++ b/src/lua_libs.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2021 by Sonic Team Junior.
+// Copyright (C) 2012-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index 04e6fde8db603d0cad6b19fe406c5c82ca399b83..23a293d6b456b162e982a86c6976f3483b0c8d48 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2021 by Sonic Team Junior.
+// Copyright (C) 2012-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/lua_mathlib.c b/src/lua_mathlib.c
index bd9218a3d54bc076093c8596ae502c0075464602..c7501da6047fbf0828392cc6c92663d86dc58d38 100644
--- a/src/lua_mathlib.c
+++ b/src/lua_mathlib.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2021 by Sonic Team Junior.
+// Copyright (C) 2012-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c
index cf8ccab2cec113df3e7038c5657a0be395b1f7ea..953b390006c3419be9a63c2a925d112716568eb4 100644
--- a/src/lua_mobjlib.c
+++ b/src/lua_mobjlib.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2021 by Sonic Team Junior.
+// Copyright (C) 2012-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c
index 1c634da45e233f5b55afdfe3320aedf508fc5cee..58cfab76cc8958674bddbbf5caebb8a1f6ca1f1d 100644
--- a/src/lua_playerlib.c
+++ b/src/lua_playerlib.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2021 by Sonic Team Junior.
+// Copyright (C) 2012-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/lua_polyobjlib.c b/src/lua_polyobjlib.c
index 5d76a912de0c06948dee8ecbb8039bfb356033ec..e254b0e4ab698b5f3d85f6db66648b91eac870b5 100644
--- a/src/lua_polyobjlib.c
+++ b/src/lua_polyobjlib.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2020-2021 by Iestyn "Monster Iestyn" Jealous.
-// Copyright (C) 2020-2021 by Sonic Team Junior.
+// Copyright (C) 2020-2022 by Iestyn "Monster Iestyn" Jealous.
+// Copyright (C) 2020-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/lua_script.c b/src/lua_script.c
index a1376ca2e37fbdad25f9c2db6fb34907e87c8cdd..a36e5bf985e477a4ebc188c622ab02ac7b260d4f 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2021 by Sonic Team Junior.
+// Copyright (C) 2012-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/lua_script.h b/src/lua_script.h
index e882569414452951429a99986c313137cc9613e9..e586b04a886d292ec65669d8f831123d0d6b57d1 100644
--- a/src/lua_script.h
+++ b/src/lua_script.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2021 by Sonic Team Junior.
+// Copyright (C) 2012-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/lua_skinlib.c b/src/lua_skinlib.c
index e66a379e9d13549610607b2aad173b29c184494c..9c7c4ad03e8ac8ee7e62e4b65828f0b493138f7f 100644
--- a/src/lua_skinlib.c
+++ b/src/lua_skinlib.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2014-2016 by John "JTE" Muniz.
-// Copyright (C) 2014-2021 by Sonic Team Junior.
+// Copyright (C) 2014-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/lua_taglib.c b/src/lua_taglib.c
index d0cf385a9d89217113d21161df820c5a838e2c01..b69416362e257aae6b281191c7cb373b56ab7429 100644
--- a/src/lua_taglib.c
+++ b/src/lua_taglib.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2020-2021 by James R.
-// Copyright (C) 2020-2021 by Sonic Team Junior.
+// Copyright (C) 2020-2022 by James R.
+// Copyright (C) 2020-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/lua_thinkerlib.c b/src/lua_thinkerlib.c
index 65bf8c313b14c942e77454f4890a81df62cdfcec..963fdbd5ad401053f90d0df1bee243460793b5cf 100644
--- a/src/lua_thinkerlib.c
+++ b/src/lua_thinkerlib.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2021 by Sonic Team Junior.
+// Copyright (C) 2012-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_aatree.c b/src/m_aatree.c
index b228ed63de0fd6d29d2cfe027a96f9c8eefd2e46..522e38a53ac4576233ad165f2bddd9c18b5faafc 100644
--- a/src/m_aatree.c
+++ b/src/m_aatree.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_aatree.h b/src/m_aatree.h
index 5a240394f77920845e53b023f314d223f27076b4..ed011644e0daa741eca8656d095bbbc222399ff4 100644
--- a/src/m_aatree.h
+++ b/src/m_aatree.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_anigif.c b/src/m_anigif.c
index fe04a5cb41fd42141295001d1860161a16c35f3d..b3a1d0fe22304faea9f7b9656bd9dcd4e7a872a9 100644
--- a/src/m_anigif.c
+++ b/src/m_anigif.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 2013-2016 by Matthew "Kaito Sinclaire" Walsh.
 // Copyright (C) 2013      by "Ninji".
-// Copyright (C) 2013-2021 by Sonic Team Junior.
+// Copyright (C) 2013-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_anigif.h b/src/m_anigif.h
index ca7563b1e9c6de3b537e287220cd83fc775d17ae..ad64dff7b78c5ca105ec8af0c7712947967722ee 100644
--- a/src/m_anigif.h
+++ b/src/m_anigif.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2013-2016 by Matthew "Kaito Sinclaire" Walsh.
-// Copyright (C) 2013-2021 by Sonic Team Junior.
+// Copyright (C) 2013-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_argv.c b/src/m_argv.c
index 453d6e45c1ff9e8ee53c6a44bfe2e774a0efdc6b..1444f0c38ab758c9b231ee25472196aece274276 100644
--- a/src/m_argv.c
+++ b/src/m_argv.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_argv.h b/src/m_argv.h
index f39db513ffdb0d371e06916a51bd964a2cbf821e..cdb6aa246abdf0f6069710b441dc8444b7574d48 100644
--- a/src/m_argv.h
+++ b/src/m_argv.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_bbox.c b/src/m_bbox.c
index e0505fd95a485a4f1fad9eb5e3b004fd22fa6fa2..7fde0c171d1260c7ab93f0c30778b982b889abd5 100644
--- a/src/m_bbox.c
+++ b/src/m_bbox.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_bbox.h b/src/m_bbox.h
index c56bd22c06714a7eb93c5cf533f3ea8ed7b342a4..588000faead6dd225473c8836635559a5b9d1205 100644
--- a/src/m_bbox.h
+++ b/src/m_bbox.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_cheat.c b/src/m_cheat.c
index ef896c9911975550905069813394f7c4a4e654d1..82d0b9d5a59af96319b8f004809b5ec2ec0639fc 100644
--- a/src/m_cheat.c
+++ b/src/m_cheat.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_cheat.h b/src/m_cheat.h
index ee4ba5f557eafe7c98b85605c08394f5bd28144d..c22e262fbb4898e33d4a9581956e204486ad7675 100644
--- a/src/m_cheat.h
+++ b/src/m_cheat.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_cond.c b/src/m_cond.c
index 85d732a48d3d7dea1a8c4289a8acaf24ce859e64..1406317c59b56bf5250420be2fbc8d5681fed4df 100644
--- a/src/m_cond.c
+++ b/src/m_cond.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by Matthew "Kaito Sinclaire" Walsh.
-// Copyright (C) 2012-2021 by Sonic Team Junior.
+// Copyright (C) 2012-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_cond.h b/src/m_cond.h
index b2c6d65e6046b030a44721568ae429baece6909a..f36c8000922d1d364046469cfe27a9d5ce66f1db 100644
--- a/src/m_cond.h
+++ b/src/m_cond.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by Matthew "Kaito Sinclaire" Walsh.
-// Copyright (C) 2012-2021 by Sonic Team Junior.
+// Copyright (C) 2012-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_dllist.h b/src/m_dllist.h
index 65303b4a339fb81ec9f7a2c9eb70f2b8cd55a20a..d8ca6648a8d0806e264b44c4e1bb2fce2077f380 100644
--- a/src/m_dllist.h
+++ b/src/m_dllist.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2005      by James Haley
-// Copyright (C) 2005-2021 by Sonic Team Junior.
+// Copyright (C) 2005-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_easing.c b/src/m_easing.c
index c871d3106e53c2e62216a8a731dab50cf3c6fb15..0f1cc1d026af55cfe5f28a5e3c585c84746ba838 100644
--- a/src/m_easing.c
+++ b/src/m_easing.c
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2020-2021 by Jaime "Lactozilla" Passos.
+// Copyright (C) 2020-2022 by Jaime "Lactozilla" Passos.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_easing.h b/src/m_easing.h
index 435ad35e7a0b6b2302ce5256713155bf18c4258f..229222a15778af2f8b0d6b5d4e975828dd2accff 100644
--- a/src/m_easing.h
+++ b/src/m_easing.h
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2020-2021 by Jaime "Lactozilla" Passos.
+// Copyright (C) 2020-2022 by Jaime "Lactozilla" Passos.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_fixed.c b/src/m_fixed.c
index d40ccd98e35604a706dd7fa39f16234e1eb28f5a..70b7623da8d8ca14cf046e4279061c80a7ef6b25 100644
--- a/src/m_fixed.c
+++ b/src/m_fixed.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_fixed.h b/src/m_fixed.h
index 1cf2f00d1e482d8fae5c77ea48a47b6940826297..fe5efc5512e5bc23940056dc3056df04c771d10a 100644
--- a/src/m_fixed.h
+++ b/src/m_fixed.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_menu.c b/src/m_menu.c
index 3c1d8d7caa07ba20ec416afddf848a75324017ab..5b85f65df0cc108d15d55756715c030bd5b55412 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -3,7 +3,7 @@
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
 // Copyright (C) 2011-2016 by Matthew "Kaito Sinclaire" Walsh.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_menu.h b/src/m_menu.h
index ba9c326a00eb42d0e9f9dfa5330ad69b4e886ddb..5d2ef69ec8811dc37b725963206a76c13f894979 100644
--- a/src/m_menu.h
+++ b/src/m_menu.h
@@ -3,7 +3,7 @@
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
 // Copyright (C) 2011-2016 by Matthew "Kaito Sinclaire" Walsh.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_misc.c b/src/m_misc.c
index 59783d5d30dc8d195d5b732d2de474833459f816..d7d6d6bbb6e9d4931c545b985e386d303af54941 100644
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_misc.h b/src/m_misc.h
index 82ccd58c75738ef121e0697ba0a2dc6177906e83..5b79c6c8c4d55473053563f722504257fe3ea157 100644
--- a/src/m_misc.h
+++ b/src/m_misc.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_perfstats.c b/src/m_perfstats.c
index 439a9da1cd009106bc80fa4f3c81e3337a0acbdf..9fc41000d24548deb4a58f3b5071c3f6a9fa77e1 100644
--- a/src/m_perfstats.c
+++ b/src/m_perfstats.c
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2020-2021 by Sonic Team Junior.
+// Copyright (C) 2020-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -228,10 +228,13 @@ static boolean PS_IsLevelActive(void)
 // Is the row valid in the current context?
 static boolean PS_IsRowValid(perfstatrow_t *row)
 {
-	return !((row->flags & PS_LEVEL && !PS_IsLevelActive()) ||
-		(row->flags & PS_SW && rendermode != render_soft) ||
-		(row->flags & PS_HW && rendermode != render_opengl) ||
-		(row->flags & PS_BATCHING && !cv_glbatching.value));
+	return !((row->flags & PS_LEVEL && !PS_IsLevelActive())
+		|| (row->flags & PS_SW && rendermode != render_soft)
+		|| (row->flags & PS_HW && rendermode != render_opengl)
+#ifdef HWRENDER
+		|| (row->flags & PS_BATCHING && !cv_glbatching.value)
+#endif
+		);
 }
 
 // Should the row be visible on the screen?
diff --git a/src/m_perfstats.h b/src/m_perfstats.h
index 3ff0e6c6b014b4c24099be602e507d3c07cfcc43..f6a7c1f74032196229c73890da44861a0d7adac9 100644
--- a/src/m_perfstats.h
+++ b/src/m_perfstats.h
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2020-2021 by Sonic Team Junior.
+// Copyright (C) 2020-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_queue.c b/src/m_queue.c
index a337ca4ce9b283afad602a6d06376136bb010aad..2cc3f7cb819fb5e62ed336bf8e7fcaf7881b5f15 100644
--- a/src/m_queue.c
+++ b/src/m_queue.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2003      by James Haley
-// Copyright (C) 2003-2021 by Sonic Team Junior.
+// Copyright (C) 2003-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_queue.h b/src/m_queue.h
index cc64b8dd7917acdd136ddc7f832ef2737904e66f..071f9d8fa1b5db4a816eac16603d0535c318ca85 100644
--- a/src/m_queue.h
+++ b/src/m_queue.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2003      by James Haley
-// Copyright (C) 2003-2021 by Sonic Team Junior.
+// Copyright (C) 2003-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_random.c b/src/m_random.c
index 2e6213e1277ce027fc04cd6e7435dbaa8e49963d..1127955006ba9aa8ba6c7e4646bd772e4864a50b 100644
--- a/src/m_random.c
+++ b/src/m_random.c
@@ -3,7 +3,7 @@
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
 // Copyright (C) 2012-2016 by Matthew "Kaito Sinclaire" Walsh.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_random.h b/src/m_random.h
index df10b4bb371ed14394b897bc07bb9abe78c0f096..aa5ffb0bbbb82e9b793188af4442b8020a362396 100644
--- a/src/m_random.h
+++ b/src/m_random.h
@@ -3,7 +3,7 @@
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
 // Copyright (C) 2012-2016 by Matthew "Kaito Sinclaire" Walsh.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_swap.h b/src/m_swap.h
index 6aa347d97d146bc9c33d1d69cde0ff296b04a8a5..df5f3e907145cd5c19227c341d188dfa6d104a29 100644
--- a/src/m_swap.h
+++ b/src/m_swap.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/mserv.c b/src/mserv.c
index f64c7bea91b6487efca07f01ba7bb41ad786358c..bff562c95fb02cdfe7dab811972aad42e83f0065 100644
--- a/src/mserv.c
+++ b/src/mserv.c
@@ -1,8 +1,8 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
-// Copyright (C) 2020-2021 by James R.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 2020-2022 by James R.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -98,6 +98,7 @@ void AddMServCommands(void)
 	CV_RegisterVar(&cv_servername);
 #ifdef MASTERSERVER
 	COM_AddCommand("listserv", Command_Listserv_f);
+	COM_AddCommand("masterserver_update", Update_parameters); // allows people to updates manually in case you were delisted by accident
 #endif
 #endif
 }
diff --git a/src/mserv.h b/src/mserv.h
index 7a3b3d8ec8461d9b4734353a23e37e587369be86..23b26fbc54670fff4d5e2673dcd742c8bf0e950c 100644
--- a/src/mserv.h
+++ b/src/mserv.h
@@ -1,8 +1,8 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
-// Copyright (C) 2020-2021 by James R.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 2020-2022 by James R.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_ceilng.c b/src/p_ceilng.c
index e28f9b5b10ce215422e3d11ec90847c1833c801a..50344ee0ccf9df0daf12f45321521cf6af0ed6e3 100644
--- a/src/p_ceilng.c
+++ b/src/p_ceilng.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_enemy.c b/src/p_enemy.c
index 9d51aced50899c8574997fa1d18b187db9e20371..26682ee326207d8c47c2c356592e612250174bea 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -200,6 +200,8 @@ 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_StateRangeByAngle(mobj_t *actor);
+void A_StateRangeByParameter(mobj_t *actor);
 void A_DualAction(mobj_t *actor);
 void A_RemoteAction(mobj_t *actor);
 void A_ToggleFlameJet(mobj_t *actor);
@@ -2655,7 +2657,7 @@ void A_LobShot(mobj_t *actor)
 {
 	INT32 locvar1 = var1;
 	INT32 locvar2 = var2 >> 16;
-	mobj_t *shot, *hitspot;
+	mobj_t *shot;
 	angle_t an;
 	fixed_t z;
 	fixed_t dist;
@@ -2694,11 +2696,6 @@ void A_LobShot(mobj_t *actor)
 		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;
@@ -3334,20 +3331,18 @@ void A_SkullAttack(mobj_t *actor)
 		actor->angle += (P_RandomChance(FRACUNIT/2)) ? ANGLE_90 : -ANGLE_90;
 	else if (locvar1 == 3)
 	{
-		statenum_t oldspawnstate = mobjinfo[MT_NULL].spawnstate;
-		UINT32 oldflags = mobjinfo[MT_NULL].flags;
-		fixed_t oldradius = mobjinfo[MT_NULL].radius;
-		fixed_t oldheight = mobjinfo[MT_NULL].height;
-		mobj_t *check;
+		statenum_t oldspawnstate = mobjinfo[MT_RAY].spawnstate;
+		UINT32 oldflags = mobjinfo[MT_RAY].flags;
+		fixed_t oldradius = mobjinfo[MT_RAY].radius;
+		fixed_t oldheight = mobjinfo[MT_RAY].height;
 		INT32 i, j;
 		static INT32 k;/* static for (at least) GCC 9.1 weirdness */
-		boolean allow;
 		angle_t testang = 0;
 
-		mobjinfo[MT_NULL].spawnstate = S_INVISIBLE;
-		mobjinfo[MT_NULL].flags = MF_NOGRAVITY|MF_NOTHINK|MF_NOCLIPTHING|MF_NOBLOCKMAP;
-		mobjinfo[MT_NULL].radius = mobjinfo[actor->type].radius;
-		mobjinfo[MT_NULL].height = mobjinfo[actor->type].height;
+		mobjinfo[MT_RAY].spawnstate = S_INVISIBLE;
+		mobjinfo[MT_RAY].flags = MF_NOGRAVITY|MF_NOTHINK|MF_NOCLIPTHING|MF_NOBLOCKMAP;
+		mobjinfo[MT_RAY].radius = mobjinfo[actor->type].radius;
+		mobjinfo[MT_RAY].height = mobjinfo[actor->type].height;
 
 		if (P_RandomChance(FRACUNIT/2)) // port priority 1?
 		{
@@ -3360,15 +3355,12 @@ void A_SkullAttack(mobj_t *actor)
 			j = 9;
 		}
 
-#define dostuff(q) check = P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_NULL);\
+#define dostuff(q) \
 			testang = actor->angle + ((i+(q))*ANG10);\
-			allow = (P_TryMove(check,\
-				P_ReturnThrustX(check, testang, dist + 2*actor->radius),\
-				P_ReturnThrustY(check, testang, dist + 2*actor->radius),\
-				true));\
-			P_RemoveMobj(check);\
-			if (allow)\
-				break;
+			if (P_CheckMove(actor,\
+				P_ReturnThrustX(actor, testang, dist + 2*actor->radius),\
+				P_ReturnThrustY(actor, testang, dist + 2*actor->radius),\
+				true)) break;
 
 		if (P_RandomChance(FRACUNIT/2)) // port priority 2?
 		{
@@ -3394,10 +3386,10 @@ void A_SkullAttack(mobj_t *actor)
 
 #undef dostuff
 
-		mobjinfo[MT_NULL].spawnstate = oldspawnstate;
-		mobjinfo[MT_NULL].flags = oldflags;
-		mobjinfo[MT_NULL].radius = oldradius;
-		mobjinfo[MT_NULL].height = oldheight;
+		mobjinfo[MT_RAY].spawnstate = oldspawnstate;
+		mobjinfo[MT_RAY].flags = oldflags;
+		mobjinfo[MT_RAY].radius = oldradius;
+		mobjinfo[MT_RAY].height = oldheight;
 	}
 
 	an = actor->angle >> ANGLETOFINESHIFT;
@@ -4189,7 +4181,6 @@ void A_CustomPower(mobj_t *actor)
 	player_t *player;
 	INT32 locvar1 = var1;
 	INT32 locvar2 = var2;
-	boolean spawnshield = false;
 
 	if (LUA_CallAction(A_CUSTOMPOWER, actor))
 		return;
@@ -4208,15 +4199,10 @@ void A_CustomPower(mobj_t *actor)
 
 	player = actor->target->player;
 
-	if (locvar1 == pw_shield && player->powers[pw_shield] != locvar2)
-		spawnshield = true;
+	P_SetPower(player, locvar1, locvar2);
 
-	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
@@ -6542,7 +6528,7 @@ void A_OldRingExplode(mobj_t *actor) {
 	{
 		const angle_t fa = (i*FINEANGLES/16) & FINEMASK;
 
-		mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1);
+		mo = P_SpawnMobjFromMobj(actor, 0, 0, 0, locvar1);
 		P_SetTarget(&mo->target, actor->target); // Transfer target so player gets the points
 
 		mo->momx = FixedMul(FINECOSINE(fa),ns);
@@ -6568,7 +6554,7 @@ void A_OldRingExplode(mobj_t *actor) {
 		}
 	}
 
-	mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1);
+	mo = P_SpawnMobjFromMobj(actor, 0, 0, 0, locvar1);
 
 	P_SetTarget(&mo->target, actor->target);
 	mo->momz = ns;
@@ -6583,7 +6569,7 @@ void A_OldRingExplode(mobj_t *actor) {
 			mo->color = skincolor_bluering;
 	}
 
-	mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1);
+	mo = P_SpawnMobjFromMobj(actor, 0, 0, 0, locvar1);
 
 	P_SetTarget(&mo->target, actor->target);
 	mo->momz = -ns;
@@ -8292,7 +8278,7 @@ void A_Boss3ShockThink(mobj_t *actor)
 			snew->angle = (actor->angle + snext->angle) >> 1;
 			P_SetTarget(&snew->target, actor->target);
 			snew->fuse = actor->fuse;
-			
+
 			P_SetScale(snew, actor->scale);
 			snew->destscale = actor->destscale;
 			snew->scalespeed = actor->scalespeed;
@@ -9290,6 +9276,49 @@ void A_RandomStateRange(mobj_t *actor)
 	P_SetMobjState(actor, P_RandomRange(locvar1, locvar2));
 }
 
+// Function: A_StateRangeByAngle
+//
+// Description: Chooses a state within the range supplied, depending on the actor's angle.
+//
+// var1 = Minimum state number to use.
+// var2 = Maximum state number to use. The difference will act as a modulo operator.
+//
+void A_StateRangeByAngle(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+
+	if (LUA_CallAction(A_STATERANGEBYANGLE, actor))
+		return;
+
+	if (locvar2 - locvar1 < 0)
+		return; // invalid range
+
+	P_SetMobjState(actor, locvar1 + (AngleFixed(actor->angle)>>FRACBITS % (1 + locvar2 - locvar1)));
+}
+
+// Function: A_StateRangeByParameter
+//
+// Description: Chooses a state within the range supplied, depending on the actor's parameter/extrainfo value.
+//
+// var1 = Minimum state number to use.
+// var2 = Maximum state number to use. The difference will act as a modulo operator.
+//
+void A_StateRangeByParameter(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	UINT8 parameter = (actor->spawnpoint ? actor->spawnpoint->extrainfo : 0);
+
+	if (LUA_CallAction(A_STATERANGEBYPARAMETER, actor))
+		return;
+
+	if (locvar2 - locvar1 < 0)
+		return; // invalid range
+
+	P_SetMobjState(actor, locvar1 + (parameter % (1 + locvar2 - locvar1)));
+}
+
 // 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.
diff --git a/src/p_floor.c b/src/p_floor.c
index dd9331e73ffdc671252d10d4fbf0b196dc38aa37..5536ee913acf2c958694e3c1d6bb451a50bcbfb8 100644
--- a/src/p_floor.c
+++ b/src/p_floor.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_inter.c b/src/p_inter.c
index b37689fd8f080bb85e790da77c105c5e550dcaf4..582cdb0d091df4fc6b0ee9d5698babcff6acd9fb 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -470,14 +470,14 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 				if (!(player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2))
 				{
 					fixed_t setmomz = -toucher->momz; // Store this, momz get changed by P_DoJump within P_DoBubbleBounce
-					
+
 					if (elementalpierce == 2) // Reset bubblewrap, part 1
 						P_DoBubbleBounce(player);
 					toucher->momz = setmomz;
 					if (elementalpierce == 2) // Reset bubblewrap, part 2
 					{
 						boolean underwater = toucher->eflags & MFE_UNDERWATER;
-							
+
 						if (underwater)
 							toucher->momz /= 2;
 						toucher->momz -= (toucher->momz/(underwater ? 8 : 4)); // Cap the height!
@@ -738,12 +738,11 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 		// Secret emblem thingy
 		case MT_EMBLEM:
 			{
-				if (demoplayback || (player->bot && player->bot != BOT_MPAI))
+				if (demoplayback || (player->bot && player->bot != BOT_MPAI) || special->health <= 0 || special->health > MAXEMBLEMS)
 					return;
 				emblemlocations[special->health-1].collected = true;
 
 				M_UpdateUnlockablesAndExtraEmblems();
-
 				G_SaveGameData();
 				break;
 			}
@@ -1387,7 +1386,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 
 				if (player->bot && player->bot != BOT_MPAI)
 					return;
-					
+
 				// Initialize my junk
 				junk.tags.tags = NULL;
 				junk.tags.count = 0;
@@ -1617,7 +1616,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 
 				if (special->tracer && !(special->tracer->flags2 & MF2_STRONGBOX))
 					macespin = true;
-				
+
 				if (macespin ? (player->powers[pw_ignorelatch] & (1<<15)) : (player->powers[pw_ignorelatch]))
 					return;
 
diff --git a/src/p_lights.c b/src/p_lights.c
index 1e41146da682a2ed5d93166974f1b04843bd40ed..d7bbea90cea852f48820663e3e8da9b3935509d5 100644
--- a/src/p_lights.c
+++ b/src/p_lights.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_local.h b/src/p_local.h
index 28a77afe5c9a5022a5e5f92641df34806f32b91d..ba8cbe166aa1df73a96536c9d813565255a1841b 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -155,6 +155,7 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff);
 
 void P_SetObjectMomZ(mobj_t *mo, fixed_t value, boolean relative);
 void P_RestoreMusic(player_t *player);
+void P_SetPower(player_t *player, powertype_t power, UINT16 value);
 void P_SpawnShieldOrb(player_t *player);
 void P_SwitchShield(player_t *player, UINT16 shieldtype);
 mobj_t *P_SpawnGhostMobj(mobj_t *mobj);
@@ -410,6 +411,7 @@ void P_SetUnderlayPosition(mobj_t *thing);
 
 boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y);
 boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam);
+boolean P_CheckMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff);
 boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff);
 boolean P_Move(mobj_t *actor, fixed_t speed);
 boolean P_TeleportMove(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z);
diff --git a/src/p_map.c b/src/p_map.c
index 329224d0bd353c8a345f5483ffd5142695b9f0e3..8cd0223d0957147e6edb08083eb0f326a035cab2 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -1465,6 +1465,86 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		return true;
 	}
 
+	// Sprite Spikes!
+	// Do not return because solidity code comes below.
+	if (tmthing->type == MT_SPIKE && tmthing->flags & MF_SOLID && thing->player) // moving spike rams into player?!
+	{
+		if (tmthing->eflags & MFE_VERTICALFLIP)
+		{
+			if (thing->z + thing->height <= tmthing->z + FixedMul(FRACUNIT, tmthing->scale)
+			&& thing->z + thing->height + thing->momz  >= tmthing->z + FixedMul(FRACUNIT, tmthing->scale) + tmthing->momz
+			&& !(thing->player->charability == CA_BOUNCE && thing->player->panim == PA_ABILITY && thing->eflags & MFE_VERTICALFLIP))
+				P_DamageMobj(thing, tmthing, tmthing, 1, DMG_SPIKE);
+		}
+		else if (thing->z >= tmthing->z + tmthing->height - FixedMul(FRACUNIT, tmthing->scale)
+		&& thing->z + thing->momz <= tmthing->z + tmthing->height - FixedMul(FRACUNIT, tmthing->scale) + tmthing->momz
+		&& !(thing->player->charability == CA_BOUNCE && thing->player->panim == PA_ABILITY && !(thing->eflags & MFE_VERTICALFLIP)))
+			P_DamageMobj(thing, tmthing, tmthing, 1, DMG_SPIKE);
+	}
+	else if (thing->type == MT_SPIKE && thing->flags & MF_SOLID && tmthing->player) // unfortunate player falls into spike?!
+	{
+		if (thing->eflags & MFE_VERTICALFLIP)
+		{
+			if (tmthing->z + tmthing->height <= thing->z - FixedMul(FRACUNIT, thing->scale)
+			&& tmthing->z + tmthing->height + tmthing->momz >= thing->z - FixedMul(FRACUNIT, thing->scale)
+			&& !(tmthing->player->charability == CA_BOUNCE && tmthing->player->panim == PA_ABILITY && tmthing->eflags & MFE_VERTICALFLIP))
+				P_DamageMobj(tmthing, thing, thing, 1, DMG_SPIKE);
+		}
+		else if (tmthing->z >= thing->z + thing->height + FixedMul(FRACUNIT, thing->scale)
+		&& tmthing->z + tmthing->momz <= thing->z + thing->height + FixedMul(FRACUNIT, thing->scale)
+		&& !(tmthing->player->charability == CA_BOUNCE && tmthing->player->panim == PA_ABILITY && !(tmthing->eflags & MFE_VERTICALFLIP)))
+			P_DamageMobj(tmthing, thing, thing, 1, DMG_SPIKE);
+	}
+
+	if (tmthing->type == MT_WALLSPIKE && tmthing->flags & MF_SOLID && thing->player) // wall spike impales player
+	{
+		fixed_t bottomz, topz;
+		bottomz = tmthing->z;
+		topz = tmthing->z + tmthing->height;
+		if (tmthing->eflags & MFE_VERTICALFLIP)
+			bottomz -= FixedMul(FRACUNIT, tmthing->scale);
+		else
+			topz += FixedMul(FRACUNIT, tmthing->scale);
+
+		if (thing->z + thing->height > bottomz // above bottom
+		&&  thing->z < topz) // below top
+		// don't check angle, the player was clearly in the way in this case
+			P_DamageMobj(thing, tmthing, tmthing, 1, DMG_SPIKE);
+	}
+	else if (thing->type == MT_WALLSPIKE && thing->flags & MF_SOLID && tmthing->player)
+	{
+		fixed_t bottomz, topz;
+		angle_t touchangle = R_PointToAngle2(thing->tracer->x, thing->tracer->y, tmthing->x, tmthing->y);
+
+		if (P_PlayerInPain(tmthing->player) && (tmthing->momx || tmthing->momy))
+		{
+			angle_t playerangle = R_PointToAngle2(0, 0, tmthing->momx, tmthing->momy) - touchangle;
+			if (playerangle > ANGLE_180)
+				playerangle = InvAngle(playerangle);
+			if (playerangle < ANGLE_90)
+				return true; // Yes, this is intentionally outside the z-height check. No standing on spikes whilst moving away from them.
+		}
+
+		bottomz = thing->z;
+		topz = thing->z + thing->height;
+
+		if (thing->eflags & MFE_VERTICALFLIP)
+			bottomz -= FixedMul(FRACUNIT, thing->scale);
+		else
+			topz += FixedMul(FRACUNIT, thing->scale);
+
+		if (tmthing->z + tmthing->height > bottomz // above bottom
+		&&  tmthing->z < topz // below top
+		&& !P_MobjWasRemoved(thing->tracer)) // this probably wouldn't work if we didn't have a tracer
+		{ // use base as a reference point to determine what angle you touched the spike at
+			touchangle = thing->angle - touchangle;
+			if (touchangle > ANGLE_180)
+				touchangle = InvAngle(touchangle);
+			if (touchangle <= ANGLE_22h) // if you touched it at this close an angle, you get poked!
+				P_DamageMobj(tmthing, thing, thing, 1, DMG_SPIKE);
+		}
+	}
+
 	if (thing->flags & MF_PUSHABLE)
 	{
 		if (tmthing->type == MT_FAN || tmthing->type == MT_STEAM)
@@ -1543,22 +1623,6 @@ static boolean PIT_CheckThing(mobj_t *thing)
 
 	if (thing->player)
 	{
-		if (tmthing->type == MT_WALLSPIKE && (tmthing->flags & MF_SOLID)) // wall spike impales player
-		{
-			fixed_t bottomz, topz;
-			bottomz = tmthing->z;
-			topz = tmthing->z + tmthing->height;
-			if (tmthing->eflags & MFE_VERTICALFLIP)
-				bottomz -= FixedMul(FRACUNIT, tmthing->scale);
-			else
-				topz += FixedMul(FRACUNIT, tmthing->scale);
-
-			if (thing->z + thing->height > bottomz // above bottom
-			&&  thing->z < topz) // below top
-			// don't check angle, the player was clearly in the way in this case
-				P_DamageMobj(thing, tmthing, tmthing, 1, DMG_SPIKE);
-		}
-
 		// Doesn't matter what gravity player's following! Just do your stuff in YOUR direction only
 		if (tmthing->eflags & MFE_VERTICALFLIP
 		&& (tmthing->z + tmthing->height + tmthing->momz < thing->z
@@ -1593,55 +1657,6 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		if (!tmthing->health)
 			return true;
 
-		if (thing->type == MT_SPIKE && (thing->flags & MF_SOLID)) // unfortunate player falls into spike?!
-		{
-			if (thing->eflags & MFE_VERTICALFLIP)
-			{
-				if (tmthing->z + tmthing->height <= thing->z - FixedMul(FRACUNIT, thing->scale)
-				&& tmthing->z + tmthing->height + tmthing->momz >= thing->z - FixedMul(FRACUNIT, thing->scale)
-				&& !(tmthing->player->charability == CA_BOUNCE && tmthing->player->panim == PA_ABILITY && tmthing->eflags & MFE_VERTICALFLIP))
-					P_DamageMobj(tmthing, thing, thing, 1, DMG_SPIKE);
-			}
-			else if (tmthing->z >= thing->z + thing->height + FixedMul(FRACUNIT, thing->scale)
-			&& tmthing->z + tmthing->momz <= thing->z + thing->height + FixedMul(FRACUNIT, thing->scale)
-			&& !(tmthing->player->charability == CA_BOUNCE && tmthing->player->panim == PA_ABILITY && !(tmthing->eflags & MFE_VERTICALFLIP)))
-				P_DamageMobj(tmthing, thing, thing, 1, DMG_SPIKE);
-		}
-
-		if (thing->type == MT_WALLSPIKE && (thing->flags & MF_SOLID))
-		{
-			fixed_t bottomz, topz;
-			angle_t touchangle = R_PointToAngle2(thing->tracer->x, thing->tracer->y, tmthing->x, tmthing->y);
-
-			if (P_PlayerInPain(tmthing->player) && (tmthing->momx || tmthing->momy))
-			{
-				angle_t playerangle = R_PointToAngle2(0, 0, tmthing->momx, tmthing->momy) - touchangle;
-				if (playerangle > ANGLE_180)
-					playerangle = InvAngle(playerangle);
-				if (playerangle < ANGLE_90)
-					return true; // Yes, this is intentionally outside the z-height check. No standing on spikes whilst moving away from them.
-			}
-
-			bottomz = thing->z;
-			topz = thing->z + thing->height;
-
-			if (thing->eflags & MFE_VERTICALFLIP)
-				bottomz -= FixedMul(FRACUNIT, thing->scale);
-			else
-				topz += FixedMul(FRACUNIT, thing->scale);
-
-			if (tmthing->z + tmthing->height > bottomz // above bottom
-			&&  tmthing->z < topz // below top
-			&& !P_MobjWasRemoved(thing->tracer)) // this probably wouldn't work if we didn't have a tracer
-			{ // use base as a reference point to determine what angle you touched the spike at
-				touchangle = thing->angle - touchangle;
-				if (touchangle > ANGLE_180)
-					touchangle = InvAngle(touchangle);
-				if (touchangle <= ANGLE_22h) // if you touched it at this close an angle, you get poked!
-					P_DamageMobj(tmthing, thing, thing, 1, DMG_SPIKE);
-			}
-		}
-
 		if (thing->type == MT_FAN || thing->type == MT_STEAM)
 			P_DoFanAndGasJet(thing, tmthing);
 		else if (thing->flags & MF_SPRING && tmthing->player->powers[pw_carry] != CR_MINECART)
@@ -1706,8 +1721,8 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		}
 	}
 
-	if ((thing->player) && (tmthing->flags & MF_SPRING || tmthing->type == MT_STEAM))
-		; // springs and gas jets should never be able to step up onto a player
+	if ((tmthing->flags & MF_SPRING || tmthing->type == MT_STEAM || tmthing->type == MT_SPIKE || tmthing->type == MT_WALLSPIKE) && (thing->player))
+		; // springs, gas jets and springs should never be able to step up onto a player
 	// z checking at last
 	// Treat noclip things as non-solid!
 	else if ((thing->flags & (MF_SOLID|MF_NOCLIP)) == MF_SOLID
@@ -1715,9 +1730,6 @@ static boolean PIT_CheckThing(mobj_t *thing)
 	{
 		fixed_t topz, tmtopz;
 
-		if (tmthing->type == MT_SPIKE || tmthing->type == MT_WALLSPIKE) // do not run height checks if you are a spike
-			return true;
-
 		if (tmthing->eflags & MFE_VERTICALFLIP)
 		{
 			// pass under
@@ -2654,17 +2666,17 @@ boolean PIT_PushableMoved(mobj_t *thing)
 	return true;
 }
 
-//
-// P_TryMove
-// Attempt to move to a new position.
-//
-boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
+static boolean
+increment_move
+(		mobj_t * thing,
+		fixed_t x,
+		fixed_t y,
+		boolean allowdropoff)
 {
 	fixed_t tryx = thing->x;
 	fixed_t tryy = thing->y;
 	fixed_t radius = thing->radius;
 	fixed_t thingtop;
-	fixed_t startingonground = P_IsObjectOnGround(thing);
 	floatok = false;
 
 	if (radius < MAXRADIUS/2)
@@ -2802,7 +2814,38 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
 		}
 	} while (tryx != x || tryy != y);
 
+	return true;
+}
+
+//
+// P_CheckMove
+// Check if a P_TryMove would be successful.
+//
+boolean P_CheckMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
+{
+	boolean moveok;
+	mobj_t *hack = P_SpawnMobjFromMobj(thing, 0, 0, 0, MT_RAY);
+
+	hack->radius = thing->radius;
+	hack->height = thing->height;
+
+	moveok = increment_move(hack, x, y, allowdropoff);
+	P_RemoveMobj(hack);
+
+	return moveok;
+}
+
+//
+// P_TryMove
+// Attempt to move to a new position.
+//
+boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
+{
+	fixed_t startingonground = P_IsObjectOnGround(thing);
+
 	// The move is ok!
+	if (!increment_move(thing, x, y, allowdropoff))
+		return false;
 
 	// If it's a pushable object, check if anything is
 	// standing on top and move it, too.
diff --git a/src/p_maputl.c b/src/p_maputl.c
index efcebe7363741a3ff1bb3a851dfc7e8653e6f3c8..43a7e92a185f4ba88ef5a2767d774bb928252bca 100644
--- a/src/p_maputl.c
+++ b/src/p_maputl.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_maputl.h b/src/p_maputl.h
index cec344d03df8b810f4a952dabb69de2746dccacd..b7779d88a3d5a4f5f43441cbb4bad3072b2565d3 100644
--- a/src/p_maputl.h
+++ b/src/p_maputl.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 87e20fd4ac5c9135394ed15ba238fad8ed262f85..17be0300a8984aebc3acb101d09ee92b90d21245 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -7845,48 +7845,6 @@ static void P_MobjSceneryThink(mobj_t *mobj)
 		if (P_MobjFlip(mobj)*mobj->momz < mobj->info->speed)
 			mobj->momz = P_MobjFlip(mobj)*mobj->info->speed;
 		break;
-	case MT_SPIKE:
-		if (mobj->fuse)
-		{
-			mobj->fuse--;
-			break;
-		}
-		P_SetMobjState(mobj, mobj->state->nextstate);
-		mobj->fuse = mobj->info->speed;
-		if (mobj->spawnpoint)
-			mobj->fuse += mobj->spawnpoint->angle;
-		break;
-	case MT_WALLSPIKE:
-		if (mobj->fuse)
-		{
-			mobj->fuse--;
-			break;
-		}
-		P_SetMobjState(mobj, mobj->state->nextstate);
-		mobj->fuse = mobj->info->speed;
-		if (mobj->spawnpoint)
-			mobj->fuse += (mobj->spawnpoint->angle / 360);
-		break;
-	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;
-		}
-#endif
-		break;
 	case MT_ROCKCRUMBLE1:
 	case MT_ROCKCRUMBLE2:
 	case MT_ROCKCRUMBLE3:
@@ -7978,6 +7936,9 @@ static void P_MobjSceneryThink(mobj_t *mobj)
 		P_SetScale(mobj, mobj->target->scale);
 	}
 	break;
+	case MT_TUTORIALFLOWER:
+		mobj->angle += FixedAngle(3*FRACUNIT);
+	break;
 	case MT_VWREF:
 	case MT_VWREB:
 	{
@@ -9283,6 +9244,25 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
 
 	switch (mobj->type)
 	{
+	case MT_WALLSPIKEBASE:
+		if (!mobj->target) {
+			P_RemoveMobj(mobj);
+			return false;
+		}
+		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;
+		}
+#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
@@ -9968,6 +9948,18 @@ static boolean P_FuseThink(mobj_t *mobj)
 		break;
 	case MT_METALSONIC_BATTLE:
 		break; // don't remove
+	case MT_SPIKE:
+		P_SetMobjState(mobj, mobj->state->nextstate);
+		mobj->fuse = mobj->info->speed;
+		if (mobj->spawnpoint)
+			mobj->fuse += mobj->spawnpoint->angle;
+		break;
+	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);
 		return false;
@@ -10481,7 +10473,24 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
 	const mobjinfo_t *info = &mobjinfo[type];
 	SINT8 sc = -1;
 	state_t *st;
-	mobj_t *mobj = Z_Calloc(sizeof (*mobj), PU_LEVEL, NULL);
+	mobj_t *mobj;
+
+	if (type == MT_NULL)
+	{
+#if 0		
+#ifdef PARANOIA
+		I_Error("Tried to spawn MT_NULL\n");
+#endif
+		return NULL;
+#endif
+		// Hack: Some code assumes that P_SpawnMobj can never return NULL
+		// So replace MT_NULL with MT_RAY in the meantime
+		// Remove when dealt properly
+		CONS_Debug(DBG_GAMELOGIC, "Tried to spawn MT_NULL, using MT_RAY\n");
+		type = MT_RAY;
+	}
+
+	mobj = Z_Calloc(sizeof (*mobj), PU_LEVEL, NULL);
 
 	// this is officially a mobj, declared as soon as possible.
 	mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker;
@@ -12827,6 +12836,25 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
 		P_SpawnMobjFromMobj(mobj, -FRACUNIT, 0, 0, MT_THZTREEBRANCH)->angle = mobjangle + ANGLE_270;
 	}
 	break;
+	case MT_TUTORIALPLANT:
+	{
+		INT32 i;
+		mobj_t *segment;
+		for (i = 0; i < 6; i++)
+		{
+			segment = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_TUTORIALLEAF);
+			segment->angle = mobj->angle + FixedAngle(i*60*FRACUNIT);
+			P_SetMobjState(segment, S_TUTORIALLEAF1 + mthing->extrainfo);
+		}
+		for (i = 0; i < 3; i++)
+		{
+			segment = P_SpawnMobjFromMobj(mobj, 0, 0, 112*FRACUNIT, MT_TUTORIALFLOWER);
+			segment->angle = mobj->angle + FixedAngle(i*120*FRACUNIT);
+			P_SetMobjState(segment, S_TUTORIALFLOWER1 + mthing->extrainfo);
+		}
+		P_SetMobjState(P_SpawnMobjFromMobj(mobj, 0, 0, 112*FRACUNIT, MT_TUTORIALFLOWERF), S_TUTORIALFLOWERF1 + mthing->extrainfo);
+	}
+	break;
 	case MT_CEZPOLE1:
 	case MT_CEZPOLE2:
 	{ // Spawn the banner
@@ -12970,18 +12998,17 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
 		// Pop up spikes!
 		if (mthing->options & MTF_OBJECTSPECIAL)
 		{
+			mobj->flags &= ~MF_SCENERY;
 			mobj->fuse = (16 - mthing->extrainfo)*(mthing->angle + mobj->info->speed)/16;
 			if (mthing->options & MTF_EXTRA)
 				P_SetMobjState(mobj, mobj->info->meleestate);
 		}
-		else
-			mobj->flags |= MF_NOTHINK;
-		// no collision for spikes if the ambush flag is checked
-		if ((mthing->options & MTF_AMBUSH) || metalrecording)
+		// Use per-thing collision for spikes if the deaf flag isn't checked.
+		if (!(mthing->options & MTF_AMBUSH) && !metalrecording)
 		{
 			P_UnsetThingPosition(mobj);
-			mobj->flags |= (MF_NOBLOCKMAP|MF_NOCLIPHEIGHT);
-			mobj->flags &= ~MF_SOLID;
+			mobj->flags &= ~(MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT);
+			mobj->flags |= MF_SOLID;
 			P_SetThingPosition(mobj);
 		}
 		break;
@@ -12989,20 +13016,20 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
 		// Pop up spikes!
 		if (mthing->options & MTF_OBJECTSPECIAL)
 		{
+			mobj->flags &= ~MF_SCENERY;
 			mobj->fuse = (16 - mthing->extrainfo)*((mthing->angle/360) + mobj->info->speed)/16;
 			if (mthing->options & MTF_EXTRA)
 				P_SetMobjState(mobj, mobj->info->meleestate);
 		}
-		else
-			mobj->flags |= MF_NOTHINK;
-		// no collision for spikes if the ambush flag is checked
-		if ((mthing->options & MTF_AMBUSH) || metalrecording)
+		// Use per-thing collision for spikes if the deaf flag isn't checked.
+		if (!(mthing->options & MTF_AMBUSH) && !metalrecording)
 		{
 			P_UnsetThingPosition(mobj);
-			mobj->flags |= (MF_NOBLOCKMAP|MF_NOCLIPHEIGHT);
-			mobj->flags &= ~MF_SOLID;
+			mobj->flags &= ~(MF_NOBLOCKMAP | MF_NOCLIPHEIGHT);
+			mobj->flags |= MF_SOLID;
 			P_SetThingPosition(mobj);
 		}
+
 		// spawn base
 		{
 			const angle_t mobjangle = FixedAngle(mthing->angle << FRACBITS); // the mobj's own angle hasn't been set quite yet so...
@@ -13016,8 +13043,6 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
 			P_SetScale(base, mobj->scale);
 			P_SetTarget(&base->target, mobj);
 			P_SetTarget(&mobj->tracer, base);
-			if (!(mthing->options & MTF_OBJECTSPECIAL))
-				base->flags |= MF_NOTHINK;
 		}
 		break;
 	case MT_RING_BOX:
diff --git a/src/p_mobj.h b/src/p_mobj.h
index 2d096385bd1689f2fb3402a5ca1489cbfd7bca40..b1b79fd82e8a14009ece5e5a7b2cb55d261621d8 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -251,7 +251,7 @@ typedef enum
 	MFE_FORCENOSUPER		= 1<<13,
 	// Makes an object use super sprites where they wouldn't have otherwise and vice-versa
 	MFE_REVERSESUPER		= MFE_FORCESUPER|MFE_FORCENOSUPER
-	
+
 	// free: to and including 1<<15
 } mobjeflag_t;
 
diff --git a/src/p_polyobj.c b/src/p_polyobj.c
index 6431e46248f0f426de801e2c2e95d9835ab07ff0..0ccb6ebde3b93de3fc532f2cf16b4b4cc2b078c0 100644
--- a/src/p_polyobj.c
+++ b/src/p_polyobj.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2006      by James Haley
-// Copyright (C) 2006-2021 by Sonic Team Junior.
+// Copyright (C) 2006-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_polyobj.h b/src/p_polyobj.h
index 7c814e0bf14766bf6f6cf2af134675389d0f8797..4cc1221dbacb6a149b23aa24b57c3557072e5ba3 100644
--- a/src/p_polyobj.h
+++ b/src/p_polyobj.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2006      by James Haley
-// Copyright (C) 2006-2021 by Sonic Team Junior.
+// Copyright (C) 2006-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_pspr.h b/src/p_pspr.h
index 27002b7136f74747d73ef08c04da46b89c91a909..4136c211823c4b7082891afecfe250e2034ff02c 100644
--- a/src/p_pspr.h
+++ b/src/p_pspr.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 722340f41f6c4fe86f2a70ebfd597bb0a142c290..99ec58bb97c74080ad171d40a65bbf804c25459c 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_saveg.h b/src/p_saveg.h
index a909282fe55e85ce1077d43fa6e69621e2da8a02..9f4a2633fd6a97f861a477a1fce723ff2a56cd15 100644
--- a/src/p_saveg.h
+++ b/src/p_saveg.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_setup.c b/src/p_setup.c
index 5d70daeec83aa971b0f16ed7ed1455138e747f69..80bc47a32eb638fad86246b2dba4f995f4869d62 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -2512,7 +2512,10 @@ static boolean P_LoadExtendedSubsectorsAndSegs(UINT8 **data, nodetype_t nodetype
 		P_InitializeSeg(seg);
 		seg->angle = R_PointToAngle2(v1->x, v1->y, v2->x, v2->y);
 		if (seg->linedef)
-			segs[i].offset = FixedHypot(v1->x - seg->linedef->v1->x, v1->y - seg->linedef->v1->y);
+		{
+			vertex_t *v = (seg->side == 1) ? seg->linedef->v2 : seg->linedef->v1;
+			segs[i].offset = FixedHypot(v1->x - v->x, v1->y - v->y);
+		}
 		seg->length = P_SegLength(seg);
 #ifdef HWRENDER
 		seg->flength = (rendermode == render_opengl) ? P_SegLengthFloat(seg) : 0;
@@ -4373,7 +4376,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
 
 	// internal game map
 	maplumpname = G_BuildMapName(gamemap);
-	lastloadedmaplumpnum = W_CheckNumForName(maplumpname);
+	lastloadedmaplumpnum = W_CheckNumForMap(maplumpname);
 	if (lastloadedmaplumpnum == LUMPERROR)
 		I_Error("Map %s not found.\n", maplumpname);
 
diff --git a/src/p_setup.h b/src/p_setup.h
index c3c680fdd3176392580373f8aedec1d154987006..9cb44ed5979e04d10accc4d64403a736b1810b0c 100644
--- a/src/p_setup.h
+++ b/src/p_setup.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_sight.c b/src/p_sight.c
index 706745f35b45aa4f1439d8e415cc2171d32083a1..1aa231a6c3db613e2bc573b312f0ba4c8d367853 100644
--- a/src/p_sight.c
+++ b/src/p_sight.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_slopes.c b/src/p_slopes.c
index bfca153a628c8246fb5c6a2629e6d609115b7a13..ffbfef2d300f5c2645b54c7dc62fa8c9dc8668b5 100644
--- a/src/p_slopes.c
+++ b/src/p_slopes.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2004      by Stephen McGranahan
-// Copyright (C) 2015-2021 by Sonic Team Junior.
+// Copyright (C) 2015-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_slopes.h b/src/p_slopes.h
index 43cd3edb0d9009f341b9ab82c168c3429c3521a5..d1a053d28fb92d1a0767e566af8c39b86eeabb08 100644
--- a/src/p_slopes.h
+++ b/src/p_slopes.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2004      by Stephen McGranahan
-// Copyright (C) 2015-2021 by Sonic Team Junior.
+// Copyright (C) 2015-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_spec.c b/src/p_spec.c
index ebabe6a7988ff38047ae4aa071032dbc20fca44d..fa590b5cb6432e80ba06ebc23c5163bb23a6d49c 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -2093,7 +2093,7 @@ void P_SwitchWeather(INT32 weathernum)
 	{
 		case PRECIP_SNOW: // snow
 			curWeather = PRECIP_SNOW;
-			
+
 			if (purge)
 				P_SpawnPrecipitation();
 
@@ -2888,25 +2888,20 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 		case 434: // Custom Power
 			if (mo && mo->player)
 			{
-				mobj_t *dummy = P_SpawnMobj(mo->x, mo->y, mo->z, MT_NULL);
-
-				var1 = sides[line->sidenum[0]].toptexture; //(line->dx>>FRACBITS)-1;
+				powertype_t power = sides[line->sidenum[0]].toptexture; //(line->dx>>FRACBITS)-1;
+				UINT16 value;
 
 				if (line->sidenum[1] != 0xffff && line->flags & ML_BLOCKMONSTERS) // read power from back sidedef
-					var2 = sides[line->sidenum[1]].toptexture;
+					value = sides[line->sidenum[1]].toptexture;
 				else if (line->flags & ML_NOCLIMB) // 'Infinite'
-					var2 = UINT16_MAX;
+					value = UINT16_MAX;
 				else
-					var2 = sides[line->sidenum[0]].textureoffset>>FRACBITS;
+					value = sides[line->sidenum[0]].textureoffset>>FRACBITS;
 
-				P_SetTarget(&dummy->target, mo);
-				A_CustomPower(dummy);
+				P_SetPower(mo->player, power, value);
 
-				if (bot) {
-					P_SetTarget(&dummy->target, bot);
-					A_CustomPower(dummy);
-				}
-				P_RemoveMobj(dummy);
+				if (bot)
+					P_SetPower(bot->player, power, value);
 			}
 			break;
 
diff --git a/src/p_spec.h b/src/p_spec.h
index 3b8abfcf8a099ee3bcb4f442d016f83c315b065f..75954abe2f7b8964c2695c67655474b35daedb41 100644
--- a/src/p_spec.h
+++ b/src/p_spec.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_telept.c b/src/p_telept.c
index 6bac5ad208e54f795288925342adb1becb05e021..cbbd0ff6bcffc7b91db3a5d6c7323d55394a4b4c 100644
--- a/src/p_telept.c
+++ b/src/p_telept.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_tick.c b/src/p_tick.c
index 55a16fd81cb8b582fd8ab53c2d64c4f18809b8e2..28ace92883a6c9d5078e1b1fe30098b07536639a 100644
--- a/src/p_tick.c
+++ b/src/p_tick.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_tick.h b/src/p_tick.h
index ae481c6a2aa10df64f680f33aca97b43b5b13bc6..d355bc6d756661e92f7dd2451732b6bbc6a66fbf 100644
--- a/src/p_tick.h
+++ b/src/p_tick.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_user.c b/src/p_user.c
index 0aff39949c2862bc4aa9321b50c09df78ae9d82b..1f14d96c135b6ca512371caab9ec2e327204206f 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -1048,7 +1048,8 @@ void P_DoPlayerPain(player_t *player, mobj_t *source, mobj_t *inflictor)
 			fallbackspeed = FixedMul(4*FRACUNIT, player->mo->scale);
 		}
 
-		player->drawangle = ang + ANGLE_180;
+		if (player->pflags & PF_DIRECTIONCHAR)
+			player->drawangle = ang + ANGLE_180;
 		P_InstaThrust(player->mo, ang, fallbackspeed);
 	}
 
@@ -2010,6 +2011,24 @@ void P_SwitchShield(player_t *player, UINT16 shieldtype)
 	}
 }
 
+//
+// P_SetPower
+//
+// Sets a power and spawns a shield orb if required.
+//
+void P_SetPower(player_t *player, powertype_t power, UINT16 value)
+{
+	boolean spawnshield = false;
+
+	if (power == pw_shield && player->powers[pw_shield] != value)
+		spawnshield = true;
+
+	player->powers[power] = value;
+
+	if (spawnshield) //workaround for a bug
+		P_SpawnShieldOrb(player);
+}
+
 //
 // P_SpawnGhostMobj
 //
@@ -6280,18 +6299,11 @@ static void P_NightsTransferPoints(player_t *player, fixed_t xspeed, fixed_t rad
 	if (player->exiting)
 		return;
 
+	if (!P_CheckMove(player->mo,
+				player->mo->x + player->mo->momx,
+				player->mo->y + player->mo->momy, true))
 	{
-		boolean notallowed;
-		mobj_t *hack = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_NULL);
-		hack->flags = MF_NOGRAVITY;
-		hack->radius = player->mo->radius;
-		hack->height = player->mo->height;
-		hack->z = player->mo->z;
-		P_SetThingPosition(hack);
-		notallowed = (!(P_TryMove(hack, player->mo->x+player->mo->momx, player->mo->y+player->mo->momy, true)));
-		P_RemoveMobj(hack);
-		if (notallowed)
-			return;
+		return;
 	}
 
 	{
@@ -11440,6 +11452,8 @@ void P_PlayerThink(player_t *player)
 		{
 			if (B_CheckRespawn(player))
 				player->playerstate = PST_REBORN;
+			else
+				B_HandleFlightIndicator(player);
 		}
 		if (player->playerstate == PST_REBORN)
 		{
diff --git a/src/r_bsp.c b/src/r_bsp.c
index b8559d39e54cb5debb00878487bae7b9b7a5a29c..c9f2698166aed732ac9c9d69834b1d305809466f 100644
--- a/src/r_bsp.c
+++ b/src/r_bsp.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_bsp.h b/src/r_bsp.h
index 40d24ffece796beb2fcdb1cd2cb5289ad30206ed..88757cf4bccfd6d2b16812c4a889aa19ea32f384 100644
--- a/src/r_bsp.h
+++ b/src/r_bsp.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_data.c b/src/r_data.c
index 2cfe9cb7ace3139d40a32a9d8dd0ba21afa9b794..51ed15dd6961f4e7b0cddd705fd8a9d032a3360d 100644
--- a/src/r_data.c
+++ b/src/r_data.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_data.h b/src/r_data.h
index 7580a94ea0eeab4d446b5b671e9dc760ff69a52b..63772e7b08380ed402b1faf7706642e0ca39dbfb 100644
--- a/src/r_data.h
+++ b/src/r_data.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_defs.h b/src/r_defs.h
index fa63de4004142a364422b83065fbe2acc9137e6f..c6467a970e0a03cfe56c60d2c59a9afb57d8cc8c 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_draw.c b/src/r_draw.c
index 65bb87bfb54de7a8002717c94c868972803409ee..e12d7ebdd46027973fcaa45f6c0ad4eeacca6ceb 100644
--- a/src/r_draw.c
+++ b/src/r_draw.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_draw.h b/src/r_draw.h
index 2173c7a5a36e5c9b92063657aa833dbb5b457726..c96b29e3016dfc464c09197786c0eeba283d552d 100644
--- a/src/r_draw.h
+++ b/src/r_draw.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -170,6 +170,7 @@ void R_DrawViewBorder(void);
 void R_DrawColumn_8(void);
 void R_DrawShadeColumn_8(void);
 void R_DrawTranslucentColumn_8(void);
+void R_DrawDropShadowColumn_8(void);
 void R_DrawTranslatedColumn_8(void);
 void R_DrawTranslatedTranslucentColumn_8(void);
 void R_Draw2sMultiPatchColumn_8(void);
diff --git a/src/r_draw16.c b/src/r_draw16.c
index 1a2fed77316b6fd80c6b91f5d1357e1b47eee99f..763fd1631e7ac57208c6901adc0cd57df4c71e16 100644
--- a/src/r_draw16.c
+++ b/src/r_draw16.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_draw8.c b/src/r_draw8.c
index b8a63d5c042d7ce49f8480bac269b763c43f5dd6..c9a9e957502ba09444fb9b9f3aca8f51540d1d01 100644
--- a/src/r_draw8.c
+++ b/src/r_draw8.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -416,6 +416,39 @@ void R_DrawTranslucentColumn_8(void)
 	}
 }
 
+// Hack: A cut-down copy of R_DrawTranslucentColumn_8 that does not read texture
+// data since something about calculating the texture reading address for drop shadows is broken.
+// dc_texturemid and dc_iscale get wrong values for drop shadows, however those are not strictly
+// needed for the current design of the shadows, so this function bypasses the issue
+// by not using those variables at all.
+void R_DrawDropShadowColumn_8(void)
+{
+	register INT32 count;
+	register UINT8 *dest;
+
+	count = dc_yh - dc_yl + 1;
+
+	if (count <= 0) // Zero length, column does not exceed a pixel.
+		return;
+
+	dest = &topleft[dc_yl*vid.width + dc_x];
+
+	{
+#define DSCOLOR 31 // palette index for the color of the shadow
+		register const UINT8 *transmap_offset = dc_transmap + (dc_colormap[DSCOLOR] << 8);
+#undef DSCOLOR
+		while ((count -= 2) >= 0)
+		{
+			*dest = *(transmap_offset + (*dest));
+			dest += vid.width;
+			*dest = *(transmap_offset + (*dest));
+			dest += vid.width;
+		}
+		if (count & 1)
+			*dest = *(transmap_offset + (*dest));
+	}
+}
+
 /**	\brief The R_DrawTranslatedTranslucentColumn_8 function
 	Spiffy function. Not only does it colormap a sprite, but does translucency as well.
 	Uber-kudos to Cyan Helkaraxe
diff --git a/src/r_draw8_npo2.c b/src/r_draw8_npo2.c
index 2433cb4024295401017fae6b08394a7db7d0d0df..49ec28dd8d0c7a8394c614ad3900ab88a5d0c7b9 100644
--- a/src/r_draw8_npo2.c
+++ b/src/r_draw8_npo2.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -992,6 +992,9 @@ void R_DrawTiltedFloorSprite_NPO2_8(void)
 	double endz, endu, endv;
 	UINT32 stepu, stepv;
 
+	struct libdivide_u32_t x_divider = libdivide_u32_gen(ds_flatwidth);
+	struct libdivide_u32_t y_divider = libdivide_u32_gen(ds_flatheight);
+
 	iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx);
 	uz = ds_sup->z + ds_sup->y*(centery-ds_y) + ds_sup->x*(ds_x1-centerx);
 	vz = ds_svp->z + ds_svp->y*(centery-ds_y) + ds_svp->x*(ds_x1-centerx);
@@ -1033,12 +1036,13 @@ void R_DrawTiltedFloorSprite_NPO2_8(void)
 
 			// Carefully align all of my Friends.
 			if (x < 0)
-				x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+				x += (libdivide_u32_do((UINT32)(-x-1), &x_divider) + 1) * ds_flatwidth;
+			else
+				x -= libdivide_u32_do((UINT32)x, &x_divider) * ds_flatwidth;
 			if (y < 0)
-				y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
-
-			x %= ds_flatwidth;
-			y %= ds_flatheight;
+				y += (libdivide_u32_do((UINT32)(-y-1), &y_divider) + 1) * ds_flatheight;
+			else
+				y -= libdivide_u32_do((UINT32)y, &y_divider) * ds_flatheight;
 
 			val = source[((y * ds_flatwidth) + x)];
 			if (val & 0xFF00)
@@ -1065,12 +1069,13 @@ void R_DrawTiltedFloorSprite_NPO2_8(void)
 
 				// Carefully align all of my Friends.
 				if (x < 0)
-					x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+					x += (libdivide_u32_do((UINT32)(-x-1), &x_divider) + 1) * ds_flatwidth;
+				else
+					x -= libdivide_u32_do((UINT32)x, &x_divider) * ds_flatwidth;
 				if (y < 0)
-					y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
-
-				x %= ds_flatwidth;
-				y %= ds_flatheight;
+					y += (libdivide_u32_do((UINT32)(-y-1), &y_divider) + 1) * ds_flatheight;
+				else
+					y -= libdivide_u32_do((UINT32)y, &y_divider) * ds_flatheight;
 
 				val = source[((y * ds_flatwidth) + x)];
 				if (val & 0xFF00)
@@ -1101,12 +1106,13 @@ void R_DrawTiltedFloorSprite_NPO2_8(void)
 
 				// Carefully align all of my Friends.
 				if (x < 0)
-					x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+					x += (libdivide_u32_do((UINT32)(-x-1), &x_divider) + 1) * ds_flatwidth;
+				else
+					x -= libdivide_u32_do((UINT32)x, &x_divider) * ds_flatwidth;
 				if (y < 0)
-					y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
-
-				x %= ds_flatwidth;
-				y %= ds_flatheight;
+					y += (libdivide_u32_do((UINT32)(-y-1), &y_divider) + 1) * ds_flatheight;
+				else
+					y -= libdivide_u32_do((UINT32)y, &y_divider) * ds_flatheight;
 
 				val = source[((y * ds_flatwidth) + x)];
 				if (val & 0xFF00)
@@ -1142,6 +1148,9 @@ void R_DrawTiltedTranslucentFloorSprite_NPO2_8(void)
 	double endz, endu, endv;
 	UINT32 stepu, stepv;
 
+	struct libdivide_u32_t x_divider = libdivide_u32_gen(ds_flatwidth);
+	struct libdivide_u32_t y_divider = libdivide_u32_gen(ds_flatheight);
+
 	iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx);
 	uz = ds_sup->z + ds_sup->y*(centery-ds_y) + ds_sup->x*(ds_x1-centerx);
 	vz = ds_svp->z + ds_svp->y*(centery-ds_y) + ds_svp->x*(ds_x1-centerx);
@@ -1183,12 +1192,13 @@ void R_DrawTiltedTranslucentFloorSprite_NPO2_8(void)
 
 			// Carefully align all of my Friends.
 			if (x < 0)
-				x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+				x += (libdivide_u32_do((UINT32)(-x-1), &x_divider) + 1) * ds_flatwidth;
+			else
+				x -= libdivide_u32_do((UINT32)x, &x_divider) * ds_flatwidth;
 			if (y < 0)
-				y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
-
-			x %= ds_flatwidth;
-			y %= ds_flatheight;
+				y += (libdivide_u32_do((UINT32)(-y-1), &y_divider) + 1) * ds_flatheight;
+			else
+				y -= libdivide_u32_do((UINT32)y, &y_divider) * ds_flatheight;
 
 			val = source[((y * ds_flatwidth) + x)];
 			if (val & 0xFF00)
@@ -1215,12 +1225,13 @@ void R_DrawTiltedTranslucentFloorSprite_NPO2_8(void)
 
 				// Carefully align all of my Friends.
 				if (x < 0)
-					x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+					x += (libdivide_u32_do((UINT32)(-x-1), &x_divider) + 1) * ds_flatwidth;
+				else
+					x -= libdivide_u32_do((UINT32)x, &x_divider) * ds_flatwidth;
 				if (y < 0)
-					y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
-
-				x %= ds_flatwidth;
-				y %= ds_flatheight;
+					y += (libdivide_u32_do((UINT32)(-y-1), &y_divider) + 1) * ds_flatheight;
+				else
+					y -= libdivide_u32_do((UINT32)y, &y_divider) * ds_flatheight;
 
 				val = source[((y * ds_flatwidth) + x)];
 				if (val & 0xFF00)
@@ -1251,12 +1262,13 @@ void R_DrawTiltedTranslucentFloorSprite_NPO2_8(void)
 
 				// Carefully align all of my Friends.
 				if (x < 0)
-					x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+					x += (libdivide_u32_do((UINT32)(-x-1), &x_divider) + 1) * ds_flatwidth;
+				else
+					x -= libdivide_u32_do((UINT32)x, &x_divider) * ds_flatwidth;
 				if (y < 0)
-					y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
-
-				x %= ds_flatwidth;
-				y %= ds_flatheight;
+					y += (libdivide_u32_do((UINT32)(-y-1), &y_divider) + 1) * ds_flatheight;
+				else
+					y -= libdivide_u32_do((UINT32)y, &y_divider) * ds_flatheight;
 
 				val = source[((y * ds_flatwidth) + x)];
 				if (val & 0xFF00)
diff --git a/src/r_local.h b/src/r_local.h
index ba78ea87dbae64b69864e8afcbd05915113e6af5..a5b590e5cea24dff0cb63952241ce8b8bd9975fb 100644
--- a/src/r_local.h
+++ b/src/r_local.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_main.c b/src/r_main.c
index 8729b5dcb36ccedb8aed999dbf3afcb60e0faf04..13d2413fae4611ab4a1079e08e260466790b1529 100644
--- a/src/r_main.c
+++ b/src/r_main.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -1450,7 +1450,7 @@ static void Mask_Post (maskcount_t* m)
 
 void R_RenderPlayerView(player_t *player)
 {
-	UINT8			nummasks	= 1;
+	INT32			nummasks	= 1;
 	maskcount_t*	masks		= malloc(sizeof(maskcount_t));
 
 	if (cv_homremoval.value && player == &players[displayplayer]) // if this is display player 1
diff --git a/src/r_main.h b/src/r_main.h
index 5f3bed9803102cb7592ac755ef09f30870cc3f9f..c0edb31b30175295ecbf9fba247ef49a699953b9 100644
--- a/src/r_main.h
+++ b/src/r_main.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_patch.c b/src/r_patch.c
index 6827cd12c75d397fdaa771f3373c5d4812a83952..e771e5c94d4ed4a381ee7ddeb937a5e146ecef5f 100644
--- a/src/r_patch.c
+++ b/src/r_patch.c
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2020-2021 by Jaime "Lactozilla" Passos.
+// Copyright (C) 2020-2022 by Jaime "Lactozilla" Passos.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_patch.h b/src/r_patch.h
index 96fbb0e28c11d3bbd91f84edc999a7d6d5223c70..26c28e1f9c4bbfb2a1b46b55e8fd737a0a0b3804 100644
--- a/src/r_patch.h
+++ b/src/r_patch.h
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2020-2021 by Jaime "Lactozilla" Passos.
+// Copyright (C) 2020-2022 by Jaime "Lactozilla" Passos.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_patchrotation.c b/src/r_patchrotation.c
index 5dbc30286630c4f51eee55bb7fe2a8d57f4a10e4..b24e065ba6cfb172ce926805e4557c11b693a00e 100644
--- a/src/r_patchrotation.c
+++ b/src/r_patchrotation.c
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2020-2021 by Jaime "Lactozilla" Passos.
+// Copyright (C) 2020-2022 by Jaime "Lactozilla" Passos.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_patchrotation.h b/src/r_patchrotation.h
index 689b7d411b63d0143804d789da0501fb1f0d1415..e6bee80edd77392f60a6880aeafb0f1984ee352d 100644
--- a/src/r_patchrotation.h
+++ b/src/r_patchrotation.h
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2020-2021 by Jaime "Lactozilla" Passos.
+// Copyright (C) 2020-2022 by Jaime "Lactozilla" Passos.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_picformats.c b/src/r_picformats.c
index 5c81d1e02186902818415c84088fcfc182235681..6aa4659b9ea7288272d0d779f007391955a5bba6 100644
--- a/src/r_picformats.c
+++ b/src/r_picformats.c
@@ -2,8 +2,8 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 2005-2009 by Andrey "entryway" Budko.
-// Copyright (C) 2018-2021 by Jaime "Lactozilla" Passos.
-// Copyright (C) 2019-2021 by Sonic Team Junior.
+// Copyright (C) 2018-2022 by Jaime "Lactozilla" Passos.
+// Copyright (C) 2019-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_picformats.h b/src/r_picformats.h
index 73010e092f3c620aceed7076630dda6c16239624..f3080479fc9c4555004173a6ac43cd7d0f3ef0b6 100644
--- a/src/r_picformats.h
+++ b/src/r_picformats.h
@@ -1,8 +1,8 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
-// Copyright (C) 2018-2021 by Jaime "Lactozilla" Passos.
-// Copyright (C) 2019-2021 by Sonic Team Junior.
+// Copyright (C) 2018-2022 by Jaime "Lactozilla" Passos.
+// Copyright (C) 2019-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_plane.c b/src/r_plane.c
index 1f5c0192e701f496d55cd36bed0e23e419ad0bbe..e31d52be9a904b2410061939bc1c5f867452382f 100644
--- a/src/r_plane.c
+++ b/src/r_plane.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_plane.h b/src/r_plane.h
index 862b95069ddacdd85382ef7f55c476a592b4177b..09648feadc938bdb66e9d03403a401c543a50c96 100644
--- a/src/r_plane.h
+++ b/src/r_plane.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_portal.c b/src/r_portal.c
index 3026f4e4c0a99ea9cde1503eb90952e06b49a26b..4d413213373c1e98522862e9e0e241396a4627b4 100644
--- a/src/r_portal.c
+++ b/src/r_portal.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -132,6 +132,7 @@ static portal_t* Portal_Add (const INT16 x1, const INT16 x2)
 
 void Portal_Remove (portal_t* portal)
 {
+	portalcullsector = NULL;
 	portal_base = portal->next;
 	Z_Free(portal->ceilingclip);
 	Z_Free(portal->floorclip);
diff --git a/src/r_portal.h b/src/r_portal.h
index 0effd07b5b272e5f799dc04848c6f66ef8dbdede..687ee058f0f4cf9e5c0e4d4c8b6d705cf4c32834 100644
--- a/src/r_portal.h
+++ b/src/r_portal.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_segs.c b/src/r_segs.c
index 157cf466e6f05385ef324f5fdf87ce57e76245c0..41ffa4103aab482ceda2070afd595c5e2d6156d1 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_segs.h b/src/r_segs.h
index da7d44ad4689f0c28da9e0554caba54e7b8aa0ba..4075cc0bba99fcbce78ad97f1e95826f84bbe1ad 100644
--- a/src/r_segs.h
+++ b/src/r_segs.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_skins.c b/src/r_skins.c
index 86c0bbc544b7907f30b6ed4ef6c07326f8669a98..cd53128d2297558c48ed7d79d026d081f0c1b8a1 100644
--- a/src/r_skins.c
+++ b/src/r_skins.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_skins.h b/src/r_skins.h
index a38997f4dd623aad8dc7cdf21aff1283f8c8aa93..aeaa9f3e05a328ebd1e299190fe1179220191e33 100644
--- a/src/r_skins.h
+++ b/src/r_skins.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_sky.c b/src/r_sky.c
index 041cccfc5546f679894a7d7ab2e6cc93c0d8c8c4..e21b7cbf16b53720dddf75a3d5b014ead890f56a 100644
--- a/src/r_sky.c
+++ b/src/r_sky.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_sky.h b/src/r_sky.h
index f4356dcfae3f4f6e47248bb7d6ef46e21495fa31..31c821d2204c14d37bede811d25e4d25916e8ea0 100644
--- a/src/r_sky.h
+++ b/src/r_sky.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_splats.c b/src/r_splats.c
index c554e9b1f002937671e19b1e043460d3168c3e2e..0a84a3a336b6a7f7fad2bf80d025fa2163f18b02 100644
--- a/src/r_splats.c
+++ b/src/r_splats.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -482,7 +482,7 @@ static void R_RasterizeFloorSplat(floorsplat_t *pSplat, vector2_t *verts, visspr
 			continue;
 
 		for (i = x1; i <= x2; i++)
-			cliptab[i] = (y >= mfloorclip[i]);
+			cliptab[i] = (y >= mfloorclip[i] || y <= mceilingclip[i]);
 
 		// clip left
 		while (cliptab[x1])
diff --git a/src/r_splats.h b/src/r_splats.h
index 7e31406d1290e94861a1f660299a05be3dfc8670..ec6885e269249cb7090a771e90093b4d645d73a7 100644
--- a/src/r_splats.h
+++ b/src/r_splats.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_state.h b/src/r_state.h
index 5a606ed8c9fa2804f5802d8834a1f7d85b963260..69989e7ac36a5540c6e99fc514d61948d1a92633 100644
--- a/src/r_state.h
+++ b/src/r_state.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_textures.c b/src/r_textures.c
index 3a761136507656667dddd9ebb34a832a2381fca5..03f8f53a50a9e80f7df130a425a1331801deb876 100644
--- a/src/r_textures.c
+++ b/src/r_textures.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_textures.h b/src/r_textures.h
index 69b59426a718ce7a89d6972d95ac231d0fbc9c8c..9aa11ad4d9da7fce8b85b961d24b1f2109d3429d 100644
--- a/src/r_textures.h
+++ b/src/r_textures.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_things.c b/src/r_things.c
index accd1e2b3cd16795a27700d23f128d25e3c99fbb..5d752b6f7c5a8df162555e5bc28f983bc628725a 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -837,6 +837,12 @@ static void R_DrawVisSprite(vissprite_t *vis)
 	else if (vis->mobj->sprite == SPR_PLAY) // Looks like a player, but doesn't have a color? Get rid of green sonic syndrome.
 		colfunc = colfuncs[COLDRAWFUNC_TRANS];
 
+	// Hack: Use a special column function for drop shadows that bypasses
+	// invalid memory access crashes caused by R_ProjectDropShadow putting wrong values
+	// in dc_texturemid and dc_iscale when the shadow is sloped.
+	if (vis->cut & SC_SHADOW)
+		colfunc = R_DrawDropShadowColumn_8;
+
 	if (vis->extra_colormap && !(vis->renderflags & RF_NOCOLORMAPS))
 	{
 		if (!dc_colormap)
@@ -3001,13 +3007,25 @@ void R_ClipVisSprite(vissprite_t *spr, INT32 x1, INT32 x2, drawseg_t* dsstart, p
 
 	if (portal)
 	{
-		for (x = x1; x <= x2; x++)
+		INT32 start_index = max(portal->start, x1);
+		INT32 end_index = min(portal->start + portal->end - portal->start, x2);
+		for (x = x1; x < start_index; x++)
+		{
+			spr->clipbot[x] = -1;
+			spr->cliptop[x] = -1;
+		}
+		for (x = start_index; x <= end_index; x++)
 		{
 			if (spr->clipbot[x] > portal->floorclip[x - portal->start])
 				spr->clipbot[x] = portal->floorclip[x - portal->start];
 			if (spr->cliptop[x] < portal->ceilingclip[x - portal->start])
 				spr->cliptop[x] = portal->ceilingclip[x - portal->start];
 		}
+		for (x = end_index + 1; x <= x2; x++)
+		{
+			spr->clipbot[x] = -1;
+			spr->cliptop[x] = -1;
+		}
 	}
 }
 
@@ -3168,10 +3186,10 @@ static void R_DrawMaskedList (drawnode_t* head)
 	}
 }
 
-void R_DrawMasked(maskcount_t* masks, UINT8 nummasks)
+void R_DrawMasked(maskcount_t* masks, INT32 nummasks)
 {
 	drawnode_t *heads;	/**< Drawnode lists; as many as number of views/portals. */
-	SINT8 i;
+	INT32 i;
 
 	heads = calloc(nummasks, sizeof(drawnode_t));
 
diff --git a/src/r_things.h b/src/r_things.h
index b1ff32b1ee4a3e723bb020f27a397c71e3f91297..857b03b24564be256e0fc8825b77e4ec6255e008 100644
--- a/src/r_things.h
+++ b/src/r_things.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -100,7 +100,7 @@ typedef struct
 	sector_t* viewsector;
 } maskcount_t;
 
-void R_DrawMasked(maskcount_t* masks, UINT8 nummasks);
+void R_DrawMasked(maskcount_t* masks, INT32 nummasks);
 
 // ----------
 // VISSPRITES
diff --git a/src/s_sound.c b/src/s_sound.c
index 76f0d67c16e490777c3e9c6e59bdf625e221945c..7e61e8a5585843240f5da219ab726157d35232a6 100644
--- a/src/s_sound.c
+++ b/src/s_sound.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/s_sound.h b/src/s_sound.h
index 8fcb816d906accd7f63b6756d8915327a97c4bc0..6223c4fdbaf3e3dca643be1a237e6b02792c4bb1 100644
--- a/src/s_sound.h
+++ b/src/s_sound.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/screen.c b/src/screen.c
index 770f1c8026aaf4fcb5dd9df97da55271f717b547..73af4313deab86ec5bdf4418f8428d6b035a4e70 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/screen.h b/src/screen.h
index 67880e2b964dc16a7693d754a6646bd031f14c04..37695316916ad839be0e8211a8531e8addf0180d 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index ccec37093698edbcc7865702fea0138db5996749..9de632734de97440b62e831a39c12a93020e7429 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -5,7 +5,7 @@
 //
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Portions Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 2014-2021 by Sonic Team Junior.
+// Copyright (C) 2014-2022 by Sonic Team Junior.
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -358,9 +358,10 @@ static void I_ReportSignal(int num, int coredumped)
 
 	I_OutputMsg("\nProcess killed by signal: %s\n\n", sigmsg);
 
-	SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
-		"Process killed by signal",
-		sigmsg, NULL);
+	if (!M_CheckParm("-dedicated"))
+		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
+			"Process killed by signal",
+			sigmsg, NULL);
 }
 
 #ifndef NEWSIGNALHANDLER
@@ -2202,9 +2203,10 @@ static void newsignalhandler_Warn(const char *pr)
 
 	I_OutputMsg("%s\n", text);
 
-	SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
-		"Startup error",
-		text, NULL);
+	if (!M_CheckParm("-dedicated"))
+		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
+			"Startup error",
+			text, NULL);
 
 	I_ShutdownConsole();
 	exit(-1);
@@ -2405,9 +2407,10 @@ void I_Error(const char *error, ...)
 			// Implement message box with SDL_ShowSimpleMessageBox,
 			// which should fail gracefully if it can't put a message box up
 			// on the target system
-			SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
-				"SRB2 "VERSIONSTRING" Recursive Error",
-				buffer, NULL);
+			if (!M_CheckParm("-dedicated"))
+				SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
+					"SRB2 "VERSIONSTRING" Recursive Error",
+					buffer, NULL);
 
 			W_Shutdown();
 			exit(-1); // recursive errors detected
@@ -2449,9 +2452,10 @@ void I_Error(const char *error, ...)
 	// Implement message box with SDL_ShowSimpleMessageBox,
 	// which should fail gracefully if it can't put a message box up
 	// on the target system
-	SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
-		"SRB2 "VERSIONSTRING" Error",
-		buffer, NULL);
+	if (!M_CheckParm("-dedicated"))
+		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
+			"SRB2 "VERSIONSTRING" Error",
+			buffer, NULL);
 	// Note that SDL_ShowSimpleMessageBox does *not* require SDL to be
 	// initialized at the time, so calling it after SDL_Quit() is
 	// perfectly okay! In addition, we do this on purpose so the
diff --git a/src/sdl/i_threads.c b/src/sdl/i_threads.c
index f73d00bcfc2ee70eba47f432812ec8cb4db7ec39..a182ae197c82ec2c97d1bca3c40d1c06f1dd70d1 100644
--- a/src/sdl/i_threads.c
+++ b/src/sdl/i_threads.c
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2020-2021 by James R.
+// Copyright (C) 2020-2022 by James R.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index ed766ff23dfb79395bbb5648211be73c45fc34b6..a27a5ebd2687b9e7c6dee529e3a40aa9c76f54e1 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -4,7 +4,7 @@
 //
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Portions Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 2014-2021 by Sonic Team Junior.
+// Copyright (C) 2014-2022 by Sonic Team Junior.
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c
index 35a79acc0b16ece0a457018db2df9d7a94b2781b..748cd374b6a54f5b65bb0637853648582163b16c 100644
--- a/src/sdl/mixer_sound.c
+++ b/src/sdl/mixer_sound.c
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2014-2021 by Sonic Team Junior.
+// Copyright (C) 2014-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/sdl/ogl_sdl.c b/src/sdl/ogl_sdl.c
index bdc693ca5306fee0a6ea20c13dc12f4a190b0821..67e98d4f5fe8c9bcbaebbe9558a1bc9b6a537c20 100644
--- a/src/sdl/ogl_sdl.c
+++ b/src/sdl/ogl_sdl.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 //
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 2014-2021 by Sonic Team Junior.
+// Copyright (C) 2014-2022 by Sonic Team Junior.
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
diff --git a/src/sdl/ogl_sdl.h b/src/sdl/ogl_sdl.h
index 8f87f688e36a4b897268c1f0fb3f55747779b908..9744bc6f12b8b3ae4babe4e97c05c889ab2a2291 100644
--- a/src/sdl/ogl_sdl.h
+++ b/src/sdl/ogl_sdl.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 //
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 2014-2021 by Sonic Team Junior.
+// Copyright (C) 2014-2022 by Sonic Team Junior.
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
diff --git a/src/sdl/sdl_sound.c b/src/sdl/sdl_sound.c
index 058b601c350072e93f41bae736a89f432d5d79cf..0de3788fe3f36db13dfe37e710abe0f95a2d84d8 100644
--- a/src/sdl/sdl_sound.c
+++ b/src/sdl/sdl_sound.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 //
 // Copyright (C) 1993-1996 by id Software, Inc.
-// Copyright (C) 2014-2021 by Sonic Team Junior.
+// Copyright (C) 2014-2022 by Sonic Team Junior.
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
diff --git a/src/sdl/sdlmain.h b/src/sdl/sdlmain.h
index a9676b5c2f1261bbc7aab49d0ada71e4cba50f91..6b6e79d9756e20b6fd4faa1072f6dd45bdce0cd1 100644
--- a/src/sdl/sdlmain.h
+++ b/src/sdl/sdlmain.h
@@ -1,7 +1,7 @@
 // Emacs style mode select   -*- C++ -*-
 //-----------------------------------------------------------------------------
 //
-// Copyright (C) 2006-2021 by Sonic Team Junior.
+// Copyright (C) 2006-2022 by Sonic Team Junior.
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
diff --git a/src/sounds.c b/src/sounds.c
index 4c5b11ee98294cf21662512c87eb2c566cae7069..f7f3ad328c12f5e2dac4b06f949091c30996a786 100644
--- a/src/sounds.c
+++ b/src/sounds.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/sounds.h b/src/sounds.h
index 2dd37953c5750e87f2f904ffa11dc17d413611af..eec5186896d25b637065e4059acbcac3d6e3e784 100644
--- a/src/sounds.h
+++ b/src/sounds.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/st_stuff.c b/src/st_stuff.c
index f17b58fa62d0161d859c5371beb3d5c0a161c6da..6c9a0eeca1461dfdfa7630b0d33ab379673edfdd 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/st_stuff.h b/src/st_stuff.h
index b1ea2942d3be73188a1a8a4905f27b5a38b5758f..c59bc2ac69ebe9f73fdbe6b013d7b93e739090fe 100644
--- a/src/st_stuff.h
+++ b/src/st_stuff.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/strcasestr.c b/src/strcasestr.c
index 1cbee286a433a722dcb0b95a25fa5dccf64485c9..6a686d6dc8eabdac832168b00e7addf521746774 100644
--- a/src/strcasestr.c
+++ b/src/strcasestr.c
@@ -2,7 +2,7 @@
 strcasestr -- case insensitive substring searching function.
 */
 /*
-Copyright 2019-2021 James R.
+Copyright 2019-2022 James R.
 All rights reserved.
 
 Redistribution and use in source forms, with or without modification, is
diff --git a/src/string.c b/src/string.c
index f32025612283c02e7da2522be5c94fe8596efc56..5534a3f0ceb83b41edbaa6e3517eeb38f304e38d 100644
--- a/src/string.c
+++ b/src/string.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2006      by Graue.
-// Copyright (C) 2006-2021 by Sonic Team Junior.
+// Copyright (C) 2006-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/tables.c b/src/tables.c
index 9263f42d327f679e10a9a3899acd81eee4171ddf..13949b6a78c337a9183a7fd857c04135899acc7e 100644
--- a/src/tables.c
+++ b/src/tables.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/tables.h b/src/tables.h
index baa3adf36de62eb88cb31dfed7e2dcd535205f50..c44c7d525b5d205197843ad083bb84d1fc257a68 100644
--- a/src/tables.h
+++ b/src/tables.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/taglist.c b/src/taglist.c
index d08750f9b096a8e647bdec63e83feb2c7dc66c35..6b50a51ab081b3604b0aef8f93b725a3e2ab3c35 100644
--- a/src/taglist.c
+++ b/src/taglist.c
@@ -1,8 +1,8 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
-// Copyright (C) 2020-2021 by Nev3r.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 2020-2022 by Nev3r.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/taglist.h b/src/taglist.h
index ae1a81c896dfb8bc19c348f50ef99eb258975d4f..d326ef357855efeb3ea70729fcc7d1812a47391c 100644
--- a/src/taglist.h
+++ b/src/taglist.h
@@ -1,8 +1,8 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
-// Copyright (C) 2020-2021 by Nev3r.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 2020-2022 by Nev3r.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/tmap.nas b/src/tmap.nas
index 5bf28359e6b75b1753e94eea8d0fa77b077978b8..f096c8141c14fd8b3d1e2138013a1963b62f0991 100644
--- a/src/tmap.nas
+++ b/src/tmap.nas
@@ -1,7 +1,7 @@
 ;; SONIC ROBO BLAST 2
 ;;-----------------------------------------------------------------------------
 ;; Copyright (C) 1998-2000 by DooM Legacy Team.
-;; Copyright (C) 1999-2021 by Sonic Team Junior.
+;; Copyright (C) 1999-2022 by Sonic Team Junior.
 ;;
 ;; This program is free software distributed under the
 ;; terms of the GNU General Public License, version 2.
diff --git a/src/tmap.s b/src/tmap.s
index 62dcf85dcc00ed350e3bf145e03eedb885e4ee6a..5bb2dea12739094aac7792f2fd6ec5681dabb04a 100644
--- a/src/tmap.s
+++ b/src/tmap.s
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/tmap_asm.s b/src/tmap_asm.s
index b5a0a51e91dd00f330b33d2cf0ca9cee60373323..8e307f42b71023b3a4cbd5a5687d6556113342c4 100644
--- a/src/tmap_asm.s
+++ b/src/tmap_asm.s
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/tmap_mmx.nas b/src/tmap_mmx.nas
index 8b6ef91a60aeff852abee8824eb8f49a6572df92..5312f3c7667edbdd0ee9c6866e99cab61f8813de 100644
--- a/src/tmap_mmx.nas
+++ b/src/tmap_mmx.nas
@@ -1,7 +1,7 @@
 ;; SONIC ROBO BLAST 2
 ;;-----------------------------------------------------------------------------
 ;; Copyright (C) 1998-2000 by DOSDOOM.
-;; Copyright (C) 2010-2021 by Sonic Team Junior.
+;; Copyright (C) 2010-2022 by Sonic Team Junior.
 ;;
 ;; This program is free software distributed under the
 ;; terms of the GNU General Public License, version 2.
diff --git a/src/tmap_vc.nas b/src/tmap_vc.nas
index b6ee26e6b8f22d481b419dac46227b010f78058f..44b2d2e7beb2495be9983cfc75cee4b62ce846ce 100644
--- a/src/tmap_vc.nas
+++ b/src/tmap_vc.nas
@@ -1,7 +1,7 @@
 ;; SONIC ROBO BLAST 2
 ;;-----------------------------------------------------------------------------
 ;; Copyright (C) 1998-2000 by DooM Legacy Team.
-;; Copyright (C) 1999-2021 by Sonic Team Junior.
+;; Copyright (C) 1999-2022 by Sonic Team Junior.
 ;;
 ;; This program is free software distributed under the
 ;; terms of the GNU General Public License, version 2.
diff --git a/src/v_video.c b/src/v_video.c
index 12588f9c2fc2bebda04d6292e137778dff504d63..da725f78ad4d195b2d37ba0df9d50e2a3903a041 100644
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -539,25 +539,21 @@ void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vsca
 	patchdrawfunc = standardpdraw;
 
 	v_translevel = NULL;
-	if (alphalevel)
+	if (alphalevel || blendmode)
 	{
-		if (alphalevel == 10)
+		if (alphalevel == 10) // V_HUDTRANSHALF
 			alphalevel = hudminusalpha[st_translucency];
-		else if (alphalevel == 11)
+		else if (alphalevel == 11) // V_HUDTRANS
 			alphalevel = 10 - st_translucency;
-		else if (alphalevel == 12)
+		else if (alphalevel == 12) // V_HUDTRANSDOUBLE
 			alphalevel = hudplusalpha[st_translucency];
 
 		if (alphalevel >= 10)
 			return; // invis
 
-		if (alphalevel)
+		if (alphalevel || blendmode)
 		{
-			if (blendmode)
-				v_translevel = R_GetBlendTable(blendmode+1, alphalevel);
-			else
-				v_translevel = R_GetTranslucencyTable(alphalevel);
-
+			v_translevel = R_GetBlendTable(blendmode+1, alphalevel);
 			patchdrawfunc = translucentpdraw;
 		}
 	}
@@ -833,25 +829,21 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, IN
 	patchdrawfunc = standardpdraw;
 
 	v_translevel = NULL;
-	if (alphalevel)
+	if (alphalevel || blendmode)
 	{
-		if (alphalevel == 10)
+		if (alphalevel == 10) // V_HUDTRANSHALF
 			alphalevel = hudminusalpha[st_translucency];
-		else if (alphalevel == 11)
+		else if (alphalevel == 11) // V_HUDTRANS
 			alphalevel = 10 - st_translucency;
-		else if (alphalevel == 12)
+		else if (alphalevel == 12) // V_HUDTRANSDOUBLE
 			alphalevel = hudplusalpha[st_translucency];
 
 		if (alphalevel >= 10)
 			return; // invis
 
-		if (alphalevel)
+		if (alphalevel || blendmode)
 		{
-			if (blendmode)
-				v_translevel = R_GetBlendTable(blendmode+1, alphalevel);
-			else
-				v_translevel = R_GetTranslucencyTable(alphalevel);
-
+			v_translevel = R_GetBlendTable(blendmode+1, alphalevel);
 			patchdrawfunc = translucentpdraw;
 		}
 	}
@@ -1410,11 +1402,11 @@ void V_DrawFillConsoleMap(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c)
 
 	if ((alphalevel = ((c & V_ALPHAMASK) >> V_ALPHASHIFT)))
 	{
-		if (alphalevel == 10)
+		if (alphalevel == 10) // V_HUDTRANSHALF
 			alphalevel = hudminusalpha[st_translucency];
-		else if (alphalevel == 11)
+		else if (alphalevel == 11) // V_HUDTRANS
 			alphalevel = 10 - st_translucency;
-		else if (alphalevel == 12)
+		else if (alphalevel == 12) // V_HUDTRANSDOUBLE
 			alphalevel = hudplusalpha[st_translucency];
 
 		if (alphalevel >= 10)
diff --git a/src/v_video.h b/src/v_video.h
index bcb39706ef26f6903a33d488242d4d23dce6f3ee..2831230a35d35bef057e37cc9436b3f9fec63414 100644
--- a/src/v_video.h
+++ b/src/v_video.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/version.h b/src/version.h
index 28fc71c36fca9dceea7f7aecc31f6436028b8d84..7a12fbbbe4638b5552510e23c09b6ba00c706b36 100644
--- a/src/version.h
+++ b/src/version.h
@@ -1,4 +1,4 @@
-#define SRB2VERSION "2.2.9"/* this must be the first line, for cmake !! */
+#define SRB2VERSION "2.2.10"/* this must be the first line, for cmake !! */
 
 // The Modification ID; must be obtained from a Master Server Admin ( https://mb.srb2.org/members/?key=ms_admin ).
 // DO NOT try to set this otherwise, or your modification will be unplayable through the Master Server.
@@ -9,7 +9,7 @@
 // it's only for detection of the version the player is using so the MS can alert them of an update.
 // Only set it higher, not lower, obviously.
 // Note that we use this to help keep internal testing in check; this is why v2.2.0 is not version "1".
-#define MODVERSION 50
+#define MODVERSION 51
 
-// Define this as a prerelease version suffix
-// #define BETAVERSION "RC1"
+// Define this as a prerelease version suffix (pre#, RC#)
+// #define BETAVERSION "pre1"
diff --git a/src/vid_copy.s b/src/vid_copy.s
index 6a37883565f57023f5687d8c31e2de56d72b288a..8e43e23c1786d51e615446fda767d2ba7a7a8f83 100644
--- a/src/vid_copy.s
+++ b/src/vid_copy.s
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/w_wad.c b/src/w_wad.c
index 9c631da658f5c3fc8edf8dd2dd1cca6d367b6001..0a8f630a804b2ea2fda9e83b902adb3af66295d1 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -1476,7 +1476,11 @@ lumpnum_t W_CheckNumForMap(const char *name)
 			{
 				p = wadfiles[i]->lumpinfo + lumpNum;
 				if (p->hash == hash && !strnicmp(name, p->name, 8))
-					return (i<<16) + lumpNum;
+				{
+					const char *extension = strrchr(p->fullname, '.');
+					if (!(extension && stricmp(extension, ".wad")))
+						return (i<<16) + lumpNum;
+				}
 			}
 		}
 	}
diff --git a/src/w_wad.h b/src/w_wad.h
index 9b8a66aa7cd66590a33df20759973806b110b8a8..c4de55d7774eef96e69470cc8b056d4124d52017 100644
--- a/src/w_wad.h
+++ b/src/w_wad.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/win32/Srb2win.rc b/src/win32/Srb2win.rc
index 0a280448b48b13d4dc5c09cc674e4213f87a9995..83948ac81978a8e77de4f127ad1dc37e6da1274a 100644
--- a/src/win32/Srb2win.rc
+++ b/src/win32/Srb2win.rc
@@ -76,8 +76,8 @@ END
 #include "../doomdef.h" // Needed for version string
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 2,2,9,0
- PRODUCTVERSION 2,2,9,0
+ FILEVERSION 2,2,10,0
+ PRODUCTVERSION 2,2,10,0
  FILEFLAGSMASK 0x3fL
 #ifdef _DEBUG
  FILEFLAGS 0x1L
@@ -97,7 +97,7 @@ BEGIN
             VALUE "FileDescription", "Sonic Robo Blast 2\0"
             VALUE "FileVersion", VERSIONSTRING_RC
             VALUE "InternalName", "srb2\0"
-            VALUE "LegalCopyright", "Copyright 1998-2021 by Sonic Team Junior\0"
+            VALUE "LegalCopyright", "Copyright 1998-2022 by Sonic Team Junior\0"
             VALUE "LegalTrademarks", "Sonic the Hedgehog and related characters are trademarks of Sega.\0"
             VALUE "OriginalFilename", "srb2win.exe\0"
             VALUE "PrivateBuild", "\0"
@@ -128,4 +128,3 @@ END
 
 /////////////////////////////////////////////////////////////////////////////
 #endif    // not APSTUDIO_INVOKED
-
diff --git a/src/y_inter.c b/src/y_inter.c
index 288a821e6f33209408ce708b1666bc93aabee93d..34e58494f1b9b9cc1c04d068d2db43b1cccf2c7d 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2004-2021 by Sonic Team Junior.
+// Copyright (C) 2004-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/y_inter.h b/src/y_inter.h
index 871142858ba1df05f0163b19a9cf9efeeafe560a..74183066e4541298d6ec8d5988829c9763a8e253 100644
--- a/src/y_inter.h
+++ b/src/y_inter.h
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2004-2021 by Sonic Team Junior.
+// Copyright (C) 2004-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/z_zone.c b/src/z_zone.c
index 34ff3d37ef3a025beaa925228d5c6a18c68f2228..b949730e35bbc2e6bd342753571e6e6a0f1759f9 100644
--- a/src/z_zone.c
+++ b/src/z_zone.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2006      by Graue.
-// Copyright (C) 2006-2021 by Sonic Team Junior.
+// Copyright (C) 2006-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/z_zone.h b/src/z_zone.h
index 17f572a905b56597bf955ee28b53814c6108bf04..d7e1ed52860389d886c3fb436c1b9474f9c584a1 100644
--- a/src/z_zone.h
+++ b/src/z_zone.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.