diff --git a/SRB2.cbp b/SRB2.cbp
index 88dc400fee58d949fe3f59eb968662e45702ffd1..2a1eb87b8565b3d2d9c13216c23d39dceecd6f92 100644
--- a/SRB2.cbp
+++ b/SRB2.cbp
@@ -1174,6 +1174,39 @@ HW3SOUND for 3D hardware sound  support
 			<Option target="Debug Mingw64/DirectX" />
 			<Option target="Release Mingw64/DirectX" />
 		</Unit>
+		<Unit filename="src/hardware/hw_clip.c">
+			<Option compilerVar="CC" />
+			<Option target="Debug Native/SDL" />
+			<Option target="Release Native/SDL" />
+			<Option target="Debug Mingw/SDL" />
+			<Option target="Release Mingw/SDL" />
+			<Option target="Debug Mingw/DirectX" />
+			<Option target="Release Mingw/DirectX" />
+			<Option target="Debug Any/Dummy" />
+			<Option target="Release Any/Dummy" />
+			<Option target="Debug Linux/SDL" />
+			<Option target="Release Linux/SDL" />
+			<Option target="Debug Mingw64/SDL" />
+			<Option target="Release Mingw64/SDL" />
+			<Option target="Debug Mingw64/DirectX" />
+			<Option target="Release Mingw64/DirectX" />
+		</Unit>
+		<Unit filename="src/hardware/hw_clip.h">
+			<Option target="Debug Native/SDL" />
+			<Option target="Release Native/SDL" />
+			<Option target="Debug Mingw/SDL" />
+			<Option target="Release Mingw/SDL" />
+			<Option target="Debug Mingw/DirectX" />
+			<Option target="Release Mingw/DirectX" />
+			<Option target="Debug Any/Dummy" />
+			<Option target="Release Any/Dummy" />
+			<Option target="Debug Linux/SDL" />
+			<Option target="Release Linux/SDL" />
+			<Option target="Debug Mingw64/SDL" />
+			<Option target="Release Mingw64/SDL" />
+			<Option target="Debug Mingw64/DirectX" />
+			<Option target="Release Mingw64/DirectX" />
+		</Unit>
 		<Unit filename="src/hardware/hw_data.h">
 			<Option target="Debug Native/SDL" />
 			<Option target="Release Native/SDL" />
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index da8438a59ef123836a88593f30c1438f2b69e4da..6a8b7e3f1228f2e7028a9f9cd324768c85182025 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -351,6 +351,7 @@ if(${SRB2_CONFIG_HWRENDER})
 	set(SRB2_HWRENDER_SOURCES
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_bsp.c
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_cache.c
+		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_clip.c
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_draw.c
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_light.c
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_main.c
@@ -359,6 +360,7 @@ if(${SRB2_CONFIG_HWRENDER})
 	)
 
 	set (SRB2_HWRENDER_HEADERS
+		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_clip.h
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_data.h
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_defs.h
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_dll.h
diff --git a/src/Makefile b/src/Makefile
index 426dc22898716a92ce7e6bc4330616939ad479a6..27569b36ca772f45fffc26b09625498e16a058d2 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -259,7 +259,7 @@ ifndef DC
 endif
 	OPTS+=-DHWRENDER
 	OBJS+=$(OBJDIR)/hw_bsp.o $(OBJDIR)/hw_draw.o $(OBJDIR)/hw_light.o \
-		 $(OBJDIR)/hw_main.o $(OBJDIR)/hw_md2.o $(OBJDIR)/hw_cache.o $(OBJDIR)/hw_trick.o
+		 $(OBJDIR)/hw_main.o $(OBJDIR)/hw_clip.o $(OBJDIR)/hw_md2.o $(OBJDIR)/hw_cache.o $(OBJDIR)/hw_trick.o
 endif
 
 ifdef NOHS
@@ -703,7 +703,7 @@ ifdef MINGW
 $(OBJDIR)/r_opengl.o: hardware/r_opengl/r_opengl.c hardware/r_opengl/r_opengl.h \
  doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_drv.h screen.h \
  command.h hardware/hw_data.h hardware/hw_glide.h hardware/hw_defs.h \
- hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h am_map.h \
+ hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h am_map.h \
  d_event.h d_player.h p_pspr.h m_fixed.h tables.h info.h d_think.h \
  p_mobj.h doomdata.h d_ticcmd.h r_defs.h hardware/hw_dll.h
 	$(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@
@@ -711,7 +711,7 @@ else
 $(OBJDIR)/r_opengl.o: hardware/r_opengl/r_opengl.c hardware/r_opengl/r_opengl.h \
  doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_drv.h screen.h \
  command.h hardware/hw_data.h hardware/hw_glide.h hardware/hw_defs.h \
- hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h am_map.h \
+ hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h am_map.h \
  d_event.h d_player.h p_pspr.h m_fixed.h tables.h info.h d_think.h \
  p_mobj.h doomdata.h d_ticcmd.h r_defs.h hardware/hw_dll.h
 	$(CC) $(CFLAGS) $(WFLAGS) -I/usr/X11R6/include -c $< -o $@
@@ -864,7 +864,7 @@ ifndef NOHW
 $(OBJDIR)/r_opengl.o: hardware/r_opengl/r_opengl.c hardware/r_opengl/r_opengl.h \
  doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_drv.h screen.h \
  command.h hardware/hw_data.h hardware/hw_glide.h hardware/hw_defs.h \
- hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h am_map.h \
+ hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h am_map.h \
  d_event.h d_player.h p_pspr.h m_fixed.h tables.h info.h d_think.h \
  p_mobj.h doomdata.h d_ticcmd.h r_defs.h hardware/hw_dll.h
 	$(CC) $(CFLAGS) $(WFLAGS) -D_WINDOWS -mwindows -c $< -o $@
@@ -872,7 +872,7 @@ $(OBJDIR)/r_opengl.o: hardware/r_opengl/r_opengl.c hardware/r_opengl/r_opengl.h
 $(OBJDIR)/ogl_win.o: hardware/r_opengl/ogl_win.c hardware/r_opengl/r_opengl.h \
  doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_drv.h screen.h \
  command.h hardware/hw_data.h hardware/hw_glide.h hardware/hw_defs.h \
- hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h am_map.h \
+ hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h am_map.h \
  d_event.h d_player.h p_pspr.h m_fixed.h tables.h info.h d_think.h \
  p_mobj.h doomdata.h d_ticcmd.h r_defs.h hardware/hw_dll.h
 	$(CC) $(CFLAGS) $(WFLAGS) -D_WINDOWS -mwindows -c $< -o $@
@@ -880,7 +880,7 @@ $(OBJDIR)/ogl_win.o: hardware/r_opengl/ogl_win.c hardware/r_opengl/r_opengl.h \
 $(OBJDIR)/r_minigl.o: hardware/r_minigl/r_minigl.c hardware/r_opengl/r_opengl.h \
  doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_drv.h screen.h \
  command.h hardware/hw_data.h hardware/hw_glide.h hardware/hw_defs.h \
- hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h am_map.h \
+ hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h am_map.h \
  d_event.h d_player.h p_pspr.h m_fixed.h tables.h info.h d_think.h \
  p_mobj.h doomdata.h d_ticcmd.h r_defs.h hardware/hw_dll.h
 	$(CC) $(CFLAGS) $(WFLAGS) -D_WINDOWS -mwindows -c $< -o $@
diff --git a/src/d_main.c b/src/d_main.c
index 02362f3458aa9bd202f706e901a950d4597f8ffd..7a8a85f255e96db1f7e4f5ad3c86963131df7325 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -419,10 +419,13 @@ static void D_Display(void)
 			}
 
 			// Image postprocessing effect
-			if (postimgtype)
-				V_DoPostProcessor(0, postimgtype, postimgparam);
-			if (postimgtype2)
-				V_DoPostProcessor(1, postimgtype2, postimgparam2);
+			if (rendermode == render_soft)
+			{
+				if (postimgtype)
+					V_DoPostProcessor(0, postimgtype, postimgparam);
+				if (postimgtype2)
+					V_DoPostProcessor(1, postimgtype2, postimgparam2);
+			}
 		}
 
 		if (lastdraw)
@@ -712,6 +715,7 @@ void D_StartTitle(void)
 	botskin = 0;
 	cv_debug = 0;
 	emeralds = 0;
+	lastmaploaded = 0;
 
 	// In case someone exits out at the same time they start a time attack run,
 	// reset modeattacking
diff --git a/src/dehacked.c b/src/dehacked.c
index 5019ad58d4a5f2a218bee8dd3e896d305ad188de..f85df62ac2b4d318e82a39d1ea159edab49ce2b2 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -1329,6 +1329,13 @@ static void readlevelheader(MYFILE *f, INT32 num)
 				else
 					mapheaderinfo[num-1]->levelflags &= ~LF_NOZONE;
 			}
+			else if (fastcmp(word, "SAVEGAME"))
+			{
+				if (i || word2[0] == 'T' || word2[0] == 'Y')
+					mapheaderinfo[num-1]->levelflags |= LF_SAVEGAME;
+				else
+					mapheaderinfo[num-1]->levelflags &= ~LF_SAVEGAME;
+			}
 
 			// Individual triggers for menu flags
 			else if (fastcmp(word, "HIDDEN"))
@@ -1776,7 +1783,6 @@ static actionpointer_t actionpointers[] =
 	{{A_CapeChase},            "A_CAPECHASE"},
 	{{A_RotateSpikeBall},      "A_ROTATESPIKEBALL"},
 	{{A_SlingAppear},          "A_SLINGAPPEAR"},
-	{{A_MaceRotate},           "A_MACEROTATE"},
 	{{A_UnidusBall},           "A_UNIDUSBALL"},
 	{{A_RockSpawn},            "A_ROCKSPAWN"},
 	{{A_SetFuse},              "A_SETFUSE"},
@@ -4951,18 +4957,62 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_SLING1",
 	"S_SLING2",
 
-	// CEZ Small Mace Chain
+	// CEZ maces and chains
 	"S_SMALLMACECHAIN",
-
-	// CEZ Big Mace Chain
 	"S_BIGMACECHAIN",
-
-	// CEZ Small Mace
 	"S_SMALLMACE",
-
-	// CEZ Big Mace
 	"S_BIGMACE",
 
+	// Yellow spring on a ball
+	"S_YELLOWSPRINGBALL",
+	"S_YELLOWSPRINGBALL2",
+	"S_YELLOWSPRINGBALL3",
+	"S_YELLOWSPRINGBALL4",
+	"S_YELLOWSPRINGBALL5",
+
+	// Red spring on a ball
+	"S_REDSPRINGBALL",
+	"S_REDSPRINGBALL2",
+	"S_REDSPRINGBALL3",
+	"S_REDSPRINGBALL4",
+	"S_REDSPRINGBALL5",
+
+	// Small Firebar
+	"S_SMALLFIREBAR1",
+	"S_SMALLFIREBAR2",
+	"S_SMALLFIREBAR3",
+	"S_SMALLFIREBAR4",
+	"S_SMALLFIREBAR5",
+	"S_SMALLFIREBAR6",
+	"S_SMALLFIREBAR7",
+	"S_SMALLFIREBAR8",
+	"S_SMALLFIREBAR9",
+	"S_SMALLFIREBAR10",
+	"S_SMALLFIREBAR11",
+	"S_SMALLFIREBAR12",
+	"S_SMALLFIREBAR13",
+	"S_SMALLFIREBAR14",
+	"S_SMALLFIREBAR15",
+	"S_SMALLFIREBAR16",
+
+	// Big Firebar
+	"S_BIGFIREBAR1",
+	"S_BIGFIREBAR2",
+	"S_BIGFIREBAR3",
+	"S_BIGFIREBAR4",
+	"S_BIGFIREBAR5",
+	"S_BIGFIREBAR6",
+	"S_BIGFIREBAR7",
+	"S_BIGFIREBAR8",
+	"S_BIGFIREBAR9",
+	"S_BIGFIREBAR10",
+	"S_BIGFIREBAR11",
+	"S_BIGFIREBAR12",
+	"S_BIGFIREBAR13",
+	"S_BIGFIREBAR14",
+	"S_BIGFIREBAR15",
+	"S_BIGFIREBAR16",
+
 	"S_CEZFLOWER1",
 
 	// Big Tumbleweed
@@ -6297,14 +6347,20 @@ static const char *const MOBJTYPE_LIST[] = {  // array length left dynamic for s
 	"MT_FLAMEPARTICLE",
 	"MT_EGGSTATUE", // Eggman Statue
 	"MT_MACEPOINT", // Mace rotation point
-	"MT_SWINGMACEPOINT", // Mace swinging point
-	"MT_HANGMACEPOINT", // Hangable mace chain
-	"MT_SPINMACEPOINT", // Spin/Controllable mace chain
+	"MT_CHAINMACEPOINT", // Combination of chains and maces point
+	"MT_SPRINGBALLPOINT", // Spring ball point
+	"MT_CHAINPOINT", // Mace chain
 	"MT_HIDDEN_SLING", // Spin mace chain (activatable)
+	"MT_FIREBARPOINT", // Firebar
+	"MT_CUSTOMMACEPOINT", // Custom mace
 	"MT_SMALLMACECHAIN", // Small Mace Chain
 	"MT_BIGMACECHAIN", // Big Mace Chain
 	"MT_SMALLMACE", // Small Mace
 	"MT_BIGMACE", // Big Mace
+	"MT_YELLOWSPRINGBALL", // Yellow spring on a ball
+	"MT_REDSPRINGBALL", // Red spring on a ball
+	"MT_SMALLFIREBAR", // Small Firebar
+	"MT_BIGFIREBAR", // Big Firebar
 	"MT_CEZFLOWER",
 
 	// Arid Canyon Scenery
@@ -6657,6 +6713,7 @@ static const char *const MOBJFLAG2_LIST[] = {
 	"AMBUSH",         // Alternate behaviour typically set by MTF_AMBUSH
 	"LINKDRAW",       // Draw vissprite of mobj immediately before/after tracer's vissprite (dependent on dispoffset and position)
 	"SHIELD",         // Thinker calls P_AddShield/P_ShieldLook (must be partnered with MF_SCENERY to use)
+	"MACEROTATE",     // Thinker calls P_MaceRotate around tracer
 	NULL
 };
 
@@ -7082,6 +7139,7 @@ struct {
 	{"LF_NOSSMUSIC",LF_NOSSMUSIC},
 	{"LF_NORELOAD",LF_NORELOAD},
 	{"LF_NOZONE",LF_NOZONE},
+	{"LF_SAVEGAME",LF_SAVEGAME},
 	// And map flags
 	{"LF2_HIDEINMENU",LF2_HIDEINMENU},
 	{"LF2_HIDEINSTATS",LF2_HIDEINSTATS},
@@ -7900,11 +7958,14 @@ void FUNCMATH DEH_Check(void)
 static inline int lib_freeslot(lua_State *L)
 {
 	int n = lua_gettop(L);
-  int r = 0; // args returned
+	int r = 0; // args returned
 	char *s, *type,*word;
 
-  while (n-- > 0)
-  {
+	if (!lua_lumploading)
+		return luaL_error(L, "This function cannot be called from within a hook or coroutine!");
+
+	while (n-- > 0)
+	{
 		s = Z_StrDup(luaL_checkstring(L,1));
 		type = strtok(s, "_");
 		if (type)
diff --git a/src/doomdef.h b/src/doomdef.h
index ec01d50c902a103674996abf2b0711fcdd87be38..ce503455f003a468fa6144657a34fa8574f706ef 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -584,6 +584,13 @@ extern const char *compdate, *comptime, *comprevision, *compbranch;
 /// Hudname padding.
 #define SKINNAMEPADDING
 
+/// FINALLY some real clipping that doesn't make walls dissappear AND speeds the game up
+/// (that was the original comment from SRB2CB, sadly it is a lie and actually slows game down)
+/// on the bright side it fixes some weird issues with translucent walls
+/// \note	SRB2CB port.
+///      	SRB2CB itself ported this from PrBoom+
+#define NEWCLIP
+
 /// Handle touching sector specials in P_PlayerAfterThink instead of P_PlayerThink.
 /// \note   Required for proper collision with moving sloped surfaces that have sector specials on them.
 //#define SECTORSPECIALSAFTERTHINK
diff --git a/src/doomstat.h b/src/doomstat.h
index a3b07c9cbf153d3d7e081a60a0afda2b91054592..a24bad79d73dc21dbd1b7b5b63728e4efb4862f0 100644
--- a/src/doomstat.h
+++ b/src/doomstat.h
@@ -41,7 +41,8 @@ extern INT16 maptol;
 extern UINT8 globalweather;
 extern INT32 curWeather;
 extern INT32 cursaveslot;
-extern INT16 lastmapsaved;
+//extern INT16 lastmapsaved;
+extern INT16 lastmaploaded;
 extern boolean gamecomplete;
 
 #define PRECIP_NONE  0
@@ -263,6 +264,7 @@ typedef struct
 #define LF_NOSSMUSIC      4 ///< Disable Super Sonic music
 #define LF_NORELOAD       8 ///< Don't reload level on death
 #define LF_NOZONE        16 ///< Don't include "ZONE" on level title
+#define LF_SAVEGAME      32 ///< Save the game upon loading this level
 
 #define LF2_HIDEINMENU     1 ///< Hide in the multiplayer menu
 #define LF2_HIDEINSTATS    2 ///< Hide in the statistics screen
diff --git a/src/g_game.c b/src/g_game.c
index 600c8c8964be2b1a7781818095cd26c39f2feea0..e996938ab30ac44cc2d655406e8008e46aaca748 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -77,7 +77,8 @@ INT16 maptol;
 UINT8 globalweather = 0;
 INT32 curWeather = PRECIP_NONE;
 INT32 cursaveslot = -1; // Auto-save 1p savegame slot
-INT16 lastmapsaved = 0; // Last map we auto-saved at
+//INT16 lastmapsaved = 0; // Last map we auto-saved at
+INT16 lastmaploaded = 0; // Last map the game loaded
 boolean gamecomplete = false;
 
 UINT16 mainwads = 0;
diff --git a/src/hardware/hw_bsp.c b/src/hardware/hw_bsp.c
index a32609fc80ebb1e0316f1c1f6d2dd5897a00c707..fa5bce308eff82bf52021c98bc19e1797ceb87ee 100644
--- a/src/hardware/hw_bsp.c
+++ b/src/hardware/hw_bsp.c
@@ -878,8 +878,8 @@ static void AdjustSegs(void)
 		count = subsectors[i].numlines;
 		lseg = &segs[subsectors[i].firstline];
 		p = extrasubsectors[i].planepoly;
-		if (!p)
-			continue;
+		//if (!p)
+			//continue;
 		for (; count--; lseg++)
 		{
 			float distv1,distv2,tmp;
@@ -892,29 +892,31 @@ static void AdjustSegs(void)
 				continue;
 #endif
 
-			for (j = 0; j < p->numpts; j++)
-			{
-				distv1 = p->pts[j].x - FIXED_TO_FLOAT(lseg->v1->x);
-				tmp    = p->pts[j].y - FIXED_TO_FLOAT(lseg->v1->y);
-				distv1 = distv1*distv1+tmp*tmp;
-				if (distv1 <= nearv1)
-				{
-					v1found = j;
-					nearv1 = distv1;
-				}
-				// the same with v2
-				distv2 = p->pts[j].x - FIXED_TO_FLOAT(lseg->v2->x);
-				tmp    = p->pts[j].y - FIXED_TO_FLOAT(lseg->v2->y);
-				distv2 = distv2*distv2+tmp*tmp;
-				if (distv2 <= nearv2)
+			if (p) {
+				for (j = 0; j < p->numpts; j++)
 				{
-					v2found = j;
-					nearv2 = distv2;
+					distv1 = p->pts[j].x - FIXED_TO_FLOAT(lseg->v1->x);
+					tmp    = p->pts[j].y - FIXED_TO_FLOAT(lseg->v1->y);
+					distv1 = distv1*distv1+tmp*tmp;
+					if (distv1 <= nearv1)
+					{
+						v1found = j;
+						nearv1 = distv1;
+					}
+					// the same with v2
+					distv2 = p->pts[j].x - FIXED_TO_FLOAT(lseg->v2->x);
+					tmp    = p->pts[j].y - FIXED_TO_FLOAT(lseg->v2->y);
+					distv2 = distv2*distv2+tmp*tmp;
+					if (distv2 <= nearv2)
+					{
+						v2found = j;
+						nearv2 = distv2;
+					}
 				}
 			}
-			if (nearv1 <= NEARDIST*NEARDIST)
+			if (p && nearv1 <= NEARDIST*NEARDIST)
 				// share vertice with segs
-				lseg->v1 = (vertex_t *)&(p->pts[v1found]);
+				lseg->pv1 = &(p->pts[v1found]);
 			else
 			{
 				// BP: here we can do better, using PointInSeg and compute
@@ -925,24 +927,24 @@ static void AdjustSegs(void)
 				polyvertex_t *pv = HWR_AllocVertex();
 				pv->x = FIXED_TO_FLOAT(lseg->v1->x);
 				pv->y = FIXED_TO_FLOAT(lseg->v1->y);
-				lseg->v1 = (vertex_t *)pv;
+				lseg->pv1 = pv;
 			}
-			if (nearv2 <= NEARDIST*NEARDIST)
-				lseg->v2 = (vertex_t *)&(p->pts[v2found]);
+			if (p && nearv2 <= NEARDIST*NEARDIST)
+				lseg->pv2 = &(p->pts[v2found]);
 			else
 			{
 				polyvertex_t *pv = HWR_AllocVertex();
 				pv->x = FIXED_TO_FLOAT(lseg->v2->x);
 				pv->y = FIXED_TO_FLOAT(lseg->v2->y);
-				lseg->v2 = (vertex_t *)pv;
+				lseg->pv2 = pv;
 			}
 
 			// recompute length
 			{
 				float x,y;
-				x = ((polyvertex_t *)lseg->v2)->x - ((polyvertex_t *)lseg->v1)->x
+				x = ((polyvertex_t *)lseg->pv2)->x - ((polyvertex_t *)lseg->pv1)->x
 					+ FIXED_TO_FLOAT(FRACUNIT/2);
-				y = ((polyvertex_t *)lseg->v2)->y - ((polyvertex_t *)lseg->v1)->y
+				y = ((polyvertex_t *)lseg->pv2)->y - ((polyvertex_t *)lseg->pv1)->y
 					+ FIXED_TO_FLOAT(FRACUNIT/2);
 				lseg->flength = (float)hypot(x, y);
 				// BP: debug see this kind of segs
diff --git a/src/hardware/hw_clip.c b/src/hardware/hw_clip.c
new file mode 100644
index 0000000000000000000000000000000000000000..8b01cabd5ed37696652b931548eed18de3aea344
--- /dev/null
+++ b/src/hardware/hw_clip.c
@@ -0,0 +1,465 @@
+/* Emacs style mode select   -*- C++ -*-
+ *-----------------------------------------------------------------------------
+ *
+ *
+ *  PrBoom: a Doom port merged with LxDoom and LSDLDoom
+ *  based on BOOM, a modified and improved DOOM engine
+ *  Copyright (C) 1999 by
+ *  id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
+ *  Copyright (C) 1999-2000 by
+ *  Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
+ *  Copyright 2005, 2006 by
+ *  Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * DESCRIPTION:
+ *
+ *---------------------------------------------------------------------
+ */
+
+/*
+ *
+ ** gl_clipper.cpp
+ **
+ ** Handles visibility checks.
+ ** Loosely based on the JDoom clipper.
+ **
+ **---------------------------------------------------------------------------
+ ** Copyright 2003 Tim Stump
+ ** All rights reserved.
+ **
+ ** Redistribution and use in source and binary forms, with or without
+ ** modification, are permitted provided that the following conditions
+ ** are met:
+ **
+ ** 1. Redistributions of source code must retain the above copyright
+ **    notice, this list of conditions and the following disclaimer.
+ ** 2. Redistributions in binary form must reproduce the above copyright
+ **    notice, this list of conditions and the following disclaimer in the
+ **    documentation and/or other materials provided with the distribution.
+ ** 3. The name of the author may not be used to endorse or promote products
+ **    derived from this software without specific prior written permission.
+ **
+ ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ **---------------------------------------------------------------------------
+ **
+ */
+
+#include <math.h>
+#include "../v_video.h"
+#include "hw_clip.h"
+#include "hw_glob.h"
+#include "../r_state.h"
+#include "../tables.h"
+#include "r_opengl/r_opengl.h"
+
+#ifdef HAVE_SPHEREFRUSTRUM
+static GLdouble viewMatrix[16];
+static GLdouble projMatrix[16];
+float frustum[6][4];
+#endif
+
+typedef struct clipnode_s
+	{
+		struct clipnode_s *prev, *next;
+		angle_t start, end;
+	} clipnode_t;
+
+clipnode_t *freelist;
+clipnode_t *clipnodes;
+clipnode_t *cliphead;
+
+static clipnode_t * gld_clipnode_GetNew(void);
+static clipnode_t * gld_clipnode_NewRange(angle_t start, angle_t end);
+static boolean gld_clipper_IsRangeVisible(angle_t startAngle, angle_t endAngle);
+static void gld_clipper_AddClipRange(angle_t start, angle_t end);
+static void gld_clipper_RemoveRange(clipnode_t * range);
+static void gld_clipnode_Free(clipnode_t *node);
+
+static clipnode_t * gld_clipnode_GetNew(void)
+{
+	if (freelist)
+	{
+		clipnode_t * p = freelist;
+		freelist = p->next;
+		return p;
+	}
+	else
+	{
+		return (clipnode_t*)malloc(sizeof(clipnode_t));
+	}
+}
+
+static clipnode_t * gld_clipnode_NewRange(angle_t start, angle_t end)
+{
+	clipnode_t * c = gld_clipnode_GetNew();
+	c->start = start;
+	c->end = end;
+	c->next = c->prev=NULL;
+	return c;
+}
+
+boolean gld_clipper_SafeCheckRange(angle_t startAngle, angle_t endAngle)
+{
+	if(startAngle > endAngle)
+	{
+		return (gld_clipper_IsRangeVisible(startAngle, ANGLE_MAX) || gld_clipper_IsRangeVisible(0, endAngle));
+	}
+
+	return gld_clipper_IsRangeVisible(startAngle, endAngle);
+}
+
+static boolean gld_clipper_IsRangeVisible(angle_t startAngle, angle_t endAngle)
+{
+	clipnode_t *ci;
+	ci = cliphead;
+
+	if (endAngle == 0 && ci && ci->start == 0)
+		return false;
+
+	while (ci != NULL && ci->start < endAngle)
+	{
+		if (startAngle >= ci->start && endAngle <= ci->end)
+		{
+			return false;
+		}
+		ci = ci->next;
+	}
+
+	return true;
+}
+
+static void gld_clipnode_Free(clipnode_t *node)
+{
+	node->next = freelist;
+	freelist = node;
+}
+
+static void gld_clipper_RemoveRange(clipnode_t *range)
+{
+	if (range == cliphead)
+	{
+		cliphead = cliphead->next;
+	}
+	else
+	{
+		if (range->prev)
+		{
+			range->prev->next = range->next;
+		}
+		if (range->next)
+		{
+			range->next->prev = range->prev;
+		}
+	}
+
+	gld_clipnode_Free(range);
+}
+
+void gld_clipper_SafeAddClipRange(angle_t startangle, angle_t endangle)
+{
+	if(startangle > endangle)
+	{
+		// The range has to added in two parts.
+		gld_clipper_AddClipRange(startangle, ANGLE_MAX);
+		gld_clipper_AddClipRange(0, endangle);
+	}
+	else
+	{
+		// Add the range as usual.
+		gld_clipper_AddClipRange(startangle, endangle);
+	}
+}
+
+static void gld_clipper_AddClipRange(angle_t start, angle_t end)
+{
+	clipnode_t *node, *temp, *prevNode, *node2, *delnode;
+
+	if (cliphead)
+	{
+		//check to see if range contains any old ranges
+		node = cliphead;
+		while (node != NULL && node->start < end)
+		{
+			if (node->start >= start && node->end <= end)
+			{
+				temp = node;
+				node = node->next;
+				gld_clipper_RemoveRange(temp);
+			}
+			else
+			{
+				if (node->start <= start && node->end >= end)
+				{
+					return;
+				}
+				else
+				{
+					node = node->next;
+				}
+			}
+		}
+
+		//check to see if range overlaps a range (or possibly 2)
+		node = cliphead;
+		while (node != NULL && node->start <= end)
+		{
+			if (node->end >= start)
+			{
+				// we found the first overlapping node
+				if (node->start > start)
+				{
+					// the new range overlaps with this node's start point
+					node->start = start;
+				}
+				if (node->end < end)
+				{
+					node->end = end;
+				}
+
+				node2 = node->next;
+				while (node2 && node2->start <= node->end)
+				{
+					if (node2->end > node->end)
+					{
+						node->end = node2->end;
+					}
+
+					delnode = node2;
+					node2 = node2->next;
+					gld_clipper_RemoveRange(delnode);
+				}
+				return;
+			}
+			node = node->next;
+		}
+
+		//just add range
+		node = cliphead;
+		prevNode = NULL;
+		temp = gld_clipnode_NewRange(start, end);
+		while (node != NULL && node->start < end)
+		{
+			prevNode = node;
+			node = node->next;
+		}
+		temp->next = node;
+		if (node == NULL)
+		{
+			temp->prev = prevNode;
+			if (prevNode)
+			{
+				prevNode->next = temp;
+			}
+			if (!cliphead)
+			{
+				cliphead = temp;
+			}
+		}
+		else
+		{
+			if (node == cliphead)
+			{
+				cliphead->prev = temp;
+				cliphead = temp;
+			}
+			else
+			{
+				temp->prev = prevNode;
+				prevNode->next = temp;
+				node->prev = temp;
+			}
+		}
+	}
+	else
+	{
+		temp = gld_clipnode_NewRange(start, end);
+		cliphead = temp;
+		return;
+	}
+}
+
+void gld_clipper_Clear(void)
+{
+	clipnode_t *node = cliphead;
+	clipnode_t *temp;
+
+	while (node != NULL)
+	{
+		temp = node;
+		node = node->next;
+		gld_clipnode_Free(temp);
+	}
+
+	cliphead = NULL;
+}
+
+#define RMUL (1.6f/1.333333f)
+
+angle_t gld_FrustumAngle(void)
+{
+	double floatangle;
+	angle_t a1;
+
+	float tilt = (float)fabs(((double)(int)aimingangle) / ANG1);
+
+	// NEWCLIP TODO: SRB2CBTODO: make a global render_fov for this function
+
+	float render_fov = FIXED_TO_FLOAT(cv_grfov.value);
+	float render_fovratio = (float)BASEVIDWIDTH / (float)BASEVIDHEIGHT; // SRB2CBTODO: NEWCLIPTODO: Is this right?
+	float render_multiplier = 64.0f / render_fovratio / RMUL;
+
+	if (tilt > 90.0f)
+	{
+		tilt = 90.0f;
+	}
+
+	// If the pitch is larger than this you can look all around at a FOV of 90
+	if (abs(aimingangle) > 46 * ANG1)
+		return 0xffffffff;
+
+	// ok, this is a gross hack that barely works...
+	// but at least it doesn't overestimate too much...
+	floatangle = 2.0f + (45.0f + (tilt / 1.9f)) * (float)render_fov * 48.0f / render_multiplier / 90.0f;
+	a1 = ANG1 * (int)floatangle;
+	if (a1 >= ANGLE_180)
+		return 0xffffffff;
+	return a1;
+}
+
+// SRB2CB I don't think used any of this stuff, let's disable for now since SRB2 probably doesn't want it either
+// compiler complains about (p)glGetDoublev anyway, in case anyone wants this
+// only r_opengl.c can use the base gl funcs as it turns out, that's a problem for whoever wants sphere frustum checks
+// btw to renable define HAVE_SPHEREFRUSTRUM in hw_clip.h
+#ifdef HAVE_SPHEREFRUSTRUM
+//
+// gld_FrustrumSetup
+//
+
+#define CALCMATRIX(a, b, c, d, e, f, g, h)\
+(float)(viewMatrix[a] * projMatrix[b] + \
+viewMatrix[c] * projMatrix[d] + \
+viewMatrix[e] * projMatrix[f] + \
+viewMatrix[g] * projMatrix[h])
+
+#define NORMALIZE_PLANE(i)\
+t = (float)sqrt(\
+frustum[i][0] * frustum[i][0] + \
+frustum[i][1] * frustum[i][1] + \
+frustum[i][2] * frustum[i][2]); \
+frustum[i][0] /= t; \
+frustum[i][1] /= t; \
+frustum[i][2] /= t; \
+frustum[i][3] /= t
+
+void gld_FrustrumSetup(void)
+{
+	float t;
+	float clip[16];
+
+	pglGetDoublev(GL_PROJECTION_MATRIX, projMatrix);
+	pglGetDoublev(GL_MODELVIEW_MATRIX, viewMatrix);
+
+	clip[0]  = CALCMATRIX(0, 0, 1, 4, 2, 8, 3, 12);
+	clip[1]  = CALCMATRIX(0, 1, 1, 5, 2, 9, 3, 13);
+	clip[2]  = CALCMATRIX(0, 2, 1, 6, 2, 10, 3, 14);
+	clip[3]  = CALCMATRIX(0, 3, 1, 7, 2, 11, 3, 15);
+
+	clip[4]  = CALCMATRIX(4, 0, 5, 4, 6, 8, 7, 12);
+	clip[5]  = CALCMATRIX(4, 1, 5, 5, 6, 9, 7, 13);
+	clip[6]  = CALCMATRIX(4, 2, 5, 6, 6, 10, 7, 14);
+	clip[7]  = CALCMATRIX(4, 3, 5, 7, 6, 11, 7, 15);
+
+	clip[8]  = CALCMATRIX(8, 0, 9, 4, 10, 8, 11, 12);
+	clip[9]  = CALCMATRIX(8, 1, 9, 5, 10, 9, 11, 13);
+	clip[10] = CALCMATRIX(8, 2, 9, 6, 10, 10, 11, 14);
+	clip[11] = CALCMATRIX(8, 3, 9, 7, 10, 11, 11, 15);
+
+	clip[12] = CALCMATRIX(12, 0, 13, 4, 14, 8, 15, 12);
+	clip[13] = CALCMATRIX(12, 1, 13, 5, 14, 9, 15, 13);
+	clip[14] = CALCMATRIX(12, 2, 13, 6, 14, 10, 15, 14);
+	clip[15] = CALCMATRIX(12, 3, 13, 7, 14, 11, 15, 15);
+
+	// Right plane
+	frustum[0][0] = clip[ 3] - clip[ 0];
+	frustum[0][1] = clip[ 7] - clip[ 4];
+	frustum[0][2] = clip[11] - clip[ 8];
+	frustum[0][3] = clip[15] - clip[12];
+	NORMALIZE_PLANE(0);
+
+	// Left plane
+	frustum[1][0] = clip[ 3] + clip[ 0];
+	frustum[1][1] = clip[ 7] + clip[ 4];
+	frustum[1][2] = clip[11] + clip[ 8];
+	frustum[1][3] = clip[15] + clip[12];
+	NORMALIZE_PLANE(1);
+
+	// Bottom plane
+	frustum[2][0] = clip[ 3] + clip[ 1];
+	frustum[2][1] = clip[ 7] + clip[ 5];
+	frustum[2][2] = clip[11] + clip[ 9];
+	frustum[2][3] = clip[15] + clip[13];
+	NORMALIZE_PLANE(2);
+
+	// Top plane
+	frustum[3][0] = clip[ 3] - clip[ 1];
+	frustum[3][1] = clip[ 7] - clip[ 5];
+	frustum[3][2] = clip[11] - clip[ 9];
+	frustum[3][3] = clip[15] - clip[13];
+	NORMALIZE_PLANE(3);
+
+	// Far plane
+	frustum[4][0] = clip[ 3] - clip[ 2];
+	frustum[4][1] = clip[ 7] - clip[ 6];
+	frustum[4][2] = clip[11] - clip[10];
+	frustum[4][3] = clip[15] - clip[14];
+	NORMALIZE_PLANE(4);
+
+	// Near plane
+	frustum[5][0] = clip[ 3] + clip[ 2];
+	frustum[5][1] = clip[ 7] + clip[ 6];
+	frustum[5][2] = clip[11] + clip[10];
+	frustum[5][3] = clip[15] + clip[14];
+	NORMALIZE_PLANE(5);
+}
+
+boolean gld_SphereInFrustum(float x, float y, float z, float radius)
+{
+	int p;
+
+	for (p = 0; p < 4; p++)
+	{
+		if (frustum[p][0] * x +
+			frustum[p][1] * y +
+			frustum[p][2] * z +
+			frustum[p][3] <= -radius)
+		{
+			return false;
+		}
+	}
+	return true;
+}
+#endif
diff --git a/src/hardware/hw_clip.h b/src/hardware/hw_clip.h
new file mode 100644
index 0000000000000000000000000000000000000000..3ba26e5e56f3dc053adc068078dc11859e8a86df
--- /dev/null
+++ b/src/hardware/hw_clip.h
@@ -0,0 +1,24 @@
+/*
+ *  hw_clip.h
+ *  SRB2CB
+ *
+ *  PrBoom's OpenGL clipping
+ *
+ *
+ */
+
+// OpenGL BSP clipping
+#include "../doomdef.h"
+#include "../tables.h"
+#include "../doomtype.h"
+
+//#define HAVE_SPHEREFRUSTRUM // enable if you want gld_SphereInFrustum and related code
+
+boolean gld_clipper_SafeCheckRange(angle_t startAngle, angle_t endAngle);
+void gld_clipper_SafeAddClipRange(angle_t startangle, angle_t endangle);
+void gld_clipper_Clear(void);
+angle_t gld_FrustumAngle(void);
+#ifdef HAVE_SPHEREFRUSTRUM
+void gld_FrustrumSetup(void);
+boolean gld_SphereInFrustum(float x, float y, float z, float radius);
+#endif
diff --git a/src/hardware/hw_light.c b/src/hardware/hw_light.c
index cf9b1c5368d44dc07ec55df33e77345aafe36729..267666749baf363ec02652fdb65f342a96f0d8f0 100644
--- a/src/hardware/hw_light.c
+++ b/src/hardware/hw_light.c
@@ -323,6 +323,10 @@ light_t *t_lspr[NUMSPRITES] =
 	&lspr[NOLIGHT],     // SPR_BMCH
 	&lspr[NOLIGHT],     // SPR_SMCE
 	&lspr[NOLIGHT],     // SPR_BMCE
+	&lspr[NOLIGHT],     // SPR_YSPB
+	&lspr[NOLIGHT],     // SPR_RSPB
+	&lspr[REDBALL_L],   // SPR_SFBR
+	&lspr[REDBALL_L],   // SPR_BFBR
 
 	// Arid Canyon Scenery
 	&lspr[NOLIGHT],     // SPR_BTBL
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 3bc071f5c02f514b694e4e001434d5f874da3c80..d74cd0587778e9181e63d75d6de7ce01ce0eafb9 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -44,6 +44,10 @@
 #endif
 #include "hw_md2.h"
 
+#ifdef NEWCLIP
+#include "hw_clip.h"
+#endif
+
 #define R_FAKEFLOORS
 #define HWPRECIP
 #define SORTING
@@ -99,8 +103,9 @@ CV_PossibleValue_t granisotropicmode_cons_t[] = {{1, "MIN"}, {16, "MAX"}, {0, NU
 boolean drawsky = true;
 
 // needs fix: walls are incorrectly clipped one column less
+#ifndef NEWCLIP
 static consvar_t cv_grclipwalls = {"gr_clipwalls", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
-
+#endif
 //development variables for diverse uses
 static consvar_t cv_gralpha = {"gr_alpha", "160", 0, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL};
 static consvar_t cv_grbeta = {"gr_beta", "0", 0, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL};
@@ -323,9 +328,6 @@ static angle_t gr_xtoviewangle[MAXVIDWIDTH+1];
 // test change fov when looking up/down but bsp projection messup :(
 //#define NOCRAPPYMLOOK
 
-/// \note crappy
-#define drawtextured true
-
 // base values set at SetViewSize
 static float gr_basecentery;
 
@@ -852,11 +854,11 @@ static void HWR_DrawSegsSplats(FSurfaceInfo * pSurf)
 
 	M_ClearBox(segbbox);
 	M_AddToBox(segbbox,
-		FLOAT_TO_FIXED(((polyvertex_t *)gr_curline->v1)->x),
-		FLOAT_TO_FIXED(((polyvertex_t *)gr_curline->v1)->y));
+		FLOAT_TO_FIXED(((polyvertex_t *)gr_curline->pv1)->x),
+		FLOAT_TO_FIXED(((polyvertex_t *)gr_curline->pv1)->y));
 	M_AddToBox(segbbox,
-		FLOAT_TO_FIXED(((polyvertex_t *)gr_curline->v2)->x),
-		FLOAT_TO_FIXED(((polyvertex_t *)gr_curline->v2)->y));
+		FLOAT_TO_FIXED(((polyvertex_t *)gr_curline->pv2)->x),
+		FLOAT_TO_FIXED(((polyvertex_t *)gr_curline->pv2)->y));
 
 	splat = (wallsplat_t *)gr_curline->linedef->splats;
 	for (; splat; splat = splat->next)
@@ -1029,6 +1031,7 @@ static void HWR_ProjectWall(wallVert3D   * wallVerts,
 // (in fact a clipping plane that has a constant, so can clip with simple 2d)
 // with the wall segment
 //
+#ifndef NEWCLIP
 static float HWR_ClipViewSegment(INT32 x, polyvertex_t *v1, polyvertex_t *v2)
 {
 	float num, den;
@@ -1057,6 +1060,7 @@ static float HWR_ClipViewSegment(INT32 x, polyvertex_t *v1, polyvertex_t *v2)
 
 	return num / den;
 }
+#endif
 
 //
 // HWR_SplitWall
@@ -1431,7 +1435,11 @@ static void HWR_DrawSkyWall(wallVert3D *wallVerts, FSurfaceInfo *Surf, fixed_t b
 // Anything between means the wall segment has been clipped with solidsegs,
 //  reducing wall overdraw to a minimum
 //
+#ifdef NEWCLIP
+static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
+#else
 static void HWR_StoreWallRange(double startfrac, double endfrac)
+#endif
 {
 	wallVert3D wallVerts[4];
 	v2d_t vs, ve; // start, end vertices of 2d line (view from above)
@@ -1456,16 +1464,18 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
 	extracolormap_t *colormap;
 	FSurfaceInfo Surf;
 
+#ifndef NEWCLIP
 	if (startfrac > endfrac)
 		return;
+#endif
 
 	gr_sidedef = gr_curline->sidedef;
 	gr_linedef = gr_curline->linedef;
 
-	vs.x = ((polyvertex_t *)gr_curline->v1)->x;
-	vs.y = ((polyvertex_t *)gr_curline->v1)->y;
-	ve.x = ((polyvertex_t *)gr_curline->v2)->x;
-	ve.y = ((polyvertex_t *)gr_curline->v2)->y;
+	vs.x = ((polyvertex_t *)gr_curline->pv1)->x;
+	vs.y = ((polyvertex_t *)gr_curline->pv1)->y;
+	ve.x = ((polyvertex_t *)gr_curline->pv2)->x;
+	ve.y = ((polyvertex_t *)gr_curline->pv2)->y;
 
 #ifdef ESLOPE
 	v1x = FLOAT_TO_FIXED(vs.x);
@@ -1473,44 +1483,21 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
 	v2x = FLOAT_TO_FIXED(ve.x);
 	v2y = FLOAT_TO_FIXED(ve.y);
 #endif
-
-	if (gr_frontsector->heightsec != -1)
-	{
 #ifdef ESLOPE
-		worldtop = worldtopslope = sectors[gr_frontsector->heightsec].ceilingheight;
-		worldbottom = worldbottomslope = sectors[gr_frontsector->heightsec].floorheight;
-#else
-		worldtop = sectors[gr_frontsector->heightsec].ceilingheight;
-		worldbottom = sectors[gr_frontsector->heightsec].floorheight;
-#endif
-	}
-	else
-	{
-#ifdef ESLOPE
-		if (gr_frontsector->c_slope)
-		{
-			worldtop      = P_GetZAt(gr_frontsector->c_slope, v1x, v1y);
-			worldtopslope = P_GetZAt(gr_frontsector->c_slope, v2x, v2y);
-		}
-		else
-		{
-			worldtop = worldtopslope = gr_frontsector->ceilingheight;
-		}
 
-		if (gr_frontsector->f_slope)
-		{
-			worldbottom      = P_GetZAt(gr_frontsector->f_slope, v1x, v1y);
-			worldbottomslope = P_GetZAt(gr_frontsector->f_slope, v2x, v2y);
-		}
-		else
-		{
-			worldbottom = worldbottomslope = gr_frontsector->floorheight;
-		}
+#define SLOPEPARAMS(slope, end1, end2, normalheight) \
+	if (slope) { \
+		end1 = P_GetZAt(slope, v1x, v1y); \
+		end2 = P_GetZAt(slope, v2x, v2y); \
+	} else \
+		end1 = end2 = normalheight;
+
+	SLOPEPARAMS(gr_frontsector->c_slope, worldtop,    worldtopslope,    gr_frontsector->ceilingheight)
+	SLOPEPARAMS(gr_frontsector->f_slope, worldbottom, worldbottomslope, gr_frontsector->floorheight)
 #else
-		worldtop    = gr_frontsector->ceilingheight;
-		worldbottom = gr_frontsector->floorheight;
+	worldtop    = gr_frontsector->ceilingheight;
+	worldbottom = gr_frontsector->floorheight;
 #endif
-	}
 
 	// remember vertices ordering
 	//  3--2
@@ -1525,20 +1512,23 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
 	wallVerts[2].z = wallVerts[1].z = ve.y;
 	wallVerts[0].w = wallVerts[1].w = wallVerts[2].w = wallVerts[3].w = 1.0f;
 
-	if (drawtextured)
 	{
 		// x offset the texture
 		fixed_t texturehpeg = gr_sidedef->textureoffset + gr_curline->offset;
 
+#ifndef NEWCLIP
 		// clip texture s start/end coords with solidsegs
 		if (startfrac > 0.0f && startfrac < 1.0f)
 			cliplow = (float)(texturehpeg + (gr_curline->flength*FRACUNIT) * startfrac);
 		else
+#endif
 			cliplow = (float)texturehpeg;
 
+#ifndef NEWCLIP
 		if (endfrac > 0.0f && endfrac < 1.0f)
 			cliphigh = (float)(texturehpeg + (gr_curline->flength*FRACUNIT) * endfrac);
 		else
+#endif
 			cliphigh = (float)(texturehpeg + (gr_curline->flength*FRACUNIT));
 	}
 
@@ -1554,43 +1544,15 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
 	{
 		INT32 gr_toptexture, gr_bottomtexture;
 		// two sided line
-		if (gr_backsector->heightsec != -1)
-		{
-#ifdef ESLOPE
-			worldhigh = worldhighslope = sectors[gr_backsector->heightsec].ceilingheight;
-			worldlow = worldlowslope = sectors[gr_backsector->heightsec].floorheight;
-#else
-			worldhigh = sectors[gr_backsector->heightsec].ceilingheight;
-			worldlow = sectors[gr_backsector->heightsec].floorheight;
-#endif
-		}
-		else
-		{
-#ifdef ESLOPE
-			if (gr_backsector->c_slope)
-			{
-				worldhigh      = P_GetZAt(gr_backsector->c_slope, v1x, v1y);
-				worldhighslope = P_GetZAt(gr_backsector->c_slope, v2x, v2y);
-			}
-			else
-			{
-				worldhigh = worldhighslope = gr_backsector->ceilingheight;
-			}
 
-			if (gr_backsector->f_slope)
-			{
-				worldlow      = P_GetZAt(gr_backsector->f_slope, v1x, v1y);
-				worldlowslope = P_GetZAt(gr_backsector->f_slope, v2x, v2y);
-			}
-			else
-			{
-				worldlow = worldlowslope = gr_backsector->floorheight;
-			}
+#ifdef ESLOPE
+		SLOPEPARAMS(gr_backsector->c_slope, worldhigh, worldhighslope, gr_backsector->ceilingheight)
+		SLOPEPARAMS(gr_backsector->f_slope, worldlow,  worldlowslope,  gr_backsector->floorheight)
+#undef SLOPEPARAMS
 #else
-			worldhigh = gr_backsector->ceilingheight;
-			worldlow  = gr_backsector->floorheight;
+		worldhigh = gr_backsector->ceilingheight;
+		worldlow  = gr_backsector->floorheight;
 #endif
-		}
 
 		// hack to allow height changes in outdoor areas
 		// This is what gets rid of the upper textures if there should be sky
@@ -1614,7 +1576,6 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
             worldhigh < worldtop
             ) && gr_toptexture)
 		{
-			if (drawtextured)
 			{
 				fixed_t texturevpegtop; // top
 
@@ -1695,7 +1656,6 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
 #endif
             worldlow > worldbottom) && gr_bottomtexture) //only if VISIBLE!!!
 		{
-			if (drawtextured)
 			{
 				fixed_t texturevpegbottom = 0; // bottom
 
@@ -1887,7 +1847,6 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
 			h = min(highcut, polytop);
 			l = max(polybottom, lowcut);
 
-			if (drawtextured)
 			{
 				// PEGGING
 #ifdef ESLOPE
@@ -1943,7 +1902,6 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
 				h = min(highcut, polytop);
 				l = max(polybottom, lowcut);
 
-				if (drawtextured)
 				{
 					// PEGGING
 					if (!!(gr_linedef->flags & ML_DONTPEGBOTTOM) ^ !!(gr_linedef->flags & ML_EFFECT3))
@@ -2135,7 +2093,6 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
 		gr_midtexture = R_GetTextureNum(gr_sidedef->midtexture);
 		if (gr_midtexture)
 		{
-			if (drawtextured)
 			{
 				fixed_t     texturevpeg;
 				// PEGGING
@@ -2276,7 +2233,7 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
 					wallVerts[0].s = wallVerts[3].s = 0;
 					wallVerts[2].s = wallVerts[1].s = 0;
 				}
-				else if (drawtextured)
+				else
 				{
 #ifdef ESLOPE // P.S. this is better-organized than the old version
 					fixed_t offs = sides[(newline ? newline : rover->master)->sidenum[0]].rowoffset;
@@ -2409,7 +2366,7 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
 					wallVerts[0].s = wallVerts[3].s = 0;
 					wallVerts[2].s = wallVerts[1].s = 0;
 				}
-				else if (drawtextured)
+				else
 				{
 					grTex = HWR_GetTexture(texnum);
 
@@ -2482,6 +2439,110 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
 //Hurdler: end of 3d-floors test
 }
 
+// From PrBoom:
+//
+// e6y: Check whether the player can look beyond this line
+//
+#ifdef NEWCLIP
+boolean checkforemptylines = true;
+// Don't modify anything here, just check
+// Kalaron: Modified for sloped linedefs
+static boolean CheckClip(seg_t * seg, sector_t * afrontsector, sector_t * abacksector)
+{
+	fixed_t frontf1,frontf2, frontc1, frontc2; // front floor/ceiling ends
+	fixed_t backf1, backf2, backc1, backc2; // back floor ceiling ends
+
+	// GZDoom method of sloped line clipping
+
+#ifdef ESLOPE
+	if (afrontsector->f_slope || afrontsector->c_slope || abacksector->f_slope || abacksector->c_slope)
+	{
+		fixed_t v1x, v1y, v2x, v2y; // the seg's vertexes as fixed_t
+		v1x = FLOAT_TO_FIXED(((polyvertex_t *)gr_curline->pv1)->x);
+		v1y = FLOAT_TO_FIXED(((polyvertex_t *)gr_curline->pv1)->y);
+		v2x = FLOAT_TO_FIXED(((polyvertex_t *)gr_curline->pv2)->x);
+		v2y = FLOAT_TO_FIXED(((polyvertex_t *)gr_curline->pv2)->y);
+#define SLOPEPARAMS(slope, end1, end2, normalheight) \
+		if (slope) { \
+			end1 = P_GetZAt(slope, v1x, v1y); \
+			end2 = P_GetZAt(slope, v2x, v2y); \
+		} else \
+			end1 = end2 = normalheight;
+
+		SLOPEPARAMS(afrontsector->f_slope, frontf1, frontf2, afrontsector->floorheight)
+		SLOPEPARAMS(afrontsector->c_slope, frontc1, frontc2, afrontsector->ceilingheight)
+		SLOPEPARAMS( abacksector->f_slope, backf1,  backf2,  abacksector->floorheight)
+		SLOPEPARAMS( abacksector->c_slope, backc1,  backc2,  abacksector->ceilingheight)
+#undef SLOPEPARAMS
+	}
+	else
+#endif
+	{
+		frontf1 = frontf2 = afrontsector->floorheight;
+		frontc1 = frontc2 = afrontsector->ceilingheight;
+		backf1 = backf2 = abacksector->floorheight;
+		backc1 = backc2 = abacksector->ceilingheight;
+	}
+
+	// now check for closed sectors!
+	if (backc1 <= frontf1 && backc2 <= frontf2)
+	{
+		checkforemptylines = false;
+		if (!seg->sidedef->toptexture)
+			return false;
+
+		if (abacksector->ceilingpic == skyflatnum && afrontsector->ceilingpic == skyflatnum)
+			return false;
+
+		return true;
+	}
+
+	if (backf1 >= frontc1 && backf2 >= frontc2)
+	{
+		checkforemptylines = false;
+		if (!seg->sidedef->bottomtexture)
+			return false;
+
+		// properly render skies (consider door "open" if both floors are sky):
+		if (abacksector->ceilingpic == skyflatnum && afrontsector->ceilingpic == skyflatnum)
+			return false;
+
+		return true;
+	}
+
+	if (backc1 <= backf1 && backc2 <= backf2)
+	{
+		checkforemptylines = false;
+		// preserve a kind of transparent door/lift special effect:
+		if (backc1 < frontc1 || backc2 < frontc2)
+		{
+			if (!seg->sidedef->toptexture)
+				return false;
+		}
+		if (backf1 > frontf1 || backf2 > frontf2)
+		{
+			if (!seg->sidedef->bottomtexture)
+				return false;
+		}
+		if (abacksector->ceilingpic == skyflatnum && afrontsector->ceilingpic == skyflatnum)
+			return false;
+
+		if (abacksector->floorpic == skyflatnum && afrontsector->floorpic == skyflatnum)
+			return false;
+
+		return true;
+	}
+
+	if (backc1 != frontc1 || backc2 != frontc2
+		|| backf1 != frontf1 || backf2 != frontf2)
+		{
+			checkforemptylines = false;
+			return false;
+		}
+
+	return false;
+}
+#else
 //Hurdler: just like in r_bsp.c
 #if 1
 #define MAXSEGS         MAXVIDWIDTH/2+1
@@ -2553,7 +2614,7 @@ static void HWR_ClipSolidWallSegment(INT32 first, INT32 last)
 		}
 		else
 		{
-			highfrac = HWR_ClipViewSegment(start->first+1, (polyvertex_t *)gr_curline->v1, (polyvertex_t *)gr_curline->v2);
+			highfrac = HWR_ClipViewSegment(start->first+1, (polyvertex_t *)gr_curline->pv1, (polyvertex_t *)gr_curline->pv2);
 			HWR_StoreWallRange(0, highfrac);
 		}
 		// Now adjust the clip size.
@@ -2577,8 +2638,8 @@ static void HWR_ClipSolidWallSegment(INT32 first, INT32 last)
 		}
 		else
 		{
-			lowfrac  = HWR_ClipViewSegment(next->last-1, (polyvertex_t *)gr_curline->v1, (polyvertex_t *)gr_curline->v2);
-			highfrac = HWR_ClipViewSegment((next+1)->first+1, (polyvertex_t *)gr_curline->v1, (polyvertex_t *)gr_curline->v2);
+			lowfrac  = HWR_ClipViewSegment(next->last-1, (polyvertex_t *)gr_curline->pv1, (polyvertex_t *)gr_curline->pv2);
+			highfrac = HWR_ClipViewSegment((next+1)->first+1, (polyvertex_t *)gr_curline->pv1, (polyvertex_t *)gr_curline->pv2);
 			HWR_StoreWallRange(lowfrac, highfrac);
 		}
 		next++;
@@ -2612,7 +2673,7 @@ static void HWR_ClipSolidWallSegment(INT32 first, INT32 last)
 		}
 		else
 		{
-			lowfrac  = HWR_ClipViewSegment(next->last-1, (polyvertex_t *)gr_curline->v1, (polyvertex_t *)gr_curline->v2);
+			lowfrac  = HWR_ClipViewSegment(next->last-1, (polyvertex_t *)gr_curline->pv1, (polyvertex_t *)gr_curline->pv2);
 			HWR_StoreWallRange(lowfrac, 1);
 		}
 	}
@@ -2675,8 +2736,8 @@ static void HWR_ClipPassWallSegment(INT32 first, INT32 last)
 		else
 		{
 			highfrac = HWR_ClipViewSegment(min(start->first + 1,
-				start->last), (polyvertex_t *)gr_curline->v1,
-				(polyvertex_t *)gr_curline->v2);
+				start->last), (polyvertex_t *)gr_curline->pv1,
+				(polyvertex_t *)gr_curline->pv2);
 			HWR_StoreWallRange(0, highfrac);
 		}
 	}
@@ -2695,8 +2756,8 @@ static void HWR_ClipPassWallSegment(INT32 first, INT32 last)
 		}
 		else
 		{
-			lowfrac  = HWR_ClipViewSegment(max(start->last-1,start->first), (polyvertex_t *)gr_curline->v1, (polyvertex_t *)gr_curline->v2);
-			highfrac = HWR_ClipViewSegment(min((start+1)->first+1,(start+1)->last), (polyvertex_t *)gr_curline->v1, (polyvertex_t *)gr_curline->v2);
+			lowfrac  = HWR_ClipViewSegment(max(start->last-1,start->first), (polyvertex_t *)gr_curline->pv1, (polyvertex_t *)gr_curline->pv2);
+			highfrac = HWR_ClipViewSegment(min((start+1)->first+1,(start+1)->last), (polyvertex_t *)gr_curline->pv1, (polyvertex_t *)gr_curline->pv2);
 			HWR_StoreWallRange(lowfrac, highfrac);
 		}
 		start++;
@@ -2726,8 +2787,8 @@ static void HWR_ClipPassWallSegment(INT32 first, INT32 last)
 		else
 		{
 			lowfrac = HWR_ClipViewSegment(max(start->last - 1,
-				start->first), (polyvertex_t *)gr_curline->v1,
-				(polyvertex_t *)gr_curline->v2);
+				start->first), (polyvertex_t *)gr_curline->pv1,
+				(polyvertex_t *)gr_curline->pv2);
 			HWR_StoreWallRange(lowfrac, 1);
 		}
 	}
@@ -2767,6 +2828,7 @@ static void HWR_ClearClipSegs(void)
 	gr_solidsegs[1].last = 0x7fffffff;
 	hw_newend = gr_solidsegs+2;
 }
+#endif // NEWCLIP
 
 // -----------------+
 // HWR_AddLine      : Clips the given segment and adds any visible pieces to the line list.
@@ -2775,24 +2837,46 @@ static void HWR_ClearClipSegs(void)
 // -----------------+
 static void HWR_AddLine(seg_t * line)
 {
-	INT32 x1, x2;
 	angle_t angle1, angle2;
+#ifndef NEWCLIP
+	INT32 x1, x2;
 	angle_t span, tspan;
+#endif
 
 	// SoM: Backsector needs to be run through R_FakeFlat
 	sector_t tempsec;
 
+	fixed_t v1x, v1y, v2x, v2y; // the seg's vertexes as fixed_t
+#ifdef POLYOBJECTS
 	if (line->polyseg && !(line->polyseg->flags & POF_RENDERSIDES))
 		return;
+#endif
 
 	gr_curline = line;
 
+	v1x = FLOAT_TO_FIXED(((polyvertex_t *)gr_curline->pv1)->x);
+	v1y = FLOAT_TO_FIXED(((polyvertex_t *)gr_curline->pv1)->y);
+	v2x = FLOAT_TO_FIXED(((polyvertex_t *)gr_curline->pv2)->x);
+	v2y = FLOAT_TO_FIXED(((polyvertex_t *)gr_curline->pv2)->y);
+
 	// OPTIMIZE: quickly reject orthogonal back sides.
-	angle1 = R_PointToAngle(FLOAT_TO_FIXED(((polyvertex_t *)gr_curline->v1)->x),
-	                        FLOAT_TO_FIXED(((polyvertex_t *)gr_curline->v1)->y));
-	angle2 = R_PointToAngle(FLOAT_TO_FIXED(((polyvertex_t *)gr_curline->v2)->x),
-	                        FLOAT_TO_FIXED(((polyvertex_t *)gr_curline->v2)->y));
+	angle1 = R_PointToAngle(v1x, v1y);
+	angle2 = R_PointToAngle(v2x, v2y);
+
+#ifdef NEWCLIP
+	 // PrBoom: Back side, i.e. backface culling - read: endAngle >= startAngle!
+	if (angle2 - angle1 < ANGLE_180)
+		return;
+
+	// PrBoom: use REAL clipping math YAYYYYYYY!!!
+
+	if (!gld_clipper_SafeCheckRange(angle2, angle1))
+    {
+		return;
+    }
 
+	checkforemptylines = true;
+#else
 	// Clip to view edges.
 	span = angle1 - angle2;
 
@@ -2833,8 +2917,8 @@ static void HWR_AddLine(seg_t * line)
 		float fx1,fx2,fy1,fy2;
 		//BP: test with a better projection than viewangletox[R_PointToAngle(angle)]
 		// do not enable this at release 4 mul and 2 div
-		fx1 = ((polyvertex_t *)(line->v1))->x-gr_viewx;
-		fy1 = ((polyvertex_t *)(line->v1))->y-gr_viewy;
+		fx1 = ((polyvertex_t *)(line->pv1))->x-gr_viewx;
+		fy1 = ((polyvertex_t *)(line->pv1))->y-gr_viewy;
 		fy2 = (fx1 * gr_viewcos + fy1 * gr_viewsin);
 		if (fy2 < 0)
 			// the point is back
@@ -2842,8 +2926,8 @@ static void HWR_AddLine(seg_t * line)
 		else
 			fx1 = gr_windowcenterx + (fx1 * gr_viewsin - fy1 * gr_viewcos) * gr_centerx / fy2;
 
-		fx2 = ((polyvertex_t *)(line->v2))->x-gr_viewx;
-		fy2 = ((polyvertex_t *)(line->v2))->y-gr_viewy;
+		fx2 = ((polyvertex_t *)(line->pv2))->x-gr_viewx;
+		fy2 = ((polyvertex_t *)(line->pv2))->y-gr_viewy;
 		fy1 = (fx2 * gr_viewcos + fy2 * gr_viewsin);
 		if (fy1 < 0)
 			// the point is back
@@ -2871,8 +2955,34 @@ static void HWR_AddLine(seg_t * line)
 		return;
 	}
 */
+#endif
+
 	gr_backsector = line->backsector;
 
+#ifdef NEWCLIP
+	if (!line->backsector)
+    {
+		gld_clipper_SafeAddClipRange(angle2, angle1);
+    }
+    else
+    {
+		gr_backsector = R_FakeFlat(gr_backsector, &tempsec, NULL, NULL, true);
+		if (CheckClip(line, gr_frontsector, gr_backsector))
+		{
+			gld_clipper_SafeAddClipRange(angle2, angle1);
+			checkforemptylines = false;
+		}
+		// Reject empty lines used for triggers and special events.
+		// Identical floor and ceiling on both sides,
+		//  identical light levels on both sides,
+		//  and no middle texture.
+		if (checkforemptylines && R_IsEmptyLine(line, gr_frontsector, gr_backsector))
+			return;
+    }
+
+	HWR_ProcessSeg(); // Doesn't need arguments because they're defined globally :D
+	return;
+#else
 	// Single sided line?
 	if (!gr_backsector)
 		goto clipsolid;
@@ -2882,14 +2992,9 @@ static void HWR_AddLine(seg_t * line)
 #ifdef ESLOPE
 	if (gr_frontsector->f_slope || gr_frontsector->c_slope || gr_backsector->f_slope || gr_backsector->c_slope)
 	{
-		fixed_t v1x, v1y, v2x, v2y; // the seg's vertexes as fixed_t
 		fixed_t frontf1,frontf2, frontc1, frontc2; // front floor/ceiling ends
 		fixed_t backf1, backf2, backc1, backc2; // back floor ceiling ends
 
-		v1x = FLOAT_TO_FIXED(((polyvertex_t *)gr_curline->v1)->x);
-		v1y = FLOAT_TO_FIXED(((polyvertex_t *)gr_curline->v1)->y);
-		v2x = FLOAT_TO_FIXED(((polyvertex_t *)gr_curline->v2)->x);
-		v2y = FLOAT_TO_FIXED(((polyvertex_t *)gr_curline->v2)->y);
 #define SLOPEPARAMS(slope, end1, end2, normalheight) \
 		if (slope) { \
 			end1 = P_GetZAt(slope, v1x, v1y); \
@@ -2910,6 +3015,13 @@ static void HWR_AddLine(seg_t * line)
 			goto clipsolid;
 		}
 
+		// Check for automap fix.
+		if (backc1 <= backf1 && backc2 <= backf2
+		&& ((backc1 >= frontc1 && backc2 >= frontc2) || gr_curline->sidedef->toptexture)
+		&& ((backf1 <= frontf1 && backf2 >= frontf2) || gr_curline->sidedef->bottomtexture)
+		&& (gr_backsector->ceilingpic != skyflatnum || gr_frontsector->ceilingpic != skyflatnum))
+			goto clipsolid;
+
 		// Window.
 		if (backc1 != frontc1 || backc2 != frontc2
 			|| backf1 != frontf1 || backf2 != frontf2)
@@ -2925,6 +3037,13 @@ static void HWR_AddLine(seg_t * line)
 			gr_backsector->floorheight >= gr_frontsector->ceilingheight)
 			goto clipsolid;
 
+		// Check for automap fix.
+		if (gr_backsector->ceilingheight <= gr_backsector->floorheight
+		&& ((gr_backsector->ceilingheight >= gr_frontsector->ceilingheight) || gr_curline->sidedef->toptexture)
+		&& ((gr_backsector->floorheight <= gr_backsector->floorheight) || gr_curline->sidedef->bottomtexture)
+		&& (gr_backsector->ceilingpic != skyflatnum || gr_frontsector->ceilingpic != skyflatnum))
+			goto clipsolid;
+
 		// Window.
 		if (gr_backsector->ceilingheight != gr_frontsector->ceilingheight ||
 			gr_backsector->floorheight != gr_frontsector->floorheight)
@@ -2935,25 +3054,8 @@ static void HWR_AddLine(seg_t * line)
 	// Identical floor and ceiling on both sides,
 	//  identical light levels on both sides,
 	//  and no middle texture.
-	if (
-#ifdef POLYOBJECTS
-		!line->polyseg &&
-#endif
-		gr_backsector->ceilingpic == gr_frontsector->ceilingpic
-		&& gr_backsector->floorpic == gr_frontsector->floorpic
-#ifdef ESLOPE
-		&& gr_backsector->f_slope == gr_frontsector->f_slope
-		&& gr_backsector->c_slope == gr_frontsector->c_slope
-#endif
-	    && gr_backsector->lightlevel == gr_frontsector->lightlevel
-		&& gr_curline->sidedef->midtexture == 0
-		&& !gr_backsector->ffloors && !gr_frontsector->ffloors)
-		// SoM: For 3D sides... Boris, would you like to take a
-		// crack at rendering 3D sides? You would need to add the
-		// above check and add code to HWR_StoreWallRange...
-	{
+	if (R_IsEmptyLine(gr_curline, gr_frontsector, gr_backsector))
 		return;
-	}
 
 clippass:
 	if (x1 == x2)
@@ -2965,6 +3067,7 @@ clipsolid:
 	if (x1 == x2)
 		goto clippass;
 	HWR_ClipSolidWallSegment(x1, x2-1);
+#endif
 }
 
 // HWR_CheckBBox
@@ -2976,9 +3079,13 @@ clipsolid:
 
 static boolean HWR_CheckBBox(fixed_t *bspcoord)
 {
-	INT32 boxpos, sx1, sx2;
+	INT32 boxpos;
 	fixed_t px1, py1, px2, py2;
-	angle_t angle1, angle2, span, tspan;
+	angle_t angle1, angle2;
+#ifndef NEWCLIP
+	INT32 sx1, sx2;
+	angle_t span, tspan;
+#endif
 
 	// Find the corners of the box
 	// that define the edges from current viewpoint.
@@ -3004,6 +3111,11 @@ static boolean HWR_CheckBBox(fixed_t *bspcoord)
 	px2 = bspcoord[checkcoord[boxpos][2]];
 	py2 = bspcoord[checkcoord[boxpos][3]];
 
+#ifdef NEWCLIP
+	angle1 = R_PointToAngle(px1, py1);
+	angle2 = R_PointToAngle(px2, py2);
+	return gld_clipper_SafeCheckRange(angle2, angle1);
+#else
 	// check clip list for an open space
 	angle1 = R_PointToAngle(px1, py1) - dup_viewangle;
 	angle2 = R_PointToAngle(px2, py2) - dup_viewangle;
@@ -3051,6 +3163,7 @@ static boolean HWR_CheckBBox(fixed_t *bspcoord)
 		return false;
 
 	return HWR_ClipToSolidSegs(sx1, sx2 - 1);
+#endif
 }
 
 #ifdef POLYOBJECTS
@@ -3084,8 +3197,8 @@ static inline void HWR_AddPolyObjectSegs(void)
 			pv2->x = FIXED_TO_FLOAT(gr_fakeline->v2->x);
 			pv2->y = FIXED_TO_FLOAT(gr_fakeline->v2->y);
 
-			gr_fakeline->v1 = (vertex_t *)pv1;
-			gr_fakeline->v2 = (vertex_t *)pv2;
+			gr_fakeline->pv1 = pv1;
+			gr_fakeline->pv2 = pv2;
 
 			HWR_AddLine(gr_fakeline);
 		}
@@ -5630,7 +5743,19 @@ if (0)
 #ifdef SORTING
 	drawcount = 0;
 #endif
+#ifdef NEWCLIP
+	if (rendermode == render_opengl)
+	{
+		angle_t a1 = gld_FrustumAngle();
+		gld_clipper_Clear();
+		gld_clipper_SafeAddClipRange(viewangle + a1, viewangle - a1);
+#ifdef HAVE_SPHEREFRUSTRUM
+		gld_FrustrumSetup();
+#endif
+	}
+#else
 	HWR_ClearClipSegs();
+#endif
 
 	//04/01/2000: Hurdler: added for T&L
 	//                     Actually it only works on Walls and Planes
@@ -5640,6 +5765,7 @@ if (0)
 
 	HWR_RenderBSPNode((INT32)numnodes-1);
 
+#ifndef NEWCLIP
 	// Make a viewangle int so we can render things based on mouselook
 	if (player == &players[consoleplayer])
 		viewangle = localaiming;
@@ -5666,6 +5792,7 @@ if (0)
 
 		dup_viewangle += ANGLE_90;
 	}
+#endif
 
 	// Check for new console commands.
 	NetUpdate();
@@ -5860,7 +5987,19 @@ if (0)
 #ifdef SORTING
 	drawcount = 0;
 #endif
+#ifdef NEWCLIP
+	if (rendermode == render_opengl)
+	{
+		angle_t a1 = gld_FrustumAngle();
+		gld_clipper_Clear();
+		gld_clipper_SafeAddClipRange(viewangle + a1, viewangle - a1);
+#ifdef HAVE_SPHEREFRUSTRUM
+		gld_FrustrumSetup();
+#endif
+	}
+#else
 	HWR_ClearClipSegs();
+#endif
 
 	//04/01/2000: Hurdler: added for T&L
 	//                     Actually it only works on Walls and Planes
@@ -5870,6 +6009,7 @@ if (0)
 
 	HWR_RenderBSPNode((INT32)numnodes-1);
 
+#ifndef NEWCLIP
 	// Make a viewangle int so we can render things based on mouselook
 	if (player == &players[consoleplayer])
 		viewangle = localaiming;
@@ -5896,6 +6036,7 @@ if (0)
 
 		dup_viewangle += ANGLE_90;
 	}
+#endif
 
 	// Check for new console commands.
 	NetUpdate();
@@ -6041,7 +6182,9 @@ static inline void HWR_AddEngineCommands(void)
 {
 	// engine state variables
 	//CV_RegisterVar(&cv_grzbuffer);
+#ifndef NEWCLIP
 	CV_RegisterVar(&cv_grclipwalls);
+#endif
 
 	// engine development mode variables
 	// - usage may vary from version to version..
diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index c7d35e9134be1a4c09809669778db14cdf670118..df2c9f59a60b675f5aacfdce1be23a30064af3bd 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -298,8 +298,8 @@ static md2_model_t *md2_readModel(const char *filename)
 	// initialize model and read header
 
 	if (fread(&model->header, sizeof (model->header), 1, file) != 1
-		|| model->header.magic !=
-		(INT32)(('2' << 24) + ('P' << 16) + ('D' << 8) + 'I'))
+		|| model->header.magic != MD2_IDENT
+		|| model->header.version != MD2_VERSION)
 	{
 		fclose(file);
 		free(model);
@@ -313,6 +313,7 @@ static md2_model_t *md2_readModel(const char *filename)
 	{ \
 		CONS_Alert(CONS_ERROR, "md2_readModel: %s has too many " msgname " (# found: %d, maximum: %d)\n", filename, field, max); \
 		md2_freeModel (model); \
+		fclose(file); \
 		return 0; \
 	}
 
@@ -334,6 +335,7 @@ static md2_model_t *md2_readModel(const char *filename)
 			fread(model->skins, sizeof (md2_skin_t), model->header.numSkins, file))
 		{
 			md2_freeModel (model);
+			fclose(file);
 			return 0;
 		}
 	}
@@ -347,6 +349,7 @@ static md2_model_t *md2_readModel(const char *filename)
 			fread(model->texCoords, sizeof (md2_textureCoordinate_t), model->header.numTexCoords, file))
 		{
 			md2_freeModel (model);
+			fclose(file);
 			return 0;
 		}
 	}
@@ -360,6 +363,7 @@ static md2_model_t *md2_readModel(const char *filename)
 			fread(model->triangles, sizeof (md2_triangle_t), model->header.numTriangles, file))
 		{
 			md2_freeModel (model);
+			fclose(file);
 			return 0;
 		}
 	}
@@ -372,6 +376,7 @@ static md2_model_t *md2_readModel(const char *filename)
 		if (!model->frames)
 		{
 			md2_freeModel (model);
+			fclose(file);
 			return 0;
 		}
 
@@ -385,6 +390,7 @@ static md2_model_t *md2_readModel(const char *filename)
 				fread(frame, 1, model->header.frameSize, file))
 			{
 				md2_freeModel (model);
+				fclose(file);
 				return 0;
 			}
 
@@ -410,6 +416,7 @@ static md2_model_t *md2_readModel(const char *filename)
 			fread(model->glCommandBuffer, sizeof (INT32), model->header.numGlCommands, file))
 		{
 			md2_freeModel (model);
+			fclose(file);
 			return 0;
 		}
 	}
diff --git a/src/hardware/hw_md2.h b/src/hardware/hw_md2.h
index 5a7e6d2b3cdaf0c3ae7d5813e835d5cf8711403e..299d1240005daba28769c1e8b5e12adfd1b67401 100644
--- a/src/hardware/hw_md2.h
+++ b/src/hardware/hw_md2.h
@@ -23,6 +23,11 @@
 
 #include "hw_glob.h"
 
+// magic number "IDP2" or 844121161
+#define MD2_IDENT                       (INT32)(('2' << 24) + ('P' << 16) + ('D' << 8) + 'I')
+// model version
+#define MD2_VERSION                     8
+
 #define MD2_MAX_TRIANGLES               8192
 #define MD2_MAX_VERTICES                4096
 #define MD2_MAX_TEXCOORDS               4096
diff --git a/src/hardware/hw_trick.c b/src/hardware/hw_trick.c
index e9ba19efb60a8fb8a6fe26c657866bcd2d764927..97d86b944890de2fae05d11bfd05ed607722b3d3 100644
--- a/src/hardware/hw_trick.c
+++ b/src/hardware/hw_trick.c
@@ -107,17 +107,17 @@ static void releaseLineChains(void)
 
 	for (i = 0; i < numsectors; i++)
 	{
-	sector = &sectors[i];
-	nextElem = sector->sectorLines;
+		sector = &sectors[i];
+		nextElem = sector->sectorLines;
 
-	while (nextElem)
-	{
-		thisElem = nextElem;
-		nextElem = thisElem->next;
-		free(thisElem);
-	}
+		while (nextElem)
+		{
+			thisElem = nextElem;
+			nextElem = thisElem->next;
+			free(thisElem);
+		}
 
-	sector->sectorLines = NULL;
+		sector->sectorLines = NULL;
 	}
 }
 
@@ -397,7 +397,7 @@ static void sortStacklist(sector_t *sector)
 		i = 0;
 		finished = true;
 
-		while (NULL != *(list+i+1))
+		while (*(list+i+1))
 		{
 			sec1 = *(list+i);
 			sec2 = *(list+i+1);
@@ -438,7 +438,7 @@ static double calcLineoutLength(sector_t *sector)
 	double length = 0.0L;
 	chain = sector->sectorLines;
 
-	while (NULL != chain) // sum up lengths of all lines
+	while (chain) // sum up lengths of all lines
 	{
 		length += lineLength(chain->line);
 		chain = chain->next;
@@ -454,7 +454,7 @@ static void calcLineouts(sector_t *sector)
 	size_t secCount = 0;
 	sector_t *encSector = *(sector->stackList);
 
-	while (NULL != encSector)
+	while (encSector)
 	{
 		if (encSector->lineoutLength < 0.0L) // if length has not yet been calculated
 		{
@@ -552,7 +552,7 @@ static boolean areBottomtexturesMissing(sector_t *thisSector)
 		if (frontSector == backSector) // skip damn renderer tricks here
 			continue;
 
-		if (frontSector == NULL || backSector == NULL)
+		if (!frontSector || !backSector)
 			continue;
 
 		sider = &sides[thisElem->line->sidenum[0]];
@@ -587,15 +587,14 @@ static boolean areBottomtexturesMissing(sector_t *thisSector)
 static boolean isCeilingFloating(sector_t *thisSector)
 {
 	sector_t *adjSector, *refSector = NULL, *frontSector, *backSector;
-	boolean floating = true;
 	linechain_t *thisElem, *nextElem;
 
 	if (!thisSector)
 		return false;
 
-	nextElem  = thisSector->sectorLines;
+	nextElem = thisSector->sectorLines;
 
-	while (NULL != nextElem) // walk through chain
+	while (nextElem) // walk through chain
 	{
 		thisElem = nextElem;
 		nextElem = thisElem->next;
@@ -609,10 +608,12 @@ static boolean isCeilingFloating(sector_t *thisSector)
 			adjSector = frontSector;
 
 		if (!adjSector) // assume floating sectors have surrounding sectors
-		{
-			floating = false;
-			break;
-		}
+			return false;
+
+#ifdef ESLOPE
+		if (adjSector->c_slope) // Don't bother with slopes
+			return false;
+#endif
 
 		if (!refSector)
 		{
@@ -621,23 +622,15 @@ static boolean isCeilingFloating(sector_t *thisSector)
 		}
 
 		// if adjacent sector has same height or more than one adjacent sector exists -> stop
-		if (thisSector->ceilingheight == adjSector->ceilingheight ||
-		   refSector != adjSector)
-		{
-			floating = false;
-			break;
-		}
+		if (thisSector->ceilingheight == adjSector->ceilingheight || refSector != adjSector)
+			return false;
 	}
 
 	// now check for walltextures
-	if (floating)
-	{
-		if (!areToptexturesMissing(thisSector))
-		{
-			floating = false;
-		}
-	}
-	return floating;
+	if (!areToptexturesMissing(thisSector))
+		return false;
+
+	return true;
 }
 
 //
@@ -647,13 +640,12 @@ static boolean isCeilingFloating(sector_t *thisSector)
 static boolean isFloorFloating(sector_t *thisSector)
 {
 	sector_t *adjSector, *refSector = NULL, *frontSector, *backSector;
-	boolean floating = true;
 	linechain_t *thisElem, *nextElem;
 
 	if (!thisSector)
 		return false;
 
-	nextElem  = thisSector->sectorLines;
+	nextElem = thisSector->sectorLines;
 
 	while (nextElem) // walk through chain
 	{
@@ -668,36 +660,30 @@ static boolean isFloorFloating(sector_t *thisSector)
 		else
 			adjSector = frontSector;
 
-		if (NULL == adjSector) // assume floating sectors have surrounding sectors
-		{
-			floating = false;
-			break;
-		}
+		if (!adjSector) // assume floating sectors have surrounding sectors
+			return false;
+
+#ifdef ESLOPE
+		if (adjSector->f_slope) // Don't bother with slopes
+			return false;
+#endif
 
-		if (NULL == refSector)
+		if (!refSector)
 		{
 			refSector = adjSector;
 			continue;
 		}
 
 		// if adjacent sector has same height or more than one adjacent sector exists -> stop
-		if (thisSector->floorheight == adjSector->floorheight ||
-		   refSector != adjSector)
-		{
-			floating = false;
-			break;
-		}
+		if (thisSector->floorheight == adjSector->floorheight || refSector != adjSector)
+			return false;
 	}
 
 	// now check for walltextures
-	if (floating)
-	{
-		if (!areBottomtexturesMissing(thisSector))
-		{
-			floating = false;
-		}
-	}
-	return floating;
+	if (!areBottomtexturesMissing(thisSector))
+		return false;
+
+	return true;
 }
 
 //
@@ -707,14 +693,12 @@ static fixed_t estimateCeilHeight(sector_t *thisSector)
 {
 	sector_t *adjSector;
 
-	if (!thisSector ||
-	 !thisSector->sectorLines ||
-	  !thisSector->sectorLines->line)
+	if (!thisSector || !thisSector->sectorLines || !thisSector->sectorLines->line)
 		return 0;
 
 	adjSector = thisSector->sectorLines->line->frontsector;
 	if (adjSector == thisSector)
-	adjSector = thisSector->sectorLines->line->backsector;
+		adjSector = thisSector->sectorLines->line->backsector;
 
 	if (!adjSector)
 		return 0;
@@ -729,17 +713,15 @@ static fixed_t estimateFloorHeight(sector_t *thisSector)
 {
 	sector_t *adjSector;
 
-	if (!thisSector ||
-	 !thisSector->sectorLines ||
-	  !thisSector->sectorLines->line)
-	return 0;
+	if (!thisSector || !thisSector->sectorLines || !thisSector->sectorLines->line)
+		return 0;
 
 	adjSector = thisSector->sectorLines->line->frontsector;
 	if (adjSector == thisSector)
-	adjSector = thisSector->sectorLines->line->backsector;
+		adjSector = thisSector->sectorLines->line->backsector;
 
-	if (NULL == adjSector)
-	return 0;
+	if (!adjSector)
+		return 0;
 
 	return adjSector->floorheight;
 }
@@ -845,18 +827,12 @@ void HWR_CorrectSWTricks(void)
 		// correct height of floating sectors
 		if (isCeilingFloating(floatSector))
 		{
-			fixed_t corrheight;
-
-			corrheight = estimateCeilHeight(floatSector);
-			floatSector->virtualCeilingheight = corrheight;
+			floatSector->virtualCeilingheight = estimateCeilHeight(floatSector);
 			floatSector->virtualCeiling = true;
 		}
 		if (isFloorFloating(floatSector))
 		{
-			fixed_t corrheight;
-
-			corrheight = estimateFloorHeight(floatSector);
-			floatSector->virtualFloorheight = corrheight;
+			floatSector->virtualFloorheight = estimateFloorHeight(floatSector);
 			floatSector->virtualFloor = true;
 		}
 	}
diff --git a/src/info.c b/src/info.c
index 90bd817f435c241771d9100227ec104f45c98c90..1f5b394abc1a7e6a5056515b20603dddead39580 100644
--- a/src/info.c
+++ b/src/info.c
@@ -211,6 +211,10 @@ char sprnames[NUMSPRITES + 1][5] =
 	"BMCH", // Big Mace Chain
 	"SMCE", // Small Mace
 	"BMCE", // Big Mace
+	"YSPB", // Yellow spring on a ball
+	"RSPB", // Red spring on a ball
+	"SFBR", // Small Firebar
+	"BFBR", // Big Firebar
 
 	// Arid Canyon Scenery
 	"BTBL", // Big tumbleweed
@@ -1830,13 +1834,13 @@ state_t states[NUMSTATES] =
 	{SPR_CHAN, 0, -1, {NULL}, 0, 0, S_NULL}, // S_CEZCHAIN
 
 	// Flame
-	{SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20,    3, {A_FlameParticle}, 3, 0, S_FLAME2}, // S_FLAME1
-	{SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|1,  3,            {NULL}, 0, 0, S_FLAME3}, // S_FLAME2
-	{SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|2,  3, {A_FlameParticle}, 3, 0, S_FLAME4}, // S_FLAME3
-	{SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|3,  3,            {NULL}, 0, 0, S_FLAME5}, // S_FLAME4
-	{SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|4,  3, {A_FlameParticle}, 3, 0, S_FLAME6}, // S_FLAME5
-	{SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|5,  3,            {NULL}, 0, 0, S_FLAME1}, // S_FLAME6
-	{SPR_FLAM, FF_FULLBRIGHT|FF_TRANS10|6, 24,            {NULL}, 0, 0, S_NULL},   // S_FLAMEPARTICLE
+	{SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20,    3, {A_FlameParticle}, 3, FRACUNIT/2, S_FLAME2}, // S_FLAME1
+	{SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|1,  3,            {NULL}, 0, 0         , S_FLAME3}, // S_FLAME2
+	{SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|2,  3, {A_FlameParticle}, 3, FRACUNIT/2, S_FLAME4}, // S_FLAME3
+	{SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|3,  3,            {NULL}, 0, 0         , S_FLAME5}, // S_FLAME4
+	{SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|4,  3, {A_FlameParticle}, 3, FRACUNIT/2, S_FLAME6}, // S_FLAME5
+	{SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|5,  3,            {NULL}, 0, 0         , S_FLAME1}, // S_FLAME6
+	{SPR_FLAM, FF_FULLBRIGHT|FF_TRANS10|6, 24,            {NULL}, 0, 0         , S_NULL},   // S_FLAMEPARTICLE
 
 	{SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|FF_ANIMATE, -1, {NULL}, 5, 3, S_FLAME2}, // S_FLAMEREST
 
@@ -1847,31 +1851,75 @@ state_t states[NUMSTATES] =
 	{SPR_NULL, 0, -1, {NULL},          0, 0, S_SLING2}, // S_SLING1
 	{SPR_NULL, 0, -1, {A_SlingAppear}, 0, 0, S_NULL},   // S_SLING2
 
-	// Small Mace Chain
-	{SPR_SMCH, 0, 1, {A_MaceRotate}, 0, 0, S_SMALLMACECHAIN}, // S_SMALLMACECHAIN
-
-	// Big Mace Chain
-	{SPR_BMCH, 0, 1, {A_MaceRotate}, 0, 0, S_BIGMACECHAIN}, // S_BIGMACECHAIN
-
-	// Small Mace
-	{SPR_SMCE, 0, 1, {A_MaceRotate}, 0, 0, S_SMALLMACE}, // S_SMALLMACE
-
-	// Big Mace
-	{SPR_BMCE, 0, 1, {A_MaceRotate}, 0, 0, S_BIGMACE}, // S_BIGMACE
+	// CEZ maces and chains
+	{SPR_SMCH, 0, -1, {NULL}, 0, 0, S_SMALLMACECHAIN}, // S_SMALLMACECHAIN
+	{SPR_BMCH, 0, -1, {NULL}, 0, 0, S_BIGMACECHAIN},   // S_BIGMACECHAIN
+	{SPR_SMCE, 0, -1, {NULL}, 0, 0, S_SMALLMACE},      // S_SMALLMACE
+	{SPR_BMCE, 0, -1, {NULL}, 0, 0, S_BIGMACE},        // S_BIGMACE
+
+	// Yellow spring on a ball
+	{SPR_YSPB, 0, -1, {NULL},   0, 0, S_NULL},              // S_YELLOWSPRINGBALL
+	{SPR_YSPB, 4,  4, {A_Pain}, 0, 0, S_YELLOWSPRINGBALL3}, // S_YELLOWSPRINGBALL2
+	{SPR_YSPB, 3,  1, {NULL},   0, 0, S_YELLOWSPRINGBALL4}, // S_YELLOWSPRINGBALL3
+	{SPR_YSPB, 2,  1, {NULL},   0, 0, S_YELLOWSPRINGBALL5}, // S_YELLOWSPRINGBALL4
+	{SPR_YSPB, 1,  1, {NULL},   0, 0, S_YELLOWSPRINGBALL},  // S_YELLOWSPRINGBALL5
+
+	// Red spring on a ball
+	{SPR_RSPB, 0, -1, {NULL},   0, 0, S_NULL},           // S_REDSPRINGBALL
+	{SPR_RSPB, 4,  4, {A_Pain}, 0, 0, S_REDSPRINGBALL3}, // S_REDSPRINGBALL2
+	{SPR_RSPB, 3,  1, {NULL},   0, 0, S_REDSPRINGBALL4}, // S_REDSPRINGBALL3
+	{SPR_RSPB, 2,  1, {NULL},   0, 0, S_REDSPRINGBALL5}, // S_REDSPRINGBALL4
+	{SPR_RSPB, 1,  1, {NULL},   0, 0, S_REDSPRINGBALL},  // S_REDSPRINGBALL5
+
+	// Small Firebar
+	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20,     1, {A_FlameParticle}, 3, FRACUNIT/3, S_SMALLFIREBAR2},  // S_SMALLFIREBAR1
+	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20| 1,  1, {NULL},            0, 0,          S_SMALLFIREBAR3},  // S_SMALLFIREBAR2
+	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20| 2,  1, {A_FlameParticle}, 3, FRACUNIT/3, S_SMALLFIREBAR4},  // S_SMALLFIREBAR3
+	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20| 3,  1, {NULL},            0, 0,          S_SMALLFIREBAR5},  // S_SMALLFIREBAR4
+	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20| 4,  1, {A_FlameParticle}, 3, FRACUNIT/3, S_SMALLFIREBAR6},  // S_SMALLFIREBAR5
+	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20| 5,  1, {NULL},            0, 0,          S_SMALLFIREBAR7},  // S_SMALLFIREBAR6
+	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20| 6,  1, {A_FlameParticle}, 3, FRACUNIT/3, S_SMALLFIREBAR8},  // S_SMALLFIREBAR7
+	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20| 7,  1, {NULL},            0, 0,          S_SMALLFIREBAR9},  // S_SMALLFIREBAR8
+	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20| 8,  1, {A_FlameParticle}, 3, FRACUNIT/3, S_SMALLFIREBAR10}, // S_SMALLFIREBAR9
+	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20| 9,  1, {NULL},            0, 0,          S_SMALLFIREBAR11}, // S_SMALLFIREBAR10
+	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20|10,  1, {A_FlameParticle}, 3, FRACUNIT/3, S_SMALLFIREBAR12}, // S_SMALLFIREBAR11
+	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20|11,  1, {NULL},            0, 0,          S_SMALLFIREBAR13}, // S_SMALLFIREBAR12
+	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20|12,  1, {A_FlameParticle}, 3, FRACUNIT/3, S_SMALLFIREBAR14}, // S_SMALLFIREBAR13
+	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20|13,  1, {NULL},            0, 0,          S_SMALLFIREBAR15}, // S_SMALLFIREBAR14
+	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20|14,  1, {A_FlameParticle}, 3, FRACUNIT/3, S_SMALLFIREBAR16}, // S_SMALLFIREBAR15
+	{SPR_SFBR, FF_FULLBRIGHT|FF_TRANS20|15,  1, {NULL},            0, 0,          S_SMALLFIREBAR1},  // S_SMALLFIREBAR16
+
+	// Big Firebar
+	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20,     1, {A_FlameParticle}, 3, FRACUNIT/2, S_BIGFIREBAR2},  // S_BIGFIREBAR1
+	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20| 1,  1, {NULL},            0, 0,          S_BIGFIREBAR3},  // S_BIGFIREBAR2
+	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20| 2,  1, {A_FlameParticle}, 3, FRACUNIT/2, S_BIGFIREBAR4},  // S_BIGFIREBAR3
+	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20| 3,  1, {NULL},            0, 0,          S_BIGFIREBAR5},  // S_BIGFIREBAR4
+	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20| 4,  1, {A_FlameParticle}, 3, FRACUNIT/2, S_BIGFIREBAR6},  // S_BIGFIREBAR5
+	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20| 5,  1, {NULL},            0, 0,          S_BIGFIREBAR7},  // S_BIGFIREBAR6
+	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20| 6,  1, {A_FlameParticle}, 3, FRACUNIT/2, S_BIGFIREBAR8},  // S_BIGFIREBAR7
+	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20| 7,  1, {NULL},            0, 0,          S_BIGFIREBAR9},  // S_BIGFIREBAR8
+	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20| 8,  1, {A_FlameParticle}, 3, FRACUNIT/2, S_BIGFIREBAR10}, // S_BIGFIREBAR9
+	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20| 9,  1, {NULL},            0, 0,          S_BIGFIREBAR11}, // S_BIGFIREBAR10
+	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20|10,  1, {A_FlameParticle}, 3, FRACUNIT/2, S_BIGFIREBAR12}, // S_BIGFIREBAR11
+	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20|11,  1, {NULL},            0, 0,          S_BIGFIREBAR13}, // S_BIGFIREBAR12
+	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20|12,  1, {A_FlameParticle}, 3, FRACUNIT/2, S_BIGFIREBAR14}, // S_BIGFIREBAR13
+	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20|13,  1, {NULL},            0, 0,          S_BIGFIREBAR15}, // S_BIGFIREBAR14
+	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20|14,  1, {A_FlameParticle}, 3, FRACUNIT/2, S_BIGFIREBAR16}, // S_BIGFIREBAR15
+	{SPR_BFBR, FF_FULLBRIGHT|FF_TRANS20|15,  1, {NULL},            0, 0,          S_BIGFIREBAR1},  // S_BIGFIREBAR16
 
 	// CEZ Flower
 	{SPR_FWR4, 0, -1, {NULL}, 0, 0, S_NULL}, // S_CEZFLOWER1
 
 	// Big Tumbleweed
-	{SPR_BTBL, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BIGTUMBLEWEED
-	{SPR_BTBL, 0, 5, {NULL}, 0, 0, S_BIGTUMBLEWEED_ROLL2}, // S_BIGTUMBLEWEED_ROLL1
-	{SPR_BTBL, 1, 5, {NULL}, 0, 0, S_BIGTUMBLEWEED_ROLL3}, // S_BIGTUMBLEWEED_ROLL2
-	{SPR_BTBL, 2, 5, {NULL}, 0, 0, S_BIGTUMBLEWEED_ROLL4}, // S_BIGTUMBLEWEED_ROLL3
-	{SPR_BTBL, 3, 5, {NULL}, 0, 0, S_BIGTUMBLEWEED_ROLL5}, // S_BIGTUMBLEWEED_ROLL4
-	{SPR_BTBL, 4, 5, {NULL}, 0, 0, S_BIGTUMBLEWEED_ROLL6}, // S_BIGTUMBLEWEED_ROLL5
-	{SPR_BTBL, 5, 5, {NULL}, 0, 0, S_BIGTUMBLEWEED_ROLL7}, // S_BIGTUMBLEWEED_ROLL6
-	{SPR_BTBL, 6, 5, {NULL}, 0, 0, S_BIGTUMBLEWEED_ROLL8}, // S_BIGTUMBLEWEED_ROLL7
-	{SPR_BTBL, 7, 5, {NULL}, 0, 0, S_BIGTUMBLEWEED_ROLL1}, // S_BIGTUMBLEWEED_ROLL8
+	{SPR_BTBL, 0, -1, {NULL}, 0, 0, S_NULL},                // S_BIGTUMBLEWEED
+	{SPR_BTBL, 0,  5, {NULL}, 0, 0, S_BIGTUMBLEWEED_ROLL2}, // S_BIGTUMBLEWEED_ROLL1
+	{SPR_BTBL, 1,  5, {NULL}, 0, 0, S_BIGTUMBLEWEED_ROLL3}, // S_BIGTUMBLEWEED_ROLL2
+	{SPR_BTBL, 2,  5, {NULL}, 0, 0, S_BIGTUMBLEWEED_ROLL4}, // S_BIGTUMBLEWEED_ROLL3
+	{SPR_BTBL, 3,  5, {NULL}, 0, 0, S_BIGTUMBLEWEED_ROLL5}, // S_BIGTUMBLEWEED_ROLL4
+	{SPR_BTBL, 4,  5, {NULL}, 0, 0, S_BIGTUMBLEWEED_ROLL6}, // S_BIGTUMBLEWEED_ROLL5
+	{SPR_BTBL, 5,  5, {NULL}, 0, 0, S_BIGTUMBLEWEED_ROLL7}, // S_BIGTUMBLEWEED_ROLL6
+	{SPR_BTBL, 6,  5, {NULL}, 0, 0, S_BIGTUMBLEWEED_ROLL8}, // S_BIGTUMBLEWEED_ROLL7
+	{SPR_BTBL, 7,  5, {NULL}, 0, 0, S_BIGTUMBLEWEED_ROLL1}, // S_BIGTUMBLEWEED_ROLL8
 
 	// Little Tumbleweed
 	{SPR_STBL, 0, -1, {NULL}, 0, 0, S_NULL}, // S_LITTLETUMBLEWEED
@@ -8822,7 +8870,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_SWINGMACEPOINT
+	{           // MT_CHAINMACEPOINT
 		1105,           // doomednum
 		S_INVISIBLE,    // spawnstate
 		1000,           // spawnhealth
@@ -8849,7 +8897,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_HANGMACEPOINT
+	{           // MT_SPRINGBALLPOINT
 		1106,           // doomednum
 		S_INVISIBLE,    // spawnstate
 		1000,           // spawnhealth
@@ -8876,7 +8924,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_SPINMACEPOINT
+	{           // MT_CHAINPOINT
 		1107,           // doomednum
 		S_INVISIBLE,    // spawnstate
 		1000,           // spawnhealth
@@ -8896,7 +8944,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		128*FRACUNIT,   // radius
 		1*FRACUNIT,     // height
 		0,              // display offset
-		200,            // mass
+		10000,          // mass
 		0,              // damage
 		sfx_None,       // activesound
 		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOCLIP|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
@@ -8930,6 +8978,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
+	{           // MT_FIREBARPOINT
+		1109,           // doomednum
+		S_INVISIBLE,    // spawnstate
+		1000,           // 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
+		10*FRACUNIT,    // speed
+		128*FRACUNIT,   // radius
+		1*FRACUNIT,     // height
+		0,              // display offset
+		200,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOCLIP|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_CUSTOMMACEPOINT
+		1111,           // doomednum
+		S_INVISIBLE,    // spawnstate
+		1000,           // 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
+		10*FRACUNIT,    // speed
+		128*FRACUNIT,   // radius
+		1*FRACUNIT,     // height
+		0,              // display offset
+		200,            // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOCLIP|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		S_NULL          // raisestate
+	},
+
 	{            // MT_SMALLMACECHAIN
 		-1,               // doomednum
 		S_SMALLMACECHAIN, // spawnstate
@@ -8953,7 +9055,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		100,              // mass
 		1,                // damage
 		sfx_None,         // activesound
-		MF_NOGRAVITY|MF_SPECIAL|MF_NOCLIPHEIGHT, // flags
+		MF_SCENERY|MF_SPECIAL|MF_NOGRAVITY, // flags
 		S_NULL            // raisestate
 	},
 
@@ -8980,7 +9082,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		100,            // mass
 		1,              // damage
 		sfx_None,       // activesound
-		MF_NOGRAVITY|MF_SPECIAL|MF_NOCLIPHEIGHT, // flags
+		MF_SCENERY|MF_SPECIAL|MF_NOGRAVITY, // flags
 		S_NULL          // raisestate
 	},
 
@@ -9003,11 +9105,11 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		24*FRACUNIT,    // speed
 		17*FRACUNIT,    // radius
 		34*FRACUNIT,    // height
-		0,              // display offset
+		1,              // display offset
 		100,            // mass
 		1,              // damage
 		sfx_mswing,     // activesound
-		MF_PAIN|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		MF_SCENERY|MF_PAIN|MF_NOGRAVITY, // flags
 		S_NULL          // raisestate
 	},
 
@@ -9030,11 +9132,119 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		48*FRACUNIT,    // speed
 		34*FRACUNIT,    // radius
 		68*FRACUNIT,    // height
-		0,              // display offset
+		1,              // display offset
 		100,            // mass
 		1,              // damage
 		sfx_mswing,     // activesound
-		MF_PAIN|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
+		MF_SCENERY|MF_PAIN|MF_NOGRAVITY, // flags
+		S_NULL          // raisestate
+	},
+
+	{            // MT_YELLOWSPRINGBALL
+		-1,             // doomednum
+		S_YELLOWSPRINGBALL, // spawnstate
+		1000,           // spawnhealth
+		S_YELLOWSPRINGBALL2, // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_spring,     // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		24*FRACUNIT,    // speed
+		17*FRACUNIT,    // radius
+		34*FRACUNIT,    // height
+		1,              // display offset
+		20*FRACUNIT,    // mass
+		0,              // damage
+		sfx_mswing,     // activesound
+		MF_SCENERY|MF_SOLID|MF_SPRING|MF_NOGRAVITY, // flags
+		S_YELLOWSPRINGBALL2 // raisestate
+	},
+
+	{            // MT_REDSPRINGBALL
+		-1,             // doomednum
+		S_REDSPRINGBALL, // spawnstate
+		1000,           // spawnhealth
+		S_REDSPRINGBALL2, // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_spring,     // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		24*FRACUNIT,    // speed
+		17*FRACUNIT,    // radius
+		34*FRACUNIT,    // height
+		1,              // display offset
+		32*FRACUNIT,    // mass
+		0,              // damage
+		sfx_mswing,     // activesound
+		MF_SCENERY|MF_SOLID|MF_SPRING|MF_NOGRAVITY, // flags
+		S_REDSPRINGBALL2 // raisestate
+	},
+
+	{            // MT_SMALLFIREBAR
+		-1,             // doomednum
+		S_SMALLFIREBAR1,     // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		MT_FLAMEPARTICLE, // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		24*FRACUNIT,    // speed
+		17*FRACUNIT,    // radius
+		34*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		1,              // damage
+		sfx_None,       // activesound
+		MF_SCENERY|MF_PAIN|MF_FIRE|MF_NOGRAVITY, // flags
+		S_NULL          // raisestate
+	},
+
+	{            // MT_BIGFIREBAR
+		-1,             // doomednum
+		S_BIGFIREBAR1,  // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		MT_FLAMEPARTICLE, // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		48*FRACUNIT,    // speed
+		34*FRACUNIT,    // radius
+		68*FRACUNIT,    // height
+		1,              // display offset
+		100,            // mass
+		1,              // damage
+		sfx_None,       // activesound
+		MF_SCENERY|MF_PAIN|MF_FIRE|MF_NOGRAVITY, // flags
 		S_NULL          // raisestate
 	},
 
diff --git a/src/info.h b/src/info.h
index 540c2853becbf4810b9cfd6780a9bd153388884d..63a4d893f5f926c91d3c30236b64ea5fba5f35d3 100644
--- a/src/info.h
+++ b/src/info.h
@@ -84,7 +84,6 @@ void A_DetonChase(); // Deton Chaser
 void A_CapeChase(); // Fake little Super Sonic cape
 void A_RotateSpikeBall(); // Spike ball rotation
 void A_SlingAppear();
-void A_MaceRotate();
 void A_UnidusBall();
 void A_RockSpawn();
 void A_SetFuse();
@@ -417,6 +416,10 @@ typedef enum sprite
 	SPR_BMCH, // Big Mace Chain
 	SPR_SMCE, // Small Mace
 	SPR_BMCE, // Big Mace
+	SPR_YSPB, // Yellow spring on a ball
+	SPR_RSPB, // Red spring on a ball
+	SPR_SFBR, // Small Firebar
+	SPR_BFBR, // Big Firebar
 
 	// Arid Canyon Scenery
 	SPR_BTBL, // Big tumbleweed
@@ -2056,18 +2059,62 @@ typedef enum state
 	S_SLING1,
 	S_SLING2,
 
-	// CEZ Small Mace Chain
+	// CEZ maces and chains
 	S_SMALLMACECHAIN,
-
-	// CEZ Big Mace Chain
 	S_BIGMACECHAIN,
-
-	// CEZ Small Mace
 	S_SMALLMACE,
-
-	// CEZ Big Mace
 	S_BIGMACE,
 
+	// Yellow spring on a ball
+	S_YELLOWSPRINGBALL,
+	S_YELLOWSPRINGBALL2,
+	S_YELLOWSPRINGBALL3,
+	S_YELLOWSPRINGBALL4,
+	S_YELLOWSPRINGBALL5,
+
+	// Red spring on a ball
+	S_REDSPRINGBALL,
+	S_REDSPRINGBALL2,
+	S_REDSPRINGBALL3,
+	S_REDSPRINGBALL4,
+	S_REDSPRINGBALL5,
+
+	// Small Firebar
+	S_SMALLFIREBAR1,
+	S_SMALLFIREBAR2,
+	S_SMALLFIREBAR3,
+	S_SMALLFIREBAR4,
+	S_SMALLFIREBAR5,
+	S_SMALLFIREBAR6,
+	S_SMALLFIREBAR7,
+	S_SMALLFIREBAR8,
+	S_SMALLFIREBAR9,
+	S_SMALLFIREBAR10,
+	S_SMALLFIREBAR11,
+	S_SMALLFIREBAR12,
+	S_SMALLFIREBAR13,
+	S_SMALLFIREBAR14,
+	S_SMALLFIREBAR15,
+	S_SMALLFIREBAR16,
+
+	// Big Firebar
+	S_BIGFIREBAR1,
+	S_BIGFIREBAR2,
+	S_BIGFIREBAR3,
+	S_BIGFIREBAR4,
+	S_BIGFIREBAR5,
+	S_BIGFIREBAR6,
+	S_BIGFIREBAR7,
+	S_BIGFIREBAR8,
+	S_BIGFIREBAR9,
+	S_BIGFIREBAR10,
+	S_BIGFIREBAR11,
+	S_BIGFIREBAR12,
+	S_BIGFIREBAR13,
+	S_BIGFIREBAR14,
+	S_BIGFIREBAR15,
+	S_BIGFIREBAR16,
+
 	S_CEZFLOWER1,
 
 	// Big Tumbleweed
@@ -3421,14 +3468,20 @@ typedef enum mobj_type
 	MT_FLAMEPARTICLE,
 	MT_EGGSTATUE, // Eggman Statue
 	MT_MACEPOINT, // Mace rotation point
-	MT_SWINGMACEPOINT, // Mace swinging point
-	MT_HANGMACEPOINT, // Hangable mace chain
-	MT_SPINMACEPOINT, // Spin/Controllable mace chain
+	MT_CHAINMACEPOINT, // Combination of chains and maces point
+	MT_SPRINGBALLPOINT, // Spring ball point
+	MT_CHAINPOINT, // Mace chain
 	MT_HIDDEN_SLING, // Spin mace chain (activatable)
+	MT_FIREBARPOINT, // Firebar
+	MT_CUSTOMMACEPOINT, // Custom mace
 	MT_SMALLMACECHAIN, // Small Mace Chain
 	MT_BIGMACECHAIN, // Big Mace Chain
 	MT_SMALLMACE, // Small Mace
 	MT_BIGMACE, // Big Mace
+	MT_YELLOWSPRINGBALL, // Yellow spring on a ball
+	MT_REDSPRINGBALL, // Red spring on a ball
+	MT_SMALLFIREBAR, // Small Firebar
+	MT_BIGFIREBAR, // Big Firebar
 	MT_CEZFLOWER,
 
 	// Arid Canyon Scenery
diff --git a/src/lua_consolelib.c b/src/lua_consolelib.c
index 322fecb64f1739b3d9749e731f1eef59b9f8570c..b688155fbfd31a9bb5cbee5a5bbaecfff89a7606 100644
--- a/src/lua_consolelib.c
+++ b/src/lua_consolelib.c
@@ -22,8 +22,13 @@
 #include "lua_libs.h"
 #include "lua_hud.h" // hud_running errors
 
+// for functions not allowed in hud.add hooks
 #define NOHUD if (hud_running)\
 return luaL_error(L, "HUD rendering code should not call this function!");
+// for functions not allowed in hooks or coroutines (supercedes above)
+#define NOHOOK if (!lua_lumploading)\
+		return luaL_error(L, "This function cannot be called from within a hook or coroutine!");
+// for functions only allowed within a level
 #define INLEVEL if (gamestate != GS_LEVEL)\
 return luaL_error(L, "This function can only be used in a level!");
 
@@ -184,7 +189,7 @@ static int lib_comAddCommand(lua_State *L)
 	strlwr(name);
 
 	luaL_checktype(L, 2, LUA_TFUNCTION);
-	NOHUD
+	NOHOOK
 	if (lua_gettop(L) >= 3)
 	{ // For the third argument, only take a boolean or a number.
 		lua_settop(L, 3);
@@ -296,7 +301,7 @@ static int lib_cvRegisterVar(lua_State *L)
 	consvar_t *cvar;
 	luaL_checktype(L, 1, LUA_TTABLE);
 	lua_settop(L, 1); // Clear out all other possible arguments, leaving only the first one.
-	NOHUD
+	NOHOOK
 	cvar = lua_newuserdata(L, sizeof(consvar_t));
 	luaL_getmetatable(L, META_CVAR);
 	lua_setmetatable(L, -2);
diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c
index 04efac070f52f722334df2e363038dc6673f39c7..3dd3f932f81a41bb82c046a7eb4fe8a564de796f 100644
--- a/src/lua_hooklib.c
+++ b/src/lua_hooklib.c
@@ -109,8 +109,8 @@ static int lib_addHook(lua_State *L)
 
 	luaL_checktype(L, 1, LUA_TFUNCTION);
 
-	if (hud_running)
-		return luaL_error(L, "HUD rendering code should not call this function!");
+	if (!lua_lumploading)
+		return luaL_error(L, "This function cannot be called from within a hook or coroutine!");
 
 	switch(hook.type)
 	{
diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c
index 5b3cd46ce8f86cf3f987dc716d2b4f0e0c86d7f3..29e4970c1d3153536315e2d7151d0bf885854ad6 100644
--- a/src/lua_hudlib.c
+++ b/src/lua_hudlib.c
@@ -612,6 +612,9 @@ static int lib_hudadd(lua_State *L)
 	luaL_checktype(L, 1, LUA_TFUNCTION);
 	field = luaL_checkoption(L, 2, "game", hudhook_opt);
 
+	if (!lua_lumploading)
+		return luaL_error(L, "This function cannot be called from within a hook or coroutine!");
+
 	lua_getfield(L, LUA_REGISTRYINDEX, "HUD");
 	I_Assert(lua_istable(L, -1));
 	lua_rawgeti(L, -1, field+2); // HUD[2+]
diff --git a/src/lua_script.c b/src/lua_script.c
index 7bc9b88ec2a2c7a8525aab75a1d18637d3e135ad..994f81a40bb54880fffcefd061bb909a43e6fead 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -161,6 +161,11 @@ void LUA_ClearExtVars(void)
 }
 #endif
 
+// Use this variable to prevent certain functions from running
+// if they were not called on lump load
+// (i.e. they were called in hooks or coroutines etc)
+boolean lua_lumploading = false;
+
 // Load a script from a MYFILE
 static inline void LUA_LoadFile(MYFILE *f, char *name)
 {
@@ -198,7 +203,9 @@ void LUA_LoadLump(UINT16 wad, UINT16 lump)
 		name[strlen(wadfiles[wad]->filename)+9] = '\0';
 	}
 
-	LUA_LoadFile(&f, name);
+	lua_lumploading = true; // turn on loading flag
+	LUA_LoadFile(&f, name); // actually load file!
+	lua_lumploading = false; // turn off again
 
 	free(name);
 	Z_Free(f.data);
diff --git a/src/lua_script.h b/src/lua_script.h
index d143ed879a25944778f6105c6b123b0190184ab0..51f1eaeaaeb4d00b4c55721aa4ae8fad165cd185 100644
--- a/src/lua_script.h
+++ b/src/lua_script.h
@@ -38,6 +38,8 @@
 void LUA_ClearExtVars(void);
 #endif
 
+extern boolean lua_lumploading; // is LUA_LoadLump being called?
+
 void LUA_LoadLump(UINT16 wad, UINT16 lump);
 #ifdef LUA_ALLOW_BYTECODE
 void LUA_DumpFile(const char *filename);
diff --git a/src/m_menu.c b/src/m_menu.c
index 7a606f450608fa441d476bebd50e0e96dbb7067e..64255e71a82d6630a962878cde7c634a8262af9c 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -6720,7 +6720,7 @@ static void M_ChoosePlayer(INT32 choice)
 	if (startmap != spstage_start)
 		cursaveslot = -1;
 
-	lastmapsaved = 0;
+	//lastmapsaved = 0;
 	gamecomplete = false;
 
 	G_DeferedInitNew(ultmode, G_BuildMapName(startmap), (UINT8)skinnum, false, fromlevelselect);
diff --git a/src/p_enemy.c b/src/p_enemy.c
index b0b03b487363a84badee7d71b65af3e2847d0e4e..e4e386d8d02a7fbbb9e6629e7ec133ebea18f2a8 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -137,7 +137,6 @@ void A_DetonChase(mobj_t *actor);
 void A_CapeChase(mobj_t *actor);
 void A_RotateSpikeBall(mobj_t *actor);
 void A_SlingAppear(mobj_t *actor);
-void A_MaceRotate(mobj_t *actor);
 void A_UnidusBall(mobj_t *actor);
 void A_RockSpawn(mobj_t *actor);
 void A_SetFuse(mobj_t *actor);
@@ -5175,15 +5174,12 @@ void A_SlingAppear(mobj_t *actor)
 	actor->movefactor = actor->threshold;
 	actor->friction = 128;
 
-	actor->flags |= MF_SLIDEME;
-
 	while (mlength > 0)
 	{
 		spawnee = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMALLMACECHAIN);
 
 		P_SetTarget(&spawnee->target, actor);
 
-		spawnee->movecount = 0;
 		spawnee->threshold = 0;
 		spawnee->reactiontime = mlength;
 
@@ -5198,129 +5194,6 @@ void A_SlingAppear(mobj_t *actor)
 	}
 }
 
-//
-// Function: A_MaceRotate
-//
-// Spins an object around its target, or, swings it from side to side.
-//
-// var1 = unused
-// var2 = unused
-//
-// So NOBODY forgets:
-// actor->
-// threshold - X tilt
-// movecount - Z tilt
-// reactiontime - link # in the chain (1 is closest)
-// lastlook - speed
-// friction - top speed
-// movedir - current angle holder
-// extravalue1 - smoothly move link into place
-//
-void A_MaceRotate(mobj_t *actor)
-{
-	TVector v;
-	TVector *res;
-	fixed_t radius;
-#ifdef HAVE_BLUA
-	if (LUA_CallAction("A_MaceRotate", actor))
-		return;
-#endif
-
-	// Target was removed.
-	if (!actor->target)
-	{
-		P_RemoveMobj(actor);
-		return;
-	}
-
-	P_UnsetThingPosition(actor);
-
-	// Radius of the link's rotation.
-	radius = FixedMul(actor->info->speed * actor->reactiontime, actor->target->scale);
-
-	// Double the radius if the chain links are made up of maces.
-	if (actor->target->type == MT_AXIS && (actor->type == MT_SMALLMACE || actor->type == MT_BIGMACE))
-		radius *= 2;
-
-	// Axis offset for the axis.
-	radius += actor->target->extravalue1;
-
-	// Smoothly move the link into position.
-	if (actor->extravalue1)
-	{
-		radius = FixedMul(radius, FixedDiv(actor->extravalue1, 100));
-		actor->extravalue1 += 1;
-		if (actor->extravalue1 >= 100)
-			actor->extravalue1 = 0;
-	}
-
-	actor->x = actor->target->x;
-	actor->y = actor->target->y;
-	actor->z = actor->target->z;
-
-	// Cut the height to align the link with the axis.
-	if (actor->type == MT_SMALLMACECHAIN || actor->type == MT_BIGMACECHAIN)
-		actor->z -= actor->height/4;
-	else
-		actor->z -= actor->height/2;
-
-	// Set the top speed for the link if it happens to be over that speed.
-	if (actor->target->lastlook > actor->target->friction)
-		actor->target->lastlook = actor->target->friction;
-
-	// Swinging Chain.
-	if (actor->target->type == MT_HANGMACEPOINT || actor->target->type == MT_SWINGMACEPOINT)
-	{
-		actor->movecount += actor->target->lastlook;
-		actor->movecount &= FINEMASK;
-
-		actor->threshold = FixedMul(FINECOSINE(actor->movecount), actor->target->lastlook << FRACBITS);
-
-		v[0] = FRACUNIT;
-		v[1] = 0;
-		v[2] = -radius;
-		v[3] = FRACUNIT;
-
-		// Calculate the angle matrixes for the link.
-		res = VectorMatrixMultiply(v, *RotateXMatrix(FixedAngle(actor->threshold)));
-		M_Memcpy(&v, res, sizeof(v));
-		res = VectorMatrixMultiply(v, *RotateZMatrix(actor->target->health << ANGLETOFINESHIFT));
-		M_Memcpy(&v, res, sizeof(v));
-	}
-	// Rotating Chain.
-	else
-	{
-		angle_t fa;
-
-		actor->threshold += actor->target->lastlook;
-		actor->threshold &= FINEMASK;
-		actor->target->health &= FINEMASK;
-
-		fa = actor->threshold;
-		v[0] = FixedMul(FINECOSINE(fa), radius);
-		v[1] = 0;
-		v[2] = FixedMul(FINESINE(fa), radius);
-		v[3] = FRACUNIT;
-
-		// Calculate the angle matrixes for the link.
-		res = VectorMatrixMultiply(v, *RotateXMatrix(actor->target->threshold << ANGLETOFINESHIFT));
-		M_Memcpy(&v, res, sizeof(v));
-		res = VectorMatrixMultiply(v, *RotateZMatrix(actor->target->health << ANGLETOFINESHIFT));
-		M_Memcpy(&v, res, sizeof(v));
-	}
-
-	// Add on the appropriate distances to the actor's co-ordinates.
-	actor->x += v[0];
-	actor->y += v[1];
-	actor->z += v[2];
-
-	P_SetThingPosition(actor);
-
-	if (!(actor->target->flags2 & MF2_BOSSNOTRAP) // flag that makes maces shut up on request
-	&& !(leveltime & 63) && (actor->type == MT_BIGMACE || actor->type == MT_SMALLMACE) && actor->target->type == MT_MACEPOINT)
-		S_StartSound(actor, actor->info->activesound);
-}
-
 // Function: A_SetFuse
 //
 // Description: Sets the actor's fuse timer if not set already. May also change state when fuse reaches the last tic, otherwise by default the actor will die or disappear. (Replaces A_SnowBall)
@@ -10833,26 +10706,33 @@ void A_FlickyFlutter(mobj_t *actor)
 // Description: Creates the mobj's painchance at a random position around the object's radius.
 //
 // var1 = momz of particle.
+// var2 = chance of particle spawn
 //
 void A_FlameParticle(mobj_t *actor)
 {
 	mobjtype_t type = (mobjtype_t)(mobjinfo[actor->type].painchance);
+	fixed_t rad, hei;
+	mobj_t *particle;
 	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
 
 #ifdef HAVE_BLUA
 	if (LUA_CallAction("A_FlameParticle", actor))
 		return;
 #endif
 
-	if (type)
-	{
-		fixed_t rad = 2*actor->radius>>FRACBITS;
-		fixed_t hei = actor->height>>FRACBITS;
-		mobj_t *particle = P_SpawnMobjFromMobj(actor,
-			P_RandomRange(rad, -rad)<<FRACBITS,
-			P_RandomRange(rad, -rad)<<FRACBITS,
-			P_RandomRange(hei/2, hei)<<FRACBITS,
-			type);
-		P_SetObjectMomZ(particle, locvar1<<FRACBITS, false);
-	}
+	if (!P_RandomChance(locvar2))
+		return;
+
+	if (!type)
+		return;
+
+	rad = 2*actor->radius>>FRACBITS;
+	hei = actor->height>>FRACBITS;
+	particle = P_SpawnMobjFromMobj(actor,
+		P_RandomRange(rad, -rad)<<FRACBITS,
+		P_RandomRange(rad, -rad)<<FRACBITS,
+		P_RandomRange(hei/2, hei)<<FRACBITS,
+		type);
+	P_SetObjectMomZ(particle, locvar1<<FRACBITS, false);
 }
diff --git a/src/p_inter.c b/src/p_inter.c
index 523d9720a89631bb9ca7dba26719b4e9e8cd6cd9..9b00d1bf07536457ea1e5386416ab21ec792da42 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -1496,10 +1496,19 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 			if (player->powers[pw_flashing])
 				return;
 
+			if (special->movefactor && special->tracer && (angle_t)special->tracer->health != ANGLE_90 && (angle_t)special->tracer->health != ANGLE_270)
+			{ // I don't expect you to understand this, Mr Bond...
+				angle_t ang = R_PointToAngle2(special->x, special->y, toucher->x, toucher->y) - special->tracer->threshold;
+				if ((special->movefactor > 0) == ((angle_t)special->tracer->health > ANGLE_90 && (angle_t)special->tracer->health < ANGLE_270))
+					ang += ANGLE_180;
+				if (ang < ANGLE_180)
+					return; // I expect you to die.
+			}
+
 			P_ResetPlayer(player);
 			P_SetTarget(&toucher->tracer, special);
 
-			if (special->target && (special->target->type == MT_SPINMACEPOINT || special->target->type == MT_HIDDEN_SLING))
+			if (special->tracer && !(special->tracer->flags2 & MF2_STRONGBOX))
 			{
 				player->powers[pw_carry] = CR_MACESPIN;
 				S_StartSound(toucher, sfx_spin);
@@ -1510,6 +1519,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 
 			// Can't jump first frame
 			player->pflags |= PF_JUMPSTASIS;
+
 			return;
 		case MT_BIGMINE:
 		case MT_BIGAIRMINE:
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 06afce7dd92f987a9e2591ecd5308d72850e1184..f71666f93ac164ed7c91c38a5b6837db4365e2e7 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -6269,6 +6269,128 @@ static void P_NightsItemChase(mobj_t *thing)
 	P_Attract(thing, thing->tracer, true);
 }
 
+//
+// P_MaceRotate
+// Spins an object around its target, or, swings it from side to side.
+//
+static void P_MaceRotate(mobj_t *mobj)
+{
+	TVector v;
+	TVector *res;
+	fixed_t radius, dist;
+	angle_t fa;
+	INT32 prevswing;
+	boolean donetwice = false;
+
+	// Tracer was removed.
+	if (!mobj->health)
+		return;
+	else if (!mobj->tracer)
+	{
+		P_KillMobj(mobj, NULL, NULL, 0);
+		return;
+	}
+
+	mobj->momx = mobj->momy = mobj->momz = 0;
+
+	prevswing = mobj->threshold;
+	mobj->threshold += mobj->tracer->lastlook;
+	mobj->threshold &= FINEMASK;
+
+	dist = ((mobj->info->speed) ? mobj->info->speed : mobjinfo[MT_SMALLMACECHAIN].speed);
+
+	// Radius of the link's rotation.
+	radius = FixedMul(dist * mobj->movecount, mobj->tracer->scale) + mobj->tracer->extravalue1;
+
+maceretry:
+
+	fa = (FixedAngle(mobj->tracer->movefactor*FRACUNIT) >> ANGLETOFINESHIFT);
+	radius = FixedMul(FINECOSINE(fa), radius);
+	v[1] = -FixedMul(FINESINE(fa), radius)
+	+ FixedMul(dist * mobj->movefactor, mobj->tracer->scale);
+	v[3] = FRACUNIT;
+
+	// Swinging Chain.
+	if (mobj->tracer->flags2 & MF2_STRONGBOX)
+	{
+		fixed_t swingmagnitude = FixedMul(FINECOSINE(mobj->threshold), mobj->tracer->lastlook << FRACBITS);
+		prevswing = FINECOSINE(prevswing);
+
+		if (!donetwice
+		&& (mobj->flags2 & MF2_BOSSNOTRAP) // at the end of the chain and can play a sound
+		&& ((prevswing > 0) != (swingmagnitude > 0))) // just passed its lowest point
+			S_StartSound(mobj, mobj->info->activesound);
+
+		fa = ((FixedAngle(swingmagnitude) >> ANGLETOFINESHIFT) + mobj->friction) & FINEMASK;
+
+		v[0] = FixedMul(FINESINE(fa), -radius);
+		v[2] = FixedMul(FINECOSINE(fa), -radius);
+	}
+	// Rotating Chain.
+	else
+	{
+		prevswing = (prevswing + mobj->friction) & FINEMASK;
+		fa = (mobj->threshold + mobj->friction) & FINEMASK;
+
+		if (!donetwice
+		&& (mobj->flags2 & MF2_BOSSNOTRAP) // at the end of the chain and can play a sound
+		&& (!(prevswing > (FINEMASK/2)) && (fa > (FINEMASK/2)))) // completed a full swing
+			S_StartSound(mobj, mobj->info->activesound);
+
+		v[0] = FixedMul(FINECOSINE(fa), radius);
+		v[2] = FixedMul(FINESINE(fa), radius);
+	}
+
+	// Calculate the angle matrixes for the link.
+	res = VectorMatrixMultiply(v, *RotateXMatrix(mobj->tracer->threshold << ANGLETOFINESHIFT));
+	M_Memcpy(&v, res, sizeof(v));
+	res = VectorMatrixMultiply(v, *RotateZMatrix(mobj->tracer->health << ANGLETOFINESHIFT));
+	M_Memcpy(&v, res, sizeof(v));
+
+	// Cut the height to align the link with the axis.
+	if (mobj->type == MT_SMALLMACECHAIN || mobj->type == MT_BIGMACECHAIN)
+		v[2] -= P_MobjFlip(mobj)*mobj->height/4;
+	else
+		v[2] -= P_MobjFlip(mobj)*mobj->height/2;
+
+	P_UnsetThingPosition(mobj);
+
+	// Add on the appropriate distances to the center's co-ordinates.
+	mobj->x = mobj->tracer->x + v[0];
+	mobj->y = mobj->tracer->y + v[1];
+	mobj->z = mobj->tracer->z + v[2];
+
+	P_SetThingPosition(mobj);
+
+	if (donetwice || P_MobjWasRemoved(mobj))
+		return;
+
+	if (mobj->flags & (MF_NOCLIP|MF_NOCLIPHEIGHT))
+		return;
+
+	if ((fa = ((mobj->tracer->threshold & (FINEMASK/2)) << ANGLETOFINESHIFT)) > ANGLE_45 && fa < ANGLE_135) // only move towards center when the motion is towards/away from the ground, rather than alongside it
+		return;
+
+	if (mobj->subsector->sector->ffloors)
+		P_AdjustMobjFloorZ_FFloors(mobj, mobj->subsector->sector, 2);
+
+	// Variable reuse
+	if (mobj->floorz > mobj->z)
+		dist = (mobj->floorz - mobj->tracer->z);
+	else if (mobj->ceilingz < mobj->z)
+		dist = (mobj->ceilingz - mobj->tracer->z);
+	else
+		return;
+
+	if ((dist = FixedDiv(dist, v[2])) > FRACUNIT)
+		return;
+
+	radius = FixedMul(radius, dist);
+	donetwice = true;
+	dist = ((mobj->info->speed) ? mobj->info->speed : mobjinfo[MT_SMALLMACECHAIN].speed);
+	goto maceretry;
+}
+
 static boolean P_ShieldLook(mobj_t *thing, shieldtype_t shield)
 {
 	if (!thing->target || thing->target->health <= 0 || !thing->target->player
@@ -6626,6 +6748,13 @@ void P_MobjThinker(mobj_t *mobj)
 		// fade out when nearing the end of fuse...
 		mobj->frame = (mobj->frame & ~FF_TRANSMASK) | (((NUMTRANSMAPS-1) - mobj->fuse / 2) << FF_TRANSSHIFT);
 
+	if (mobj->flags2 & MF2_MACEROTATE)
+	{
+		P_MaceRotate(mobj);
+		if (P_MobjWasRemoved(mobj))
+			return;
+	}
+
 	// Special thinker for scenery objects
 	if (mobj->flags & MF_SCENERY)
 	{
@@ -7291,7 +7420,8 @@ void P_MobjThinker(mobj_t *mobj)
 				}
 			}
 			break;
-		case MT_SPINMACEPOINT:
+		case MT_CHAINPOINT:
+		case MT_CHAINMACEPOINT:
 			if (leveltime & 1)
 			{
 				if (mobj->lastlook > mobj->movecount)
@@ -9635,15 +9765,20 @@ void P_SpawnMapThing(mapthing_t *mthing)
 		mobj->movedir = mthing->extrainfo;
 		break;
 	case MT_MACEPOINT:
-	case MT_SWINGMACEPOINT:
-	case MT_HANGMACEPOINT:
-	case MT_SPINMACEPOINT:
-	{
-		fixed_t mlength, mspeed, mxspeed, mzspeed, mstartangle, mmaxspeed;
-		mobjtype_t chainlink = MT_SMALLMACECHAIN;
-		mobjtype_t macetype = MT_SMALLMACE;
-		boolean firsttime;
+	case MT_CHAINMACEPOINT:
+	case MT_SPRINGBALLPOINT:
+	case MT_CHAINPOINT:
+	case MT_FIREBARPOINT:
+	case MT_CUSTOMMACEPOINT:
+	{
+		fixed_t mlength, mlengthset, mspeed, mphase, myaw, mpitch, mmaxspeed, mnumspokes, mnumspokesset, mpinch, mroll, mnumnospokes, mwidth, mmin, msound, radiusfactor;
+		angle_t mspokeangle;
+		mobjtype_t chainlink, macetype, firsttype, linktype;
+		boolean mdoall = true;
 		mobj_t *spawnee;
+		mobjflag_t mflagsapply;
+		mobjflag2_t mflags2apply;
+		mobjeflag_t meflagsapply;
 		INT32 line;
 		const size_t mthingi = (size_t)(mthing - mapthings);
 
@@ -9657,93 +9792,240 @@ void P_SpawnMapThing(mapthing_t *mthing)
 			return;
 		}
 /*
-No deaf - small mace
-Deaf - big mace
-
-ML_NOCLIMB : Direction not controllable
+mapthing -
+MTF_AMBUSH :
+	MT_SPRINGBALLPOINT - upgrade from yellow to red spring
+	anything else - bigger mace/chain theory
+MTF_OBJECTSPECIAL - force silent
+MTF_GRAVFLIP - flips objects, doesn't affect chain arrangements
+Parameter value : number of "spokes"
+
+linedef -
+ML_NOCLIMB :
+	MT_CHAINPOINT/MT_CHAINMACEPOINT with ML_EFFECT1 applied - Direction not controllable
+	anything else - no functionality
+ML_EFFECT1 : Swings instead of spins
+ML_EFFECT2 : Linktype is replaced with macetype for all spokes not ending in chains (inverted for MT_FIREBARPOINT)
+ML_EFFECT3 : Spawn a bonus macetype at the hinge point
+ML_EFFECT4 : Don't clip inside the ground
 */
 		mlength = abs(lines[line].dx >> FRACBITS);
-		mspeed = abs(lines[line].dy >> FRACBITS);
-		mxspeed = sides[lines[line].sidenum[0]].textureoffset >> FRACBITS;
-		mzspeed = sides[lines[line].sidenum[0]].rowoffset >> FRACBITS;
-		mstartangle = lines[line].frontsector->floorheight >> FRACBITS;
-		mmaxspeed = lines[line].frontsector->ceilingheight >> FRACBITS;
+		mspeed = abs(lines[line].dy >> (FRACBITS - 4));
+		mphase = (sides[lines[line].sidenum[0]].textureoffset >> FRACBITS) % 360;
+		if ((mmaxspeed = sides[lines[line].sidenum[0]].rowoffset >> (FRACBITS - 4)) < mspeed)
+			mmaxspeed = mspeed << 1;
+		mpitch = (lines[line].frontsector->floorheight >> FRACBITS) % 360;
+		myaw = (lines[line].frontsector->ceilingheight >> FRACBITS) % 360;
+
+		mnumspokes = mthing->extrainfo + 1;
+		mspokeangle = FixedAngle((360*FRACUNIT)/mnumspokes)>>ANGLETOFINESHIFT;
 
-		mstartangle %= 360;
-		mxspeed %= 360;
-		mzspeed %= 360;
+		if (lines[line].backsector)
+		{
+			mpinch = (lines[line].backsector->floorheight >> FRACBITS) % 360;
+			mroll = (lines[line].backsector->ceilingheight >> FRACBITS) % 360;
+			mnumnospokes = (sides[lines[line].sidenum[1]].textureoffset >> FRACBITS);
+			if ((mwidth = sides[lines[line].sidenum[1]].rowoffset >> FRACBITS) < 0)
+				mwidth = 0;
+		}
+		else
+			mpinch = mroll = mnumnospokes = mwidth = 0;
 
-		CONS_Debug(DBG_GAMELOGIC, "Mace Chain (mapthing #%s):\n"
+		CONS_Debug(DBG_GAMELOGIC, "Mace/Chain (mapthing #%s):\n"
 				"Length is %d\n"
 				"Speed is %d\n"
-				"Xspeed is %d\n"
-				"Zspeed is %d\n"
-				"startangle is %d\n"
-				"maxspeed is %d\n",
-				sizeu1(mthingi), mlength, mspeed, mxspeed, mzspeed, mstartangle, mmaxspeed);
+				"Phase is %d\n"
+				"Yaw is %d\n"
+				"Pitch is %d\n"
+				"Max. speed is %d\n"
+				"No. of spokes is %d\n"
+				"Pinch is %d\n"
+				"Roll is %d\n"
+				"No. of antispokes is %d\n"
+				"Width is %d\n",
+				sizeu1(mthingi), mlength, mspeed, mphase, myaw, mpitch, mmaxspeed, mnumspokes, mpinch, mroll, mnumnospokes, mwidth);
+
+		if (mnumnospokes > 0 && (mnumnospokes < mnumspokes))
+			mnumnospokes = mnumspokes/mnumnospokes;
+		else
+			mnumnospokes = ((mobj->type == MT_CHAINMACEPOINT) ? (mnumspokes - 1) : 0);
 
-		mobj->lastlook = mspeed << 4;
+		mobj->lastlook = mspeed;
 		mobj->movecount = mobj->lastlook;
-		mobj->health = (FixedAngle(mzspeed*FRACUNIT)>>ANGLETOFINESHIFT) + (FixedAngle(mstartangle*FRACUNIT)>>ANGLETOFINESHIFT);
-		mobj->threshold = (FixedAngle(mxspeed*FRACUNIT)>>ANGLETOFINESHIFT) + (FixedAngle(mstartangle*FRACUNIT)>>ANGLETOFINESHIFT);
-		mobj->movefactor = mobj->threshold;
+		mobj->health = (FixedAngle(myaw*FRACUNIT)>>ANGLETOFINESHIFT);
+		mobj->threshold = (FixedAngle(mpitch*FRACUNIT)>>ANGLETOFINESHIFT);
 		mobj->friction = mmaxspeed;
+		mobj->movefactor = mpinch;
 
-		if (lines[line].flags & ML_NOCLIMB)
-			mobj->flags |= MF_SLIDEME;
+		// Mobjtype selection
+		switch(mobj->type)
+		{
+			case MT_SPRINGBALLPOINT:
+				macetype = ((mthing->options & MTF_AMBUSH)
+					? MT_REDSPRINGBALL
+					: MT_YELLOWSPRINGBALL);
+				chainlink = MT_SMALLMACECHAIN;
+				break;
+			case MT_FIREBARPOINT:
+				macetype = ((mthing->options & MTF_AMBUSH)
+						? MT_BIGFIREBAR
+						: MT_SMALLFIREBAR);
+				chainlink = MT_NULL;
+				break;
+			case MT_CUSTOMMACEPOINT:
+				macetype = (mobjtype_t)sides[lines[line].sidenum[0]].toptexture;
+				if (lines[line].backsector)
+					chainlink = (mobjtype_t)sides[lines[line].sidenum[1]].toptexture;
+				else
+					chainlink = MT_NULL;
+				break;
+			default:
+				if (mthing->options & MTF_AMBUSH)
+				{
+					macetype = MT_BIGMACE;
+					chainlink = MT_BIGMACECHAIN;
+				}
+				else
+				{
+					macetype = MT_SMALLMACE;
+					chainlink = MT_SMALLMACECHAIN;
+				}
+				break;
+		}
 
-		mobj->reactiontime = 0;
+		if (!macetype)
+			break;
 
-		if (mthing->options & MTF_AMBUSH)
+		if (mobj->type != MT_CHAINPOINT)
+		{
+			firsttype = macetype;
+			mlength++;
+		}
+		else
 		{
-			chainlink = MT_BIGMACECHAIN;
-			macetype = MT_BIGMACE;
+			if (!mlength)
+				break;
+			firsttype = chainlink;
 		}
 
-		if (mthing->options & MTF_OBJECTSPECIAL)
-			mobj->flags2 |= MF2_BOSSNOTRAP; // shut up maces.
+		// Adjustable direction
+		if (lines[line].flags & ML_NOCLIMB)
+			mobj->flags |= MF_SLIDEME;
 
-		if (mobj->type == MT_HANGMACEPOINT || mobj->type == MT_SPINMACEPOINT)
-			firsttime = true;
+		// Swinging
+		if (lines[line].flags & ML_EFFECT1)
+		{
+			mobj->flags2 |= MF2_STRONGBOX;
+			mmin = ((mnumnospokes > 1) ? 1 : 0);
+		}
 		else
+			mmin = mnumspokes;
+
+		// Make the links the same type as the end - repeated below
+		if ((mobj->type != MT_CHAINPOINT) && (!(lines[line].flags & ML_EFFECT2) == (mobj->type == MT_FIREBARPOINT))) // exclusive or
 		{
-			firsttime = false;
+			linktype = macetype;
+			radiusfactor = 2; // Double the radius.
+		}
+		else
+			radiusfactor = (((linktype = chainlink) == MT_NULL) ? 2 : 1);
 
-			spawnee = P_SpawnMobj(mobj->x, mobj->y, mobj->z, macetype);
-			P_SetTarget(&spawnee->target, mobj);
+		mflagsapply = ((lines[line].flags & ML_EFFECT4) ? 0 : (MF_NOCLIP|MF_NOCLIPHEIGHT));
+		mflags2apply = (MF2_MACEROTATE|((mthing->options & MTF_OBJECTFLIP) ? MF2_OBJECTFLIP : 0));
+		meflagsapply = ((mthing->options & MTF_OBJECTFLIP) ? MFE_VERTICALFLIP : 0);
 
-			if (mobj->type == MT_SWINGMACEPOINT)
-				spawnee->movecount = FixedAngle(mstartangle*FRACUNIT)>>ANGLETOFINESHIFT;
-			else
-				spawnee->movecount = 0;
+		msound = ((firsttype == chainlink) ? 0 : (mwidth & 1));
 
-			spawnee->threshold = FixedAngle(mstartangle*FRACUNIT)>>ANGLETOFINESHIFT;
-			spawnee->reactiontime = mlength+1;
-		}
+		// Quick and easy preparatory variable setting
+		mphase = (FixedAngle(mphase*FRACUNIT)>>ANGLETOFINESHIFT);
+		mroll = (FixedAngle(mroll*FRACUNIT)>>ANGLETOFINESHIFT);
+
+#define makemace(mobjtype, dist, moreflags2) P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobjtype);\
+				P_SetTarget(&spawnee->tracer, mobj);\
+				spawnee->threshold = mphase;\
+				spawnee->friction = mroll;\
+				spawnee->movefactor = mwidth;\
+				spawnee->movecount = dist;\
+				spawnee->angle = myaw;\
+				spawnee->flags |= (MF_NOGRAVITY|mflagsapply);\
+				spawnee->flags2 |= (mflags2apply|moreflags2);\
+				spawnee->eflags |= meflagsapply
+
+domaceagain:
+		mnumspokesset = mnumspokes;
 
-		while (mlength > 0)
+		if (mdoall && lines[line].flags & ML_EFFECT3) // Innermost mace/link
+			{ spawnee = makemace(macetype, 0, MF2_AMBUSH); }
+
+		// The actual spawning of spokes
+		while (mnumspokesset-- > 0)
 		{
-			spawnee = P_SpawnMobj(mobj->x, mobj->y, mobj->z, chainlink);
+			// Offsets
+			if (lines[line].flags & ML_EFFECT1) // Swinging
+				mroll = (mroll - mspokeangle) & FINEMASK;
+			else // Spinning
+				mphase = (mphase - mspokeangle) & FINEMASK;
 
-			P_SetTarget(&spawnee->target, mobj);
+			if (mnumnospokes && !(mnumspokesset % mnumnospokes)) // Skipping a "missing" spoke
+			{
+				if (mobj->type != MT_CHAINMACEPOINT)
+					continue;
 
-			if (mobj->type == MT_HANGMACEPOINT || mobj->type == MT_SWINGMACEPOINT)
-				spawnee->movecount = FixedAngle(mstartangle*FRACUNIT)>>ANGLETOFINESHIFT;
+				firsttype = linktype = chainlink;
+				mlengthset = 1 + (mlength - 1)*radiusfactor;
+				radiusfactor = 1;
+			}
 			else
-				spawnee->movecount = 0;
+			{
+				if (mobj->type == MT_CHAINMACEPOINT)
+				{
+					// Make the links the same type as the end - repeated above
+					if (lines[line].flags & ML_EFFECT2)
+					{
+						linktype = macetype;
+						radiusfactor = 2;
+					}
+					else
+					{
+						linktype = chainlink;
+						radiusfactor = (((linktype = chainlink) == MT_NULL) ? 2 : 1);
+					}
 
-			spawnee->threshold = FixedAngle(mstartangle*FRACUNIT)>>ANGLETOFINESHIFT;
-			spawnee->reactiontime = mlength;
+					firsttype = macetype;
+				}
 
-			if (firsttime)
-			{
-				// This is the outermost link in the chain
-				spawnee->flags2 |= MF2_AMBUSH;
-				firsttime = false;
+				mlengthset = mlength;
 			}
 
-			mlength--;
+			// Outermost mace/link
+			spawnee = makemace(firsttype, radiusfactor*(mlengthset--), MF2_AMBUSH);
+
+			if (mspeed && (mwidth == msound) && !(mthing->options & MTF_OBJECTSPECIAL) && mnumspokesset <= mmin) // Can it make a sound?
+				spawnee->flags2 |= MF2_BOSSNOTRAP;
+
+			if (!mdoall || !linktype)
+				continue;
+
+			// The rest of the links
+			while (mlengthset > 0)
+				{ spawnee = makemace(linktype, radiusfactor*(mlengthset--), 0); }
 		}
+
+		if (mwidth > 0)
+		{
+			mwidth *= -1;
+			goto domaceagain;
+		}
+		else if (mwidth != 0)
+		{
+			if ((mwidth = -(mwidth + ((firsttype == chainlink) ? 1 : 2))) < 0)
+				break;
+			mdoall = false;
+			goto domaceagain;
+		}
+
+#undef makemace
+
 		break;
 	}
 	case MT_PARTICLEGEN:
@@ -11116,4 +11398,4 @@ mobj_t *P_SpawnMobjFromMobj(mobj_t *mobj, fixed_t xofs, fixed_t yofs, fixed_t zo
 	newmobj->destscale = mobj->destscale;
 	P_SetScale(newmobj, mobj->scale);
 	return newmobj;
-}
+}
\ No newline at end of file
diff --git a/src/p_mobj.h b/src/p_mobj.h
index f6ebd3cadc497465312a139860ee2975b74619cc..c6e1bfbf2317b3f6a4f2f83f42fe04f9e63102fb 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -194,6 +194,7 @@ typedef enum
 	MF2_AMBUSH         = 1<<27, // Alternate behaviour typically set by MTF_AMBUSH
 	MF2_LINKDRAW       = 1<<28, // Draw vissprite of mobj immediately before/after tracer's vissprite (dependent on dispoffset and position)
 	MF2_SHIELD         = 1<<29, // Thinker calls P_AddShield/P_ShieldLook (must be partnered with MF_SCENERY to use)
+	MF2_MACEROTATE     = 1<<30, // Thinker calls P_MaceRotate around tracer
 	// free: to and including 1<<31
 } mobjflag2_t;
 
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 3853dc7e63826c08a337de65aeea322a9aba6b94..2d3412e65736115c07122edb3b0dddb80328ed8f 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -3159,7 +3159,8 @@ static inline void P_ArchiveMisc(void)
 	else
 		WRITEINT16(save_p, gamemap);
 
-	lastmapsaved = gamemap;
+	//lastmapsaved = gamemap;
+	lastmaploaded = gamemap;
 
 	WRITEUINT16(save_p, (botskin ? (emeralds|(1<<10)) : emeralds)+357);
 	WRITESTRINGN(save_p, timeattackfolder, sizeof(timeattackfolder));
@@ -3184,7 +3185,8 @@ static inline void P_UnArchiveSPGame(INT16 mapoverride)
 	if(!mapheaderinfo[gamemap-1])
 		P_AllocMapHeader(gamemap-1);
 
-	lastmapsaved = gamemap;
+	//lastmapsaved = gamemap;
+	lastmaploaded = gamemap;
 
 	tokenlist = 0;
 	token = 0;
diff --git a/src/p_setup.c b/src/p_setup.c
index 8a91de76bb57e4b3a2654901682263eb41ed432a..529589bec6c6c0546b79c99078b927a18a597db9 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -489,6 +489,7 @@ static void P_LoadSegs(lumpnum_t lumpnum)
 			//Hurdler: 04/12/2000: for now, only used in hardware mode
 			li->lightmaps = NULL; // list of static lightmap for this seg
 		}
+		li->pv1 = li->pv2 = NULL;
 #endif
 
 		li->angle = (SHORT(ml->angle))<<FRACBITS;
@@ -1593,6 +1594,7 @@ static void P_LoadSideDefs2(lumpnum_t lumpnum)
 				break;
 			}
 
+			case 9: // Mace parameters
 			case 14: // Bustable block parameters
 			case 15: // Fan particle spawner parameters
 			case 425: // Calls P_SetMobjState on calling mobj
@@ -2571,6 +2573,21 @@ static void P_LoadNightsGhosts(void)
 	free(gpath);
 }
 
+static boolean CanSaveLevel(INT32 mapnum)
+{
+	if (ultimatemode) // never save in ultimate (probably redundant with cursaveslot also being checked)
+		return false;
+
+	if (G_IsSpecialStage(mapnum) // don't save in special stages
+		|| mapnum == lastmaploaded) // don't save if the last map loaded was this one
+		return false;
+	
+	// Any levels that have the savegame flag can save normally.
+	// If the game is complete for this save slot, then any level can save!
+	// On the other side of the spectrum, if lastmaploaded is 0, then the save file has only just been created and needs to save ASAP!
+	return (mapheaderinfo[mapnum-1]->levelflags & LF_SAVEGAME || gamecomplete || !lastmaploaded);
+}
+
 /** Loads a level from a lump or external wad.
   *
   * \param skipprecip If true, don't spawn precipitation.
@@ -3034,11 +3051,11 @@ boolean P_SetupLevel(boolean skipprecip)
 	P_RunCachedActions();
 
 	if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking || players[consoleplayer].lives <= 0)
-		&& (!modifiedgame || savemoddata) && cursaveslot >= 0 && !ultimatemode
-		&& !(mapheaderinfo[gamemap-1]->menuflags & LF2_HIDEINMENU)
-		&& (!G_IsSpecialStage(gamemap)) && gamemap != lastmapsaved && (mapheaderinfo[gamemap-1]->actnum < 2 || gamecomplete))
+		&& (!modifiedgame || savemoddata) && cursaveslot >= 0 && CanSaveLevel(gamemap))
 		G_SaveGame((UINT32)cursaveslot);
 
+	lastmaploaded = gamemap; // HAS to be set after saving!!
+
 	if (savedata.lives > 0)
 	{
 		players[consoleplayer].continues = savedata.continues;
diff --git a/src/p_user.c b/src/p_user.c
index afa0ee960c940fb7108620121b5f59f3a2f24e65..a2c8c9279a14f19af242170f18b1aeab6a5651d9 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -10107,7 +10107,7 @@ void P_PlayerAfterThink(player_t *player)
 			}
 		}
 	}
-	else if (player->powers[pw_carry] == CR_MACESPIN && player->mo->tracer && player->mo->tracer->target)
+	else if (player->powers[pw_carry] == CR_MACESPIN && player->mo->tracer && player->mo->tracer->tracer)
 	{
 		player->mo->height = P_GetPlayerSpinHeight(player);
 		// tracer is what you're hanging onto....
@@ -10123,14 +10123,20 @@ void P_PlayerAfterThink(player_t *player)
 			player->pflags &= ~PF_THOKKED;
 
 			if (cmd->forwardmove > 0)
-				player->mo->tracer->target->lastlook += 2;
-			else if (cmd->forwardmove < 0 && player->mo->tracer->target->lastlook > player->mo->tracer->target->movecount)
-				player->mo->tracer->target->lastlook -= 2;
+			{
+				if ((player->mo->tracer->tracer->lastlook += 2) > player->mo->tracer->tracer->friction)
+					player->mo->tracer->tracer->lastlook = player->mo->tracer->tracer->friction;
+			}
+			else if (cmd->forwardmove < 0)
+			{
+				if ((player->mo->tracer->tracer->lastlook -= 2) < player->mo->tracer->tracer->movecount)
+					player->mo->tracer->tracer->lastlook = player->mo->tracer->tracer->movecount;
+			}
 
-			if (!(player->mo->tracer->target->flags & MF_SLIDEME) // Noclimb on chain parameters gives this
+			if ((player->mo->tracer->tracer->flags & MF_SLIDEME) // Noclimb on chain parameters gives this
 			&& !(twodlevel || player->mo->flags2 & MF2_TWOD)) // why on earth would you want to turn them in 2D mode?
 			{
-				player->mo->tracer->target->health += cmd->sidemove;
+				player->mo->tracer->tracer->health += cmd->sidemove;
 				player->mo->angle += cmd->sidemove<<ANGLETOFINESHIFT; // 2048 --> ANGLE_MAX
 
 				if (!demoplayback || P_AnalogMove(player))
diff --git a/src/r_bsp.c b/src/r_bsp.c
index abb11204a60233a8e59faf5f6618fc4fe09938b0..4ce89f009154b3145a96fd1f8ef63b87b6bab4fd 100644
--- a/src/r_bsp.c
+++ b/src/r_bsp.c
@@ -365,6 +365,36 @@ sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, INT32 *floorlightlevel,
 	return sec;
 }
 
+boolean R_IsEmptyLine(seg_t *line, sector_t *front, sector_t *back)
+{
+	return (
+#ifdef POLYOBJECTS
+		!line->polyseg &&
+#endif
+		back->ceilingpic == front->ceilingpic
+		&& back->floorpic == front->floorpic
+#ifdef ESLOPE
+		&& back->f_slope == front->f_slope
+		&& back->c_slope == front->c_slope
+#endif
+		&& back->lightlevel == front->lightlevel
+		&& !line->sidedef->midtexture
+		// Check offsets too!
+		&& back->floor_xoffs == front->floor_xoffs
+		&& back->floor_yoffs == front->floor_yoffs
+		&& back->floorpic_angle == front->floorpic_angle
+		&& back->ceiling_xoffs == front->ceiling_xoffs
+		&& back->ceiling_yoffs == front->ceiling_yoffs
+		&& back->ceilingpic_angle == front->ceilingpic_angle
+		// Consider altered lighting.
+		&& back->floorlightsec == front->floorlightsec
+		&& back->ceilinglightsec == front->ceilinglightsec
+		// Consider colormaps
+		&& back->extra_colormap == front->extra_colormap
+		&& ((!front->ffloors && !back->ffloors)
+		|| front->tag == back->tag));
+}
+
 //
 // R_AddLine
 // Clips the given segment and adds any visible pieces to the line list.
@@ -526,36 +556,8 @@ static void R_AddLine(seg_t *line)
 	// Identical floor and ceiling on both sides, identical light levels on both sides,
 	// and no middle texture.
 
-	if (
-#ifdef POLYOBJECTS
-		!line->polyseg &&
-#endif
-		backsector->ceilingpic == frontsector->ceilingpic
-		&& backsector->floorpic == frontsector->floorpic
-#ifdef ESLOPE
-		&& backsector->f_slope == frontsector->f_slope
-		&& backsector->c_slope == frontsector->c_slope
-#endif
-		&& backsector->lightlevel == frontsector->lightlevel
-		&& !curline->sidedef->midtexture
-		// Check offsets too!
-		&& backsector->floor_xoffs == frontsector->floor_xoffs
-		&& backsector->floor_yoffs == frontsector->floor_yoffs
-		&& backsector->floorpic_angle == frontsector->floorpic_angle
-		&& backsector->ceiling_xoffs == frontsector->ceiling_xoffs
-		&& backsector->ceiling_yoffs == frontsector->ceiling_yoffs
-		&& backsector->ceilingpic_angle == frontsector->ceilingpic_angle
-		// Consider altered lighting.
-		&& backsector->floorlightsec == frontsector->floorlightsec
-		&& backsector->ceilinglightsec == frontsector->ceilinglightsec
-		// Consider colormaps
-		&& backsector->extra_colormap == frontsector->extra_colormap
-		&& ((!frontsector->ffloors && !backsector->ffloors)
-		|| frontsector->tag == backsector->tag))
-	{
+	if (R_IsEmptyLine(line, frontsector, backsector))
 		return;
-	}
-
 
 clippass:
 	R_ClipPassWallSegment(x1, x2 - 1);
diff --git a/src/r_bsp.h b/src/r_bsp.h
index e871b5dde5c6ae704b5d772d06371f94e62ad361..80824831b8511fe1f6b02b22b66213bb82e26b31 100644
--- a/src/r_bsp.h
+++ b/src/r_bsp.h
@@ -50,6 +50,7 @@ extern polyobj_t **po_ptrs; // temp ptr array to sort polyobject pointers
 
 sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, INT32 *floorlightlevel,
 	INT32 *ceilinglightlevel, boolean back);
+boolean R_IsEmptyLine(seg_t *line, sector_t *front, sector_t *back);
 
 INT32 R_GetPlaneLight(sector_t *sector, fixed_t planeheight, boolean underside);
 void R_Prep3DFloors(sector_t *sector);
diff --git a/src/r_defs.h b/src/r_defs.h
index 32be7c4c8d360a6de6bbef0dbbff78fdf9175861..7744a12d8e93c81279842c9fc4ee5e514215b750 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -574,6 +574,9 @@ typedef struct seg_s
 	sector_t *backsector;
 
 #ifdef HWRENDER
+	// new pointers so that AdjustSegs doesn't mess with v1/v2
+	void *pv1; // polyvertex_t
+	void *pv2; // polyvertex_t
 	float flength; // length of the seg, used by hardware renderer
 
 	lightmap_t *lightmaps; // for static lightmap
diff --git a/src/v_video.c b/src/v_video.c
index f7a5c2841a11f52c45447d3d4bb8a58f3b6301bb..6ac101f2d6474f6464c787bbafe0b00caa546292 100644
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -865,11 +865,7 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_
 //
 void V_DrawContinueIcon(INT32 x, INT32 y, INT32 flags, INT32 skinnum, UINT8 skincolor)
 {
-	if (skins[skinnum].flags & SF_HIRES
-#ifdef HWRENDER
-//	|| (rendermode != render_soft && rendermode != render_none)
-#endif
-	)
+	if (skins[skinnum].flags & SF_HIRES)
 		V_DrawScaledPatch(x - 10, y - 14, flags, W_CachePatchName("CONTINS", PU_CACHE));
 	else
 	{