diff --git a/debian/docs b/debian/docs
index f3d4ef20e5a4ce3dd58bd8cf313bd95fcd7ec564..b43bf86b50fd8d3529a0dc062c30006ed38f309e 100644
--- a/debian/docs
+++ b/debian/docs
@@ -1,2 +1 @@
-readme.txt
-readme.txt
+README.md
diff --git a/src/Makefile b/src/Makefile
index ce4b569eebd7dcc94cfc8858daea7bf4f38dd287..493796820a8edb2f26d296798485bda6d51a1ab9 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -179,6 +179,9 @@ endif
 
 ifdef LINUX
 UNIXCOMMON=1
+ifndef NOGME
+HAVE_LIBGME=1
+endif
 endif
 
 ifdef SOLARIS
@@ -315,6 +318,13 @@ LIBS+=$(PNG_LDFLAGS)
 CFLAGS+=$(PNG_CFLAGS)
 endif
 
+ZLIB_PKGCONFIG?=zlib
+ZLIB_CFLAGS?=$(shell $(PKG_CONFIG) $(ZLIB_PKGCONFIG) --cflags)
+ZLIB_LDFLAGS?=$(shell $(PKG_CONFIG) $(ZLIB_PKGCONFIG) --libs)
+
+LIBS+=$(ZLIB_LDFLAGS)
+CFLAGS+=$(ZLIB_CFLAGS)
+
 ifdef HAVE_LIBGME
 OPTS+=-DHAVE_LIBGME
 
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 4f73a25648db4c56d99f8fbb97e7c3e2773a7dc6..66c90f91996ff153da6747c26c00056ee12f3583 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -3960,7 +3960,7 @@ static void Command_Archivetest_f(void)
 	}
 
 	// assign mobjnum
-	i = 0;
+	i = 1;
 	for (th = thinkercap.next; th != &thinkercap; th = th->next)
 		if (th->function.acp1 == (actionf_p1)P_MobjThinker)
 			((mobj_t *)th)->mobjnum = i++;
@@ -4062,8 +4062,7 @@ static void Skin_OnChange(void)
 	if (!Playing())
 		return; // do whatever you want
 
-	if (!(cv_debug || devparm) && !(multiplayer || netgame) // In single player.
-		&& (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_CONTINUING))
+	if (!(cv_debug || devparm) && !(multiplayer || netgame)) // In single player.
 	{
 		CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name);
 		return;
diff --git a/src/lua_hook.h b/src/lua_hook.h
index 804d99e12e5728a49015829644b6b827ce649613..53e0a7d8e8bf2eb402ed50c62e89bb49704bdec8 100644
--- a/src/lua_hook.h
+++ b/src/lua_hook.h
@@ -60,7 +60,7 @@ UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which);
 #define LUAh_MobjMoveCollide(thing1, thing2) LUAh_MobjCollideHook(thing1, thing2, hook_MobjMoveCollide) // Hook for PIT_CheckThing by (tmthing) mobj type
 boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher); // Hook for P_TouchSpecialThing by mobj type
 #define LUAh_MobjFuse(mo) LUAh_MobjHook(mo, hook_MobjFuse) // Hook for mobj->fuse == 0 by mobj type
-#define LUAh_MobjThinker(mo) LUAh_MobjHook(mo, hook_MobjThinker) // Hook for P_MobjThinker or P_SceneryThinker by mobj type
+boolean LUAh_MobjThinker(mobj_t *mo); // Hook for P_MobjThinker or P_SceneryThinker by mobj type
 #define LUAh_BossThinker(mo) LUAh_MobjHook(mo, hook_BossThinker) // Hook for P_GenericBossThinker by mobj type
 UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage); // Hook for P_DamageMobj by mobj type (Should mobj take damage?)
 boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage); // Hook for P_DamageMobj by mobj type (Mobj actually takes damage!)
diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c
index 1b9652571164a9de45056cf62d1fc66958efa01f..a24473bad47b69fd9cf11608773357cd605efc19 100644
--- a/src/lua_hooklib.c
+++ b/src/lua_hooklib.c
@@ -74,12 +74,30 @@ typedef struct hook_s* hook_p;
 
 #define FMT_HOOKID "hook_%d"
 
+// For each mobj type, a linked list to its thinker and collision hooks.
+// That way, we don't have to iterate through all the hooks.
+// We could do that with all other mobj hooks, but it would probably just be
+// a waste of memory since they are only called occasionally. Probably...
+static hook_p mobjthinkerhooks[NUMMOBJTYPES];
+static hook_p mobjcollidehooks[NUMMOBJTYPES];
+
+// For each mobj type, a linked list for other mobj hooks
+static hook_p mobjhooks[NUMMOBJTYPES];
+
+// A linked list for player hooks
+static hook_p playerhooks;
+
+// A linked list for linedef executor hooks
+static hook_p linedefexecutorhooks;
+
+// For other hooks, a unique linked list
 hook_p roothook;
 
 // Takes hook, function, and additional arguments (mobj type to act on, etc.)
 static int lib_addHook(lua_State *L)
 {
 	static struct hook_s hook = {NULL, 0, 0, {0}, false};
+	static UINT32 nextid;
 	hook_p hookp, *lastp;
 
 	hook.type = luaL_checkoption(L, 1, NULL, hookNames);
@@ -109,6 +127,7 @@ static int lib_addHook(lua_State *L)
 		hook.s.mt = MT_NULL;
 		if (lua_isnumber(L, 2))
 			hook.s.mt = lua_tonumber(L, 2);
+		luaL_argcheck(L, hook.s.mt < NUMMOBJTYPES, 2, "invalid mobjtype_t");
 		break;
 	case hook_BotAI:
 		hook.s.skinname = NULL;
@@ -141,18 +160,49 @@ static int lib_addHook(lua_State *L)
 
 	hooksAvailable[hook.type/8] |= 1<<(hook.type%8);
 
-	// iterate the hook metadata structs
 	// set hook.id to the highest id + 1
-	// set lastp to the last hook struct's "next" pointer.
-	lastp = &roothook;
-	hook.id = 0;
-	for (hookp = roothook; hookp; hookp = hookp->next)
+	hook.id = nextid++;
+
+	// Special cases for some hook types (see the comments above mobjthinkerhooks declaration)
+	switch(hook.type)
 	{
-		if (hookp->id >= hook.id)
-			hook.id = hookp->id+1;
-		lastp = &hookp->next;
+	case hook_MobjThinker:
+		lastp = &mobjthinkerhooks[hook.s.mt];
+		break;
+	case hook_MobjCollide:
+	case hook_MobjMoveCollide:
+		lastp = &mobjcollidehooks[hook.s.mt];
+		break;
+	case hook_MobjSpawn:
+	case hook_TouchSpecial:
+	case hook_MobjFuse:
+	case hook_BossThinker:
+	case hook_ShouldDamage:
+	case hook_MobjDamage:
+	case hook_MobjDeath:
+	case hook_BossDeath:
+	case hook_MobjRemoved:
+		lastp = &mobjhooks[hook.s.mt];
+		break;
+	case hook_JumpSpecial:
+	case hook_AbilitySpecial:
+	case hook_SpinSpecial:
+	case hook_JumpSpinSpecial:
+	case hook_PlayerSpawn:
+		lastp = &playerhooks;
+		break;
+	case hook_LinedefExecute:
+		lastp = &linedefexecutorhooks;
+		break;
+	default:
+		lastp = &roothook;
+		break;
 	}
 
+	// iterate the hook metadata structs
+	// set lastp to the last hook struct's "next" pointer.
+	for (hookp = *lastp; hookp; hookp = hookp->next)
+		lastp = &hookp->next;
 	// allocate a permanent memory struct to stuff hook.
 	hookp = ZZ_Alloc(sizeof(struct hook_s));
 	memcpy(hookp, &hook, sizeof(struct hook_s));
@@ -183,9 +233,29 @@ boolean LUAh_MobjHook(mobj_t *mo, enum hook which)
 
 	lua_settop(gL, 0);
 
-	for (hookp = roothook; hookp; hookp = hookp->next)
-		if (hookp->type == which
-		&& (hookp->s.mt == MT_NULL || hookp->s.mt == mo->type))
+	// Look for all generic mobj hooks
+	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
+		if (hookp->type == which)
+		{
+			if (lua_gettop(gL) == 0)
+				LUA_PushUserdata(gL, mo, META_MOBJ);
+			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+			lua_gettable(gL, LUA_REGISTRYINDEX);
+			lua_pushvalue(gL, -2);
+			if (lua_pcall(gL, 1, 1, 0)) {
+				if (!hookp->error || cv_debug & DBG_LUA)
+					CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+				lua_pop(gL, 1);
+				hookp->error = true;
+				continue;
+			}
+			if (lua_toboolean(gL, -1))
+				hooked = true;
+			lua_pop(gL, 1);
+		}
+
+	for (hookp = mobjhooks[mo->type]; hookp; hookp = hookp->next)
+		if (hookp->type == which)
 		{
 			if (lua_gettop(gL) == 0)
 				LUA_PushUserdata(gL, mo, META_MOBJ);
@@ -217,7 +287,7 @@ boolean LUAh_PlayerHook(player_t *plr, enum hook which)
 
 	lua_settop(gL, 0);
 
-	for (hookp = roothook; hookp; hookp = hookp->next)
+	for (hookp = playerhooks; hookp; hookp = hookp->next)
 		if (hookp->type == which)
 		{
 			if (lua_gettop(gL) == 0)
@@ -338,9 +408,38 @@ UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which)
 
 	lua_settop(gL, 0);
 
-	for (hookp = roothook; hookp; hookp = hookp->next)
-		if (hookp->type == which
-		&& (hookp->s.mt == MT_NULL || hookp->s.mt == thing1->type))
+	// Look for all generic mobj collision hooks
+	for (hookp = mobjcollidehooks[MT_NULL]; hookp; hookp = hookp->next)
+		if (hookp->type == which)
+		{
+			if (lua_gettop(gL) == 0)
+			{
+				LUA_PushUserdata(gL, thing1, META_MOBJ);
+				LUA_PushUserdata(gL, thing2, META_MOBJ);
+			}
+			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+			lua_gettable(gL, LUA_REGISTRYINDEX);
+			lua_pushvalue(gL, -3);
+			lua_pushvalue(gL, -3);
+			if (lua_pcall(gL, 2, 1, 0)) {
+				if (!hookp->error || cv_debug & DBG_LUA)
+					CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+				lua_pop(gL, 1);
+				hookp->error = true;
+				continue;
+			}
+			if (!lua_isnil(gL, -1))
+			{ // if nil, leave shouldCollide = 0.
+				if (lua_toboolean(gL, -1))
+					shouldCollide = 1; // Force yes
+				else
+					shouldCollide = 2; // Force no
+			}
+			lua_pop(gL, 1);
+		}
+
+	for (hookp = mobjcollidehooks[thing1->type]; hookp; hookp = hookp->next)
+		if (hookp->type == which)
 		{
 			if (lua_gettop(gL) == 0)
 			{
@@ -372,6 +471,59 @@ UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which)
 	return shouldCollide;
 }
 
+// Hook for mobj thinkers
+boolean LUAh_MobjThinker(mobj_t *mo)
+{
+	hook_p hookp;
+	boolean hooked = false;
+	if (!gL || !(hooksAvailable[hook_MobjThinker/8] & (1<<(hook_MobjThinker%8))))
+		return false;
+
+	lua_settop(gL, 0);
+
+	// Look for all generic mobj thinker hooks
+	for (hookp = mobjthinkerhooks[MT_NULL]; hookp; hookp = hookp->next)
+	{
+		if (lua_gettop(gL) == 0)
+			LUA_PushUserdata(gL, mo, META_MOBJ);
+		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+		lua_gettable(gL, LUA_REGISTRYINDEX);
+		lua_pushvalue(gL, -2);
+		if (lua_pcall(gL, 1, 1, 0)) {
+			if (!hookp->error || cv_debug & DBG_LUA)
+				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+			lua_pop(gL, 1);
+			hookp->error = true;
+			continue;
+		}
+		if (lua_toboolean(gL, -1))
+			hooked = true;
+		lua_pop(gL, 1);
+	}
+
+	for (hookp = mobjthinkerhooks[mo->type]; hookp; hookp = hookp->next)
+	{
+		if (lua_gettop(gL) == 0)
+			LUA_PushUserdata(gL, mo, META_MOBJ);
+		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+		lua_gettable(gL, LUA_REGISTRYINDEX);
+		lua_pushvalue(gL, -2);
+		if (lua_pcall(gL, 1, 1, 0)) {
+			if (!hookp->error || cv_debug & DBG_LUA)
+				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+			lua_pop(gL, 1);
+			hookp->error = true;
+			continue;
+		}
+		if (lua_toboolean(gL, -1))
+			hooked = true;
+		lua_pop(gL, 1);
+	}
+
+	lua_settop(gL, 0);
+	return hooked;
+}
+
 // Hook for P_TouchSpecialThing by mobj type
 boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher)
 {
@@ -382,9 +534,33 @@ boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher)
 
 	lua_settop(gL, 0);
 
-	for (hookp = roothook; hookp; hookp = hookp->next)
-		if (hookp->type == hook_TouchSpecial
-		&& (hookp->s.mt == MT_NULL || hookp->s.mt == special->type))
+	// Look for all generic touch special hooks
+	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
+		if (hookp->type == hook_TouchSpecial)
+		{
+			if (lua_gettop(gL) == 0)
+			{
+				LUA_PushUserdata(gL, special, META_MOBJ);
+				LUA_PushUserdata(gL, toucher, META_MOBJ);
+			}
+			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+			lua_gettable(gL, LUA_REGISTRYINDEX);
+			lua_pushvalue(gL, -3);
+			lua_pushvalue(gL, -3);
+			if (lua_pcall(gL, 2, 1, 0)) {
+				if (!hookp->error || cv_debug & DBG_LUA)
+					CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+				lua_pop(gL, 1);
+				hookp->error = true;
+				continue;
+			}
+			if (lua_toboolean(gL, -1))
+				hooked = true;
+			lua_pop(gL, 1);
+		}
+
+	for (hookp = mobjhooks[special->type]; hookp; hookp = hookp->next)
+		if (hookp->type == hook_TouchSpecial)
 		{
 			if (lua_gettop(gL) == 0)
 			{
@@ -421,9 +597,42 @@ UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
 
 	lua_settop(gL, 0);
 
-	for (hookp = roothook; hookp; hookp = hookp->next)
-		if (hookp->type == hook_ShouldDamage
-		&& (hookp->s.mt == MT_NULL || hookp->s.mt == target->type))
+	// Look for all generic should damage hooks
+	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
+		if (hookp->type == hook_ShouldDamage)
+		{
+			if (lua_gettop(gL) == 0)
+			{
+				LUA_PushUserdata(gL, target, META_MOBJ);
+				LUA_PushUserdata(gL, inflictor, META_MOBJ);
+				LUA_PushUserdata(gL, source, META_MOBJ);
+				lua_pushinteger(gL, damage);
+			}
+			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+			lua_gettable(gL, LUA_REGISTRYINDEX);
+			lua_pushvalue(gL, -5);
+			lua_pushvalue(gL, -5);
+			lua_pushvalue(gL, -5);
+			lua_pushvalue(gL, -5);
+			if (lua_pcall(gL, 4, 1, 0)) {
+				if (!hookp->error || cv_debug & DBG_LUA)
+					CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+				lua_pop(gL, 1);
+				hookp->error = true;
+				continue;
+			}
+			if (!lua_isnil(gL, -1))
+			{
+				if (lua_toboolean(gL, -1))
+					shouldDamage = 1; // Force yes
+				else
+					shouldDamage = 2; // Force no
+			}
+			lua_pop(gL, 1);
+		}
+
+	for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next)
+		if (hookp->type == hook_ShouldDamage)
 		{
 			if (lua_gettop(gL) == 0)
 			{
@@ -469,9 +678,37 @@ boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
 
 	lua_settop(gL, 0);
 
-	for (hookp = roothook; hookp; hookp = hookp->next)
-		if (hookp->type == hook_MobjDamage
-		&& (hookp->s.mt == MT_NULL || hookp->s.mt == target->type))
+	// Look for all generic mobj damage hooks
+	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
+		if (hookp->type == hook_MobjDamage)
+		{
+			if (lua_gettop(gL) == 0)
+			{
+				LUA_PushUserdata(gL, target, META_MOBJ);
+				LUA_PushUserdata(gL, inflictor, META_MOBJ);
+				LUA_PushUserdata(gL, source, META_MOBJ);
+				lua_pushinteger(gL, damage);
+			}
+			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+			lua_gettable(gL, LUA_REGISTRYINDEX);
+			lua_pushvalue(gL, -5);
+			lua_pushvalue(gL, -5);
+			lua_pushvalue(gL, -5);
+			lua_pushvalue(gL, -5);
+			if (lua_pcall(gL, 4, 1, 0)) {
+				if (!hookp->error || cv_debug & DBG_LUA)
+					CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+				lua_pop(gL, 1);
+				hookp->error = true;
+				continue;
+			}
+			if (lua_toboolean(gL, -1))
+				hooked = true;
+			lua_pop(gL, 1);
+		}
+
+	for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next)
+		if (hookp->type == hook_MobjDamage)
 		{
 			if (lua_gettop(gL) == 0)
 			{
@@ -512,9 +749,35 @@ boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source)
 
 	lua_settop(gL, 0);
 
-	for (hookp = roothook; hookp; hookp = hookp->next)
-		if (hookp->type == hook_MobjDeath
-		&& (hookp->s.mt == MT_NULL || hookp->s.mt == target->type))
+	// Look for all generic mobj death hooks
+	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
+		if (hookp->type == hook_MobjDeath)
+		{
+			if (lua_gettop(gL) == 0)
+			{
+				LUA_PushUserdata(gL, target, META_MOBJ);
+				LUA_PushUserdata(gL, inflictor, META_MOBJ);
+				LUA_PushUserdata(gL, source, META_MOBJ);
+			}
+			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+			lua_gettable(gL, LUA_REGISTRYINDEX);
+			lua_pushvalue(gL, -4);
+			lua_pushvalue(gL, -4);
+			lua_pushvalue(gL, -4);
+			if (lua_pcall(gL, 3, 1, 0)) {
+				if (!hookp->error || cv_debug & DBG_LUA)
+					CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+				lua_pop(gL, 1);
+				hookp->error = true;
+				continue;
+			}
+			if (lua_toboolean(gL, -1))
+				hooked = true;
+			lua_pop(gL, 1);
+		}
+
+	for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next)
+		if (hookp->type == hook_MobjDeath)
 		{
 			if (lua_gettop(gL) == 0)
 			{
@@ -652,9 +915,8 @@ boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector)
 
 	lua_settop(gL, 0);
 
-	for (hookp = roothook; hookp; hookp = hookp->next)
-		if (hookp->type == hook_LinedefExecute
-		&& !strcmp(hookp->s.funcname, line->text))
+	for (hookp = linedefexecutorhooks; hookp; hookp = hookp->next)
+		if (!strcmp(hookp->s.funcname, line->text))
 		{
 			if (lua_gettop(gL) == 0)
 			{
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 5e457ca3ab30a62f354ff908f0cbb29bcf7eda6c..14386ff15a96fdc60bb5f7b3a13734674a01d6bb 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -3285,7 +3285,7 @@ void P_SaveNetGame(void)
 {
 	thinker_t *th;
 	mobj_t *mobj;
-	INT32 i = 0;
+	INT32 i = 1; // don't start from 0, it'd be confused with a blank pointer otherwise
 
 	CV_SaveNetVars(&save_p);
 	P_NetArchiveMisc();
diff --git a/src/p_user.c b/src/p_user.c
index 56ecdf804296329c173ac4c5fb5ddccb0d8ec941..5ea1ae47194bcb084d0483fdf1884ed0542453ac 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -7100,7 +7100,6 @@ static void P_DoZoomTube(player_t *player)
 	mobj_t *waypoint = NULL;
 	fixed_t dist;
 	boolean reverse;
-	fixed_t speedx,speedy,speedz;
 
 	player->mo->height = P_GetPlayerSpinHeight(player);
 
@@ -7121,17 +7120,17 @@ static void P_DoZoomTube(player_t *player)
 	if (dist < 1)
 		dist = 1;
 
-	speedx = FixedMul(FixedDiv(player->mo->tracer->x - player->mo->x, dist), (speed));
-	speedy = FixedMul(FixedDiv(player->mo->tracer->y - player->mo->y, dist), (speed));
-	speedz = FixedMul(FixedDiv(player->mo->tracer->z - player->mo->z, dist), (speed));
+	player->mo->momx = FixedMul(FixedDiv(player->mo->tracer->x - player->mo->x, dist), (speed));
+	player->mo->momy = FixedMul(FixedDiv(player->mo->tracer->y - player->mo->y, dist), (speed));
+	player->mo->momz = FixedMul(FixedDiv(player->mo->tracer->z - player->mo->z, dist), (speed));
 
 	// Calculate the distance between the player and the waypoint
 	// 'dist' already equals this.
 
-	// Will the player be FURTHER away if the momx/momy/momz is added to
-	// his current coordinates, or closer? (shift down to fracunits to avoid approximation errors)
-	if (dist>>FRACBITS <= P_AproxDistance(P_AproxDistance(player->mo->tracer->x - player->mo->x - speedx, player->mo->tracer->y - player->mo->y - speedy), player->mo->tracer->z - player->mo->z - speedz)>>FRACBITS)
+	// Will the player go past the waypoint?
+	if (speed > dist)
 	{
+		speed -= dist;
 		// If further away, set XYZ of player to waypoint location
 		P_UnsetThingPosition(player->mo);
 		player->mo->x = player->mo->tracer->x;
@@ -7171,14 +7170,9 @@ static void P_DoZoomTube(player_t *player)
 		{
 			CONS_Debug(DBG_GAMELOGIC, "Found waypoint (sequence %d, number %d).\n", waypoint->threshold, waypoint->health);
 
-			// calculate MOMX/MOMY/MOMZ for next waypoint
-			// change angle
-			player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, player->mo->tracer->x, player->mo->tracer->y);
+			P_SetTarget(&player->mo->tracer, waypoint);
 
-			if (player == &players[consoleplayer])
-				localangle = player->mo->angle;
-			else if (player == &players[secondarydisplayplayer])
-				localangle2 = player->mo->angle;
+			// calculate MOMX/MOMY/MOMZ for next waypoint
 
 			// change slope
 			dist = P_AproxDistance(P_AproxDistance(player->mo->tracer->x - player->mo->x, player->mo->tracer->y - player->mo->y), player->mo->tracer->z - player->mo->z);
@@ -7189,22 +7183,14 @@ static void P_DoZoomTube(player_t *player)
 			player->mo->momx = FixedMul(FixedDiv(player->mo->tracer->x - player->mo->x, dist), (speed));
 			player->mo->momy = FixedMul(FixedDiv(player->mo->tracer->y - player->mo->y, dist), (speed));
 			player->mo->momz = FixedMul(FixedDiv(player->mo->tracer->z - player->mo->z, dist), (speed));
-
-			P_SetTarget(&player->mo->tracer, waypoint);
 		}
 		else
 		{
-			P_SetTarget(&player->mo->tracer, NULL); // Else, we just let him fly.
+			P_SetTarget(&player->mo->tracer, NULL); // Else, we just let them fly.
 
 			CONS_Debug(DBG_GAMELOGIC, "Next waypoint not found, releasing from track...\n");
 		}
 	}
-	else
-	{
-		player->mo->momx = speedx;
-		player->mo->momy = speedy;
-		player->mo->momz = speedz;
-	}
 
 	// change angle
 	if (player->mo->tracer)
@@ -7232,24 +7218,10 @@ static void P_DoRopeHang(player_t *player)
 	mobj_t *mo2;
 	mobj_t *waypoint = NULL;
 	fixed_t dist;
-	fixed_t speedx,speedy,speedz;
 	fixed_t playerz;
 
 	player->mo->height = P_GetPlayerHeight(player);
 
-	if (player->cmd.buttons & BT_USE && !(player->pflags & PF_STASIS)) // Drop off of the rope
-	{
-		P_SetTarget(&player->mo->tracer, NULL);
-
-		player->pflags |= PF_JUMPED;
-		player->pflags &= ~PF_ROPEHANG;
-
-		if (!(player->pflags & PF_SLIDING) && (player->pflags & PF_JUMPED)
-		&& !(player->panim == PA_ROLL) && player->charability2 == CA2_SPINDASH)
-			P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
-		return;
-	}
-
 	// Play the 'clink' sound only if the player is moving.
 	if (!(leveltime & 7) && player->speed)
 		S_StartSound(player->mo, sfx_s3k55);
@@ -7266,9 +7238,22 @@ static void P_DoRopeHang(player_t *player)
 	if (dist < 1)
 		dist = 1;
 
-	speedx = FixedMul(FixedDiv(player->mo->tracer->x - player->mo->x, dist), (speed));
-	speedy = FixedMul(FixedDiv(player->mo->tracer->y - player->mo->y, dist), (speed));
-	speedz = FixedMul(FixedDiv(player->mo->tracer->z - playerz, dist), (speed));
+	player->mo->momx = FixedMul(FixedDiv(player->mo->tracer->x - player->mo->x, dist), (speed));
+	player->mo->momy = FixedMul(FixedDiv(player->mo->tracer->y - player->mo->y, dist), (speed));
+	player->mo->momz = FixedMul(FixedDiv(player->mo->tracer->z - playerz, dist), (speed));
+
+	if (player->cmd.buttons & BT_USE && !(player->pflags & PF_STASIS)) // Drop off of the rope
+	{
+		P_SetTarget(&player->mo->tracer, NULL);
+
+		player->pflags |= PF_JUMPED;
+		player->pflags &= ~PF_ROPEHANG;
+
+		if (!(player->pflags & PF_SLIDING) && (player->pflags & PF_JUMPED)
+		&& !(player->panim == PA_ROLL) && player->charability2 == CA2_SPINDASH)
+			P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
+		return;
+	}
 
 	// If not allowed to move, we're done here.
 	if (!speed)
@@ -7277,15 +7262,16 @@ static void P_DoRopeHang(player_t *player)
 	// Calculate the distance between the player and the waypoint
 	// 'dist' already equals this.
 
-	// Will the player be FURTHER away if the momx/momy/momz is added to
-	// his current coordinates, or closer? (shift down to fracunits to avoid approximation errors)
-	if (dist>>FRACBITS <= P_AproxDistance(P_AproxDistance(player->mo->tracer->x - player->mo->x - speedx, player->mo->tracer->y - player->mo->y - speedy), player->mo->tracer->z - playerz - speedz)>>FRACBITS)
+	// Will the player go past the waypoint?
+	if (speed > dist)
 	{
+		speed -= dist;
 		// If further away, set XYZ of player to waypoint location
 		P_UnsetThingPosition(player->mo);
 		player->mo->x = player->mo->tracer->x;
 		player->mo->y = player->mo->tracer->y;
 		player->mo->z = player->mo->tracer->z - player->mo->height;
+		playerz = player->mo->tracer->z;
 		P_SetThingPosition(player->mo);
 
 		CONS_Debug(DBG_GAMELOGIC, "Looking for next waypoint...\n");
@@ -7341,6 +7327,8 @@ static void P_DoRopeHang(player_t *player)
 		{
 			CONS_Debug(DBG_GAMELOGIC, "Found waypoint (sequence %d, number %d).\n", waypoint->threshold, waypoint->health);
 
+			P_SetTarget(&player->mo->tracer, waypoint);
+
 			// calculate MOMX/MOMY/MOMZ for next waypoint
 			// change slope
 			dist = P_AproxDistance(P_AproxDistance(player->mo->tracer->x - player->mo->x, player->mo->tracer->y - player->mo->y), player->mo->tracer->z - playerz);
@@ -7351,8 +7339,6 @@ static void P_DoRopeHang(player_t *player)
 			player->mo->momx = FixedMul(FixedDiv(player->mo->tracer->x - player->mo->x, dist), (speed));
 			player->mo->momy = FixedMul(FixedDiv(player->mo->tracer->y - player->mo->y, dist), (speed));
 			player->mo->momz = FixedMul(FixedDiv(player->mo->tracer->z - playerz, dist), (speed));
-
-			P_SetTarget(&player->mo->tracer, waypoint);
 		}
 		else
 		{
@@ -7371,12 +7357,6 @@ static void P_DoRopeHang(player_t *player)
 			CONS_Debug(DBG_GAMELOGIC, "Next waypoint not found!\n");
 		}
 	}
-	else
-	{
-		player->mo->momx = speedx;
-		player->mo->momy = speedy;
-		player->mo->momz = speedz;
-	}
 }
 
 #if 0
diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index 71baca510dabd7e21fd247721a10b050e2981f46..aa572e6e036fe41323c4cbcbc3802b5d13aa7c9d 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -33,14 +33,6 @@
 #pragma warning(default : 4214 4244)
 #endif
 
-#if SDL_VERSION_ATLEAST(1,3,0)
-#define SDLK_EQUALS SDLK_KP_EQUALSAS400
-#define SDLK_LMETA SDLK_LGUI
-#define SDLK_RMETA SDLK_RGUI
-#else
-#define HAVE_SDLMETAKEYS
-#endif
-
 #ifdef HAVE_TTF
 #include "i_ttf.h"
 #endif
@@ -189,14 +181,14 @@ static void SDLSetMode(INT32 width, INT32 height, SDL_bool fullscreen)
 			wasfullscreen = SDL_TRUE;
 			SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
 		}
-		else if (!fullscreen && wasfullscreen)
+		else if (wasfullscreen)
 		{
 			wasfullscreen = SDL_FALSE;
 			SDL_SetWindowFullscreen(window, 0);
 			SDL_SetWindowSize(window, width, height);
 			SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED_DISPLAY(1), SDL_WINDOWPOS_CENTERED_DISPLAY(1));
 		}
-		else if (!wasfullscreen)
+		else
 		{
 			// Reposition window only in windowed mode
 			SDL_SetWindowSize(window, width, height);
@@ -282,129 +274,70 @@ static INT32 Impl_SDL_Scancode_To_Keycode(SDL_Scancode code)
 	}
 	switch (code)
 	{
-		case SDL_SCANCODE_F11: // F11 and F12 are
-			return KEY_F11;    // separated from the
-		case SDL_SCANCODE_F12: // rest of the function
-			return KEY_F12;    // keys
-
-		case SDL_SCANCODE_KP_0:
-			return KEY_KEYPAD0;
-		case SDL_SCANCODE_KP_1:
-			return KEY_KEYPAD1;
-		case SDL_SCANCODE_KP_2:
-			return KEY_KEYPAD2;
-		case SDL_SCANCODE_KP_3:
-			return KEY_KEYPAD3;
-		case SDL_SCANCODE_KP_4:
-			return KEY_KEYPAD4;
-		case SDL_SCANCODE_KP_5:
-			return KEY_KEYPAD5;
-		case SDL_SCANCODE_KP_6:
-			return KEY_KEYPAD6;
-		case SDL_SCANCODE_KP_7:
-			return KEY_KEYPAD7;
-		case SDL_SCANCODE_KP_8:
-			return KEY_KEYPAD8;
-		case SDL_SCANCODE_KP_9:
-			return KEY_KEYPAD9;
-
-		case SDL_SCANCODE_RETURN:
-			return KEY_ENTER;
-		case SDL_SCANCODE_ESCAPE:
-			return KEY_ESCAPE;
-		case SDL_SCANCODE_BACKSPACE:
-			return KEY_BACKSPACE;
-		case SDL_SCANCODE_TAB:
-			return KEY_TAB;
-		case SDL_SCANCODE_SPACE:
-			return KEY_SPACE;
-		case SDL_SCANCODE_MINUS:
-			return KEY_MINUS;
-		case SDL_SCANCODE_EQUALS:
-			return KEY_EQUALS;
-		case SDL_SCANCODE_LEFTBRACKET:
-			return '[';
-		case SDL_SCANCODE_RIGHTBRACKET:
-			return ']';
-		case SDL_SCANCODE_BACKSLASH:
-			return '\\';
-		case SDL_SCANCODE_NONUSHASH:
-			return '#';
-		case SDL_SCANCODE_SEMICOLON:
-			return ';';
-		case SDL_SCANCODE_APOSTROPHE:
-			return '\'';
-		case SDL_SCANCODE_GRAVE:
-			return '`';
-		case SDL_SCANCODE_COMMA:
-			return ',';
-		case SDL_SCANCODE_PERIOD:
-			return '.';
-		case SDL_SCANCODE_SLASH:
-			return '/';
-		case SDL_SCANCODE_CAPSLOCK:
-			return KEY_CAPSLOCK;
-		case SDL_SCANCODE_PRINTSCREEN:
-			return 0; // undefined?
-		case SDL_SCANCODE_SCROLLLOCK:
-			return KEY_SCROLLLOCK;
-		case SDL_SCANCODE_PAUSE:
-			return KEY_PAUSE;
-		case SDL_SCANCODE_INSERT:
-			return KEY_INS;
-		case SDL_SCANCODE_HOME:
-			return KEY_HOME;
-		case SDL_SCANCODE_PAGEUP:
-			return KEY_PGUP;
-		case SDL_SCANCODE_DELETE:
-			return KEY_DEL;
-		case SDL_SCANCODE_END:
-			return KEY_END;
-		case SDL_SCANCODE_PAGEDOWN:
-			return KEY_PGDN;
-		case SDL_SCANCODE_RIGHT:
-			return KEY_RIGHTARROW;
-		case SDL_SCANCODE_LEFT:
-			return KEY_LEFTARROW;
-		case SDL_SCANCODE_DOWN:
-			return KEY_DOWNARROW;
-		case SDL_SCANCODE_UP:
-			return KEY_UPARROW;
-		case SDL_SCANCODE_NUMLOCKCLEAR:
-			return KEY_NUMLOCK;
-		case SDL_SCANCODE_KP_DIVIDE:
-			return KEY_KPADSLASH;
-		case SDL_SCANCODE_KP_MULTIPLY:
-			return '*'; // undefined?
-		case SDL_SCANCODE_KP_MINUS:
-			return KEY_MINUSPAD;
-		case SDL_SCANCODE_KP_PLUS:
-			return KEY_PLUSPAD;
-		case SDL_SCANCODE_KP_ENTER:
-			return KEY_ENTER;
-		case SDL_SCANCODE_KP_PERIOD:
-			return KEY_KPADDEL;
-		case SDL_SCANCODE_NONUSBACKSLASH:
-			return '\\';
-
-		case SDL_SCANCODE_LSHIFT:
-			return KEY_LSHIFT;
-		case SDL_SCANCODE_RSHIFT:
-			return KEY_RSHIFT;
-		case SDL_SCANCODE_LCTRL:
-			return KEY_LCTRL;
-		case SDL_SCANCODE_RCTRL:
-			return KEY_RCTRL;
-		case SDL_SCANCODE_LALT:
-			return KEY_LALT;
-		case SDL_SCANCODE_RALT:
-			return KEY_RALT;
-		case SDL_SCANCODE_LGUI:
-			return KEY_LEFTWIN;
-		case SDL_SCANCODE_RGUI:
-			return KEY_RIGHTWIN;
-		default:
-			break;
+		// F11 and F12 are separated from the rest of the function keys
+		case SDL_SCANCODE_F11: return KEY_F11;
+		case SDL_SCANCODE_F12: return KEY_F12;
+
+		case SDL_SCANCODE_KP_0: return KEY_KEYPAD0;
+		case SDL_SCANCODE_KP_1: return KEY_KEYPAD1;
+		case SDL_SCANCODE_KP_2: return KEY_KEYPAD2;
+		case SDL_SCANCODE_KP_3: return KEY_KEYPAD3;
+		case SDL_SCANCODE_KP_4: return KEY_KEYPAD4;
+		case SDL_SCANCODE_KP_5: return KEY_KEYPAD5;
+		case SDL_SCANCODE_KP_6: return KEY_KEYPAD6;
+		case SDL_SCANCODE_KP_7: return KEY_KEYPAD7;
+		case SDL_SCANCODE_KP_8: return KEY_KEYPAD8;
+		case SDL_SCANCODE_KP_9: return KEY_KEYPAD9;
+
+		case SDL_SCANCODE_RETURN:         return KEY_ENTER;
+		case SDL_SCANCODE_ESCAPE:         return KEY_ESCAPE;
+		case SDL_SCANCODE_BACKSPACE:      return KEY_BACKSPACE;
+		case SDL_SCANCODE_TAB:            return KEY_TAB;
+		case SDL_SCANCODE_SPACE:          return KEY_SPACE;
+		case SDL_SCANCODE_MINUS:          return KEY_MINUS;
+		case SDL_SCANCODE_EQUALS:         return KEY_EQUALS;
+		case SDL_SCANCODE_LEFTBRACKET:    return '[';
+		case SDL_SCANCODE_RIGHTBRACKET:   return ']';
+		case SDL_SCANCODE_BACKSLASH:      return '\\';
+		case SDL_SCANCODE_NONUSHASH:      return '#';
+		case SDL_SCANCODE_SEMICOLON:      return ';';
+		case SDL_SCANCODE_APOSTROPHE:     return '\'';
+		case SDL_SCANCODE_GRAVE:          return '`';
+		case SDL_SCANCODE_COMMA:          return ',';
+		case SDL_SCANCODE_PERIOD:         return '.';
+		case SDL_SCANCODE_SLASH:          return '/';
+		case SDL_SCANCODE_CAPSLOCK:       return KEY_CAPSLOCK;
+		case SDL_SCANCODE_PRINTSCREEN:    return 0; // undefined?
+		case SDL_SCANCODE_SCROLLLOCK:     return KEY_SCROLLLOCK;
+		case SDL_SCANCODE_PAUSE:          return KEY_PAUSE;
+		case SDL_SCANCODE_INSERT:         return KEY_INS;
+		case SDL_SCANCODE_HOME:           return KEY_HOME;
+		case SDL_SCANCODE_PAGEUP:         return KEY_PGUP;
+		case SDL_SCANCODE_DELETE:         return KEY_DEL;
+		case SDL_SCANCODE_END:            return KEY_END;
+		case SDL_SCANCODE_PAGEDOWN:       return KEY_PGDN;
+		case SDL_SCANCODE_RIGHT:          return KEY_RIGHTARROW;
+		case SDL_SCANCODE_LEFT:           return KEY_LEFTARROW;
+		case SDL_SCANCODE_DOWN:           return KEY_DOWNARROW;
+		case SDL_SCANCODE_UP:             return KEY_UPARROW;
+		case SDL_SCANCODE_NUMLOCKCLEAR:   return KEY_NUMLOCK;
+		case SDL_SCANCODE_KP_DIVIDE:      return KEY_KPADSLASH;
+		case SDL_SCANCODE_KP_MULTIPLY:    return '*'; // undefined?
+		case SDL_SCANCODE_KP_MINUS:       return KEY_MINUSPAD;
+		case SDL_SCANCODE_KP_PLUS:        return KEY_PLUSPAD;
+		case SDL_SCANCODE_KP_ENTER:       return KEY_ENTER;
+		case SDL_SCANCODE_KP_PERIOD:      return KEY_KPADDEL;
+		case SDL_SCANCODE_NONUSBACKSLASH: return '\\';
+
+		case SDL_SCANCODE_LSHIFT: return KEY_LSHIFT;
+		case SDL_SCANCODE_RSHIFT: return KEY_RSHIFT;
+		case SDL_SCANCODE_LCTRL:  return KEY_LCTRL;
+		case SDL_SCANCODE_RCTRL:  return KEY_RCTRL;
+		case SDL_SCANCODE_LALT:   return KEY_LALT;
+		case SDL_SCANCODE_RALT:   return KEY_RALT;
+		case SDL_SCANCODE_LGUI:   return KEY_LEFTWIN;
+		case SDL_SCANCODE_RGUI:   return KEY_RIGHTWIN;
+		default:                  break;
 	}
 #ifdef HWRENDER
 	DBG_Printf("Unknown incoming scancode: %d, represented %c\n",
@@ -432,15 +365,10 @@ static void VID_Command_NumModes_f (void)
 	CONS_Printf(M_GetText("%d video mode(s) available(s)\n"), VID_NumModes());
 }
 
+// SDL2 doesn't have SDL_GetVideoSurface or a lot of the SDL_Surface flags that SDL 1.2 had
 static void SurfaceInfo(const SDL_Surface *infoSurface, const char *SurfaceText)
 {
-#if 1
-	(void)infoSurface;
-	(void)SurfaceText;
-	SDL2STUB();
-#else
 	INT32 vfBPP;
-	const SDL_Surface *VidSur = SDL_GetVideoSurface();
 
 	if (!infoSurface)
 		return;
@@ -453,49 +381,12 @@ static void SurfaceInfo(const SDL_Surface *infoSurface, const char *SurfaceText)
 	CONS_Printf("\x82" "%s\n", SurfaceText);
 	CONS_Printf(M_GetText(" %ix%i at %i bit color\n"), infoSurface->w, infoSurface->h, vfBPP);
 
-	if (infoSurface->flags&SDL_HWSURFACE)
-		CONS_Printf("%s", M_GetText(" Stored in video memory\n"));
-	else if (infoSurface->flags&SDL_OPENGL)
-		CONS_Printf("%s", M_GetText(" Stored in an OpenGL context\n"));
-	else if (infoSurface->flags&SDL_PREALLOC)
+	if (infoSurface->flags&SDL_PREALLOC)
 		CONS_Printf("%s", M_GetText(" Uses preallocated memory\n"));
 	else
 		CONS_Printf("%s", M_GetText(" Stored in system memory\n"));
-
-	if (infoSurface->flags&SDL_ASYNCBLIT)
-		CONS_Printf("%s", M_GetText(" Uses asynchronous blits if possible\n"));
-	else
-		CONS_Printf("%s", M_GetText(" Uses synchronous blits if possible\n"));
-
-	if (infoSurface->flags&SDL_ANYFORMAT)
-		CONS_Printf("%s", M_GetText(" Allows any pixel-format\n"));
-
-	if (infoSurface->flags&SDL_HWPALETTE)
-		CONS_Printf("%s", M_GetText(" Has exclusive palette access\n"));
-	else if (VidSur == infoSurface)
-		CONS_Printf("%s", M_GetText(" Has nonexclusive palette access\n"));
-
-	if (infoSurface->flags&SDL_DOUBLEBUF)
-		CONS_Printf("%s", M_GetText(" Double buffered\n"));
-	else if (VidSur == infoSurface)
-		CONS_Printf("%s", M_GetText(" No hardware flipping\n"));
-
-	if (infoSurface->flags&SDL_FULLSCREEN)
-		CONS_Printf("%s", M_GetText(" Full screen\n"));
-	else if (infoSurface->flags&SDL_RESIZABLE)
-		CONS_Printf("%s", M_GetText(" Resizable window\n"));
-	else if (VidSur == infoSurface)
-		CONS_Printf("%s", M_GetText(" Nonresizable window\n"));
-
-	if (infoSurface->flags&SDL_HWACCEL)
-		CONS_Printf("%s", M_GetText(" Uses hardware acceleration blit\n"));
-	if (infoSurface->flags&SDL_SRCCOLORKEY)
-		CONS_Printf("%s", M_GetText(" Use colorkey blitting\n"));
 	if (infoSurface->flags&SDL_RLEACCEL)
 		CONS_Printf("%s", M_GetText(" Colorkey RLE acceleration blit\n"));
-	if (infoSurface->flags&SDL_SRCALPHA)
-		CONS_Printf("%s", M_GetText(" Use alpha blending acceleration blit\n"));
-#endif
 }
 
 static void VID_Command_Info_f (void)
@@ -579,23 +470,6 @@ static void VID_Command_Mode_f (void)
 		setmodeneeded = modenum+1; // request vid mode change
 }
 
-#if 0
-#if defined(RPC_NO_WINDOWS_H)
-static VOID MainWndproc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
-{
-	UNREFERENCED_PARAMETER(hWnd);
-	UNREFERENCED_PARAMETER(message);
-	UNREFERENCED_PARAMETER(wParam);
-	switch (message)
-	{
-		case WM_SETTEXT:
-			COM_BufAddText((LPCSTR)lParam);
-			break;
-	}
-}
-#endif
-#endif
-
 static inline void SDLJoyRemap(event_t *event)
 {
 	(void)event;
@@ -954,218 +828,6 @@ void I_GetEvent(void)
 	// In order to make wheels act like buttons, we have to set their state to Up.
 	// This is because wheel messages don't have an up/down state.
 	gamekeydown[KEY_MOUSEWHEELDOWN] = gamekeydown[KEY_MOUSEWHEELUP] = 0;
-
-#if 0
-	SDL_Event inputEvent;
-	static SDL_bool sdlquit = SDL_FALSE; //Alam: once, just once
-	event_t event;
-
-	if (!graphics_started)
-		return;
-
-	memset(&inputEvent, 0x00, sizeof(inputEvent));
-	while (SDL_PollEvent(&inputEvent))
-	{
-		memset(&event,0x00,sizeof (event_t));
-		switch (inputEvent.type)
-		{
-			case SDL_ACTIVEEVENT:
-				if (inputEvent.active.state  & (SDL_APPACTIVE|SDL_APPINPUTFOCUS))
-				{
-					// pause music when alt-tab
-					if (inputEvent.active.gain /*&& !paused */)
-					{
-						static SDL_bool firsttimeonmouse = SDL_TRUE;
-						if (!firsttimeonmouse)
-						{
-							if (cv_usemouse.value) I_StartupMouse();
-						}
-						else firsttimeonmouse = SDL_FALSE;
-						//if (!netgame && !con_destlines) paused = false;
-						if (gamestate == GS_LEVEL)
-							if (!paused) I_ResumeSong(0); //resume it
-					}
-					else /*if (!paused)*/
-					{
-						if (!disable_mouse)
-							SDLforceUngrabMouse();
-						if (!netgame && gamestate == GS_LEVEL) paused = true;
-						memset(gamekeydown, 0, NUMKEYS);
-						//S_PauseSound();
-						if (gamestate == GS_LEVEL)
-							I_PauseSong(0); //pause it
-					}
-				}
-				if (MOUSE_MENU)
-				{
-					SDLdoUngrabMouse();
-					break;
-				}
-				if ((SDL_APPMOUSEFOCUS&inputEvent.active.state) && USE_MOUSEINPUT && inputEvent.active.gain)
-					HalfWarpMouse(realwidth, realheight);
-				break;
-			case SDL_KEYDOWN:
-			case SDL_KEYUP:
-				/// \todo inputEvent.key.which?
-				if (inputEvent.type == SDL_KEYUP)
-					event.type = ev_keyup;
-				else if (inputEvent.type == SDL_KEYDOWN)
-					event.type = ev_keydown;
-				else break;
-				event.data1 = SDLatekey(inputEvent.key.keysym.sym);
-				if (event.data1) D_PostEvent(&event);
-				break;
-			case SDL_MOUSEMOTION:
-				/// \todo inputEvent.motion.which
-				if (MOUSE_MENU)
-				{
-					SDLdoUngrabMouse();
-					break;
-				}
-				//if (USE_MOUSEINPUT) TODO SDL2 stub
-				{
-					// If the event is from warping the pointer back to middle
-					// of the screen then ignore it.
-					if ((inputEvent.motion.x == realwidth/2) &&
-					    (inputEvent.motion.y == realheight/2))
-					{
-						break;
-					}
-					else
-					{
-						event.data2 = +inputEvent.motion.xrel;
-						event.data3 = -inputEvent.motion.yrel;
-					}
-					event.type = ev_mouse;
-					D_PostEvent(&event);
-					// Warp the pointer back to the middle of the window
-					//  or we cannot move any further if it's at a border.
-					if ((inputEvent.motion.x < (realwidth/2 )-(realwidth/4 )) ||
-					    (inputEvent.motion.y < (realheight/2)-(realheight/4)) ||
-					    (inputEvent.motion.x > (realwidth/2 )+(realwidth/4 )) ||
-					    (inputEvent.motion.y > (realheight/2)+(realheight/4) ) )
-					{
-						//if (SDL_GRAB_ON == SDL_WM_GrabInput(SDL_GRAB_QUERY) || !mousegrabok)
-							HalfWarpMouse(realwidth, realheight);
-					}
-				}
-				break;
-			case SDL_MOUSEBUTTONDOWN:
-			case SDL_MOUSEBUTTONUP:
-				/// \todo inputEvent.button.which
-				if (USE_MOUSEINPUT)
-				{
-					if (inputEvent.type == SDL_MOUSEBUTTONUP)
-						event.type = ev_keyup;
-					else if (inputEvent.type == SDL_MOUSEBUTTONDOWN)
-						event.type = ev_keydown;
-					else break;
-					if (inputEvent.button.button==SDL_BUTTON_WHEELUP || inputEvent.button.button==SDL_BUTTON_WHEELDOWN)
-					{
-						if (inputEvent.type == SDL_MOUSEBUTTONUP)
-							event.data1 = 0; //Alam: dumb! this could be a real button with some mice
-						else
-							event.data1 = KEY_MOUSEWHEELUP + inputEvent.button.button - SDL_BUTTON_WHEELUP;
-					}
-					else if (inputEvent.button.button == SDL_BUTTON_MIDDLE)
-						event.data1 = KEY_MOUSE1+2;
-					else if (inputEvent.button.button == SDL_BUTTON_RIGHT)
-						event.data1 = KEY_MOUSE1+1;
-					else if (inputEvent.button.button <= MOUSEBUTTONS)
-						event.data1 = KEY_MOUSE1 + inputEvent.button.button - SDL_BUTTON_LEFT;
-					if (event.data1) D_PostEvent(&event);
-				}
-				break;
-			case SDL_JOYAXISMOTION:
-				inputEvent.jaxis.which++;
-				inputEvent.jaxis.axis++;
-				event.data1 = event.data2 = event.data3 = INT32_MAX;
-				if (cv_usejoystick.value == inputEvent.jaxis.which)
-				{
-					event.type = ev_joystick;
-				}
-				else if (cv_usejoystick.value == inputEvent.jaxis.which)
-				{
-					event.type = ev_joystick2;
-				}
-				else break;
-				//axis
-				if (inputEvent.jaxis.axis > JOYAXISSET*2)
-					break;
-				//vaule
-				if (inputEvent.jaxis.axis%2)
-				{
-					event.data1 = inputEvent.jaxis.axis / 2;
-					event.data2 = SDLJoyAxis(inputEvent.jaxis.value, event.type);
-				}
-				else
-				{
-					inputEvent.jaxis.axis--;
-					event.data1 = inputEvent.jaxis.axis / 2;
-					event.data3 = SDLJoyAxis(inputEvent.jaxis.value, event.type);
-				}
-				D_PostEvent(&event);
-				break;
-			case SDL_JOYBALLMOTION:
-			case SDL_JOYHATMOTION:
-				break; //NONE
-			case SDL_JOYBUTTONDOWN:
-			case SDL_JOYBUTTONUP:
-				inputEvent.jbutton.which++;
-				if (cv_usejoystick.value == inputEvent.jbutton.which)
-					event.data1 = KEY_JOY1;
-				else if (cv_usejoystick.value == inputEvent.jbutton.which)
-					event.data1 = KEY_2JOY1;
-				else break;
-				if (inputEvent.type == SDL_JOYBUTTONUP)
-					event.type = ev_keyup;
-				else if (inputEvent.type == SDL_JOYBUTTONDOWN)
-					event.type = ev_keydown;
-				else break;
-				if (inputEvent.jbutton.button < JOYBUTTONS)
-					event.data1 += inputEvent.jbutton.button;
-				else
-					break;
-				SDLJoyRemap(&event);
-				if (event.type != ev_console) D_PostEvent(&event);
-				break;
-			case SDL_QUIT:
-				if (!sdlquit)
-				{
-					sdlquit = SDL_TRUE;
-					M_QuitResponse('y');
-				}
-				break;
-#if defined(RPC_NO_WINDOWS_H)
-			case SDL_SYSWMEVENT:
-				MainWndproc(inputEvent.syswm.msg->hwnd,
-					inputEvent.syswm.msg->msg,
-					inputEvent.syswm.msg->wParam,
-					inputEvent.syswm.msg->lParam);
-				break;
-#endif
-			case SDL_VIDEORESIZE:
-				if (gamestate == GS_LEVEL || gamestate == GS_TITLESCREEN || gamestate == GS_EVALUATION)
-				    setmodeneeded = VID_GetModeForSize(inputEvent.resize.w,inputEvent.resize.h)+1;
-				if (render_soft == rendermode)
-				{
-					SDLSetMode(realwidth, realheight, vid.bpp*8, surfaceFlagsW);
-					if (vidSurface) SDL_SetColors(vidSurface, localPalette, 0, 256);
-				}
-				else
-					SDLSetMode(realwidth, realheight, vid.bpp*8, surfaceFlagsW);
-				if (!vidSurface)
-					I_Error("Could not reset vidmode: %s\n",SDL_GetError());
-				break;
-			case SDL_VIDEOEXPOSE:
-				exposevideo = SDL_TRUE;
-				break;
-			default:
-				break;
-		}
-	}
-	//reset wheel like in win32, I don't understand it but works
-#endif
 }
 
 void I_StartupMouse(void)
@@ -1494,11 +1156,6 @@ void VID_PrepareModeList(void)
 #endif
 }
 
-static inline void SDLESSet(void)
-{
-	SDL2STUB();
-}
-
 INT32 VID_SetMode(INT32 modeNum)
 {
 	SDLdoUngrabMouse();
@@ -1550,6 +1207,12 @@ INT32 VID_SetMode(INT32 modeNum)
 static SDL_bool Impl_CreateWindow(SDL_bool fullscreen)
 {
 	int flags = 0;
+
+	if (rendermode == render_none) // dedicated
+	{
+		return SDL_TRUE; // Monster Iestyn -- not sure if it really matters what we return here tbh
+	}
+
 	if (window != NULL)
 	{
 		return SDL_FALSE;
@@ -1568,38 +1231,43 @@ static SDL_bool Impl_CreateWindow(SDL_bool fullscreen)
 #ifdef HWRENDER
 	if (rendermode == render_opengl)
 	{
-		window = SDL_CreateWindow("SRB2 "VERSIONSTRING, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
-				realwidth, realheight, flags | SDL_WINDOW_OPENGL);
-		if (window != NULL)
+		flags |= SDL_WINDOW_OPENGL;
+	}
+#endif
+
+	// Create a window
+	window = SDL_CreateWindow("SRB2 "VERSIONSTRING, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
+			realwidth, realheight, flags);
+
+	if (window == NULL)
+	{
+		CONS_Printf(M_GetText("Couldn't create window: %s\n"), SDL_GetError());
+		return SDL_FALSE;
+	}
+
+	// Renderer-specific stuff
+#ifdef HWRENDER
+	if (rendermode == render_opengl)
+	{
+		sdlglcontext = SDL_GL_CreateContext(window);
+		if (sdlglcontext == NULL)
 		{
-			sdlglcontext = SDL_GL_CreateContext(window);
-			if (sdlglcontext == NULL)
-			{
-				SDL_DestroyWindow(window);
-				I_Error("Failed to create a GL context: %s\n", SDL_GetError());
-			}
-			else
-			{
-				SDL_GL_MakeCurrent(window, sdlglcontext);
-			}
+			SDL_DestroyWindow(window);
+			I_Error("Failed to create a GL context: %s\n", SDL_GetError());
 		}
-		else return SDL_FALSE;
+		SDL_GL_MakeCurrent(window, sdlglcontext);
 	}
+	else
 #endif
 	if (rendermode == render_soft)
 	{
-		window = SDL_CreateWindow("SRB2 "VERSIONSTRING, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
-				realwidth, realheight, flags);
-		if (window != NULL)
+		renderer = SDL_CreateRenderer(window, -1, (usesdl2soft ? SDL_RENDERER_SOFTWARE : 0) | (cv_vidwait.value && !usesdl2soft ? SDL_RENDERER_PRESENTVSYNC : 0));
+		if (renderer == NULL)
 		{
-			renderer = SDL_CreateRenderer(window, -1, (usesdl2soft ? SDL_RENDERER_SOFTWARE : 0) | (cv_vidwait.value && !usesdl2soft ? SDL_RENDERER_PRESENTVSYNC : 0));
-			if (renderer != NULL)
-			{
-				SDL_RenderSetLogicalSize(renderer, BASEVIDWIDTH, BASEVIDHEIGHT);
-			}
-			else return SDL_FALSE;
+			CONS_Printf(M_GetText("Couldn't create rendering context: %s\n"), SDL_GetError());
+			return SDL_FALSE;
 		}
-		else return SDL_FALSE;
+		SDL_RenderSetLogicalSize(renderer, BASEVIDWIDTH, BASEVIDHEIGHT);
 	}
 
 	return SDL_TRUE;
@@ -1620,7 +1288,7 @@ static void Impl_SetWindowIcon(void)
 	{
 		return;
 	}
-	SDL2STUB();
+	//SDL2STUB(); // Monster Iestyn: why is this stubbed?
 	SDL_SetWindowIcon(window, icoSurface);
 }
 
@@ -1718,7 +1386,6 @@ void I_StartupGraphics(void)
 	borderlesswindow = M_CheckParm("-borderless");
 
 	//SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY>>1,SDL_DEFAULT_REPEAT_INTERVAL<<2);
-	SDLESSet();
 	VID_Command_ModeList_f();
 #ifdef HWRENDER
 	if (M_CheckParm("-opengl") || rendermode == render_opengl)
diff --git a/src/win32/Makefile.cfg b/src/win32/Makefile.cfg
index f309f7db11d158fb878eaa8a4615bed1b9dbf6b3..99b8bc9b2ca4b6949944429546b3e379808e804a 100644
--- a/src/win32/Makefile.cfg
+++ b/src/win32/Makefile.cfg
@@ -85,13 +85,21 @@ endif
 	OBJS=$(OBJDIR)/dx_error.o $(OBJDIR)/fabdxlib.o $(OBJDIR)/win_vid.o $(OBJDIR)/win_dll.o
 endif
 
+
+ZLIB_CFLAGS?=-I../libs/zlib
+ifdef MINGW64
+ZLIB_LDFLAGS?=-L../libs/zlib/win32 -lz64
+else
+ZLIB_LDFLAGS?=-L../libs/zlib/win32 -lz32
+endif
+
 ifndef NOPNG
 ifndef PNG_CONFIG
-	PNG_CFLAGS?=-I../libs/libpng-src -I../libs/zlib
+	PNG_CFLAGS?=-I../libs/libpng-src
 ifdef MINGW64
-	PNG_LDFLAGS?=-L../libs/libpng-src/projects -lpng64 -L../libs/zlib/win32 -lz64
+	PNG_LDFLAGS?=-L../libs/libpng-src/projects -lpng64
 else
-	PNG_LDFLAGS?=-L../libs/libpng-src/projects -lpng32 -L../libs/zlib/win32 -lz32
+	PNG_LDFLAGS?=-L../libs/libpng-src/projects -lpng32
 endif #MINGW64
 endif #PNG_CONFIG
 endif #NOPNG