From: sparky4 <sparky4@cock.li>
Date: Fri, 24 Mar 2017 17:34:44 +0000 (-0500)
Subject: [16_ca needs huge amounts of work and I should remember what needs to be done soon... 
X-Git-Url: http://4ch.mooo.com/gitweb/?a=commitdiff_plain;h=52fab2ff9ef6a39ed9303b0df1ce0ad9c9180ef1;p=16.git

[16_ca needs huge amounts of work and I should remember what needs to be done soon][OpenVGMFile needs to be ported to 16_snd.c]going to port rest of code to borland c some time so we can use the core components of id engine here [going to add 16_us.c eventually but the debug system and CA_ PM_ and MM_ usage is priority now]
---

diff --git a/src/lib/16_in_o.c b/16/src/lib/16_in_o.c
similarity index 100%
rename from src/lib/16_in_o.c
rename to 16/src/lib/16_in_o.c
diff --git a/src/lib/16_in_o.h b/16/src/lib/16_in_o.h
similarity index 100%
rename from src/lib/16_in_o.h
rename to 16/src/lib/16_in_o.h
diff --git a/_pm_use.txt b/_pm_use.txt
index 934d267f..43ba6c4a 100755
--- a/_pm_use.txt
+++ b/_pm_use.txt
@@ -31,7 +31,11 @@
 16/wf3d8086/wl_game.c:		PM_CheckMainMem ();
 16/wf3d8086/wl_game.c:			PM_CheckMainMem ();
 
-16/wf3d8086/wl_inter.c:	PM_Preload (PreloadUpdate);
+
+
+	16/wf3d8086/wl_inter.c:	PM_Preload (PreloadUpdate);	//related to chunksinfile
+
+
 
 16/wf3d8086/wl_main.c:	PM_Shutdown ();
 16/wf3d8086/wl_main.c:	PM_Startup ();
diff --git a/makefile b/makefile
index b7a7a580..ea59beda 100755
--- a/makefile
+++ b/makefile
@@ -34,7 +34,7 @@
 # comment this out on game release.
 # serial output goes to COM1 at 9600 baud 1 stop bit odd parity.
 # serial output is plain text ASCII.
-DEBUGSERIAL=0
+DEBUGSERIAL=1
 
 DELLOGFILE=1
 
@@ -357,7 +357,7 @@ mapread.$(OBJ):$(SRCLIB)/mapread.c $(SRCLIB)/mapread.h
 16_map.$(OBJ):$(SRCLIB)/16_map.c $(SRCLIB)/16_map.h
 16_timer.$(OBJ):$(SRCLIB)/16_timer.c $(SRCLIB)/16_timer.h
 16_in.$(OBJ):	 $(SRCLIB)/16_in.c $(SRCLIB)/16_in.h
-16_in_1.$(OBJ):	 $(SRCLIB)/16_in_1.c $(SRCLIB)/16_in_1.h
+#16_in_1.$(OBJ):	 $(SRCLIB)/16_in_1.c $(SRCLIB)/16_in_1.h
 16_rf.$(OBJ):	 $(SRCLIB)/16_rf.c	$(SRCLIB)/16_rf.h
 16_mm.$(OBJ):	 $(SRCLIB)/16_mm.c	$(SRCLIB)/16_mm.h
 16_pm.$(OBJ):	 $(SRCLIB)/16_pm.c	$(SRCLIB)/16_pm.h
diff --git a/src/16.c b/src/16.c
index af711c37..e87446c6 100755
--- a/src/16.c
+++ b/src/16.c
@@ -28,8 +28,6 @@ main(int argc, char *argv[])
 	static global_game_variables_t gvar;
 	Startup16(&gvar);
 
-	gvar.engi_stat = ENGI_RUN;
-
 	/* save the palette */
 	modexPalSave(gvar.video.dpal);
 	modexFadeOff(4, gvar.video.dpal);
@@ -39,10 +37,10 @@ main(int argc, char *argv[])
 //	modexPalBlack();	//so player will not see loadings~
 	IN_Default(0,&gvar.player[0],ctrl_Joystick, &gvar);
 	//modexprint(&screen, 32, 32, 1, 2, 0, "a", 1);
-	while(ENGI_QUIT != gvar.engi_stat)
+	while(1)
 	{
 		IN_ReadControl(&gvar.player[0], &gvar);
-		if(IN_KeyDown(sc_Escape)) gvar.engi_stat = ENGI_QUIT;
+		if(IN_KeyDown(sc_Escape)) break;
 		shinku(&gvar);
 		_DEBUGF("Serial debug output printf test %u %u %u\n",1U,2U,3U);
 	}
diff --git a/src/lib/16_dbg.c b/src/lib/16_dbg.c
index db8f71a1..9878998b 100755
--- a/src/lib/16_dbg.c
+++ b/src/lib/16_dbg.c
@@ -257,12 +257,10 @@ static	char	buf[10];
 //		VW_UpdateScreen();
 
 		while (!(scan = gvar->in.inst->LastScan))
-		{
-		}
-//			scan = *IN_GetScanName(scan);
+		{}
 //			SD_Poll();
 
-if(IN_KeyDown(sc_Escape)) break;
+//if(IN_KeyDown(sc_Escape)) break;
 
 		IN_ClearKey(scan);
 		switch (scan)
diff --git a/src/lib/16_dbg.h b/src/lib/16_dbg.h
index 3d7ef4af..3be65fa6 100755
--- a/src/lib/16_dbg.h
+++ b/src/lib/16_dbg.h
@@ -9,6 +9,7 @@
 #define __DEBUG__
 #define __DEBUG_InputMgr__
 #define __DEBUG_MAP__
+//#define __DEBUG_2__
 //#define __DEBUG_CA__
 //#define __DEBUG_PM__
 //#define __DEBUG_MM__
diff --git a/src/lib/16_pm.c b/src/lib/16_pm.c
index 4d9ac9d8..9fabd9be 100755
--- a/src/lib/16_pm.c
+++ b/src/lib/16_pm.c
@@ -1207,7 +1207,7 @@ PM_GetPage(int pagenum, global_game_variables_t *gvar)
 	if (pagenum >= gvar->pm.fi.ChunksInFile)
 		Quit (gvar, "PM_GetPage: Invalid page request");
 
-#ifdef __DEBUG_2__	// for debugging
+//#ifdef __DEBUG_2__	// for debugging
 	__asm {
 		mov	dx,STATUS_REGISTER_1
 		in	al,dx
@@ -1217,7 +1217,7 @@ PM_GetPage(int pagenum, global_game_variables_t *gvar)
 		mov	al,10	// bright green
 		out	dx,al
 	}
-#endif
+//#endif
 
 	if (!(result = PM_GetPageAddress(pagenum, gvar)))
 	{
@@ -1235,7 +1235,7 @@ if (!gvar->pm.PMPages[pagenum].offset)	// JDC: sparse page
 	}
 	gvar->pm.PMPages[pagenum].lastHit =  gvar->pm.PMFrameCount;
 
-#ifdef __DEBUG_2__	// for debugging
+//#ifdef __DEBUG_2__	// for debugging
 	__asm{
 		mov	dx,STATUS_REGISTER_1
 		in	al,dx
@@ -1246,7 +1246,8 @@ if (!gvar->pm.PMPages[pagenum].offset)	// JDC: sparse page
 		out	dx,al
 		mov	al,0x20	// normal
 		out	dx,al
-#endif
+	}
+//#endif
 
 	return(result);
 }
diff --git a/src/lib/16_tail.c b/src/lib/16_tail.c
index 7b5fdcb4..3b1cb42c 100755
--- a/src/lib/16_tail.c
+++ b/src/lib/16_tail.c
@@ -394,37 +394,37 @@ char global_temp_status_text2[512];
 void turboXT(byte bakapee)
 {
 	__asm {
-	push	ax
-	push	bx
-	push	cx
-	in	al, 61h 			//; Read equipment flags
-	xor	al, bakapee			//;   toggle speed
-	out	61h, al 			//; Write new flags back
-
-	mov	bx, 0F89h			//; low pitch blip
-	and	al, 4				//; Is turbo mode set?
-	jz	@@do_beep
-	mov	bx, 52Eh			//; high pitch blip
-
-@@do_beep:
-	mov	al, 10110110b		//; Timer IC 8253 square waves
-	out	43h, al 			//;   channel 2, speaker
-	mov	ax, bx
-	out	42h, al 			//;   send low order
-	mov	al, ah				//;   load high order
-	out	42h, al 			//;   send high order
-	in	al, 61h 			//; Read IC 8255 machine status
-	push	ax
-	or	al, 00000011b
-	out	61h, al 			//; Turn speaker on
-	mov	cx, 2000h
-@@delay:
-	loop	@@delay
-	pop	ax
-	out	61h, al 			//; Turn speaker off
-	pop	cx
-	pop	bx
-	pop	ax
+		push	ax
+		push	bx
+		push	cx
+		in	al, 61h 			//; Read equipment flags
+		xor	al, bakapee			//;   toggle speed
+		out	61h, al 			//; Write new flags back
+
+		mov	bx, 0F89h			//; low pitch blip
+		and	al, 4				//; Is turbo mode set?
+		jz	@@do_beep
+		mov	bx, 52Eh			//; high pitch blip
+
+	@@do_beep:
+		mov	al, 10110110b		//; Timer IC 8253 square waves
+		out	43h, al 			//;   channel 2, speaker
+		mov	ax, bx
+		out	42h, al 			//;   send low order
+		mov	al, ah				//;   load high order
+		out	42h, al 			//;   send high order
+		in	al, 61h 			//; Read IC 8255 machine status
+		push	ax
+		or	al, 00000011b
+		out	61h, al 			//; Turn speaker on
+		mov	cx, 2000h
+	@@delay:
+		loop	@@delay
+		pop	ax
+		out	61h, al 			//; Turn speaker off
+		pop	cx
+		pop	bx
+		pop	ax
 	}
 }
 #endif
diff --git a/src/lib/16_tdef.h b/src/lib/16_tdef.h
index 08b96996..e9f19bbd 100755
--- a/src/lib/16_tdef.h
+++ b/src/lib/16_tdef.h
@@ -642,17 +642,17 @@ typedef struct	//TODO: USE THIS!!!!
 //==========================================================================
 
 //actual global game varables!
-typedef enum {
+/*typedef enum {
 	ENGI_QUIT,
 	ENGI_RUN,
 	ENGI_MENU,
 	ENGI_PAUSE
-} engi_stat_t;
+} engi_stat_t;*/
 //ENGI_INPUT,
 
 typedef struct
 {
-	engi_stat_t	engi_stat;
+//----	engi_stat_t	engi_stat;
 	video_t	video;	// video settings variable
 	ca_t		ca;	// ca stuff
 	pm_t		pm;	// pm stuff
diff --git a/src/lib/16_vl.h b/src/lib/16_vl.h
index b1b03a21..cec79507 100755
--- a/src/lib/16_vl.h
+++ b/src/lib/16_vl.h
@@ -84,6 +84,9 @@ extern byte far*  VGA;  /* The VGA Memory */
 #define LOW_ADDRESS		0x0D
 #define VRETRACE		0x08
 //#define INPUT_STATUS_1		0x03da	defined in 16_head
+#define STATUS_REGISTER_1	INPUT_STATUS_1
+#define ATR_INDEX			AC_INDEX
+#define ATR_OVERSCAN		17
 #define DISPLAY_ENABLE		0x01
 #define MAP_MASK		0x02
 #define PAL_READ_REG			0x03C7   /* Color register, read address */
diff --git a/src/lib/hb/c3_act1.c b/src/lib/hb/c3_act1.c
new file mode 100755
index 00000000..2706b4f1
--- /dev/null
+++ b/src/lib/hb/c3_act1.c
@@ -0,0 +1,1259 @@
+/* Catacomb 3-D Source Code
+ * Copyright (C) 1993-2014 Flat Rock Software
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// C3_PLAY.C
+
+#include "C3_DEF.H"
+#pragma hdrstop
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+
+dirtype dirtable[9] = {northwest,north,northeast,west,nodir,east,
+	southwest,south,southeast};
+
+
+
+/*
+=============================================================================
+
+						  BONUS ITEMS
+
+=============================================================================
+*/
+
+extern statetype	s_boltbonus2;
+extern statetype	s_nukebonus2;
+
+statetype s_boltbonus = {BOLTOBJPIC,8,NULL,&s_boltbonus2};
+statetype s_boltbonus2 = {BOLTOBJ2PIC,8,NULL,&s_boltbonus};
+
+statetype s_nukebonus = {NUKEOBJPIC,8,NULL,&s_nukebonus2};
+statetype s_nukebonus2 = {NUKEOBJ2PIC,8,NULL,&s_nukebonus};
+
+statetype s_potionbonus = {POTIONOBJPIC,0,NULL,&s_potionbonus};
+statetype s_rkeybonus = {RKEYOBJPIC,0,NULL,&s_rkeybonus};
+statetype s_ykeybonus = {YKEYOBJPIC,0,NULL,&s_ykeybonus};
+statetype s_gkeybonus = {GKEYOBJPIC,0,NULL,&s_gkeybonus};
+statetype s_bkeybonus = {BKEYOBJPIC,0,NULL,&s_bkeybonus};
+statetype s_scrollbonus = {SCROLLOBJPIC,0,NULL,&s_scrollbonus};
+statetype s_chestbonus = {CHESTOBJPIC,0,NULL,&s_chestbonus};
+statetype s_goalbonus = {NEMESISPIC,0,NULL,&s_goalbonus};
+
+/*
+===============
+=
+= SpawnBonus
+=
+===============
+*/
+
+void SpawnBonus (int tilex, int tiley, int number)
+{
+	statetype *state;
+
+	if (number == B_BOLT)
+		state = &s_boltbonus;
+	else if (number == B_NUKE)
+		state = &s_nukebonus;
+	else if (number == B_POTION)
+		state = &s_potionbonus;
+	else if (number == B_RKEY)
+		state = &s_rkeybonus;
+	else if (number == B_YKEY)
+		state = &s_ykeybonus;
+	else if (number == B_GKEY)
+		state = &s_gkeybonus;
+	else if (number == B_BKEY)
+		state = &s_bkeybonus;
+	else if (number >= B_SCROLL1 && number <= B_SCROLL8)
+		state = &s_scrollbonus;
+	else if (number == B_CHEST)
+		state = &s_chestbonus;
+	else if (number == B_GOAL)
+		state = &s_goalbonus;
+
+	SpawnNewObj (tilex,tiley,state,TILEGLOBAL/2);
+	new->tileobject = true;
+	new->temp1 = number;
+	new->obclass = bonusobj;
+	new->shootable = false;
+}
+
+
+/*
+=============================================================================
+
+					  EXPLODING WALL
+
+=============================================================================
+*/
+
+
+void T_WallDie (objtype *ob);
+
+extern	statetype s_walldie1;
+extern	statetype s_walldie2;
+extern	statetype s_walldie3;
+extern	statetype s_walldie4;
+extern	statetype s_walldie5;
+extern	statetype s_walldie6;
+
+statetype s_walldie1 = {0,20,NULL,&s_walldie2};
+statetype s_walldie2 = {0,-1,T_WallDie,&s_walldie3};
+statetype s_walldie3 = {0,20,NULL,&s_walldie4};
+statetype s_walldie4 = {0,-1,T_WallDie,&s_walldie5};
+statetype s_walldie5 = {0,20,NULL,&s_walldie6};
+statetype s_walldie6 = {0,-1,T_WallDie,NULL};
+
+/*
+================
+=
+= ExplodeWall
+=
+================
+*/
+
+void ExplodeWall (int tilex, int tiley)
+{
+	SpawnNewObj (tilex,tiley,&s_walldie1,0);
+	new->obclass = inertobj;
+	new->active = true;
+	(unsigned)actorat[new->tilex][new->tiley] = tilemap[new->tilex][new->tiley] =
+	*(mapsegs[0]+farmapylookup[new->tiley]+new->tilex) = WALLEXP;
+}
+
+
+/*
+================
+=
+= T_WallDie
+=
+================
+*/
+
+void T_WallDie (objtype *ob)
+{
+	unsigned tile,other;
+
+	if (++ob->temp1 == 3)
+		tile = 0;
+	else
+		tile = WALLEXP-1 + ob->temp1;
+
+	(unsigned)actorat[ob->tilex][ob->tiley] = tilemap[ob->tilex][ob->tiley] =
+	*(mapsegs[0]+farmapylookup[ob->tiley]+ob->tilex) = tile;
+
+	if (ob->temp1 == 1)
+	{
+	//
+	// blow up nearby walls
+	//
+		other = tilemap[ob->tilex-1][ob->tiley];
+		if ((unsigned)(other-EXPWALLSTART)<NUMEXPWALLS)
+			ExplodeWall (ob->tilex-1,ob->tiley);
+		other = tilemap[ob->tilex+1][ob->tiley];
+		if ((unsigned)(other-EXPWALLSTART)<NUMEXPWALLS)
+			ExplodeWall (ob->tilex+1,ob->tiley);
+		other = tilemap[ob->tilex][ob->tiley-1];
+		if ((unsigned)(other-EXPWALLSTART)<NUMEXPWALLS)
+			ExplodeWall (ob->tilex,ob->tiley-1);
+		other = tilemap[ob->tilex][ob->tiley+1];
+		if ((unsigned)(other-EXPWALLSTART)<NUMEXPWALLS)
+			ExplodeWall (ob->tilex,ob->tiley+1);
+	}
+}
+
+
+/*
+=============================================================================
+
+						 WARP GATE
+
+=============================================================================
+*/
+
+void T_Gate (objtype *ob);
+
+extern	statetype s_gate1;
+extern	statetype s_gate2;
+extern	statetype s_gate3;
+extern	statetype s_gate4;
+
+extern	statetype s_fgate1;
+extern	statetype s_fgate2;
+extern	statetype s_fgate3;
+extern	statetype s_fgate4;
+
+statetype s_gate1 = {WARP1PIC,12,T_Gate,&s_gate2};
+statetype s_gate2 = {WARP2PIC,12,T_Gate,&s_gate3};
+statetype s_gate3 = {WARP3PIC,12,T_Gate,&s_gate4};
+statetype s_gate4 = {WARP4PIC,12,T_Gate,&s_gate1};
+
+statetype s_fgate1 = {WARP1PIC,6,T_Gate,&s_fgate2};
+statetype s_fgate2 = {WARP2PIC,6,T_Gate,&s_fgate3};
+statetype s_fgate3 = {WARP3PIC,6,T_Gate,&s_fgate4};
+statetype s_fgate4 = {WARP4PIC,6,T_Gate,&s_fgate1};
+
+/*
+===============
+=
+= SpawnWarp
+=
+===============
+*/
+
+void SpawnWarp (int tilex, int tiley, int type)
+{
+	if (type)
+		SpawnNewObj (tilex,tiley,&s_fgate1,TILEGLOBAL/3);
+	else
+		SpawnNewObj (tilex,tiley,&s_gate1,TILEGLOBAL/3);
+	new->obclass = gateobj;
+	new->temp1 = type;
+}
+
+
+/*
+===============
+=
+= T_Gate
+=
+===============
+*/
+
+#define STATUSCOLOR	4
+
+void T_Gate (objtype *ob)
+{
+	int	spot;
+	objtype *check;
+	unsigned	temp;
+
+	if (CheckHandAttack (ob) && !playstate)
+	{
+	//
+	// warp
+	//
+		temp = bufferofs;
+		bufferofs = 0;
+		VW_Bar (26,4,232,9,STATUSCOLOR);		// clear text description
+		bufferofs = temp;
+		IN_ClearKeysDown ();
+		if (ob->temp1)
+		{
+		//
+		// teleport inside level
+		//
+			for (check=player->next;check;check=check->next)
+				if (check->obclass==gateobj && check->temp1==ob->temp1 &&
+					check != ob)
+				{
+					player->x = check->x;
+					player->y = check->y;
+					Thrust (player->angle,TILEGLOBAL/2);		// move forwards
+					Thrust (player->angle,TILEGLOBAL/2);		// move forwards
+					Thrust (player->angle,TILEGLOBAL/2);		// move forwards
+					fizzlein=true;
+				}
+		}
+		else
+		{
+		//
+		// teleport out of level
+		//
+			playstate = ex_warped;
+			spot = *(mapsegs[0]+farmapylookup[ob->tiley]+ob->tilex)-NAMESTART;
+			if (spot<1)
+				gamestate.mapon++;
+			else
+				gamestate.mapon=spot-1;
+			SD_PlaySound(WARPUPSND);
+		}
+	}
+}
+
+
+/*
+=============================================================================
+
+						   TROLLS
+
+=============================================================================
+*/
+
+void T_Troll (objtype *ob);
+
+extern	statetype s_trollpause;
+
+extern	statetype s_troll1;
+extern	statetype s_troll2;
+extern	statetype s_troll3;
+extern	statetype s_troll4;
+
+extern	statetype s_trollattack1;
+extern	statetype s_trollattack2;
+extern	statetype s_trollattack3;
+
+extern	statetype s_trollouch;
+
+extern	statetype s_trolldie1;
+extern	statetype s_trolldie2;
+extern	statetype s_trolldie3;
+
+
+statetype s_trollpause = {TROLL1PIC,40,NULL,&s_troll2};
+
+statetype s_troll1 = {TROLL1PIC,13,T_Troll,&s_troll2};
+statetype s_troll2 = {TROLL2PIC,13,T_Troll,&s_troll3};
+statetype s_troll3 = {TROLL3PIC,13,T_Troll,&s_troll4};
+statetype s_troll4 = {TROLL4PIC,13,T_Troll,&s_troll1};
+
+statetype s_trollattack1 = {TROLLATTACK1PIC,20,NULL,&s_trollattack2};
+statetype s_trollattack2 = {TROLLATTACK2PIC,10,T_DoDamage,&s_trollattack3};
+statetype s_trollattack3 = {TROLLATTACK2PIC,40,NULL,&s_trollpause};
+
+statetype s_trollouch = {TROLLOUCHPIC,8,NULL,&s_troll1};
+
+statetype s_trolldie1 = {TROLLDIE1PIC,8,NULL,&s_trolldie2};
+statetype s_trolldie2 = {TROLLDIE2PIC,8,NULL,&s_trolldie3};
+statetype s_trolldie3 = {TROLLDIE3PIC,0,NULL,&s_trolldie3};
+
+
+/*
+===============
+=
+= SpawnTroll
+=
+===============
+*/
+
+void SpawnTroll (int tilex, int tiley)
+{
+	SpawnNewObj(tilex,tiley,&s_troll1,40*PIXRADIUS);
+	new->speed = 2500;
+	new->obclass = trollobj;
+	new->shootable = true;
+	new->hitpoints = 10;
+}
+
+
+/*
+===============
+=
+= T_Troll
+=
+===============
+*/
+
+void T_Troll (objtype *ob)
+{
+	if (Chase (ob,true))
+	{
+		ob->state = &s_trollattack1;
+		ob->ticcount = ob->state->tictime;
+		return;
+	}
+}
+
+
+
+/*
+=============================================================================
+
+						   ORCS
+
+=============================================================================
+*/
+
+void T_Orc (objtype *ob);
+
+extern	statetype s_orcpause;
+
+extern	statetype s_orc1;
+extern	statetype s_orc2;
+extern	statetype s_orc3;
+extern	statetype s_orc4;
+
+extern	statetype s_orcattack1;
+extern	statetype s_orcattack2;
+extern	statetype s_orcattack3;
+
+extern	statetype s_orcouch;
+
+extern	statetype s_orcdie1;
+extern	statetype s_orcdie2;
+extern	statetype s_orcdie3;
+
+
+
+statetype s_orcpause = {ORC1PIC,40,NULL,&s_orc2};
+
+statetype s_orc1 = {ORC1PIC,20,T_Orc,&s_orc2};
+statetype s_orc2 = {ORC2PIC,20,T_Orc,&s_orc3};
+statetype s_orc3 = {ORC3PIC,20,T_Orc,&s_orc4};
+statetype s_orc4 = {ORC4PIC,20,T_Orc,&s_orc1};
+
+statetype s_orcattack1 = {ORCATTACK1PIC,20,NULL,&s_orcattack2};
+statetype s_orcattack2 = {ORCATTACK2PIC,10,T_DoDamage,&s_orcattack3};
+statetype s_orcattack3 = {ORCATTACK2PIC,40,NULL,&s_orcpause};
+
+statetype s_orcouch = {ORCOUCHPIC,10,NULL,&s_orc1};
+
+statetype s_orcdie1 = {ORCDIE1PIC,8,NULL,&s_orcdie2};
+statetype s_orcdie2 = {ORCDIE2PIC,8,NULL,&s_orcdie3};
+statetype s_orcdie3 = {ORCDIE3PIC,0,NULL,&s_orcdie3};
+
+
+/*
+===============
+=
+= SpawnOrc
+=
+===============
+*/
+
+void SpawnOrc (int tilex, int tiley)
+{
+	SpawnNewObj(tilex,tiley,&s_orc1,PIXRADIUS*32);
+	new->obclass = orcobj;
+	new->speed = 1536;
+	new->shootable = true;
+	new->hitpoints = 3;
+}
+
+
+/*
+===============
+=
+= T_Orc
+=
+===============
+*/
+
+void T_Orc (objtype *ob)
+{
+	if (Chase (ob,true))
+	{
+		ob->state = &s_orcattack1;
+		ob->ticcount = ob->state->tictime;
+		return;
+	}
+}
+
+
+/*
+=============================================================================
+
+						   DEMON
+
+=============================================================================
+*/
+
+void T_Demon (objtype *ob);
+
+
+extern	statetype s_demonpause;
+
+extern	statetype s_demon1;
+extern	statetype s_demon2;
+extern	statetype s_demon3;
+extern	statetype s_demon4;
+
+extern	statetype s_demonattack1;
+extern	statetype s_demonattack2;
+extern	statetype s_demonattack3;
+
+extern	statetype s_demonouch;
+
+extern	statetype s_demondie1;
+extern	statetype s_demondie2;
+extern	statetype s_demondie3;
+
+
+statetype s_demonpause = {DEMON1PIC,40,NULL,&s_demon2};
+
+statetype s_demon1 = {DEMON1PIC,20,T_Demon,&s_demon2};
+statetype s_demon2 = {DEMON2PIC,20,T_Demon,&s_demon3};
+statetype s_demon3 = {DEMON3PIC,20,T_Demon,&s_demon4};
+statetype s_demon4 = {DEMON4PIC,20,T_Demon,&s_demon1};
+
+statetype s_demonattack1 = {DEMONATTACK1PIC,20,NULL,&s_demonattack2};
+statetype s_demonattack2 = {DEMONATTACK2PIC,20,T_DoDamage,&s_demonattack3};
+statetype s_demonattack3 = {DEMONATTACK3PIC,30,NULL,&s_demonpause};
+
+statetype s_demonouch = {DEMONOUCHPIC,10,NULL,&s_demon1};
+
+statetype s_demondie1 = {DEMONDIE1PIC,20,NULL,&s_demondie2};
+statetype s_demondie2 = {DEMONDIE2PIC,20,NULL,&s_demondie3};
+statetype s_demondie3 = {DEMONDIE3PIC,0,NULL,&s_demondie3};
+
+
+
+/*
+===============
+=
+= SpawnDemon
+=
+===============
+*/
+
+void SpawnDemon (int tilex, int tiley)
+{
+	SpawnNewObj(tilex,tiley,&s_demon1,TILEGLOBAL/2);
+	new->obclass = demonobj;
+	new->speed = 2048;
+	new->shootable = true;
+	new->hitpoints = 50;
+}
+
+
+/*
+===============
+=
+= T_Demon
+=
+===============
+*/
+
+void T_Demon (objtype *ob)
+{
+	if (Chase (ob,true))
+	{
+		ob->state = &s_demonattack1;
+		ob->ticcount = ob->state->tictime;
+		return;
+	}
+}
+
+/*
+=============================================================================
+
+							MSHOTS
+
+temp1 = dir
+
+=============================================================================
+*/
+
+#define MSHOTDAMAGE	2
+#define MSHOTSPEED	10000
+
+void T_Mshot (objtype *ob);
+
+
+extern	statetype s_mshot1;
+extern	statetype s_mshot2;
+
+statetype s_mshot1 = {PSHOT1PIC,8,&T_Mshot,&s_mshot2};
+statetype s_mshot2 = {PSHOT2PIC,8,&T_Mshot,&s_mshot1};
+
+
+/*
+===============
+=
+= T_Mshot
+=
+===============
+*/
+
+void T_Mshot (objtype *ob)
+{
+	objtype	*check;
+	long	xmove,ymove,speed;
+
+	xmove = ymove = 0;
+
+	switch (ob->dir)
+	{
+	case north:
+		ymove = -ob->speed*tics;
+		break;
+	case east:
+		xmove = ob->speed*tics;
+		break;
+	case south:
+		ymove = ob->speed*tics;
+		break;
+	case west:
+		xmove = -ob->speed*tics;
+		break;
+	}
+
+	ob->x+=xmove;
+	ob->y+=ymove;
+
+	CalcBounds (ob);
+
+	ob->tilex = ob->x>>TILESHIFT;
+	ob->tiley = ob->y>>TILESHIFT;
+
+	if (tilemap[ob->tilex][ob->tiley])
+	{
+		SD_PlaySound (SHOOTWALLSND);
+		ob->state = NULL;
+		return;
+	}
+
+//
+// check final position for monsters hit
+//
+	if ( ob->xl <= player->xh
+	&& ob->xh >= player->xl
+	&& ob->yl <= player->yh
+	&& ob->yh >= player->yl)
+	{
+		TakeDamage (MSHOTDAMAGE*2);
+		ob->state = NULL;
+		return;
+	}
+
+	for (check = player->next; check; check=check->next)
+		if (ob->shootable && ob->obclass != mageobj
+		&& ob->xl <= check->xh
+		&& ob->xh >= check->xl
+		&& ob->yl <= check->yh
+		&& ob->yh >= check->yl)
+		{
+			ShootActor (check,MSHOTDAMAGE);
+			ob->state = NULL;
+			return;
+		}
+}
+
+
+
+
+/*
+=============================================================================
+
+							MAGE
+
+=============================================================================
+*/
+
+
+void T_Mage (objtype *ob);
+void T_MageShoot (objtype *ob);
+
+extern	statetype s_magepause;
+
+extern	statetype s_mage1;
+extern	statetype s_mage2;
+
+extern	statetype s_mageattack1;
+extern	statetype s_mageattack2;
+extern	statetype s_mageattack3;
+
+extern	statetype s_mageouch;
+
+extern	statetype s_magedie1;
+extern	statetype s_magedie2;
+
+
+statetype s_magepause = {MAGE1PIC,100,NULL,&s_mage2};
+
+statetype s_mage1 = {MAGE1PIC,20,T_Mage,&s_mage2};
+statetype s_mage2 = {MAGE2PIC,20,T_Mage,&s_mage1};
+
+statetype s_mageattack1 = {MAGEATTACKPIC,20,NULL,&s_mageattack2};
+statetype s_mageattack2 = {MAGEATTACKPIC,-1,T_MageShoot,&s_mageattack3};
+statetype s_mageattack3 = {MAGEATTACKPIC,30,NULL,&s_magepause};
+
+statetype s_mageouch = {MAGEOUCHPIC,10,NULL,&s_mage1};
+
+statetype s_magedie1 = {MAGEDIE1PIC,20,NULL,&s_magedie2};
+statetype s_magedie2 = {MAGEDIE2PIC,0,NULL,&s_magedie2};
+
+
+/*
+===============
+=
+= SpawnMage
+=
+===============
+*/
+
+void SpawnMage (int tilex, int tiley)
+{
+	SpawnNewObj(tilex,tiley,&s_mage1,TILEGLOBAL/2);
+	new->obclass = mageobj;
+	new->speed = 2048;
+	new->shootable = true;
+	new->hitpoints = 5;
+}
+
+
+/*
+===============
+=
+= T_Mage
+=
+===============
+*/
+
+void T_Mage (objtype *ob)
+{
+	Chase (ob,false);
+//
+// check for line up with player
+//
+
+	if (ob->x-PIXRADIUS*14 < player->xh
+	&& ob->x+PIXRADIUS > player->xl)
+	{
+		ob->temp1 = 1;
+		ob->state = &s_mageattack1;
+	}
+	else if (ob->y-PIXRADIUS*14 < player->yh
+	&& ob->y+PIXRADIUS > player->yl)
+	{
+		ob->temp1 = 0;
+		ob->state = &s_mageattack1;
+	}
+}
+
+
+/*
+===============
+=
+= T_MageShoot
+=
+===============
+*/
+
+void T_MageShoot (objtype *ob)
+{
+	SpawnNewObjFrac (ob->x,ob->y,&s_mshot1,PIXRADIUS*14);
+	new->obclass = mshotobj;
+	new->speed = MSHOTSPEED;
+	if (ob->temp1)
+	{
+		if (ob->tiley < player->tiley)
+			new->dir = south;
+		else
+			new->dir = north;
+	}
+	else
+	{
+		if (ob->tilex < player->tilex)
+			new->dir = east;
+		else
+			new->dir = west;
+	}
+}
+
+
+/*
+=============================================================================
+
+							nemesis
+
+=============================================================================
+*/
+
+
+void T_Nemesis (objtype *ob);
+void T_NemesisShoot (objtype *ob);
+
+extern	statetype s_grelpause;
+
+extern	statetype s_grel1;
+extern	statetype s_grel2;
+
+extern	statetype s_grelattack1;
+extern	statetype s_grelattack2;
+extern	statetype s_grelattack3;
+
+extern	statetype s_grelouch;
+
+extern	statetype s_greldie1;
+extern	statetype s_greldie2;
+extern	statetype s_greldie3;
+extern	statetype s_greldie4;
+extern	statetype s_greldie5;
+extern	statetype s_greldie6;
+
+
+statetype s_grelpause = {GREL1PIC,50,NULL,&s_grel2};
+
+statetype s_grel1 = {GREL1PIC,20,T_Nemesis,&s_grel2};
+statetype s_grel2 = {GREL2PIC,20,T_Nemesis,&s_grel1};
+
+statetype s_grelattack1 = {GRELATTACKPIC,20,NULL,&s_grelattack2};
+statetype s_grelattack2 = {GRELATTACKPIC,-1,T_NemesisShoot,&s_grelattack3};
+statetype s_grelattack3 = {GRELATTACKPIC,30,NULL,&s_grelpause};
+
+statetype s_grelouch = {GRELHITPIC,6,NULL,&s_grel1};
+
+statetype s_greldie1 = {GRELDIE1PIC,20,NULL,&s_greldie2};
+statetype s_greldie2 = {GRELDIE2PIC,20,NULL,&s_greldie3};
+statetype s_greldie3 = {GRELDIE3PIC,20,NULL,&s_greldie4};
+statetype s_greldie4 = {GRELDIE4PIC,20,NULL,&s_greldie5};
+statetype s_greldie5 = {GRELDIE5PIC,20,NULL,&s_greldie6};
+statetype s_greldie6 = {GRELDIE6PIC,0,NULL,&s_greldie6};
+
+
+/*
+===============
+=
+= SpawnNemesis
+=
+===============
+*/
+
+void SpawnNemesis (int tilex, int tiley)
+{
+	SpawnNewObj(tilex,tiley,&s_grel1,PIXRADIUS*56);
+	new->obclass = grelmobj;
+	new->speed = 2048;
+	new->shootable = true;
+	new->hitpoints = 100;
+}
+
+
+/*
+===============
+=
+= T_Nemesis
+=
+===============
+*/
+
+void T_Nemesis (objtype *ob)
+{
+	Chase (ob,false);
+//
+// check for line up with player
+//
+	if (ob->tilex == player->tilex)
+	{
+		ob->temp1 = 1;
+		ob->state = &s_grelattack1;
+	}
+	else if (ob->tiley == player->tiley)
+	{
+		ob->temp1 = 0;
+		ob->state = &s_grelattack1;
+	}
+}
+
+
+/*
+===============
+=
+= T_NemesisShoot
+=
+===============
+*/
+
+void T_NemesisShoot (objtype *ob)
+{
+	SpawnNewObjFrac (ob->x,ob->y,&s_mshot1,PIXRADIUS*14);
+	new->obclass = mshotobj;
+	new->speed = MSHOTSPEED;
+	if (ob->temp1)
+	{
+		if (ob->tiley < player->tiley)
+			new->dir = south;
+		else
+			new->dir = north;
+	}
+	else
+	{
+		if (ob->tilex < player->tilex)
+			new->dir = east;
+		else
+			new->dir = west;
+	}
+}
+
+
+/*
+=============================================================================
+
+						   BAT
+
+=============================================================================
+*/
+
+void T_Bat (objtype *ob);
+void T_BatPast (objtype *ob);
+
+extern	statetype s_bat1;
+extern	statetype s_bat2;
+extern	statetype s_bat3;
+extern	statetype s_bat4;
+
+extern	statetype s_batdie1;
+extern	statetype s_batdie2;
+
+
+statetype s_bat1 = {BAT1PIC,6,T_Bat,&s_bat2};
+statetype s_bat2 = {BAT2PIC,6,T_Bat,&s_bat3};
+statetype s_bat3 = {BAT3PIC,6,T_Bat,&s_bat4};
+statetype s_bat4 = {BAT4PIC,6,T_Bat,&s_bat1};
+
+statetype s_batpast = {BAT4PIC,80,T_BatPast,&s_bat1};
+
+statetype s_batdie1 = {BATDIE1PIC,8,NULL,&s_batdie2};
+statetype s_batdie2 = {BATDIE2PIC,8,NULL,NULL};
+
+
+/*
+===============
+=
+= SpawnBat
+=
+===============
+*/
+
+void SpawnBat (int tilex, int tiley)
+{
+	SpawnNewObj(tilex,tiley,&s_bat1,PIXRADIUS*24);
+	new->obclass =batobj;
+	new->shootable = true;
+
+	new->hitpoints = 1;
+	new->speed = 2000;
+}
+
+
+/*
+==================================
+=
+= BatChaseThink
+=
+==================================
+*/
+
+void BatChaseThink (objtype *obj)
+{
+	int deltax,deltay;
+
+	deltax=player->tilex - obj->tilex;
+	deltay=player->tiley - obj->tiley;
+
+	if (deltax>0)
+		deltax = 2;
+	else if (deltax<0)
+		deltax = 0;
+	else deltax = 1;
+
+	if (deltay>0)
+		deltay = 2;
+	else if (deltay<0)
+		deltay = 0;
+	else deltay = 1;
+
+	obj->dir = dirtable[deltay*3+deltax];
+	if (Walk(obj))
+		return;
+
+	obj->dir = dirtable[3+deltax];
+	if (Walk(obj))
+		return;
+
+	obj->dir = dirtable[deltay*3+1];
+	if (Walk(obj))
+		return;
+
+	obj->dir = nodir;
+}
+
+
+void BatRunThink (objtype *obj)
+{
+	int deltax,deltay;
+
+	deltax=player->tilex - obj->tilex;
+	deltay=player->tiley - obj->tiley;
+
+	if (deltax>=0)
+		deltax = 0;
+	else
+		deltax = 2;
+
+	if (deltay>=0)
+		deltay = 0;
+	else
+		deltay = 2;
+
+	obj->dir = dirtable[deltay*3+deltax];
+	if (Walk(obj))
+		return;
+
+	obj->dir = dirtable[3+deltax];
+	if (Walk(obj))
+		return;
+
+	obj->dir = dirtable[deltay*3+1];
+	Walk(obj);
+}
+
+
+
+/*
+===============
+=
+= T_Bat
+=
+===============
+*/
+
+void T_Bat (objtype *ob)
+{
+	long move;
+	long deltax,deltay,size;
+
+	move = ob->speed*tics;
+	size = (long)ob->size + player->size + move;
+
+
+	do
+	{
+		deltax = ob->x - player->x;
+		deltay = ob->y - player->y;
+
+		if (deltax <= size && deltax >= -size
+		&& deltay <= size && deltay >= -size && !ob->temp1)
+		{
+			TakeDamage (4);
+			ob->temp1 = 2;
+		}
+
+		if (move < ob->distance)
+		{
+			MoveObj (ob,move);
+			break;
+		}
+
+		actorat[ob->tilex][ob->tiley] = 0;	// pick up marker from goal
+		if (ob->dir == nodir)
+			ob->dir = north;
+
+		ob->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;
+		ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;
+		move -= ob->distance;
+
+		if (ob->temp1)
+		{
+			Walk (ob);				// go straight
+			if (!--ob->temp1)
+			{
+				ob->state = &s_batpast;
+				ob->ticcount = ob->state->tictime;
+			}
+		}
+		else
+			BatChaseThink (ob);		// head towards player
+
+		actorat[ob->tilex][ob->tiley] = ob;	// set down a new goal marker
+	} while (0);	// just once
+	CalcBounds (ob);
+}
+
+
+/*
+===============
+=
+= T_BatPast
+=
+===============
+*/
+
+void T_BatPast (objtype *ob)
+{
+	long move;
+	long deltax,deltay,size;
+
+	move = ob->speed*tics;
+
+	do
+	{
+		if (move < ob->distance)
+		{
+			MoveObj (ob,move);
+			break;
+		}
+		actorat[ob->tilex][ob->tiley] = 0;	// pick up marker from goal
+
+		ob->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;
+		ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;
+		move -= ob->distance;
+
+		BatRunThink (ob);
+
+		actorat[ob->tilex][ob->tiley] = ob;	// set down a new goal marker
+	} while (0);	//(move)
+	CalcBounds (ob);
+}
+
+
+
+/*
+=============================================================================
+
+						   BOUNCE
+
+temp2 = set when hit player, reset when hit wall
+
+=============================================================================
+*/
+
+#define SPDBOUNCE	4096
+#define DMGBOUNCE	10
+
+void T_Bounce (objtype *ob);
+
+extern	statetype s_bounce1;
+extern	statetype s_bounce2;
+
+
+statetype s_bounce1 = {BIGPSHOT1PIC,8,T_Bounce,&s_bounce2};
+statetype s_bounce2 = {BIGPSHOT2PIC,8,T_Bounce,&s_bounce1};
+
+/*
+===============
+=
+= SpawnBounce
+=
+===============
+*/
+
+void SpawnBounce (int tilex, int tiley, boolean towest)
+{
+	SpawnNewObj(tilex,tiley,&s_bounce1,24*PIXRADIUS);
+	new->obclass = bounceobj;
+	if (towest)
+		new->dir = west;
+	else
+		new->dir = north;
+}
+
+
+/*
+===============
+=
+= T_Bounce
+=
+===============
+*/
+
+void T_Bounce (objtype *ob)
+{
+	long move;
+	long deltax,deltay,size;
+
+	move = SPDBOUNCE*tics;
+	size = (long)ob->size + player->size + move;
+
+	while (move)
+	{
+		deltax = ob->x - player->x;
+		deltay = ob->y - player->y;
+
+		if (deltax <= size && deltax >= -size
+		&& deltay <= size && deltay >= -size && !ob->temp2)
+		{
+			ob->temp2 = 1;
+			TakeDamage (DMGBOUNCE);
+		}
+
+		if (move < ob->distance)
+		{
+			MoveObj (ob,move);
+			break;
+		}
+		actorat[ob->tilex][ob->tiley] = 0;	// pick up marker from goal
+
+		ob->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;
+		ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;
+		move -= ob->distance;
+
+		//
+		// bounce if hit wall
+		//
+		switch (ob->dir)
+		{
+		case north:
+			if (tilemap[ob->tilex][--ob->tiley])
+			{
+				ob->dir = south;
+				ob->tiley+=2;
+				ob->temp2 = 0;
+			}
+			break;
+		case east:
+			if (tilemap[++ob->tilex][ob->tiley])
+			{
+				ob->dir = west;
+				ob->tilex-=2;
+				ob->temp2 = 0;
+			}
+			break;
+		case south:
+			if (tilemap[ob->tilex][++ob->tiley])
+			{
+				ob->dir = north;
+				ob->tiley-=2;
+				ob->temp2 = 0;
+			}
+			break;
+		case west:
+			if (tilemap[--ob->tilex][ob->tiley])
+			{
+				ob->dir = east;
+				ob->tilex+=2;
+				ob->temp2 = 0;
+			}
+			break;
+		}
+
+		ob->distance = TILEGLOBAL;
+
+		actorat[ob->tilex][ob->tiley] = ob;	// set down a new goal marker
+	}
+	CalcBounds (ob);
+}
+
diff --git a/src/lib/hb/c3_asm.asm b/src/lib/hb/c3_asm.asm
new file mode 100755
index 00000000..d9da6eb6
--- /dev/null
+++ b/src/lib/hb/c3_asm.asm
@@ -0,0 +1,197 @@
+; Catacomb 3-D Source Code
+; Copyright (C) 1993-2014 Flat Rock Software
+;
+; 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.,
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+IDEAL
+
+MODEL	MEDIUM,C
+
+VIEWWIDTH	=	(33*8)
+GC_INDEX	=	03CEh
+
+DATASEG
+EVEN
+
+;=================== Tables filled in by DrawVWall ==========================
+
+;
+; wallheight has the height (scale number) of that collumn of scaled wall
+; it is pre bounded to 1-MAXSCALE (the actuial height on screen is 2*height)
+;
+wallheight	dw	VIEWWIDTH dup (?)
+
+;
+; wallwidth has the pixel width (1-7) of that collumn
+;
+wallwidth	dw	VIEWWIDTH dup (?)
+
+;
+; wallseg has the segment of the wall picture
+;
+wallseg		dw	VIEWWIDTH dup (?)
+
+;
+; wallofs has the offset of the wall picture
+;
+wallofs		dw	VIEWWIDTH dup (?)
+
+;============================================================================
+
+;
+; screenbyte is just position/8
+;
+LABEL		screenbyte	WORD
+pos	=	0
+REPT		VIEWWIDTH
+			dw	pos/8
+pos	=	pos+1
+ENDM
+
+;
+; screenbit is (position&7)*16
+;
+LABEL		screenbit	WORD
+pos	=	0
+REPT		VIEWWIDTH
+			dw	(pos AND 7)*16
+pos	=	pos+1
+ENDM
+
+;
+; Use offset: screenbit[]+pixwidth*2
+; acess from bitmasks-2+offset for one biased pixwidth
+; the low byte of bitmasks is for the first screen byte, the high byte
+; is the bitmask for the second screen byte (if non 0)
+;
+
+bitmasks	dw	0080h,00c0h,00e0h,00f0h,00f8h,00fch,00feh,00ffh
+			dw	0040h,0060h,0070h,0078h,007ch,007eh,007fh,807fh
+			dw	0020h,0030h,0038h,003ch,003eh,003fh,803fh,0c03fh
+			dw	0010h,0018h,001ch,001eh,001fh,801fh,0c01fh,0e01fh
+			dw	0008h,000ch,000eh,000fh,800fh,0c00fh,0e00fh,0f00fh
+			dw	0004h,0006h,0007h,8007h,0c007h,0e007h,0f007h,0f807h
+			dw	0002h,0003h,8003h,0c003h,0e003h,0f003h,0f803h,0fc03h
+			dw	0001h,8001h,0c001h,0e001h,0f001h,0f801h,0fc01h,0fe01h
+
+
+;
+; wallscalecall is a far pointer to the start of a compiled scaler
+; The low word will never change, while the high word is set to
+; compscaledirectory[scale]
+;
+wallscalecall	dd	(65*6)			; offset of t_compscale->code[0]
+
+
+PUBLIC	wallheight,wallwidth,wallseg,wallofs,screenbyte,screenbit
+PUBLIC	bitmasks,wallscalecall
+
+
+EXTRN	scaledirectory:WORD			; array of MAXSCALE segment pointers to
+									; compiled scalers
+EXTRN	screenseg:WORD				; basically just 0xa000
+EXTRN	bufferofs:WORD				; offset of the current work screen
+
+CODESEG
+
+;============================================================================
+;
+; ScaleWalls
+;
+; AX	AL is scratched in bit mask setting and scaling
+; BX	table index
+; CX	pixwidth*2
+; DX	GC_INDEX
+; SI	offset into wall data to scale from, allways 0,64,128,...4032
+; DI    byte at top of screen that the collumn is contained in
+; BP	x pixel * 2, index into VIEWWIDTH wide tables
+; DS	segment of the wall data to texture map
+; ES	screenseg
+; SS	addressing DGROUP variables
+;
+;============================================================================
+
+PROC	ScaleWalls
+PUBLIC	ScaleWalls
+USES	SI,DI,BP
+
+	xor	bp,bp						; start at location 0 in the tables
+	mov	dx,GC_INDEX+1
+	mov	es,[screenseg]
+
+;
+; scale one collumn of data, possibly across two bytes
+;
+nextcollumn:
+
+	mov	bx,[wallheight+bp]			; height of walls (1-MAXSCALE)
+	shl	bx,1
+	mov	ax,[ss:scaledirectory+bx]	; segment of the compiled scaler
+	mov [WORD PTR ss:wallscalecall+2],ax
+
+	mov	cx,[wallwidth+bp]
+	or	cx,cx
+	jnz	okwidth
+	mov	cx,2
+	jmp	next
+
+okwidth:
+	shl	cx,1
+	mov	ds,[wallseg+bp]
+	mov	si,[wallofs+bp]
+
+	mov	di,[screenbyte+bp]			; byte at the top of the scaled collumn
+	add	di,[ss:bufferofs]			; offset of current page flip
+	mov	bx,[screenbit+bp]			; 0-7 << 4
+	add	bx,cx
+	mov	ax,[ss:bitmasks-2+bx]
+	out	dx,al						; set bit mask register
+	call [DWORD PTR ss:wallscalecall]		; scale the line of pixels
+	or	ah,ah						; is there anything in the second byte?
+	jnz	secondbyte
+;
+; next
+;
+next:
+	add	bp,cx
+	cmp	bp,VIEWWIDTH*2
+	jb	nextcollumn
+	jmp	done
+
+;
+; draw a second byte for vertical strips that cross two bytes
+;
+secondbyte:
+	mov	al,ah
+	inc	di								; next byte over
+	out	dx,al							; set bit mask register
+	call [DWORD PTR ss:wallscalecall]	; scale the line of pixels
+;
+; next
+;
+	add	bp,cx
+	cmp	bp,VIEWWIDTH*2
+	jb	nextcollumn
+
+done:
+	mov	ax,ss
+	mov	ds,ax
+	ret
+
+ENDP
+
+
+END
+
diff --git a/src/lib/hb/c3_debug.c b/src/lib/hb/c3_debug.c
new file mode 100755
index 00000000..4aa68312
--- /dev/null
+++ b/src/lib/hb/c3_debug.c
@@ -0,0 +1,606 @@
+/* Catacomb 3-D Source Code
+ * Copyright (C) 1993-2014 Flat Rock Software
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// C3_DEBUG.C
+
+#include "C3_DEF.H"
+#pragma hdrstop
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+#define VIEWTILEX	20
+#define VIEWTILEY	(VIEWHEIGHT/16)
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+
+int	maporgx;
+int	maporgy;
+enum {mapview,tilemapview,actoratview,visview}	viewtype;
+
+void ViewMap (void);
+
+//===========================================================================
+
+
+
+/*
+==================
+=
+= DebugMemory
+=
+==================
+*/
+
+void DebugMemory (void)
+{
+	int	i;
+	char    scratch[80],str[10];
+	long	mem;
+	spritetype _seg	*block;
+
+	VW_FixRefreshBuffer ();
+	US_CenterWindow (16,7);
+
+#if 0
+	CA_OpenDebug ();
+	for (i=0;i<NUMCHUNKS;i++)
+	{
+		if (grsegs[i])
+		{
+			strcpy (scratch,"Chunk:");
+			itoa (i,str,10);
+			strcat (scratch,str);
+			strcat (scratch,"\n");
+			write (debughandle,scratch,strlen(scratch));
+		}
+	}
+	CA_CloseDebug ();
+#endif
+
+	US_CPrint ("Memory Usage");
+	US_CPrint ("------------");
+	US_Print ("Total     :");
+	US_PrintUnsigned (mminfo.mainmem/1024);
+	US_Print ("k\nFree      :");
+	US_PrintUnsigned (MM_UnusedMemory()/1024);
+	US_Print ("k\nWith purge:");
+	US_PrintUnsigned (MM_TotalFree()/1024);
+	US_Print ("k\n");
+	VW_UpdateScreen();
+	IN_Ack ();
+}
+
+//===========================================================================
+
+/*
+================
+=
+= PicturePause
+=
+================
+*/
+
+void PicturePause (void)
+{
+	int	y;
+	unsigned	source;
+
+	source = displayofs+panadjust;
+
+	VW_ColorBorder (15);
+	VW_SetLineWidth (40);
+	VW_SetScreen (0,0);
+
+	if (source<0x10000l-200*64)
+	{
+	//
+	// copy top line first
+	//
+		for (y=0;y<200;y++)
+			VW_ScreenToScreen (source+y*64,y*40,40,1);
+	}
+	else
+	{
+	//
+	// copy bottom line first
+	//
+		for (y=199;y>=0;y--)
+			VW_ScreenToScreen (source+y*64,y*40,40,1);
+	}
+
+	IN_Shutdown ();
+
+	VW_WaitVBL(70);
+	bioskey(0);
+	VW_WaitVBL(70);
+	Quit (NULL);
+}
+
+
+//===========================================================================
+
+/*
+================
+=
+= ShapeTest
+=
+================
+*/
+
+void ShapeTest (void)
+{
+
+}
+
+
+//===========================================================================
+
+#define	sc_1			0x02
+#define	sc_2			0x03
+#define	sc_3			0x04
+#define	sc_4			0x05
+#define	sc_5			0x06
+#define	sc_6			0x07
+#define	sc_7			0x08
+#define	sc_8			0x09
+#define	sc_9			0x0a
+#define	sc_0			0x0b
+
+
+
+/*
+================
+=
+= DebugKeys
+=
+================
+*/
+
+int DebugKeys (void)
+{
+	boolean esc;
+	int level,i;
+
+	if (Keyboard[sc_B])		// B = border color
+	{
+		CenterWindow(24,3);
+		PrintY+=6;
+		US_Print(" Border color (0-15):");
+		VW_UpdateScreen();
+		esc = !US_LineInput (px,py,str,NULL,true,2,0);
+		if (!esc)
+		{
+			level = atoi (str);
+			if (level>=0 && level<=15)
+				VW_ColorBorder (level);
+		}
+		return 1;
+	}
+
+#if 0
+
+	if (Keyboard[sc_C])		// C = count objects
+	{
+		CountObjects();
+		return 1;
+	}
+
+
+	if (Keyboard[sc_D])		// D = start / end demo record
+	{
+		if (DemoMode == demo_Off)
+			StartDemoRecord ();
+		else if (DemoMode == demo_Record)
+		{
+			EndDemoRecord ();
+			playstate = ex_completed;
+		}
+		return 1;
+	}
+
+#endif
+
+	if (Keyboard[sc_E])		// E = quit level
+	{
+		if (tedlevel)
+			TEDDeath();
+		playstate = ex_warped;
+		gamestate.mapon++;
+	}
+
+	if (Keyboard[sc_F])		// F = facing spot
+	{
+		CenterWindow (12,4);
+		US_Print ("X:");
+		US_PrintUnsigned (player->x);
+		US_Print ("Y:");
+		US_PrintUnsigned (player->y);
+		US_Print ("A:");
+		US_PrintUnsigned (player->angle);
+		VW_UpdateScreen();
+		IN_Ack();
+		return 1;
+	}
+
+	if (Keyboard[sc_G])		// G = god mode
+	{
+		CenterWindow (12,2);
+		if (godmode)
+		  US_PrintCentered ("God mode OFF");
+		else
+		  US_PrintCentered ("God mode ON");
+		VW_UpdateScreen();
+		IN_Ack();
+		godmode ^= 1;
+		return 1;
+	}
+	if (Keyboard[sc_H])		// H = hurt self
+	{
+		TakeDamage (5);
+	}
+	else if (Keyboard[sc_I])			// I = item cheat
+	{
+		CenterWindow (12,3);
+		US_PrintCentered ("Free items!");
+		VW_UpdateScreen();
+		for (i=0;i<4;i++)
+		{
+			GiveBolt ();
+			GiveNuke ();
+			GivePotion ();
+			if (!gamestate.keys[i])
+				GiveKey (i);
+		}
+		for (i=0;i<8;i++)
+			GiveScroll (i,false);
+
+		IN_Ack ();
+		return 1;
+	}
+	else if (Keyboard[sc_M])			// M = memory info
+	{
+		DebugMemory();
+		return 1;
+	}
+	else if (Keyboard[sc_O])			// O = overhead
+	{
+		ViewMap();
+		return 1;
+	}
+	else if (Keyboard[sc_P])			// P = pause with no screen disruptioon
+	{
+		PicturePause ();
+		return 1;
+	}
+	else if (Keyboard[sc_S])	// S = slow motion
+	{
+		singlestep^=1;
+		CenterWindow (18,3);
+		if (singlestep)
+			US_PrintCentered ("Slow motion ON");
+		else
+			US_PrintCentered ("Slow motion OFF");
+		VW_UpdateScreen();
+		IN_Ack ();
+		return 1;
+	}
+	else if (Keyboard[sc_S])	// T = shape test
+	{
+		ShapeTest ();
+		return 1;
+	}
+	else if (Keyboard[sc_V])			// V = extra VBLs
+	{
+		CenterWindow(30,3);
+		PrintY+=6;
+		US_Print("  Add how many extra VBLs(0-8):");
+		VW_UpdateScreen();
+		esc = !US_LineInput (px,py,str,NULL,true,2,0);
+		if (!esc)
+		{
+			level = atoi (str);
+			if (level>=0 && level<=8)
+				extravbls = level;
+		}
+		return 1;
+	}
+	else if (Keyboard[sc_W])	// W = warp to level
+	{
+		CenterWindow(26,3);
+		PrintY+=6;
+		US_Print("  Warp to which level(1-21):");
+		VW_UpdateScreen();
+		esc = !US_LineInput (px,py,str,NULL,true,2,0);
+		if (!esc)
+		{
+			level = atoi (str);
+			if (level>0 && level<21)
+			{
+				gamestate.mapon = level-1;
+				playstate = ex_warped;
+			}
+		}
+		return 1;
+	}
+	else if (Keyboard[sc_X])			// X = item cheat
+	{
+		CenterWindow (12,3);
+		US_PrintCentered ("Extra stuff!");
+		VW_UpdateScreen();
+		for (i=0;i<4;i++)
+		{
+			GiveBolt ();
+			GiveNuke ();
+			GivePotion ();
+		}
+		IN_Ack ();
+		return 1;
+	}
+	else if (Keyboard[sc_Z])			// Z = game over
+	{
+
+	}
+	else if (LastScan >= sc_1 && LastScan <= sc_8)	// free scrolls
+	{
+		GiveScroll (LastScan-sc_1,false);
+		IN_ClearKeysDown ();
+	}
+
+	return 0;
+}
+
+
+/*
+=====================
+=
+= LatchDrawChar
+=
+=====================
+*/
+
+void LatchDrawChar (unsigned x, unsigned y, unsigned picnum)
+{
+	unsigned	source, dest;
+
+	dest = bufferofs + ylookup[y]+x;
+	source = latchpics[0]+picnum*8;
+
+	EGAWRITEMODE(1);
+	EGAMAPMASK(15);
+
+asm	mov	bx,[linewidth]
+asm	dec	bx
+
+asm	mov	ax,[screenseg]
+asm	mov	es,ax
+asm	mov	ds,ax
+
+asm	mov	si,[source]
+asm	mov	di,[dest]
+
+asm	movsb
+asm	add	di,bx
+asm	movsb
+asm	add	di,bx
+asm	movsb
+asm	add	di,bx
+asm	movsb
+asm	add	di,bx
+asm	movsb
+asm	add	di,bx
+asm	movsb
+asm	add	di,bx
+asm	movsb
+asm	add	di,bx
+asm	movsb
+
+asm	mov	ax,ss
+asm	mov	ds,ax					// restore turbo's data segment
+
+	EGAWRITEMODE(0);
+}
+
+
+/*
+=====================
+=
+= LatchDrawTile
+=
+=====================
+*/
+
+void LatchDrawTile (unsigned x, unsigned y, unsigned picnum)
+{
+	unsigned	source, dest;
+
+	dest = bufferofs + ylookup[y]+x;
+	source = tileoffsets[picnum];
+
+	EGAWRITEMODE(1);
+	EGAMAPMASK(15);
+
+asm	mov	bx,[linewidth]
+asm	sub	bx,2
+
+asm	mov	ax,[screenseg]
+asm	mov	es,ax
+asm	mov	ds,ax
+
+asm	mov	si,[source]
+asm	mov	di,[dest]
+asm	mov	dx,16
+
+lineloop:
+asm	movsb
+asm	movsb
+asm	add	di,bx
+
+asm	dec	dx
+asm	jnz	lineloop
+
+asm	mov	ax,ss
+asm	mov	ds,ax					// restore turbo's data segment
+
+	EGAWRITEMODE(0);
+}
+
+
+/*
+===================
+=
+= OverheadRefresh
+=
+===================
+*/
+
+void OverheadRefresh (void)
+{
+	unsigned	x,y,endx,endy,sx,sy;
+	unsigned	tile;
+
+
+	if (++screenpage == 3)
+		screenpage = 0;
+
+	bufferofs = screenloc[screenpage];
+
+	endx = maporgx+VIEWTILEX;
+	endy = maporgy+VIEWTILEY;
+
+	for (y=maporgy;y<endy;y++)
+		for (x=maporgx;x<endx;x++)
+		{
+			sx = (x-maporgx)*2;
+			sy = (y-maporgy)*16;
+
+			switch (viewtype)
+			{
+			case mapview:
+				tile = *(mapsegs[0]+farmapylookup[y]+x);
+				break;
+
+			case tilemapview:
+				tile = tilemap[x][y];
+				break;
+
+			case actoratview:
+				tile = (unsigned)actorat[x][y];
+				break;
+
+			case visview:
+				tile = spotvis[x][y];
+				break;
+
+			}
+
+			if (tile<NUMTILE16)
+				LatchDrawTile(sx,sy,tile);
+			else
+			{
+				LatchDrawChar(sx,sy,NUMBERCHARS+((tile&0xf000)>>12));
+				LatchDrawChar(sx+1,sy,NUMBERCHARS+((tile&0x0f00)>>8));
+				LatchDrawChar(sx,sy+8,NUMBERCHARS+((tile&0x00f0)>>4));
+				LatchDrawChar(sx+1,sy+8,NUMBERCHARS+(tile&0x000f));
+			}
+		}
+
+	VW_SetScreen (bufferofs,0);
+	displayofs = bufferofs;
+}
+
+
+/*
+===================
+=
+= ViewMap
+=
+===================
+*/
+
+void ViewMap (void)
+{
+	boolean		button0held;
+
+	viewtype = actoratview;
+	button0held = false;
+
+
+	maporgx = player->tilex - VIEWTILEX/2;
+	if (maporgx<0)
+		maporgx = 0;
+	maporgy = player->tiley - VIEWTILEY/2;
+	if (maporgy<0)
+		maporgy = 0;
+
+	do
+	{
+//
+// let user pan around
+//
+		IN_ReadControl(0,&c);
+		if (c.xaxis == -1 && maporgx>0)
+			maporgx--;
+		if (c.xaxis == 1 && maporgx<mapwidth-VIEWTILEX)
+			maporgx++;
+		if (c.yaxis == -1 && maporgy>0)
+			maporgy--;
+		if (c.yaxis == 1 && maporgy<mapheight-VIEWTILEY)
+			maporgy++;
+
+		if (c.button0 && !button0held)
+		{
+			button0held = true;
+			viewtype++;
+			if (viewtype>visview)
+				viewtype = mapview;
+		}
+		if (!c.button0)
+			button0held = false;
+
+
+		OverheadRefresh ();
+
+	} while (!Keyboard[sc_Escape]);
+
+	IN_ClearKeysDown ();
+	DrawPlayScreen ();
+}
+
+
diff --git a/src/lib/hb/c3_def.h b/src/lib/hb/c3_def.h
new file mode 100755
index 00000000..c0fe07fb
--- /dev/null
+++ b/src/lib/hb/c3_def.h
@@ -0,0 +1,533 @@
+/* Catacomb 3-D Source Code
+ * Copyright (C) 1993-2014 Flat Rock Software
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "ID_HEADS.H"
+#include <MATH.H>
+#include <VALUES.H>
+
+//#define PROFILE
+
+/*
+=============================================================================
+
+						 GLOBAL CONSTANTS
+
+=============================================================================
+*/
+
+#define NAMESTART	180
+
+
+#define UNMARKGRCHUNK(chunk)	(grneeded[chunk]&=~ca_levelbit)
+
+#define MOUSEINT	0x33
+
+#define EXPWALLSTART	8
+#define NUMEXPWALLS		7
+#define WALLEXP			15
+#define NUMFLOORS		36
+
+#define NUMFLOORS	36
+
+#define NUMLATCHPICS	100
+#define NUMSCALEPICS	100
+#define NUMSCALEWALLS	30
+
+
+#define FLASHCOLOR	5
+#define FLASHTICS	4
+
+
+#define NUMLEVELS	20
+
+#define VIEWX		0		// corner of view window
+#define VIEWY		0
+#define VIEWWIDTH	(33*8)		// size of view window
+#define VIEWHEIGHT	(18*8)
+#define VIEWXH		(VIEWX+VIEWWIDTH-1)
+#define VIEWYH		(VIEWY+VIEWHEIGHT-1)
+
+#define CENTERX		(VIEWX+VIEWWIDTH/2-1)	// middle of view window
+#define CENTERY		(VIEWY+VIEWHEIGHT/2-1)
+
+#define GLOBAL1		(1l<<16)
+#define TILEGLOBAL  GLOBAL1
+#define TILESHIFT	16l
+
+#define MINDIST		(2*GLOBAL1/5)
+#define FOCALLENGTH	(TILEGLOBAL)	// in global coordinates
+
+#define ANGLES		360		// must be divisable by 4
+
+#define MAPSIZE		64		// maps are 64*64 max
+#define MAXACTORS	150		// max number of tanks, etc / map
+
+#define NORTH	0
+#define EAST	1
+#define SOUTH	2
+#define WEST	3
+
+#define SIGN(x) ((x)>0?1:-1)
+#define ABS(x) ((int)(x)>0?(x):-(x))
+#define LABS(x) ((long)(x)>0?(x):-(x))
+
+#define	MAXSCALE	(VIEWWIDTH/2)
+
+
+#define MAXBODY			64
+#define MAXSHOTPOWER	56
+
+#define SCREEN1START	0
+#define SCREEN2START	8320
+
+#define PAGE1START		0x900
+#define PAGE2START		0x2000
+#define	PAGE3START		0x3700
+#define	FREESTART		0x4e00
+
+#define PIXRADIUS		512
+
+#define STATUSLINES		(200-VIEWHEIGHT)
+
+enum bonusnumbers {B_BOLT,B_NUKE,B_POTION,B_RKEY,B_YKEY,B_GKEY,B_BKEY,B_SCROLL1,
+ B_SCROLL2,B_SCROLL3,B_SCROLL4,B_SCROLL5,B_SCROLL6,B_SCROLL7,B_SCROLL8,
+ B_GOAL,B_CHEST};
+
+
+/*
+=============================================================================
+
+						   GLOBAL TYPES
+
+=============================================================================
+*/
+
+enum {BLANKCHAR=9,BOLTCHAR,NUKECHAR,POTIONCHAR,KEYCHARS,SCROLLCHARS=17,
+	NUMBERCHARS=25};
+
+typedef long fixed;
+
+typedef struct {int x,y;} tilept;
+typedef struct {fixed x,y;} globpt;
+
+typedef struct
+{
+  int	x1,x2,leftclip,rightclip;// first pixel of wall (may not be visable)
+  unsigned	height1,height2,color,walllength,side;
+	long	planecoord;
+} walltype;
+
+typedef enum
+  {nothing,playerobj,bonusobj,orcobj,batobj,skeletonobj,trollobj,demonobj,
+  mageobj,pshotobj,bigpshotobj,mshotobj,inertobj,bounceobj,grelmobj
+  ,gateobj} classtype;
+
+typedef enum {north,east,south,west,northeast,southeast,southwest,
+		  northwest,nodir} dirtype;		// a catacombs 2 carryover
+
+
+typedef struct	statestruct
+{
+	int		shapenum;
+	int		tictime;
+	void	(*think) ();
+	struct	statestruct	*next;
+} statetype;
+
+
+typedef struct objstruct
+{
+  enum {no,yes}	active;
+  int		ticcount;
+  classtype	obclass;
+  statetype	*state;
+
+  boolean	shootable;
+  boolean	tileobject;		// true if entirely inside one tile
+
+  long		distance;
+  dirtype	dir;
+  fixed 	x,y;
+  unsigned	tilex,tiley;
+  int	 	viewx;
+  unsigned	viewheight;
+
+  int 		angle;
+  int		hitpoints;
+  long		speed;
+
+  unsigned	size;			// global radius for hit rect calculation
+  fixed		xl,xh,yl,yh;	// hit rectangle
+
+  int		temp1,temp2;
+  struct	objstruct	*next,*prev;
+} objtype;
+
+
+typedef	struct
+{
+	int		difficulty;
+	int		mapon;
+	int		bolts,nukes,potions,keys[4],scrolls[8];
+	long	score;
+	int		body,shotpower;
+} gametype;
+
+typedef	enum	{ex_stillplaying,ex_died,ex_warped,ex_resetgame
+	,ex_loadedgame,ex_victorious,ex_abort} exittype;
+
+
+/*
+=============================================================================
+
+						 C3_MAIN DEFINITIONS
+
+=============================================================================
+*/
+
+extern	char		str[80],str2[20];
+extern	unsigned	tedlevelnum;
+extern	boolean		tedlevel;
+extern	gametype	gamestate;
+extern	exittype	playstate;
+
+
+void NewGame (void);
+boolean	SaveTheGame(int file);
+boolean	LoadTheGame(int file);
+void ResetGame(void);
+void ShutdownId (void);
+void InitGame (void);
+void Quit (char *error);
+void TEDDeath(void);
+void DemoLoop (void);
+void SetupScalePic (unsigned picnum);
+void SetupScaleWall (unsigned picnum);
+void SetupScaling (void);
+void main (void);
+
+/*
+=============================================================================
+
+						 C3_GAME DEFINITIONS
+
+=============================================================================
+*/
+
+extern	unsigned	latchpics[NUMLATCHPICS];
+extern	unsigned	tileoffsets[NUMTILE16];
+extern	unsigned	textstarts[27];
+
+
+#define	L_CHARS		0
+#define L_NOSHOT	1
+#define L_SHOTBAR	2
+#define L_NOBODY	3
+#define L_BODYBAR	4
+
+
+void ScanInfoPlane (void);
+void ScanText (void);
+void SetupGameLevel (void);
+void Victory (void);
+void Died (void);
+void NormalScreen (void);
+void DrawPlayScreen (void);
+void LoadLatchMem (void);
+void FizzleFade (unsigned source, unsigned dest,
+	unsigned width,unsigned height, boolean abortable);
+void FizzleOut (int showlevel);
+void FreeUpMemory (void);
+void GameLoop (void);
+
+
+/*
+=============================================================================
+
+						 C3_PLAY DEFINITIONS
+
+=============================================================================
+*/
+
+extern	ControlInfo	c;
+extern	boolean		running,slowturn;
+
+extern	int			bordertime;
+
+extern	byte		tilemap[MAPSIZE][MAPSIZE];
+extern	objtype		*actorat[MAPSIZE][MAPSIZE];
+extern	byte		spotvis[MAPSIZE][MAPSIZE];
+
+extern	objtype 	objlist[MAXACTORS],*new,*obj,*player;
+
+extern	unsigned	farmapylookup[MAPSIZE];
+extern	byte		*nearmapylookup[MAPSIZE];
+extern	byte		update[];
+
+extern	boolean		godmode,singlestep;
+extern	int			extravbls;
+
+extern	int			mousexmove,mouseymove;
+extern	int			pointcount,pointsleft;
+
+
+void CenterWindow(word w,word h);
+void DebugMemory (void);
+void PicturePause (void);
+int  DebugKeys (void);
+void CheckKeys (void);
+void InitObjList (void);
+void GetNewObj (boolean usedummy);
+void RemoveObj (objtype *gone);
+void PollControlls (void);
+void PlayLoop (void);
+
+
+/*
+=============================================================================
+
+						 C3_STATE DEFINITIONS
+
+=============================================================================
+*/
+
+void SpawnNewObj (unsigned x, unsigned y, statetype *state, unsigned size);
+void SpawnNewObjFrac (long x, long y, statetype *state, unsigned size);
+boolean CheckHandAttack (objtype *ob);
+void T_DoDamage (objtype *ob);
+boolean Walk (objtype *ob);
+void ChaseThink (objtype *obj, boolean diagonal);
+void MoveObj (objtype *ob, long move);
+boolean Chase (objtype *ob, boolean diagonal);
+
+extern	dirtype opposite[9];
+
+/*
+=============================================================================
+
+						 C3_TRACE DEFINITIONS
+
+=============================================================================
+*/
+
+int FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max);
+int BackTrace (int finish);
+void ForwardTrace (void);
+int FinishWall (void);
+void InsideCorner (void);
+void OutsideCorner (void);
+void FollowWalls (void);
+
+extern	boolean	aborttrace;
+
+/*
+=============================================================================
+
+						 C3_DRAW DEFINITIONS
+
+=============================================================================
+*/
+
+#define MAXWALLS	50
+#define DANGERHIGH	45
+
+#define	MIDWALL		(MAXWALLS/2)
+
+//==========================================================================
+
+extern	tilept	tile,lasttile,focal,left,mid,right;
+
+extern	globpt	edge,view;
+
+extern	unsigned screenloc[3];
+extern	unsigned freelatch;
+
+extern	int screenpage;
+
+extern	boolean		fizzlein;
+
+extern	long lasttimecount;
+
+extern	int firstangle,lastangle;
+
+extern	fixed prestep;
+
+extern	int traceclip,tracetop;
+
+extern	fixed sintable[ANGLES+ANGLES/4],*costable;
+
+extern	fixed	viewx,viewy,viewsin,viewcos;			// the focal point
+extern	int	viewangle;
+
+extern	fixed scale,scaleglobal;
+extern	unsigned slideofs;
+
+extern	int zbuffer[VIEWXH+1];
+
+extern	walltype	walls[MAXWALLS],*leftwall,*rightwall;
+
+
+extern	fixed	tileglobal;
+extern	fixed	focallength;
+extern	fixed	mindist;
+extern	int		viewheight;
+extern	fixed scale;
+
+extern	int	walllight1[NUMFLOORS];
+extern	int	walldark1[NUMFLOORS];
+extern	int	walllight2[NUMFLOORS];
+extern	int	walldark2[NUMFLOORS];
+
+//==========================================================================
+
+void	DrawLine (int xl, int xh, int y,int color);
+void	DrawWall (walltype *wallptr);
+void	TraceRay (unsigned angle);
+fixed	FixedByFrac (fixed a, fixed b);
+void	TransformPoint (fixed gx, fixed gy, int *screenx, unsigned *screenheight);
+fixed	TransformX (fixed gx, fixed gy);
+int	FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max);
+void	ForwardTrace (void);
+int	FinishWall (void);
+int	TurnClockwise (void);
+int	TurnCounterClockwise (void);
+void	FollowWall (void);
+
+void	NewScene (void);
+void	BuildTables (void);
+
+
+/*
+=============================================================================
+
+						 C3_SCALE DEFINITIONS
+
+=============================================================================
+*/
+
+
+#define COMPSCALECODESTART	(65*6)		// offset to start of code in comp scaler
+
+typedef struct
+{
+	unsigned	codeofs[65];
+	unsigned	start[65];
+	unsigned	width[65];
+	byte		code[];
+}	t_compscale;
+
+typedef struct
+{
+	unsigned	width;
+	unsigned	codeofs[64];
+}	t_compshape;
+
+
+extern unsigned	scaleblockwidth,
+		scaleblockheight,
+		scaleblockdest;
+
+extern	byte	plotpix[8];
+extern	byte	bitmasks1[8][8];
+extern	byte	bitmasks2[8][8];
+
+
+extern	t_compscale _seg *scaledirectory[MAXSCALE+1];
+extern	t_compshape _seg *shapedirectory[NUMSCALEPICS];
+extern	memptr			walldirectory[NUMSCALEWALLS];
+extern	unsigned	shapesize[MAXSCALE+1];
+
+void 		DeplanePic (int picnum);
+void ScaleShape (int xcenter, t_compshape _seg *compshape, unsigned scale);
+unsigned	BuildCompShape (t_compshape _seg **finalspot);
+
+
+/*
+=============================================================================
+
+						 C3_ASM DEFINITIONS
+
+=============================================================================
+*/
+
+extern	unsigned	wallheight	[VIEWWIDTH];
+extern	unsigned	wallwidth	[VIEWWIDTH];
+extern	unsigned	wallseg		[VIEWWIDTH];
+extern	unsigned	wallofs		[VIEWWIDTH];
+extern	unsigned	screenbyte	[VIEWWIDTH];
+extern	unsigned	screenbit	[VIEWWIDTH];
+extern	unsigned	bitmasks	[64];
+
+extern	long		wallscalecall;
+
+void	ScaleWalls (void);
+
+/*
+=============================================================================
+
+						 C3_WIZ DEFINITIONS
+
+=============================================================================
+*/
+
+#define MAXHANDHEIGHT	72
+
+extern	long	lastnuke;
+extern	int		handheight;
+extern	int		boltsleft;
+
+/*
+=============================================================================
+
+						 C3_ACT1 DEFINITIONS
+
+=============================================================================
+*/
+
+extern	statetype s_trollouch;
+extern	statetype s_trolldie1;
+
+
+extern	statetype s_orcpause;
+
+extern	statetype s_orc1;
+extern	statetype s_orc2;
+extern	statetype s_orc3;
+extern	statetype s_orc4;
+
+extern	statetype s_orcattack1;
+extern	statetype s_orcattack2;
+extern	statetype s_orcattack3;
+
+extern	statetype s_orcouch;
+
+extern	statetype s_orcdie1;
+extern	statetype s_orcdie2;
+extern	statetype s_orcdie3;
+
+
+extern	statetype s_demonouch;
+extern	statetype s_demondie1;
+
+extern	statetype s_mageouch;
+extern	statetype s_magedie1;
+
+extern	statetype s_grelouch;
+extern	statetype s_greldie1;
+
+extern	statetype s_batdie1;
diff --git a/src/lib/hb/c3_draw.c b/src/lib/hb/c3_draw.c
new file mode 100755
index 00000000..dfb055e5
--- /dev/null
+++ b/src/lib/hb/c3_draw.c
@@ -0,0 +1,1592 @@
+/* Catacomb 3-D Source Code
+ * Copyright (C) 1993-2014 Flat Rock Software
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// C3_DRAW.C
+
+#include "C3_DEF.H"
+#pragma hdrstop
+
+//#define DRAWEACH				// draw walls one at a time for debugging
+
+unsigned	highest;
+unsigned	mostwalls,numwalls;
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+#define PI	3.141592657
+#define ANGLEQUAD	(ANGLES/4)
+
+unsigned	oldend;
+
+#define FINEANGLES	3600
+
+#define MINRATIO	16
+
+
+const	unsigned	MAXSCALEHEIGHT	= (VIEWWIDTH/2);
+const	unsigned	MAXVISHEIGHT	= (VIEWHEIGHT/2);
+const	unsigned	BASESCALE		= 32;
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+//
+// calculate location of screens in video memory so they have the
+// maximum possible distance seperating them (for scaling overflow)
+//
+
+unsigned screenloc[3]= {0x900,0x2000,0x3700};
+unsigned freelatch = 0x4e00;
+
+boolean		fizzlein;
+
+long	scaleshapecalll;
+long	scaletablecall;
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+long 	bytecount,endcount;		// for profiling
+int		animframe;
+int		pixelangle[VIEWWIDTH];
+int		far finetangent[FINEANGLES+1];
+int		fineviewangle;
+unsigned	viewxpix,viewypix;
+
+/*
+============================================================================
+
+			   3 - D  DEFINITIONS
+
+============================================================================
+*/
+
+fixed	tileglobal	= TILEGLOBAL;
+fixed	focallength	= FOCALLENGTH;
+fixed	mindist		= MINDIST;
+int		viewheight	= VIEWHEIGHT;
+fixed scale;
+
+
+tilept	tile,lasttile,		// tile of wall being followed
+	focal,			// focal point in tiles
+	left,mid,right;		// rightmost tile in view
+
+globpt edge,view;
+
+int	segstart[VIEWHEIGHT],	// addline tracks line segment and draws
+	segend[VIEWHEIGHT],
+	segcolor[VIEWHEIGHT];	// only when the color changes
+
+
+walltype	walls[MAXWALLS],*leftwall,*rightwall;
+
+
+//==========================================================================
+
+//
+// refresh stuff
+//
+
+int screenpage;
+
+long lasttimecount;
+
+//
+// rendering stuff
+//
+
+int firstangle,lastangle;
+
+fixed prestep;
+
+fixed sintable[ANGLES+ANGLES/4],*costable = sintable+(ANGLES/4);
+
+fixed	viewx,viewy;			// the focal point
+int	viewangle;
+fixed	viewsin,viewcos;
+
+int	zbuffer[VIEWXH+1];	// holds the height of the wall at that point
+
+//==========================================================================
+
+void	DrawLine (int xl, int xh, int y,int color);
+void	DrawWall (walltype *wallptr);
+void	TraceRay (unsigned angle);
+fixed	FixedByFrac (fixed a, fixed b);
+fixed	FixedAdd (void);
+fixed	TransformX (fixed gx, fixed gy);
+int		FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max);
+int		BackTrace (int finish);
+void	ForwardTrace (void);
+int		TurnClockwise (void);
+int		TurnCounterClockwise (void);
+void	FollowWall (void);
+
+void	NewScene (void);
+void	BuildTables (void);
+
+//==========================================================================
+
+
+/*
+==================
+=
+= DrawLine
+=
+= Must be in write mode 2 with all planes enabled
+= The bit mask is left set to the end value, so clear it after all lines are
+= drawn
+=
+= draws a black dot at the left edge of the line
+=
+==================
+*/
+
+unsigned static	char dotmask[8] = {0x80,0x40,0x20,0x10,8,4,2,1};
+unsigned static	char leftmask[8] = {0xff,0x7f,0x3f,0x1f,0xf,7,3,1};
+unsigned static	char rightmask[8] = {0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff};
+
+void DrawLine (int xl, int xh, int y,int color)
+{
+  unsigned dest,xlp,xlb,xhb,maskleft,maskright,maskdot,mid;
+
+  xlb=xl/8;
+  xhb=xh/8;
+
+  if (xh<xl)
+	Quit("DrawLine: xh<xl");
+  if (y<VIEWY)
+	Quit("DrawLine: y<VIEWY");
+  if (y>VIEWYH)
+	Quit("DrawLine: y>VIEWYH");
+
+	xlp = xl&7;
+	maskleft = leftmask[xlp];
+	maskright = rightmask[xh&7];
+
+  mid = xhb-xlb-1;
+  dest = bufferofs+ylookup[y]+xlb;
+
+	//
+	// set the GC index register to point to the bit mask register
+	//
+	asm	mov	al,GC_BITMASK
+	asm	mov	dx,GC_INDEX
+	asm	out	dx,al
+
+  if (xlb==xhb)
+  {
+  //
+  // entire line is in one byte
+  //
+
+	maskleft&=maskright;
+
+	asm	mov	es,[screenseg]
+	asm	mov	di,[dest]
+	asm	mov	dx,GC_INDEX+1
+
+	asm	mov	al,[BYTE PTR maskleft]
+	asm	out	dx,al		// mask off pixels
+
+	asm	mov	al,[BYTE PTR color]
+	asm	xchg	al,[es:di]	// load latches and write pixels
+
+	return;
+  }
+
+asm	mov	es,[screenseg]
+asm	mov	di,[dest]
+asm	mov	dx,GC_INDEX+1
+asm	mov	bh,[BYTE PTR color]
+
+//
+// draw left side
+//
+asm	mov	al,[BYTE PTR maskleft]
+asm	out	dx,al		// mask off pixels
+
+asm	mov	al,bh
+asm	xchg	al,[es:di]	// load latches and write pixels
+asm	inc	di
+
+//
+// draw middle
+//
+asm	mov	al,255
+asm	out	dx,al		// no masking
+
+asm	mov	al,bh
+asm	mov	cx,[mid]
+asm	rep	stosb
+
+//
+// draw right side
+//
+asm	mov	al,[BYTE PTR maskright]
+asm	out	dx,al		// mask off pixels
+asm	xchg	bh,[es:di]	// load latches and write pixels
+
+}
+
+//==========================================================================
+
+void DrawLineDot (int xl, int xh, int y,int color)
+{
+  unsigned dest,xlp,xlb,xhb,maskleft,maskright,maskdot,mid;
+
+  xlb=xl/8;
+  xhb=xh/8;
+
+  if (xh<xl)
+	Quit("DrawLine: xh<xl");
+  if (y<VIEWY)
+	Quit("DrawLine: y<VIEWY");
+  if (y>VIEWYH)
+	Quit("DrawLine: y>VIEWYH");
+
+	xlp = xl&7;
+	maskdot = dotmask[xlp];
+	maskleft = leftmask[xlp];
+	maskright = rightmask[xh&7];
+
+  mid = xhb-xlb-1;
+  dest = bufferofs+ylookup[y]+xlb;
+
+	//
+	// set the GC index register to point to the bit mask register
+	//
+	asm	mov	al,GC_BITMASK
+	asm	mov	dx,GC_INDEX
+	asm	out	dx,al
+
+  if (xlb==xhb)
+  {
+  //
+  // entire line is in one byte
+  //
+
+	maskleft&=maskright;
+
+	asm	mov	es,[screenseg]
+	asm	mov	di,[dest]
+	asm	mov	dx,GC_INDEX+1
+
+	asm	mov	al,[BYTE PTR maskleft]
+	asm	out	dx,al		// mask off pixels
+
+	asm	mov	al,[BYTE PTR color]
+	asm	xchg	al,[es:di]	// load latches and write pixels
+
+
+	//
+	// write the black dot at the start
+	//
+	asm	mov	al,[BYTE PTR maskdot]
+	asm	out	dx,al		// mask off pixels
+
+	asm	xor	al,al
+	asm	xchg	al,[es:di]	// load latches and write pixels
+
+
+	return;
+  }
+
+asm	mov	es,[screenseg]
+asm	mov	di,[dest]
+asm	mov	dx,GC_INDEX+1
+asm	mov	bh,[BYTE PTR color]
+
+//
+// draw left side
+//
+asm	mov	al,[BYTE PTR maskleft]
+asm	out	dx,al		// mask off pixels
+
+asm	mov	al,bh
+asm	xchg	al,[es:di]	// load latches and write pixels
+
+//
+// write the black dot at the start
+//
+asm	mov	al,[BYTE PTR maskdot]
+asm	out	dx,al		// mask off pixels
+asm	xor	al,al
+asm	xchg	al,[es:di]	// load latches and write pixels
+asm	inc	di
+
+//
+// draw middle
+//
+asm	mov	al,255
+asm	out	dx,al		// no masking
+
+asm	mov	al,bh
+asm	mov	cx,[mid]
+asm	rep	stosb
+
+//
+// draw right side
+//
+asm	mov	al,[BYTE PTR maskright]
+asm	out	dx,al		// mask off pixels
+asm	xchg	bh,[es:di]	// load latches and write pixels
+
+}
+
+//==========================================================================
+
+
+long		wallscalesource;
+
+#ifdef DRAWEACH
+/*
+====================
+=
+= ScaleOneWall
+=
+====================
+*/
+
+void near ScaleOneWall (int xl, int xh)
+{
+	int	x,pixwidth,height;
+
+	*(((unsigned *)&wallscalesource)+1) = wallseg[xl];
+
+	for (x=xl;x<=xh;x+=pixwidth)
+	{
+		height = wallheight[x];
+		pixwidth = wallwidth[x];
+		(unsigned)wallscalesource = wallofs[x];
+
+		*(((unsigned *)&scaletablecall)+1) = (unsigned)scaledirectory[height];
+		(unsigned)scaletablecall = scaledirectory[height]->codeofs[0];
+
+		//
+		// scale a byte wide strip of wall
+		//
+		asm	mov	bx,[x]
+		asm	mov	di,bx
+		asm	shr	di,1
+		asm	shr	di,1
+		asm	shr	di,1						// X in bytes
+		asm	add	di,[bufferofs]
+		asm	and	bx,7
+		asm	shl	bx,1
+		asm	shl	bx,1
+		asm	shl	bx,1
+		asm	add	bx,[pixwidth]				// bx = pixel*8+pixwidth-1
+		asm	dec	bx
+		asm	mov	al,BYTE PTR [bitmasks1+bx]
+		asm	mov	dx,GC_INDEX+1
+		asm	out	dx,al						// set bit mask register
+		asm	mov	es,[screenseg]
+		asm	lds	si,[wallscalesource]
+		asm	call [DWORD PTR ss:scaletablecall]		// scale the line of pixels
+
+		asm	mov	al,BYTE PTR [ss:bitmasks2+bx]
+		asm	or	al,al
+		asm	jz	nosecond
+
+		//
+		// draw a second byte for vertical strips that cross two bytes
+		//
+		asm	inc	di
+		asm	out	dx,al						// set bit mask register
+		asm	call [DWORD PTR ss:scaletablecall]	// scale the line of pixels
+	nosecond:
+		asm	mov	ax,ss
+		asm	mov	ds,ax
+	}
+}
+
+#endif
+
+int	walllight1[NUMFLOORS] = {0,
+	WALL1LPIC,WALL2LPIC,WALL3LPIC,WALL4LPIC,WALL5LPIC,WALL6LPIC,WALL7LPIC,
+	WALL1LPIC,WALL2LPIC,WALL3LPIC,WALL4LPIC,WALL5LPIC,WALL6LPIC,WALL7LPIC,
+	EXPWALL1PIC,EXPWALL2PIC,EXPWALL3PIC,
+	RDOOR1PIC,RDOOR2PIC,RDOOR1PIC,RDOOR2PIC,
+	YDOOR1PIC,YDOOR2PIC,YDOOR1PIC,YDOOR2PIC,
+	GDOOR1PIC,GDOOR2PIC,GDOOR1PIC,GDOOR2PIC,
+	BDOOR1PIC,BDOOR2PIC,BDOOR1PIC,BDOOR2PIC};
+
+int	walldark1[NUMFLOORS] = {0,
+	WALL1DPIC,WALL2DPIC,WALL3DPIC,WALL4DPIC,WALL5DPIC,WALL6DPIC,WALL7DPIC,
+	WALL1DPIC,WALL2DPIC,WALL3DPIC,WALL4DPIC,WALL5DPIC,WALL6DPIC,WALL7DPIC,
+	EXPWALL1PIC,EXPWALL2PIC,EXPWALL3PIC,
+	RDOOR1PIC,RDOOR2PIC,RDOOR1PIC,RDOOR2PIC,
+	YDOOR1PIC,YDOOR2PIC,YDOOR1PIC,YDOOR2PIC,
+	GDOOR1PIC,GDOOR2PIC,GDOOR1PIC,GDOOR2PIC,
+	BDOOR1PIC,BDOOR2PIC,BDOOR1PIC,BDOOR2PIC};
+
+int	walllight2[NUMFLOORS] = {0,
+	WALL1LPIC,WALL2LPIC,WALL3LPIC,WALL4LPIC,WALL5LPIC,WALL6LPIC,WALL7LPIC,
+	WALL1LPIC,WALL2LPIC,WALL3LPIC,WALL4LPIC,WALL5LPIC,WALL6LPIC,WALL7LPIC,
+	EXPWALL1PIC,EXPWALL2PIC,EXPWALL3PIC,
+	RDOOR2PIC,RDOOR1PIC,RDOOR2PIC,RDOOR1PIC,
+	YDOOR2PIC,YDOOR1PIC,YDOOR2PIC,YDOOR1PIC,
+	GDOOR2PIC,GDOOR1PIC,GDOOR2PIC,GDOOR1PIC,
+	BDOOR2PIC,BDOOR1PIC,BDOOR2PIC,BDOOR1PIC};
+
+int	walldark2[NUMFLOORS] = {0,
+	WALL1DPIC,WALL2DPIC,WALL3DPIC,WALL4DPIC,WALL5DPIC,WALL6DPIC,WALL7DPIC,
+	WALL1DPIC,WALL2DPIC,WALL3DPIC,WALL4DPIC,WALL5DPIC,WALL6DPIC,WALL7DPIC,
+	EXPWALL1PIC,EXPWALL2PIC,EXPWALL3PIC,
+	RDOOR2PIC,RDOOR1PIC,RDOOR2PIC,RDOOR1PIC,
+	YDOOR2PIC,YDOOR1PIC,YDOOR2PIC,YDOOR1PIC,
+	GDOOR2PIC,GDOOR1PIC,GDOOR2PIC,GDOOR1PIC,
+	BDOOR2PIC,BDOOR1PIC,BDOOR2PIC,BDOOR1PIC};
+
+/*
+=====================
+=
+= DrawVWall
+=
+= Draws a wall by vertical segments, for texture mapping!
+=
+= wallptr->side is true for east/west walls (constant x)
+=
+= fracheight and fracstep are 16.16 bit fractions
+=
+=====================
+*/
+
+void DrawVWall (walltype *wallptr)
+{
+	int			x,i;
+	unsigned	source;
+	unsigned	width,sourceint;
+	unsigned	wallpic,wallpicseg;
+	unsigned	skip;
+	long		fracheight,fracstep,longheightchange;
+	unsigned	height;
+	int			heightchange;
+	unsigned	slope,distance;
+	int			traceangle,angle;
+	int			mapadd;
+	unsigned	lastpix,lastsource,lastwidth;
+
+
+	if (wallptr->rightclip < wallptr->leftclip)
+		Quit ("DrawVWall: Right < Left");
+
+//
+// setup for height calculation
+//
+	wallptr->height1 >>= 1;
+	wallptr->height2 >>= 1;
+	wallptr->planecoord>>=10;			// remove non significant bits
+
+	width = wallptr->x2 - wallptr->x1;
+	if (width)
+	{
+		heightchange = wallptr->height2 - wallptr->height1;
+		asm	mov	ax,[heightchange]
+		asm	mov	WORD PTR [longheightchange+2],ax
+		asm	mov	WORD PTR [longheightchange],0	// avoid long shift by 16
+		fracstep = longheightchange/width;
+	}
+
+	fracheight = ((long)wallptr->height1<<16)+0x8000;
+	skip = wallptr->leftclip - wallptr->x1;
+	if (skip)
+		fracheight += fracstep*skip;
+
+//
+// setup for texture mapping
+//
+// mapadd is 64*64 (to keep source positive) + the origin wall intercept
+// distance has 6 unit bits, and 6 frac bits
+// traceangle is the center view angle in FINEANGLES, moved to be in
+// the +-90 degree range (to thew right of origin)
+//
+	traceangle = fineviewangle;
+	//
+	// find wall picture to map from
+	//
+	if (wallptr->side)
+	{	// east or west wall
+		if (animframe)
+			wallpic = walllight2[wallptr->color];
+		else
+			wallpic = walllight1[wallptr->color];
+
+		if (wallptr->planecoord < viewxpix)
+		{
+			distance = viewxpix-wallptr->planecoord;
+			traceangle -= FINEANGLES/2;
+			mapadd = (64-viewypix&63);		// the pixel spot of the origin
+		}
+		else
+		{
+			distance = wallptr->planecoord-viewxpix;
+			// traceangle is correct
+			mapadd = viewypix&63;		// the pixel spot of the origin
+		}
+	}
+	else
+	{	// north or south wall
+		if (animframe)
+			wallpic = walldark2[wallptr->color];
+		else
+			wallpic = walldark1[wallptr->color];
+
+		if (wallptr->planecoord < viewypix)
+		{
+			distance = viewypix-wallptr->planecoord;
+			traceangle -= FINEANGLES/4;
+			mapadd = viewxpix&63;		// the pixel spot of the origin
+		}
+		else
+		{
+			distance = wallptr->planecoord-viewypix;
+			traceangle -= FINEANGLES*3/4;
+			mapadd = (64-viewxpix&63);		// the pixel spot of the origin
+		}
+	}
+
+	mapadd = 64*64-mapadd;				// make sure it stays positive
+
+	wallpicseg = (unsigned)walldirectory[wallpic-FIRSTWALLPIC];
+	if (traceangle > FINEANGLES/2)
+		traceangle -= FINEANGLES;
+
+//
+// calculate everything
+//
+// IMPORTANT!  This loop is executed around 5000 times / second!
+//
+	lastpix = lastsource = (unsigned)-1;
+
+	for (x = wallptr->leftclip ; x <= wallptr->rightclip ; x++)
+	{
+		//
+		// height
+		//
+		asm	mov	ax,WORD PTR [fracheight]
+		asm	mov	dx,WORD PTR [fracheight+2]
+		asm	mov	cx,dx
+		asm	add	ax,WORD PTR [fracstep]
+		asm	adc	dx,WORD PTR [fracstep+2]
+		asm	mov	WORD PTR [fracheight],ax
+		asm	mov	WORD PTR [fracheight+2],dx
+		asm	mov	bx,[x]
+		asm	shl	bx,1
+		asm	cmp	cx,MAXSCALEHEIGHT
+		asm	jbe	storeheight
+		asm	mov	cx,MAXSCALEHEIGHT
+storeheight:
+		asm	mov WORD PTR [wallheight+bx],cx
+		asm	mov WORD PTR [zbuffer+bx],cx
+
+//		height = fracheight>>16;
+//		fracheight += fracstep;
+//		if (height > MAXSCALEHEIGHT)
+//			height = MAXSCALEHEIGHT;
+//		wallheight[x] = zbuffer[x] = height;
+
+		//
+		// texture map
+		//
+		angle = pixelangle[x]+traceangle;
+		if (angle<0)
+			angle+=FINEANGLES;
+
+		slope = finetangent[angle];
+
+//
+// distance is an unsigned 6.6 bit number (12 pixel bits)
+// slope is a signed 5.10 bit number
+// result is a signed 11.16 bit number
+//
+
+#if 0
+		source = distance*slope;
+		source >>=20;
+
+		source += mapadd;
+		source &= 63;				// mask off the unused units
+		source = 63-source;
+		source <<= 6;				// multiply by 64 for offset into pic
+#endif
+		asm	mov	ax,[distance]
+		asm	imul	[slope]			// ax is the source pixel
+		asm	mov	al,ah
+		asm	shr	al,1
+		asm	shr	al,1				// low 6 bits is now pixel number
+		asm	add	ax,[mapadd]
+		asm	and ax,63
+		asm	mov	dx,63
+		asm	sub	dx,ax				// otherwise it is backwards
+		asm	shl	dx,1
+		asm	shl	dx,1
+		asm	shl	dx,1
+		asm	shl	dx,1
+		asm	shl	dx,1
+		asm	shl	dx,1				// *64 to index into shape
+		asm	mov	[source],dx
+
+		if (source != lastsource)
+		{
+			if (lastpix != (unsigned)-1)
+			{
+				wallofs[lastpix] = lastsource;
+				wallseg[lastpix] = wallpicseg;
+				wallwidth[lastpix] = lastwidth;
+			}
+			lastpix = x;
+			lastsource = source;
+			lastwidth = 1;
+		}
+		else
+			lastwidth++;			// optimized draw, same map as last one
+	}
+	wallofs[lastpix] = lastsource;
+	wallseg[lastpix] = wallpicseg;
+	wallwidth[lastpix] = lastwidth;
+}
+
+
+//==========================================================================
+
+
+/*
+=================
+=
+= TraceRay
+=
+= Used to find the left and rightmost tile in the view area to be traced from
+= Follows a ray of the given angle from viewx,viewy in the global map until
+= it hits a solid tile
+= sets:
+=   tile.x,tile.y	: tile coordinates of contacted tile
+=   tilecolor	: solid tile's color
+=
+==================
+*/
+
+int tilecolor;
+
+void TraceRay (unsigned angle)
+{
+  long tracex,tracey,tracexstep,traceystep,searchx,searchy;
+  fixed fixtemp;
+  int otx,oty,searchsteps;
+
+  tracexstep = costable[angle];
+  traceystep = sintable[angle];
+
+//
+// advance point so it is even with the view plane before we start checking
+//
+  fixtemp = FixedByFrac(prestep,tracexstep);
+  tracex = viewx+fixtemp;
+  fixtemp = FixedByFrac(prestep,traceystep);
+  tracey = viewy-fixtemp;
+
+  tile.x = tracex>>TILESHIFT;	// starting point in tiles
+  tile.y = tracey>>TILESHIFT;
+
+
+  if (tracexstep<0)			// use 2's complement, not signed magnitude
+	tracexstep = -(tracexstep&0x7fffffff);
+
+  if (traceystep<0)			// use 2's complement, not signed magnitude
+	traceystep = -(traceystep&0x7fffffff);
+
+//
+// we assume viewx,viewy is not inside a solid tile, so go ahead one step
+//
+
+  do	// until a solid tile is hit
+  {
+    otx = tile.x;
+	oty = tile.y;
+	spotvis[otx][oty] = true;
+	tracex += tracexstep;
+    tracey -= traceystep;
+    tile.x = tracex>>TILESHIFT;
+	tile.y = tracey>>TILESHIFT;
+
+	if (tile.x!=otx && tile.y!=oty && (tilemap[otx][tile.y] || tilemap[tile.x][oty]) )
+    {
+      //
+	  // trace crossed two solid tiles, so do a binary search along the line
+	  // to find a spot where only one tile edge is crossed
+      //
+      searchsteps = 0;
+      searchx = tracexstep;
+      searchy = traceystep;
+      do
+      {
+	searchx/=2;
+	searchy/=2;
+	if (tile.x!=otx && tile.y!=oty)
+	{
+	 // still too far
+	  tracex -= searchx;
+	  tracey += searchy;
+	}
+	else
+	{
+	 // not far enough, no tiles crossed
+	  tracex += searchx;
+	  tracey -= searchy;
+	}
+
+	//
+	// if it is REAL close, go for the most clockwise intersection
+	//
+	if (++searchsteps == 16)
+	{
+	  tracex = (long)otx<<TILESHIFT;
+	  tracey = (long)oty<<TILESHIFT;
+	  if (tracexstep>0)
+	  {
+		if (traceystep<0)
+		{
+		  tracex += TILEGLOBAL-1;
+		  tracey += TILEGLOBAL;
+		}
+		else
+		{
+		  tracex += TILEGLOBAL;
+		}
+	  }
+	  else
+	  {
+		if (traceystep<0)
+		{
+		  tracex --;
+		  tracey += TILEGLOBAL-1;
+		}
+		else
+		{
+		  tracey --;
+		}
+	  }
+	}
+
+	tile.x = tracex>>TILESHIFT;
+	tile.y = tracey>>TILESHIFT;
+
+	  } while (( tile.x!=otx && tile.y!=oty) || (tile.x==otx && tile.y==oty) );
+	}
+  } while (!(tilecolor = tilemap[tile.x][tile.y]) );
+
+}
+
+//==========================================================================
+
+
+/*
+========================
+=
+= FixedByFrac
+=
+= multiply a 16/16 bit, 2's complement fixed point number by a 16 bit
+= fraction, passed as a signed magnitude 32 bit number
+=
+========================
+*/
+
+#pragma warn -rvl			// I stick the return value in with ASMs
+
+fixed FixedByFrac (fixed a, fixed b)
+{
+  fixed value;
+
+//
+// setup
+//
+asm	mov	si,[WORD PTR b+2]	// sign of result = sign of fraction
+
+asm	mov	ax,[WORD PTR a]
+asm	mov	cx,[WORD PTR a+2]
+
+asm	or	cx,cx
+asm	jns	aok:				// negative?
+asm	not	ax
+asm	not	cx
+asm	add	ax,1
+asm	adc	cx,0
+asm	xor	si,0x8000			// toggle sign of result
+aok:
+
+//
+// multiply  cx:ax by bx
+//
+asm	mov	bx,[WORD PTR b]
+asm	mul	bx					// fraction*fraction
+asm	mov	di,dx				// di is low word of result
+asm	mov	ax,cx				//
+asm	mul	bx					// units*fraction
+asm add	ax,di
+asm	adc	dx,0
+
+//
+// put result dx:ax in 2's complement
+//
+asm	test	si,0x8000		// is the result negative?
+asm	jz	ansok:
+asm	not	ax
+asm	not	dx
+asm	add	ax,1
+asm	adc	dx,0
+
+ansok:;
+
+}
+
+#pragma warn +rvl
+
+//==========================================================================
+
+
+/*
+========================
+=
+= TransformPoint
+=
+= Takes paramaters:
+=   gx,gy		: globalx/globaly of point
+=
+= globals:
+=   viewx,viewy		: point of view
+=   viewcos,viewsin	: sin/cos of viewangle
+=
+=
+= defines:
+=   CENTERX		: pixel location of center of view window
+=   TILEGLOBAL		: size of one
+=   FOCALLENGTH		: distance behind viewx/y for center of projection
+=   scale		: conversion from global value to screen value
+=
+= returns:
+=   screenx,screenheight: projected edge location and size
+=
+========================
+*/
+
+void TransformPoint (fixed gx, fixed gy, int *screenx, unsigned *screenheight)
+{
+  int ratio;
+  fixed gxt,gyt,nx,ny;
+
+//
+// translate point to view centered coordinates
+//
+  gx = gx-viewx;
+  gy = gy-viewy;
+
+//
+// calculate newx
+//
+  gxt = FixedByFrac(gx,viewcos);
+  gyt = FixedByFrac(gy,viewsin);
+  nx = gxt-gyt;
+
+//
+// calculate newy
+//
+  gxt = FixedByFrac(gx,viewsin);
+  gyt = FixedByFrac(gy,viewcos);
+  ny = gyt+gxt;
+
+//
+// calculate perspective ratio
+//
+  if (nx<0)
+	nx = 0;
+
+  ratio = nx*scale/FOCALLENGTH;
+
+  if (ratio<=MINRATIO)
+	ratio = MINRATIO;
+
+  *screenx = CENTERX + ny/ratio;
+
+  *screenheight = TILEGLOBAL/ratio;
+
+}
+
+
+//
+// transform actor
+//
+void TransformActor (objtype *ob)
+{
+  int ratio;
+  fixed gx,gy,gxt,gyt,nx,ny;
+
+//
+// translate point to view centered coordinates
+//
+  gx = ob->x-viewx;
+  gy = ob->y-viewy;
+
+//
+// calculate newx
+//
+  gxt = FixedByFrac(gx,viewcos);
+  gyt = FixedByFrac(gy,viewsin);
+  nx = gxt-gyt-ob->size;
+
+//
+// calculate newy
+//
+  gxt = FixedByFrac(gx,viewsin);
+  gyt = FixedByFrac(gy,viewcos);
+  ny = gyt+gxt;
+
+//
+// calculate perspective ratio
+//
+  if (nx<0)
+	nx = 0;
+
+  ratio = nx*scale/FOCALLENGTH;
+
+  if (ratio<=MINRATIO)
+	ratio = MINRATIO;
+
+  ob->viewx = CENTERX + ny/ratio;
+
+  ob->viewheight = TILEGLOBAL/ratio;
+}
+
+//==========================================================================
+
+fixed TransformX (fixed gx, fixed gy)
+{
+  int ratio;
+  fixed gxt,gyt,nx,ny;
+
+//
+// translate point to view centered coordinates
+//
+  gx = gx-viewx;
+  gy = gy-viewy;
+
+//
+// calculate newx
+//
+  gxt = FixedByFrac(gx,viewcos);
+  gyt = FixedByFrac(gy,viewsin);
+
+  return gxt-gyt;
+}
+
+//==========================================================================
+
+/*
+==================
+=
+= BuildTables
+=
+= Calculates:
+=
+= scale			projection constant
+= sintable/costable	overlapping fractional tables
+= firstangle/lastangle	angles from focalpoint to left/right view edges
+= prestep		distance from focal point before checking for tiles
+=
+==================
+*/
+
+void BuildTables (void)
+{
+  int 		i;
+  long		intang;
+  long		x;
+  float 	angle,anglestep,radtoint;
+  double	tang;
+  fixed 	value;
+
+//
+// calculate the angle offset from view angle of each pixel's ray
+//
+	radtoint = (float)FINEANGLES/2/PI;
+	for (i=0;i<VIEWWIDTH/2;i++)
+	{
+	// start 1/2 pixel over, so viewangle bisects two middle pixels
+		x = (TILEGLOBAL*i+TILEGLOBAL/2)/VIEWWIDTH;
+		tang = (float)x/(FOCALLENGTH+MINDIST);
+		angle = atan(tang);
+		intang = angle*radtoint;
+		pixelangle[VIEWWIDTH/2-1-i] = intang;
+		pixelangle[VIEWWIDTH/2+i] = -intang;
+	}
+
+//
+// calculate fine tangents
+// 1 sign bit, 5 units (clipped to), 10 fracs
+//
+#define MININT	(-MAXINT)
+
+	for (i=0;i<FINEANGLES/4;i++)
+	{
+		intang = tan(i/radtoint)*(1l<<10);
+
+		//
+		// if the tangent is not reprentable in this many bits, bound the
+		// units part ONLY
+		//
+		if (intang>MAXINT)
+			intang = 0x8f00 | (intang & 0xff);
+		else if (intang<MININT)
+			intang = 0xff00 | (intang & 0xff);
+
+		finetangent[i] = intang;
+//		finetangent[FINEANGLES/2+i] = intang;
+//		finetangent[FINEANGLES/2-i-1] = -intang;
+		finetangent[FINEANGLES-i-1] = -intang;
+	}
+
+//
+// calculate scale value so one tile at mindist allmost fills the view horizontally
+//
+  scale = GLOBAL1/VIEWWIDTH;
+  scale *= focallength;
+  scale /= (focallength+mindist);
+
+//
+// costable overlays sintable with a quarter phase shift
+// ANGLES is assumed to be divisable by four
+//
+// The low word of the value is the fraction, the high bit is the sign bit,
+// bits 16-30 should be 0
+//
+
+  angle = 0;
+  anglestep = PI/2/ANGLEQUAD;
+  for (i=0;i<=ANGLEQUAD;i++)
+  {
+	value=GLOBAL1*sin(angle);
+	sintable[i]=
+	  sintable[i+ANGLES]=
+	  sintable[ANGLES/2-i] = value;
+	sintable[ANGLES-i]=
+	  sintable[ANGLES/2+i] = value | 0x80000000l;
+	angle += anglestep;
+  }
+
+//
+// figure trace angles for first and last pixel on screen
+//
+  angle = atan((float)VIEWWIDTH/2*scale/FOCALLENGTH);
+  angle *= ANGLES/(PI*2);
+
+  intang = (int)angle+1;
+  firstangle = intang;
+  lastangle = -intang;
+
+  prestep = GLOBAL1*((float)FOCALLENGTH/costable[firstangle]);
+
+//
+// misc stuff
+//
+  walls[0].x2 = VIEWX-1;
+  walls[0].height2 = 32000;
+}
+
+
+//==========================================================================
+
+/*
+=====================
+=
+= ClearScreen
+=
+=====================
+*/
+
+void ClearScreen (void)
+{
+  //
+  // clear the screen
+  //
+asm	mov	dx,GC_INDEX
+asm	mov	ax,GC_MODE + 256*2		// read mode 0, write mode 2
+asm	out	dx,ax
+asm	mov	ax,GC_BITMASK + 255*256
+asm	out	dx,ax
+
+asm	mov	dx,40-VIEWWIDTH/8
+asm	mov	bl,VIEWWIDTH/16
+asm	mov	bh,CENTERY+1
+
+asm	xor	ax,ax
+asm	mov	es,[screenseg]
+asm	mov	di,[bufferofs]
+
+toploop:
+asm	mov	cl,bl
+asm	rep	stosw
+asm	stosb
+asm	add	di,dx
+asm	dec	bh
+asm	jnz	toploop
+
+asm	mov	bh,CENTERY+1
+asm	mov	ax,0x0808
+
+bottomloop:
+asm	mov	cl,bl
+asm	rep	stosw
+asm	stosb
+asm	add	di,dx
+asm	dec	bh
+asm	jnz	bottomloop
+
+
+asm	mov	dx,GC_INDEX
+asm	mov	ax,GC_MODE + 256*10		// read mode 1, write mode 2
+asm	out	dx,ax
+asm	mov	al,GC_BITMASK
+asm	out	dx,al
+
+}
+
+//==========================================================================
+
+/*
+=====================
+=
+= DrawWallList
+=
+= Clips and draws all the walls traced this refresh
+=
+=====================
+*/
+
+void DrawWallList (void)
+{
+	int i,leftx,newleft,rightclip;
+	walltype *wall, *check;
+
+asm	mov	ax,ds
+asm	mov	es,ax
+asm	mov	di,OFFSET wallwidth
+asm	xor	ax,ax
+asm	mov	cx,VIEWWIDTH/2
+asm	rep	stosw
+
+	ClearScreen ();
+
+	rightwall->x1 = VIEWXH+1;
+	rightwall->height1 = 32000;
+	(rightwall+1)->x1 = 32000;
+
+	leftx = -1;
+
+	for (wall=&walls[1];wall<rightwall && leftx<=VIEWXH ;wall++)
+	{
+	  if (leftx >= wall->x2)
+		continue;
+
+	  rightclip = wall->x2;
+
+	  check = wall+1;
+	  while (check->x1 <= rightclip && check->height1 >= wall->height2)
+	  {
+		rightclip = check->x1-1;
+		check++;
+	  }
+
+	  if (rightclip>VIEWXH)
+		rightclip=VIEWXH;
+
+	  if (leftx < wall->x1 - 1)
+		newleft = wall->x1-1;		// there was black space between walls
+	  else
+		newleft = leftx;
+
+	  if (rightclip > newleft)
+	  {
+		wall->leftclip = newleft+1;
+		wall->rightclip = rightclip;
+		DrawVWall (wall);
+		leftx = rightclip;
+	  }
+	}
+
+#ifndef DRAWEACH
+	ScaleWalls ();					// draw all the walls
+#endif
+}
+
+//==========================================================================
+
+/*
+=====================
+=
+= DrawScaleds
+=
+= Draws all objects that are visable
+=
+=====================
+*/
+
+objtype *depthsort[MAXACTORS];
+
+void DrawScaleds (void)
+{
+	int 		i,j,least,numvisable,height;
+	objtype 	*obj,**vislist,*farthest;
+	memptr		shape;
+	byte		*tilespot,*visspot;
+
+	numvisable = 0;
+
+//
+// calculate base positions of all objects
+//
+	vislist = &depthsort[0];
+
+	for (obj = player->next;obj;obj=obj->next)
+	{
+		if (!obj->state->shapenum)
+			continue;
+
+		tilespot = &tilemap[0][0]+(obj->tilex<<6)+obj->tiley;
+		visspot = &spotvis[0][0]+(obj->tilex<<6)+obj->tiley;
+		//
+		// could be in any of the nine surrounding tiles
+		//
+		if (*visspot
+		|| ( *(visspot-1) && !*(tilespot-1) )
+		|| ( *(visspot+1) && !*(tilespot+1) )
+		|| ( *(visspot-65) && !*(tilespot-65) )
+		|| ( *(visspot-64) && !*(tilespot-64) )
+		|| ( *(visspot-63) && !*(tilespot-63) )
+		|| ( *(visspot+65) && !*(tilespot+65) )
+		|| ( *(visspot+64) && !*(tilespot+64) )
+		|| ( *(visspot+63) && !*(tilespot+63) ) )
+		{
+			obj->active = true;
+			TransformActor (obj);
+			if (!obj->viewheight || obj->viewheight > VIEWWIDTH)
+				continue;			// too close or far away
+
+			*vislist++ = obj;
+			numvisable++;
+		}
+	}
+
+	if (vislist == &depthsort[0])
+		return;						// no visable objects
+
+//
+// draw from back to front
+//
+	for (i = 0; i<numvisable; i++)
+	{
+		least = 32000;
+		for (j=0;j<numvisable;j++)
+		{
+			height = depthsort[j]->viewheight;
+			if (height < least)
+			{
+				least = height;
+				farthest = depthsort[j];
+			}
+		}
+		//
+		// draw farthest
+		//
+		shape = shapedirectory[farthest->state->shapenum-FIRSTSCALEPIC];
+		ScaleShape(farthest->viewx,shape,farthest->viewheight);
+		farthest->viewheight = 32000;
+	}
+}
+
+//==========================================================================
+
+
+/*
+=====================
+=
+= CalcTics
+=
+=====================
+*/
+
+void CalcTics (void)
+{
+	long	newtime,oldtimecount;
+
+
+#ifdef PROFILE
+	tics = 1;
+	return;
+#endif
+
+//
+// calculate tics since last refresh for adaptive timing
+//
+	if (lasttimecount > TimeCount)
+		TimeCount = lasttimecount;		// if the game was paused a LONG time
+
+	if (DemoMode)					// demo recording and playback needs
+	{								// to be constant
+//
+// take DEMOTICS or more tics, and modify Timecount to reflect time taken
+//
+		oldtimecount = lasttimecount;
+		while (TimeCount<oldtimecount+DEMOTICS*2)
+		;
+		lasttimecount = oldtimecount + DEMOTICS;
+		TimeCount = lasttimecount + DEMOTICS;
+		tics = DEMOTICS;
+	}
+	else
+	{
+//
+// non demo, so report actual time
+//
+		newtime = TimeCount;
+		tics = newtime-lasttimecount;
+		lasttimecount = newtime;
+
+#ifdef FILEPROFILE
+			strcpy (scratch,"\tTics:");
+			itoa (tics,str,10);
+			strcat (scratch,str);
+			strcat (scratch,"\n");
+			write (profilehandle,scratch,strlen(scratch));
+#endif
+
+		if (tics>MAXTICS)
+		{
+			TimeCount -= (tics-MAXTICS);
+			tics = MAXTICS;
+		}
+	}
+}
+
+
+//==========================================================================
+
+
+/*
+========================
+=
+= DrawHand
+=
+========================
+*/
+
+void	DrawHand (void)
+{
+	int	picnum;
+	memptr source;
+	unsigned dest,width,height;
+
+	picnum = HAND1PICM;
+	if (gamestate.shotpower || boltsleft)
+		picnum += (((unsigned)TimeCount>>3)&1);
+
+	source = grsegs[picnum];
+	dest = ylookup[VIEWHEIGHT-handheight]+12+bufferofs;
+	width = picmtable[picnum-STARTPICM].width;
+	height = picmtable[picnum-STARTPICM].height;
+
+	VW_MaskBlock(source,0,dest,width,handheight,width*height);
+	EGAMAPMASK(15);
+}
+
+//==========================================================================
+
+
+/*
+========================
+=
+= ThreeDRefresh
+=
+========================
+*/
+
+void	ThreeDRefresh (void)
+{
+	int tracedir;
+
+restart:
+	aborttrace = false;
+
+//
+// clear out the traced array
+//
+asm	mov	ax,ds
+asm	mov	es,ax
+asm	mov	di,OFFSET spotvis
+asm	xor	ax,ax
+asm	mov	cx,[mapwidth]		// mapheight*32 words
+asm	shl	cx,1
+asm	shl	cx,1
+asm	shl	cx,1
+asm	shl	cx,1
+asm	shl	cx,1
+asm	rep stosw
+
+
+//
+// set up variables for this view
+//
+
+	viewangle = player->angle;
+	fineviewangle = viewangle*(FINEANGLES/ANGLES);
+	viewsin = sintable[viewangle];
+	viewcos = costable[viewangle];
+	viewx = player->x - FixedByFrac(FOCALLENGTH,viewcos);
+	viewy = player->y + FixedByFrac(FOCALLENGTH,viewsin);
+	viewx &= 0xfffffc00;		// stop on a pixel boundary
+	viewy &= 0xfffffc00;
+	viewx += 0x180;
+	viewy += 0x180;
+	viewxpix = viewx>>10;
+	viewypix = viewy>>10;
+
+	focal.x = viewx>>TILESHIFT;
+	focal.y = viewy>>TILESHIFT;
+
+//
+// find the rightmost visable tile in view
+//
+	tracedir = viewangle + lastangle;
+	if (tracedir<0)
+	  tracedir+=ANGLES;
+	else if (tracedir>=ANGLES)
+	  tracedir-=ANGLES;
+	TraceRay( tracedir );
+	right.x = tile.x;
+	right.y = tile.y;
+
+//
+// find the leftmost visable tile in view
+//
+	tracedir = viewangle + firstangle;
+	if (tracedir<0)
+	  tracedir+=ANGLES;
+	else if (tracedir>=ANGLES)
+	  tracedir-=ANGLES;
+	TraceRay( tracedir );
+
+//
+// follow the walls from there to the right
+//
+	rightwall = &walls[1];
+	FollowWalls ();
+
+	if (aborttrace)
+		goto restart;
+
+//
+// actually draw stuff
+//
+	if (++screenpage == 3)
+		screenpage = 0;
+
+	bufferofs = screenloc[screenpage];
+
+	EGAWRITEMODE(2);
+	EGAMAPMASK(15);
+
+//
+// draw the wall list saved be FollowWalls ()
+//
+	animframe = (TimeCount&8)>>3;
+
+//
+// draw all the scaled images
+//
+	asm	mov	dx,GC_INDEX
+
+	asm	mov	ax,GC_COLORDONTCARE
+	asm	out	dx,ax						// don't look at any of the planes
+
+	asm	mov	ax,GC_MODE + 256*(10)		// read mode 1, write mode 2
+	asm	out	dx,ax
+
+	asm	mov	al,GC_BITMASK
+	asm	out	dx,al
+
+	DrawWallList();
+	DrawScaleds();
+
+	EGAWRITEMODE(0);
+	EGABITMASK(0xff);
+
+//
+// draw hand
+//
+	if (handheight)
+		DrawHand ();
+
+//
+// show screen and time last cycle
+//
+	if (fizzlein)
+	{
+		fizzlein = false;
+		FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,true);
+		lasttimecount = TimeCount;
+		if (MousePresent) Mouse(MDelta);	// Clear accumulated mouse movement
+	}
+
+asm	cli
+asm	mov	cx,[bufferofs]
+asm	mov	dx,3d4h		// CRTC address register
+asm	mov	al,0ch		// start address high register
+asm	out	dx,al
+asm	inc	dx
+asm	mov	al,ch
+asm	out	dx,al   	// set the high byte
+asm	dec	dx
+asm	mov	al,0dh		// start address low register
+asm	out	dx,al
+asm	inc	dx
+asm	mov	al,cl
+asm	out	dx,al		// set the low byte
+asm	sti
+
+	displayofs = bufferofs;
+
+	CalcTics ();
+
+}
+
diff --git a/src/lib/hb/c3_game.c b/src/lib/hb/c3_game.c
new file mode 100755
index 00000000..a71d2546
--- /dev/null
+++ b/src/lib/hb/c3_game.c
@@ -0,0 +1,1199 @@
+/* Catacomb 3-D Source Code
+ * Copyright (C) 1993-2014 Flat Rock Software
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// C3_GAME.C
+
+#include "C3_DEF.H"
+#pragma hdrstop
+
+#ifdef PROFILE
+#include "TIME.H"
+#endif
+
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+#define NUMLUMPS        25
+
+#define CONTROLSLUMP    0
+#define ORCLUMP         1
+#define TROLLLUMP        2
+#define WARPLUMP        3
+#define BOLTLUMP        4
+#define NUKELUMP        5
+#define POTIONLUMP      6
+#define RKEYLUMP        7
+#define YKEYLUMP        8
+#define GKEYLUMP        9
+#define BKEYLUMP        10
+#define SCROLLLUMP      11
+#define CHESTLUMP       12
+#define PLAYERLUMP      13
+#define WALL1LUMP       14
+#define WALL2LUMP       15
+#define BDOORLUMP       16
+#define DEMONLUMP               17
+#define MAGELUMP                18
+#define BATLUMP                 19
+#define GRELLUMP                20
+#define GOALLUMP                21
+
+
+int     lumpstart[NUMLUMPS] = {
+CONTROLS_LUMP_START,
+ORC_LUMP_START,
+TROLL_LUMP_START,
+WARP_LUMP_START,
+BOLT_LUMP_START,
+NUKE_LUMP_START,
+POTION_LUMP_START,
+RKEY_LUMP_START,
+YKEY_LUMP_START,
+GKEY_LUMP_START,
+BKEY_LUMP_START,
+SCROLL_LUMP_START,
+CHEST_LUMP_START,
+PLAYER_LUMP_START,
+WALL1_LUMP_START,
+WALL2_LUMP_START,
+BDOOR_LUMP_START,
+DEMON_LUMP_START,
+MAGE_LUMP_START,
+BAT_LUMP_START,
+GREL_LUMP_START,
+NEMESISPIC
+};
+
+
+int     lumpend[NUMLUMPS] = {
+CONTROLS_LUMP_END,
+ORC_LUMP_END,
+TROLL_LUMP_END,
+WARP_LUMP_END,
+BOLT_LUMP_END,
+NUKE_LUMP_END,
+POTION_LUMP_END,
+RKEY_LUMP_END,
+YKEY_LUMP_END,
+GKEY_LUMP_END,
+BKEY_LUMP_END,
+SCROLL_LUMP_END,
+CHEST_LUMP_END,
+PLAYER_LUMP_END,
+WALL1_LUMP_END,
+WALL2_LUMP_END,
+BDOOR_LUMP_END,
+DEMON_LUMP_END,
+MAGE_LUMP_END,
+BAT_LUMP_END,
+GREL_LUMP_END,
+NEMESISPIC
+};
+
+
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+unsigned        latchpics[NUMLATCHPICS];
+unsigned        tileoffsets[NUMTILE16];
+unsigned        textstarts[27];
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+boolean lumpneeded[NUMLUMPS];
+
+
+//===========================================================================
+
+
+/*
+==========================
+=
+= ScanInfoPlane
+=
+= Spawn all actors and mark down special places
+=
+==========================
+*/
+
+void ScanInfoPlane (void)
+{
+	unsigned        x,y,i,j;
+	int                     tile;
+	unsigned        far     *start;
+
+	InitObjList();                  // start spawning things with a clean slate
+
+	memset (lumpneeded,0,sizeof(lumpneeded));
+
+	start = mapsegs[2];
+	for (y=0;y<mapheight;y++)
+		for (x=0;x<mapwidth;x++)
+		{
+			tile = *start++;
+			if (!tile)
+				continue;
+
+			switch (tile)
+			{
+			case 1:
+			case 2:
+			case 3:
+			case 4:
+				lumpneeded[PLAYERLUMP] = true;
+				SpawnPlayer(x,y,NORTH+tile-1);
+				break;
+
+			case 5:
+			case 6:
+			case 7:
+			case 8:
+			case 9:
+			case 10:
+			case 11:
+				lumpneeded[tile-5+BOLTLUMP] = true;
+				SpawnBonus(x,y,tile-5);
+				break;
+
+			case 12:
+			case 13:
+			case 14:
+			case 15:
+			case 16:
+			case 17:
+			case 18:
+			case 19:
+				lumpneeded[SCROLLLUMP] = true;
+				SpawnBonus(x,y,B_SCROLL1+tile-12);
+				break;
+
+			case 20:        // goal
+				lumpneeded[GOALLUMP] = true;
+				SpawnBonus(x,y,B_GOAL);
+				break;
+
+			case 21:        // chest
+				lumpneeded[CHESTLUMP] = true;
+				SpawnBonus(x,y,B_CHEST);
+				break;
+
+			case 24:
+				lumpneeded[WARPLUMP] = true;
+				SpawnWarp (x,y,0);
+				break;
+//------
+			case 41:
+				if (gamestate.difficulty <gd_Hard)
+					break;
+			case 36:
+				if (gamestate.difficulty <gd_Normal)
+					break;
+			case 22:
+				lumpneeded[TROLLLUMP] = true;
+				SpawnTroll (x,y);
+				break;
+
+			case 42:
+				if (gamestate.difficulty <gd_Hard)
+					break;
+			case 37:
+				if (gamestate.difficulty <gd_Normal)
+					break;
+			case 23:
+				lumpneeded[ORCLUMP] = true;
+				SpawnOrc (x,y);
+				break;
+
+			case 43:
+				if (gamestate.difficulty <gd_Hard)
+					break;
+			case 38:
+				if (gamestate.difficulty <gd_Normal)
+					break;
+			case 25:
+				lumpneeded[BATLUMP] = true;
+				SpawnBat (x,y);
+				break;
+
+			case 44:
+				if (gamestate.difficulty <gd_Hard)
+					break;
+			case 39:
+				if (gamestate.difficulty <gd_Normal)
+					break;
+			case 26:
+				lumpneeded[DEMONLUMP] = true;
+				SpawnDemon (x,y);
+				break;
+
+			case 45:
+				if (gamestate.difficulty <gd_Hard)
+					break;
+			case 40:
+				if (gamestate.difficulty <gd_Normal)
+					break;
+			case 27:
+				lumpneeded[MAGELUMP] = true;
+				SpawnMage (x,y);
+				break;
+
+			case 28:
+				lumpneeded[GRELLUMP] = true;
+				SpawnNemesis (x,y);
+				break;
+
+			case 29:
+				SpawnBounce (x,y,0);
+				break;
+
+			case 30:
+				SpawnBounce (x,y,1);
+				break;
+
+			case 31:
+			case 32:
+			case 33:
+			case 34:
+				lumpneeded[WARPLUMP] = true;
+				SpawnWarp (x,y,tile-30);
+				break;
+			}
+		}
+
+}
+
+//==========================================================================
+
+/*
+==================
+=
+= ScanText
+=
+==================
+*/
+
+void ScanText (void)
+{
+	int     i;
+	char far *text;
+
+	text = (char _seg *)grsegs[LEVEL1TEXT+mapon];
+
+	textstarts[0] = 0;
+
+	for (i=1;i<=26;i++)
+	{
+		while (*text != '\n')
+		{
+			if (*text == '\r')
+				*text = 0;
+			text++;
+		}
+		text++;
+		textstarts[i] = FP_OFF(text);
+	}
+
+}
+
+//==========================================================================
+
+/*
+==================
+=
+= DrawEnterScreen
+=
+==================
+*/
+
+static  char    *levelnames[] =
+				{
+					"The Approach",
+					"Nemesis's Keep",
+					"Ground Floor",
+					"Second Floor",
+					"Third Floor",
+					"Tower One",
+					"Tower Two",
+					"Secret Halls",
+					"Access Floor",
+					"The Dungeon",
+					"Lower Dungeon",
+					"Catacomb",
+					"Lower Reaches",
+					"The Warrens",
+					"Hidden Caverns",
+					"The Fens of Insanity",
+					"Chaos Corridors",
+					"The Labyrinth",
+					"Halls of Blood",
+					"Nemesis's Lair"
+				};
+void DrawEnterScreen (void)
+{
+	int     x,y;
+
+	VW_Bar(0,0,VIEWWIDTH,VIEWHEIGHT,9);     // Medium blue
+
+	x = (VIEWWIDTH - (18 * 8)) / 2 -3;
+	y = (VIEWHEIGHT - (5 * 8)) / 2;
+	VW_DrawPic(x / 8,y,ENTERPLAQUEPIC);
+
+	WindowX = x;
+	WindowW = 18 * 8;
+	PrintY = (VIEWHEIGHT/2) + 3;
+	US_CPrint (levelnames[gamestate.mapon]);
+}
+
+//==========================================================================
+
+boolean tileneeded[NUMFLOORS];
+
+
+/*
+==================
+=
+= CacheScaleds
+=
+==================
+*/
+
+void CacheScaleds (void)
+{
+	int     i,j;
+	unsigned        source,dest;
+
+	FreeUpMemory ();
+	CA_CacheGrChunk(LEVEL1TEXT+mapon);
+	ScanText ();
+
+//
+// make sure we are displaying screenpage 0
+//
+	if (screenpage)
+	{
+		source = screenloc[screenpage];
+		dest = screenloc[0];
+		VW_ScreenToScreen (source,dest,40,VIEWHEIGHT);
+		screenpage = 0;
+		VW_SetScreen (dest,0);
+		displayofs = dest;
+	}
+
+//
+// cache wall pictures
+//
+	for (i=1;i<NUMFLOORS;i++)
+		if (tileneeded[i])
+		{
+			SetupScaleWall (walllight1[i]);
+			SetupScaleWall (walllight2[i]);
+			SetupScaleWall (walldark1[i]);
+			SetupScaleWall (walldark2[i]);
+		}
+
+//
+// cache the actor pictures
+//
+	for (i=0;i<NUMLUMPS;i++)
+		if (lumpneeded[i])
+			for (j=lumpstart[i];j<=lumpend[i];j++)
+				SetupScalePic(j);
+
+	source = screenloc[0];
+	for (i=1;i<=2;i++)
+	{
+		dest = screenloc[i];
+		VW_ScreenToScreen (source,dest,40,VIEWHEIGHT);
+	}
+
+	screenpage = 1;
+}
+
+//==========================================================================
+
+/*
+==================
+=
+= SetupGameLevel
+=
+==================
+*/
+
+void SetupGameLevel (void)
+{
+	int     x,y,i;
+	unsigned        far *map,tile,spot;
+
+	memset (tileneeded,0,sizeof(tileneeded));
+//
+// randomize if not a demo
+//
+	if (DemoMode)
+	{
+		US_InitRndT(false);
+		gamestate.difficulty = gd_Normal;
+	}
+	else
+		US_InitRndT(true);
+
+//
+// load the level
+//
+	CA_CacheMap (gamestate.mapon);
+
+	mapwidth = mapheaderseg[mapon]->width;
+	mapheight = mapheaderseg[mapon]->height;
+
+//
+// make a lookup table for the maps left edge
+//
+	spot = 0;
+	for (y=0;y<mapheight;y++)
+	{
+	  farmapylookup[y] = spot;
+	  spot += mapwidth;
+	}
+
+//
+// copy the wall data to a data segment array
+//
+	memset (tilemap,0,sizeof(tilemap));
+	memset (actorat,0,sizeof(actorat));
+	map = mapsegs[0];
+	for (y=0;y<mapheight;y++)
+		for (x=0;x<mapwidth;x++)
+		{
+			tile = *map++;
+			if (tile<NUMFLOORS)
+			{
+				tileneeded[tile] = true;
+				tilemap[x][y] = tile;
+				if (tile>=EXPWALLSTART && tile<EXPWALLSTART+NUMEXPWALLS)
+				{
+					tileneeded[WALLEXP] = tileneeded[WALLEXP+1]
+					= tileneeded[WALLEXP+2] = true;
+				}
+
+				if (tile>0)
+					(unsigned)actorat[x][y] = tile;
+			}
+		}
+
+
+//
+// decide which graphics are needed and spawn actors
+//
+	ScanInfoPlane ();
+
+//
+// have the caching manager load and purge stuff to make sure all marks
+// are in memory
+//
+	CA_LoadAllSounds ();
+
+}
+
+
+//==========================================================================
+
+/*
+=====================
+=
+= LatchDrawPic
+=
+=====================
+*/
+
+void LatchDrawPic (unsigned x, unsigned y, unsigned picnum)
+{
+	unsigned wide, height, source, dest;
+
+	wide = pictable[picnum-STARTPICS].width;
+	height = pictable[picnum-STARTPICS].height;
+	dest = bufferofs + ylookup[y]+x;
+	source = latchpics[picnum-FIRSTLATCHPIC];
+
+	EGAWRITEMODE(1);
+	EGAMAPMASK(15);
+
+asm     mov     bx,[linewidth]
+asm     sub     bx,[wide]
+
+asm     mov     ax,[screenseg]
+asm     mov     es,ax
+asm     mov     ds,ax
+
+asm     mov     si,[source]
+asm     mov     di,[dest]
+asm     mov     dx,[height]                             // scan lines to draw
+asm     mov     ax,[wide]
+
+lineloop:
+asm     mov     cx,ax
+asm     rep     movsb
+asm     add     di,bx
+
+asm     dec     dx
+asm     jnz     lineloop
+
+asm     mov     ax,ss
+asm     mov     ds,ax                                   // restore turbo's data segment
+
+	EGAWRITEMODE(0);
+}
+
+
+//==========================================================================
+
+/*
+=====================
+=
+= Victory
+=
+=====================
+*/
+
+void Victory (void)
+{
+	FreeUpMemory ();
+	NormalScreen ();
+	CA_CacheGrChunk (FINALEPIC);
+	VWB_DrawPic (0,0,FINALEPIC);
+	UNMARKGRCHUNK(FINALEPIC);
+	VW_UpdateScreen ();
+	SD_PlaySound (GETBOLTSND);
+	SD_WaitSoundDone ();
+	SD_PlaySound (GETNUKESND);
+	SD_WaitSoundDone ();
+	SD_PlaySound (GETPOTIONSND);
+	SD_WaitSoundDone ();
+	SD_PlaySound (GETKEYSND);
+	SD_WaitSoundDone ();
+	SD_PlaySound (GETSCROLLSND);
+	SD_WaitSoundDone ();
+	SD_PlaySound (GETPOINTSSND);
+	SD_WaitSoundDone ();
+	IN_ClearKeysDown ();
+	IN_Ack();
+}
+
+//==========================================================================
+
+/*
+===================
+=
+= Died
+=
+===================
+*/
+
+void Died (void)
+{
+	unsigned page1,page2;
+//
+// fizzle fade screen to grey
+//
+	FreeUpMemory ();
+	SD_PlaySound (GAMEOVERSND);
+	bufferofs = screenloc[(screenpage+1)%3];
+	LatchDrawPic(0,0,DEADPIC);
+	FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,false);
+	IN_ClearKeysDown();
+	IN_Ack();
+	VW_SetScreen (bufferofs,0);
+}
+
+//==========================================================================
+
+/*
+===================
+=
+= NormalScreen
+=
+===================
+*/
+
+void NormalScreen (void)
+{
+	 VW_SetSplitScreen (200);
+	 bufferofs = displayofs = SCREEN1START;
+	 VW_Bar(0,0,320,200,0);
+	 bufferofs = SCREEN2START;
+	 VW_Bar(0,0,320,200,0);
+	 VW_SetScreen (displayofs,0);
+}
+
+//==========================================================================
+
+/*
+===================
+=
+= DrawPlayScreen
+=
+===================
+*/
+
+void DrawPlayScreen (void)
+{
+	int     i,j,p,m;
+
+	screenpage = 0;
+
+	bufferofs = 0;
+	VW_Bar (0,0,320,STATUSLINES,7);
+	for (i=0;i<3;i++)
+	{
+		bufferofs = screenloc[i];
+		VW_Bar (0,0,320,VIEWHEIGHT,7);
+	}
+
+
+	VW_SetSplitScreen(144);
+	VW_SetScreen(screenloc[0],0);
+	bufferofs = 0;
+
+	CA_CacheGrChunk (STATUSPIC);
+	CA_CacheGrChunk (SIDEBARSPIC);
+
+	VW_DrawPic (0,0,STATUSPIC);
+
+	for (i=0;i<3;i++)
+	{
+		bufferofs = screenloc[i];
+		VW_DrawPic (33,0,SIDEBARSPIC);
+	}
+
+	grneeded[STATUSPIC]&= ~ca_levelbit;
+	grneeded[SIDEBARSPIC]&= ~ca_levelbit;
+	MM_SetPurge(&grsegs[STATUSPIC],3);
+	MM_SetPurge(&grsegs[SIDEBARSPIC],3);
+
+	RedrawStatusWindow ();
+	bufferofs = displayofs = screenloc[0];
+}
+
+
+//==========================================================================
+
+/*
+===================
+=
+= LoadLatchMem
+=
+===================
+*/
+
+void LoadLatchMem (void)
+{
+	int     i,j,p,m;
+	byte    far *src, far *dest;
+	unsigned        destoff;
+
+	EGAWRITEMODE(0);
+
+//
+// draw some pics into latch memory
+//
+
+//
+// tile 8s
+//
+	latchpics[0] = freelatch;
+	src = (byte _seg *)grsegs[STARTTILE8];
+	dest = MK_FP(0xa000,freelatch);
+
+	for (i=0;i<NUMTILE8;i++)
+	{
+		for (p=0;p<4;p++)
+		{
+			m = 1<<p;
+			asm     mov     dx,SC_INDEX
+			asm     mov     al,SC_MAPMASK
+			asm     mov     ah,[BYTE PTR m]
+			asm     out     dx,ax
+			for (j=0;j<8;j++)
+				*(dest+j)=*src++;
+		}
+		dest+=8;
+	}
+
+//
+// tile 16s
+//
+	src = (byte _seg *)grsegs[STARTTILE16];
+
+	for (i=0;i<NUMTILE16;i++)
+	{
+		CA_CacheGrChunk (STARTTILE16+i);
+		src = (byte _seg *)grsegs[STARTTILE16+i];
+		if (src)
+		{
+			tileoffsets[i] = FP_OFF(dest);
+			for (p=0;p<4;p++)
+			{
+				m = 1<<p;
+				asm     mov     dx,SC_INDEX
+				asm     mov     al,SC_MAPMASK
+				asm     mov     ah,[BYTE PTR m]
+				asm     out     dx,ax
+				for (j=0;j<32;j++)
+					*(dest+j)=*src++;
+			}
+			dest+=32;
+			MM_FreePtr (&grsegs[STARTTILE16+i]);
+			UNMARKGRCHUNK(STARTTILE16+i);
+		}
+		else
+			tileoffsets[i] = 0;
+	}
+
+
+//
+// pics
+//
+	destoff = FP_OFF(dest);
+	for (i=FIRSTLATCHPIC+1;i<FIRSTSCALEPIC;i++)
+	{
+		latchpics[i-FIRSTLATCHPIC] = destoff;
+		CA_CacheGrChunk (i);
+		j = pictable[i-STARTPICS].width * pictable[i-STARTPICS].height;
+		VW_MemToScreen (grsegs[i],destoff,j,1);
+		destoff+=j;
+		MM_FreePtr (&grsegs[i]);
+		UNMARKGRCHUNK(i);
+	}
+
+	EGAMAPMASK(15);
+}
+
+//==========================================================================
+
+/*
+===================
+=
+= FizzleFade
+=
+===================
+*/
+
+#define PIXPERFRAME     1600
+
+void FizzleFade (unsigned source, unsigned dest,
+	unsigned width,unsigned height, boolean abortable)
+{
+	unsigned        drawofs,pagedelta;
+	unsigned        char maskb[8] = {1,2,4,8,16,32,64,128};
+	unsigned        x,y,p,frame;
+	long            rndval;
+
+	pagedelta = dest-source;
+	VW_SetScreen (dest,0);
+	rndval = 1;
+	y = 0;
+
+asm     mov     es,[screenseg]
+asm     mov     dx,SC_INDEX
+asm     mov     al,SC_MAPMASK
+asm     out     dx,al
+
+	TimeCount=frame=0;
+	do      // while (1)
+	{
+		if (abortable)
+		{
+			IN_ReadControl(0,&c);
+			if (c.button0 || c.button1 || Keyboard[sc_Space]
+			|| Keyboard[sc_Enter])
+			{
+				VW_ScreenToScreen (source,dest,width/8,height);
+				return;
+			}
+		}
+
+		for (p=0;p<PIXPERFRAME;p++)
+		{
+			//
+			// seperate random value into x/y pair
+			//
+			asm     mov     ax,[WORD PTR rndval]
+			asm     mov     dx,[WORD PTR rndval+2]
+			asm     mov     bx,ax
+			asm     dec     bl
+			asm     mov     [BYTE PTR y],bl                 // low 8 bits - 1 = y xoordinate
+			asm     mov     bx,ax
+			asm     mov     cx,dx
+			asm     shr     cx,1
+			asm     rcr     bx,1
+			asm     shr     bx,1
+			asm     shr     bx,1
+			asm     shr     bx,1
+			asm     shr     bx,1
+			asm     shr     bx,1
+			asm     shr     bx,1
+			asm     shr     bx,1
+			asm     mov     [x],bx                                  // next 9 bits = x xoordinate
+			//
+			// advance to next random element
+			//
+			asm     shr     dx,1
+			asm     rcr     ax,1
+			asm     jnc     noxor
+			asm     xor     dx,0x0001
+			asm     xor     ax,0x2000
+noxor:
+			asm     mov     [WORD PTR rndval],ax
+			asm     mov     [WORD PTR rndval+2],dx
+
+			if (x>width || y>height)
+				continue;
+			drawofs = source+ylookup[y];
+
+			asm     mov     cx,[x]
+			asm     mov     si,cx
+			asm     and     si,7
+			asm     mov dx,GC_INDEX
+			asm     mov al,GC_BITMASK
+			asm     mov     ah,BYTE PTR [maskb+si]
+			asm     out dx,ax
+
+			asm     mov     si,[drawofs]
+			asm     shr     cx,1
+			asm     shr     cx,1
+			asm     shr     cx,1
+			asm     add     si,cx
+			asm     mov     di,si
+			asm     add     di,[pagedelta]
+
+			asm     mov     dx,GC_INDEX
+			asm     mov     al,GC_READMAP                   // leave GC_INDEX set to READMAP
+			asm     out     dx,al
+
+			asm     mov     dx,SC_INDEX+1
+			asm     mov     al,1
+			asm     out     dx,al
+			asm     mov     dx,GC_INDEX+1
+			asm     mov     al,0
+			asm     out     dx,al
+
+			asm     mov     bl,[es:si]
+			asm     xchg [es:di],bl
+
+			asm     mov     dx,SC_INDEX+1
+			asm     mov     al,2
+			asm     out     dx,al
+			asm     mov     dx,GC_INDEX+1
+			asm     mov     al,1
+			asm     out     dx,al
+
+			asm     mov     bl,[es:si]
+			asm     xchg [es:di],bl
+
+			asm     mov     dx,SC_INDEX+1
+			asm     mov     al,4
+			asm     out     dx,al
+			asm     mov     dx,GC_INDEX+1
+			asm     mov     al,2
+			asm     out     dx,al
+
+			asm     mov     bl,[es:si]
+			asm     xchg [es:di],bl
+
+			asm     mov     dx,SC_INDEX+1
+			asm     mov     al,8
+			asm     out     dx,al
+			asm     mov     dx,GC_INDEX+1
+			asm     mov     al,3
+			asm     out     dx,al
+
+			asm     mov     bl,[es:si]
+			asm     xchg [es:di],bl
+
+			if (rndval == 1)                // entire sequence has been completed
+			{
+				EGABITMASK(255);
+				EGAMAPMASK(15);
+				return;
+			};
+		}
+		frame++;
+		while (TimeCount<frame)         // don't go too fast
+		;
+	} while (1);
+
+
+}
+
+//==========================================================================
+
+/*
+===================
+=
+= FizzleOut
+=
+===================
+*/
+
+void FizzleOut (int showlevel)
+{
+	unsigned page1,page2;
+//
+// fizzle fade screen to grey
+//
+	bufferofs = screenloc[(screenpage+1)%3];
+	if (showlevel)
+		DrawEnterScreen ();
+	FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,false);
+}
+
+//==========================================================================
+
+/*
+====================
+=
+= FreeUpMemory
+=
+====================
+*/
+
+void FreeUpMemory (void)
+{
+	int     i;
+
+	for (i=0;i<NUMSCALEPICS;i++)
+		if (shapedirectory[i])
+			MM_SetPurge (&(memptr)shapedirectory[i],3);
+
+	for (i=0;i<NUMSCALEWALLS;i++)
+		if (walldirectory[i])
+			MM_SetPurge (&(memptr)walldirectory[i],3);
+}
+
+//==========================================================================
+
+/*
+==================
+=
+= DrawHighScores
+=
+==================
+*/
+
+void    DrawHighScores(void)
+{
+	char            buffer[16],*str;
+	word            i,j,
+				w,h,
+				x,y;
+	HighScore       *s;
+
+
+	CA_CacheGrChunk (HIGHSCORESPIC);
+	VWB_DrawPic (0,0,HIGHSCORESPIC);
+	MM_SetPurge (&grsegs[HIGHSCORESPIC],3);
+	UNMARKGRCHUNK(HIGHSCORESPIC);
+
+	for (i = 0,s = Scores;i < MaxScores;i++,s++)
+	{
+		PrintY = 68 + (16 * i);
+
+		//
+		// name
+		//
+		PrintX = 60;
+		US_Print(s->name);
+
+		//
+		// level
+		//
+		ultoa(s->completed,buffer,10);
+		for (str = buffer;*str;str++)
+			*str = *str + (129 - '0');      // Used fixed-width numbers (129...)
+		USL_MeasureString(buffer,&w,&h);
+		PrintX = (25 * 8) - 8 - w;
+		US_Print(buffer);
+
+		//
+		// score
+		//
+		ultoa(s->score,buffer,10);
+		for (str = buffer;*str;str++)
+			*str = *str + (129 - '0');      // Used fixed-width numbers (129...)
+		USL_MeasureString(buffer,&w,&h);
+		PrintX = (34 * 8) - 8 - w;
+		US_Print(buffer);
+	}
+
+	fontcolor = F_BLACK;
+}
+
+
+
+/*
+=======================
+=
+= CheckHighScore
+=
+=======================
+*/
+
+void    CheckHighScore (long score,word other)
+{
+	word            i,j;
+	int                     n;
+	HighScore       myscore;
+
+	strcpy(myscore.name,"");
+	myscore.score = score;
+	myscore.completed = other;
+
+	for (i = 0,n = -1;i < MaxScores;i++)
+	{
+		if
+		(
+			(myscore.score > Scores[i].score)
+		||      (
+				(myscore.score == Scores[i].score)
+			&&      (myscore.completed > Scores[i].completed)
+			)
+		)
+		{
+			for (j = MaxScores;--j > i;)
+				Scores[j] = Scores[j - 1];
+			Scores[i] = myscore;
+			n = i;
+			HighScoresDirty = true;
+			break;
+		}
+	}
+
+	if (n != -1)
+	{
+	//
+	// got a high score
+	//
+		DrawHighScores ();
+		PrintY = 68 + (16 * n);
+		PrintX = 60;
+		US_LineInput(PrintX,PrintY,Scores[n].name,nil,true,MaxHighName,100);
+	}
+}
+
+
+//==========================================================================
+
+/*
+===================
+=
+= GameLoop
+=
+===================
+*/
+
+void GameLoop (void)
+{
+	int i,xl,yl,xh,yh;
+	char num[20];
+#ifdef PROFILE
+	clock_t start,end;
+#endif
+
+	DrawPlayScreen ();
+
+restart:
+	if (!loadedgame)
+	{
+		gamestate.difficulty = restartgame;
+		restartgame = gd_Continue;
+		DrawEnterScreen ();
+	}
+
+	do
+	{
+		playstate = gd_Continue;
+		if (!loadedgame)
+			SetupGameLevel ();
+		else
+			loadedgame = false;
+
+		CacheScaleds ();
+
+#ifdef PROFILE
+start = clock();
+while (start == clock());
+start++;
+#endif
+		PlayLoop ();
+#ifdef PROFILE
+end = clock();
+itoa(end-start,str,10);
+		Quit (str);
+#endif
+
+
+		switch (playstate)
+		{
+		case ex_died:
+			Died ();
+			NormalScreen ();
+			FreeUpMemory ();
+			CheckHighScore (gamestate.score,gamestate.mapon+1);
+			return;
+		case ex_warped:
+			FizzleOut (true);
+			if (gamestate.mapon >= NUMLEVELS)
+			{
+				Victory ();
+				FreeUpMemory ();
+				CheckHighScore(gamestate.score,gamestate.mapon+1);
+				return;
+			}
+			break;
+		case ex_abort:
+			FreeUpMemory ();
+			return;
+		case ex_resetgame:
+		case ex_loadedgame:
+			goto restart;
+		case ex_victorious:
+			Victory ();
+			FreeUpMemory();
+			CheckHighScore(gamestate.score,gamestate.mapon+1);
+			return;
+		}
+
+	} while (1);
+
+}
diff --git a/src/lib/hb/c3_main.c b/src/lib/hb/c3_main.c
new file mode 100755
index 00000000..684cb329
--- /dev/null
+++ b/src/lib/hb/c3_main.c
@@ -0,0 +1,756 @@
+/* Catacomb 3-D Source Code
+ * Copyright (C) 1993-2014 Flat Rock Software
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// C3_MAIN.C
+
+#include "C3_DEF.H"
+#pragma hdrstop
+
+/*
+=============================================================================
+
+						   CATACOMB 3-D
+
+					  An Id Software production
+
+						   by John Carmack
+
+=============================================================================
+*/
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+memptr		scalesegs[NUMPICS];
+char		str[80],str2[20];
+unsigned	tedlevelnum;
+boolean		tedlevel;
+gametype	gamestate;
+exittype	playstate;
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+
+
+//===========================================================================
+
+// JAB Hack begin
+#define	MyInterrupt	0x60
+void interrupt (*intaddr)();
+void interrupt (*oldintaddr)();
+	char	*JHParmStrings[] = {"no386",nil};
+
+void
+jabhack(void)
+{
+extern void far jabhack2(void);
+extern int far	CheckIs386(void);
+
+	int	i;
+
+	oldintaddr = getvect(MyInterrupt);
+
+	for (i = 1;i < _argc;i++)
+		if (US_CheckParm(_argv[i],JHParmStrings) == 0)
+			return;
+
+	if (CheckIs386())
+	{
+		jabhack2();
+		setvect(MyInterrupt,intaddr);
+	}
+}
+
+void
+jabunhack(void)
+{
+	setvect(MyInterrupt,oldintaddr);
+}
+//	JAB Hack end
+
+//===========================================================================
+
+/*
+=====================
+=
+= NewGame
+=
+= Set up new game to start from the beginning
+=
+=====================
+*/
+
+void NewGame (void)
+{
+	memset (&gamestate,0,sizeof(gamestate));
+	gamestate.mapon = 0;
+	gamestate.body = MAXBODY;
+}
+
+//===========================================================================
+
+#define RLETAG	0xABCD
+
+/*
+==================
+=
+= SaveTheGame
+=
+==================
+*/
+
+boolean	SaveTheGame(int file)
+{
+	word	i,compressed,expanded;
+	objtype	*o;
+	memptr	bigbuffer;
+
+	if (!CA_FarWrite(file,(void far *)&gamestate,sizeof(gamestate)))
+		return(false);
+
+	expanded = mapwidth * mapheight * 2;
+	MM_GetPtr (&bigbuffer,expanded);
+
+	for (i = 0;i < 3;i+=2)	// Write planes 0 and 2
+	{
+//
+// leave a word at start of compressed data for compressed length
+//
+		compressed = (unsigned)CA_RLEWCompress ((unsigned huge *)mapsegs[i]
+			,expanded,((unsigned huge *)bigbuffer)+1,RLETAG);
+
+		*(unsigned huge *)bigbuffer = compressed;
+
+		if (!CA_FarWrite(file,(void far *)bigbuffer,compressed+2) )
+		{
+			MM_FreePtr (&bigbuffer);
+			return(false);
+		}
+	}
+
+	for (o = player;o;o = o->next)
+		if (!CA_FarWrite(file,(void far *)o,sizeof(objtype)))
+		{
+			MM_FreePtr (&bigbuffer);
+			return(false);
+		}
+
+	MM_FreePtr (&bigbuffer);
+
+	return(true);
+}
+
+//===========================================================================
+
+/*
+==================
+=
+= LoadTheGame
+=
+==================
+*/
+
+boolean	LoadTheGame(int file)
+{
+	unsigned	i,x,y;
+	objtype		*obj,*prev,*next,*followed;
+	unsigned	compressed,expanded;
+	unsigned	far *map,tile;
+	memptr		bigbuffer;
+
+	if (!CA_FarRead(file,(void far *)&gamestate,sizeof(gamestate)))
+		return(false);
+
+	SetupGameLevel ();		// load in and cache the base old level
+
+	expanded = mapwidth * mapheight * 2;
+	MM_GetPtr (&bigbuffer,expanded);
+
+	for (i = 0;i < 3;i+=2)	// Read planes 0 and 2
+	{
+		if (!CA_FarRead(file,(void far *)&compressed,sizeof(compressed)) )
+		{
+			MM_FreePtr (&bigbuffer);
+			return(false);
+		}
+
+		if (!CA_FarRead(file,(void far *)bigbuffer,compressed) )
+		{
+			MM_FreePtr (&bigbuffer);
+			return(false);
+		}
+
+		CA_RLEWexpand ((unsigned huge *)bigbuffer,
+			(unsigned huge *)mapsegs[i],expanded,RLETAG);
+	}
+
+	MM_FreePtr (&bigbuffer);
+//
+// copy the wall data to a data segment array again, to handle doors and
+// bomb walls that are allready opened
+//
+	memset (tilemap,0,sizeof(tilemap));
+	memset (actorat,0,sizeof(actorat));
+	map = mapsegs[0];
+	for (y=0;y<mapheight;y++)
+		for (x=0;x<mapwidth;x++)
+		{
+			tile = *map++;
+			if (tile<NUMFLOORS)
+			{
+				tilemap[x][y] = tile;
+				if (tile>0)
+					(unsigned)actorat[x][y] = tile;
+			}
+		}
+
+
+	// Read the object list back in - assumes at least one object in list
+
+	InitObjList ();
+	new = player;
+	while (true)
+	{
+		prev = new->prev;
+		next = new->next;
+		if (!CA_FarRead(file,(void far *)new,sizeof(objtype)))
+			return(false);
+		followed = new->next;
+		new->prev = prev;
+		new->next = next;
+		actorat[new->tilex][new->tiley] = new;	// drop a new marker
+
+		if (followed)
+			GetNewObj (false);
+		else
+			break;
+	}
+
+	return(true);
+}
+
+//===========================================================================
+
+/*
+==================
+=
+= ResetGame
+=
+==================
+*/
+
+void ResetGame(void)
+{
+	NewGame ();
+
+	ca_levelnum--;
+	ca_levelbit>>=1;
+	CA_ClearMarks();
+	ca_levelbit<<=1;
+	ca_levelnum++;
+}
+
+//===========================================================================
+
+
+/*
+==========================
+=
+= ShutdownId
+=
+= Shuts down all ID_?? managers
+=
+==========================
+*/
+
+void ShutdownId (void)
+{
+  US_Shutdown ();
+#ifndef PROFILE
+  SD_Shutdown ();
+  IN_Shutdown ();
+#endif
+  VW_Shutdown ();
+  CA_Shutdown ();
+  MM_Shutdown ();
+}
+
+
+//===========================================================================
+
+/*
+==========================
+=
+= InitGame
+=
+= Load a few things right away
+=
+==========================
+*/
+
+void InitGame (void)
+{
+	unsigned	segstart,seglength;
+	int			i,x,y;
+	unsigned	*blockstart;
+
+//	US_TextScreen();
+
+	MM_Startup ();
+	VW_Startup ();
+#ifndef PROFILE
+	IN_Startup ();
+	SD_Startup ();
+#endif
+	US_Startup ();
+
+//	US_UpdateTextScreen();
+
+	CA_Startup ();
+	US_Setup ();
+
+	US_SetLoadSaveHooks(LoadTheGame,SaveTheGame,ResetGame);
+
+//
+// load in and lock down some basic chunks
+//
+
+	CA_ClearMarks ();
+
+	CA_MarkGrChunk(STARTFONT);
+	CA_MarkGrChunk(STARTTILE8);
+	CA_MarkGrChunk(STARTTILE8M);
+	CA_MarkGrChunk(HAND1PICM);
+	CA_MarkGrChunk(HAND2PICM);
+	CA_MarkGrChunk(ENTERPLAQUEPIC);
+
+	CA_CacheMarks (NULL);
+
+	MM_SetLock (&grsegs[STARTFONT],true);
+	MM_SetLock (&grsegs[STARTTILE8],true);
+	MM_SetLock (&grsegs[STARTTILE8M],true);
+	MM_SetLock (&grsegs[HAND1PICM],true);
+	MM_SetLock (&grsegs[HAND2PICM],true);
+	MM_SetLock (&grsegs[ENTERPLAQUEPIC],true);
+
+	fontcolor = WHITE;
+
+
+//
+// build some tables
+//
+	for (i=0;i<MAPSIZE;i++)
+		nearmapylookup[i] = &tilemap[0][0]+MAPSIZE*i;
+
+	for (i=0;i<PORTTILESHIGH;i++)
+		uwidthtable[i] = UPDATEWIDE*i;
+
+	blockstart = &blockstarts[0];
+	for (y=0;y<UPDATEHIGH;y++)
+		for (x=0;x<UPDATEWIDE;x++)
+			*blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;
+
+	BuildTables ();			// 3-d tables
+
+	SetupScaling ();
+
+#ifndef PROFILE
+//	US_FinishTextScreen();
+#endif
+
+//
+// reclaim the memory from the linked in text screen
+//
+	segstart = FP_SEG(&introscn);
+	seglength = 4000/16;
+	if (FP_OFF(&introscn))
+	{
+		segstart++;
+		seglength--;
+	}
+
+	MML_UseSpace (segstart,seglength);
+
+	VW_SetScreenMode (GRMODE);
+	VW_ColorBorder (3);
+	VW_ClearVideo (BLACK);
+
+//
+// initialize variables
+//
+	updateptr = &update[0];
+	*(unsigned *)(updateptr + UPDATEWIDE*PORTTILESHIGH) = UPDATETERMINATE;
+	bufferofs = 0;
+	displayofs = 0;
+	VW_SetLineWidth(SCREENWIDTH);
+}
+
+//===========================================================================
+
+void clrscr (void);		// can't include CONIO.H because of name conflicts...
+
+/*
+==========================
+=
+= Quit
+=
+==========================
+*/
+
+void Quit (char *error)
+{
+	unsigned	finscreen;
+
+#if 0
+	if (!error)
+	{
+		CA_SetAllPurge ();
+		CA_CacheGrChunk (PIRACY);
+		finscreen = (unsigned)grsegs[PIRACY];
+	}
+#endif
+
+	ShutdownId ();
+	if (error && *error)
+	{
+	  puts(error);
+	  exit(1);
+	}
+
+#if 0
+	if (!NoWait)
+	{
+		movedata (finscreen,0,0xb800,0,4000);
+		bioskey (0);
+		clrscr();
+	}
+#endif
+
+	exit(0);
+}
+
+//===========================================================================
+
+/*
+==================
+=
+= TEDDeath
+=
+==================
+*/
+
+void	TEDDeath(void)
+{
+	ShutdownId();
+	execlp("TED5.EXE","TED5.EXE","/LAUNCH",NULL);
+}
+
+//===========================================================================
+
+/*
+=====================
+=
+= DemoLoop
+=
+=====================
+*/
+
+static	char *ParmStrings[] = {"easy","normal","hard",""};
+
+void	DemoLoop (void)
+{
+	int	i,level;
+
+//
+// check for launch from ted
+//
+	if (tedlevel)
+	{
+		NewGame();
+		gamestate.mapon = tedlevelnum;
+		restartgame = gd_Normal;
+		for (i = 1;i < _argc;i++)
+		{
+			if ( (level = US_CheckParm(_argv[i],ParmStrings)) == -1)
+				continue;
+
+			restartgame = gd_Easy+level;
+			break;
+		}
+		GameLoop();
+		TEDDeath();
+	}
+
+
+//
+// main game cycle
+//
+	displayofs = bufferofs = 0;
+	VW_Bar (0,0,320,200,0);
+
+	while (1)
+	{
+		CA_CacheGrChunk (TITLEPIC);
+		bufferofs = SCREEN2START;
+		displayofs = SCREEN1START;
+		VWB_DrawPic (0,0,TITLEPIC);
+		MM_SetPurge (&grsegs[TITLEPIC],3);
+		UNMARKGRCHUNK(TITLEPIC);
+		FizzleFade (bufferofs,displayofs,320,200,true);
+
+		if (!IN_UserInput(TickBase*3,false))
+		{
+			CA_CacheGrChunk (CREDITSPIC);
+			VWB_DrawPic (0,0,CREDITSPIC);
+			MM_SetPurge (&grsegs[CREDITSPIC],3);
+			UNMARKGRCHUNK(CREDITSPIC);
+			FizzleFade (bufferofs,displayofs,320,200,true);
+
+		}
+
+		if (!IN_UserInput(TickBase*3,false))
+		{
+highscores:
+			DrawHighScores ();
+			FizzleFade (bufferofs,displayofs,320,200,true);
+			IN_UserInput(TickBase*3,false);
+		}
+
+		if (IN_IsUserInput())
+		{
+			US_ControlPanel ();
+
+			if (restartgame || loadedgame)
+			{
+				GameLoop ();
+				goto highscores;
+			}
+		}
+
+	}
+}
+
+//===========================================================================
+
+/*
+==========================
+=
+= SetupScalePic
+=
+==========================
+*/
+
+void SetupScalePic (unsigned picnum)
+{
+	unsigned	scnum;
+
+	scnum = picnum-FIRSTSCALEPIC;
+
+	if (shapedirectory[scnum])
+	{
+		MM_SetPurge (&(memptr)shapedirectory[scnum],0);
+		return;					// allready in memory
+	}
+
+	CA_CacheGrChunk (picnum);
+	DeplanePic (picnum);
+	shapesize[scnum] = BuildCompShape (&shapedirectory[scnum]);
+	grneeded[picnum]&= ~ca_levelbit;
+	MM_FreePtr (&grsegs[picnum]);
+}
+
+//===========================================================================
+
+/*
+==========================
+=
+= SetupScaleWall
+=
+==========================
+*/
+
+void SetupScaleWall (unsigned picnum)
+{
+	int		x,y;
+	unsigned	scnum;
+	byte	far *dest;
+
+	scnum = picnum-FIRSTWALLPIC;
+
+	if (walldirectory[scnum])
+	{
+		MM_SetPurge (&walldirectory[scnum],0);
+		return;					// allready in memory
+	}
+
+	CA_CacheGrChunk (picnum);
+	DeplanePic (picnum);
+	MM_GetPtr(&walldirectory[scnum],64*64);
+	dest = (byte far *)walldirectory[scnum];
+	for (x=0;x<64;x++)
+		for (y=0;y<64;y++)
+			*dest++ = spotvis[y][x];
+	grneeded[picnum]&= ~ca_levelbit;
+	MM_FreePtr (&grsegs[picnum]);
+}
+
+//===========================================================================
+
+/*
+==========================
+=
+= SetupScaling
+=
+==========================
+*/
+
+void SetupScaling (void)
+{
+	int		i,x,y;
+	byte	far *dest;
+
+//
+// build the compiled scalers
+//
+	for (i=1;i<=VIEWWIDTH/2;i++)
+		BuildCompScale (i*2,&scaledirectory[i]);
+}
+
+//===========================================================================
+
+int	showscorebox;
+
+void RF_FixOfs (void)
+{
+}
+
+void HelpScreens (void)
+{
+}
+
+
+/*
+==================
+=
+= CheckMemory
+=
+==================
+*/
+
+#define MINMEMORY	335000l
+
+void	CheckMemory(void)
+{
+	unsigned	finscreen;
+
+	if (mminfo.nearheap+mminfo.farheap+mminfo.EMSmem+mminfo.XMSmem
+		>= MINMEMORY)
+		return;
+
+	CA_CacheGrChunk (OUTOFMEM);
+	finscreen = (unsigned)grsegs[OUTOFMEM];
+	ShutdownId ();
+	movedata (finscreen,7,0xb800,0,4000);
+	gotoxy (1,24);
+	exit(1);
+}
+
+//===========================================================================
+
+
+/*
+==========================
+=
+= main
+=
+==========================
+*/
+
+void main (void)
+{
+	short i;
+
+	if (stricmp(_argv[1], "/VER") == 0)
+	{
+		printf("Catacomb 3-D version 1.22  (Rev 1)\n");
+		printf("Copyright 1991-93 Softdisk Publishing\n");
+		printf("Developed for use with 100%% IBM compatibles\n");
+		printf("that have 640K memory and DOS version 3.3 or later\n");
+		printf("and EGA graphics or better.\n");
+		exit(0);
+	}
+
+	if (stricmp(_argv[1], "/?") == 0)
+	{
+		printf("Catacomb 3-D version 1.22\n");
+		printf("Copyright 1991-93 Softdisk Publishing\n\n");
+		printf("Syntax:\n");
+		printf("CAT3D [/<switch>]\n\n");
+		printf("Switch       What it does\n");
+		printf("/?           This Information\n");
+		printf("/VER         Display Program Version Information\n");
+		printf("/COMP        Fix problems with SVGA screens\n");
+		printf("/NOAL        No AdLib or SoundBlaster detection\n");
+		printf("/NOJOYS      Tell program to ignore joystick\n");
+		printf("/NOMOUSE     Tell program to ignore mouse\n");
+		printf("/HIDDENCARD  Overrides video detection\n\n");
+		printf("Each switch must include a '/' and multiple switches\n");
+		printf("must be seperated by at least one space.\n\n");
+
+		exit(0);
+	}
+
+	jabhack();
+
+	InitGame ();
+
+	CheckMemory ();
+
+	LoadLatchMem ();
+
+#ifdef PROFILE
+	NewGame ();
+	GameLoop ();
+#endif
+
+//NewGame ();
+//GameLoop ();
+
+	DemoLoop();
+	Quit("Demo loop exited???");
+}
diff --git a/src/lib/hb/c3_play.c b/src/lib/hb/c3_play.c
new file mode 100755
index 00000000..112041f9
--- /dev/null
+++ b/src/lib/hb/c3_play.c
@@ -0,0 +1,584 @@
+/* Catacomb 3-D Source Code
+ * Copyright (C) 1993-2014 Flat Rock Software
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// C3_PLAY.C
+
+#include "C3_DEF.H"
+#pragma hdrstop
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+#define POINTTICS	6
+
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+ControlInfo	c;
+boolean		running,slowturn;
+
+int			bordertime;
+objtype objlist[MAXACTORS],*new,*obj,*player,*lastobj,*objfreelist;
+
+unsigned	farmapylookup[MAPSIZE];
+byte		*nearmapylookup[MAPSIZE];
+
+boolean		singlestep,godmode;
+int			extravbls;
+
+//
+// replacing refresh manager
+//
+unsigned	mapwidth,mapheight,tics;
+boolean		compatability;
+byte		*updateptr;
+unsigned	mapwidthtable[64];
+unsigned	uwidthtable[UPDATEHIGH];
+unsigned	blockstarts[UPDATEWIDE*UPDATEHIGH];
+#define	UPDATESCREENSIZE	(UPDATEWIDE*PORTTILESHIGH+2)
+#define	UPDATESPARESIZE		(UPDATEWIDE*2+4)
+#define UPDATESIZE			(UPDATESCREENSIZE+2*UPDATESPARESIZE)
+byte		update[UPDATESIZE];
+
+int		mousexmove,mouseymove;
+int		pointcount,pointsleft;
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+void CalcBounds (objtype *ob);
+void DrawPlayScreen (void);
+
+
+//
+// near data map array (wall values only, get text number from far data)
+//
+byte		tilemap[MAPSIZE][MAPSIZE];
+byte		spotvis[MAPSIZE][MAPSIZE];
+objtype		*actorat[MAPSIZE][MAPSIZE];
+
+objtype dummyobj;
+
+int bordertime;
+int	objectcount;
+
+void StopMusic(void);
+void StartMusic(void);
+
+
+//==========================================================================
+
+///////////////////////////////////////////////////////////////////////////
+//
+//	CenterWindow() - Generates a window of a given width & height in the
+//		middle of the screen
+//
+///////////////////////////////////////////////////////////////////////////
+
+#define MAXX	264
+#define MAXY	146
+
+void	CenterWindow(word w,word h)
+{
+	US_DrawWindow(((MAXX / 8) - w) / 2,((MAXY / 8) - h) / 2,w,h);
+}
+
+//===========================================================================
+
+
+/*
+=====================
+=
+= CheckKeys
+=
+=====================
+*/
+
+void CheckKeys (void)
+{
+	if (screenfaded)			// don't do anything with a faded screen
+		return;
+
+//
+// pause key wierdness can't be checked as a scan code
+//
+	if (Paused)
+	{
+		CenterWindow (8,3);
+		US_PrintCentered ("PAUSED");
+		VW_UpdateScreen ();
+		SD_MusicOff();
+		IN_Ack();
+		SD_MusicOn();
+		Paused = false;
+		if (MousePresent) Mouse(MDelta);	// Clear accumulated mouse movement
+	}
+
+//
+// F1-F7/ESC to enter control panel
+//
+	if ( (LastScan >= sc_F1 && LastScan <= sc_F7) || LastScan == sc_Escape)
+	{
+		StopMusic ();
+		NormalScreen ();
+		FreeUpMemory ();
+		US_CenterWindow (20,8);
+		US_CPrint ("Loading");
+		VW_UpdateScreen ();
+		US_ControlPanel();
+		if (abortgame)
+		{
+			playstate = ex_abort;
+			return;
+		}
+		StartMusic ();
+		IN_ClearKeysDown();
+		if (restartgame)
+			playstate = ex_resetgame;
+		if (loadedgame)
+			playstate = ex_loadedgame;
+		DrawPlayScreen ();
+		CacheScaleds ();
+		lasttimecount = TimeCount;
+		if (MousePresent) Mouse(MDelta);	// Clear accumulated mouse movement
+	}
+
+//
+// F10-? debug keys
+//
+	if (Keyboard[sc_F10])
+	{
+		DebugKeys();
+		if (MousePresent) Mouse(MDelta);	// Clear accumulated mouse movement
+		lasttimecount = TimeCount;
+	}
+
+}
+
+
+//===========================================================================
+
+/*
+#############################################################################
+
+				  The objlist data structure
+
+#############################################################################
+
+objlist containt structures for every actor currently playing.  The structure
+is accessed as a linked list starting at *player, ending when ob->next ==
+NULL.  GetNewObj inserts a new object at the end of the list, meaning that
+if an actor spawn another actor, the new one WILL get to think and react the
+same frame.  RemoveObj unlinks the given object and returns it to the free
+list, but does not damage the objects ->next pointer, so if the current object
+removes itself, a linked list following loop can still safely get to the
+next element.
+
+<backwardly linked free list>
+
+#############################################################################
+*/
+
+
+/*
+=========================
+=
+= InitObjList
+=
+= Call to clear out the entire object list, returning them all to the free
+= list.  Allocates a special spot for the player.
+=
+=========================
+*/
+
+void InitObjList (void)
+{
+	int	i;
+
+	for (i=0;i<MAXACTORS;i++)
+	{
+		objlist[i].prev = &objlist[i+1];
+		objlist[i].next = NULL;
+	}
+
+	objlist[MAXACTORS-1].prev = NULL;
+
+	objfreelist = &objlist[0];
+	lastobj = NULL;
+
+	objectcount = 0;
+
+//
+// give the player and score the first free spots
+//
+	GetNewObj (false);
+	player = new;
+}
+
+//===========================================================================
+
+/*
+=========================
+=
+= GetNewObj
+=
+= Sets the global variable new to point to a free spot in objlist.
+= The free spot is inserted at the end of the liked list
+=
+= When the object list is full, the caller can either have it bomb out ot
+= return a dummy object pointer that will never get used
+=
+=========================
+*/
+
+void GetNewObj (boolean usedummy)
+{
+	if (!objfreelist)
+	{
+		if (usedummy)
+		{
+			new = &dummyobj;
+			return;
+		}
+		Quit ("GetNewObj: No free spots in objlist!");
+	}
+
+	new = objfreelist;
+	objfreelist = new->prev;
+	memset (new,0,sizeof(*new));
+
+	if (lastobj)
+		lastobj->next = new;
+	new->prev = lastobj;	// new->next is allready NULL from memset
+
+	new->active = false;
+	lastobj = new;
+
+	objectcount++;
+}
+
+//===========================================================================
+
+/*
+=========================
+=
+= RemoveObj
+=
+= Add the given object back into the free list, and unlink it from it's
+= neighbors
+=
+=========================
+*/
+
+void RemoveObj (objtype *gone)
+{
+	objtype **spotat;
+
+	if (gone == player)
+		Quit ("RemoveObj: Tried to remove the player!");
+
+//
+// fix the next object's back link
+//
+	if (gone == lastobj)
+		lastobj = (objtype *)gone->prev;
+	else
+		gone->next->prev = gone->prev;
+
+//
+// fix the previous object's forward link
+//
+	gone->prev->next = gone->next;
+
+//
+// add it back in to the free list
+//
+	gone->prev = objfreelist;
+	objfreelist = gone;
+}
+
+//==========================================================================
+
+/*
+===================
+=
+= PollControls
+=
+===================
+*/
+
+void PollControls (void)
+{
+	unsigned buttons;
+
+	IN_ReadControl(0,&c);
+
+	if (MousePresent)
+	{
+		Mouse(MButtons);
+		buttons = _BX;
+		Mouse(MDelta);
+		mousexmove = _CX;
+		mouseymove = _DX;
+
+		if (buttons&1)
+			c.button0 = 1;
+		if (buttons&2)
+			c.button1 = 1;
+
+	}
+
+	if (Controls[0]==ctrl_Joystick)
+	{
+		if (c.x>120 || c.x <-120 || c.y>120 || c.y<-120)
+			running = true;
+		else
+			running = false;
+		if (c.x>-48 && c.x<48)
+			slowturn = true;
+		else
+			slowturn = false;
+	}
+	else
+	{
+		if (Keyboard[sc_RShift])
+			running = true;
+		else
+			running = false;
+		if (c.button0)
+			slowturn = true;
+		else
+			slowturn = false;
+	}
+}
+
+//==========================================================================
+
+/*
+=================
+=
+= StopMusic
+=
+=================
+*/
+
+void StopMusic(void)
+{
+	int	i;
+
+	SD_MusicOff();
+	for (i = 0;i < LASTMUSIC;i++)
+		if (audiosegs[STARTMUSIC + i])
+		{
+			MM_SetPurge(&((memptr)audiosegs[STARTMUSIC + i]),3);
+			MM_SetLock(&((memptr)audiosegs[STARTMUSIC + i]),false);
+		}
+}
+
+//==========================================================================
+
+
+/*
+=================
+=
+= StartMusic
+=
+=================
+*/
+
+// JAB - Cache & start the appropriate music for this level
+void StartMusic(void)
+{
+	musicnames	chunk;
+
+	SD_MusicOff();
+	chunk =	TOOHOT_MUS;
+//	if ((chunk == -1) || (MusicMode != smm_AdLib))
+//DEBUG control panel		return;
+
+	MM_BombOnError (false);
+	CA_CacheAudioChunk(STARTMUSIC + chunk);
+	MM_BombOnError (true);
+	if (mmerror)
+		mmerror = false;
+	else
+	{
+		MM_SetLock(&((memptr)audiosegs[STARTMUSIC + chunk]),true);
+		SD_StartMusic((MusicGroup far *)audiosegs[STARTMUSIC + chunk]);
+	}
+}
+
+//==========================================================================
+
+
+/*
+===================
+=
+= PlayLoop
+=
+===================
+*/
+
+void PlayLoop (void)
+{
+	int		give;
+
+	void (*think)();
+
+	ingame = true;
+	playstate = TimeCount = 0;
+	gamestate.shotpower = handheight = 0;
+	pointcount = pointsleft = 0;
+
+	DrawLevelNumber (gamestate.mapon);
+	DrawBars ();
+
+#ifndef PROFILE
+	fizzlein = true;				// fizzle fade in the first refresh
+#endif
+	TimeCount = lasttimecount = lastnuke = 0;
+
+	PollControls ();				// center mouse
+	StartMusic ();
+	do
+	{
+#ifndef PROFILE
+		PollControls();
+#else
+		c.xaxis = 1;
+		if (++TimeCount == 300)
+			return;
+#endif
+
+		for (obj = player;obj;obj = obj->next)
+			if (obj->active)
+			{
+				if (obj->ticcount)
+				{
+					obj->ticcount-=tics;
+					while ( obj->ticcount <= 0)
+					{
+						think = obj->state->think;
+						if (think)
+						{
+							think (obj);
+							if (!obj->state)
+							{
+								RemoveObj (obj);
+								goto nextactor;
+							}
+						}
+
+						obj->state = obj->state->next;
+						if (!obj->state)
+						{
+							RemoveObj (obj);
+							goto nextactor;
+						}
+						if (!obj->state->tictime)
+						{
+							obj->ticcount = 0;
+							goto nextactor;
+						}
+						if (obj->state->tictime>0)
+							obj->ticcount += obj->state->tictime;
+					}
+				}
+				think =	obj->state->think;
+				if (think)
+				{
+					think (obj);
+					if (!obj->state)
+						RemoveObj (obj);
+				}
+nextactor:;
+			}
+
+
+		if (bordertime)
+		{
+			bordertime -= tics;
+			if (bordertime<=0)
+			{
+				bordertime = 0;
+				VW_ColorBorder (3);
+			}
+		}
+
+		if (pointcount)
+		{
+			pointcount -= tics;
+			if (pointcount <= 0)
+			{
+				pointcount += POINTTICS;
+				give = (pointsleft > 1000)? 1000 :
+						(
+							(pointsleft > 100)? 100 :
+								((pointsleft < 20)? pointsleft : 20)
+						);
+				SD_PlaySound (GETPOINTSSND);
+				AddPoints (give);
+				pointsleft -= give;
+				if (!pointsleft)
+					pointcount = 0;
+			}
+		}
+
+		ThreeDRefresh ();
+
+		CheckKeys();
+		if (singlestep)
+		{
+			VW_WaitVBL(14);
+			lasttimecount = TimeCount;
+		}
+		if (extravbls)
+			VW_WaitVBL(extravbls);
+
+	}while (!playstate);
+	StopMusic ();
+
+	ingame = false;
+	if (bordertime)
+	{
+		bordertime = 0;
+		VW_ColorBorder (3);
+	}
+
+	if (!abortgame)
+		AddPoints (pointsleft);
+	else
+		abortgame = false;
+}
+
diff --git a/src/lib/hb/c3_sca_a.asm b/src/lib/hb/c3_sca_a.asm
new file mode 100755
index 00000000..58a8737a
--- /dev/null
+++ b/src/lib/hb/c3_sca_a.asm
@@ -0,0 +1,153 @@
+; Catacomb 3-D Source Code
+; Copyright (C) 1993-2014 Flat Rock Software
+;
+; 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.,
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+IDEAL
+MODEL	MEDIUM,C
+
+include "ID_ASM.EQU"
+
+;===========================================================================
+;
+;                    SCALING GRAPHICS
+;
+;===========================================================================
+
+
+
+MACRO	MAKELAB NUM
+
+lab&NUM:
+
+ENDM
+
+MACRO	MAKEREF NUM
+
+dw OFFSET lab&NUM
+
+ENDM
+
+
+;=========================================================================
+
+MAXSCALES equ 256
+
+	DATASEG
+
+EXTRN	screenseg:WORD
+EXTRN	linewidth:WORD
+
+LABEL endtable WORD
+labcount = 0
+REPT MAXSCALES
+MAKEREF %labcount
+labcount = labcount + 1
+ENDM
+
+
+	CODESEG
+
+;==================================================
+;
+; void scaleline (int scale, unsigned picseg, unsigned maskseg,
+;                 unsigned screen, unsigned width)
+;
+;==================================================
+
+PROC	ScaleLine pixels:word, scaleptr:dword, picptr:dword, screen:word
+USES	si,di
+PUBLIC	ScaleLine
+
+;
+; modify doline procedure for proper width
+;
+	mov    	bx,[pixels]
+	cmp	bx,MAXSCALES
+	jbe	@@scaleok
+	mov     bx,MAXSCALES
+@@scaleok:
+	shl	bx,1
+	mov	bx,[endtable+bx]
+	push	[cs:bx]			;save the code that will be modified over
+	mov	[WORD cs:bx],0d18eh	;mov ss,cx
+	push	[cs:bx+2]		;save the code that will be modified over
+	mov	[WORD cs:bx+2],90c3h	;ret / nop
+	push	bx
+
+	mov	dx,[linewidth]
+
+	mov	di,[WORD screen]
+	mov	es,[screenseg]
+
+	mov	si,[WORD scaleptr]
+	mov	ds,[WORD scaleptr+2]
+
+	mov	bx,[WORD picptr]
+	mov	ax,[WORD picptr+2]	;will be moved into ss after call
+
+	mov	bp,bx
+
+	cli
+	call	doline
+	sti
+;
+; restore doline to regular state
+;
+	pop	bx		;address of modified code
+	pop     [cs:bx+2]
+	pop     [cs:bx]
+
+	mov	ax,ss
+	mov	ds,ax
+	ret
+
+;================
+;
+; doline
+;
+; Big unwound scaling routine
+;
+; ds:si = scale table
+; ss:bx = pic data
+; es:di = screen location
+;
+;================
+
+doline:
+
+	mov	cx,ss
+	mov	ss,ax		;can't call a routine with ss used...
+
+labcount = 0
+
+REPT MAXSCALES
+
+MAKELAB %labcount
+labcount = labcount + 1
+
+	lodsb			; get scaled pixel number
+	xlat	[ss:bx]		; look it up in the picture
+	xchg	[es:di],al	; load latches and write pixel to screen
+	add	di,dx		; down to next line
+
+ENDM
+
+	mov	ss,cx
+	ret
+
+ENDP
+
+END
\ No newline at end of file
diff --git a/src/lib/hb/c3_scale.c b/src/lib/hb/c3_scale.c
new file mode 100755
index 00000000..018083d3
--- /dev/null
+++ b/src/lib/hb/c3_scale.c
@@ -0,0 +1,690 @@
+/* Catacomb 3-D Source Code
+ * Copyright (C) 1993-2014 Flat Rock Software
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// C3_SCALE.C
+
+#include "C3_DEF.H"
+#pragma hdrstop
+
+//const	unsigned	viewheight = 144;
+const	unsigned	screenbwide = 40;
+const	byte		BACKGROUNDPIX	=   5;
+
+unsigned		shapesize[MAXSCALE+1];
+t_compscale _seg *scaledirectory[MAXSCALE+1];
+t_compshape _seg *shapedirectory[NUMSCALEPICS];
+memptr			walldirectory[NUMSCALEWALLS];
+
+/*
+===========================
+=
+= DeplanePic
+=
+= Takes a raw bit map of width bytes by height and creates a scaleable shape
+=
+= Returns the length of the shape in bytes
+=
+= Fills in spotvis (a convenient 64*64 array) with the color values
+=
+===========================
+*/
+
+void DeplanePic (int picnum)
+{
+	byte		far *plane0,far *plane1,far *plane2,far *plane3;
+	byte		by0,by1,by2,by3;
+	unsigned	x,y,b,color,shift,width,height;
+	byte		*dest;
+
+//
+// convert ega pixels to byte color values in a temp buffer
+//
+	width = pictable[picnum-STARTPICS].width;
+	height = pictable[picnum-STARTPICS].height;
+
+	if (width>64 || height!=64)
+		Quit ("DePlanePic: Bad size shape");
+
+	memset (spotvis,BACKGROUNDPIX,sizeof(spotvis));
+
+	plane0 = (byte _seg *)grsegs[picnum];
+	plane1 = plane0 + width*height;
+	plane2 = plane1 + width*height;
+	plane3 = plane2 + width*height;
+
+	for (y=0;y<height;y++)
+	{
+		dest = &spotvis[y][0];
+		for (x=0;x<width;x++)
+		{
+			by0 = *plane0++;
+			by1 = *plane1++;
+			by2 = *plane2++;
+			by3 = *plane3++;
+
+			for (b=0;b<8;b++)
+			{
+				shift=8-b;
+
+				color = 0;
+				asm	mov	cl,[BYTE PTR shift]
+				asm	mov	al,[BYTE PTR by3]
+				asm	rcr	al,cl;
+				asm	rcl	[BYTE PTR color],1;
+
+				asm	mov	cl,[BYTE PTR shift]
+				asm	mov	al,[BYTE PTR by2]
+				asm	rcr	al,cl;
+				asm	rcl	[BYTE PTR color],1;
+
+				asm	mov	cl,[BYTE PTR shift]
+				asm	mov	al,[BYTE PTR by1]
+				asm	rcr	al,cl;
+				asm	rcl	[BYTE PTR color],1;
+
+				asm	mov	cl,[BYTE PTR shift]
+				asm	mov	al,[BYTE PTR by0]
+				asm	rcr	al,cl;
+				asm	rcl	[BYTE PTR color],1;
+
+				*dest++ = color;
+			}	// B
+		}		// X
+	}			// Y
+}
+
+
+
+
+/*
+========================
+=
+= BuildCompScale
+=
+= Builds a compiled scaler object that will scale a 64 tall object to
+= the given height (centered vertically on the screen)
+=
+= height should be even
+=
+= Call with
+= ---------
+= DS:SI		Source for scale
+= ES:DI		Dest for scale
+=
+= Calling the compiled scaler only destroys AL
+=
+========================
+*/
+
+unsigned BuildCompScale (int height, memptr *finalspot)
+{
+	t_compscale 	_seg *work;
+	byte		far *code;
+
+	int			i;
+	long		fix,step;
+	unsigned	src,totalscaled,totalsize;
+	int			startpix,endpix,toppix;
+
+
+	MM_GetPtr (&(memptr)work,20000);
+
+	step = ((long)height<<16) / 64;
+	code = &work->code[0];
+	toppix = (viewheight-height)/2;
+	fix = 0;
+
+	for (src=0;src<=64;src++)
+	{
+		startpix = fix>>16;
+		fix += step;
+		endpix = fix>>16;
+
+		work->start[src] = startpix;
+		if (endpix>startpix)
+			work->width[src] = endpix-startpix;
+		else
+			work->width[src] = 0;
+
+//
+// mark the start of the code
+//
+		work->codeofs[src] = FP_OFF(code);
+
+//
+// compile some code if the source pixel generates any screen pixels
+//
+		startpix+=toppix;
+		endpix+=toppix;
+
+		if (startpix == endpix || endpix < 0 || startpix >= VIEWHEIGHT || src == 64)
+			continue;
+
+	//
+	// mov al,[si+src]
+	//
+		*code++ = 0x8a;
+		*code++ = 0x44;
+		*code++ = src;
+
+		for (;startpix<endpix;startpix++)
+		{
+			if (startpix >= VIEWHEIGHT)
+				break;						// off the bottom of the view area
+			if (startpix < 0)
+				continue;					// not into the view area
+
+		//
+		// and [es:di+heightofs],al
+		//
+			*code++ = 0x26;
+			*code++ = 0x20;
+			*code++ = 0x85;
+			*((unsigned far *)code)++ = startpix*screenbwide;
+		}
+
+	}
+
+//
+// retf
+//
+	*code++ = 0xcb;
+
+	totalsize = FP_OFF(code);
+	MM_GetPtr (finalspot,totalsize);
+	_fmemcpy ((byte _seg *)(*finalspot),(byte _seg *)work,totalsize);
+	MM_FreePtr (&(memptr)work);
+
+	return totalsize;
+}
+
+
+
+
+/*
+========================
+=
+= BuildCompShape
+=
+= typedef struct
+= {
+=	unsigned	width;
+=	unsigned	codeofs[64];
+= }	t_compshape;
+=
+= Width is the number of compiled line draws in the shape.  The shape
+= drawing code will assume that the midpoint of the shape is in the
+= middle of the width.
+=
+= The non background pixel data will start at codeofs[width], so codeofs
+= greater than width will be invalid.
+=
+= Each code offset will draw one vertical line of the shape, consisting
+= of 0 or more segments of scaled pixels.
+=
+= The scaled shapes use ss:0-4 as a scratch variable for the far call to
+= the compiled scaler, so zero it back out after the shape is scaled, or
+= a "null pointer assignment" will result upon termination.
+=
+= Setup for a call to a compiled shape
+= -----------------------------------
+= ax	toast
+= bx	toast
+= cx	toast
+= dx	segment of compiled shape
+= si	toast
+= di	byte at top of view area to draw the line in
+= bp	0
+= ss:2 and ds  the segment of the compiled scaler to use
+= es	screenseg
+=
+= Upon return, ds IS NOT SET to the data segment.  Do:
+=	mov	ax,ss
+=	mov	ds,ax
+=
+=
+= GC_BITMASK	set to the pixels to be drawn in the row of bytes under DI
+= GC_MODE		read mode 1, write mode 2
+= GC_COLORDONTCARE  set to 0, so all reads from video memory return 0xff
+=
+=
+= Code generated for each segment
+= -------------------------------
+=	mov	bx,[(segend+1)*2]
+=	mov	cx,[bx]
+=	mov	[BYTE PTR bx],0xc8		// far return
+=	mov	ax,[segstart*2]
+=	mov	[ss:0],ax				// entry point into the compiled scaler
+=	mov	ds,dx                   // (mov ds,cs) the data is after the compiled code
+=	mov	si,ofs data
+=	call	[bp]				// scale some pixels
+=	mov	ds,[bp+2]
+=	mov	[bx],cx					// un patch return
+=
+= Code generated after all segments on a line
+= -------------------------------------------
+=	retf
+=
+========================
+*/
+
+unsigned BuildCompShape (t_compshape _seg **finalspot)
+{
+	t_compshape 	_seg *work;
+	byte		far *code;
+	int			firstline,lastline,x,y;
+	unsigned	firstpix,lastpix,width;
+	unsigned	totalsize,pixelofs;
+	unsigned	buff;
+
+
+//	MM_GetPtr (&(memptr)work,20000);
+	EGAWRITEMODE(0);
+	EGAREADMAP(0);		// use ega screen memory for temp buffer
+	EGAMAPMASK(1);
+	buff = screenloc[1];
+	work = (t_compshape _seg *)(0xa000+(buff+15)/16);
+
+//
+// find the width of the shape
+//
+	firstline = -1;
+	x=0;
+	do
+	{
+		for (y=0;y<64;y++)
+			if (spotvis[y][x] != BACKGROUNDPIX)
+			{
+				firstline = x;
+				break;
+			}
+		if (++x == 64)
+			Quit ("BuildCompShape: No shape data!");
+	} while (firstline == -1);
+
+	lastline = -1;
+	x=63;
+	do
+	{
+		for (y=0;y<64;y++)
+			if (spotvis[y][x] != BACKGROUNDPIX)
+			{
+				lastline = x;
+				break;
+			}
+		x--;
+	} while (lastline == -1);
+
+	width = lastline-firstline+1;
+
+	work->width = width;
+	code = (byte far *)&work->codeofs[width];
+
+//
+// copy all non background pixels to the work space
+//
+	pixelofs = FP_OFF(code);
+
+	for (x=firstline;x<=lastline;x++)
+		for (y=0;y<64;y++)
+			if (spotvis[y][x] != BACKGROUNDPIX)
+				*code++ = spotvis[y][x];
+
+//
+// start compiling the vertical lines
+//
+	for (x=firstline;x<=lastline;x++)
+	{
+		work->codeofs[x-firstline] = FP_OFF(code);
+
+		y=0;
+		do
+		{
+		//
+		// scan past black background pixels
+		//
+			while (spotvis[y][x] == BACKGROUNDPIX && y<64)
+				y++;
+
+			if (y>63)		// no more segments
+				break;
+
+			firstpix = y+1;		// +1 because width is before codeofs
+
+		//
+		// scan past scalable pixels
+		//
+			while (spotvis[y][x] != BACKGROUNDPIX && y<64)
+				y++;
+
+			if (y>63)
+				lastpix = 65;
+			else
+				lastpix = y+1;	// actually one pixel past the last displayed
+
+		//
+		// compile the scale call
+		//
+			*code++ = 0x8b;		// mov bx,[lastpix*2]
+			*code++ = 0x1e;
+			*((unsigned far *)code)++ = lastpix*2;
+
+			*code++ = 0x8b;		// mov cx,[bx]
+			*code++ = 0x0f;
+
+			*code++ = 0xc6;		// move [BYTE bx],0xcb
+			*code++ = 0x07;
+			*code++ = 0xcb;
+
+			*code++ = 0xa1;		// mov ax,[firstpix*2]	/*************
+			*((unsigned far *)code)++ = firstpix*2;
+
+			*code++ = 0x36;		// mov [ss:0],ax
+			*code++ = 0xa3;
+			*code++ = 0x00;
+			*code++ = 0x00;
+
+			*code++ = 0x8e;		// mov ds,dx	(mov ds,cs)
+			*code++ = 0xda;
+
+			*code++ = 0xbe;		// mov si,OFFSET pixelofs-firstpixel
+			*((unsigned far *)code)++ = pixelofs-firstpix;
+
+			*code++ = 0xff;		// call [DWORD bp]
+			*code++ = 0x5e;
+			*code++ = 0x00;
+
+			*code++ = 0x8e;		// mov ds,[bp+2]
+			*code++ = 0x5e;
+			*code++ = 0x02;
+
+			*code++ = 0x89;		// mov [bx],cx
+			*code++ = 0x0f;
+
+			pixelofs += (lastpix-firstpix);
+		} while (y<63);
+
+	//
+	// retf
+	//
+		*code++ = 0xcb;
+	}
+
+
+//
+// copy the final shape to a properly sized buffer
+//
+	totalsize = FP_OFF(code);
+	MM_GetPtr ((memptr *)finalspot,totalsize);
+	_fmemcpy ((byte _seg *)(*finalspot),(byte _seg *)work,totalsize);
+//	MM_FreePtr (&(memptr)work);
+
+	return totalsize;
+}
+
+
+
+/*
+=======================
+=
+= ScaleShape
+=
+= Draws a compiled shape at [scale] pixels high
+=
+= Setup for call
+= --------------
+= GC_MODE			read mode 1, write mode 2
+= GC_COLORDONTCARE  set to 0, so all reads from video memory return 0xff
+= GC_INDEX			pointing at GC_BITMASK
+=
+=======================
+*/
+
+static	long		longtemp;
+
+void ScaleShape (int xcenter, t_compshape _seg *compshape, unsigned scale)
+{
+	t_compscale _seg *comptable;
+	unsigned	width,scalewidth;
+	int			x,pixel,lastpixel,pixwidth,min;
+	unsigned	far *codehandle, far *widthptr;
+	unsigned	badcodeptr;
+	int			rightclip;
+
+	if (!compshape)
+		Quit ("ScaleShape: NULL compshape ptr!");
+
+	scale = (scale+1)/2;
+	if (!scale)
+		return;								// too far away
+	if (scale>MAXSCALE)
+		scale = MAXSCALE;
+	comptable = scaledirectory[scale];
+
+	width = compshape->width;
+	scalewidth = comptable->start[width];
+
+	pixel = xcenter - scalewidth/2;
+	lastpixel = pixel+scalewidth-1;
+	if (pixel >= VIEWWIDTH || lastpixel < 0)
+		return;								// totally off screen
+
+//
+// scan backwards from the right edge until pixels are visable
+// rightclip is the first NON VISABLE pixel
+//
+	if (lastpixel>=VIEWWIDTH-1)
+		rightclip = VIEWWIDTH-1;
+	else
+		rightclip = lastpixel;
+
+	if (zbuffer[rightclip]>scale)
+	{
+		if (pixel>0)
+			min = pixel;
+		else
+			min = 0;
+		do
+		{
+			if (--rightclip < min)
+				return;							// totally covered or before 0
+			if (zbuffer[rightclip]<=scale)
+				break;
+		} while (1);
+	}
+	rightclip++;
+
+//
+// scan from the left until it is on screen, leaving
+// [pixel],[pixwidth],[codehandle],and [widthptr] set correctly
+//
+	*(((unsigned *)&longtemp)+1) = (unsigned)compshape;	// seg of shape
+	codehandle = &compshape->codeofs[0];
+	badcodeptr = compshape->codeofs[width];
+	widthptr = &comptable->width[0];
+	asm	mov	ax,[comptable]
+	asm	mov	WORD PTR [2],ax				// ds:0-4 is used as a far call pointer
+										// by the compiled shapes
+	pixwidth = *widthptr;				// scaled width of this pixel
+	while (!pixwidth)
+	{
+		pixwidth = *++widthptr;			// find the first visable pixel
+		codehandle++;
+	}
+
+	if (pixel<0)
+	{
+		do
+		{
+			if (pixel+pixwidth>0)
+			{
+				pixwidth += pixel;
+				pixel = 0;
+				break;
+			}
+			do
+			{
+				pixwidth = *++widthptr;
+				codehandle++;
+			} while (!pixwidth);
+			pixel+=pixwidth;
+		} while (1);
+	}
+
+//
+// scan until it is visable, leaving
+// [pixel],[pixwidth],[codehandle],and [widthptr] set correctly
+//
+	do
+	{
+		if (zbuffer[pixel] <= scale)
+			break;							// start drawing here
+		pixel++;
+		if (!--pixwidth)
+		{
+			do
+			{
+				pixwidth = *++widthptr;
+				codehandle++;
+			} while (!pixwidth);
+		}
+	} while (1);
+
+	if (pixel+pixwidth>rightclip)
+		pixwidth = rightclip-pixel;
+//
+// draw lines
+//
+	do		// while (1)
+	{
+	//
+	// scale a vertical segment [pixwidth] pixels wide at [pixel]
+	//
+		(unsigned)longtemp = *codehandle;	// offset of compiled code
+		if ((unsigned)longtemp == badcodeptr)
+			Quit ("ScaleShape: codehandle past end!");
+
+		asm	mov	bx,[pixel]
+		asm	mov	di,bx
+		asm	shr	di,1
+		asm	shr	di,1
+		asm	shr	di,1						// X in bytes
+		asm	add	di,[bufferofs]
+		asm	and	bx,7
+		asm	shl	bx,1
+		asm	shl	bx,1
+		asm	shl	bx,1
+		asm	add	bx,[pixwidth]				// bx = pixel*8+pixwidth-1
+		asm	dec	bx
+		asm	push	bx
+		asm	mov	al,BYTE PTR [bitmasks1+bx]
+		asm	mov	dx,GC_INDEX+1
+		asm	out	dx,al						// set bit mask register
+
+		asm	mov	es,[screenseg]
+		asm	push	si
+		asm	push	di
+		asm	push	bp
+		asm	xor	bp,bp
+		asm	mov	dx,[WORD PTR longtemp+2]
+		asm	mov	ds,[2]
+		asm	call ss:[DWORD PTR longtemp]		// scale the line of pixels
+		asm	mov	ax,ss
+		asm	mov	ds,ax
+		asm	pop		bp
+		asm	pop		di
+		asm	pop		si
+
+		asm	pop	bx
+		asm	mov	al,BYTE PTR [bitmasks2+bx]
+		asm	or	al,al
+		asm	jz	nosecond
+
+	//
+	// draw a second byte for vertical strips that cross two bytes
+	//
+		asm	inc	di
+		asm	mov	dx,GC_INDEX+1
+		asm	out	dx,al						// set bit mask register
+		asm	push	si
+		asm	push	di
+		asm	push	bp
+		asm	xor	bp,bp
+		asm	mov	dx,[WORD PTR longtemp+2]
+		asm	mov	ds,[2]
+		asm	call ss:[DWORD PTR longtemp]		// scale the line of pixels
+		asm	mov	ax,ss
+		asm	mov	ds,ax
+		asm	pop		bp
+		asm	pop		di
+		asm	pop		si
+
+
+	//
+	// advance to the next drawn line
+	//
+nosecond:;
+		if ( (pixel+=pixwidth) == rightclip )
+		{
+			asm	mov	WORD PTR [0],0
+			asm	mov	WORD PTR [2],0
+			return;							// all done!
+		}
+
+		do
+		{
+			pixwidth = *++widthptr;
+			codehandle++;
+		} while (!pixwidth);
+
+		if (pixel+pixwidth > rightclip)
+			pixwidth = rightclip-pixel;
+
+	} while (1);
+
+}
+
+//
+// bit mask tables for drawing scaled strips up to eight pixels wide
+//
+
+byte	bitmasks1[8][8] = {
+{0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff},
+{0x40,0x60,0x70,0x78,0x7c,0x7e,0x7f,0x7f},
+{0x20,0x30,0x38,0x3c,0x3e,0x3f,0x3f,0x3f},
+{0x10,0x18,0x1c,0x1e,0x1f,0x1f,0x1f,0x1f},
+{0x8,0xc,0xe,0xf,0xf,0xf,0xf,0xf},
+{0x4,0x6,0x7,0x7,0x7,0x7,0x7,0x7},
+{0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3},
+{0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1} };
+
+byte	bitmasks2[8][8] = {
+{0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0x80},
+{0,0,0,0,0,0,0x80,0xc0},
+{0,0,0,0,0,0x80,0xc0,0xe0},
+{0,0,0,0,0x80,0xc0,0xe0,0xf0},
+{0,0,0,0x80,0xc0,0xe0,0xf0,0xf8},
+{0,0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc},
+{0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe} };
+
+
+
+
+
+
diff --git a/src/lib/hb/c3_state.c b/src/lib/hb/c3_state.c
new file mode 100755
index 00000000..8c31eb06
--- /dev/null
+++ b/src/lib/hb/c3_state.c
@@ -0,0 +1,546 @@
+/* Catacomb 3-D Source Code
+ * Copyright (C) 1993-2014 Flat Rock Software
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// C3_STATE.C
+
+#include "C3_DEF.H"
+#pragma hdrstop
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+
+dirtype opposite[9] =
+	{south,west,north,east,southwest,northwest,northeast,southeast,nodir};
+
+
+
+//===========================================================================
+
+
+/*
+===================
+=
+= SpawnNewObj
+=
+===================
+*/
+
+void SpawnNewObj (unsigned x, unsigned y, statetype *state, unsigned size)
+{
+	GetNewObj (false);
+	new->size = size;
+	new->state = state;
+	new->ticcount = random (state->tictime)+1;
+
+	new->tilex = x;
+	new->tiley = y;
+	new->x = ((long)x<<TILESHIFT)+TILEGLOBAL/2;
+	new->y = ((long)y<<TILESHIFT)+TILEGLOBAL/2;
+	CalcBounds(new);
+	new->dir = nodir;
+
+	actorat[new->tilex][new->tiley] = new;
+}
+
+void SpawnNewObjFrac (long x, long y, statetype *state, unsigned size)
+{
+	GetNewObj (false);
+	new->size = size;
+	new->state = state;
+	new->ticcount = random (state->tictime)+1;
+	new->active = true;
+
+	new->x = x;
+	new->y = y;
+	new->tilex = x>>TILESHIFT;
+	new->tiley = y>>TILESHIFT;
+	CalcBounds(new);
+	new->distance = 100;
+	new->dir = nodir;
+}
+
+
+
+/*
+===================
+=
+= CheckHandAttack
+=
+= If the object can move next to the player, it will return true
+=
+===================
+*/
+
+boolean CheckHandAttack (objtype *ob)
+{
+	long deltax,deltay,size;
+
+	size = (long)ob->size + player->size + ob->speed*tics;
+	deltax = ob->x - player->x;
+	deltay = ob->y - player->y;
+
+	if (deltax > size || deltax < -size || deltay > size || deltay < -size)
+		return false;
+
+	return true;
+}
+
+
+/*
+===================
+=
+= T_DoDamage
+=
+= Attacks the player if still nearby, then immediately changes to next state
+=
+===================
+*/
+
+void T_DoDamage (objtype *ob)
+{
+	int	points;
+
+
+	if (!CheckHandAttack (ob))
+	{
+		SD_PlaySound (MONSTERMISSSND);
+	}
+	else
+	{
+		points = 0;
+
+		switch (ob->obclass)
+		{
+		case orcobj:
+			points = 4;
+			break;
+		case trollobj:
+			points = 8;
+			break;
+		case demonobj:
+			points = 15;
+			break;
+		}
+		TakeDamage (points);
+	}
+
+	ob->state = ob->state->next;
+}
+
+
+//==========================================================================
+
+/*
+==================================
+=
+= Walk
+=
+==================================
+*/
+
+boolean Walk (objtype *ob)
+{
+	switch (ob->dir)
+	{
+	case north:
+		if (actorat[ob->tilex][ob->tiley-1])
+			return false;
+		ob->tiley--;
+		ob->distance = TILEGLOBAL;
+		return true;
+
+	case northeast:
+		if (actorat[ob->tilex+1][ob->tiley-1])
+			return false;
+		ob->tilex++;
+		ob->tiley--;
+		ob->distance = TILEGLOBAL;
+		return true;
+
+	case east:
+		if (actorat[ob->tilex+1][ob->tiley])
+			return false;
+		ob->tilex++;
+		ob->distance = TILEGLOBAL;
+		return true;
+
+	case southeast:
+		if (actorat[ob->tilex+1][ob->tiley+1])
+			return false;
+		ob->tilex++;
+		ob->tiley++;
+		ob->distance = TILEGLOBAL;
+		return true;
+
+	case south:
+		if (actorat[ob->tilex][ob->tiley+1])
+			return false;
+		ob->tiley++;
+		ob->distance = TILEGLOBAL;
+		return true;
+
+	case southwest:
+		if (actorat[ob->tilex-1][ob->tiley+1])
+			return false;
+		ob->tilex--;
+		ob->tiley++;
+		ob->distance = TILEGLOBAL;
+		return true;
+
+	case west:
+		if (actorat[ob->tilex-1][ob->tiley])
+			return false;
+		ob->tilex--;
+		ob->distance = TILEGLOBAL;
+		return true;
+
+	case northwest:
+		if (actorat[ob->tilex-1][ob->tiley-1])
+			return false;
+		ob->tilex--;
+		ob->tiley--;
+		ob->distance = TILEGLOBAL;
+		return true;
+
+	case nodir:
+		return false;
+	}
+
+	Quit ("Walk: Bad dir");
+	return false;
+}
+
+
+
+/*
+==================================
+=
+= ChaseThink
+= have the current monster go after the player,
+= either diagonally or straight on
+=
+==================================
+*/
+
+void ChaseThink (objtype *obj, boolean diagonal)
+{
+	int deltax,deltay,i;
+	dirtype d[3];
+	dirtype tdir, olddir, turnaround;
+
+
+	olddir=obj->dir;
+	turnaround=opposite[olddir];
+
+	deltax=player->tilex - obj->tilex;
+	deltay=player->tiley - obj->tiley;
+
+	d[1]=nodir;
+	d[2]=nodir;
+
+	if (deltax>0)
+		d[1]= east;
+	if (deltax<0)
+		d[1]= west;
+	if (deltay>0)
+		d[2]=south;
+	if (deltay<0)
+		d[2]=north;
+
+	if (abs(deltay)>abs(deltax))
+	{
+		tdir=d[1];
+		d[1]=d[2];
+		d[2]=tdir;
+	}
+
+	if (d[1]==turnaround)
+		d[1]=nodir;
+	if (d[2]==turnaround)
+		d[2]=nodir;
+
+
+	if (diagonal)
+	{                           /*ramdiagonals try the best dir first*/
+		if (d[1]!=nodir)
+		{
+			obj->dir=d[1];
+			if (Walk(obj))
+				return;     /*either moved forward or attacked*/
+		}
+
+		if (d[2]!=nodir)
+		{
+			obj->dir=d[2];
+			if (Walk(obj))
+				return;
+		}
+	}
+	else
+	{                  /*ramstraights try the second best dir first*/
+
+		if (d[2]!=nodir)
+		{
+			obj->dir=d[2];
+			if (Walk(obj))
+				return;
+		}
+
+		if (d[1]!=nodir)
+		{
+			obj->dir=d[1];
+			if (Walk(obj))
+				return;
+		}
+	}
+
+/* there is no direct path to the player, so pick another direction */
+
+	obj->dir=olddir;
+	if (Walk(obj))
+		return;
+
+	if (US_RndT()>128) 	/*randomly determine direction of search*/
+	{
+		for (tdir=north;tdir<=west;tdir++)
+		{
+			if (tdir!=turnaround)
+			{
+				obj->dir=tdir;
+				if (Walk(obj))
+					return;
+			}
+		}
+	}
+	else
+	{
+		for (tdir=west;tdir>=north;tdir--)
+		{
+			if (tdir!=turnaround)
+			{
+			  obj->dir=tdir;
+			  if (Walk(obj))
+				return;
+			}
+		}
+	}
+
+	obj->dir=turnaround;
+	Walk(obj);		/*last chance, don't worry about returned value*/
+}
+
+
+/*
+=================
+=
+= MoveObj
+=
+=================
+*/
+
+void MoveObj (objtype *ob, long move)
+{
+	ob->distance -=move;
+
+	switch (ob->dir)
+	{
+	case north:
+		ob->y -= move;
+		return;
+	case northeast:
+		ob->x += move;
+		ob->y -= move;
+		return;
+	case east:
+		ob->x += move;
+		return;
+	case southeast:
+		ob->x += move;
+		ob->y += move;
+		return;
+	case south:
+		ob->y += move;
+		return;
+	case southwest:
+		ob->x -= move;
+		ob->y += move;
+		return;
+	case west:
+		ob->x -= move;
+		return;
+	case northwest:
+		ob->x -= move;
+		ob->y -= move;
+		return;
+
+	case nodir:
+		return;
+	}
+}
+
+
+/*
+=================
+=
+= Chase
+=
+= returns true if hand attack range
+=
+=================
+*/
+
+boolean Chase (objtype *ob, boolean diagonal)
+{
+	long move;
+	long deltax,deltay,size;
+
+	move = ob->speed*tics;
+	size = (long)ob->size + player->size + move;
+
+	while (move)
+	{
+		deltax = ob->x - player->x;
+		deltay = ob->y - player->y;
+
+		if (deltax <= size && deltax >= -size
+		&& deltay <= size && deltay >= -size)
+		{
+			CalcBounds (ob);
+			return true;
+		}
+
+		if (move < ob->distance)
+		{
+			MoveObj (ob,move);
+			break;
+		}
+		actorat[ob->tilex][ob->tiley] = 0;	// pick up marker from goal
+		if (ob->dir == nodir)
+			ob->dir = north;
+
+		ob->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;
+		ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;
+		move -= ob->distance;
+
+		ChaseThink (ob,diagonal);
+		if (!ob->distance)
+			break;			// no possible move
+		actorat[ob->tilex][ob->tiley] = ob;	// set down a new goal marker
+	}
+	CalcBounds (ob);
+	return false;
+}
+
+//===========================================================================
+
+
+/*
+===================
+=
+= ShootActor
+=
+===================
+*/
+
+void ShootActor (objtype *ob, unsigned damage)
+{
+	ob->hitpoints -= damage;
+	if (ob->hitpoints<=0)
+	{
+		switch (ob->obclass)
+		{
+		case orcobj:
+			ob->state = &s_orcdie1;
+			GivePoints (100);
+			break;
+		case trollobj:
+			ob->state = &s_trolldie1;
+			GivePoints (400);
+			break;
+		case demonobj:
+			ob->state = &s_demondie1;
+			GivePoints (1000);
+			break;
+		case mageobj:
+			ob->state = &s_magedie1;
+			GivePoints (600);
+			break;
+		case batobj:
+			ob->state = &s_batdie1;
+			GivePoints (100);
+			break;
+		case grelmobj:
+			ob->state = &s_greldie1;
+			GivePoints (10000);
+			break;
+
+		}
+		ob->obclass = inertobj;
+		ob->shootable = false;
+		actorat[ob->tilex][ob->tiley] = NULL;
+	}
+	else
+	{
+		switch (ob->obclass)
+		{
+		case orcobj:
+			ob->state = &s_orcouch;
+			break;
+		case trollobj:
+			ob->state = &s_trollouch;
+			break;
+		case demonobj:
+			ob->state = &s_demonouch;
+			break;
+		case mageobj:
+			ob->state = &s_mageouch;
+			break;
+		case grelmobj:
+			ob->state = &s_grelouch;
+			break;
+
+		}
+	}
+	ob->ticcount = ob->state->tictime;
+}
+
diff --git a/src/lib/hb/c3_trace.c b/src/lib/hb/c3_trace.c
new file mode 100755
index 00000000..45cb1bca
--- /dev/null
+++ b/src/lib/hb/c3_trace.c
@@ -0,0 +1,872 @@
+/* Catacomb 3-D Source Code
+ * Copyright (C) 1993-2014 Flat Rock Software
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// C3_TRACE.C
+
+#include "C3_DEF.H"
+#pragma hdrstop
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+
+//
+// TESTWALLVISABLE will set the global variable wallvisable to 1 or 0
+// depending on if tile.x,tile.y,wallon is visable from focal point
+//
+#define TESTWALLVISABLE {						\
+	if (tile.y<focal.y)                         \
+		voffset = 0;                            \
+	else if (tile.y==focal.y)                   \
+		voffset = 3;                            \
+	else                                        \
+		voffset = 6;                            \
+	if (tile.x==focal.x)                        \
+		voffset ++;                             \
+	else if (tile.x>focal.x)                    \
+		voffset += 2;                           \
+	wallvisable = visable[voffset][wallon]; }
+
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+boolean	aborttrace;
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+unsigned	wallvisable,voffset;
+
+
+fixed edgex,edgey;
+
+int wallon;
+int basecolor;
+
+walltype *oldwall;
+
+//
+// offsets from upper left corner of a tile to the left and right edges of
+// a given wall (NORTH-WEST)
+//
+fixed point1x[4] = {GLOBAL1,GLOBAL1,0      ,0       };
+fixed point1y[4] = {0      ,GLOBAL1,GLOBAL1,0       };
+
+fixed point2x[4] = {0      ,GLOBAL1,GLOBAL1,0       };
+fixed point2y[4] = {0     ,0	   ,GLOBAL1 ,GLOBAL1};
+
+
+//
+// offset from tile.x,tile.y of the tile that shares wallon side
+// (side is not visable if it is shared)
+//
+int sharex[4] = { 0, 1, 0,-1};
+int sharey[4] = {-1, 0, 1, 0};
+
+//
+// amount to move tile.x,tile.y to follow wallon to another tile
+//
+int followx[4] = {-1, 0, 1, 0};
+int followy[4] = { 0,-1, 0, 1};
+
+//
+// cornerwall gives the wall on the same tile to start following when the
+// wall ends at an empty tile (go around an edge on same tile)
+// turnwall gives the wall on tile.x+sharex,tile.y+sharey to start following
+// when the wall hits another tile (right angle corner)
+//
+int cornerwall[4] = {WEST,NORTH,EAST,SOUTH};
+int turnwall[4] = {EAST,SOUTH,WEST,NORTH};
+
+//
+// wall visabilities in reletive locations
+// -,- 0,- +,-
+// -,0 0,0 +,0
+// -,+ 0,+ +,+
+//
+int visable[9][4] =
+{
+ {0,1,1,0}, {0,0,1,0}, {0,0,1,1},
+ {0,1,0,0}, {0,0,0,0}, {0,0,0,1},
+ {1,1,0,0}, {1,0,0,0}, {1,0,0,1}
+};
+
+int startwall[9] =  {2,2,3, 1,0,3, 1,0,0};
+int backupwall[9] = {3,3,0, 2,0,0, 2,1,1};
+
+
+int	walllength;
+
+/*
+=============================================================================
+
+					 FUNCTIONS
+
+=============================================================================
+*/
+
+/*
+========================
+=
+= FollowTrace
+=
+========================
+*/
+
+int FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max)
+{
+	int tx,ty,otx,oty;
+	long absdx,absdy,xstep,ystep;
+
+	tx = tracex>>TILESHIFT;
+	ty = tracey>>TILESHIFT;
+
+	spotvis[tx][ty] = true;
+
+	absdx=LABS(deltax);
+	absdy=LABS(deltay);
+
+	if (absdx>absdy)
+	{
+		ystep = (deltay<<8)/(absdx>>8);
+
+		if (!ystep)
+			ystep = deltay>0 ? 1 : -1;
+
+		oty = (tracey+ystep)>>TILESHIFT;
+		if (deltax>0)
+		{
+//###############
+//
+// step x by +1
+//
+//###############
+			do
+			{
+				tx++;
+				spotvis[tx][ty] = true;
+				tracey+=ystep;
+				ty = tracey>>TILESHIFT;
+
+				if (ty!=oty)
+				{
+					if (tilemap[tx-1][ty])
+					{
+						tile.x = tx-1;
+						tile.y = ty;
+						return 1;
+					}
+					oty = ty;
+				}
+				if (tilemap[tx][ty])
+				{
+					tile.x = tx;
+					tile.y = ty;
+					return 1;
+				}
+			} while (--max);
+			return 0;
+		}
+		else
+		{
+//###############
+//
+// step x by -1
+//
+//###############
+			do
+			{
+				tx--;
+				spotvis[tx][ty] = true;
+				tracey+=ystep;
+				ty = tracey>>TILESHIFT;
+
+				if (ty!=oty)
+				{
+					if (tilemap[tx][oty])
+					{
+						tile.x = tx;
+						tile.y = oty;
+						return 1;
+					}
+					oty = ty;
+				}
+				if (tilemap[tx][ty])
+				{
+					tile.x = tx;
+					tile.y = ty;
+					return 1;
+				}
+			} while (--max);
+			return 0;
+
+		}
+	}
+	else
+	{
+		xstep = (deltax<<8)/(absdy>>8);
+		if (!xstep)
+			xstep = deltax>0 ? 1 : -1;
+
+
+		otx = (tracex+xstep)>>TILESHIFT;
+		if (deltay>0)
+		{
+//###############
+//
+// step y by +1
+//
+//###############
+			do
+			{
+				ty++;
+				spotvis[tx][ty] = true;
+				tracex+=xstep;
+				tx = tracex>>TILESHIFT;
+
+				if (tx!=otx)
+				{
+					if (tilemap[tx][ty-1])
+					{
+						tile.x = tx;
+						tile.y = ty-1;
+						return 1;
+					}
+					otx = tx;
+				}
+				if (tilemap[tx][ty])
+				{
+					tile.x = tx;
+					tile.y = ty;
+					return 1;
+				}
+			} while (--max);
+			return 0;
+		}
+		else
+		{
+//###############
+//
+// step y by -1
+//
+//###############
+			do
+			{
+				ty--;
+				spotvis[tx][ty] = true;
+				tracex+=xstep;
+				tx = tracex>>TILESHIFT;
+
+				if (tx!=otx)
+				{
+					if (tilemap[otx][ty])
+					{
+						tile.x = otx;
+						tile.y = ty;
+						return 1;
+					}
+					otx = tx;
+				}
+				if (tilemap[tx][ty])
+				{
+					tile.x = tx;
+					tile.y = ty;
+					return 1;
+				}
+			} while (--max);
+			return 0;
+		}
+
+	}
+
+}
+
+
+//===========================================================================
+
+
+/*
+=================
+=
+= BackTrace
+=
+= Traces backwards from edgex,edgey to viewx,viewy to see if a closer
+= tile obscures the given point.  If it does, it finishes the wall and
+= starts a new one.
+= Returns true if a tile is hit.
+= Call with a 1 to have it automatically finish the current wall
+=
+=================
+*/
+
+int BackTrace (int finish)
+{
+  fixed tracex,tracey;
+  long deltax,deltay,absdx,absdy;
+  int steps,otx,oty,testx,testheight,offset,wall;
+
+  deltax = viewx-edgex;
+  deltay = viewy-edgey;
+
+  absdx = LABS(deltax);
+  absdy = LABS(deltay);
+
+  if (absdx>absdy)
+    steps = ABS(focal.x-(edgex>>TILESHIFT))-1;
+  else
+    steps = ABS(focal.y-(edgey>>TILESHIFT))-1;
+
+  if (steps<=0)
+    return 0;
+
+  otx = tile.x;
+  oty = tile.y;
+  if (!FollowTrace(edgex,edgey,deltax,deltay,steps))
+    return 0;
+
+//
+// if the start wall is behind the focal point, the trace went too far back
+//
+  if (ABS(tile.x-focal.x)<2 && ABS(tile.y-focal.y)<2)	// too close
+  {
+    if (tile.x == focal.x && tile.y == focal.y)
+    {
+      tile.x = otx;
+      tile.y = oty;
+      return 0;
+    }
+
+    if (tile.x<focal.x)
+    {
+      if (tile.y<focal.y)
+	wall = SOUTH;
+      else
+	wall = EAST;
+    }
+    else if (tile.x==focal.x)
+    {
+	  if (tile.y<focal.y)
+	wall = SOUTH;
+      else
+	wall = NORTH;
+    }
+    else
+	{
+      if (tile.y<=focal.y)
+	wall = WEST;
+      else
+	wall = NORTH;
+    }
+
+    //
+    // rotate the X value to see if it is behind the view plane
+    //
+    if (TransformX (((long)tile.x<<16)+point1x[wall],
+		    ((long)tile.y<<16)+point1y[wall]) < FOCALLENGTH)
+    {
+      tile.x = otx;
+      tile.y = oty;
+      return 0;
+    }
+  }
+
+//
+// if the old wall is still behind a closer wall, ignore the back trace
+// and continue on (dealing with limited precision...)
+//
+  if (finish && !FinishWall ())	// the wall is still behind a forward wall
+  {
+    tile.x = otx;
+    tile.y = oty;
+    rightwall->x1 = oldwall->x2;		// common edge with last wall
+    rightwall->height1 = oldwall->height2;
+    return 0;
+  }
+
+
+//
+// back up along the intersecting face to find the rightmost wall
+//
+
+  if (tile.y<focal.y)
+    offset = 0;
+  else if (tile.y==focal.y)
+    offset = 3;
+  else
+    offset = 6;
+  if (tile.x==focal.x)
+    offset ++;
+  else if (tile.x>focal.x)
+    offset += 2;
+
+  wallon = backupwall[offset];
+
+  while (tilemap[tile.x][tile.y])
+  {
+    tile.x += followx[wallon];
+    tile.y += followy[wallon];
+  };
+
+  tile.x -= followx[wallon];
+  tile.y -= followy[wallon];
+
+  wallon = cornerwall[wallon];	// turn to first visable face
+
+  edgex = ((long)tile.x<<16);
+  edgey = ((long)tile.y<<16);
+
+  TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon],
+    &rightwall->x1,&rightwall->height1);
+
+  basecolor = tilemap[tile.x][tile.y];
+
+  return 1;
+}
+
+//===========================================================================
+
+
+/*
+=================
+=
+= ForwardTrace
+=
+= Traces forwards from edgex,edgey along the line from viewx,viewy until
+= a solid tile is hit.  Sets tile.x,tile.y
+=
+=================
+*/
+
+void ForwardTrace (void)
+{
+  int offset;
+  fixed tracex,tracey;
+  long deltax,deltay;
+
+  deltax = edgex-viewx;
+  deltay = edgey-viewy;
+
+  FollowTrace(edgex,edgey,deltax,deltay,0);
+
+  if (tile.y<focal.y)
+    offset = 0;
+  else if (tile.y==focal.y)
+    offset = 3;
+  else
+    offset = 6;
+  if (tile.x==focal.x)
+    offset ++;
+  else if (tile.x>focal.x)
+    offset += 2;
+
+  wallon = startwall[offset];
+
+//
+// start the new wall
+//
+  edgex = ((long)tile.x<<16);
+  edgey = ((long)tile.y<<16);
+
+//
+// if entire first wall is invisable, corner
+//
+  TransformPoint (edgex+point2x[wallon],edgey+point2y[wallon],
+    &rightwall->x2,&rightwall->height2);
+
+  if (tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]]
+  || rightwall->x2 < (rightwall-1)->x2 )
+    wallon = cornerwall [wallon];
+
+//
+// transform first point
+//
+
+  TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon],
+    &rightwall->x1,&rightwall->height1);
+
+  basecolor = tilemap[tile.x][tile.y];
+}
+
+
+//===========================================================================
+
+
+/*
+=================
+=
+= FinishWall
+=
+= Transforms edgex,edgey as the next point of the current wall
+= and sticks it in the wall list
+=
+=================
+*/
+
+int FinishWall (void)
+{
+  char num[20];
+
+  oldwall = rightwall;
+
+	rightwall->color  = basecolor;
+
+  TransformPoint (edgex,edgey,&rightwall->x2,&rightwall->height2);
+
+  if (rightwall->x2 <= (rightwall-1)->x2+2
+  && rightwall->height2 < (rightwall-1)->height2 )
+	return 0;
+
+  rightwall->walllength = walllength;
+
+  switch (wallon)
+  {
+  case north:
+  case south:
+	  rightwall->side = 0;
+	  rightwall->planecoord = edgey;
+	  break;
+
+  case west:
+  case east:
+	  rightwall->side = 1;
+	  rightwall->planecoord = edgex;
+	  break;
+  }
+
+  walllength = 1;
+
+  rightwall++;
+
+  return 1;
+}
+
+//===========================================================================
+
+
+/*
+=================
+=
+= InsideCorner
+=
+=================
+*/
+
+void InsideCorner (void)
+{
+  int offset;
+
+  //
+  // the wall turned -90 degrees, so draw what we have, move to the new tile,
+  // change wallon, change color, and continue following.
+  //
+  FinishWall ();
+
+  tile.x += sharex[wallon];
+  tile.y += sharey[wallon];
+
+  wallon = turnwall[wallon];
+
+  //
+  // if the new wall is visable, continue following it.  Otherwise
+  // follow it backwards until it turns
+  //
+  TESTWALLVISABLE;
+
+  if (wallvisable)
+  {
+  //
+  // just turn to the next wall and continue
+  //
+    rightwall->x1 = oldwall->x2;		// common edge with last wall
+    rightwall->height1 = oldwall->height2;
+    basecolor = tilemap[tile.x][tile.y];
+    return;			// continue from here
+  }
+
+  //
+  // back follow the invisable wall until it turns, then follow that
+  //
+  do
+  {
+	tile.x += followx[wallon];
+    tile.y += followy[wallon];
+  } while (tilemap[tile.x][tile.y]);
+
+  tile.x -= followx[wallon];
+  tile.y -= followy[wallon];
+
+  wallon = cornerwall[wallon];	// turn to first visable face
+
+  edgex = ((long)tile.x<<16)+point1x[wallon];
+  edgey = ((long)tile.y<<16)+point1y[wallon];
+
+  if (!BackTrace(0))		// backtrace without finishing a wall
+  {
+    TransformPoint (edgex,edgey,&rightwall->x1,&rightwall->height1);
+    basecolor = tilemap[tile.x][tile.y];
+  }
+}
+
+//===========================================================================
+
+
+/*
+=================
+=
+= OutsideCorner
+=
+=================
+*/
+
+void OutsideCorner (void)
+{
+  int offset;
+
+  //
+  // edge is the outside edge of a corner, so draw the current wall and
+  // turn the corner (+90 degrees)
+  //
+  FinishWall ();
+
+  tile.x -= followx[wallon];	// backup to the real tile
+  tile.y -= followy[wallon];
+  wallon = cornerwall[wallon];
+
+  //
+  // if the new wall is visable, continue following it.  Otherwise
+  // trace a ray from the corner to find a wall in the distance to
+  // follow
+  //
+  TESTWALLVISABLE;
+
+  if (wallvisable)
+  {
+  //
+  // the new wall is visable, so just continue on
+  //
+    rightwall->x1 = oldwall->x2;		// common edge with last wall
+    rightwall->height1 = oldwall->height2;
+    return;			// still on same tile, so color is ok
+  }
+
+//
+// start from a new tile further away
+//
+  ForwardTrace();		// find the next wall further back
+
+}
+
+
+//===========================================================================
+
+
+/*
+=================
+=
+= FollowWalls
+=
+= Starts a wall edge at the leftmost edge of tile.x,tile.y and follows it
+= until something else is seen or the entire view area is covered
+=
+=================
+*/
+
+void FollowWalls (void)
+{
+  int height,newcolor,offset,wall;
+
+//####################
+//
+// figure leftmost wall of new tile
+//
+//####################
+
+restart:
+
+  walllength = 1;
+
+  if (tile.y<focal.y)
+	offset = 0;
+  else if (tile.y==focal.y)
+	offset = 3;
+  else
+	offset = 6;
+  if (tile.x==focal.x)
+	offset ++;
+  else if (tile.x>focal.x)
+	offset += 2;
+
+  wallon = startwall[offset];
+
+//
+// if the start wall is inside a block, skip it by cornering to the second wall
+//
+  if ( tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]])
+	wallon = cornerwall [wallon];
+
+//
+// transform first edge to screen coordinates
+//
+  edgex = ((long)tile.x<<16);
+  edgey = ((long)tile.y<<16);
+
+  TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon],
+	&rightwall->x1,&rightwall->height1);
+
+  basecolor = tilemap[tile.x][tile.y];
+
+//##################
+//
+// follow the wall as long as possible
+//
+//##################
+
+advance:
+
+  do	// while ( tile.x != right.x || tile.y != right.y)
+  {
+//
+// check for conditions that shouldn't happed...
+//
+	if (rightwall->x1 > VIEWXH)	// somehow missed right tile...
+	  return;
+
+	if (rightwall == &walls[DANGERHIGH])
+	{
+  //
+  // somethiing got messed up!  Correct by thrusting ahead...
+  //
+		VW_ColorBorder(6);
+		bordertime = 60;
+		Thrust(player->angle,TILEGLOBAL/4);
+		player->angle+=5;
+		if (player->angle>ANGLES)
+			player->angle-=ANGLES;
+		aborttrace = true;
+		return;
+
+#if 0
+	  strcpy (str,"Wall list overflow at LE:");
+	  itoa(mapon+1,str2,10);
+	  strcat (str,str2);
+	  strcat (str," X:");
+	  ltoa(objlist[0].x,str2,10);
+	  strcat (str,str2);
+	  strcat (str," Y:");
+	  ltoa(objlist[0].y,str2,10);
+	  strcat (str,str2);
+	  strcat (str," AN:");
+	  itoa(objlist[0].angle,str2,10);
+	  strcat (str,str2);
+
+	  Quit (str);
+#endif
+	}
+
+//
+// proceed along wall
+//
+
+	edgex = ((long)tile.x<<16)+point2x[wallon];
+	edgey = ((long)tile.y<<16)+point2y[wallon];
+
+	if (BackTrace(1))		// went behind a closer wall
+	  continue;
+
+	//
+	// advance to next tile along wall
+	//
+	tile.x += followx[wallon];
+	tile.y += followy[wallon];
+
+	if (tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]])
+	{
+	  InsideCorner ();		// turn at a corner
+	  continue;
+	}
+
+	newcolor = tilemap[tile.x][tile.y];
+
+	if (!newcolor)		// turn around an edge
+	{
+	  OutsideCorner ();
+	  continue;
+	}
+
+	if (newcolor != basecolor)
+	{
+	  //
+	  // wall changed color, so draw what we have and continue following
+	  //
+	  FinishWall ();
+	  rightwall->x1 = oldwall->x2;	// new wall shares this edge
+	  rightwall->height1 = oldwall->height2;
+	  basecolor = newcolor;
+
+	  continue;
+	}
+	walllength++;
+  } while (tile.x != right.x || tile.y != right.y);
+
+
+
+//######################
+//
+// draw the last tile
+//
+//######################
+
+  edgex = ((long)tile.x<<16)+point2x[wallon];
+  edgey = ((long)tile.y<<16)+point2y[wallon];
+  FinishWall();
+
+  wallon = cornerwall[wallon];
+
+  //
+  // if the corner wall is visable, draw it
+  //
+  TESTWALLVISABLE;
+
+  if (wallvisable)
+  {
+    rightwall->x1 = oldwall->x2;		// common edge with last wall
+    rightwall->height1 = oldwall->height2;
+    edgex = ((long)tile.x<<16)+point2x[wallon];
+    edgey = ((long)tile.y<<16)+point2y[wallon];
+    FinishWall();
+  }
+
+}
+
+//===========================================================================
diff --git a/src/lib/hb/c3_wiz.c b/src/lib/hb/c3_wiz.c
new file mode 100755
index 00000000..1d68bc75
--- /dev/null
+++ b/src/lib/hb/c3_wiz.c
@@ -0,0 +1,2046 @@
+/* Catacomb 3-D Source Code
+ * Copyright (C) 1993-2014 Flat Rock Software
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// C3_WIZ.C
+
+#include "C3_DEF.H"
+#pragma hdrstop
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+#define NUMSCROLLS	8
+
+#define	SHOWITEMS	9
+
+#define	NUKETIME	40
+#define NUMBOLTS	10
+#define BOLTTICS	6
+
+#define STATUSCOLOR	4
+#define TEXTCOLOR	14
+
+#define SIDEBARWIDTH	5
+
+#define BODYLINE    8
+#define POWERLINE	80
+
+#define SPECTILESTART	18
+
+
+#define SHOTDAMAGE		1
+#define BIGSHOTDAMAGE	3
+
+
+#define PLAYERSPEED	5120
+#define RUNSPEED	8192
+
+#define SHOTSPEED	10000
+
+#define LASTWALLTILE	17
+#define LASTSPECIALTILE	37
+
+#define FIRETIME	4	// DEBUG 60
+
+#define HANDPAUSE	60
+
+#define COMPASSX	33
+#define COMPASSY	0
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+long		lastnuke,lasthand;
+int			handheight;
+int			boltsleft;
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+int			lasttext,lastcompass;
+int			bolttimer;
+unsigned	lastfiretime;
+
+int	strafeangle[9] = {0,90,180,270,45,135,225,315,0};
+
+
+//===========================================================================
+
+void DrawChar (unsigned x, unsigned y, unsigned tile);
+void RedrawStatusWindow (void);
+void GiveBolt (void);
+void TakeBolt (void);
+void GiveNuke (void);
+void TakeNuke (void);
+void GivePotion (void);
+void TakePotion (void);
+void GiveKey (int keytype);
+void TakeKey (int keytype);
+void GiveScroll (int scrolltype,boolean show);
+void ReadScroll (int scroll);
+void GivePoints (int points);
+void DrawLevelNumber (int number);
+void DrawText (void);
+void DrawBars (void);
+
+//----------
+
+void Shoot (void);
+void BigShoot (void);
+void CastBolt (void);
+void CastNuke (void);
+void DrinkPotion (void);
+
+//----------
+
+void SpawnPlayer (int tilex, int tiley, int dir);
+void Thrust (int angle, unsigned speed);
+void T_Player (objtype *ob);
+
+void AddPoints (int points);
+
+void ClipMove (objtype *ob, long xmove, long ymove);
+boolean ShotClipMove (objtype *ob, long xmove, long ymove);
+
+//===========================================================================
+
+
+/*
+===============
+=
+= DrawChar
+=
+===============
+*/
+
+void DrawChar (unsigned x, unsigned y, unsigned tile)
+{
+	unsigned junk = latchpics[0];
+
+	EGAWRITEMODE(1);
+asm	mov	bx,[y]
+asm	shl	bx,1
+asm	mov	di,[WORD PTR ylookup+bx]
+asm	add	di,[x]
+asm	mov	si,[tile]
+asm	shl	si,1
+asm	shl	si,1
+asm	shl	si,1
+asm	add	si,[junk]		// the damn inline assembler won't reference latchpics
+asm	mov	ax,[screenseg]
+asm	mov	es,ax
+asm	mov	ds,ax
+asm	mov	dx,SCREENWIDTH-1
+asm	movsb
+asm	add	di,dx
+asm	movsb
+asm	add	di,dx
+asm	movsb
+asm	add	di,dx
+asm	movsb
+asm	add	di,dx
+asm	movsb
+asm	add	di,dx
+asm	movsb
+asm	add	di,dx
+asm	movsb
+asm	add	di,dx
+asm	movsb
+
+asm	mov	ax,ss
+asm	mov	ds,ax
+	EGAWRITEMODE(0);
+}
+
+
+//===========================================================================
+
+/*
+===============
+=
+= RedrawStatusWindow
+=
+===============
+*/
+
+void RedrawStatusWindow (void)
+{
+	int	i,j,x;
+
+	DrawLevelNumber (gamestate.mapon);
+	lasttext = -1;
+	lastcompass = -1;
+
+	j = gamestate.bolts < SHOWITEMS ? gamestate.bolts : SHOWITEMS;
+	for (i=0;i<j;i++)
+		DrawChar(7+i,20,BOLTCHAR);
+	j = gamestate.nukes < SHOWITEMS ? gamestate.nukes : SHOWITEMS;
+	for (i=0;i<j;i++)
+		DrawChar(7+i,30,NUKECHAR);
+	j = gamestate.potions < SHOWITEMS ? gamestate.potions : SHOWITEMS;
+	for (i=0;i<j;i++)
+		DrawChar(7+i,40,POTIONCHAR);
+
+	x=24;
+	for (i=0;i<4;i++)
+		for (j=0;j<gamestate.keys[i];j++)
+			DrawChar(x++,20,KEYCHARS+i);
+
+	x=24;
+	for (i=0;i<8;i++)
+		if (gamestate.scrolls[i])
+			DrawChar(x++,30,SCROLLCHARS+i);
+
+	AddPoints(0);
+
+	DrawBars ();
+}
+
+
+//===========================================================================
+
+/*
+===============
+=
+= GiveBolt
+=
+===============
+*/
+
+void GiveBolt (void)
+{
+	SD_PlaySound (GETBOLTSND);
+	if (++gamestate.bolts<=9)
+		DrawChar(6+gamestate.bolts,20,BOLTCHAR);
+}
+
+
+/*
+===============
+=
+= TakeBolt
+=
+===============
+*/
+
+void TakeBolt (void)
+{
+	SD_PlaySound (USEBOLTSND);
+	if (--gamestate.bolts<=9)
+		DrawChar(7+gamestate.bolts,20,BLANKCHAR);
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= GiveNuke
+=
+===============
+*/
+
+void GiveNuke (void)
+{
+	SD_PlaySound (GETNUKESND);
+	if (++gamestate.nukes<=9)
+		DrawChar(6+gamestate.nukes,30,NUKECHAR);
+}
+
+
+/*
+===============
+=
+= TakeNuke
+=
+===============
+*/
+
+void TakeNuke (void)
+{
+	SD_PlaySound (USENUKESND);
+	if (--gamestate.nukes<=9)
+		DrawChar(7+gamestate.nukes,30,BLANKCHAR);
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= GivePotion
+=
+===============
+*/
+
+void GivePotion (void)
+{
+	SD_PlaySound (GETPOTIONSND);
+	if (++gamestate.potions<=9)
+		DrawChar(6+gamestate.potions,40,POTIONCHAR);
+}
+
+
+/*
+===============
+=
+= TakePotion
+=
+===============
+*/
+
+void TakePotion (void)
+{
+	SD_PlaySound (USEPOTIONSND);
+	if (--gamestate.potions<=9)
+		DrawChar(7+gamestate.potions,40,BLANKCHAR);
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= GiveKey
+=
+===============
+*/
+
+void GiveKey (int keytype)
+{
+	int	i,j,x;
+
+	SD_PlaySound (GETKEYSND);
+	gamestate.keys[keytype]++;
+
+	x=24;
+	for (i=0;i<4;i++)
+		for (j=0;j<gamestate.keys[i];j++)
+			DrawChar(x++,20,KEYCHARS+i);
+
+}
+
+
+/*
+===============
+=
+= TakeKey
+=
+===============
+*/
+
+void TakeKey (int keytype)
+{
+	int	i,j,x;
+
+	SD_PlaySound (USEKEYSND);
+	gamestate.keys[keytype]--;
+
+	x=24;
+	for (i=0;i<4;i++)
+		for (j=0;j<gamestate.keys[i];j++)
+			DrawChar(x++,20,KEYCHARS+i);
+
+	DrawChar(x,20,BLANKCHAR);
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= GiveScroll
+=
+===============
+*/
+
+void GiveScroll (int scrolltype,boolean show)
+{
+	int	i,x;
+
+	SD_PlaySound (GETSCROLLSND);
+	gamestate.scrolls[scrolltype] = true;
+
+	x=24;
+	for (i=0;i<8;i++)
+		if (gamestate.scrolls[i])
+			DrawChar(x++,30,SCROLLCHARS+i);
+	if (show)
+		ReadScroll(scrolltype);
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= GivePoints
+=
+===============
+*/
+
+void GivePoints (int points)
+{
+	pointcount = 1;
+	pointsleft += points;
+}
+
+
+//===========================================================================
+
+/*
+===============
+=
+= AddPoints
+=
+===============
+*/
+
+void AddPoints (int points)
+{
+	char	str[10];
+	int		len,x,i;
+
+	gamestate.score += points;
+
+	ltoa (gamestate.score,str,10);
+	len = strlen (str);
+
+	x=24+(8-len);
+	for (i=0;i<len;i++)
+		DrawChar(x++,40,NUMBERCHARS+str[i]-'0');
+}
+
+
+//===========================================================================
+
+/*
+===============
+=
+= GiveChest
+=
+===============
+*/
+
+void GiveChest (void)
+{
+	SD_PlaySound (GETPOINTSSND);
+	GivePoints ((gamestate.mapon+1)*100);
+}
+
+
+//===========================================================================
+
+/*
+===============
+=
+= GiveGoal
+=
+===============
+*/
+
+void GiveGoal (void)
+{
+	SD_PlaySound (GETPOINTSSND);
+	GivePoints (100000);
+	playstate = ex_victorious;
+}
+
+
+//===========================================================================
+
+/*
+===============
+=
+= DrawLevelNumber
+=
+===============
+*/
+
+void DrawLevelNumber (int number)
+{
+	char	str[10];
+	int		len;
+	unsigned	temp;
+
+	bufferofs = 0;
+	if (number<9)
+		PrintX=13;
+	else
+		PrintX = 5;
+	PrintY = 4;
+	VW_Bar (5,4,16,9,STATUSCOLOR);
+	temp = fontcolor;
+	fontcolor = TEXTCOLOR^STATUSCOLOR;
+	US_PrintUnsigned (number+1);
+	fontcolor = temp;
+}
+
+
+//===========================================================================
+
+/*
+===============
+=
+= DrawText
+=
+===============
+*/
+
+void DrawText (void)
+{
+	unsigned	number;
+	char		str[80];
+	char 		far *text;
+	unsigned	temp;
+
+	//
+	// draw a new text description if needed
+	//
+	number = *(mapsegs[0]+farmapylookup[player->tiley]+player->tilex)-NAMESTART;
+
+	if ( number>26 )
+		number = 0;
+
+	if (number == lasttext)
+		return;
+
+	bufferofs = 0;
+	lasttext = number;
+
+	PrintY = 4;
+	WindowX = 26;
+	WindowW = 232;
+
+	text = (char _seg *)grsegs[LEVEL1TEXT+mapon]+textstarts[number];
+
+	_fmemcpy (str,text,80);
+
+	VW_Bar (26,4,232,9,STATUSCOLOR);
+	temp = fontcolor;
+	fontcolor = TEXTCOLOR^STATUSCOLOR;
+	US_CPrintLine (str);
+	fontcolor = temp;
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= DrawCompass
+=
+===============
+*/
+
+void DrawCompass (void)
+{
+	int		angle,number;
+
+	//
+	// draw the compass if needed
+	//
+	angle = player->angle-ANGLES/4;
+	angle -= ANGLES/32;
+	if (angle<0)
+		angle+=ANGLES;
+	number = angle/(ANGLES/16);
+	if (number>15)					// because 360 angles doesn't divide by 16
+		number = 15;
+
+	if (number == lastcompass)
+		return;
+
+	lastcompass = number;
+
+	bufferofs = 0;
+	LatchDrawPic (COMPASSX,COMPASSY,COMPAS1PIC+15-number);
+}
+
+//===========================================================================
+
+
+/*
+===============
+=
+= DrawBars
+=
+===============
+*/
+
+void DrawBars (void)
+{
+	int			i;
+	unsigned	source,dest,topline;
+
+	for (i=0;i<3;i++)
+	{
+		bufferofs = screenloc[i];
+		VW_Bar (34*8,POWERLINE,40,MAXSHOTPOWER,1);
+	}
+	EGAWRITEMODE(1);
+	asm	mov	es,[screenseg]
+
+//
+// shot power
+//
+	if (gamestate.shotpower)
+	{
+		topline = MAXSHOTPOWER - gamestate.shotpower;
+
+		source = latchpics[SHOTPOWERPIC-FIRSTLATCHPIC]+topline*SIDEBARWIDTH;
+		dest = (POWERLINE+topline)*SCREENWIDTH+34;
+
+		asm	mov	si,[source]
+		asm	mov	di,[dest]
+
+		asm	mov	cx,[WORD PTR gamestate.shotpower]
+newline:
+	asm	mov	al,[es:si]
+	asm	mov	[es:di+PAGE1START],al
+	asm	mov	[es:di+PAGE2START],al
+	asm	mov	[es:di+PAGE3START],al
+	asm	mov	al,[es:si+1]
+	asm	mov	[es:di+1+PAGE1START],al
+	asm	mov	[es:di+1+PAGE2START],al
+	asm	mov	[es:di+1+PAGE3START],al
+	asm	mov	al,[es:si+2]
+	asm	mov	[es:di+2+PAGE1START],al
+	asm	mov	[es:di+2+PAGE2START],al
+	asm	mov	[es:di+2+PAGE3START],al
+	asm	mov	al,[es:si+3]
+	asm	mov	[es:di+3+PAGE1START],al
+	asm	mov	[es:di+3+PAGE2START],al
+	asm	mov	[es:di+3+PAGE3START],al
+	asm	mov	al,[es:si+4]
+	asm	mov	[es:di+4+PAGE1START],al
+	asm	mov	[es:di+4+PAGE2START],al
+	asm	mov	[es:di+4+PAGE3START],al
+
+	asm	add	di,SCREENWIDTH
+	asm	add	si,5
+
+		asm	loop	newline
+	}
+
+//
+// body
+//
+	if (gamestate.body)
+	{
+		source = latchpics[BODYPIC-FIRSTLATCHPIC];
+		dest = BODYLINE*SCREENWIDTH+34;
+
+		asm	mov	si,[source]
+		asm	mov	di,[dest]
+
+		asm	mov	cx,[WORD PTR gamestate.body]
+newline2:
+	asm	mov	al,[es:si]
+	asm	mov	[es:di+PAGE1START],al
+	asm	mov	[es:di+PAGE2START],al
+	asm	mov	[es:di+PAGE3START],al
+	asm	mov	al,[es:si+1]
+	asm	mov	[es:di+1+PAGE1START],al
+	asm	mov	[es:di+1+PAGE2START],al
+	asm	mov	[es:di+1+PAGE3START],al
+	asm	mov	al,[es:si+2]
+	asm	mov	[es:di+2+PAGE1START],al
+	asm	mov	[es:di+2+PAGE2START],al
+	asm	mov	[es:di+2+PAGE3START],al
+	asm	mov	al,[es:si+3]
+	asm	mov	[es:di+3+PAGE1START],al
+	asm	mov	[es:di+3+PAGE2START],al
+	asm	mov	[es:di+3+PAGE3START],al
+	asm	mov	al,[es:si+4]
+	asm	mov	[es:di+4+PAGE1START],al
+	asm	mov	[es:di+4+PAGE2START],al
+	asm	mov	[es:di+4+PAGE3START],al
+
+	asm	add	di,SCREENWIDTH
+	asm	add	si,5
+
+		asm	loop	newline2
+	}
+
+	if (gamestate.body != MAXBODY)
+	{
+		source = latchpics[NOBODYPIC-FIRSTLATCHPIC]+gamestate.body*SIDEBARWIDTH;
+		dest = (BODYLINE+gamestate.body)*SCREENWIDTH+34;
+		topline = MAXBODY-gamestate.body;
+
+		asm	mov	si,[source]
+		asm	mov	di,[dest]
+
+		asm	mov	cx,[WORD PTR topline]
+newline3:
+	asm	mov	al,[es:si]
+	asm	mov	[es:di+PAGE1START],al
+	asm	mov	[es:di+PAGE2START],al
+	asm	mov	[es:di+PAGE3START],al
+	asm	mov	al,[es:si+1]
+	asm	mov	[es:di+1+PAGE1START],al
+	asm	mov	[es:di+1+PAGE2START],al
+	asm	mov	[es:di+1+PAGE3START],al
+	asm	mov	al,[es:si+2]
+	asm	mov	[es:di+2+PAGE1START],al
+	asm	mov	[es:di+2+PAGE2START],al
+	asm	mov	[es:di+2+PAGE3START],al
+	asm	mov	al,[es:si+3]
+	asm	mov	[es:di+3+PAGE1START],al
+	asm	mov	[es:di+3+PAGE2START],al
+	asm	mov	[es:di+3+PAGE3START],al
+	asm	mov	al,[es:si+4]
+	asm	mov	[es:di+4+PAGE1START],al
+	asm	mov	[es:di+4+PAGE2START],al
+	asm	mov	[es:di+4+PAGE3START],al
+
+	asm	add	di,SCREENWIDTH
+	asm	add	si,5
+
+		asm	loop	newline3
+	}
+
+	EGAWRITEMODE(0);
+}
+
+
+/*
+=============================================================================
+
+							SHOTS
+
+=============================================================================
+*/
+
+void T_Pshot (objtype *ob);
+
+
+extern	statetype s_pshot1;
+extern	statetype s_pshot2;
+
+extern	statetype s_bigpshot1;
+extern	statetype s_bigpshot2;
+
+
+statetype s_pshot1 = {PSHOT1PIC,8,&T_Pshot,&s_pshot2};
+statetype s_pshot2 = {PSHOT2PIC,8,&T_Pshot,&s_pshot1};
+
+statetype s_shotexplode = {PSHOT2PIC,8,NULL,NULL};
+
+statetype s_bigpshot1 = {BIGPSHOT1PIC,8,&T_Pshot,&s_bigpshot2};
+statetype s_bigpshot2 = {BIGPSHOT2PIC,8,&T_Pshot,&s_bigpshot1};
+
+
+/*
+===================
+=
+= SpawnPShot
+=
+===================
+*/
+
+void SpawnPShot (void)
+{
+	SpawnNewObjFrac (player->x,player->y,&s_pshot1,PIXRADIUS*14);
+	new->obclass = pshotobj;
+	new->speed = SHOTSPEED;
+	new->angle = player->angle;
+}
+
+void SpawnBigPShot (void)
+{
+	SpawnNewObjFrac (player->x,player->y,&s_bigpshot1,24*PIXRADIUS);
+	new->obclass = bigpshotobj;
+	new->speed = SHOTSPEED;
+	new->angle = player->angle;
+}
+
+
+/*
+===============
+=
+= T_Pshot
+=
+===============
+*/
+
+void T_Pshot (objtype *ob)
+{
+	objtype	*check;
+	long	xmove,ymove,speed;
+
+//
+// check current position for monsters having moved into it
+//
+	for (check = player->next; check; check=check->next)
+		if (check->shootable
+		&& ob->xl <= check->xh
+		&& ob->xh >= check->xl
+		&& ob->yl <= check->yh
+		&& ob->yh >= check->yl)
+		{
+			SD_PlaySound (SHOOTMONSTERSND);
+			if (ob->obclass == bigpshotobj)
+				ShootActor (check,BIGSHOTDAMAGE);
+			else
+				ShootActor (check,SHOTDAMAGE);
+			ob->state = &s_shotexplode;
+			ob->ticcount = ob->state->tictime;
+			return;
+		}
+
+
+//
+// move ahead, possibly hitting a wall
+//
+	speed = ob->speed*tics;
+
+	xmove = FixedByFrac(speed,costable[ob->angle]);
+	ymove = -FixedByFrac(speed,sintable[ob->angle]);
+
+	if (ShotClipMove(ob,xmove,ymove))
+	{
+		ob->state = &s_shotexplode;
+		ob->ticcount = ob->state->tictime;
+		return;
+	}
+
+	ob->tilex = ob->x >> TILESHIFT;
+	ob->tiley = ob->y >> TILESHIFT;
+
+//
+// check final position for monsters hit
+//
+	for (check = player->next; check; check=check->next)
+		if (ob->shootable
+		&& ob->xl <= check->xh
+		&& ob->xh >= check->xl
+		&& ob->yl <= check->yh
+		&& ob->yh >= check->yl)
+		{
+			ShootActor (check,SHOTDAMAGE);
+			ob->state = &s_shotexplode;
+			ob->ticcount = ob->state->tictime;
+			return;
+		}
+
+}
+
+
+
+/*
+=============================================================================
+
+						   PLAYER ACTIONS
+
+=============================================================================
+*/
+
+
+/*
+===============
+=
+= BuildShotPower
+=
+===============
+*/
+
+void BuildShotPower (void)
+{
+	int		newlines,topline;
+	long	i;
+	unsigned	source,dest;
+
+	if (gamestate.shotpower == MAXSHOTPOWER)
+		return;
+
+	newlines = 0;
+	for (i=lasttimecount-tics;i<lasttimecount;i++)
+		newlines += (i&1);
+
+	gamestate.shotpower += newlines;
+
+	if (gamestate.shotpower > MAXSHOTPOWER)
+	{
+		newlines -= (gamestate.shotpower - MAXSHOTPOWER);
+		gamestate.shotpower = MAXSHOTPOWER;
+	}
+
+	topline = MAXSHOTPOWER - gamestate.shotpower;
+
+	source = latchpics[L_SHOTBAR]+topline*SIDEBARWIDTH;
+	dest = (POWERLINE+topline)*SCREENWIDTH+34;
+
+	asm	mov	es,[screenseg]
+	asm	mov	si,[source]
+	asm	mov	di,[dest]
+
+	EGAWRITEMODE(1);
+
+	if (newlines)
+	{
+		asm	mov	cx,[newlines]
+newline:
+		asm	mov	al,[es:si]
+		asm	mov	[es:di+PAGE1START],al
+		asm	mov	[es:di+PAGE2START],al
+		asm	mov	[es:di+PAGE3START],al
+		asm	mov	al,[es:si+1]
+		asm	mov	[es:di+1+PAGE1START],al
+		asm	mov	[es:di+1+PAGE2START],al
+		asm	mov	[es:di+1+PAGE3START],al
+		asm	mov	al,[es:si+2]
+		asm	mov	[es:di+2+PAGE1START],al
+		asm	mov	[es:di+2+PAGE2START],al
+		asm	mov	[es:di+2+PAGE3START],al
+		asm	mov	al,[es:si+3]
+		asm	mov	[es:di+3+PAGE1START],al
+		asm	mov	[es:di+3+PAGE2START],al
+		asm	mov	[es:di+3+PAGE3START],al
+		asm	mov	al,[es:si+4]
+		asm	mov	[es:di+4+PAGE1START],al
+		asm	mov	[es:di+4+PAGE2START],al
+		asm	mov	[es:di+4+PAGE3START],al
+
+		asm	add	di,SCREENWIDTH
+		asm	add	si,5
+
+		asm	loop	newline
+	}
+
+	EGAWRITEMODE(0);
+}
+
+
+//===========================================================================
+
+/*
+===============
+=
+= ClearShotPower
+=
+===============
+*/
+
+void ClearShotPower (void)
+{
+	unsigned	source,dest,topline;
+
+	topline = MAXSHOTPOWER - gamestate.shotpower;
+
+	source = latchpics[L_NOSHOT]+topline*SIDEBARWIDTH;
+	dest = (POWERLINE+topline)*SCREENWIDTH+34;
+
+	asm	mov	es,[screenseg]
+	asm	mov	si,[source]
+	asm	mov	di,[dest]
+
+	if (!gamestate.shotpower)
+		return;
+
+	EGAWRITEMODE(1);
+
+	asm	mov	cx,[WORD PTR gamestate.shotpower]
+newline:
+	asm	mov	al,[es:si]
+	asm	mov	[es:di+PAGE1START],al
+	asm	mov	[es:di+PAGE2START],al
+	asm	mov	[es:di+PAGE3START],al
+	asm	mov	al,[es:si+1]
+	asm	mov	[es:di+1+PAGE1START],al
+	asm	mov	[es:di+1+PAGE2START],al
+	asm	mov	[es:di+1+PAGE3START],al
+	asm	mov	al,[es:si+2]
+	asm	mov	[es:di+2+PAGE1START],al
+	asm	mov	[es:di+2+PAGE2START],al
+	asm	mov	[es:di+2+PAGE3START],al
+	asm	mov	al,[es:si+3]
+	asm	mov	[es:di+3+PAGE1START],al
+	asm	mov	[es:di+3+PAGE2START],al
+	asm	mov	[es:di+3+PAGE3START],al
+	asm	mov	al,[es:si+4]
+	asm	mov	[es:di+4+PAGE1START],al
+	asm	mov	[es:di+4+PAGE2START],al
+	asm	mov	[es:di+4+PAGE3START],al
+
+	asm	add	di,SCREENWIDTH
+	asm	add	si,5
+
+	asm	loop	newline
+
+	EGAWRITEMODE(0);
+
+	gamestate.shotpower = 0;
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= Shoot
+=
+===============
+*/
+
+void Shoot (void)
+{
+	ClearShotPower ();
+	SD_PlaySound (SHOOTSND);
+	SpawnPShot ();
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= BigShoot
+=
+===============
+*/
+
+void BigShoot (void)
+{
+	ClearShotPower ();
+	SD_PlaySound (BIGSHOOTSND);
+	SpawnBigPShot ();
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= CastBolt
+=
+===============
+*/
+
+void CastBolt (void)
+{
+	if (!gamestate.bolts)
+	{
+		SD_PlaySound (NOITEMSND);
+		return;
+	}
+
+	TakeBolt ();
+	boltsleft = NUMBOLTS;
+	bolttimer = BOLTTICS;
+	BigShoot ();
+}
+
+
+/*
+===============
+=
+= ContinueBolt
+=
+===============
+*/
+
+void ContinueBolt (void)
+{
+	bolttimer-=tics;
+	if (bolttimer<0)
+	{
+		boltsleft--;
+		bolttimer = BOLTTICS;
+		BigShoot ();
+	}
+}
+
+
+//===========================================================================
+
+/*
+===============
+=
+= CastNuke
+=
+===============
+*/
+
+void CastNuke (void)
+{
+	int	angle;
+
+	if (!gamestate.nukes)
+	{
+		SD_PlaySound (NOITEMSND);
+		return;
+	}
+
+	TakeNuke ();
+	lastnuke = TimeCount;
+
+	for (angle = 0; angle < ANGLES; angle+= ANGLES/16)
+	{
+		SpawnNewObjFrac (player->x,player->y,&s_bigpshot1,24*PIXRADIUS);
+		new->obclass = bigpshotobj;
+		new->speed = SHOTSPEED;
+		new->angle = angle;
+	}
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= DrinkPotion
+=
+===============
+*/
+
+void DrinkPotion (void)
+{
+	unsigned	source,dest,topline;
+
+	if (!gamestate.potions)
+	{
+		SD_PlaySound (NOITEMSND);
+		return;
+	}
+
+	TakePotion ();
+	gamestate.body = MAXBODY;
+
+//
+// draw a full up bar
+//
+	source = latchpics[L_BODYBAR];
+	dest = BODYLINE*SCREENWIDTH+34;
+
+	asm	mov	es,[screenseg]
+	asm	mov	si,[source]
+	asm	mov	di,[dest]
+
+	EGAWRITEMODE(1);
+
+	asm	mov	cx,MAXBODY
+newline:
+	asm	mov	al,[es:si]
+	asm	mov	[es:di+PAGE1START],al
+	asm	mov	[es:di+PAGE2START],al
+	asm	mov	[es:di+PAGE3START],al
+	asm	mov	al,[es:si+1]
+	asm	mov	[es:di+1+PAGE1START],al
+	asm	mov	[es:di+1+PAGE2START],al
+	asm	mov	[es:di+1+PAGE3START],al
+	asm	mov	al,[es:si+2]
+	asm	mov	[es:di+2+PAGE1START],al
+	asm	mov	[es:di+2+PAGE2START],al
+	asm	mov	[es:di+2+PAGE3START],al
+	asm	mov	al,[es:si+3]
+	asm	mov	[es:di+3+PAGE1START],al
+	asm	mov	[es:di+3+PAGE2START],al
+	asm	mov	[es:di+3+PAGE3START],al
+	asm	mov	al,[es:si+4]
+	asm	mov	[es:di+4+PAGE1START],al
+	asm	mov	[es:di+4+PAGE2START],al
+	asm	mov	[es:di+4+PAGE3START],al
+	asm	add	di,SCREENWIDTH
+	asm	add	si,5
+
+	asm	loop	newline
+
+	EGAWRITEMODE(0);
+}
+
+
+
+//===========================================================================
+
+/*
+===============
+=
+= ReadScroll
+=
+===============
+*/
+
+extern	boolean	tileneeded[NUMFLOORS];
+
+void ReadScroll (int scroll)
+{
+	int	i;
+
+//
+// make wall pictures purgable
+//
+	for (i=0;i<NUMSCALEWALLS;i++)
+		if (walldirectory[i])
+			MM_SetPurge (&(memptr)walldirectory[i],3);
+
+	CA_CacheGrChunk (SCROLLTOPPIC);
+	CA_CacheGrChunk (SCROLL1PIC + scroll);
+	VW_DrawPic (0,0,SCROLLTOPPIC);
+	VW_DrawPic (0,32,SCROLL1PIC + scroll);
+	UNMARKGRCHUNK(SCROLL1PIC + scroll);
+	UNMARKGRCHUNK(SCROLLTOPPIC);
+	MM_FreePtr (&grsegs[SCROLL1PIC + scroll]);
+	MM_FreePtr (&grsegs[SCROLLTOPPIC]);
+
+//
+// cache wall pictures back in
+//
+	for (i=1;i<NUMFLOORS;i++)
+		if (tileneeded[i])
+		{
+			SetupScaleWall (walllight1[i]);
+			SetupScaleWall (walllight2[i]);
+			SetupScaleWall (walldark1[i]);
+			SetupScaleWall (walldark2[i]);
+		}
+
+	VW_WaitVBL(80);
+waitkey:
+	IN_ClearKeysDown ();
+	IN_Ack();
+
+}
+
+
+
+/*
+===============
+=
+= TakeDamage
+=
+===============
+*/
+
+void TakeDamage (int points)
+{
+	unsigned	source,dest,topline;
+
+	if (!gamestate.body || bordertime || godmode)
+		return;
+
+	if (points >= gamestate.body)
+	{
+		points = gamestate.body;
+		playstate = ex_died;
+	}
+
+	bordertime = points*FLASHTICS;
+	VW_ColorBorder (FLASHCOLOR);
+
+	if (gamestate.body<MAXBODY/3)
+		SD_PlaySound (TAKEDMGHURTSND);
+	else
+		SD_PlaySound (TAKEDAMAGESND);
+
+	gamestate.body -= points;
+//
+// shrink the body bar
+//
+	source = latchpics[L_NOBODY]+gamestate.body*SIDEBARWIDTH;
+	dest = (BODYLINE+gamestate.body)*SCREENWIDTH+34;
+
+
+	asm	mov	es,[screenseg]
+	asm	mov	si,[source]
+	asm	mov	di,[dest]
+
+	EGAWRITEMODE(1);
+
+	asm	mov	cx,[points]
+newline:
+	asm	mov	al,[es:si]
+	asm	mov	[es:di+PAGE1START],al
+	asm	mov	[es:di+PAGE2START],al
+	asm	mov	[es:di+PAGE3START],al
+	asm	mov	al,[es:si+1]
+	asm	mov	[es:di+1+PAGE1START],al
+	asm	mov	[es:di+1+PAGE2START],al
+	asm	mov	[es:di+1+PAGE3START],al
+	asm	mov	al,[es:si+2]
+	asm	mov	[es:di+2+PAGE1START],al
+	asm	mov	[es:di+2+PAGE2START],al
+	asm	mov	[es:di+2+PAGE3START],al
+	asm	mov	al,[es:si+3]
+	asm	mov	[es:di+3+PAGE1START],al
+	asm	mov	[es:di+3+PAGE2START],al
+	asm	mov	[es:di+3+PAGE3START],al
+	asm	mov	al,[es:si+4]
+	asm	mov	[es:di+4+PAGE1START],al
+	asm	mov	[es:di+4+PAGE2START],al
+	asm	mov	[es:di+4+PAGE3START],al
+
+	asm	add	di,SCREENWIDTH
+	asm	add	si,5
+
+	asm	loop	newline
+
+	EGAWRITEMODE(0);
+
+}
+
+
+
+/*
+=============================================================================
+
+							INTERACTION
+
+=============================================================================
+*/
+
+
+/*
+==================
+=
+= OpenDoor
+=
+==================
+*/
+
+void OpenDoor (unsigned bx, unsigned by, unsigned doorbase)
+{
+	int x,y;
+	unsigned	far *map;
+
+	x=bx;
+	y=by;
+	map = mapsegs[0]+farmapylookup[y]+x;
+	while (tilemap[x][y]-doorbase<4)
+	{
+		tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;
+		map--;
+		x--;
+	}
+	x=bx+1;
+	map = mapsegs[0]+farmapylookup[y]+x;
+	while (tilemap[x][y]-doorbase<4)
+	{
+		tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;
+		map++;
+		x++;
+	}
+	x=bx;
+	y=by-1;
+	map = mapsegs[0]+farmapylookup[y]+x;
+	while (tilemap[x][y]-doorbase<4)
+	{
+		tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;
+		map-=mapwidth;
+		y--;
+	}
+	y=by+1;
+	map = mapsegs[0]+farmapylookup[y]+x;
+	while (tilemap[x][y]-doorbase<4)
+	{
+		tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;
+		map+=mapwidth;
+		y++;
+	}
+}
+
+
+/*
+==================
+=
+= HitSpecialTile
+=
+= Returns true if the move is blocked
+=
+==================
+*/
+
+boolean HitSpecialTile (unsigned x, unsigned y, unsigned tile)
+{
+	switch (tile)
+	{
+	case 0:
+	case 1:
+	case 2:
+	case 3:
+		if (!gamestate.keys[0])
+			return true;
+		TakeKey(0);
+		OpenDoor (x,y,SPECTILESTART+0);
+		return false;
+
+	case 4:
+	case 5:
+	case 6:
+	case 7:
+		if (!gamestate.keys[1])
+			return true;
+		TakeKey(1);
+		OpenDoor (x,y,SPECTILESTART+4);
+		return false;
+
+	case 8:
+	case 9:
+	case 10:
+	case 11:
+		if (!gamestate.keys[2])
+			return true;
+		TakeKey(2);
+		OpenDoor (x,y,SPECTILESTART+8);
+		return false;
+
+	case 12:
+	case 13:
+	case 14:
+	case 15:
+		if (!gamestate.keys[3])
+			return true;
+		TakeKey(3);
+		OpenDoor (x,y,SPECTILESTART+12);
+		return false;
+
+	}
+
+	return true;
+}
+
+
+
+/*
+==================
+=
+= TouchActor
+=
+= Returns true if the move is blocked
+=
+==================
+*/
+
+boolean TouchActor (objtype *ob, objtype *check)
+{
+	if (ob->xh < check->xl || ob->xl > check->xh ||
+		ob->yh < check->yl || ob->yl > check->yh)
+		return false;				// not quite touching
+
+	switch (check->obclass)
+	{
+	case bonusobj:
+		if (check->temp1 == B_BOLT)
+			GiveBolt ();
+		else if (check->temp1 == B_NUKE)
+			GiveNuke ();
+		else if (check->temp1 == B_POTION)
+			GivePotion ();
+		else if (check->temp1 >= B_RKEY && check->temp1 <= B_BKEY)
+			GiveKey (check->temp1-B_RKEY);
+		else if (check->temp1 >= B_SCROLL1 && check->temp1 <= B_SCROLL8)
+			GiveScroll (check->temp1-B_SCROLL1,true);
+		else if (check->temp1 == B_CHEST)
+			GiveChest ();
+		else if (check->temp1 == B_GOAL)
+			GiveGoal ();
+		(unsigned)actorat[check->tilex][check->tiley] = 0;
+		RemoveObj (check);
+
+		return false;
+
+	}
+	return	true;
+}
+
+
+/*
+==================
+=
+= CalcBounds
+=
+==================
+*/
+
+void CalcBounds (objtype *ob)
+{
+//
+// calculate hit rect
+//
+  ob->xl = ob->x - ob->size;
+  ob->xh = ob->x + ob->size;
+  ob->yl = ob->y - ob->size;
+  ob->yh = ob->y + ob->size;
+}
+
+
+/*
+===================
+=
+= LocationInActor
+=
+===================
+*/
+
+boolean LocationInActor (objtype *ob)
+{
+	int	x,y,xmin,ymin,xmax,ymax;
+	objtype *check;
+
+	CalcBounds (ob);
+
+	xmin = (ob->x >> TILESHIFT)-2;
+	ymin = (ob->y >> TILESHIFT)-2;
+	xmax = xmin+5;
+	ymax = ymin+5;
+
+	for (x=xmin;x<xmax;x++)
+		for (y=ymin;y<ymax;y++)
+		{
+			check = actorat[x][y];
+			if (check>(objtype *)LASTSPECIALTILE
+			&& check->shootable
+			&& ob->xl <= check->xh
+			&& ob->xh >= check->xl
+			&& ob->yl <= check->yh
+			&& ob->yh >= check->yl)
+				return true;
+		}
+
+	return false;
+}
+
+
+/*
+===================
+=
+= ClipMove
+=
+= Only checks corners, so the object better be less than one tile wide!
+=
+===================
+*/
+
+void ClipMove (objtype *ob, long xmove, long ymove)
+{
+	int			xl,yl,xh,yh,tx,ty,nt1,nt2,x,y;
+	long		intersect,basex,basey,pointx,pointy;
+	unsigned	inside,total,tile;
+	objtype		*check;
+	boolean		moveok;
+
+//
+// move player and check to see if any corners are in solid tiles
+//
+	basex = ob->x;
+	basey = ob->y;
+
+	ob->x += xmove;
+	ob->y += ymove;
+
+	CalcBounds (ob);
+
+	xl = ob->xl>>TILESHIFT;
+	yl = ob->yl>>TILESHIFT;
+
+	xh = ob->xh>>TILESHIFT;
+	yh = ob->yh>>TILESHIFT;
+
+	for (y=yl;y<=yh;y++)
+		for (x=xl;x<=xh;x++)
+		{
+			check = actorat[x][y];
+			if (!check)
+				continue;		// blank floor, walk ok
+
+			if ((unsigned)check<=LASTWALLTILE)
+				goto blockmove;	// solid wall
+
+			if ((unsigned)check<=LASTSPECIALTILE)
+			{
+				if ( HitSpecialTile (x,y,(unsigned)check-SPECTILESTART) )
+					goto blockmove;		// whatever it was, it blocked the move
+				else
+					continue;
+			}
+			TouchActor(ob,check);		// pick up items
+		}
+
+//
+// check nearby actors
+//
+	if (LocationInActor(ob))
+	{
+		ob->x -= xmove;
+		if (LocationInActor(ob))
+		{
+			ob->x += xmove;
+			ob->y -= ymove;
+			if (LocationInActor(ob))
+				ob->x -= xmove;
+		}
+	}
+	return;		// move is OK!
+
+
+blockmove:
+
+	if (!SD_SoundPlaying())
+		SD_PlaySound (HITWALLSND);
+
+	moveok = false;
+
+	do
+	{
+		xmove /= 2;
+		ymove /= 2;
+		if (moveok)
+		{
+			ob->x += xmove;
+			ob->y += ymove;
+		}
+		else
+		{
+			ob->x -= xmove;
+			ob->y -= ymove;
+		}
+		CalcBounds (ob);
+		xl = ob->xl>>TILESHIFT;
+		yl = ob->yl>>TILESHIFT;
+		xh = ob->xh>>TILESHIFT;
+		yh = ob->yh>>TILESHIFT;
+		if (tilemap[xl][yl] || tilemap[xh][yl]
+		|| tilemap[xh][yh] || tilemap[xl][yh] )
+		{
+			moveok = false;
+			if (xmove>=-2048 && xmove <=2048 && ymove>=-2048 && ymove <=2048)
+			{
+				ob->x = basex;
+				ob->y = basey;
+				return;
+			}
+		}
+		else
+		{
+			if (xmove>=-2048 && xmove <=2048 && ymove>=-2048 && ymove <=2048)
+				return;
+			moveok = true;
+		}
+	} while (1);
+}
+
+
+//==========================================================================
+
+
+/*
+===================
+=
+= ShotClipMove
+=
+= Only checks corners, so the object better be less than one tile wide!
+=
+===================
+*/
+
+boolean ShotClipMove (objtype *ob, long xmove, long ymove)
+{
+	int			xl,yl,xh,yh,tx,ty,nt1,nt2,x,y;
+	long		intersect,basex,basey,pointx,pointy;
+	unsigned	inside,total,tile;
+	objtype		*check;
+	boolean		moveok;
+
+//
+// move shot and check to see if any corners are in solid tiles
+//
+	basex = ob->x;
+	basey = ob->y;
+
+	ob->x += xmove;
+	ob->y += ymove;
+
+	CalcBounds (ob);
+
+	xl = ob->xl>>TILESHIFT;
+	yl = ob->yl>>TILESHIFT;
+
+	xh = ob->xh>>TILESHIFT;
+	yh = ob->yh>>TILESHIFT;
+
+	for (y=yl;y<=yh;y++)
+		for (x=xl;x<=xh;x++)
+		{
+			tile = tilemap[x][y];
+			if (tile)
+			{
+				if ((unsigned)(tile-EXPWALLSTART)<NUMEXPWALLS)
+					ExplodeWall (x,y);
+				goto blockmove;
+			}
+		}
+	return false;		// move is OK!
+
+
+blockmove:
+
+	SD_PlaySound (SHOOTWALLSND);
+
+	moveok = false;
+
+	do
+	{
+		xmove /= 2;
+		ymove /= 2;
+		if (moveok)
+		{
+			ob->x += xmove;
+			ob->y += ymove;
+		}
+		else
+		{
+			ob->x -= xmove;
+			ob->y -= ymove;
+		}
+		CalcBounds (ob);
+		xl = ob->xl>>TILESHIFT;
+		yl = ob->yl>>TILESHIFT;
+		xh = ob->xh>>TILESHIFT;
+		yh = ob->yh>>TILESHIFT;
+		if (tilemap[xl][yl] || tilemap[xh][yl]
+		|| tilemap[xh][yh] || tilemap[xl][yh] )
+		{
+			moveok = false;
+			if (xmove>=-2048 && xmove <=2048 && ymove>=-2048 && ymove <=2048)
+			{
+				ob->x = basex;
+				ob->y = basey;
+				return true;
+			}
+		}
+		else
+		{
+			if (xmove>=-2048 && xmove <=2048 && ymove>=-2048 && ymove <=2048)
+				return true;
+			moveok = true;
+		}
+	} while (1);
+}
+
+
+
+/*
+=============================================================================
+
+						   PLAYER CONTROL
+
+=============================================================================
+*/
+
+
+
+void	T_Player (objtype *ob);
+
+statetype s_player = {0,0,&T_Player,&s_player};
+
+/*
+===============
+=
+= SpawnPlayer
+=
+===============
+*/
+
+void SpawnPlayer (int tilex, int tiley, int dir)
+{
+	player->obclass = playerobj;
+	player->active = true;
+	player->tilex = tilex;
+	player->tiley = tiley;
+	player->x = ((long)tilex<<TILESHIFT)+TILEGLOBAL/2;
+	player->y = ((long)tiley<<TILESHIFT)+TILEGLOBAL/2;
+	player->state = &s_player;
+	player->angle = (1-dir)*90;
+	player->size = MINDIST;
+	CalcBounds (player);
+	if (player->angle<0)
+		player->angle += ANGLES;
+}
+
+
+/*
+===================
+=
+= Thrust
+=
+===================
+*/
+
+void Thrust (int angle, unsigned speed)
+{
+	long xmove,ymove;
+
+	if (lasttimecount>>5 != ((lasttimecount-tics)>>5) )
+	{
+	//
+	// walk sound
+	//
+		if (lasttimecount&32)
+			SD_PlaySound (WALK1SND);
+		else
+			SD_PlaySound (WALK2SND);
+	}
+
+	xmove = FixedByFrac(speed,costable[angle]);
+	ymove = -FixedByFrac(speed,sintable[angle]);
+
+	ClipMove(player,xmove,ymove);
+	player->tilex = player->x >> TILESHIFT;
+	player->tiley = player->y >> TILESHIFT;
+}
+
+
+
+/*
+=======================
+=
+= ControlMovement
+=
+=======================
+*/
+
+void ControlMovement (objtype *ob)
+{
+	int	angle;
+	long	speed;
+
+
+	if (c.button1)
+	{
+	//
+	// strafing
+	//
+		//
+		// side to side move
+		//
+		if (!mousexmove)
+			speed = 0;
+		else if (mousexmove<0)
+			speed = -(long)mousexmove*300;
+		else
+			speed = -(long)mousexmove*300;
+
+		if (c.xaxis == -1)
+		{
+			if (running)
+				speed += RUNSPEED*tics;
+			else
+				speed += PLAYERSPEED*tics;
+		}
+		else if (c.xaxis == 1)
+		{
+			if (running)
+				speed -= RUNSPEED*tics;
+			else
+				speed -= PLAYERSPEED*tics;
+		}
+
+		if (speed > 0)
+		{
+			if (speed >= TILEGLOBAL)
+				speed = TILEGLOBAL-1;
+			angle = ob->angle + ANGLES/4;
+			if (angle >= ANGLES)
+				angle -= ANGLES;
+			Thrust (angle,speed);				// move to left
+		}
+		else if (speed < 0)
+		{
+			if (speed <= -TILEGLOBAL)
+				speed = -TILEGLOBAL+1;
+			angle = ob->angle - ANGLES/4;
+			if (angle < 0)
+				angle += ANGLES;
+			Thrust (angle,-speed);				// move to right
+		}
+	}
+	else
+	{
+	//
+	// not strafing
+	//
+
+		//
+		// turning
+		//
+		if (c.xaxis == 1)
+		{
+			ob->angle -= tics;
+			if (running)				// fast turn
+				ob->angle -= tics;
+		}
+		else if (c.xaxis == -1)
+		{
+			ob->angle+= tics;
+			if (running)				// fast turn
+				ob->angle += tics;
+		}
+
+		ob->angle -= (mousexmove/10);
+
+		if (ob->angle >= ANGLES)
+			ob->angle -= ANGLES;
+		if (ob->angle < 0)
+			ob->angle += ANGLES;
+
+	}
+
+	//
+	// forward/backwards move
+	//
+	if (!mouseymove)
+		speed = 0;
+	else if (mouseymove<0)
+		speed = -(long)mouseymove*500;
+	else
+		speed = -(long)mouseymove*200;
+
+	if (c.yaxis == -1)
+	{
+		if (running)
+			speed += RUNSPEED*tics;
+		else
+			speed += PLAYERSPEED*tics;
+	}
+	else if (c.yaxis == 1)
+	{
+		if (running)
+			speed -= RUNSPEED*tics;
+		else
+			speed -= PLAYERSPEED*tics;
+	}
+
+	if (speed > 0)
+	{
+		if (speed >= TILEGLOBAL)
+			speed = TILEGLOBAL-1;
+		Thrust (ob->angle,speed);			// move forwards
+	}
+	else if (speed < 0)
+	{
+		if (speed <= -TILEGLOBAL)
+			speed = -TILEGLOBAL+1;
+		angle = ob->angle + ANGLES/2;
+		if (angle >= ANGLES)
+			angle -= ANGLES;
+		Thrust (angle,-speed);				// move backwards
+	}
+}
+
+
+/*
+===============
+=
+= T_Player
+=
+===============
+*/
+
+void	T_Player (objtype *ob)
+{
+	int	angle,speed,scroll;
+	unsigned	text,tilex,tiley;
+	long	lspeed;
+
+
+	ControlMovement (ob);
+
+
+	//
+	// firing
+	//
+	if (boltsleft)
+	{
+		handheight+=(tics<<2);
+		if (handheight>MAXHANDHEIGHT)
+			handheight = MAXHANDHEIGHT;
+
+		ContinueBolt ();
+		lasthand = lasttimecount;
+	}
+	else
+	{
+		if (c.button0)
+		{
+			handheight+=(tics<<2);
+			if (handheight>MAXHANDHEIGHT)
+				handheight = MAXHANDHEIGHT;
+
+			if ((unsigned)TimeCount/FIRETIME != lastfiretime)
+				BuildShotPower ();
+			lasthand = lasttimecount;
+		}
+		else
+		{
+			if (lasttimecount > lasthand+HANDPAUSE)
+			{
+				handheight-=(tics<<1);
+				if (handheight<0)
+					handheight = 0;
+			}
+
+			if (gamestate.shotpower == MAXSHOTPOWER)
+			{
+				lastfiretime = (unsigned)TimeCount/FIRETIME;
+				BigShoot ();
+			}
+			else if (gamestate.shotpower)
+			{
+				lastfiretime = (unsigned)TimeCount/FIRETIME;
+				Shoot ();
+			}
+		}
+	}
+
+	//
+	// special actions
+	//
+
+	if ( (Keyboard[sc_Space] || Keyboard[sc_H]) && gamestate.body != MAXBODY)
+		DrinkPotion ();
+
+	if (Keyboard[sc_B] && !boltsleft)
+		CastBolt ();
+
+	if ( (Keyboard[sc_Enter] || Keyboard[sc_N]) && TimeCount-lastnuke > NUKETIME)
+		CastNuke ();
+
+	scroll = LastScan-2;
+	if ( scroll>=0 && scroll<NUMSCROLLS && gamestate.scrolls[scroll])
+		ReadScroll (scroll);
+
+	DrawText ();
+	DrawCompass ();
+
+}
diff --git a/src/lib/hb/c6_act1.c b/src/lib/hb/c6_act1.c
new file mode 100755
index 00000000..f94a8787
--- /dev/null
+++ b/src/lib/hb/c6_act1.c
@@ -0,0 +1,930 @@
+/* Catacomb Apocalypse Source Code
+ * Copyright (C) 1993-2014 Flat Rock Software
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// C3_PLAY.C
+
+#include "DEF.H"
+#pragma hdrstop
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+#if 0
+#define MSHOTDAMAGE	2
+#define MSHOTSPEED	10000
+
+#define ESHOTDAMAGE	1
+#define ESHOTSPEED	5000
+
+#define SSHOTDAMAGE	3
+#define SSHOTSPEED	6500
+
+#define RANDOM_ATTACK 20
+#endif
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+boolean ShootPlayer (objtype *ob, short obclass, short speed, statetype *state);
+void T_ShootPlayer(objtype *ob);
+
+short zombie_base_delay;
+
+short other_x[] = {0,39,39,0},
+		other_y[] = {0,0,27,27};
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+
+dirtype dirtable[9] = {northwest,north,northeast,west,nodir,east,
+	southwest,south,southeast};
+
+
+/*
+=============================================================================
+
+						  BONUS ITEMS
+
+=============================================================================
+*/
+
+statetype s_boltbonus = {BOLTOBJPIC,8,NULL,&s_boltbonus2};
+statetype s_boltbonus2 = {BOLT2OBJPIC,8,NULL,&s_boltbonus3};
+statetype s_boltbonus3 = {BOLT3OBJPIC,8,NULL,&s_boltbonus};
+
+statetype s_nukebonus = {NUKEOBJPIC,8,NULL,&s_nukebonus2};
+statetype s_nukebonus2 = {NUKE2OBJPIC,8,NULL,&s_nukebonus3};
+statetype s_nukebonus3 = {NUKE3OBJPIC,8,NULL,&s_nukebonus};
+
+statetype s_potionbonus = {POTIONOBJPIC,0,NULL,&s_potionbonus};
+statetype s_rkeybonus = {RKEYOBJPIC,0,NULL,&s_rkeybonus};
+statetype s_ykeybonus = {YKEYOBJPIC,0,NULL,&s_ykeybonus};
+statetype s_gkeybonus = {GKEYOBJPIC,0,NULL,&s_gkeybonus};
+statetype s_bkeybonus = {BKEYOBJPIC,0,NULL,&s_bkeybonus};
+statetype s_chestbonus = {CHESTOBJPIC,0,NULL,&s_chestbonus};
+statetype s_oldchestbonus = {OLD_CHESTPIC,0,NULL,&s_oldchestbonus};
+
+statetype s_waterchestbonus1 = {O_WATER_CHEST1PIC, 10, NULL, &s_waterchestbonus2};
+statetype s_waterchestbonus2 = {O_WATER_CHEST2PIC, 10, NULL, &s_waterchestbonus3};
+statetype s_waterchestbonus3 = {O_WATER_CHEST3PIC, 10, NULL, &s_waterchestbonus1};
+
+statetype s_rgem1bonus = {RGEM1PIC,30,NULL,&s_rgem1bonus};
+statetype s_ygem1bonus = {YGEM1PIC,30,NULL,&s_ygem1bonus};
+statetype s_ggem1bonus = {GGEM1PIC,30,NULL,&s_ggem1bonus};
+statetype s_bgem1bonus = {BGEM1PIC,30,NULL,&s_bgem1bonus};
+statetype s_pgem1bonus = {PGEM1PIC,30,NULL,&s_pgem1bonus};
+
+statetype s_bonus_die = {0,8,NULL,NULL};
+
+/*
+===============
+=
+= SpawnBonus
+=
+===============
+*/
+
+void SpawnBonus (int tilex, int tiley, int number)
+{
+	extern unsigned gcolor;
+
+	statetype *state;
+
+	switch (number)
+	{
+		case B_BOLT:			state = &s_boltbonus;		break;
+		case B_NUKE:			state = &s_nukebonus;		break;
+		case B_POTION:			state = &s_potionbonus;		break;
+
+		case B_RKEY:			state = &s_rkeybonus;		break;
+		case B_YKEY:			state = &s_ykeybonus;		break;
+		case B_GKEY:			state = &s_gkeybonus;		break;
+		case B_BKEY:			state = &s_bkeybonus;		break;
+
+		case B_RGEM:			state = &s_rgem1bonus;		break;
+		case B_YGEM:			state = &s_ygem1bonus;		break;
+		case B_GGEM:			state = &s_ggem1bonus;		break;
+		case B_BGEM:			state = &s_bgem1bonus;		break;
+		case B_PGEM:			state = &s_pgem1bonus;		break;
+
+		case B_CHEST:
+			if (gcolor == 0x0101)
+				state = &s_waterchestbonus1;
+			else
+				state = &s_chestbonus;
+		break;
+
+		case B_OLDCHEST:		state = &s_oldchestbonus;	break;
+
+
+		default:
+			Quit("SpawnBonus(): INVALID BONUS");
+		break;
+	}
+
+	SpawnNewObj (tilex,tiley,state,TILEGLOBAL/2);
+	new->temp1 = number;
+	new->obclass = bonusobj;
+
+	switch (number)
+	{
+		case B_POTION:
+		case B_OLDCHEST:
+		case B_CHEST:
+		case B_BOLT:
+		case B_NUKE:
+			new->flags |= of_shootable;
+		break;
+
+		default:
+			new->flags &= ~of_shootable;
+		break;
+	}
+}
+
+
+
+/*
+============================================================================
+
+									FREEZE TIME OBJECT
+
+============================================================================
+*/
+
+extern statetype s_ftimebonus;
+extern statetype s_ftimebonus2;
+
+statetype s_ftimebonus = {TIMEOBJ1PIC,6,NULL,&s_ftimebonus2};
+statetype s_ftimebonus2 = {TIMEOBJ2PIC,6,NULL,&s_ftimebonus};
+
+/*
+===============
+=
+= SpawnFTime
+=
+===============
+*/
+void SpawnFTime(int tilex, int tiley)
+{
+	SpawnNewObj(tilex,tiley,&s_ftimebonus,TILEGLOBAL/2);
+//	new->tileobject = true;
+	new->obclass = freezeobj;
+	new->flags |= of_shootable;
+}
+
+/*
+=============================================================================
+
+					  EXPLODING WALL
+
+=============================================================================
+*/
+
+
+void T_WallDie (objtype *ob);
+
+extern	statetype s_walldie1;
+extern	statetype s_walldie2;
+extern	statetype s_walldie3;
+extern	statetype s_walldie4;
+extern	statetype s_walldie5;
+extern	statetype s_walldie6;
+
+statetype s_walldie1 = {0,20,NULL,&s_walldie2};
+statetype s_walldie2 = {0,-1,T_WallDie,&s_walldie3};
+statetype s_walldie3 = {0,20,NULL,&s_walldie4};
+statetype s_walldie4 = {0,-1,T_WallDie,&s_walldie5};
+statetype s_walldie5 = {0,20,NULL,&s_walldie6};
+statetype s_walldie6 = {0,-1,T_WallDie,NULL};
+
+
+/*
+================
+=
+= ExplodeWall
+=
+================
+*/
+
+void ExplodeWall (int tilex, int tiley)
+{
+	extern unsigned gcolor;
+	unsigned tilenum;
+
+	DSpawnNewObj (tilex,tiley,&s_walldie1,0);
+	if (new == &dummyobj)
+		return;
+	new->obclass = inertobj;
+	new->active = always;
+	if (gcolor == 0x0101)
+		tilenum = WATEREXP;
+	else
+		tilenum = WALLEXP;
+	(unsigned)actorat[new->tilex][new->tiley] = tilemap[new->tilex][new->tiley] =
+		*(mapsegs[0]+farmapylookup[new->tiley]+new->tilex) = tilenum;
+	*(mapsegs[2]+farmapylookup[new->tiley]+new->tilex) &= 0xFF;
+}
+
+
+/*
+================
+=
+= T_WallDie
+=
+================
+*/
+
+void T_WallDie (objtype *ob)
+{
+	extern unsigned gcolor;
+	unsigned tile,other,spot,x,y;
+
+	if (++ob->temp1 == 3)
+		tile = 0;
+	else
+		if (gcolor == 0x0101)
+			tile = WATEREXP-1 + ob->temp1;
+		else
+			tile = WALLEXP-1 + ob->temp1;
+	x = ob->tilex;
+	y = ob->tiley;
+
+	(unsigned)actorat[x][y] = tilemap[x][y] = *(mapsegs[0]+farmapylookup[y]+x) = tile;
+
+	if (ob->temp1 == 1)
+	{
+	//
+	// blow up nearby walls
+	//
+		spot = (*(mapsegs[2]+farmapylookup[y]+(x-1))) >> 8;
+		if (spot == EXP_WALL_CODE)
+			ExplodeWall (x-1,y);
+		spot = (*(mapsegs[2]+farmapylookup[y]+(x+1))) >> 8;
+		if (spot == EXP_WALL_CODE)
+			ExplodeWall (x+1,y);
+		spot = (*(mapsegs[2]+farmapylookup[y-1]+x)) >> 8;
+		if (spot == EXP_WALL_CODE)
+			ExplodeWall (x,y-1);
+		spot = (*(mapsegs[2]+farmapylookup[y+1]+x)) >> 8;
+		if (spot == EXP_WALL_CODE)
+			ExplodeWall (x,y+1);
+	}
+}
+/*
+=============================================================================
+
+								OBJ_WARP GATE
+
+=============================================================================
+*/
+
+void T_Gate (objtype *ob);
+void T_Gate_Wait (objtype *ob);
+
+extern statetype s_portal_wait;
+statetype s_portal_wait = {0, 10, &T_Gate_Wait, &s_portal_wait};
+
+statetype s_portal1 = {PORTAL1PIC, 6, &T_Gate, &s_portal2};
+statetype s_portal2 = {PORTAL2PIC, 6, &T_Gate, &s_portal3};
+statetype s_portal3 = {PORTAL3PIC, 6, &T_Gate, &s_portal4};
+statetype s_portal4 = {PORTAL4PIC, 6, &T_Gate, &s_portal5};
+statetype s_portal5 = {PORTAL5PIC, 6, &T_Gate, &s_portal6};
+statetype s_portal6 = {PORTAL6PIC, 6, &T_Gate, &s_portal1};
+
+//---------------------------------------------------------------------------
+//	SpawnWarp()
+//---------------------------------------------------------------------------
+void SpawnWarp (int tilex, int tiley)
+{
+	unsigned spot;
+	objtype *ob;
+
+	spot = (*(mapsegs[2]+farmapylookup[tiley]+tilex+1)) >> 8;
+
+	if (spot)
+	{
+		SpawnNewObj (tilex, tiley, &s_portal_wait, TILEGLOBAL/3);
+		new->temp1 = spot*70;
+	}
+	else
+		SpawnNewObj (tilex, tiley, &s_portal1, TILEGLOBAL/3);
+
+	new->obclass = gateobj;
+}
+
+/*
+===============
+=
+= T_Gate_Wait
+=
+===============
+*/
+
+void T_Gate_Wait (objtype *ob)
+{
+	if ((ob->temp1 -= tics) <= 0)
+	{
+		if ((ob->tilex == player->tilex) && (ob->tiley == player->tiley))
+			return;
+		if (CheckHandAttack(ob))
+			return;
+
+		SD_PlaySound(PORTALSND);
+		ob->state = &s_portal1;
+		ob->ticcount = ob->state->tictime;
+
+	}
+
+}
+
+
+
+/*
+===============
+=
+= T_Gate
+=
+===============
+*/
+
+void T_Gate (objtype *ob)
+{
+	objtype *check;
+	unsigned	temp,spot;
+
+	if (CheckHandAttack (ob) && !playstate)
+	{
+		//
+		// teleport out of level
+		//
+		playstate = ex_warped;
+		spot = (*(mapsegs[2]+farmapylookup[ob->tiley+1]+ob->tilex)) >> 8;
+		gamestate.mapon=spot;
+		SD_PlaySound(WARPUPSND);
+	}
+}
+
+
+
+/*
+=============================================================================
+
+											AQUAMAN
+
+=============================================================================
+*/
+
+void T_AquaMan(objtype *ob);
+
+statetype s_aqua_under1 = {EYESTALKUNDER1PIC, 25, &T_AquaMan, &s_aqua_under2};
+statetype s_aqua_under2 = {EYESTALKUNDER2PIC, 20, &T_AquaMan, &s_aqua_under3};
+statetype s_aqua_under3 = {EYESTALKUNDER3PIC, 20, &T_AquaMan, &s_aqua_under2};
+
+statetype s_aqua_left = {EYESTALKUNDER4PIC, 40, NULL, &s_aqua_under3};
+statetype s_aqua_right = {EYESTALKUNDER5PIC, 40, NULL, &s_aqua_under3};
+
+statetype s_aqua_rise1 = {EYESTALKRISE1PIC, 20, NULL, &s_aqua_rise2};
+statetype s_aqua_rise2 = {EYESTALKRISE2PIC, 15, NULL, &s_aqua_walk1};
+
+statetype s_aqua_sink1 = {EYESTALKRISE2PIC, 15, NULL, &s_aqua_sink2};
+statetype s_aqua_sink2 = {EYESTALKRISE1PIC, 20, NULL, &s_aqua_under1};
+
+statetype s_aqua_walk1 = {EYESTALKWALK1PIC, 12, &T_AquaMan, &s_aqua_walk2};
+statetype s_aqua_walk2 = {EYESTALKWALK2PIC, 12, &T_AquaMan, &s_aqua_walk1};
+
+statetype s_aqua_attack1 = {EYESTALKATTACKPIC, 10, NULL, &s_aqua_attack2};
+statetype s_aqua_attack2 = {EYESTALKWALK1PIC, 10, &T_DoDamage, &s_aqua_walk1};
+
+statetype s_aqua_die1 = {EYESTALKDEATH1PIC, 8, NULL, &s_aqua_die2};
+statetype s_aqua_die2 = {EYESTALKDEATH2PIC, 8, NULL, &s_aqua_die3};
+statetype s_aqua_die3 = {EYESTALKDEATH2PIC, -1, &T_AlternateStates, &s_aqua_die1};
+statetype s_aqua_die4 = {EYESTALKDEATH2PIC, 30, NULL, &s_aqua_die5};
+statetype s_aqua_die5 = {EYESTALKDEATH3PIC, 40, NULL, &s_aqua_die6};
+statetype s_aqua_die6 = {EYESTALKDEATH4PIC, 30, &ExplosionSnd, &s_aqua_die7};
+statetype s_aqua_die7 = {EYESTALKDEATH5PIC, 20, NULL, NULL};
+
+typedef enum {wt_UNDER, wt_WALK} AquaManTypes;
+
+#define AQ_TIMEREMAIN 	(ob->temp1)
+#define AQ_STAGE			(ob->temp2)
+
+/*
+===============
+=
+= SpawnAquaMan
+=
+===============
+*/
+void SpawnAquaMan(int tilex, int tiley)
+{
+	objtype *ob;
+	SpawnNewObj(tilex,tiley,&s_aqua_under1,PIXRADIUS*32);
+	ob = new;
+
+	AQ_STAGE = wt_UNDER;
+	AQ_TIMEREMAIN = 60*4+random(60*3);
+
+	new->obclass = aquamanobj;
+	new->speed = 1000;
+	new->flags &= ~of_shootable;
+	new->hitpoints = EasyHitPoints(15);
+}
+
+void ExplosionSnd(objtype *ob)
+{
+	if (ob->temp1 != SOUNDPLAYED)
+	{
+		SD_PlaySound(BODY_EXPLODESND);
+		ob->temp1 = SOUNDPLAYED;
+
+	}
+}
+
+
+/*
+===============
+=
+= T_AquaMan
+=
+===============
+*/
+
+void T_AquaMan(objtype *ob)
+{
+	switch (AQ_STAGE)
+	{
+		case wt_UNDER:
+			ob->flags &= ~of_shootable;
+			if (Chase(ob,true))
+			{
+				// RISE & GOTO WALK STAGE
+				//
+
+				AQ_STAGE = wt_WALK;
+				AQ_TIMEREMAIN = 60*5+random(60*5);
+					ob->state = &s_aqua_rise1;
+				ob->speed = 2200;
+				ob->ticcount = ob->state->tictime;
+			}
+			else
+			{
+				// DEC COUNTER - And check for WALK
+				//
+				if ((AQ_TIMEREMAIN-=realtics) < 0)
+				{
+					// RISE & GOTO WALK STAGE
+					//
+
+					if (CheckHandAttack(ob))
+						break;
+
+					AQ_STAGE = wt_WALK;
+					AQ_TIMEREMAIN = 60+random(60*2);
+						ob->state = &s_aqua_rise1;
+					ob->speed = 2200;
+					ob->ticcount = ob->state->tictime;
+				}
+				else
+				if (random(1000)<5)
+				{
+					// RANDOM PEEK UP OUT OF WATER
+					//
+						if (random(2) == 0)
+							ob->state = &s_aqua_left;
+						else
+							ob->state = &s_aqua_right;
+					ob->ticcount = ob->state->tictime;
+				}
+			}
+			break;
+
+
+		case wt_WALK:
+			ob->flags |= of_shootable;
+			if (Chase(ob,true) || (random(1000)<RANDOM_ATTACK))
+			{
+					ob->state = &s_aqua_attack1;
+				ob->ticcount = ob->state->tictime;
+			}
+			else
+			{
+				// DEC COUNTER - And check for SINK
+				//
+				if ((AQ_TIMEREMAIN-=realtics) < 0)
+				{
+					// SINK & GOTO BUBBLE STAGE
+					//
+
+					AQ_STAGE = wt_UNDER;
+					AQ_TIMEREMAIN = 60*4+random(60*3);
+						ob->state = &s_aqua_sink1;
+					ob->speed = 1200;
+					ob->ticcount = ob->state->tictime;
+					ob->flags &= ~of_shootable;
+				}
+
+			}
+			break;
+	}
+}
+
+
+
+
+/*
+=============================================================================
+
+											WIZARD
+
+=============================================================================
+*/
+
+void T_Wizard(objtype *ob);
+void T_WizardShoot(objtype *ob);
+
+statetype s_wizard_walk1 = {WIZARDWALK1PIC, 20, &T_Wizard, &s_wizard_walk2};
+statetype s_wizard_walk2 = {WIZARDWALK2PIC, 20, &T_Wizard, &s_wizard_walk3};
+statetype s_wizard_walk3 = {WIZARDWALK3PIC, 20, &T_Wizard, &s_wizard_walk4};
+statetype s_wizard_walk4 = {WIZARDWALK4PIC, 20, &T_Wizard, &s_wizard_walk1};
+
+statetype s_wizard_attack1 = {WIZARDATTACK1PIC, 20, NULL, &s_wizard_attack2};
+statetype s_wizard_attack2 = {WIZARDATTACK2PIC, 20, &T_DoDamage, &s_wizard_walk1};
+
+statetype s_wizard_ouch = {WIZARDOUCHPIC, 15, NULL, &s_wizard_walk1};
+
+statetype s_wizard_die1 = {WIZARDDEATH1PIC, 45, &SmallSound, &s_wizard_die2};
+statetype s_wizard_die2 = {WIZARDDEATH2PIC, 30, NULL, &s_wizard_die3};
+statetype s_wizard_die3 = {WIZARDDEATH3PIC, 15, NULL, &s_wizard_die4};
+statetype s_wizard_die4 = {WIZARDDEATH4PIC, 15, NULL, &s_wizard_die4};
+
+statetype s_wizard_shoot1 = {WIZARDATTACK1PIC, 20, NULL, &s_wizard_shoot2};
+statetype s_wizard_shoot2 = {WIZARDATTACK1PIC, -1, &T_WizardShoot, &s_wizard_shoot3};
+statetype s_wizard_shoot3 = {WIZARDATTACK2PIC, 20, NULL, &s_wizard_walk1};
+
+statetype s_wizard_shot1 = {WIZARD_SHOT1PIC, 8, &T_ShootPlayer, &s_wizard_shot2};
+statetype s_wizard_shot2 = {WIZARD_SHOT2PIC, 8, &T_ShootPlayer, &s_wizard_shot1};
+
+
+/*
+===============
+=
+= SpawnWizard
+=
+===============
+*/
+
+void SpawnWizard (int tilex, int tiley)
+{
+	SpawnNewObj(tilex,tiley,&s_wizard_walk1,TILEGLOBAL/2);
+	new->obclass	= wizardobj;
+	new->speed		= 1536;
+	new->flags |= of_shootable;
+	new->hitpoints	= EasyHitPoints(10);
+}
+
+
+/*
+===============
+=
+= T_Wizard
+=
+===============
+*/
+
+void T_Wizard(objtype *ob)
+{
+	if (Chase (ob,true))// || (random(1000)<RANDOM_ATTACK))
+	{
+		ob->state = &s_wizard_attack1;
+		ob->ticcount = ob->state->tictime;
+		return;
+	}
+	else
+		if (AngleNearPlayer(ob) != -1)
+		{
+			ob->state = &s_wizard_shoot1;
+			ob->ticcount = ob->state->tictime;
+			return;
+		}
+}
+
+/*
+===============
+=
+= T_Wizard
+=
+===============
+*/
+void T_WizardShoot(objtype *ob)
+{
+	ShootPlayer(ob, wshotobj, 10000, &s_wizard_shot1);
+}
+
+
+
+/*
+=============================================================================
+
+											RAY
+
+=============================================================================
+*/
+
+void T_BlobRay(objtype *ob);
+void T_RayShoot (objtype *ob);
+
+statetype s_ray_under = {0, 20, &T_BlobRay, &s_ray_under};
+
+statetype s_ray_rise = {RAYRISEPIC, 30, NULL, &s_ray_fly1};
+
+statetype s_ray_sink = {RAYRISEPIC, 30, NULL, &s_ray_under};
+
+statetype s_ray_fly1 = {RAYFLY1PIC, 10, &T_BlobRay, &s_ray_fly2};
+statetype s_ray_fly2 = {RAYFLY2PIC, 10, &T_BlobRay, &s_ray_fly3};
+statetype s_ray_fly3 = {RAYFLY1PIC, 10, &T_BlobRay, &s_ray_fly4};
+statetype s_ray_fly4 = {RAYFLY3PIC, 10, &T_BlobRay, &s_ray_fly1};
+
+statetype s_ray_attack1 = {RAYSHOOT1PIC, 15, NULL, &s_ray_attack2};
+statetype s_ray_attack2 = {RAYSHOOT2PIC, -1, &T_RayShoot, &s_ray_attack3};
+statetype s_ray_attack3 = {RAYSHOOT2PIC, 20, NULL, &s_ray_fly1};
+
+statetype s_ray_die1 = {RAYDEATH1PIC, 50, &SmallSound, &s_ray_die2};
+statetype s_ray_die2 = {RAYDEATH2PIC, 30, NULL, NULL};
+
+statetype s_ray_shot1 = {RAYSHOT1PIC, 8, &T_ShootPlayer, &s_ray_shot2};
+statetype s_ray_shot2 = {RAYSHOT2PIC, 8, &T_ShootPlayer, &s_ray_shot1};
+
+
+typedef enum {br_GND, br_WALK, br_CORNER1, br_CORNER2, br_CORNER3, br_CORNER4} BlobTypes;
+
+#define BR_TIMEREMAIN	(ob->temp1)
+#define BR_STAGE			(ob->temp2)
+#define BLOB_LEAVE		0x04
+
+/*
+===============
+=
+= SpawnRay
+=
+===============
+*/
+void SpawnRay(int tilex, int tiley)
+{
+	objtype *ob;
+	SpawnNewObj(tilex, tiley, &s_ray_under, PIXRADIUS*25);
+	ob=new;
+
+	BR_STAGE = br_GND;
+	BR_TIMEREMAIN = random(60)+random(100);
+
+	new->obclass	= rayobj;
+	new->speed		= 1700;
+	new->flags	&= ~of_shootable;
+	new->hitpoints	= EasyHitPoints(15);
+}
+
+
+
+/*
+=============================================================================
+
+										BLOB
+
+=============================================================================
+*/
+
+
+statetype s_blob_gnd1 = {BLOBGND1PIC, 13, T_BlobRay, &s_blob_gnd2};
+statetype s_blob_gnd2 = {BLOBGND2PIC, 15, T_BlobRay, &s_blob_gnd1};
+
+statetype s_blob_rise1 = {BLOBRISE1PIC, 20, NULL, &s_blob_rise2};
+statetype s_blob_rise2 = {BLOBRISE2PIC, 20, NULL, &s_blob_walk1};
+
+statetype s_blob_sink1 = {BLOBRISE2PIC, 20, NULL, &s_blob_sink2};
+statetype s_blob_sink2 = {BLOBRISE1PIC, 20, NULL, &s_blob_gnd1};
+
+statetype s_blob_walk1 = {BLOBWALK1PIC, 15, T_BlobRay, &s_blob_walk2};
+statetype s_blob_walk2 = {BLOBWALK2PIC, 15, T_BlobRay, &s_blob_walk3};
+statetype s_blob_walk3 = {BLOBWALK3PIC, 15, T_BlobRay, &s_blob_walk1};
+
+statetype s_blob_ouch = {BLOBRISE2PIC, 10, T_BlobRay, &s_blob_walk1};
+
+statetype s_blob_die1 = {BLOBDEATH1PIC, 30, &ExplosionSnd, &s_blob_die2};
+statetype s_blob_die2 = {BLOBDEATH2PIC, 30, NULL, &s_blob_die3};
+statetype s_blob_die3 = {BLOBDEATH3PIC, 30, NULL, NULL};
+
+statetype s_blob_shot1 = {BLOB_SHOT1PIC, 8, &T_ShootPlayer, &s_blob_shot2};
+statetype s_blob_shot2 = {BLOB_SHOT2PIC, 8, &T_ShootPlayer, &s_blob_shot1};
+
+
+/*
+===============
+=
+= SpawnBlob
+=
+===============
+*/
+void SpawnBlob(int tilex, int tiley)
+{
+	objtype *ob;
+	SpawnNewObj(tilex, tiley, &s_blob_gnd1, PIXRADIUS*14);
+	ob=new;
+
+	BR_STAGE = br_GND;
+	BR_TIMEREMAIN = random(60)+random(100);
+
+	new->obclass	= blobobj;
+	new->speed		= 1200;
+	new->flags	&= ~of_shootable;
+	new->hitpoints	= EasyHitPoints(13);
+}
+
+
+/*
+===============
+=
+= T_BlobRay
+=
+===============
+*/
+
+void T_BlobRay(objtype *ob)
+{
+	switch (BR_STAGE)
+	{
+		case br_GND:
+			ob->flags &= ~of_shootable;
+			if (Chase(ob,true))
+			{
+				// RISE & GOTO WALK STAGE
+				//
+
+				BR_STAGE			= br_WALK;
+				BR_TIMEREMAIN	= 60*8+random(60*5);
+				if (ob->obclass == blobobj)
+					ob->state	= &s_blob_rise1;
+				else
+					ob->state	= &s_ray_rise;
+				ob->speed		= 2200;
+				ob->ticcount	= ob->state->tictime;
+			}
+			else
+			{
+				// DEC COUNTER - And check for WALK
+				//
+				if ((BR_TIMEREMAIN -= realtics) < 0)
+				{
+					// RISE & GOTO WALK STAGE
+					//
+
+					BR_STAGE			= br_WALK;
+					BR_TIMEREMAIN	= 60*8+random(60*5);
+					if (ob->obclass == blobobj)
+						ob->state	= &s_blob_rise1;
+					else
+						ob->state	= &s_ray_rise;
+					ob->speed		= 2200;
+					ob->ticcount	= ob->state->tictime;
+				}
+			}
+			break;
+
+
+		case br_WALK:
+			ob->flags |= of_shootable;
+
+			if (Chase(ob,true) || (CheckHandAttack(ob)))
+
+			{
+					ob->flags		|= BLOB_LEAVE;
+					BR_STAGE			= random(br_CORNER3) + 2;
+					BR_TIMEREMAIN	= 60*2+(random(6)*60);
+					if (ob->obclass == blobobj)
+						ob->state	= &s_blob_gnd1;
+					else
+						ob->state	= &s_ray_under;
+					ob->ticcount	= ob->state->tictime;
+			}
+			else
+				if (AngleNearPlayer(ob) != -1)
+				{
+					if (ob->obclass == blobobj)
+					{
+						if (!(random(15)))
+								ShootPlayer(ob, bshotobj, 10000, &s_blob_shot1);
+					}
+					else
+						if (!(random(7)))
+						{
+							ob->state = &s_ray_attack1;
+							ob->ticcount = ob->state->tictime;
+						}
+				}
+
+			else
+			{
+				// DEC COUNTER - And check for SINK
+				//
+				if ((BR_TIMEREMAIN -= realtics) < 0)
+				{
+					// SINK & GOTO GROUND STAGE
+					//
+
+					BR_STAGE			= br_GND;
+					BR_TIMEREMAIN	= 60*2+random(60*2);
+					if (ob->obclass == blobobj)
+					{
+						ob->state		= &s_blob_sink1;
+						ob->speed		= 1200;
+					}
+					else
+					{
+						ob->state		= &s_ray_sink;
+						ob->speed		= 1700;
+					}
+					ob->ticcount	= ob->state->tictime;
+					ob->flags		&= ~of_shootable;
+				}
+
+			}
+			break;
+		case br_CORNER1:
+		case br_CORNER2:
+		case br_CORNER3:
+		case br_CORNER4:
+			ob->flags &= ~of_shootable;
+			if ((BR_TIMEREMAIN -= realtics) < 0)
+			{
+				BR_STAGE = br_GND;
+				ob->flags &= ~BLOB_LEAVE;
+			}
+			else
+			{
+				fixed tempx,tempy;
+				unsigned temp_tilex,temp_tiley;
+
+				tempx			= player->x;
+				tempy			= player->y;
+				temp_tilex	= player->tilex;
+				temp_tiley	= player->tiley;
+
+				player->x = ((long)other_x[BR_STAGE-2]<<TILESHIFT)+TILEGLOBAL/2;
+				player->y = ((long)other_y[BR_STAGE-2]<<TILESHIFT)+TILEGLOBAL/2;
+				player->tilex = other_x[BR_STAGE-2];
+				player->tiley = other_y[BR_STAGE-2];
+
+
+				Chase(ob,true);
+
+				player->x		= tempx;
+				player->y		= tempy;
+				player->tilex	= temp_tilex;
+				player->tiley	= temp_tiley;
+			}
+			break;
+	}
+}
+
+/*
+===============
+=
+= T_RayShoot
+=
+===============
+*/
+void T_RayShoot (objtype *ob)
+{
+	ShootPlayer(ob, rshotobj, 10000, &s_ray_shot1);
+}
\ No newline at end of file
diff --git a/src/lib/hb/c6_act2.c b/src/lib/hb/c6_act2.c
new file mode 100755
index 00000000..803fe8b9
--- /dev/null
+++ b/src/lib/hb/c6_act2.c
@@ -0,0 +1,891 @@
+/* Catacomb Apocalypse Source Code
+ * Copyright (C) 1993-2014 Flat Rock Software
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// C3_PLAY.C
+
+#include "DEF.H"
+#pragma hdrstop
+
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+boolean ShootPlayer (objtype *ob, short obclass, short speed, statetype *state);
+void T_ShootPlayer(objtype *ob);
+
+short head_base_delay;
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+
+void T_ShooterObj(objtype *ob);
+
+void SpawnRamBone(int tilex, int tiley);
+void T_SkeletonShoot(objtype *ob);
+void SpawnFutureMage (int tilex, int tiley);
+void T_FMageShoot(objtype *ob);
+void SpawnRoboTank(int tilex, int tiley);
+void T_RoboTankShoot(objtype *ob);
+void SpawnStompy(int tilex, int tiley);
+void T_StompyShoot(objtype *ob);
+void SpawnBug(int tilex, int tiley);
+void T_BugShoot(objtype *ob);
+void SpawnShooterEye(int tilex, int tiley);
+void T_EyeShootPlayer(objtype *ob);
+void SpawnRunningEye(int tilex, int tiley);
+void T_RunningEye(objtype *ob);
+
+
+/*
+=============================================================================
+
+										LARGE SOUND
+
+=============================================================================
+*/
+void LargeSound (objtype *ob)
+{
+	if (ob->temp1 != SOUNDPLAYED)
+	{
+		SD_PlaySound(LARGEMONSTERSND);
+		ob->temp1 = SOUNDPLAYED;
+
+	}
+}
+
+
+/*
+=============================================================================
+
+										SMALL SOUND
+
+=============================================================================
+*/
+void SmallSound (objtype *ob)
+{
+	if (ob->temp1 != SOUNDPLAYED)
+	{
+		SD_PlaySound(SMALLMONSTERSND);
+		ob->temp1 = SOUNDPLAYED;
+
+	}
+}
+
+
+
+/*
+=============================================================================
+
+										RAMBONE
+
+=============================================================================
+*/
+
+
+statetype s_skel_1 = {RAMBONEWALK1PIC, 10, &T_ShooterObj, &s_skel_2};
+statetype s_skel_2 = {RAMBONEWALK2PIC, 10, &T_ShooterObj, &s_skel_3};
+statetype s_skel_3 = {RAMBONEWALK3PIC, 10, &T_ShooterObj, &s_skel_4};
+statetype s_skel_4 = {RAMBONEWALK4PIC, 10, &T_ShooterObj, &s_skel_1};
+
+statetype s_skel_attack1 = {RAMBONEATTACK1PIC, 12, NULL, &s_skel_attack2};
+statetype s_skel_attack2 = {RAMBONEATTACK2PIC, 20, NULL, &s_skel_attack3};
+statetype s_skel_attack3 = {RAMBONEATTACK2PIC, -1, T_SkeletonShoot, &s_skel_attack4};
+statetype s_skel_attack4 = {RAMBONEATTACK3PIC, 20, NULL, &s_skel_ouch};
+
+statetype s_skel_ouch = {RAMBONEATTACK1PIC, 10, NULL, &s_skel_1};
+
+statetype s_skel_die1 = {RAMBONEDEATH1PIC, 40, NULL, &s_skel_die2};
+statetype s_skel_die2 = {RAMBONEDEATH2PIC, 30, NULL, &s_skel_die3};
+statetype s_skel_die3 = {RAMBONEDEATH3PIC, 20, &LargeSound, NULL};
+
+statetype s_skel_shot1 = {RAMBONESHOT1PIC, 10, &T_ShootPlayer, &s_skel_shot2};
+statetype s_skel_shot2 = {RAMBONESHOT2PIC, 10, &T_ShootPlayer, &s_skel_shot1};
+
+#define shooter_mode		ob->temp1
+#define shooter_delay	ob->temp2
+
+
+/*
+===============
+=
+= SpawnSkeleton
+=
+===============
+*/
+void SpawnRamBone(int tilex, int tiley)
+{
+	SpawnNewObj(tilex, tiley, &s_skel_1,PIXRADIUS*20);
+	new->obclass	= ramboneobj;
+	new->speed		= 2036;
+	new->flags		|= of_shootable;
+	new->hitpoints	= EasyHitPoints(12);
+}
+
+
+/*
+=================
+=
+= T_SkeletonShoot
+=
+=================
+*/
+void T_SkeletonShoot(objtype *ob)
+{
+	ShootPlayer(ob, rbshotobj, MSHOTSPEED, &s_skel_shot1);
+}
+
+
+
+/*
+=============================================================================
+
+										FUTURE MAGE
+
+=============================================================================
+*/
+
+statetype s_fmage1 = {FMAGEWALK1PIC, 20, &T_ShooterObj, &s_fmage2};
+statetype s_fmage2 = {FMAGEWALK2PIC, 20, &T_ShooterObj, &s_fmage3};
+statetype s_fmage3 = {FMAGEWALK3PIC, 20, &T_ShooterObj, &s_fmage1};
+
+statetype s_fmageattack1 = {FMAGEATTACK1PIC, 20, NULL, &s_fmageattack2};
+statetype s_fmageattack2 = {FMAGEATTACK1PIC, -1, &T_FMageShoot, &s_fmageattack3};
+statetype s_fmageattack3 = {FMAGEATTACK2PIC, 30, NULL, &s_fmage1};
+
+statetype s_fmageouch = {FMAGEATTACK1PIC, 10, NULL, &s_fmage1};
+
+statetype s_fmagedie1 = {FMAGEDEATH1PIC, 40, NULL, &s_fmagedie2};
+statetype s_fmagedie2 = {FMAGEDEATH2PIC, 30, &SmallSound, &s_fmagedie3};
+statetype s_fmagedie3 = {FMAGEDEATH3PIC, 0, NULL, &s_fmagedie3};
+
+statetype s_fmshot1 = {FMAGESHOT1PIC, 8, &T_ShootPlayer, &s_fmshot2};
+statetype s_fmshot2 = {FMAGESHOT2PIC, 8, &T_ShootPlayer, &s_fmshot1};
+
+/*
+=================
+=
+= SpawnFutureMage
+=
+=================
+*/
+
+void SpawnFutureMage (int tilex, int tiley)
+{
+	SpawnNewObj(tilex, tiley, &s_fmage1, PIXRADIUS*15);
+	new->obclass	= fmageobj;
+	new->speed		= 3072;
+	new->flags		|= of_shootable;
+	new->hitpoints	= EasyHitPoints(12);
+}
+
+
+/*
+=================
+=
+= T_FMageShoot
+=
+=================
+*/
+void T_FMageShoot(objtype *ob)
+{
+	ShootPlayer(ob, fmshotobj, MSHOTSPEED, &s_fmshot1);
+}
+
+
+
+/*
+=============================================================================
+
+										ROBO TANK
+
+=============================================================================
+*/
+
+statetype s_robotank_walk1 = {ROBOTANKWALK1PIC, 15, &T_ShooterObj, &s_robotank_walk2};
+statetype s_robotank_walk2 = {ROBOTANKWALK2PIC, 15, &T_ShooterObj, &s_robotank_walk3};
+statetype s_robotank_walk3 = {ROBOTANKWALK3PIC, 15, &T_ShooterObj, &s_robotank_walk4};
+statetype s_robotank_walk4 = {ROBOTANKWALK4PIC, 15, &T_ShooterObj, &s_robotank_walk1};
+
+statetype s_robotank_attack1 = {ROBOTANKWALK1PIC, 15, NULL, &s_robotank_attack2};
+statetype s_robotank_attack2 = {ROBOTANKATTACK1PIC, 15, NULL, &s_robotank_attack3};
+statetype s_robotank_attack3 = {ROBOTANKATTACK1PIC, -1, &T_RoboTankShoot, &s_robotank_attack4};
+statetype s_robotank_attack4 = {ROBOTANKWALK1PIC, 15, NULL, &s_robotank_walk1};
+
+statetype s_robotank_death1 = {ROBOTANKDEATH1PIC, 8, NULL, &s_robotank_death2};
+statetype s_robotank_death2 = {ROBOTANKDEATH2PIC, 8, NULL, &s_robotank_death3};
+statetype s_robotank_death3 = {ROBOTANKDEATH2PIC, -1, &T_AlternateStates, &s_robotank_death1};
+statetype s_robotank_death4 = {ROBOTANKDEATH3PIC, 25, &ExplosionSnd, &s_robotank_death5};
+statetype s_robotank_death5 = {ROBOTANKDEATH4PIC, 20, NULL, &s_robotank_death5};
+
+statetype s_robotank_shot1 = {PSHOT1PIC, 10, &T_ShootPlayer, &s_robotank_shot2};
+statetype s_robotank_shot2 = {PSHOT2PIC, 10, &T_ShootPlayer, &s_robotank_shot1};
+
+
+/*
+=================
+=
+= SpawnRoboTank
+=
+=================
+*/
+void SpawnRoboTank(int tilex, int tiley)
+{
+	SpawnNewObj(tilex, tiley, &s_robotank_walk1, PIXRADIUS*35);
+	new->obclass	= robotankobj;
+	new->speed		= 1700;
+	new->flags		|= of_shootable;
+	new->hitpoints	= EasyHitPoints(25);
+}
+
+
+/*
+=================
+=
+= T_RoboTankShoot
+=
+=================
+*/
+void T_RoboTankShoot(objtype *ob)
+{
+	ShootPlayer(ob, rtshotobj, 7000, &s_robotank_shot1);
+}
+
+
+
+/*
+====================
+=
+= T_AlternateStates
+=
+====================
+*/
+void T_AlternateStates(objtype *ob)
+{
+	if (ob->temp1--)
+	{
+		ob->state = ob->state->next;
+	}
+	else
+	{
+		if (ob->state == &s_robotank_death3)
+			ob->state = &s_robotank_death4;
+		else
+			ob->state = &s_aqua_die4;
+	}
+	ob->ticcount	= ob->state->tictime;
+}
+
+
+
+
+
+
+/*
+=============================================================================
+
+											STOMPY
+
+=============================================================================
+*/
+
+statetype s_stompy_walk1 = {STOMPYWALK1PIC, 15, &T_ShooterObj, &s_stompy_walk2};
+statetype s_stompy_walk2 = {STOMPYWALK2PIC, 15, &T_ShooterObj, &s_stompy_walk3};
+statetype s_stompy_walk3 = {STOMPYWALK3PIC, 15, &T_ShooterObj, &s_stompy_walk4};
+statetype s_stompy_walk4 = {STOMPYWALK4PIC, 15, &T_ShooterObj, &s_stompy_walk1};
+
+statetype s_stompy_attack1 = {STOMPYATTACK1PIC, 10, NULL, &s_stompy_attack2};
+statetype s_stompy_attack2 = {STOMPYATTACK2PIC, 15, NULL, &s_stompy_attack3};
+statetype s_stompy_attack3 = {STOMPYATTACK2PIC, -1, T_StompyShoot, &s_stompy_attack4};
+statetype s_stompy_attack4 = {STOMPYATTACK1PIC, 10, NULL, &s_stompy_walk1};
+
+statetype s_stompy_ouch = {STOMPYATTACK1PIC, 10, NULL, &s_stompy_walk2};
+
+statetype s_stompy_death1 = {STOMPYDEATH1PIC, 45, &ExplosionSnd, &s_stompy_death2};
+statetype s_stompy_death2 = {STOMPYDEATH2PIC, 30, NULL, &s_stompy_death3};
+statetype s_stompy_death3 = {STOMPYDEATH3PIC, 25, NULL, &s_stompy_death4};
+statetype s_stompy_death4 = {STOMPYDEATH4PIC, 20, NULL, NULL};
+
+statetype s_stompy_shot1 = {STOMPYSHOT1PIC, 6, &T_ShootPlayer, &s_stompy_shot2};
+statetype s_stompy_shot2 = {STOMPYSHOT2PIC, 6, &T_ShootPlayer, &s_stompy_shot3};
+statetype s_stompy_shot3 = {STOMPYSHOT1PIC, 6, &T_ShootPlayer, &s_stompy_shot4};
+statetype s_stompy_shot4 = {STOMPYSHOT3PIC, 6, &T_ShootPlayer, &s_stompy_shot5};
+statetype s_stompy_shot5 = {STOMPYSHOT4PIC, 6, &T_ShootPlayer, &s_stompy_shot4};
+
+
+/*
+=================
+=
+= SpawnStompy
+=
+=================
+*/
+void SpawnStompy(int tilex, int tiley)
+{
+	SpawnNewObj(tilex, tiley, &s_stompy_walk1, PIXRADIUS*25);
+	new->obclass	= stompyobj;
+	new->speed		= 1800;
+	new->flags		|= of_shootable;
+	new->hitpoints	= EasyHitPoints(20);
+}
+
+
+/*
+=================
+=
+= T_StompyShoot
+=
+=================
+*/
+void T_StompyShoot(objtype *ob)
+{
+	ShootPlayer(ob, syshotobj, 8500, &s_stompy_shot1);
+}
+
+
+
+/*
+=============================================================================
+
+												BUG
+
+=============================================================================
+*/
+
+statetype s_bug_walk1 = {BUG_WALK1PIC, 15, &T_ShooterObj, &s_bug_walk2};
+statetype s_bug_walk2 = {BUG_WALK2PIC, 15, &T_ShooterObj, &s_bug_walk3};
+statetype s_bug_walk3 = {BUG_WALK3PIC, 15, &T_ShooterObj, &s_bug_walk1};
+
+statetype s_bug_attack1 = {BUG_ATTACK1PIC, 20, NULL, &s_bug_attack2};
+statetype s_bug_attack2 = {BUG_ATTACK2PIC, 20, NULL, &s_bug_attack3};
+statetype s_bug_attack3 = {BUG_ATTACK2PIC, -1, &T_BugShoot, &s_bug_attack4};
+statetype s_bug_attack4 = {BUG_ATTACK1PIC, 15, NULL, &s_bug_walk1};
+
+statetype s_bug_ouch = {BUG_WALK1PIC, 10, NULL, &s_bug_walk2};
+
+statetype s_bug_death1 = {BUG_DEATH1PIC, 35, &SmallSound, &s_bug_death2};
+statetype s_bug_death2 = {BUG_DEATH2PIC, 10, NULL, &s_bug_death2};
+
+statetype s_bug_shot1 = {BUG_SHOT1PIC, 10, &T_ShootPlayer, &s_bug_shot2};
+statetype s_bug_shot2 = {BUG_SHOT2PIC, 10, &T_ShootPlayer, &s_bug_shot1};
+
+
+/*
+=================
+=
+= SpawnBug
+=
+=================
+*/
+void SpawnBug(int tilex, int tiley)
+{
+	SpawnNewObj(tilex, tiley, &s_bug_walk1, PIXRADIUS*20);
+	new->obclass	= bugobj;
+	new->speed		= 1500;
+	new->flags		|= of_shootable;
+	new->hitpoints	= EasyHitPoints(10);
+}
+
+
+/*
+=================
+=
+= T_BugShoot
+=
+=================
+*/
+void T_BugShoot(objtype *ob)
+{
+	ShootPlayer(ob, bgshotobj, 8000, &s_bug_shot1);
+}
+
+
+
+
+
+/*
+=============================================================================
+
+									MEC EYE
+
+=============================================================================
+*/
+
+void T_EyeShootPlayer (objtype *ob);
+
+statetype s_eye_pause = {EYE_WALK1PIC,40,NULL,&s_eye_2};
+
+statetype s_eye_1 = {EYE_WALK1PIC,20,T_ShooterObj,&s_eye_2};
+statetype s_eye_2 = {EYE_WALK2PIC,20,T_ShooterObj,&s_eye_3};
+statetype s_eye_3 = {EYE_WALK3PIC,20,T_ShooterObj,&s_eye_4};
+statetype s_eye_4 = {EYE_WALK2PIC,20,T_ShooterObj,&s_eye_1};
+statetype s_eye_shootplayer_1 = {EYE_WALK1PIC,1,T_EyeShootPlayer,&s_eye_shootplayer_2};
+statetype s_eye_shootplayer_2 = {EYE_WALK1PIC,20,NULL,&s_eye_1};
+
+statetype s_eye_ouch = {EYE_OUCH1PIC,8,NULL,&s_eye_ouch2};
+statetype s_eye_ouch2 = {EYE_OUCH2PIC,8,NULL,&s_eye_1};
+
+statetype s_eye_die1 = {EYE_DEATH1PIC,22,NULL,&s_eye_die2};
+statetype s_eye_die2 = {EYE_DEATH2PIC,22,&SmallSound,&s_eye_die3};
+statetype s_eye_die3 = {EYE_DEATH3PIC,22,NULL,&s_eye_die4};
+statetype s_eye_die4 = {EYE_DEATH4PIC,22,NULL,&s_eye_die4};
+
+statetype s_eshot1 = {EYE_SHOT1PIC,8,&T_ShootPlayer,&s_eshot2};
+statetype s_eshot2 = {EYE_SHOT2PIC,8,&T_ShootPlayer,&s_eshot1};
+
+
+//-------------------------------------------------------------------------
+// SpawnEye()
+//-------------------------------------------------------------------------
+void SpawnShooterEye(int tilex, int tiley)
+{
+	objtype *ob;
+
+	SpawnNewObj(tilex,tiley,&s_eye_1,PIXRADIUS*10);
+	ob = new;
+	new->obclass = eyeobj;
+	new->speed = 3000;
+	new->flags |= of_shootable;
+	new->hitpoints = EasyHitPoints(15);
+	shooter_mode = sm_other1;
+}
+
+
+//---------------------------------------------------------------------------
+// T_EyeShootPlayer
+//---------------------------------------------------------------------------
+void T_EyeShootPlayer (objtype *ob)
+{
+	ShootPlayer(ob, eshotobj, ESHOTSPEED, &s_eshot1);
+}
+
+
+/*
+===============
+=
+= T_ShooterObj
+=
+= **********
+= ***NOTE*** This routine controls the thinks for the RamBone, RoboTank,
+= **********	Stompy, Future Mage, Bug, and Old Mage
+=
+===============
+*/
+
+void T_ShooterObj(objtype *ob)
+{
+	fixed tempx,tempy;
+	unsigned temp_tilex,temp_tiley;
+	int angle;
+
+	shooter_delay -= realtics;
+	if (shooter_delay < 0)
+	{
+		shooter_mode = random(sm_dummy);
+		shooter_delay = random(10*60)+random(50);
+	}
+
+	tempx = player->x;
+	tempy = player->y;
+	temp_tilex = player->tilex;
+	temp_tiley = player->tiley;
+
+
+	switch (shooter_mode)
+	{
+		case sm_other1:
+		case sm_other2:
+		case sm_other3:
+		case sm_other4:
+			player->x = ((long)other_x[shooter_mode]<<TILESHIFT)+TILEGLOBAL/2;
+			player->y = ((long)other_y[shooter_mode]<<TILESHIFT)+TILEGLOBAL/2;
+			player->tilex = other_x[shooter_mode];
+			player->tiley = other_y[shooter_mode];
+		break;
+	}
+
+	if (Chase(ob,true))
+		shooter_delay = 0;
+
+	player->x = tempx;
+	player->y = tempy;
+	player->tilex = temp_tilex;
+	player->tiley = temp_tiley;
+
+	angle = AngleNearPlayer(ob);
+
+
+  // Handle shooting for the different characters controlled by this think.
+	switch (ob->obclass)
+	{
+		case ramboneobj:
+			if (!random(2) && (angle != -1))
+			{
+				ob->state = &s_skel_attack1;
+				ob->ticcount = ob->state->tictime;
+			}
+		break;
+
+		case fmageobj:
+			if (!random(8) && (angle != -1))
+			{
+				ob->state = &s_fmageattack1;
+				ob->ticcount = ob->state->tictime;
+			}
+		break;
+
+		case robotankobj:
+			if (!random(15) && (angle != -1))
+			{
+				ob->state = &s_robotank_attack1;
+				ob->ticcount = ob->state->tictime;
+			}
+		break;
+
+		case stompyobj:
+			if (angle != -1)
+			{
+				ob->state = &s_stompy_attack1;
+				ob->ticcount = ob->state->tictime;
+			}
+		break;
+
+		case bugobj:
+			if (!random(5) && (angle != -1))
+			{
+				ob->state = &s_bug_attack1;
+				ob->ticcount = ob->state->tictime;
+			}
+		break;
+
+		case eyeobj:
+			if (!random(2) && (angle != -1))
+			{
+				ob->state = &s_eye_shootplayer_1;
+				ob->ticcount = ob->state->tictime;
+			}
+		break;
+	}
+}
+
+
+
+
+
+
+/*
+=============================================================================
+
+										RUNNING EYE
+
+=============================================================================
+*/
+
+statetype s_reye_1 = {EYE_WALK1PIC, 20, &T_RunningEye, &s_reye_2};
+statetype s_reye_2 = {EYE_WALK2PIC, 20, &T_RunningEye, &s_reye_3};
+statetype s_reye_3 = {EYE_WALK3PIC, 20, &T_RunningEye, &s_reye_4};
+statetype s_reye_4 = {EYE_WALK2PIC, 20, &T_RunningEye, &s_reye_1};
+
+statetype s_reye_ouch = {EYE_OUCH1PIC, 8, NULL, &s_reye_ouch2};
+statetype s_reye_ouch2 = {EYE_OUCH2PIC, 8, NULL, &s_reye_1};
+
+statetype s_reye_die1 = {EYE_DEATH1PIC, 22, NULL, &s_reye_die2};
+statetype s_reye_die2 = {EYE_DEATH2PIC, 22, &SmallSound, &s_reye_die3};
+statetype s_reye_die3 = {EYE_DEATH3PIC, 22, NULL, &s_reye_die4};
+statetype s_reye_die4 = {EYE_DEATH4PIC, 22, NULL, &s_reye_die4};
+
+/*
+====================
+=
+= SpawnRunningEye()
+=
+====================
+*/
+void SpawnRunningEye(int tilex, int tiley)
+{
+	SpawnNewObj(tilex,tiley,&s_reye_1,PIXRADIUS*25);
+	new->obclass = reyeobj;
+	new->speed = 3500;
+	new->flags |= of_shootable;
+	new->hitpoints = EasyHitPoints(15);
+	new->temp2 = (*(mapsegs[2]+farmapylookup[tiley+1]+tilex))>>8;
+	*(mapsegs[2]+farmapylookup[tiley+1]+tilex) = 0;
+
+	new->temp1 = 2;
+
+	if (!new->temp2)
+		Quit("Initialize the running eye!\n");
+}
+
+
+/*
+====================
+=
+= T_RunningEye
+=
+====================
+*/
+void T_RunningEye(objtype *ob)
+{
+	int x, y, dir_num, switch_num;
+	fixed tempx,tempy;
+	unsigned temp_tilex,temp_tiley;
+
+	dir_num = *(mapsegs[2]+farmapylookup[ob->tiley]+ob->tilex);
+	dir_num = dir_num>>8;
+
+	if (!dir_num)
+		dir_num = ob->temp2;
+
+	if (dir_num == 5)
+	{
+		if (ob->temp1)
+		{
+			ob->temp1--;
+		}
+		else
+		{
+			ob->temp1 = 2;
+			actorat[ob->tilex][ob->tiley] = 0;
+			switch (ob->temp2)
+			{
+				case 1:
+					ob->tiley = ob->tiley-1;
+					ob->y = ((long)(ob->tiley)<<TILESHIFT)+TILEGLOBAL/2;
+				break;
+
+				case 2:
+					ob->tilex = ob->tilex+1;
+					ob->x = ((long)(ob->tilex)<<TILESHIFT)+TILEGLOBAL/2;
+				break;
+
+				case 3:
+					ob->tiley = ob->tiley+1;
+					ob->y = ((long)(ob->tiley)<<TILESHIFT)+TILEGLOBAL/2;
+				break;
+
+				case 0:
+				case 4:
+					ob->tilex = ob->tilex-1;
+					ob->x = ((long)(ob->tilex)<<TILESHIFT)+TILEGLOBAL/2;
+				break;
+			}
+			CalcBounds (ob);
+			ChaseThink(ob,false);
+			actorat[ob->tilex][ob->tiley] = ob;
+			return;
+		}
+	}
+
+	tempx = player->x;
+	tempy = player->y;
+	temp_tilex = player->tilex;
+	temp_tiley = player->tiley;
+
+	if (dir_num == 5)
+		switch_num = ob->temp2;
+	else
+		switch_num = dir_num;
+
+	switch (switch_num)
+	{
+		case 1:
+			player->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;
+			player->y = ((long)(ob->tiley-2)<<TILESHIFT)+TILEGLOBAL/2;
+			player->tilex = ob->tilex;
+			player->tiley = ob->tiley-2;
+		break;
+
+		case 2:
+			player->x = ((long)(ob->tilex+2)<<TILESHIFT)+TILEGLOBAL/2;
+			player->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;
+			player->tilex = ob->tilex+2;
+			player->tiley = ob->tiley;
+		break;
+
+		case 3:
+			player->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;
+			player->y = ((long)(ob->tiley+2)<<TILESHIFT)+TILEGLOBAL/2;
+			player->tilex = ob->tilex;
+			player->tiley = ob->tiley+2;
+		break;
+
+		case 0:
+		case 4:
+			player->x = ((long)(ob->tilex-2)<<TILESHIFT)+TILEGLOBAL/2;
+			player->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;
+			player->tilex = ob->tilex-2;
+			player->tiley = ob->tiley;
+		break;
+	}
+
+	Chase(ob, false);
+
+	player->x = tempx;
+	player->y = tempy;
+	player->tilex = temp_tilex;
+	player->tiley = temp_tiley;
+
+	if (dir_num != 5)
+		ob->temp2 = dir_num;
+}
+
+
+/*
+=============================================================================
+
+										EGYPTIAN HEAD
+
+=============================================================================
+*/
+
+void T_Head(objtype *ob);
+
+statetype s_head = {HEADPIC, 20, &T_Head, &s_head};
+
+statetype s_head_shot1 = {PSHOT1PIC, 10, &T_ShootPlayer, &s_head_shot2};
+statetype s_head_shot2 = {PSHOT2PIC, 10, &T_ShootPlayer, &s_head_shot1};
+
+
+/*
+===================
+=
+= SpawnEgyptianHead
+=
+===================
+*/
+
+void SpawnEgyptianHead (int tilex, int tiley)
+{
+	objtype *ob;
+	short current_head_delay;
+	unsigned tile;
+
+	SpawnNewObj(tilex, tiley, &s_head, PIXRADIUS*35);
+	ob = new;
+	head_mode = h_wait_to_rise;
+
+	tile = *(mapsegs[2]+farmapylookup[tiley+1]+tilex);
+	if (tile)
+		head_delay = (tile>>8)*30;
+	else
+	{
+		current_head_delay = (3*60)+random(3*60);
+		head_delay = head_base_delay+current_head_delay;
+		head_base_delay += current_head_delay;
+		if (head_base_delay > 8*60)
+			head_base_delay = 0;
+	}
+
+	new->obclass	= realsolidobj;
+	new->speed		= 3000;
+	new->flags	  |= of_shootable;
+}
+
+
+
+//--------------------------------------------------------------------------
+// T_Head()
+//--------------------------------------------------------------------------
+void T_Head(objtype *ob)
+{
+	fixed tempx,tempy;
+	unsigned temp_tilex,temp_tiley;
+	int angle;
+
+	switch (head_mode)
+	{
+		case h_wait_to_rise:
+			if (head_delay < 0)
+			{
+				if ((ob->tilex == player->tilex) && (ob->tiley == player->tiley))
+					break;
+				if (CheckHandAttack(ob))
+					break;
+
+				ob->obclass	= headobj;
+				ob->active	= always;
+				head_mode	= h_active;
+				head_delay 	= random(100)+random(60);
+				ob->hitpoints = EasyHitPoints(16);
+			}
+			else
+				head_delay -= tics;
+
+		break;
+
+		case h_player1:
+		case h_player2:
+		case h_player3:
+		case h_player4:
+		case h_active:
+			Chase (ob,true);
+
+			if (!random(2) && (angle != -1))
+				ShootPlayer(ob, hshotobj, 10000, &s_head_shot1);
+
+			head_delay -= tics;
+			if (head_delay < 0)
+			{
+				head_mode = random(h_other4)+1;
+				head_delay = random(10*60)+random(50);
+			}
+		break;
+
+		case h_other1:
+		case h_other2:
+		case h_other3:
+		case h_other4:
+
+			tempx = player->x;
+			tempy = player->y;
+			temp_tilex = player->tilex;
+			temp_tiley = player->tiley;
+
+			player->x = ((long)other_x[head_mode]<<TILESHIFT)+TILEGLOBAL/2;
+			player->y = ((long)other_y[head_mode]<<TILESHIFT)+TILEGLOBAL/2;
+			player->tilex = other_x[head_mode];
+			player->tiley = other_y[head_mode];
+
+			if (Chase(ob,true))
+				head_delay = 0;
+
+			player->x = tempx;
+			player->y = tempy;
+			player->tilex = temp_tilex;
+			player->tiley = temp_tiley;
+
+			head_delay -= tics;
+			if (head_delay <= 0)
+			{
+				head_mode = h_active;
+				head_delay = random(10*60)+random(50);
+			}
+		break;
+	}
+}
diff --git a/src/lib/hb/c6_act3.c b/src/lib/hb/c6_act3.c
new file mode 100755
index 00000000..37402895
--- /dev/null
+++ b/src/lib/hb/c6_act3.c
@@ -0,0 +1,802 @@
+/* Catacomb Apocalypse Source Code
+ * Copyright (C) 1993-2014 Flat Rock Software
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// C3_PLAY.C
+
+#include "DEF.H"
+#pragma hdrstop
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+boolean ShootPlayer (objtype *ob, short obclass, short speed, statetype *state);
+void T_ShootPlayer(objtype *ob);
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+
+
+
+
+/*
+=============================================================================
+
+							DEMON
+
+=============================================================================
+*/
+
+void T_TrollDemon (objtype *ob);
+
+statetype s_demonpause = {DEMON1PIC,40,NULL,&s_demon2};
+
+statetype s_demon1 = {DEMON1PIC,20,&T_TrollDemon,&s_demon2};
+statetype s_demon2 = {DEMON2PIC,20,&T_TrollDemon,&s_demon3};
+statetype s_demon3 = {DEMON3PIC,20,&T_TrollDemon,&s_demon4};
+statetype s_demon4 = {DEMON4PIC,20,&T_TrollDemon,&s_demon1};
+
+statetype s_demonattack1 = {DEMONATTACK1PIC,20,NULL,&s_demonattack2};
+statetype s_demonattack2 = {DEMONATTACK2PIC,20,NULL,&s_demonattack3};
+statetype s_demonattack3 = {DEMONATTACK3PIC,30,&T_DoDamage,&s_demonpause};
+
+statetype s_demonouch = {DEMONOUCHPIC,15,&T_TrollDemon,&s_demon1};
+
+statetype s_demondie1 = {DEMONDIE1PIC,40,NULL,&s_demondie2};
+statetype s_demondie2 = {DEMONDIE2PIC,30,&LargeSound,&s_demondie3};
+statetype s_demondie3 = {DEMONDIE3PIC,0,NULL,&s_demondie3};
+
+
+
+/*
+===============
+=
+= SpawnDemon
+=
+===============
+*/
+
+void SpawnDemon (int tilex, int tiley)
+{
+	SpawnNewObj(tilex,tiley,&s_demon1,PIXRADIUS*35);
+	new->obclass = demonobj;
+	new->speed = 2048;
+	new->flags |= of_shootable;
+	new->hitpoints = EasyHitPoints(30);
+}
+
+
+/*
+=============================================================================
+
+										TROLL
+
+=============================================================================
+*/
+
+statetype s_trollpause = {TROLL1PIC, 30, &T_DoDamage, &s_troll2};
+
+statetype s_troll1 = {TROLL1PIC, 13, &T_TrollDemon, &s_troll2};
+statetype s_troll2 = {TROLL2PIC, 13, &T_TrollDemon, &s_troll3};
+statetype s_troll3 = {TROLL3PIC, 13, &T_TrollDemon, &s_troll4};
+statetype s_troll4 = {TROLL4PIC, 13, &T_TrollDemon, &s_troll1};
+
+statetype s_trollattack1 = {TROLLATTACK1PIC, 15, NULL, &s_trollattack2};
+statetype s_trollattack2 = {TROLLATTACK2PIC, 20, NULL, &s_trollpause};
+
+statetype s_trollouch = {TROLLOUCHPIC, 14, &T_TrollDemon, &s_troll1};
+
+statetype s_trolldie1 = {TROLLDIE1PIC, 18, NULL, &s_trolldie2};
+statetype s_trolldie2 = {TROLLDIE2PIC, 15, &LargeSound, &s_trolldie3};
+statetype s_trolldie3 = {TROLLDIE3PIC, 0, NULL, &s_trolldie3};
+
+
+/*
+===============
+=
+= SpawnTroll
+=
+===============
+*/
+
+void SpawnTroll (int tilex, int tiley)
+{
+	SpawnNewObj(tilex,tiley,&s_troll1,35*PIXRADIUS);
+	new->speed = 2500;
+	new->obclass = trollobj;
+	new->flags |= of_shootable;
+	new->hitpoints = EasyHitPoints(15);
+}
+
+
+/*
+=============================================================================
+
+										CYBORG DEMON
+
+=============================================================================
+*/
+
+void T_Demon (objtype *ob);
+
+statetype s_cyborg_demon1 = {CYBORG1PIC, 20, T_TrollDemon, &s_cyborg_demon2};
+statetype s_cyborg_demon2 = {CYBORG2PIC, 20, T_TrollDemon, &s_cyborg_demon3};
+statetype s_cyborg_demon3 = {CYBORG3PIC, 20, T_TrollDemon, &s_cyborg_demon4};
+statetype s_cyborg_demon4 = {CYBORG4PIC, 20, T_TrollDemon, &s_cyborg_demon1};
+
+statetype s_cyborg_demonattack1 = {CYBORGATTACK1PIC, 20, NULL, &s_cyborg_demonattack2};
+statetype s_cyborg_demonattack2 = {CYBORGATTACK2PIC, 20, NULL, &s_cyborg_demonattack3};
+statetype s_cyborg_demonattack3 = {CYBORGATTACK3PIC, 30, T_DoDamage, &s_cyborg_demon2};
+
+statetype s_cyborg_demonouch = {CYBORGOUCHPIC, 30, NULL, &s_cyborg_demon1};
+
+statetype s_cyborg_demondie1 = {CYBORGOUCHPIC, 40, NULL, &s_cyborg_demondie2};
+statetype s_cyborg_demondie2 = {CYBORGDIE1PIC, 30, &LargeSound, &s_cyborg_demondie3};
+statetype s_cyborg_demondie3 = {CYBORGDIE2PIC, 20, NULL, &s_cyborg_demondie3};
+
+/*
+===============
+=
+= SpawnCyborgDemon
+=
+===============
+*/
+
+void SpawnCyborgDemon (int tilex, int tiley)
+{
+	SpawnNewObj(tilex, tiley, &s_cyborg_demon1, PIXRADIUS*35);
+	new->obclass	= cyborgdemonobj;
+	new->speed = 2048;
+	new->flags |= of_shootable;
+	new->hitpoints = EasyHitPoints(30);
+}
+
+
+/*
+===============
+=
+= T_TrollDemon
+=
+===============
+*/
+
+void T_TrollDemon (objtype *ob)
+{
+	if (Chase (ob,true) || (random(1000)<RANDOM_ATTACK))
+	{
+		if (ob->obclass == cyborgdemonobj)
+			ob->state = &s_cyborg_demonattack1;
+		else
+			if (ob->obclass == trollobj)
+				ob->state = &s_trollattack1;
+			else
+				ob->state = &s_demonattack1;
+		ob->ticcount = ob->state->tictime;
+	}
+}
+
+
+
+
+
+/*
+=============================================================================
+
+										INVISIBLE DUDE!
+
+=============================================================================
+*/
+
+void T_InvisibleDude (objtype *ob);
+
+statetype s_invis_fizz1 = {INVIS_FIZZ1PIC, 8, &T_InvisibleDude, &s_invis_fizz2};
+statetype s_invis_fizz2 = {INVIS_FIZZ2PIC, 8, &T_InvisibleDude, &s_invis_fizz3};
+statetype s_invis_fizz3 = {INVIS_FIZZ3PIC, 8, &T_InvisibleDude, &s_invis_walk};
+
+statetype s_invis_walk = {0, 25, &T_InvisibleDude, &s_invis_walk};
+statetype s_invis_attack = {0, -1, &T_DoDamage, &s_invis_pause};
+statetype s_invis_pause = {0, 40, NULL, &s_invis_walk};
+
+statetype s_invis_flash1 = {INVIS_FIZZ1PIC, 8, &T_InvisibleDude, &s_invis_walk};
+statetype s_invis_flash2 = {INVIS_FIZZ2PIC, 8, &T_InvisibleDude, &s_invis_walk};
+statetype s_invis_flash3 = {INVIS_FIZZ3PIC, 8, &T_InvisibleDude, &s_invis_walk};
+
+statetype s_invis_death1 = {INVIS_DEATH1PIC, 40, NULL, &s_invis_death2};
+statetype s_invis_death2 = {INVIS_DEATH2PIC, 30, &LargeSound, &s_invis_death3};
+statetype s_invis_death3 = {INVIS_DEATH3PIC, 20, NULL, &s_invis_death3};
+
+/*
+===============
+=
+= SpawnInvisDude
+=
+===============
+*/
+void SpawnInvisDude(int tilex, int tiley)
+{
+	SpawnNewObj(tilex, tiley, &s_invis_walk, PIXRADIUS*20);
+	new->obclass	= invisdudeobj;
+	new->speed		= 2048;
+	new->flags		|= of_shootable;
+	new->hitpoints	= EasyHitPoints(20);
+	new->temp1		= 0;		// for random flashing of pictures
+}
+
+
+/*
+===============
+=
+= T_InvisibleDude
+=
+===============
+*/
+void T_InvisibleDude (objtype *ob)
+{
+	if (!random(100))
+	{
+		switch (ob->temp1++)
+		{
+			case 0:
+				ob->state = &s_invis_flash1;
+			break;
+
+			case 1:
+				ob->state = &s_invis_flash2;
+			break;
+
+			case 2:
+				ob->state = &s_invis_flash3;
+				ob->temp1 = 0;
+			break;
+		}
+		ob->ticcount = ob->state->tictime;
+	}
+
+
+	if (Chase (ob,true))
+	{
+		ob->state = &s_invis_attack;
+		ob->ticcount = ob->state->tictime;
+	}
+
+}
+
+
+
+/*
+=============================================================================
+
+											BOUNCE
+
+temp2 = set when hit player, reset when hit wall
+
+=============================================================================
+*/
+
+#define SPDBOUNCE	4096
+#define DMGBOUNCE	10
+
+void T_Bounce (objtype *ob);
+void T_Bounce_Death (objtype *ob);
+
+statetype s_bounce1 = {PSHOT1PIC, 8, &T_Bounce, &s_bounce2};
+statetype s_bounce2 = {PSHOT2PIC, 8, &T_Bounce, &s_bounce1};
+
+/*
+===============
+=
+= SpawnBounce
+=
+===============
+*/
+
+void SpawnBounce (int tilex, int tiley, boolean towest)
+{
+	SpawnNewObj(tilex, tiley, &s_bounce1, 24*PIXRADIUS);
+	new->obclass = bounceobj;
+	new->hitpoints = EasyHitPoints(10);
+	new->flags |= of_shootable;
+	if (towest)
+		new->dir = west;
+	else
+		new->dir = north;
+}
+
+
+/*
+===============
+=
+= T_Bounce
+=
+===============
+*/
+
+void T_Bounce (objtype *ob)
+{
+	long move;
+	long deltax,deltay,size;
+
+	move = SPDBOUNCE*tics;
+	size = (long)ob->size + player->size + move;
+
+	while (move)
+	{
+		deltax = ob->x - player->x;
+		deltay = ob->y - player->y;
+
+		if (deltax <= size && deltax >= -size
+		&& deltay <= size && deltay >= -size && !ob->temp2)
+		{
+			ob->temp2 = 1;
+			TakeDamage (DMGBOUNCE);
+		}
+
+		if (move < ob->distance)
+		{
+			MoveObj (ob,move);
+			break;
+		}
+		actorat[ob->tilex][ob->tiley] = 0;	// pick up marker from goal
+
+		ob->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;
+		ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;
+		move -= ob->distance;
+
+		//
+		// bounce if hit wall
+		//
+		switch (ob->dir)
+		{
+		case north:
+			if (tilemap[ob->tilex][--ob->tiley])
+			{
+				ob->dir = south;
+				ob->tiley+=2;
+				ob->temp2 = 0;
+			}
+			break;
+		case east:
+			if (tilemap[++ob->tilex][ob->tiley])
+			{
+				ob->dir = west;
+				ob->tilex-=2;
+				ob->temp2 = 0;
+			}
+			break;
+		case south:
+			if (tilemap[ob->tilex][++ob->tiley])
+			{
+				ob->dir = north;
+				ob->tiley-=2;
+				ob->temp2 = 0;
+			}
+			break;
+		case west:
+			if (tilemap[--ob->tilex][ob->tiley])
+			{
+				ob->dir = east;
+				ob->tilex+=2;
+				ob->temp2 = 0;
+			}
+			break;
+		}
+
+		ob->distance = TILEGLOBAL;
+
+		actorat[ob->tilex][ob->tiley] = ob;	// set down a new goal marker
+	}
+	CalcBounds (ob);
+}
+
+
+/*
+=============================================================================
+
+							GRELMINAR
+
+=============================================================================
+*/
+
+
+void T_Grelminar (objtype *ob);
+void T_GrelminarShoot (objtype *ob);
+void T_Grelm_DropKey(objtype *ob);
+
+statetype s_grelpause = {GREL1PIC,50,NULL,&s_grel2};
+
+statetype s_grel1 = {GREL1PIC,20,T_Grelminar,&s_grel2};
+statetype s_grel2 = {GREL2PIC,20,T_Grelminar,&s_grel1};
+
+statetype s_grelattack3 = {GRELATTACKPIC,30,NULL,&s_grelpause};
+
+statetype s_grelouch = {GRELHITPIC,6,NULL,&s_grel1};
+
+statetype s_greldie1 = {GRELDIE1PIC,22,NULL,&s_greldie2};
+statetype s_greldie2 = {GRELDIE2PIC,22,NULL,&s_greldie3};
+statetype s_greldie3 = {GRELDIE3PIC,22,NULL,&s_greldie4};
+statetype s_greldie4 = {GRELDIE4PIC,22,NULL,&s_greldie5};
+statetype s_greldie5 = {GRELDIE5PIC,22,NULL,&s_greldie5a};
+statetype s_greldie5a = {GRELDIE5PIC,-1,T_Grelm_DropKey,&s_greldie6};
+statetype s_greldie6 = {GRELDIE6PIC,0,NULL,&s_greldie6};
+
+statetype s_gshot1 = {SKULL_SHOTPIC,8,T_ShootPlayer,&s_gshot1};
+
+/*
+===============
+=
+= SpawnGrelminar
+=
+===============
+*/
+
+void SpawnGrelminar (int tilex, int tiley)
+{
+	unsigned Grel_Hard;
+	unsigned DropKey;
+
+	SpawnNewObj(tilex,tiley,&s_grel1,PIXRADIUS*25);
+	new->obclass = grelmobj;
+	new->speed = 2048;
+	new->flags |= of_shootable;
+
+	//
+	// if Grelminar is to drop a key the info-plane byte to the right
+	//		should have a 1 in the highbyte, else he will not drop the key.
+	//
+	DropKey = *(mapsegs[2]+farmapylookup[tiley]+tilex+1);
+	if (DropKey)
+		new->temp1 = DropKey>>8;
+	else
+		new->temp1 = 0;
+
+	//
+	// The info-plane byte below Grelminar will determine how powerful
+	//		Grelminar is.  If nothing is there, he is the most powerful.
+	//			-- affected are the hit points and the shot damage.
+	//	The hit points are controlled here, the shot damage is controlled
+	// 	within the spawning of the shot.  See ShootPlayer for more info.
+	//
+	Grel_Hard = *(mapsegs[2]+farmapylookup[tiley+1]+tilex);
+	if (Grel_Hard)
+	{
+		new->temp2 = Grel_Hard>>8;
+		new->hitpoints = EasyHitPoints((new->temp2 * 10));
+	}
+	else
+	{
+		new->hitpoints = EasyHitPoints(100);
+		new->temp2 = 10;
+	}
+}
+
+
+/*
+===============
+=
+= T_Grelminar
+=
+===============
+*/
+
+void T_Grelminar (objtype *ob)
+{
+	Chase (ob,false);
+
+	if (!random(10))
+		if (ShootPlayer(ob,gshotobj,ob->temp2,&s_gshot1))
+		{
+			ob->state = &s_grelattack3;
+			ob->ticcount = ob->state->tictime;
+		}
+	if (CheckHandAttack(ob))
+		TakeDamage (ob->temp2*3);
+
+}
+
+
+//=================================
+//
+// T_Grelm_DropKey
+//
+//=================================
+void T_Grelm_DropKey(objtype *ob)
+{
+	if (!(ob->temp1))
+	{
+		ob->state = NULL;
+		return;
+	}
+
+	SpawnBonus(ob->tilex,ob->tiley,B_RKEY);
+	SD_PlaySound(GRELM_DEADSND);
+	ob->temp1 = false;
+}
+
+
+
+//--------------------------------------------------------------------------
+// ShootPlayer()
+//--------------------------------------------------------------------------
+boolean ShootPlayer(objtype *ob, short obclass, short speed, statetype *state)
+{
+	int angle = AngleNearPlayer(ob);
+
+	if (angle == -1)
+		return(false);
+
+	DSpawnNewObjFrac (ob->x,ob->y,state,PIXRADIUS*14);
+	new->obclass = obclass;
+	new->active = always;
+	new->angle = angle;
+
+	//
+	//	If the shot is Grelminar's, then determine the power of the shot.
+	//	The shot speed is hard-wired as 10000.  But the shot power is
+	//		determined by speed.  Speed now contains "Grelminar's level of
+	//		hardness" and this is multiplied by 3 to get the shot power.
+	//
+	if (obclass == gshotobj)
+	{
+		new->speed = 10000;
+		new->temp1 = speed*3;
+	}
+	else
+		new->speed = speed;
+
+
+	return(true);
+}
+
+//--------------------------------------------------------------------------
+// T_ShootPlayer()
+//--------------------------------------------------------------------------
+void T_ShootPlayer(objtype *ob)
+{
+	objtype *check;
+	long xmove,ymove,speed;
+
+	speed = ob->speed*tics;
+
+	xmove = FixedByFrac(speed,costable[ob->angle]);
+	ymove = -FixedByFrac(speed,sintable[ob->angle]);
+
+	if (ShotClipMove(ob,xmove,ymove))
+	{
+		ob->state = &s_pshot_exp1;
+		ob->ticcount = ob->state->tictime;
+		return;
+	}
+
+	ob->tilex = ob->x >> TILESHIFT;
+	ob->tiley = ob->y >> TILESHIFT;
+
+
+// check for collision with wall
+//
+	if (tilemap[ob->tilex][ob->tiley])
+	{
+//		SD_PlaySound (SHOOTWALLSND);
+		ob->state = &s_pshot_exp1;
+		ob->ticcount = s_pshot_exp1.tictime;
+		return;
+	}
+
+
+
+// check for collision with player
+//
+	if ( ob->xl <= player->xh
+	&& ob->xh >= player->xl
+	&& ob->yl <= player->yh
+	&& ob->yh >= player->yl)
+	{
+		switch (ob->obclass)
+		{
+			case wshotobj:						// Wizard's shot
+				TakeDamage (7);
+			break;
+
+			case hshotobj:						// Egyptian Head's shot
+				TakeDamage (5);
+			break;
+
+			case bshotobj:						// Blob's shot
+				TakeDamage (5);
+			break;
+
+			case rshotobj:						// Ray's shot
+				TakeDamage (5);
+			break;
+
+			case rbshotobj:					// RamBone's shot
+				TakeDamage(7);
+			break;
+
+			case fmshotobj:					// Future Mage's shot
+				TakeDamage(7);
+			break;
+
+			case rtshotobj:					// RoboTank's shot
+				TakeDamage(15);
+			break;
+
+			case syshotobj:					// Stompy's shot
+				TakeDamage(7);
+			break;
+
+			case bgshotobj:					// Bug's shot
+				TakeDamage(7);
+			break;
+
+			case eshotobj:						// Eye's shot
+				TakeDamage(5);
+			break;
+
+			case gshotobj:
+				TakeDamage (ob->temp1);		// the damage of Grelminar's shot -
+			break;								//   see Grelminar's spawning
+
+		}
+		ob->state = NULL;
+		return;
+	}
+
+// check for collision with other solid and realsolid objects.
+//  Great terminology!! -- solid objects really aren't solid
+//                      -- realsolid objects ARE solid
+//	if ((actorat[ob->tilex][ob->tiley]) && (actorat[ob->tilex][ob->tiley]->obclass != ob->obclass))
+	if (((actorat[ob->tilex][ob->tiley]->obclass == realsolidobj) ||
+		 (actorat[ob->tilex][ob->tiley]->obclass == solidobj)) &&
+		 (actorat[ob->tilex][ob->tiley]->flags & of_shootable))
+	{
+			ob->state = &s_pshot_exp1;
+			ob->ticcount = s_pshot_exp1.tictime;
+			return;
+	}
+
+
+// check for collision with player
+//
+	for (check = player->next; check; check=check->next)
+		if ((ob->flags & of_shootable)
+		&& ob->xl <= check->xh
+		&& ob->xh >= check->xl
+		&& ob->yl <= check->yh
+		&& ob->yh >= check->yl)
+		{
+			switch (ob->obclass)
+			{
+// APOCALYPSE
+				case wshotobj:						// Wizard's shot
+					ShootActor (check, 3);
+				break;
+
+				case hshotobj:						// Egyptian Head's shot
+					ShootActor (check, 5);
+				break;
+
+				case bshotobj:						// Blob's shot
+					ShootActor (check, 2);
+				break;
+
+				case rshotobj:						// Ray's shot
+					ShootActor (check, 5);
+				break;
+
+				case rbshotobj:					// RamBone's shot
+					ShootActor (check, 5);
+				break;
+
+				case fmshotobj:					// Future Mage's shot
+					ShootActor (check, 5);
+				break;
+
+				case rtshotobj:					// RoboTank's shot
+					ShootActor (check, 15);
+				break;
+
+				case syshotobj:					// Stompy's shot
+					ShootActor (check, 5);
+				break;
+
+				case bgshotobj:					// Bug's shot
+					ShootActor (check, 3);
+				break;
+
+				case eshotobj:						// Eye's shot
+					ShootActor (check, 2);
+				break;
+
+				case gshotobj:
+					ShootActor (check,25);		//NOLAN--check on me!!!!!!!
+				break;
+
+				case pshotobj:
+					ShootActor (check,25);
+				break;
+
+			}
+			ob->state = &s_pshot_exp1;
+			ob->ticcount = s_pshot_exp1.tictime;
+			return;
+		}
+}
+
+//-------------------------------------------------------------------------
+// AngleNearPlayer()
+//-------------------------------------------------------------------------
+int AngleNearPlayer(objtype *ob)
+{
+	int angle=-1;
+	int xdiff = ob->tilex-player->tilex;
+	int ydiff = ob->tiley-player->tiley;
+
+	if (ob->tiley == player->tiley)
+	{
+		if (ob->tilex < player->tilex)
+			angle = 0;
+		else
+			angle = 180;
+	}
+	else
+	if (ob->tilex == player->tilex)
+	{
+		if (ob->tiley < player->tiley)
+			angle = 270;
+		else
+			angle = 90;
+	}
+	else
+	if (xdiff == ydiff)
+		if (ob->tilex < player->tilex)
+		{
+			if (ob->tiley < player->tiley)
+				angle = 315;
+			else
+				angle = 45;
+		}
+		else
+		{
+			if (ob->tiley < player->tiley)
+				angle = 225;
+			else
+				angle = 135;
+		}
+
+	return(angle);
+}
+
+
diff --git a/src/lib/hb/c6_act4.c b/src/lib/hb/c6_act4.c
new file mode 100755
index 00000000..ced11788
--- /dev/null
+++ b/src/lib/hb/c6_act4.c
@@ -0,0 +1,260 @@
+/* Catacomb Apocalypse Source Code
+ * Copyright (C) 1993-2014 Flat Rock Software
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// C4_PLAY.C
+
+#include "DEF.H"
+#pragma hdrstop
+
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+
+//-------------------------------------------------------------------------
+//
+//				MISC OBJECTS
+//
+//-------------------------------------------------------------------------
+
+
+//-------------------------------------------------------------------------
+//		COLUMN, SULPHUR GAS HOLE, FIRE POT, FOUNTAIN
+//-------------------------------------------------------------------------
+
+
+void SpawnMiscObjects(int tilex, int tiley, int num);
+
+statetype s_column1 = {COLUMN1PIC, 20, NULL, &s_column1};
+statetype s_column2 = {COLUMN2PIC, 20, NULL, &s_column2};
+statetype s_column3 = {COLUMN3PIC, 20, NULL, &s_column3};
+statetype s_column4 = {COLUMN4PIC, 20, NULL, &s_column4};
+statetype s_column5 = {COLUMN5PIC, 20, NULL, &s_column5};
+statetype s_ffire_pot = {FFIRE_POTPIC, 20, NULL, &s_ffire_pot};
+statetype s_ofire_pot1 = {OFIRE_POT1PIC, 20, NULL, &s_ofire_pot2};
+statetype s_ofire_pot2 = {OFIRE_POT2PIC, 20, NULL, &s_ofire_pot1};
+statetype s_tomb1 = {TOMB1PIC, 20, NULL, &s_tomb1};
+statetype s_tomb2 = {TOMB2PIC, 20, NULL, &s_tomb2};
+void SpawnMiscObjects(int tilex, int tiley, int num)
+{
+	statetype	*objstate;
+
+	switch (num)
+	{
+		case 1:
+			objstate = &s_column1;
+		break;
+
+		case 2:
+			objstate = &s_column2;
+		break;
+
+		case 3:
+			objstate = &s_column3;
+		break;
+
+		case 4:
+			objstate = &s_ffire_pot;
+		break;
+
+		case 5:
+			objstate = &s_column4;
+		break;
+
+		case 6:
+			objstate = &s_ofire_pot1;
+		break;
+
+		case 7:
+			objstate = &s_tomb1;
+		break;
+
+		case 8:
+			objstate = &s_tomb2;
+		break;
+
+		case 9:
+			objstate = &s_column5;
+		break;
+	}
+
+	SpawnNewObj(tilex, tiley, objstate, PIXRADIUS*10);
+	new->obclass = realsolidobj;
+	new->flags |= of_shootable;
+}
+
+
+
+//------------------------------------------------------------------------
+//				FORCE FIELD
+//------------------------------------------------------------------------
+
+void SpawnForceField(int tilex, int tiley);
+void T_ForceField(objtype *ob);
+void T_ForceFieldRemove(objtype *ob);
+
+statetype s_force_field_1 = {FORCE_FIELD_1PIC, 10, T_ForceField, &s_force_field_2};
+statetype s_force_field_2 = {FORCE_FIELD_2PIC, 10, T_ForceField, &s_force_field_3};
+statetype s_force_field_3 = {FORCE_FIELD_3PIC, 10, T_ForceField, &s_force_field_4};
+statetype s_force_field_4 = {FORCE_FIELD_4PIC, 10, T_ForceField, &s_force_field_1};
+
+statetype s_force_field_die = {0,0,T_ForceFieldRemove,&s_force_field_die1};
+statetype s_force_field_die1 = {0,0,NULL,NULL};
+
+void SpawnForceField(int tilex, int tiley)
+{
+	SpawnNewObj(tilex,tiley,&s_force_field_1,PIXRADIUS*35);
+	new->obclass = solidobj;
+	new->hitpoints = EasyHitPoints(20);
+	new->temp1 = 0;
+	new->flags |= of_forcefield;		//sets bit 7 :: makes it nonsolid, but also detectable
+						//		without adding another object type!
+	new->flags |= of_shootable;
+}
+
+void T_ForceField(objtype *ob)
+{
+	long move,deltax,deltay,size;
+
+	size = (long)ob->size + player->size;
+
+	deltax = ob->x - player->x;
+	deltay = ob->y - player->y;
+
+	if (deltax <= size && deltax >= -size
+	&& deltay <= size && deltay >= -size)
+		if (!new->temp1)
+		{
+			TakeDamage (94);
+			new->temp1 = 1;
+			return;
+		}
+		else
+			return;
+	new->temp1 = 0;
+}
+
+void T_ForceFieldRemove(objtype *ob)
+{
+	actorat[ob->tilex][ob->tiley] = 0;
+}
+
+
+
+
+//-------------------------------------------------------------------------
+//
+//								INVISIBLE WALL CONTROLLER
+//
+//-------------------------------------------------------------------------
+
+void SpawnInvisWallCntroller(int x, int y);
+void T_InvisWall(objtype *ob);
+
+extern statetype s_invis_wall_control;
+
+statetype s_invis_wall_control = {0, 10, T_InvisWall, &s_invis_wall_control};
+
+void SpawnInvisWallCntroller(int tilex, int tiley)
+{
+	SpawnNewObj(tilex,tiley,&s_invis_wall_control,PIXRADIUS*35);
+	new->obclass = solidobj;
+	new->flags &= ~of_shootable;
+	new->temp1 = tilemap[tilex][tiley];		// Number for the wall tile here
+														// Used for replacing the wall tile
+}
+
+void T_InvisWall(objtype *ob)
+{
+	long move,deltax,deltay,size;
+
+	size = (long)ob->size + player->size;
+
+	deltax = ob->x - player->x;
+	deltay = ob->y - player->y;
+
+	if ((deltax <= size && deltax >= -size
+		&& deltay <= size && deltay >= -size) ||
+		(ob->tilex == player->tilex) && (ob->tiley == player->tiley))
+	{
+	  // Get rid of the wall tile if you are on it
+		tilemap[ob->tilex][ob->tiley] = 0;
+	}
+	else
+	{
+	  // Replace wall tile
+		tilemap[ob->tilex][ob->tiley] = ob->temp1;
+	}
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+//	EasyHitPoints
+//
+//	Checks to see if the player has selected the easy mode for playing.
+//	If so then the normal hit points are cut in half.
+//	This is when the object is spawned.
+//
+//	Parms
+//		NrmHitPts - the normal hit points
+//
+//	Returns
+//		Half of NrmHitPts
+//
+/////////////////////////////////////////////////////////////////////////////
+
+int EasyHitPoints(int NrmHitPts)
+{
+	if (EASYMODEON)				// Wimpy, Wimpy, Wimpy!!!!!
+	{
+		return(NrmHitPts/4);
+	}
+	else
+		return(NrmHitPts);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+//	EasyDoDamage
+//
+//	Checks to see if the player has selected the easy mode for playing.
+//	If so then the normal amount of damage is cut in half.
+//	This is called each time a monster does damage.
+//
+//	Parms
+//		Damage - the normal damage taken
+//
+//	Returns
+//		Half of Damage
+//
+/////////////////////////////////////////////////////////////////////////////
+
+int EasyDoDamage(int Damage)
+{
+	if (EASYMODEON)				// Wimpy, Wimpy, Wimpy!!!!!
+		return(Damage/2);
+	else
+		return(Damage);
+}
+
diff --git a/src/lib/hb/c6_asm.asm b/src/lib/hb/c6_asm.asm
new file mode 100755
index 00000000..004c20ae
--- /dev/null
+++ b/src/lib/hb/c6_asm.asm
@@ -0,0 +1,248 @@
+; Catacomb Apocalypse Source Code
+; Copyright (C) 1993-2014 Flat Rock Software
+;
+; 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.,
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+IDEAL
+
+MODEL	MEDIUM,C
+
+INCLUDE	"ID_ASM.EQU"
+
+VIEWWIDTH	=	(40*8)			;33
+GC_INDEX	=	03CEh
+
+DATASEG
+EVEN
+
+;=================== Tables filled in by DrawVWall ==========================
+
+;
+; wallheight has the height (scale number) of that collumn of scaled wall
+; it is pre bounded to 1-MAXSCALE (the actuial height on screen is 2*height)
+;
+wallheight	dw	VIEWWIDTH dup (?)
+
+;
+; wallwidth has the pixel width (1-7) of that collumn
+;
+wallwidth	dw	VIEWWIDTH dup (?)
+
+;
+; wallseg has the segment of the wall picture
+;
+wallseg		dw	VIEWWIDTH dup (?)
+
+;
+; wallofs has the offset of the wall picture
+;
+wallofs		dw	VIEWWIDTH dup (?)
+
+;============================================================================
+
+;
+; screenbyte is just position/8
+;
+LABEL		screenbyte	WORD
+pos	=	0
+REPT		VIEWWIDTH
+			dw	pos/8
+pos	=	pos+1
+ENDM
+
+;
+; screenbit is (position&7)*16
+;
+LABEL		screenbit	WORD
+pos	=	0
+REPT		VIEWWIDTH
+			dw	(pos AND 7)*16
+pos	=	pos+1
+ENDM
+
+;
+; Use offset: screenbit[]+pixwidth*2
+; acess from bitmasks-2+offset for one biased pixwidth
+; the low byte of bitmasks is for the first screen byte, the high byte
+; is the bitmask for the second screen byte (if non 0)
+;
+
+bitmasks	dw	0080h,00c0h,00e0h,00f0h,00f8h,00fch,00feh,00ffh
+			dw	0040h,0060h,0070h,0078h,007ch,007eh,007fh,807fh
+			dw	0020h,0030h,0038h,003ch,003eh,003fh,803fh,0c03fh
+			dw	0010h,0018h,001ch,001eh,001fh,801fh,0c01fh,0e01fh
+			dw	0008h,000ch,000eh,000fh,800fh,0c00fh,0e00fh,0f00fh
+			dw	0004h,0006h,0007h,8007h,0c007h,0e007h,0f007h,0f807h
+			dw	0002h,0003h,8003h,0c003h,0e003h,0f003h,0f803h,0fc03h
+			dw	0001h,8001h,0c001h,0e001h,0f001h,0f801h,0fc01h,0fe01h
+
+
+;
+; wallscalecall is a far pointer to the start of a compiled scaler
+; The low word will never change, while the high word is set to
+; compscaledirectory[scale]
+;
+wallscalecall	dd	(65*6)			; offset of t_compscale->code[0]
+
+
+PUBLIC	wallheight,wallwidth,wallseg,wallofs,screenbyte,screenbit
+PUBLIC	bitmasks,wallscalecall
+
+
+EXTRN	scaledirectory:WORD			; array of MAXSCALE segment pointers to
+									; compiled scalers
+EXTRN	screenseg:WORD				; basically just 0xa000
+EXTRN	bufferofs:WORD				; offset of the current work screen
+EXTRN ylookup:WORD
+EXTRN screenpage:WORD
+
+CODESEG
+
+;============================================================================
+;
+; ScaleWalls
+;
+; AX	AL is scratched in bit mask setting and scaling
+; BX	table index
+; CX	pixwidth*2
+; DX	GC_INDEX
+; SI	offset into wall data to scale from, allways 0,64,128,...4032
+; DI    byte at top of screen that the collumn is contained in
+; BP	x pixel * 2, index into VIEWWIDTH wide tables
+; DS	segment of the wall data to texture map
+; ES	screenseg
+; SS	addressing DGROUP variables
+;
+;============================================================================
+
+PROC	ScaleWalls
+PUBLIC	ScaleWalls
+USES	SI,DI,BP
+
+	xor	bp,bp						; start at location 0 in the tables
+	mov	dx,GC_INDEX+1
+	mov	es,[screenseg]
+
+;
+; scale one collumn of data, possibly across two bytes
+;
+nextcollumn:
+
+	mov	bx,[wallheight+bp]			; height of walls (1-MAXSCALE)
+	shl	bx,1
+	mov	ax,[ss:scaledirectory+bx]	; segment of the compiled scaler
+	mov [WORD PTR ss:wallscalecall+2],ax
+
+	mov	cx,[wallwidth+bp]
+	or	cx,cx
+	jnz	okwidth
+	mov	cx,2
+	jmp	next
+
+okwidth:
+	shl	cx,1
+	mov	ds,[wallseg+bp]
+	mov	si,[wallofs+bp]
+
+	mov	di,[screenbyte+bp]			; byte at the top of the scaled collumn
+	add	di,[ss:bufferofs]			; offset of current page flip
+	mov	bx,[screenbit+bp]			; 0-7 << 4
+	add	bx,cx
+	mov	ax,[ss:bitmasks-2+bx]
+	out	dx,al						; set bit mask register
+	call [DWORD PTR ss:wallscalecall]		; scale the line of pixels
+	or	ah,ah						; is there anything in the second byte?
+	jnz	secondbyte
+;
+; next
+;
+next:
+	add	bp,cx
+	cmp	bp,VIEWWIDTH*2
+	jb	nextcollumn
+	jmp	done
+
+;
+; draw a second byte for vertical strips that cross two bytes
+;
+secondbyte:
+	mov	al,ah
+	inc	di								; next byte over
+	out	dx,al							; set bit mask register
+	call [DWORD PTR ss:wallscalecall]	; scale the line of pixels
+;
+; next
+;
+	add	bp,cx
+	cmp	bp,VIEWWIDTH*2
+	jb	nextcollumn
+
+done:
+	mov	ax,ss
+	mov	ds,ax
+	ret
+
+ENDP
+
+;---------------------------------------------------------------------------
+;
+; RadarBlip()
+;
+; Displays a 'blip' (1 pixel wide X 2 pixel high) on the radar at
+; an (X,Y) relative to (RADAR_X,RADAR_Y) (defined below...)
+;
+;---------------------------------------------------------------------------
+
+PROC	RadarBlip x:WORD, y:WORD, color:WORD
+USES	SI,DI
+PUBLIC	RadarBlip
+
+	mov	ax,[screenseg]
+
+	mov	es,ax
+	xor	di,di
+
+	lea	si,[ylookup]
+	add	si,[y]
+	add	si,[y]
+	add	di,[si]
+
+	mov	ax,[x]
+	shr	ax,1
+	shr	ax,1
+	shr	ax,1
+	add	di,ax
+
+	mov	ax,[x]
+	and	ax,7
+	mov	cl,al
+	mov	ah,080h
+	shr	ah,cl
+	cli
+	mov	al,GC_BITMASK
+	mov	dx,GC_INDEX
+	out	dx,ax
+	sti
+
+	mov	ax,[color]
+	mov	ah,[es:di]						; read into latches
+	mov	[es:di],al						; write latches / color bit
+
+	ret
+
+ENDP
+
+END
+
diff --git a/src/lib/hb/c6_debug.c b/src/lib/hb/c6_debug.c
new file mode 100755
index 00000000..f3ce665a
--- /dev/null
+++ b/src/lib/hb/c6_debug.c
@@ -0,0 +1,799 @@
+/* Catacomb Apocalypse Source Code
+ * Copyright (C) 1993-2014 Flat Rock Software
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// C3_DEBUG.C
+
+#include "DEF.H"
+#include "gelib.h"
+#pragma hdrstop
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+#define DEBUG_OVERHEAD 0
+
+
+#define VIEWTILEX	20
+#define VIEWTILEY	(VIEWHEIGHT/16)
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+short colordelay=0;
+//boolean autofire=false;
+int	maporgx;
+int	maporgy;
+enum {mapview,tilemapview,actoratview,visview,mapseg2,lastview}	viewtype;
+
+void ViewMap (void);
+
+//===========================================================================
+
+#if 0
+/*
+================
+=
+= PicturePause
+=
+================
+*/
+
+void PicturePause (void)
+{
+	int	y;
+	unsigned	source;
+
+	source = displayofs+panadjust;
+
+//	VW_ColorBorder (15);
+	VW_SetLineWidth (40);
+	VW_SetScreen (0,0);
+
+	if (source<0x10000l-200*64)
+	{
+	//
+	// copy top line first
+	//
+		for (y=0;y<200;y++)
+			VW_ScreenToScreen (source+y*64,y*40,40,1);
+	}
+	else
+	{
+	//
+	// copy bottom line first
+	//
+		for (y=199;y>=0;y--)
+			VW_ScreenToScreen (source+y*64,y*40,40,1);
+	}
+
+	IN_Shutdown ();
+
+	VW_WaitVBL(70);
+	bioskey(0);
+	VW_WaitVBL(70);
+	Quit (NULL);
+}
+#endif
+
+
+//===========================================================================
+
+//===========================================================================
+
+#define	sc_1			0x02
+#define	sc_2			0x03
+#define	sc_3			0x04
+#define	sc_4			0x05
+#define	sc_5			0x06
+#define	sc_6			0x07
+#define	sc_7			0x08
+#define	sc_8			0x09
+#define	sc_9			0x0a
+#define	sc_0			0x0b
+
+
+
+/*
+================
+=
+= DebugKeys
+=
+================
+*/
+
+int DebugKeys (void)
+{
+	boolean esc;
+	int level,i;
+
+#if DEBUG_KEYS_AVAILABLE
+	if (Keyboard[sc_R])
+	{
+		CenterWindow (12,2);
+		if (autofire)
+		  US_PrintCentered ("Rapid-Fire OFF");
+		else
+		  US_PrintCentered ("Rapid-Fire ON");
+		VW_UpdateScreen();
+		IN_Ack();
+		autofire ^= 1;
+		return 1;
+	}
+#endif
+
+#if DEBUG_KEYS_AVAILABLE
+	if (Keyboard[sc_A])
+	{
+		char levelstr[50];
+		unsigned org_tile,org_mapon,msgnum;
+		boolean newmsg=true,newlevel=false;
+
+		VW_FixRefreshBuffer ();
+		CenterWindow (16,3);
+		US_Print("\n");
+		US_CPrint("Message Test");
+		VW_UpdateScreen();
+
+		org_mapon = mapon;
+		msgnum = (org_tile = *(mapsegs[0]+farmapylookup[player->tiley]+player->tilex))-NAMESTART;
+		while (1)
+		{
+	// Get outta' here
+	//
+			if (Keyboard[sc_Escape])
+			{
+				while (Keyboard[sc_Escape]);
+				break;
+			}
+
+	// Move to previous message
+	//
+			if (Keyboard[sc_UpArrow])
+			{
+				if (msgnum)
+				{
+					msgnum--;
+					newmsg = true;
+				}
+			}
+
+	// Move to next message
+	//
+			if (Keyboard[sc_DownArrow])
+			{
+				if (msgnum < 24)
+				{
+					msgnum++;
+					newmsg = true;
+				}
+			}
+
+	// Move to previous level
+	//
+			if (Keyboard[sc_LeftArrow])
+			{
+				if (mapon)
+				{
+					MM_SetPurge(&grsegs[LEVEL1TEXT+mapon],3);
+					mapon--;
+					newlevel = true;
+				}
+			}
+
+	// Move to next level
+	//
+			if (Keyboard[sc_RightArrow])
+			{
+				if (mapon < LASTMAP-1)
+				{
+					MM_SetPurge(&grsegs[LEVEL1TEXT+mapon],3);
+					mapon++;
+					newlevel = true;
+				}
+			}
+
+	// Load new level text
+	//
+			if (newlevel)
+			{
+				CA_CacheGrChunk(LEVEL1TEXT+mapon);
+				ScanText();
+				newmsg = true;
+				newlevel=false;
+			}
+
+	// Display new message text
+	//
+			if (newmsg)
+			{
+				*(mapsegs[0]+farmapylookup[player->tiley]+player->tilex) = msgnum+NAMESTART;
+				DrawText(true);
+				strcpy(levelstr,"Level: ");
+				itoa(mapon,levelstr+strlen(levelstr),10);
+				strcat(levelstr,"  Msg: ");
+				itoa(msgnum,levelstr+strlen(levelstr),10);
+				DisplaySMsg(levelstr,NULL);
+				newmsg = false;
+
+				if (Keyboard[sc_UpArrow] || Keyboard[sc_DownArrow] || Keyboard[sc_LeftArrow] || Keyboard[sc_RightArrow])
+					VW_WaitVBL(6);
+			}
+
+		}
+// Restore game
+//
+		MM_SetPurge(&grsegs[LEVEL1TEXT+mapon],3);
+		mapon = org_mapon;
+		CA_CacheGrChunk(LEVEL1TEXT+mapon);
+		ScanText();
+		*(mapsegs[0]+farmapylookup[player->tiley]+player->tilex) = org_tile;
+		DrawText(true);
+		status_flag = 0;
+	}
+
+
+	if (Keyboard[sc_V])
+	{
+		displayofs = bufferofs = screenloc[screenpage];
+		CenterWindow (16,4);
+		US_CPrint("\n"GAMENAME);
+		US_CPrint(VERSION);
+		US_CPrint(REVISION);
+		VW_UpdateScreen();
+		IN_Ack ();
+	}
+
+#endif
+	if (Keyboard[sc_Q])			// Q = Insta-Quit!
+		Quit("Insta-Quit!");
+#if 0
+	if (Keyboard[sc_Z])		// Z = freeze Time
+	{
+		if (FreezeTime)
+		  FreezeTime = 1;		// Allow refresh to dec to zero..
+		else
+			StopTime();
+
+		IN_Ack();
+		return 1;
+	}
+#endif
+
+
+//	if (Keyboard[sc_E])
+//		FaceDoor((player->x>>16l)+1,(player->y>>16l));
+//		FaceAngle(90);
+
+#if 0
+	if (Keyboard[sc_B])		// B = border color
+	{
+		CenterWindow(24,3);
+		PrintY+=6;
+		US_Print(" Border color (0-15):");
+		VW_UpdateScreen();
+		esc = !US_LineInput (px,py,str,NULL,true,2,0);
+		if (!esc)
+		{
+			level = atoi (str);
+			if (level>=0 && level<=15)
+				VW_ColorBorder (level);
+		}
+		return 1;
+	}
+#endif
+
+
+#if 1//DEBUG_KEYS_AVAILABLE
+	if (Keyboard[sc_O])
+	{
+		extern unsigned objectcount,latchmemavail;
+		unsigned unused,total;
+
+		CenterWindow (30,13);
+		US_Print ("Objects: ");
+		US_PrintUnsigned (objectcount);
+
+		US_Print("\n\nTics: ");
+		US_PrintUnsigned (tics);
+		US_Print("      Real Tics: ");
+		US_PrintUnsigned(realtics);
+
+		US_Print ("\n\n    Total Available: ");
+		US_PrintUnsigned (mminfo.mainmem/1024);
+		US_Print ("k\n        Mem In Use: ");
+		unused=MM_UnusedMemory()/1024;
+		US_PrintUnsigned (unused);
+		US_Print ("k\n Mem After Purge: ");
+		total=MM_TotalFree()/1024;
+		US_PrintUnsigned (total);
+		US_Print ("k (");
+		US_PrintUnsigned (total-unused);
+
+		US_Print (")\n\nLatch Mem Free: ");
+		US_PrintUnsigned (latchmemavail);
+		US_Print ("\n");
+		VW_UpdateScreen();
+		IN_Ack();
+	}
+
+	if (colordelay<1)
+	{
+		if (Keyboard[26])
+		{
+			extern unsigned *groundcolor,debug_gnd;
+
+			groundcolor = &debug_gnd;
+			debug_gnd += 0x0101;
+			if (debug_gnd == 0x1010)
+				debug_gnd = 0;
+			colordelay = 10;
+		}
+
+		if (Keyboard[27])
+		{
+			extern unsigned *skycolor,debug_sky;
+
+			skycolor = &debug_sky;
+			debug_sky += 0x0101;
+			if (debug_sky == 0x1010)
+				debug_sky = 0;
+			colordelay = 10;
+		}
+	}
+	else
+		colordelay -= realtics;
+#endif
+
+
+#if 0
+	if (Keyboard[sc_C])		// C = count objects
+	{
+		CountObjects();
+		return 1;
+	}
+
+
+	if (Keyboard[sc_D])		// D = start / end demo record
+	{
+		if (DemoMode == demo_Off)
+			StartDemoRecord ();
+		else if (DemoMode == demo_Record)
+		{
+			EndDemoRecord ();
+			playstate = ex_completed;
+		}
+		return 1;
+	}
+#endif
+
+#if 0
+	if (Keyboard[sc_E])		// E = quit level
+	{
+		if (tedlevel)
+			TEDDeath();
+		playstate = ex_warped;
+		gamestate.mapon++;
+	}
+#endif
+
+#if 0
+	if (Keyboard[sc_F])		// F = facing spot
+	{
+		CenterWindow (12,4);
+		US_Print ("X:");
+		US_PrintUnsigned (player->x);
+		US_Print ("Y:");
+		US_PrintUnsigned (player->y);
+		US_Print ("A:");
+		US_PrintUnsigned (player->angle);
+		VW_UpdateScreen();
+		IN_Ack();
+		return 1;
+	}
+#endif
+
+	if (Keyboard[sc_G])		// G = god mode
+	{
+		CenterWindow (12,2);
+		if (godmode)
+		  US_PrintCentered ("God mode OFF");
+		else
+		  US_PrintCentered ("God mode ON");
+		VW_UpdateScreen();
+		IN_Ack();
+		godmode ^= 1;
+		return 1;
+	}
+
+#if 0
+	if (Keyboard[sc_H])		// H = hurt self
+	{
+		TakeDamage (5);
+	}
+#endif
+
+	if (Keyboard[sc_I])			// I = item cheat
+	{
+		extern boolean redraw_gems;
+
+		CenterWindow (12,3);
+		US_PrintCentered ("Free items!");
+		VW_UpdateScreen();
+		for (i=0;i<4;i++)
+		{
+			GiveBolt ();
+			GiveNuke ();
+			GivePotion ();
+//			if (!gamestate.keys[i])
+				GiveKey (i);
+			gamestate.gems[i] = GEM_DELAY_TIME;
+		}
+		gamestate.gems[4] = GEM_DELAY_TIME;
+		redraw_gems = true;
+/////////		for (i=0;i<8;i++)
+/////////			GiveScroll (i,false);
+
+		IN_Ack ();
+		return 1;
+	}
+
+#if DEBUG_OVERHEAD
+	if (Keyboard[sc_Z])			// O is used elsewhere...
+	{
+		ViewMap();
+		return 1;
+	}
+#endif
+
+#if 0
+	if (Keyboard[sc_P])			// P = pause with no screen disruptioon
+	{
+		PicturePause ();
+		return 1;
+	}
+#endif
+
+#if 0
+	if (Keyboard[sc_S])	// S = slow motion
+	{
+		singlestep^=1;
+		CenterWindow (18,3);
+		if (singlestep)
+			US_PrintCentered ("Slow motion ON");
+		else
+			US_PrintCentered ("Slow motion OFF");
+		VW_UpdateScreen();
+		IN_Ack ();
+		return 1;
+	}
+#endif
+
+#if 0
+	if (Keyboard[sc_V])			// V = extra VBLs
+	{
+		CenterWindow(30,3);
+		PrintY+=6;
+		US_Print("  Add how many extra VBLs(0-8):");
+		VW_UpdateScreen();
+		esc = !US_LineInput (px,py,str,NULL,true,2,0);
+		if (!esc)
+		{
+			level = atoi (str);
+			if (level>=0 && level<=8)
+				extravbls = level;
+		}
+		return 1;
+	}
+#endif
+
+	if (Keyboard[sc_W])	// W = warp to level
+	{
+		CenterWindow(26,3);
+		PrintY+=6;
+		US_Print("  Warp to which level(0-17):");
+		VW_UpdateScreen();
+		esc = !US_LineInput (px,py,str,NULL,true,2,0);
+		if (!esc)
+		{
+			level = atoi (str);
+			if (level>=0 && level<=LASTMAP-1)
+			{
+				gamestate.mapon = level;
+				playstate = ex_warped;
+				lasttext = -1;
+			}
+		}
+		return 1;
+	}
+
+#if 0
+	if (Keyboard[sc_X])			// X = item cheat
+	{
+		CenterWindow (12,3);
+		US_PrintCentered ("Extra stuff!");
+		VW_UpdateScreen();
+		for (i=0;i<4;i++)
+		{
+			GiveBolt ();
+			GiveNuke ();
+			GivePotion ();
+		}
+		IN_Ack ();
+		return 1;
+	}
+#endif
+
+////////	if (LastScan >= sc_1 && LastScan <= sc_8)	// free scrolls
+////////	{
+////////		GiveScroll (LastScan-sc_1,false);
+////////		IN_ClearKeysDown ();
+////////	}
+
+	return 0;
+}
+
+
+#if DEBUG_OVERHEAD
+
+/*
+=====================
+=
+= LatchDrawChar
+=
+=====================
+*/
+
+void LatchDrawChar (unsigned x, unsigned y, unsigned picnum)
+{
+	unsigned	source, dest;
+
+	dest = bufferofs + ylookup[y]+x;
+	source = latchpics[0]+picnum*8;
+
+	EGAWRITEMODE(1);
+	EGAMAPMASK(15);
+
+asm	mov	bx,[linewidth]
+asm	dec	bx
+
+asm	mov	ax,[screenseg]
+asm	mov	es,ax
+asm	mov	ds,ax
+
+asm	mov	si,[source]
+asm	mov	di,[dest]
+
+asm	movsb
+asm	add	di,bx
+asm	movsb
+asm	add	di,bx
+asm	movsb
+asm	add	di,bx
+asm	movsb
+asm	add	di,bx
+asm	movsb
+asm	add	di,bx
+asm	movsb
+asm	add	di,bx
+asm	movsb
+asm	add	di,bx
+asm	movsb
+
+asm	mov	ax,ss
+asm	mov	ds,ax					// restore turbo's data segment
+
+	EGAWRITEMODE(0);
+}
+
+#endif
+
+
+#if DEBUG_OVERHEAD
+/*
+=====================
+=
+= LatchDrawTile
+=
+=====================
+*/
+
+void LatchDrawTile (unsigned x, unsigned y, unsigned picnum)
+{
+	unsigned	source, dest;
+
+	dest = bufferofs + ylookup[y]+x;
+	source = tileoffsets[picnum];
+
+	EGAWRITEMODE(1);
+	EGAMAPMASK(15);
+
+asm	mov	bx,[linewidth]
+asm	sub	bx,2
+
+asm	mov	ax,[screenseg]
+asm	mov	es,ax
+asm	mov	ds,ax
+
+asm	mov	si,[source]
+asm	mov	di,[dest]
+asm	mov	dx,16
+
+lineloop:
+asm	movsb
+asm	movsb
+asm	add	di,bx
+
+asm	dec	dx
+asm	jnz	lineloop
+
+asm	mov	ax,ss
+asm	mov	ds,ax					// restore turbo's data segment
+
+	EGAWRITEMODE(0);
+}
+#endif
+
+
+#if DEBUG_OVERHEAD
+/*
+===================
+=
+= OverheadRefresh
+=
+===================
+*/
+
+void OverheadRefresh (void)
+{
+	unsigned	x,y,endx,endy,sx,sy;
+	unsigned	tile;
+
+
+	if (++screenpage == 3)
+		screenpage = 0;
+
+	bufferofs = screenloc[screenpage];
+
+	endx = maporgx+VIEWTILEX;
+	endy = maporgy+VIEWTILEY;
+
+	for (y=maporgy;y<endy;y++)
+		for (x=maporgx;x<endx;x++)
+		{
+			sx = (x-maporgx)*2;
+			sy = (y-maporgy)*16;
+
+			switch (viewtype)
+			{
+			case mapview:
+				tile = *(mapsegs[0]+farmapylookup[y]+x);
+				break;
+
+			case tilemapview:
+				tile = tilemap[x][y];
+				break;
+
+			case actoratview:
+				tile = (unsigned)actorat[x][y];
+				break;
+
+			case visview:
+				tile = spotvis[x][y];
+				break;
+
+			case mapseg2:
+				tile = *(mapsegs[2]+farmapylookup[y]+x);
+				if (tile < 256)
+					tile = *(mapsegs[0]+farmapylookup[y]+x);
+			break;
+
+			}
+
+			if (tile<NUMTILE16)
+				LatchDrawTile(sx,sy,tile);
+			else
+			{
+				LatchDrawChar(sx,sy,NUMBERCHARS+((tile&0xf000)>>12));
+				LatchDrawChar(sx+1,sy,NUMBERCHARS+((tile&0x0f00)>>8));
+				LatchDrawChar(sx,sy+8,NUMBERCHARS+((tile&0x00f0)>>4));
+				LatchDrawChar(sx+1,sy+8,NUMBERCHARS+(tile&0x000f));
+			}
+		}
+
+	VW_SetScreen (bufferofs,0);
+	displayofs = bufferofs;
+}
+
+
+/*
+===================
+=
+= ViewMap
+=
+===================
+*/
+
+void ViewMap (void)
+{
+	boolean		button0held;
+
+	viewtype = actoratview;
+	button0held = false;
+
+
+	maporgx = player->tilex - VIEWTILEX/2;
+	if (maporgx<0)
+		maporgx = 0;
+	maporgy = player->tiley - VIEWTILEY/2;
+	if (maporgy<0)
+		maporgy = 0;
+
+	do
+	{
+//
+// let user pan around
+//
+		IN_ReadControl(0,&control);
+		if (control.xaxis == -1 && maporgx>0)
+			maporgx--;
+		if (control.xaxis == 1 && maporgx<mapwidth-VIEWTILEX)
+			maporgx++;
+		if (control.yaxis == -1 && maporgy>0)
+			maporgy--;
+		if (control.yaxis == 1 && maporgy<mapheight-VIEWTILEY)
+			maporgy++;
+
+		if (control.button0 && !button0held)
+		{
+			button0held = true;
+			viewtype++;
+			if (viewtype==lastview)
+				viewtype = mapview;
+		}
+		if (!control.button0)
+			button0held = false;
+
+
+		OverheadRefresh ();
+
+	} while (!Keyboard[sc_Escape]);
+
+	IN_ClearKeysDown ();
+	DrawPlayScreen ();
+}
+#endif
+
diff --git a/src/lib/hb/c6_draw.c b/src/lib/hb/c6_draw.c
new file mode 100755
index 00000000..9d261a04
--- /dev/null
+++ b/src/lib/hb/c6_draw.c
@@ -0,0 +1,1970 @@
+/* Catacomb Apocalypse Source Code
+ * Copyright (C) 1993-2014 Flat Rock Software
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// C3_DRAW.C
+
+#include "DEF.H"
+#pragma hdrstop
+
+//#define DRAWEACH                              // draw walls one at a time for debugging
+
+unsigned        highest;
+unsigned        mostwalls,numwalls;
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+#define PI      3.141592657
+#define ANGLEQUAD       (ANGLES/4)
+
+unsigned        oldend;
+
+#define FINEANGLES      3600
+
+#define MINRATIO        16
+
+
+const   unsigned        MAXSCALEHEIGHT  = (VIEWWIDTH/2);
+const   unsigned        MAXVISHEIGHT    = (VIEWHEIGHT/2);
+const   unsigned        BASESCALE               = 32;
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+//
+// calculate location of screens in video memory so they have the
+// maximum possible distance seperating them (for scaling overflow)
+//
+
+unsigned screenloc[3]= {PAGE1START,PAGE2START,PAGE3START};
+unsigned freelatch = FREESTART;
+
+boolean         fizzlein;
+
+long    scaleshapecalll;
+long    scaletablecall;
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+long    bytecount,endcount;             // for profiling
+int             animframe;
+int             pixelangle[VIEWWIDTH];
+int             far finetangent[FINEANGLES+1];
+int             fineviewangle;
+unsigned        viewxpix,viewypix;
+
+/*
+============================================================================
+
+			   3 - D  DEFINITIONS
+
+============================================================================
+*/
+
+fixed   tileglobal      = TILEGLOBAL;
+fixed   focallength     = FOCALLENGTH;
+fixed   mindist         = MINDIST;
+int             viewheight      = VIEWHEIGHT;
+fixed scale;
+
+
+tilept  tile,lasttile,          // tile of wall being followed
+	focal,                  // focal point in tiles
+	left,mid,right;         // rightmost tile in view
+
+globpt edge,view;
+
+int     segstart[VIEWHEIGHT],   // addline tracks line segment and draws
+	segend[VIEWHEIGHT],
+	segcolor[VIEWHEIGHT];   // only when the color changes
+
+
+walltype        walls[MAXWALLS],*leftwall,*rightwall;
+
+
+//==========================================================================
+
+//
+// refresh stuff
+//
+
+int screenpage;
+
+long lasttimecount;
+
+//
+// rendering stuff
+//
+
+int firstangle,lastangle;
+
+fixed prestep;
+
+fixed sintable[ANGLES+ANGLES/4],*costable = sintable+(ANGLES/4);
+
+fixed   viewx,viewy;                    // the focal point
+int     viewangle;
+fixed   viewsin,viewcos;
+
+int     zbuffer[VIEWXH+1];      // holds the height of the wall at that point
+
+//==========================================================================
+
+void    DrawLine (int xl, int xh, int y,int color);
+void    DrawWall (walltype *wallptr);
+void    TraceRay (unsigned angle);
+fixed   FixedByFrac (fixed a, fixed b);
+fixed   FixedAdd (void);
+fixed   TransformX (fixed gx, fixed gy);
+int             FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max);
+int             BackTrace (int finish);
+void    ForwardTrace (void);
+int             TurnClockwise (void);
+int             TurnCounterClockwise (void);
+void    FollowWall (void);
+
+void    NewScene (void);
+void    BuildTables (void);
+
+//==========================================================================
+
+
+#if 0
+/*
+==================
+=
+= DrawLine
+=
+= Must be in write mode 2 with all planes enabled
+= The bit mask is left set to the end value, so clear it after all lines are
+= drawn
+=
+= draws a black dot at the left edge of the line
+=
+==================
+*/
+
+unsigned static char dotmask[8] = {0x80,0x40,0x20,0x10,8,4,2,1};
+unsigned static char leftmask[8] = {0xff,0x7f,0x3f,0x1f,0xf,7,3,1};
+unsigned static char rightmask[8] = {0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff};
+
+void DrawLine (int xl, int xh, int y,int color)
+{
+  unsigned dest,xlp,xlb,xhb,maskleft,maskright,maskdot,mid;
+
+  xlb=xl/8;
+  xhb=xh/8;
+
+  if (xh<xl)
+	Quit("DrawLine: xh<xl");
+  if (y<VIEWY)
+	Quit("DrawLine: y<VIEWY");
+  if (y>VIEWYH)
+	Quit("DrawLine: y>VIEWYH");
+
+	xlp = xl&7;
+	maskleft = leftmask[xlp];
+	maskright = rightmask[xh&7];
+
+  mid = xhb-xlb-1;
+  dest = bufferofs+ylookup[y]+xlb;
+
+	//
+	// set the GC index register to point to the bit mask register
+	//
+	asm     mov     al,GC_BITMASK
+	asm     mov     dx,GC_INDEX
+	asm     out     dx,al
+
+  if (xlb==xhb)
+  {
+  //
+  // entire line is in one byte
+  //
+
+	maskleft&=maskright;
+
+	asm     mov     es,[screenseg]
+	asm     mov     di,[dest]
+	asm     mov     dx,GC_INDEX+1
+
+	asm     mov     al,[BYTE PTR maskleft]
+	asm     out     dx,al           // mask off pixels
+
+	asm     mov     al,[BYTE PTR color]
+	asm     xchg    al,[es:di]      // load latches and write pixels
+
+	return;
+  }
+
+asm     mov     es,[screenseg]
+asm     mov     di,[dest]
+asm     mov     dx,GC_INDEX+1
+asm     mov     bh,[BYTE PTR color]
+
+//
+// draw left side
+//
+asm     mov     al,[BYTE PTR maskleft]
+asm     out     dx,al           // mask off pixels
+
+asm     mov     al,bh
+asm     xchg    al,[es:di]      // load latches and write pixels
+asm     inc     di
+
+//
+// draw middle
+//
+asm     mov     al,255
+asm     out     dx,al           // no masking
+
+asm     mov     al,bh
+asm     mov     cx,[mid]
+asm     rep     stosb
+
+//
+// draw right side
+//
+asm     mov     al,[BYTE PTR maskright]
+asm     out     dx,al           // mask off pixels
+asm     xchg    bh,[es:di]      // load latches and write pixels
+
+}
+
+//==========================================================================
+
+void DrawLineDot (int xl, int xh, int y,int color)
+{
+  unsigned dest,xlp,xlb,xhb,maskleft,maskright,maskdot,mid;
+
+  xlb=xl/8;
+  xhb=xh/8;
+
+  if (xh<xl)
+	Quit("DrawLine: xh<xl");
+  if (y<VIEWY)
+	Quit("DrawLine: y<VIEWY");
+  if (y>VIEWYH)
+	Quit("DrawLine: y>VIEWYH");
+
+	xlp = xl&7;
+	maskdot = dotmask[xlp];
+	maskleft = leftmask[xlp];
+	maskright = rightmask[xh&7];
+
+  mid = xhb-xlb-1;
+  dest = bufferofs+ylookup[y]+xlb;
+
+	//
+	// set the GC index register to point to the bit mask register
+	//
+	asm     mov     al,GC_BITMASK
+	asm     mov     dx,GC_INDEX
+	asm     out     dx,al
+
+  if (xlb==xhb)
+  {
+  //
+  // entire line is in one byte
+  //
+
+	maskleft&=maskright;
+
+	asm     mov     es,[screenseg]
+	asm     mov     di,[dest]
+	asm     mov     dx,GC_INDEX+1
+
+	asm     mov     al,[BYTE PTR maskleft]
+	asm     out     dx,al           // mask off pixels
+
+	asm     mov     al,[BYTE PTR color]
+	asm     xchg    al,[es:di]      // load latches and write pixels
+
+
+	//
+	// write the black dot at the start
+	//
+	asm     mov     al,[BYTE PTR maskdot]
+	asm     out     dx,al           // mask off pixels
+
+	asm     xor     al,al
+	asm     xchg    al,[es:di]      // load latches and write pixels
+
+
+	return;
+  }
+
+asm     mov     es,[screenseg]
+asm     mov     di,[dest]
+asm     mov     dx,GC_INDEX+1
+asm     mov     bh,[BYTE PTR color]
+
+//
+// draw left side
+//
+asm     mov     al,[BYTE PTR maskleft]
+asm     out     dx,al           // mask off pixels
+
+asm     mov     al,bh
+asm     xchg    al,[es:di]      // load latches and write pixels
+
+//
+// write the black dot at the start
+//
+asm     mov     al,[BYTE PTR maskdot]
+asm     out     dx,al           // mask off pixels
+asm     xor     al,al
+asm     xchg    al,[es:di]      // load latches and write pixels
+asm     inc     di
+
+//
+// draw middle
+//
+asm     mov     al,255
+asm     out     dx,al           // no masking
+
+asm     mov     al,bh
+asm     mov     cx,[mid]
+asm     rep     stosb
+
+//
+// draw right side
+//
+asm     mov     al,[BYTE PTR maskright]
+asm     out     dx,al           // mask off pixels
+asm     xchg    bh,[es:di]      // load latches and write pixels
+
+}
+
+#endif
+
+//==========================================================================
+
+
+long            wallscalesource;
+
+#ifdef DRAWEACH
+/*
+====================
+=
+= ScaleOneWall
+=
+====================
+*/
+
+void near ScaleOneWall (int xl, int xh)
+{
+	int     x,pixwidth,height;
+
+	*(((unsigned *)&wallscalesource)+1) = wallseg[xl];
+
+	for (x=xl;x<=xh;x+=pixwidth)
+	{
+		height = wallheight[x];
+		pixwidth = wallwidth[x];
+		(unsigned)wallscalesource = wallofs[x];
+
+		*(((unsigned *)&scaletablecall)+1) = (unsigned)scaledirectory[height];
+		(unsigned)scaletablecall = scaledirectory[height]->codeofs[0];
+
+		//
+		// scale a byte wide strip of wall
+		//
+		asm     mov     bx,[x]
+		asm     mov     di,bx
+		asm     shr     di,1
+		asm     shr     di,1
+		asm     shr     di,1                                            // X in bytes
+		asm     add     di,[bufferofs]
+		asm     and     bx,7
+		asm     shl     bx,1
+		asm     shl     bx,1
+		asm     shl     bx,1
+		asm     add     bx,[pixwidth]                           // bx = pixel*8+pixwidth-1
+		asm     dec     bx
+		asm     mov     al,BYTE PTR [bitmasks1+bx]
+		asm     mov     dx,GC_INDEX+1
+		asm     out     dx,al                                           // set bit mask register
+		asm     mov     es,[screenseg]
+		asm     lds     si,[wallscalesource]
+		asm     call [DWORD PTR ss:scaletablecall]              // scale the line of pixels
+
+		asm     mov     al,BYTE PTR [ss:bitmasks2+bx]
+		asm     or      al,al
+		asm     jz      nosecond
+
+		//
+		// draw a second byte for vertical strips that cross two bytes
+		//
+		asm     inc     di
+		asm     out     dx,al                                           // set bit mask register
+		asm     call [DWORD PTR ss:scaletablecall]      // scale the line of pixels
+	nosecond:
+		asm     mov     ax,ss
+		asm     mov     ds,ax
+	}
+}
+
+#endif
+
+char wall_anim_pos[NUMFLOORS];
+
+// EAST / WEST WALLS
+//
+int     far walllight1[NUMFLOORS] = {0,
+
+	0,//CRYSTAL1LIGHTPIC,
+	0,//EGYPT1LIGHTPIC,
+	EGYPT2LIGHTPIC,
+	EGYPT3LIGHTPIC,
+
+	FIREWALL1PIC,
+	FIREWALL2PIC,
+	FIREWALL3PIC,
+	FIREWALL4PIC,
+
+
+	NEMESISPIC,
+
+	ALTARLEFTPIC,
+	ALTARRIGHTPIC,
+
+	TEMPLEWALLLIGHTPIC,
+
+	TORCHWALL1PIC,
+	TORCHWALL2PIC,
+
+	BRNBRKLIGHTPIC,
+	BRNBRKEMLIGHTPIC,
+
+	IRONGATEPIC,
+
+	BRNFLGLIGHTPIC,
+	BRNFLGWINDOWLIGHTPIC,
+	BRNFLGVINELIGHTPIC,
+	BRNFLGDMGLIGHTPIC,
+
+	SPACEDMG1LIGHTPIC,
+	SPACEDMG2LIGHTPIC,
+
+	SPACE1LIGHTPIC,
+	SPACE2LIGHTPIC,
+	SPACE3LIGHTPIC,
+	SPACE4LIGHTPIC,
+
+	SPACE5LIGHTPIC,
+	SPACE6LIGHTPIC,
+	SPACE7LIGHTPIC,
+	SPACE8LIGHTPIC,
+
+	0,//SPACE9LIGHTPIC,
+	0,//SPACEDMG9LIGHTPIC,
+	SPACE10LIGHTPIC,
+	RUSTDOORLIGHTPIC,
+
+	SPACE11LIGHTPIC,
+	SPACE12LIGHTPIC,
+	SPACE13LIGHTPIC,
+	SPACE14LIGHTPIC,
+
+	SPACEDMG5LIGHTPIC,
+	SPACEDMG6LIGHTPIC,
+
+	TAP1PIC,
+	TAP2PIC,
+	ENDPIC,
+	0,//SIRONLIGHTPIC,
+
+	SPCDOOR1LIGHTPIC,
+	SPCDOOR2LIGHTPIC,
+	SPCDOOR3LIGHTPIC,
+	SPCDOOR4LIGHTPIC,
+
+	COLUMNSLIGHTPIC,
+
+	DEMONSTATUELIGHTPIC,
+
+	0,//CRYSTALBWALL1LIGHTPIC,
+
+	0,//SRUSTLIGHTPIC,
+
+	TROLLSTATUELIGHTPIC,
+
+	BRNDMGVINELIGHTPIC,
+	TAP3PIC,
+	HORNDOORPIC,
+	RUNEDOORPIC,
+
+	EXP_WALL_1PIC,
+	EXP_WALL_2PIC,
+	EXP_WALL_3PIC,
+	WATER_EXP_WALL_1PIC,
+	WATER_EXP_WALL_2PIC,
+	WATER_EXP_WALL_3PIC,
+
+	IRONDMGLIGHTPIC,
+	IRONLIGHTPIC,
+	0,
+	TROLLBLOODYLIGHTPIC,
+	TROLLLIGHTPIC,
+
+	0,												// INVISIBLE WALL
+
+	STONEDOORLIGHTPIC,
+	0,
+
+	IRONWTR1LIGHTPIC,
+	IRONWTR2LIGHTPIC,
+	IRONWTR3LIGHTPIC,
+
+	RUSTWTR1LIGHTPIC,
+	RUSTWTR2LIGHTPIC,
+	RUSTWTR3LIGHTPIC,
+
+	CEMETARYLIGHTPIC,
+	0,	//	STAIRDWNLIGHTPIC,
+
+	WGRATE1LIGHTPIC,
+	WGRATE2LIGHTPIC,
+	WGRATE3LIGHTPIC,
+
+	MAS_WIN_LIGHTPIC,
+	MAS_DOOR_LIGHTPIC,
+	MAS_VINE1_LIGHTPIC,
+	MAS_VINE2_LIGHTPIC,
+
+  // Start of non-solid walls
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+
+  // solid walls
+	SGRATEPIC,
+};
+
+// NORTH / SOUTH WALLS
+//
+int     far walldark1[NUMFLOORS] = {0,
+
+	0,//CRYSTAL1DARKPIC,
+	0,//EGYPT1DARKPIC,
+	EGYPT2DARKPIC,
+	EGYPT3DARKPIC,
+
+	FIREWALL1PIC,
+	FIREWALL2PIC,
+	FIREWALL3PIC,
+	FIREWALL4PIC,
+
+	NEMESISPIC,
+
+	ALTARLEFTPIC,
+	ALTARRIGHTPIC,
+
+	TEMPLEWALLDARKPIC,
+
+	TORCHWALL1PIC,
+	TORCHWALL2PIC,
+
+	BRNBRKDARKPIC,
+	BRNBRKEMDARKPIC,
+
+	IRONGATEPIC,
+
+	BRNFLGDARKPIC,
+	BRNFLGWINDOWDARKPIC,
+	BRNFLGVINEDARKPIC,
+	BRNFLGDMGDARKPIC,
+
+	SPACEDMG1DARKPIC,
+	SPACEDMG2DARKPIC,
+
+	SPACE1DARKPIC,
+	SPACE2DARKPIC,
+	SPACE3DARKPIC,
+	SPACE4DARKPIC,
+
+	SPACE5DARKPIC,
+	SPACE6DARKPIC,
+	SPACE7DARKPIC,
+	SPACE8DARKPIC,
+
+	0,//SPACE9DARKPIC,
+	0,//SPACEDMG9DARKPIC,
+	SPACE10DARKPIC,
+	RUSTDOORDARKPIC,
+
+	SPACE11DARKPIC,
+	SPACE12DARKPIC,
+	SPACE13DARKPIC,
+	SPACE14DARKPIC,
+
+	SPACEDMG5DARKPIC,
+	SPACEDMG6DARKPIC,
+
+	TAP1PIC,
+	TAP2PIC,
+	ENDPIC,
+	0,//SIRONDARKPIC,
+
+	SPCDOOR1DARKPIC,
+	SPCDOOR2DARKPIC,
+	SPCDOOR3DARKPIC,
+	SPCDOOR4DARKPIC,
+
+	COLUMNSDARKPIC,
+
+	DEMONSTATUEDARKPIC,
+
+	0,//CRYSTALBWALL1DARKPIC,
+
+	0,//SRUSTDARKPIC,
+
+	TROLLSTATUEDARKPIC,
+
+	BRNDMGVINEDARKPIC,
+	TAP3PIC,
+	HORNDOORPIC,
+	RUNEDOORPIC,
+
+	EXP_WALL_1PIC,
+	EXP_WALL_2PIC,
+	EXP_WALL_3PIC,
+
+	WATER_EXP_WALL_1PIC,
+	WATER_EXP_WALL_2PIC,
+	WATER_EXP_WALL_3PIC,
+
+	IRONDMGDARKPIC,
+	IRONDARKPIC,
+	0,
+	TROLLBLOODYDARKPIC,
+
+	TROLLDARKPIC,
+
+	0,											// INVISIBLE WALL
+
+	STONEDOORDARKPIC,
+	0,
+
+	IRONWTR1DARKPIC,
+	IRONWTR2DARKPIC,
+	IRONWTR3DARKPIC,
+
+	RUSTWTR1DARKPIC,
+	RUSTWTR2DARKPIC,
+	RUSTWTR3DARKPIC,
+
+	CEMETARYDARKPIC,
+	0,
+
+	WGRATE1DARKPIC,
+	WGRATE2DARKPIC,
+	WGRATE3DARKPIC,
+
+	MAS_WIN_DARKPIC,
+	MAS_DOOR_DARKPIC,
+	MAS_VINE1_DARKPIC,
+	MAS_VINE2_DARKPIC,
+
+  // Start of non-solid walls
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+
+  // solid walls
+	SGRATEPIC,
+};
+
+
+/*
+=====================
+=
+= DrawVWall
+=
+= Draws a wall by vertical segments, for texture mapping!
+=
+= wallptr->side is true for east/west walls (constant x)
+=
+= fracheight and fracstep are 16.16 bit fractions
+=
+=====================
+*/
+
+void DrawVWall (walltype *wallptr)
+{
+	int                     x,i;
+	unsigned        source;
+	unsigned        width,sourceint;
+	unsigned        wallpic,wallpicseg;
+	unsigned        skip;
+	long            fracheight,fracstep,longheightchange;
+	unsigned        height;
+	int                     heightchange;
+	unsigned        slope,distance;
+	int                     traceangle,angle;
+	int                     mapadd;
+	unsigned        lastpix,lastsource,lastwidth;
+
+	if (wallptr->rightclip < wallptr->leftclip)
+		Quit ("DrawVWall: Right < Left");
+
+//
+// setup for height calculation
+//
+	wallptr->height1 >>= 1;
+	wallptr->height2 >>= 1;
+	wallptr->planecoord>>=10;                       // remove non significant bits
+
+	width = wallptr->x2 - wallptr->x1;
+	if (width)
+	{
+		heightchange = wallptr->height2 - wallptr->height1;
+		asm     mov     ax,[heightchange]
+		asm     mov     WORD PTR [longheightchange+2],ax
+		asm     mov     WORD PTR [longheightchange],0   // avoid long shift by 16
+		fracstep = longheightchange/width;
+	}
+
+	fracheight = ((long)wallptr->height1<<16)+0x8000;
+	skip = wallptr->leftclip - wallptr->x1;
+	if (skip)
+		fracheight += fracstep*skip;
+
+//
+// setup for texture mapping
+//
+// mapadd is 64*64 (to keep source positive) + the origin wall intercept
+// distance has 6 unit bits, and 6 frac bits
+// traceangle is the center view angle in FINEANGLES, moved to be in
+// the +-90 degree range (to thew right of origin)
+//
+	traceangle = fineviewangle;
+	//
+	// find wall picture to map from
+	//
+	if (wallptr->side)
+	{       // east or west wall
+
+		wallpic = walllight1[wallptr->color+wall_anim_pos[wallptr->color]];
+		if (wallptr->planecoord < viewxpix)
+		{
+			distance = viewxpix-wallptr->planecoord;
+			traceangle -= FINEANGLES/2;
+			mapadd = (64-viewypix&63);              // the pixel spot of the origin
+		}
+		else
+		{
+			distance = wallptr->planecoord-viewxpix;
+			// traceangle is correct
+			mapadd = viewypix&63;           // the pixel spot of the origin
+		}
+	}
+	else
+	{       // north or south wall
+
+		wallpic = walldark1[wallptr->color+wall_anim_pos[wallptr->color]];
+		if (wallptr->planecoord < viewypix)
+		{
+			distance = viewypix-wallptr->planecoord;
+			traceangle -= FINEANGLES/4;
+			mapadd = viewxpix&63;           // the pixel spot of the origin
+		}
+		else
+		{
+			distance = wallptr->planecoord-viewypix;
+			traceangle -= FINEANGLES*3/4;
+			mapadd = (64-viewxpix&63);              // the pixel spot of the origin
+		}
+	}
+
+	mapadd = 64*64-mapadd;                          // make sure it stays positive
+
+	wallpicseg = (unsigned)walldirectory[wallpic-FIRSTWALLPIC];
+	if (traceangle > FINEANGLES/2)
+		traceangle -= FINEANGLES;
+
+//
+// calculate everything
+//
+// IMPORTANT!  This loop is executed around 5000 times / second!
+//
+	lastpix = lastsource = (unsigned)-1;
+
+	for (x = wallptr->leftclip ; x <= wallptr->rightclip ; x++)
+	{
+		//
+		// height
+		//
+		asm     mov     ax,WORD PTR [fracheight]
+		asm     mov     dx,WORD PTR [fracheight+2]
+		asm     mov     cx,dx
+		asm     add     ax,WORD PTR [fracstep]
+		asm     adc     dx,WORD PTR [fracstep+2]
+		asm     mov     WORD PTR [fracheight],ax
+		asm     mov     WORD PTR [fracheight+2],dx
+		asm     mov     bx,[x]
+		asm     shl     bx,1
+		asm     cmp     cx,MAXSCALEHEIGHT
+		asm     jbe     storeheight
+		asm     mov     cx,MAXSCALEHEIGHT
+storeheight:
+		asm     mov WORD PTR [wallheight+bx],cx
+		asm     mov WORD PTR [zbuffer+bx],cx
+
+//              height = fracheight>>16;
+//              fracheight += fracstep;
+//              if (height > MAXSCALEHEIGHT)
+//                      height = MAXSCALEHEIGHT;
+//              wallheight[x] = zbuffer[x] = height;
+
+		//
+		// texture map
+		//
+		angle = pixelangle[x]+traceangle;
+		if (angle<0)
+			angle+=FINEANGLES;
+
+		slope = finetangent[angle];
+
+//
+// distance is an unsigned 6.6 bit number (12 pixel bits)
+// slope is a signed 5.10 bit number
+// result is a signed 11.16 bit number
+//
+
+#if 0
+		source = distance*slope;
+		source >>=20;
+
+		source += mapadd;
+		source &= 63;                           // mask off the unused units
+		source = 63-source;
+		source <<= 6;                           // multiply by 64 for offset into pic
+#endif
+		asm     mov     ax,[distance]
+		asm     imul    [slope]                 // ax is the source pixel
+		asm     mov     al,ah
+		asm     shr     al,1
+		asm     shr     al,1                            // low 6 bits is now pixel number
+		asm     add     ax,[mapadd]
+		asm     and ax,63
+		asm     mov     dx,63
+		asm     sub     dx,ax                           // otherwise it is backwards
+		asm     shl     dx,1
+		asm     shl     dx,1
+		asm     shl     dx,1
+		asm     shl     dx,1
+		asm     shl     dx,1
+		asm     shl     dx,1                            // *64 to index into shape
+		asm     mov     [source],dx
+
+		if (source != lastsource)
+		{
+			if (lastpix != (unsigned)-1)
+			{
+				wallofs[lastpix] = lastsource;
+				wallseg[lastpix] = wallpicseg;
+				wallwidth[lastpix] = lastwidth;
+			}
+			lastpix = x;
+			lastsource = source;
+			lastwidth = 1;
+		}
+		else
+			lastwidth++;                    // optimized draw, same map as last one
+	}
+	wallofs[lastpix] = lastsource;
+	wallseg[lastpix] = wallpicseg;
+	wallwidth[lastpix] = lastwidth;
+}
+
+
+//==========================================================================
+
+
+/*
+=================
+=
+= TraceRay
+=
+= Used to find the left and rightmost tile in the view area to be traced from
+= Follows a ray of the given angle from viewx,viewy in the global map until
+= it hits a solid tile
+= sets:
+=   tile.x,tile.y       : tile coordinates of contacted tile
+=   tilecolor   : solid tile's color
+=
+==================
+*/
+
+int tilecolor;
+
+void TraceRay (unsigned angle)
+{
+  long tracex,tracey,tracexstep,traceystep,searchx,searchy;
+  fixed fixtemp;
+  int otx,oty,searchsteps;
+
+  tracexstep = costable[angle];
+  traceystep = sintable[angle];
+
+//
+// advance point so it is even with the view plane before we start checking
+//
+  fixtemp = FixedByFrac(prestep,tracexstep);
+  tracex = viewx+fixtemp;
+  fixtemp = FixedByFrac(prestep,traceystep);
+  tracey = viewy-fixtemp;
+
+  tile.x = tracex>>TILESHIFT;   // starting point in tiles
+  tile.y = tracey>>TILESHIFT;
+
+
+  if (tracexstep<0)                     // use 2's complement, not signed magnitude
+	tracexstep = -(tracexstep&0x7fffffff);
+
+  if (traceystep<0)                     // use 2's complement, not signed magnitude
+	traceystep = -(traceystep&0x7fffffff);
+
+//
+// we assume viewx,viewy is not inside a solid tile, so go ahead one step
+//
+
+  do    // until a solid tile is hit
+  {
+    otx = tile.x;
+	oty = tile.y;
+	spotvis[otx][oty] = true;
+	tracex += tracexstep;
+    tracey -= traceystep;
+    tile.x = tracex>>TILESHIFT;
+	tile.y = tracey>>TILESHIFT;
+
+	if (tile.x!=otx && tile.y!=oty && (tilemap[otx][tile.y] || tilemap[tile.x][oty]) )
+    {
+      //
+	  // trace crossed two solid tiles, so do a binary search along the line
+	  // to find a spot where only one tile edge is crossed
+      //
+      searchsteps = 0;
+      searchx = tracexstep;
+      searchy = traceystep;
+      do
+      {
+	searchx/=2;
+	searchy/=2;
+	if (tile.x!=otx && tile.y!=oty)
+	{
+	 // still too far
+	  tracex -= searchx;
+	  tracey += searchy;
+	}
+	else
+	{
+	 // not far enough, no tiles crossed
+	  tracex += searchx;
+	  tracey -= searchy;
+	}
+
+	//
+	// if it is REAL close, go for the most clockwise intersection
+	//
+	if (++searchsteps == 16)
+	{
+	  tracex = (long)otx<<TILESHIFT;
+	  tracey = (long)oty<<TILESHIFT;
+	  if (tracexstep>0)
+	  {
+		if (traceystep<0)
+		{
+		  tracex += TILEGLOBAL-1;
+		  tracey += TILEGLOBAL;
+		}
+		else
+		{
+		  tracex += TILEGLOBAL;
+		}
+	  }
+	  else
+	  {
+		if (traceystep<0)
+		{
+		  tracex --;
+		  tracey += TILEGLOBAL-1;
+		}
+		else
+		{
+		  tracey --;
+		}
+	  }
+	}
+
+	tile.x = tracex>>TILESHIFT;
+	tile.y = tracey>>TILESHIFT;
+
+	  } while (( tile.x!=otx && tile.y!=oty) || (tile.x==otx && tile.y==oty) );
+	}
+  } while (!(tilecolor = tilemap[tile.x][tile.y]) );
+
+}
+
+//==========================================================================
+
+
+/*
+========================
+=
+= FixedByFrac
+=
+= multiply a 16/16 bit, 2's complement fixed point number by a 16 bit
+= fraction, passed as a signed magnitude 32 bit number
+=
+========================
+*/
+
+#pragma warn -rvl                       // I stick the return value in with ASMs
+
+fixed FixedByFrac (fixed a, fixed b)
+{
+  fixed value;
+
+//
+// setup
+//
+asm     mov     si,[WORD PTR b+2]       // sign of result = sign of fraction
+
+asm     mov     ax,[WORD PTR a]
+asm     mov     cx,[WORD PTR a+2]
+
+asm     or      cx,cx
+asm     jns     aok:                            // negative?
+asm     not     ax
+asm     not     cx
+asm     add     ax,1
+asm     adc     cx,0
+asm     xor     si,0x8000                       // toggle sign of result
+aok:
+
+//
+// multiply  cx:ax by bx
+//
+asm     mov     bx,[WORD PTR b]
+asm     mul     bx                                      // fraction*fraction
+asm     mov     di,dx                           // di is low word of result
+asm     mov     ax,cx                           //
+asm     mul     bx                                      // units*fraction
+asm add ax,di
+asm     adc     dx,0
+
+//
+// put result dx:ax in 2's complement
+//
+asm     test    si,0x8000               // is the result negative?
+asm     jz      ansok:
+asm     not     ax
+asm     not     dx
+asm     add     ax,1
+asm     adc     dx,0
+
+ansok:;
+
+}
+
+#pragma warn +rvl
+
+#if 0
+/*
+=========================
+=
+= FixedAdd
+=
+= add two 16 bit fixed point numbers
+= to subtract, invert the sign of B before invoking
+=
+=========================
+*/
+
+fixed FixedAdd (fixed a, fixed b)
+{
+  fixed value;
+
+asm     mov     ax,[WORD PTR a]
+asm     mov     dx,[WORD PTR a+2]
+
+asm     mov     bx,[WORD PTR b]
+asm     mov     cx,[WORD PTR b+2]
+
+asm     or      dx,dx
+asm     jns     aok:            // negative?
+asm     and     dx,0x7fff
+asm     not     ax              // convert a from signed magnitude to 2's compl
+asm     not     dx
+asm     add     ax,1
+asm     adc     dx,0
+aok:
+
+asm     or      cx,cx
+asm     jns     bok:            // negative?
+asm     and     cx,0x7fff
+asm     not     bx              // convert b from signed magnitude to 2's compl
+asm     not     cx
+asm     add     bx,1
+asm     adc     cx,0
+bok:
+
+asm     add     ax,bx           // perform the addition
+asm     adc     dx,cx
+asm     jns     done
+
+asm     and     dx,0x7fff       // value was negative
+asm     not     ax              // back to signed magnitude
+asm     not     dx
+asm     add     ax,1
+asm     adc     dx,0
+
+done:
+
+asm     mov     [WORD PTR value],ax
+asm     mov     [WORD PTR value+2],dx
+
+  return value;
+}
+#endif
+
+//==========================================================================
+
+
+/*
+========================
+=
+= TransformPoint
+=
+= Takes paramaters:
+=   gx,gy               : globalx/globaly of point
+=
+= globals:
+=   viewx,viewy         : point of view
+=   viewcos,viewsin     : sin/cos of viewangle
+=
+=
+= defines:
+=   CENTERX             : pixel location of center of view window
+=   TILEGLOBAL          : size of one
+=   FOCALLENGTH         : distance behind viewx/y for center of projection
+=   scale               : conversion from global value to screen value
+=
+= returns:
+=   screenx,screenheight: projected edge location and size
+=
+========================
+*/
+
+void TransformPoint (fixed gx, fixed gy, int *screenx, unsigned *screenheight)
+{
+  int ratio;
+  fixed gxt,gyt,nx,ny;
+
+//
+// translate point to view centered coordinates
+//
+  gx = gx-viewx;
+  gy = gy-viewy;
+
+//
+// calculate newx
+//
+  gxt = FixedByFrac(gx,viewcos);
+  gyt = FixedByFrac(gy,viewsin);
+  nx = gxt-gyt;
+
+//
+// calculate newy
+//
+  gxt = FixedByFrac(gx,viewsin);
+  gyt = FixedByFrac(gy,viewcos);
+  ny = gyt+gxt;
+
+//
+// calculate perspective ratio
+//
+  if (nx<0)
+	nx = 0;
+
+  ratio = nx*scale/FOCALLENGTH;
+
+  if (ratio<=MINRATIO)
+	ratio = MINRATIO;
+
+  *screenx = CENTERX + ny/ratio;
+
+  *screenheight = TILEGLOBAL/ratio;
+
+}
+
+
+//
+// transform actor
+//
+void TransformActor (objtype *ob)
+{
+  int ratio;
+  fixed gx,gy,gxt,gyt,nx,ny;
+
+//
+// translate point to view centered coordinates
+//
+  gx = ob->x-viewx;
+  gy = ob->y-viewy;
+
+//
+// calculate newx
+//
+  gxt = FixedByFrac(gx,viewcos);
+  gyt = FixedByFrac(gy,viewsin);
+  nx = gxt-gyt-ob->size;
+
+//
+// calculate newy
+//
+  gxt = FixedByFrac(gx,viewsin);
+  gyt = FixedByFrac(gy,viewcos);
+  ny = gyt+gxt;
+
+//
+// calculate perspective ratio
+//
+  if (nx<0)
+	nx = 0;
+
+  ratio = nx*scale/FOCALLENGTH;
+
+  if (ratio<=MINRATIO)
+	ratio = MINRATIO;
+
+  ob->viewx = CENTERX + ny/ratio;
+
+  ob->viewheight = TILEGLOBAL/ratio;
+}
+
+//==========================================================================
+
+fixed TransformX (fixed gx, fixed gy)
+{
+  int ratio;
+  fixed gxt,gyt,nx,ny;
+
+//
+// translate point to view centered coordinates
+//
+  gx = gx-viewx;
+  gy = gy-viewy;
+
+//
+// calculate newx
+//
+  gxt = FixedByFrac(gx,viewcos);
+  gyt = FixedByFrac(gy,viewsin);
+
+  return gxt-gyt;
+}
+
+//==========================================================================
+
+/*
+==================
+=
+= BuildTables
+=
+= Calculates:
+=
+= scale                 projection constant
+= sintable/costable     overlapping fractional tables
+= firstangle/lastangle  angles from focalpoint to left/right view edges
+= prestep               distance from focal point before checking for tiles
+=
+==================
+*/
+
+void BuildTables (void)
+{
+  int           i;
+  long          intang;
+  long          x;
+  float         angle,anglestep,radtoint;
+  double        tang;
+  fixed         value;
+
+//
+// calculate the angle offset from view angle of each pixel's ray
+//
+	radtoint = (float)FINEANGLES/2/PI;
+	for (i=0;i<VIEWWIDTH/2;i++)
+	{
+	// start 1/2 pixel over, so viewangle bisects two middle pixels
+		x = (TILEGLOBAL*i+TILEGLOBAL/2)/VIEWWIDTH;
+		tang = (float)x/(FOCALLENGTH+MINDIST);
+		angle = atan(tang);
+		intang = angle*radtoint;
+		pixelangle[VIEWWIDTH/2-1-i] = intang;
+		pixelangle[VIEWWIDTH/2+i] = -intang;
+	}
+
+//
+// calculate fine tangents
+// 1 sign bit, 5 units (clipped to), 10 fracs
+//
+#define MININT  (-MAXINT)
+
+	for (i=0;i<FINEANGLES/4;i++)
+	{
+		intang = tan(i/radtoint)*(1l<<10);
+
+		//
+		// if the tangent is not reprentable in this many bits, bound the
+		// units part ONLY
+		//
+		if (intang>MAXINT)
+			intang = 0x8f00 | (intang & 0xff);
+		else if (intang<MININT)
+			intang = 0xff00 | (intang & 0xff);
+
+		finetangent[i] = intang;
+//              finetangent[FINEANGLES/2+i] = intang;
+//              finetangent[FINEANGLES/2-i-1] = -intang;
+		finetangent[FINEANGLES-i-1] = -intang;
+	}
+
+//
+// calculate scale value so one tile at mindist allmost fills the view horizontally
+//
+  scale = GLOBAL1/VIEWWIDTH;
+  scale *= focallength;
+  scale /= (focallength+mindist);
+
+//
+// costable overlays sintable with a quarter phase shift
+// ANGLES is assumed to be divisable by four
+//
+// The low word of the value is the fraction, the high bit is the sign bit,
+// bits 16-30 should be 0
+//
+
+  angle = 0;
+  anglestep = PI/2/ANGLEQUAD;
+  for (i=0;i<=ANGLEQUAD;i++)
+  {
+	value=GLOBAL1*sin(angle);
+	sintable[i]=
+	  sintable[i+ANGLES]=
+	  sintable[ANGLES/2-i] = value;
+	sintable[ANGLES-i]=
+	  sintable[ANGLES/2+i] = value | 0x80000000l;
+	angle += anglestep;
+  }
+
+//
+// figure trace angles for first and last pixel on screen
+//
+  angle = atan((float)VIEWWIDTH/2*scale/FOCALLENGTH);
+  angle *= ANGLES/(PI*2);
+
+  intang = (int)angle+1;
+  firstangle = intang;
+  lastangle = -intang;
+
+  prestep = GLOBAL1*((float)FOCALLENGTH/costable[firstangle]);
+
+//
+// misc stuff
+//
+  walls[0].x2 = VIEWX-1;
+  walls[0].height2 = 32000;
+}
+
+
+//==========================================================================
+
+/*
+=====================
+=
+= ClearScreen
+=
+=====================
+*/
+
+void ClearScreen (void)
+{
+	unsigned topcolor=*skycolor, bottomcolor=*groundcolor;
+	unsigned topimage=topcolor&0xf0,bottomimage=bottomcolor&0xf0;
+	unsigned pfoffset=0;
+
+
+#if USE_STRIPS
+	if (topimage == 0x20)		// special code for lightning
+		topimage = topcolor = 0;
+
+// Manually wipe screen with solid color.
+// If BOTH sky and ground are 'images' don't manually clear it!
+//
+	if ((!topimage) || (!bottomimage))
+	{
+#endif
+
+  //
+  // clear the screen
+  //
+asm     mov     dx,GC_INDEX
+asm     mov     ax,GC_MODE + 256*2              // read mode 0, write mode 2
+asm     out     dx,ax
+asm     mov     ax,GC_BITMASK + 255*256
+asm     out     dx,ax
+
+//asm     mov     dx,40-VIEWWIDTH/8					// dx = modulo
+asm     mov     bl,VIEWWIDTH/16
+asm     mov     bh,CENTERY+1
+
+asm     mov     ax,topcolor
+asm     mov     es,[screenseg]
+asm     mov     di,[bufferofs]
+asm     add     di,((SCREENWIDTH*VIEWY)+(VIEWX/8))
+
+toploop:
+asm     mov     cl,bl
+asm     rep     stosw
+asm     stosb
+//asm     add     di,dx					// no need to add "0" modulo
+asm     dec     bh
+asm     jnz     toploop
+
+asm     mov     bh,CENTERY+1
+asm     mov     ax,bottomcolor
+
+bottomloop:
+asm     mov     cl,bl
+asm     rep     stosw
+asm     stosb
+//asm     add     di,dx					// no need to add "0" modulo
+asm     dec     bh
+asm     jnz     bottomloop
+
+#if USE_STRIPS
+	}
+
+
+//
+// code to test parallax turning
+//
+
+	if (topimage)
+	{
+		topimage -= 16;
+		pfoffset = LONG_PERCENTAGE(3200,359,(359-player->angle),12);
+		while (pfoffset >= 640)
+			pfoffset -= 640;
+		LatchDrawPicStrip(0,0,SKY1PIC+topimage,pfoffset+8);
+	}
+
+	if (bottomimage)
+	{
+////		pfoffset = LONG_PERCENTAGE(3200,359,(359-player->angle),12)+320;
+//		pfoffset += 320;
+//		while (pfoffset >= 640)
+//			pfoffset -= 640;
+//		LatchDrawPicStrip(0,64,SKY1PIC+topimage,pfoffset+8);
+		bottomimage -= 16;
+		LatchDrawPic(0,64,GND1PIC+bottomimage);
+	}
+#endif
+
+
+asm     mov     dx,GC_INDEX
+asm     mov     ax,GC_MODE + 256*10             // read mode 1, write mode 2
+asm     out     dx,ax
+asm     mov     al,GC_BITMASK
+asm     out     dx,al
+
+}
+
+//==========================================================================
+
+/*
+=====================
+=
+= DrawWallList
+=
+= Clips and draws all the walls traced this refresh
+=
+=====================
+*/
+
+void DrawWallList (void)
+{
+	int i,leftx,newleft,rightclip;
+	walltype *wall, *check;
+
+asm     mov     ax,ds
+asm     mov     es,ax
+asm     mov     di,OFFSET wallwidth
+asm     xor     ax,ax
+asm     mov     cx,VIEWWIDTH/2
+asm     rep     stosw
+
+	ClearScreen ();
+
+	rightwall->x1 = VIEWXH+1;
+	rightwall->height1 = 32000;
+	(rightwall+1)->x1 = 32000;
+
+	leftx = -1;
+
+	for (wall=&walls[1];wall<rightwall && leftx<=VIEWXH ;wall++)
+	{
+	  if (leftx >= wall->x2)
+		continue;
+
+	  rightclip = wall->x2;
+
+	  check = wall+1;
+	  while (check->x1 <= rightclip && check->height1 >= wall->height2)
+	  {
+		rightclip = check->x1-1;
+		check++;
+	  }
+
+	  if (rightclip>VIEWXH)
+		rightclip=VIEWXH;
+
+	  if (leftx < wall->x1 - 1)
+		newleft = wall->x1-1;           // there was black space between walls
+	  else
+		newleft = leftx;
+
+	  if (rightclip > newleft)
+	  {
+		wall->leftclip = newleft+1;
+		wall->rightclip = rightclip;
+		DrawVWall (wall);
+		leftx = rightclip;
+	  }
+	}
+
+#ifndef DRAWEACH
+	ScaleWalls ();                                  // draw all the walls
+#endif
+}
+
+//==========================================================================
+
+/*
+=====================
+=
+= DrawScaleds
+=
+= Draws all objects that are visable
+=
+=====================
+*/
+
+objtype *depthsort[MAXACTORS];
+
+void DrawScaleds (void)
+{
+#if USE_INERT_LIST
+		extern inertobjtype inertobjlist[], *inert;
+
+		boolean inertlist=false;
+#endif
+	int             i,j,least,numvisable,height;
+	objtype         *obj,**vislist,*farthest;
+	memptr          shape;
+	byte            *tilespot,*visspot;
+
+	numvisable = 0;
+
+//
+// calculate base positions of all objects
+//
+	vislist = &depthsort[0];
+
+	obj = player->next;
+	while (obj)
+	{
+		tilespot = &tilemap[0][0]+(obj->tilex<<6)+obj->tiley;
+		visspot = &spotvis[0][0]+(obj->tilex<<6)+obj->tiley;
+		//
+		// could be in any of the nine surrounding tiles
+		//
+		if (*visspot
+		|| ( *(visspot-1) && !*(tilespot-1) )
+		|| ( *(visspot+1) && !*(tilespot+1) )
+		|| ( *(visspot-65) && !*(tilespot-65) )
+		|| ( *(visspot-64) && !*(tilespot-64) )
+		|| ( *(visspot-63) && !*(tilespot-63) )
+		|| ( *(visspot+65) && !*(tilespot+65) )
+		|| ( *(visspot+64) && !*(tilespot+64) )
+		|| ( *(visspot+63) && !*(tilespot+63) ) )
+		{
+#if USE_INERT_LIST
+			if (!inertlist)
+#endif
+				if ((obj->active == noalways) || (obj->active == always))
+					obj->active = always;
+				else
+					obj->active = yes;
+			TransformActor (obj);
+			if (!obj->viewheight || obj->viewheight > VIEWWIDTH)
+				goto cont;                       // too close or far away
+
+			if (!obj->state->shapenum)
+				goto cont;
+
+			*vislist++ = obj;
+			numvisable++;
+		}
+		else
+#if USE_INERT_LIST
+			if (!inertlist)
+#endif
+				if ((obj->active != always) && (obj->active != noalways))
+					obj->active = no;
+
+cont:;
+		obj = obj->next;
+#if USE_INERT_LIST
+		if ((!obj) && (!inertlist))
+		{
+			if (inert != inertobjlist)
+				obj = (objtype *)inertobjlist;
+			inertlist = true;
+		}
+#endif
+	}
+
+	if (vislist == &depthsort[0])
+		return;                                         // no visable objects
+
+//
+// draw from back to front
+//
+	for (i = 0; i<numvisable; i++)
+	{
+		least = 32000;
+		for (j=0;j<numvisable;j++)
+		{
+			height = depthsort[j]->viewheight;
+			if (height < least)
+			{
+				least = height;
+				farthest = depthsort[j];
+			}
+		}
+		//
+		// draw farthest
+		//
+		shape = shapedirectory[farthest->state->shapenum-FIRSTSCALEPIC];
+		ScaleShape(farthest->viewx,shape,farthest->viewheight);
+		farthest->viewheight = 32000;
+	}
+}
+
+//==========================================================================
+
+
+/*
+=====================
+=
+= CalcTics
+=
+=====================
+*/
+
+void CalcTics (void)
+{
+	long    newtime,oldtimecount;
+
+
+#ifdef PROFILE
+	tics = 1;
+	return;
+#endif
+
+//
+// calculate tics since last refresh for adaptive timing
+//
+	if (lasttimecount > TimeCount)
+		TimeCount = lasttimecount;              // if the game was paused a LONG time
+
+#if 0
+	if (DemoMode)                                   // demo recording and playback needs
+	{                                                               // to be constant
+//
+// take DEMOTICS or more tics, and modify Timecount to reflect time taken
+//
+		oldtimecount = lasttimecount;
+		while (TimeCount<oldtimecount+DEMOTICS*2)
+		;
+		lasttimecount = oldtimecount + DEMOTICS;
+		TimeCount = lasttimecount + DEMOTICS;
+		realtics = tics = DEMOTICS;
+	}
+	else
+#endif
+	{
+//
+// non demo, so report actual time
+//
+		newtime = TimeCount;
+		realtics = tics = newtime-lasttimecount;
+		lasttimecount = newtime;
+
+#ifdef FILEPROFILE
+			strcpy (scratch,"\tTics:");
+			itoa (tics,str,10);
+			strcat (scratch,str);
+			strcat (scratch,"\n");
+			write (profilehandle,scratch,strlen(scratch));
+#endif
+
+		if (tics>MAXTICS)
+		{
+			TimeCount -= (tics-MAXTICS);
+			tics = MAXTICS;
+		}
+
+		if (realtics>MAXREALTICS)
+			realtics = MAXREALTICS;
+	}
+}
+
+
+//==========================================================================
+
+
+/*
+========================
+=
+= DrawHand
+=
+========================
+*/
+
+void    DrawHand (void)
+{
+	#define HAND_X_POS      ((VIEWWIDTH/16)-(10/2))         // "10" = hand width in bytes
+
+	#define picnum HAND1PICM
+
+	memptr source;
+	unsigned dest,width,height;
+
+//      if (gamestate.shotpower || boltsleft)
+//              picnum += (((unsigned)TimeCount>>3)&1);
+
+	source = grsegs[picnum];
+	dest = ylookup[VIEWHEIGHT-handheight]+HAND_X_POS+bufferofs;                     // 12
+	width = picmtable[picnum-STARTPICM].width;
+	height = picmtable[picnum-STARTPICM].height;
+
+	VW_MaskBlock(source,0,dest,width,handheight,width*height);
+	EGAMAPMASK(15);
+}
+
+//==========================================================================
+
+
+/*
+========================
+=
+= ThreeDRefresh
+=
+========================
+*/
+
+void    ThreeDRefresh (void)
+{
+	int tracedir;
+
+restart:
+	aborttrace = false;
+
+//
+// clear out the traced array
+//
+asm     mov     ax,ds
+asm     mov     es,ax
+asm     mov     di,OFFSET spotvis
+asm     xor     ax,ax
+asm     mov     cx,[mapwidth]           // mapheight*32 words
+asm     shl     cx,1
+asm     shl     cx,1
+asm     shl     cx,1
+asm     shl     cx,1
+asm     shl     cx,1
+asm     rep stosw
+
+
+//
+// set up variables for this view
+//
+
+	viewangle = player->angle;
+	fineviewangle = viewangle*(FINEANGLES/ANGLES);
+	viewsin = sintable[viewangle];
+	viewcos = costable[viewangle];
+	viewx = player->x - FixedByFrac(FOCALLENGTH,viewcos);
+	viewy = player->y + FixedByFrac(FOCALLENGTH,viewsin);
+	viewx &= 0xfffffc00;            // stop on a pixel boundary
+	viewy &= 0xfffffc00;
+	viewx += 0x180;
+	viewy += 0x180;
+	viewxpix = viewx>>10;
+	viewypix = viewy>>10;
+
+	focal.x = viewx>>TILESHIFT;
+	focal.y = viewy>>TILESHIFT;
+
+//
+// find the rightmost visable tile in view
+//
+	tracedir = viewangle + lastangle;
+	if (tracedir<0)
+	  tracedir+=ANGLES;
+	else if (tracedir>=ANGLES)
+	  tracedir-=ANGLES;
+	TraceRay( tracedir );
+	right.x = tile.x;
+	right.y = tile.y;
+
+//
+// find the leftmost visable tile in view
+//
+	tracedir = viewangle + firstangle;
+	if (tracedir<0)
+	  tracedir+=ANGLES;
+	else if (tracedir>=ANGLES)
+	  tracedir-=ANGLES;
+	TraceRay( tracedir );
+
+//
+// follow the walls from there to the right
+//
+	rightwall = &walls[1];
+	FollowWalls ();
+
+	if (aborttrace)
+		goto restart;
+
+//
+// actually draw stuff
+//
+	if (++screenpage == 3)
+		screenpage = 0;
+
+	bufferofs = screenloc[screenpage];
+
+	EGAWRITEMODE(2);
+	EGAMAPMASK(15);
+
+//
+// draw the wall list saved be FollowWalls ()
+//
+//      animframe = (TimeCount&8)>>3;
+
+//
+// draw all the scaled images
+//
+	asm     mov     dx,GC_INDEX
+
+	asm     mov     ax,GC_COLORDONTCARE
+	asm     out     dx,ax                                           // don't look at any of the planes
+
+	asm     mov     ax,GC_MODE + 256*(10)           // read mode 1, write mode 2
+	asm     out     dx,ax
+
+	asm     mov     al,GC_BITMASK
+	asm     out     dx,al
+
+	AnimateWallList();
+	DrawWallList();
+	DrawScaleds();
+
+	EGAWRITEMODE(0);
+	EGABITMASK(0xff);
+
+//
+// draw hand
+//
+	if (handheight)
+		DrawHand ();
+
+//
+// show screen and time last cycle
+//
+	if (fizzlein)
+	{
+		fizzlein = false;
+		FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,true);
+		lasttimecount = TimeCount;
+		if (MousePresent) Mouse(MDelta);        // Clear accumulated mouse movement
+	}
+
+asm     cli
+asm     mov     cx,[bufferofs]
+asm     mov     dx,3d4h         // CRTC address register
+asm     mov     al,0ch          // start address high register
+asm     out     dx,al
+asm     inc     dx
+asm     mov     al,ch
+asm     out     dx,al           // set the high byte
+asm     dec     dx
+asm     mov     al,0dh          // start address low register
+asm     out     dx,al
+asm     inc     dx
+asm     mov     al,cl
+asm     out     dx,al           // set the low byte
+asm     sti
+
+	displayofs = bufferofs;
+
+	CalcTics ();
+
+}
+
diff --git a/src/lib/hb/c6_game.c b/src/lib/hb/c6_game.c
new file mode 100755
index 00000000..d31a0470
--- /dev/null
+++ b/src/lib/hb/c6_game.c
@@ -0,0 +1,1599 @@
+/* Catacomb Apocalypse Source Code
+ * Copyright (C) 1993-2014 Flat Rock Software
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// C3_GAME.C
+
+#include <stdlib.h>
+
+#include "DEF.H"
+#include "gelib.h"
+#pragma hdrstop
+
+#ifdef PROFILE
+#include "TIME.H"
+#endif
+
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+#define NUMLUMPS				45
+
+#define EYESTALKLUMP			0
+#define BLOBLUMP				1
+#define BOLTLUMP				2
+#define NUKELUMP				3
+#define POTIONLUMP			4
+#define RKEYLUMP				5
+#define YKEYLUMP				6
+#define GKEYLUMP				7
+#define BKEYLUMP				8
+#define RGEMLUMP				9
+#define GGEMLUMP				10
+#define BGEMLUMP				11
+#define YGEMLUMP				12
+#define PGEMLUMP				13
+#define CHESTLUMP				14
+#define PLAYERLUMP			15
+#define FTIMELUMP				16
+#define PORTALLUMP			17
+#define COLUMN1LUMP			18
+#define FIREPOTLUMP			19
+#define COLUMN2LUMP			20
+#define EYELUMP				21
+#define FUTUREMAGELUMP		22
+#define FORCEFIELDLUMP		23
+#define ROBOTANKLUMP			24
+#define RAMBONELUMP			25
+#define STOMPYLUMP			26
+#define TROLLLUMP				27
+#define WIZARDLUMP			28
+#define HEADLUMP				29
+#define INVISDUDELUMP		30
+#define BUGLUMP				31
+#define CYBORGLUMP			32
+#define WATERCHESTLUMP		33
+#define GRELLUMP				34
+#define RAYLUMP				35
+#define COLUMN3LUMP			36
+#define OLDCHESTLUMP			37
+#define OLDFIREPOTLUMP		38
+#define COLUMN4LUMP			39
+#define TOMB1LUMP				40
+#define TOMB2LUMP				41
+#define DEMONLUMP				42
+#define COLUMN5LUMP			43
+
+int     lumpstart[NUMLUMPS] = {
+EYESTALK_LUMP_START,
+BLOB_LUMP_START,
+BOLT_LUMP_START,
+NUKE_LUMP_START,
+POTION_LUMP_START,
+RKEY_LUMP_START,
+YKEY_LUMP_START,
+GKEY_LUMP_START,
+BKEY_LUMP_START,
+RGEM_LUMP_START,
+GGEM_LUMP_START,
+BGEM_LUMP_START,
+YGEM_LUMP_START,
+PGEM_LUMP_START,
+CHEST_LUMP_START,
+PLAYER_LUMP_START,
+TIME_LUMP_START,
+PORTAL_LUMP_START,
+COLUMN1_LUMP_START,
+FFIREPOT_LUMP_START,
+COLUMN2_LUMP_START,
+EYE_LUMP_START,
+FUTUREMAGE_LUMP_START,
+FORCEFIELD_LUMP_START,
+ROBOTANK_LUMP_START,
+RAMBONE_LUMP_START,
+STOMPY_LUMP_START,
+TROLL_LUMP_START,
+WIZARD_LUMP_START,
+HEAD_LUMP_START,
+INVISDUDE_LUMP_START,
+BUG_LUMP_START,
+CYBORG_LUMP_START,
+O_WATER_CHEST_LUMP_START,
+GREL_LUMP_START,
+RAY_LUMP_START,
+COLUMN3_LUMP_START,
+OLD_CHEST_LUMP_START,
+OFIREPOT_LUMP_START,
+COLUMN4_LUMP_START,
+TOMB1_LUMP_START,
+TOMB2_LUMP_START,
+DEMON_LUMP_START,
+COLUMN5_LUMP_START,
+};
+
+
+int     lumpend[NUMLUMPS] = {
+EYESTALK_LUMP_END,
+BLOB_LUMP_END,
+BOLT_LUMP_END,
+NUKE_LUMP_END,
+POTION_LUMP_END,
+RKEY_LUMP_END,
+YKEY_LUMP_END,
+GKEY_LUMP_END,
+BKEY_LUMP_END,
+RGEM_LUMP_END,
+GGEM_LUMP_END,
+BGEM_LUMP_END,
+YGEM_LUMP_END,
+PGEM_LUMP_END,
+CHEST_LUMP_END,
+PLAYER_LUMP_END,
+TIME_LUMP_END,
+PORTAL_LUMP_END,
+COLUMN1_LUMP_END,
+FFIREPOT_LUMP_END,
+COLUMN2_LUMP_END,
+EYE_LUMP_END,
+FUTUREMAGE_LUMP_END,
+FORCEFIELD_LUMP_END,
+ROBOTANK_LUMP_END,
+RAMBONE_LUMP_END,
+STOMPY_LUMP_END,
+TROLL_LUMP_END,
+WIZARD_LUMP_END,
+HEAD_LUMP_END,
+INVISDUDE_LUMP_END,
+BUG_LUMP_END,
+CYBORG_LUMP_END,
+O_WATER_CHEST_LUMP_END,
+GREL_LUMP_END,
+RAY_LUMP_END,
+COLUMN3_LUMP_END,
+OLD_CHEST_LUMP_END,
+OFIREPOT_LUMP_END,
+COLUMN4_LUMP_END,
+TOMB1_LUMP_END,
+TOMB2_LUMP_END,
+DEMON_LUMP_END,
+COLUMN5_LUMP_END,
+};
+
+
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+unsigned        latchpics[NUMLATCHPICS];
+unsigned        tileoffsets[NUMTILE16];
+unsigned        textstarts[27];
+
+boolean splitscreen=false;
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+boolean lumpneeded[NUMLUMPS];
+
+
+//===========================================================================
+
+
+
+/*
+==========================
+=
+= ScanInfoPlane
+=
+= Spawn all actors and mark down special places
+=
+==========================
+*/
+
+void ScanInfoPlane (void)
+{
+	unsigned char hibyte;
+	unsigned        x,y,i,j;
+	unsigned int tile;
+	unsigned        far     *start;
+
+	InitObjList();                  // start spawning things with a clean slate
+
+	scolor = gcolor = 0;
+	skycolor = &scolor;
+	groundcolor = &gcolor;
+
+
+	memset (lumpneeded,0,sizeof(lumpneeded));
+
+	start = mapsegs[2];
+	for (y=0;y<mapheight;y++)
+		for (x=0;x<mapwidth;x++)
+		{
+			tile = *start++;
+			hibyte = tile >> 8;
+			tile &= 0xff;
+
+			switch (hibyte)
+			{
+				char hi;
+
+				case 0xFB:
+					wall_anim_time = tile;
+					tile = 0;
+					break;
+
+				case 0xfa:								// sky/ground color
+					x++;
+					tile = *start++;
+					hi = tile >> 8;
+					tile &= 0xff;
+					switch (hibyte)
+					{
+						case 0xfa:			// sky / ground color
+							scolor = ((hi)|(hi<<8));
+							gcolor = ((tile)|(tile<<8));
+							skycolor = &scolor;
+							groundcolor = &gcolor;
+						break;
+
+					}
+				break;
+			}
+
+			if ((!tile) || (hibyte))
+				continue;
+
+			switch (tile)
+			{
+			case 1:
+			case 2:
+			case 3:
+			case 4:
+				lumpneeded[PLAYERLUMP] = true;
+				SpawnPlayer(x,y,NORTH+tile-1);
+			break;
+
+			case 5:
+			case 6:
+			case 7:
+			case 8:
+			case 9:
+			case 10:
+			case 11:
+				lumpneeded[tile-5+BOLTLUMP] = true;
+				SpawnBonus(x,y,tile-5);
+			break;
+
+			case 12:
+				lumpneeded[EYESTALKLUMP] = true;
+				SpawnAquaMan(x, y);
+			break;
+
+
+			case 13:
+				lumpneeded[BLOBLUMP] = true;
+				SpawnBlob(x, y);
+			break;
+
+
+			case 14:
+				lumpneeded[BUGLUMP] = true;
+				SpawnBug(x, y);
+			break;
+
+			case 15:
+				lumpneeded[CYBORGLUMP] = true;
+				SpawnCyborgDemon(x, y);
+			break;
+
+			case 16:
+				lumpneeded[EYELUMP] = true;
+				SpawnShooterEye(x, y);
+			break;
+
+			case 17:
+				lumpneeded[FUTUREMAGELUMP] = true;
+				SpawnFutureMage(x, y);
+			break;
+
+			case 18:
+				lumpneeded[INVISDUDELUMP] = true;
+				SpawnInvisDude(x, y);
+			break;
+
+			case 19:
+				lumpneeded[ROBOTANKLUMP] = true;
+				SpawnRoboTank(x, y);
+			break;
+
+			case 20:
+				lumpneeded[RAMBONELUMP] = true;
+				SpawnRamBone(x, y);
+			break;
+
+			case 21:
+				lumpneeded[STOMPYLUMP] = true;
+				SpawnStompy(x, y);
+			break;
+
+			case 22:
+				lumpneeded[TROLLLUMP] = true;
+				SpawnTroll(x, y);
+			break;
+
+			case 23:
+				lumpneeded[WIZARDLUMP] = true;
+				SpawnWizard(x, y);
+			break;
+
+			case 24:
+				SpawnBounce(x, y, 0);
+			break;
+
+			case 25:
+				SpawnBounce(x, y, 1);
+			break;
+
+			case 26:
+				lumpneeded[RKEYLUMP] = lumpneeded[GRELLUMP] = true;
+				SpawnGrelminar (x,y);
+			break;
+
+			case 27:
+				lumpneeded[EYELUMP] = true;
+				SpawnRunningEye(x,y);
+			break;
+
+			case 28:
+				lumpneeded[RAYLUMP] = true;
+				SpawnRay(x, y);
+			break;
+
+			case 29:
+				lumpneeded[HEADLUMP] = true;
+				SpawnEgyptianHead(x, y);
+			break;
+
+			case 30:
+				lumpneeded[DEMONLUMP] = true;
+				SpawnDemon(x, y);
+			break;
+
+			case 31:
+				lumpneeded[COLUMN5LUMP] = true;
+				SpawnMiscObjects(x, y, 9);
+			break;
+
+			case 32:
+				SpawnInvisWallCntroller(x, y);
+			break;
+
+			case 33:
+			break;
+
+			case 34:
+			break;
+
+			case 35:
+			break;
+
+			case 36:
+				lumpneeded[COLUMN1LUMP] = true;
+				SpawnMiscObjects(x, y, 1);
+			break;
+
+			case 37:
+				lumpneeded[FIREPOTLUMP] = true;
+				SpawnMiscObjects(x, y, 4);
+			break;
+
+			case 38:
+				lumpneeded[PORTALLUMP] = true;
+				SpawnWarp(x, y);
+			break;
+
+			case 39:
+				lumpneeded[FTIMELUMP] = true;
+				SpawnFTime(x,y);
+			break;
+
+
+			case 40:
+			case 41:
+			case 42:
+			case 43:
+			case 44:
+				lumpneeded[tile-40+RGEMLUMP] = true;
+				SpawnBonus(x,y,tile-40+B_RGEM);
+			break;
+
+			case 45:
+				lumpneeded[COLUMN2LUMP] = true;
+				SpawnMiscObjects(x, y, 2);
+			break;
+
+			case 46:
+				lumpneeded[COLUMN3LUMP] = true;
+				SpawnMiscObjects(x, y, 3);
+			break;
+
+			case 47:
+				lumpneeded[FORCEFIELDLUMP] = true;
+				SpawnForceField(x, y);
+			break;
+
+			case 48:
+				lumpneeded[OLDCHESTLUMP] = true;
+				SpawnBonus(x, y, B_OLDCHEST);
+			break;
+
+			case 49:        // chest
+				if (gcolor == 0x0101)
+					lumpneeded[WATERCHESTLUMP] = true;
+				else
+					lumpneeded[CHESTLUMP] = true;
+				SpawnBonus(x,y,B_CHEST);
+			break;
+
+			case 50:
+				lumpneeded[COLUMN4LUMP] = true;
+				SpawnMiscObjects(x, y, 5);
+			break;
+
+			case 51:
+				lumpneeded[OLDFIREPOTLUMP] = true;
+				SpawnMiscObjects(x, y, 6);
+			break;
+
+			case 52:
+				lumpneeded[TOMB1LUMP] = true;
+				SpawnMiscObjects(x, y, 7);
+			break;
+
+			case 53:
+				lumpneeded[TOMB2LUMP] = true;
+				SpawnMiscObjects(x, y, 8);
+			break;
+
+			case 54:
+			break;
+
+			case 55:
+			break;
+
+			case 56:
+			break;
+
+			case 57:
+			break;
+
+			case 58:
+			break;
+
+			case 59:
+			break;
+
+			case 60:
+			break;
+
+			case 61:
+			break;
+
+			case 62:
+			break;
+
+			case 63:
+			break;
+
+			case 64:
+			break;
+
+			case 65:
+			break;
+
+			case 66:
+			break;
+
+			case 67:
+			break;
+
+			case 68:
+			break;
+
+			case 69:
+			break;
+
+			case 70:
+			break;
+
+			case 71:
+			break;
+
+			}
+		}
+
+}
+
+//==========================================================================
+
+/*
+==================
+=
+= ScanText
+=
+==================
+*/
+
+void ScanText (void)
+{
+	int     i;
+	char far *text;
+
+	text = (char _seg *)grsegs[LEVEL1TEXT+mapon];
+
+	textstarts[0] = 0;
+
+	for (i=1;i<=26;i++)
+	{
+		while (*text != '\n')
+		{
+			if (*text == '\r')
+				*text = 0;
+			text++;
+		}
+		text++;
+		textstarts[i] = FP_OFF(text);
+	}
+
+}
+
+//==========================================================================
+
+/*
+==================
+=
+= DrawEnterScreen
+=
+==================
+*/
+#if 0
+static  char    *levelnames[] =
+				{
+					"Programmers Test Map",
+					"The Garden of Tears",
+					"The Den of Zombies",
+					"The Mausoleum Grounds",
+					"The Main Floor of the Mausoleum",
+					"Mike's Blastable Passage",
+					"The Crypt of Nemesis the Undead",
+					"The Subterranean Vault",
+					"The Ancient Aqueduct",
+					"The Orc Mines",
+					"The Lair of the Troll",
+					"The Demon's Inferno",
+					"The Battleground of the Titans",
+					"The Coven of Mages",
+					"The Inner Sanctum",
+					"The Haunt of Nemesis",
+					"The Passage to the Surface",
+					"Big Jim's Domain",
+					"Nolan",
+					"19",
+					"20",
+					"21",
+					"22",
+					"23",
+					"24",
+					"25",
+					"26",
+					"27",
+					"28",
+					"29",
+					"30",
+					"31",
+					"32",
+					"33",
+					"34",
+					"35",
+					"36",
+					"37",
+					"38",
+					"39",
+				};
+#endif
+
+void DrawEnterScreen ()
+{
+	int width;
+
+	bufferofs = displayofs = screenloc[screenpage];
+	VW_Bar(0,0,VIEWWIDTH,VIEWHEIGHT,0);
+
+//	width = strlen(levelnames[gamestate.mapon]);
+	width = strlen("A new challenge awaits you.");
+	if (width < 20)
+		width = 20;
+	CenterWindow(width,3);
+	US_CPrint("\nA new challenge awaits you.\n");
+//	US_CPrint(levelnames[gamestate.mapon]);
+}
+
+//==========================================================================
+
+boolean tileneeded[NUMFLOORS];
+
+
+/*
+==================
+=
+= CacheScaleds
+=
+==================
+*/
+
+void CacheScaleds (void)
+{
+	int     i,j;
+	unsigned        source,dest;
+
+	FreeUpMemory ();
+	CA_CacheGrChunk(LEVEL1TEXT+mapon);
+	ScanText ();
+
+//
+// make sure we are displaying screenpage 0
+//
+	if (screenpage)
+	{
+		source = screenloc[screenpage];
+		dest = screenloc[0];
+		VW_ScreenToScreen (source,dest,40,VIEWHEIGHT);
+		screenpage = 0;
+		VW_SetScreen (dest,0);
+		displayofs = dest;
+	}
+
+//
+// cache wall pictures
+//
+	for (i=1;i<NUMFLOORS;i++)
+		if (tileneeded[i])
+		{
+			SetupScaleWall (walllight1[i]);
+			SetupScaleWall (walldark1[i]);
+		}
+
+//
+// cache the actor pictures
+//
+	for (i=0;i<NUMLUMPS;i++)
+		if (lumpneeded[i])
+			for (j=lumpstart[i];j<=lumpend[i];j++)
+				SetupScalePic(j);
+
+	source = screenloc[0];
+	for (i=1;i<=2;i++)
+	{
+		dest = screenloc[i];
+		VW_ScreenToScreen (source,dest,40,VIEWHEIGHT);
+	}
+
+	screenpage = 1;
+}
+
+//==========================================================================
+
+
+/*
+==================
+=
+= SetupGameLevel
+=
+==================
+*/
+
+void SetupGameLevel ()
+{
+	int     x,y,i,loop;
+	unsigned        far *map,tile,far *spotptr,spot;
+	unsigned                search_tile;
+	boolean		exploding_walls_present = false;
+
+	memset (tileneeded,0,sizeof(tileneeded));
+//
+// randomize if not a demo
+//
+#if 0
+	if (DemoMode)
+	{
+		US_InitRndT(false);
+		gamestate.difficulty = gd_Normal;
+	}
+	else
+#endif
+		US_InitRndT(true);
+
+//
+// load the level
+//
+	CA_CacheMap (gamestate.mapon);
+
+	mapwidth = mapheaderseg[mapon]->width;
+	mapheight = mapheaderseg[mapon]->height;
+
+//
+// make a lookup table for the maps left edge
+//
+	spot = 0;
+	for (y=0;y<mapheight;y++)
+	{
+	  farmapylookup[y] = spot;
+	  spot += mapwidth;
+	}
+
+
+//
+// copy the wall data to a data segment array
+//
+	memset (tilemap,0,sizeof(tilemap));
+	memset (actorat,0,sizeof(actorat));
+	map = mapsegs[0];
+	spotptr = mapsegs[2];
+	for (y=0;y<mapheight;y++)
+		for (x=0;x<mapwidth;x++)
+		{
+			tile = *map++;
+
+			if (((*spotptr)>>8) == EXP_WALL_CODE)
+			{
+				exploding_walls_present = true;
+			}
+
+			if (tile<NUMFLOORS)
+			{
+#if 0
+				if (tile == WALL_SKELETON_CODE)
+				{
+					tileneeded[tile+1] = tileneeded[tile+2] = true;
+					tilemap[x][y] = tile;
+				}
+#endif
+				if ((tile == 66) || (tile == 67) || (tile == 68) || (tile == 69))
+				{
+					if ((tile == 66) || (tile == 67))
+						tileneeded[tile+2] = true;
+					tileneeded[21] = tileneeded[tile] = true;
+					tilemap[x][y] = tile;
+				}
+				else
+				if (tile != INVISIBLEWALL)
+				{
+					tileneeded[tile] = true;
+					tilemap[x][y] = tile;
+					if (ANIM_FLAGS(tile))
+					{
+						search_tile = tile+(char signed)ANIM_FLAGS(tile);
+
+						if (!tileneeded[search_tile])
+							while (search_tile != tile)
+							{
+								tileneeded[search_tile] = true;
+								if (ANIM_FLAGS(search_tile))
+									search_tile += (char signed)ANIM_FLAGS(search_tile);
+								else
+									TrashProg("Unending Tile Animation!");
+							}
+					}
+
+				}
+				if (tile>0)
+					(unsigned)actorat[x][y] = tile;
+			}
+			spotptr++;
+		}
+
+
+	//
+	// Mark any gfx chunks needed
+	//
+
+//      CA_MarkGrChunk(NORTHICONSPR);
+//      CA_CacheMarks(NULL);
+
+
+//
+// decide which graphics are needed and spawn actors
+//
+	head_base_delay = 0;  // (1*60) + random(1*60);
+	ScanInfoPlane ();
+	_fmemset(wall_anim_pos,0,sizeof(wall_anim_pos));
+
+
+//
+// mark which exploding walls are needed ---- the check for floor color
+// is preformed in ScanInfoPlane.
+//
+
+	if (exploding_walls_present)
+	{
+				extern unsigned gnd_colors[];
+
+				if (gcolor == 0x0101)
+					tileneeded[WATEREXP] = tileneeded[WATEREXP+1] = tileneeded[WATEREXP+2] = true;
+				else
+					tileneeded[WALLEXP] = tileneeded[WALLEXP+1] = tileneeded[WALLEXP+2] = true;
+
+	}
+
+
+//
+// have the caching manager load and purge stuff to make sure all marks
+// are in memory
+//
+	CA_LoadAllSounds ();
+}
+
+//==========================================================================
+
+/*
+=====================
+=
+= LatchDrawPic
+=
+=====================
+*/
+
+void LatchDrawPic (unsigned x, unsigned y, unsigned picnum)
+{
+	unsigned height, source, dest;
+	unsigned wide;
+
+	wide = pictable[picnum-STARTPICS].width;
+	height = pictable[picnum-STARTPICS].height;
+	dest = bufferofs + ylookup[y]+x;
+	source = latchpics[picnum-FIRSTLATCHPIC];
+
+	EGAWRITEMODE(1);
+	EGAMAPMASK(15);
+
+asm     mov     bx,[linewidth]
+asm     sub     bx,[wide]
+
+asm     mov     ax,[screenseg]
+asm     mov     es,ax
+asm     mov     ds,ax
+
+asm     mov     si,[source]
+asm     mov     di,[dest]
+asm     mov     dx,[height]                             // scan lines to draw
+asm     mov     ax,[wide]
+
+lineloop:
+asm     mov     cx,ax
+asm     rep     movsb
+asm     add     di,bx
+
+asm     dec     dx
+asm     jnz     lineloop
+
+asm     mov     ax,ss
+asm     mov     ds,ax                                   // restore turbo's data segment
+
+	EGAWRITEMODE(0);
+}
+
+#if USE_STRIPS
+
+//--------------------------------------------------------------------------
+// LatchDrawPicStrip() - srcoff is distance into source file (in PIXELS!)
+//--------------------------------------------------------------------------
+void LatchDrawPicStrip (unsigned x, unsigned y, unsigned picnum, unsigned srcoff)
+{
+	unsigned wide, height, source, dest, shift, srcmod;
+
+	shift = (srcoff & 7) >> 1;
+	srcoff >>= 3;
+	wide = pictable[picnum-STARTPICS].width;
+	srcmod = wide - linewidth + (shift != 3);
+	if (wide > linewidth)
+		wide = linewidth;
+	height = pictable[picnum-STARTPICS].height;
+	dest = bufferofs + ylookup[y]+x;
+
+	picnum = ((picnum - (FIRSTSTRIPPIC+1)) >> 2) + (shift);
+	source = latchpics[(FIRSTSTRIPPIC-FIRSTLATCHPIC+1)+picnum];
+
+	EGAWRITEMODE(1);
+	EGAMAPMASK(15);
+
+asm     mov     bx,[linewidth]
+asm     sub     bx,[wide]
+
+asm     mov     ax,[screenseg]
+asm     mov     es,ax
+asm     mov     ds,ax
+
+asm     mov     si,[source]
+asm	  add		 si,[srcoff]
+asm     mov     di,[dest]
+asm     mov     dx,[height]                             // scan lines to draw
+asm     mov     ax,[wide]
+
+lineloop:
+asm     mov     cx,ax
+asm     rep     movsb
+asm     add     di,bx
+asm	  add     si,[srcmod]
+
+asm     dec     dx
+asm     jnz     lineloop
+
+asm     mov     ax,ss
+asm     mov     ds,ax                                   // restore turbo's data segment
+
+	EGAWRITEMODE(0);
+}
+
+#endif
+
+
+//==========================================================================
+
+/*
+=====================
+=
+= Victory
+=
+=====================
+*/
+
+void Victory (boolean playsounds)
+{
+	struct Shape shape;
+
+	if (playsounds)
+	{
+		SD_PlaySound (GETBOLTSND);
+		SD_WaitSoundDone ();
+		SD_PlaySound (GETNUKESND);
+		SD_WaitSoundDone ();
+		SD_PlaySound (GETPOTIONSND);
+		SD_WaitSoundDone ();
+		SD_PlaySound (GETKEYSND);
+		SD_WaitSoundDone ();
+//		SD_PlaySound (GETSCROLLSND);
+//		SD_WaitSoundDone ();
+		SD_PlaySound (GETPOINTSSND);
+	}
+
+	FreeUpMemory();
+
+	if (!screenfaded)
+		VW_FadeOut();
+
+	NormalScreen ();
+
+	screenpage = bufferofs = 0;
+
+	CA_CacheGrChunk (FINALEPIC);
+	UNMARKGRCHUNK(FINALEPIC);
+	VW_DrawPic(0, 0, FINALEPIC);
+
+	VW_FadeIn();
+
+}
+
+//==========================================================================
+
+#if 0
+/*
+===================
+=
+= Died
+=
+===================
+*/
+
+void Died (void)
+{
+	unsigned page1,page2;
+//
+// fizzle fade screen to grey
+//
+	FreeUpMemory ();
+	SD_PlaySound (GAMEOVERSND);
+	bufferofs = screenloc[(screenpage+1)%3];
+	DisplayMsg("Though fallen, your Spirit ...",NULL);
+//      LatchDrawPic(0,0,DEADPIC);
+//      FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,false);
+	IN_ClearKeysDown();
+	while (!Keyboard[sc_Enter]);
+//      IN_Ack();
+	VW_SetScreen (bufferofs,0);
+	VW_ColorBorder(0);
+}
+#endif
+
+//==========================================================================
+
+/*
+===================
+=
+= NormalScreen
+=
+===================
+*/
+
+void NormalScreen (void)
+{
+	 VW_SetSplitScreen (200);
+	 bufferofs = displayofs = SCREEN1START;
+	 VW_Bar(0,0,320,200,0);
+	 bufferofs = SCREEN2START;
+	 VW_Bar(0,0,320,200,0);
+	 VW_SetScreen (displayofs,0);
+	 splitscreen = false;
+}
+
+//==========================================================================
+
+/*
+===================
+=
+= DrawPlayScreen
+=
+===================
+*/
+
+void DrawPlayScreen (void)
+{
+	int     i,j,p,m;
+
+	screenpage = 0;
+
+	bufferofs = 0;
+	VW_Bar (0,0,320,STATUSLINES,0);
+	for (i=0;i<3;i++)
+	{
+		bufferofs = screenloc[i];
+		VW_Bar (0,0,320,VIEWHEIGHT,0);
+	}
+
+	splitscreen = true;
+	VW_SetSplitScreen(120);
+	VW_SetScreen(screenloc[0],0);
+
+	CA_CacheGrChunk (STATUSPIC);
+
+	bufferofs = 0;
+	VW_DrawPic (0,0,STATUSPIC);
+
+	grneeded[STATUSPIC] &= ~ca_levelbit;
+	MM_SetPurge(&grsegs[STATUSPIC],3);
+
+//	RedrawStatusWindow ();
+	bufferofs = displayofs = screenloc[0];
+}
+
+
+//==========================================================================
+
+/*
+===================
+=
+= LoadLatchMem
+=
+===================
+*/
+
+unsigned latchmemavail;
+
+void LoadLatchMem (void)
+{
+	static unsigned base_destoff=0;
+	static int base_numpics=0;
+	int     i,j,p,m,numpics;
+	byte    far *src, far *dest;
+	unsigned        destoff;
+
+	EGAWRITEMODE(0);
+
+//
+// draw some pics into latch memory
+//
+
+  if (!base_numpics)
+  {
+
+//
+// tile 8s
+//
+	latchpics[0] = freelatch;
+	src = (byte _seg *)grsegs[STARTTILE8];
+	dest = MK_FP(0xa000,freelatch);
+
+	for (i=0;i<NUMTILE8;i++)
+	{
+		for (p=0;p<4;p++)
+		{
+			m = 1<<p;
+			asm     mov     dx,SC_INDEX
+			asm     mov     al,SC_MAPMASK
+			asm     mov     ah,[BYTE PTR m]
+			asm     out     dx,ax
+			for (j=0;j<8;j++)
+				*(dest+j)=*src++;
+		}
+		dest+=8;
+	}
+
+//
+// tile 16s
+//
+	src = (byte _seg *)grsegs[STARTTILE16];
+
+	for (i=0;i<NUMTILE16;i++)
+	{
+		CA_CacheGrChunk (STARTTILE16+i);
+		src = (byte _seg *)grsegs[STARTTILE16+i];
+		if (src)
+		{
+			tileoffsets[i] = FP_OFF(dest);
+			for (p=0;p<4;p++)
+			{
+				m = 1<<p;
+				asm     mov     dx,SC_INDEX
+				asm     mov     al,SC_MAPMASK
+				asm     mov     ah,[BYTE PTR m]
+				asm     out     dx,ax
+				for (j=0;j<32;j++)
+					*(dest+j)=*src++;
+			}
+			dest+=32;
+			MM_FreePtr (&grsegs[STARTTILE16+i]);
+			UNMARKGRCHUNK(STARTTILE16+i);
+		}
+		else
+			tileoffsets[i] = 0;
+	}
+
+
+//
+// pics
+//
+	numpics=1;
+	destoff = FP_OFF(dest);
+	for (i=FIRSTLATCHPIC+1;i<FIRSTGROUNDPIC;i++)
+	{
+		latchpics[numpics++] = destoff;
+		CA_CacheGrChunk (i);
+		j = pictable[i-STARTPICS].width * pictable[i-STARTPICS].height;
+		VW_MemToScreen (grsegs[i],destoff,j,1);
+		destoff+=j;
+		MM_FreePtr (&grsegs[i]);
+		UNMARKGRCHUNK(i);
+	}
+
+	base_numpics = numpics;
+	base_destoff = destoff;
+
+  }
+
+	numpics = base_numpics;
+	destoff = base_destoff;
+
+#if USE_STRIPS
+//
+// ground pics
+//
+	numpics++;
+	for (i=FIRSTGROUNDPIC+1;i<FIRSTSTRIPPIC;i++)
+	{
+		int shape = (*groundcolor & 0xf0) - 16;
+
+	// Is current shape needed?
+	//
+		if (shape != (i-(FIRSTGROUNDPIC+1)))
+		{
+			numpics++;
+			continue;
+		}
+
+		latchpics[numpics++] = destoff;
+		CA_CacheGrChunk (i);
+		j = pictable[i-STARTPICS].width * pictable[i-STARTPICS].height;
+		VW_MemToScreen (grsegs[i],destoff,j,1);
+		destoff+=j;
+		MM_FreePtr (&grsegs[i]);
+		UNMARKGRCHUNK(i);
+	}
+
+
+//
+// 'parallax' strips - used in place of a sky color
+//
+// Under current setup, each strip takes about 7k in latch memory.
+// To create 2 pixel scrolling, 4 strips are needed, that's 28k of
+// latch memory needed to produce this effect.
+//
+	numpics++;
+	for (i=FIRSTSTRIPPIC+1;i<FIRSTSCALEPIC;i++)
+	{
+		memptr work;
+		unsigned workdest,stripsize,planesize;
+		short loop,pic=i-STARTPICS;
+		int shape = (*skycolor & 0xf0) - 16;
+
+	// Is current shape needed?
+	//
+		if (shape != (i-(FIRSTSTRIPPIC+1)))
+		{
+			numpics++;
+			continue;
+		}
+
+	// CAL_ShiftSprite() works with the SRC and DST in the same
+	// segment. So we must allocate memory for two strips, and
+	// move the base strip into that segment. Then we can use the
+	// 2nd half of that memory for each shifted strip.
+	//
+		CA_CacheGrChunk (i);
+		planesize = (pictable[pic].width+1) * pictable[pic].height;
+		stripsize = planesize * 4;
+//		MM_GetPtr(&work,(stripsize*2)+0000);
+		MM_GetPtr(&work,65536);
+		movedata((unsigned)grsegs[i],0,(unsigned)work,0,stripsize);
+		workdest = 32768; //(stripsize+15) & 0xFFF0;
+
+	// Free base strip
+	//
+		MM_FreePtr (&grsegs[i]);
+		UNMARKGRCHUNK(i);
+
+	// Create three shifted strips and move 'em to latch!
+	//
+		for (loop=3; loop; loop--)
+		{
+		// Produce current shift for this strip
+		//
+			latchpics[numpics++] = destoff;
+			CAL_ShiftSprite ((unsigned)work,0,workdest,pictable[pic].width,
+								  pictable[pic].height,loop*2,false);
+
+		// Copy this shift to latch memory
+		//
+			VW_MemToScreen ((memptr)((unsigned)work+(workdest>>4)),destoff,planesize,1);
+			destoff+=planesize;
+		}
+
+	// Copy unshifted strip to latch
+	//
+		latchpics[numpics++] = destoff;
+		planesize = pictable[pic].width * pictable[pic].height;
+		VW_MemToScreen (work,destoff,planesize,1);
+		destoff+=planesize;
+
+	// Free work buffer
+	//
+		MM_FreePtr(&work);
+	}
+#endif
+
+// Keep track of how much latch memory we have...
+//
+	latchmemavail = 65535-destoff;
+
+	EGAMAPMASK(15);
+}
+
+//==========================================================================
+
+/*
+===================
+=
+= FizzleOut
+=
+===================
+*/
+
+void FizzleOut (int showlevel)
+{
+	unsigned page1,page2;
+//
+// fizzle fade screen to grey
+//
+	bufferofs = screenloc[(screenpage+1)%3];
+	if (showlevel)
+		DrawEnterScreen ();
+	FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,false);
+}
+
+//==========================================================================
+
+/*
+====================
+=
+= FreeUpMemory
+=
+====================
+*/
+
+void FreeUpMemory (void)
+{
+	int     i;
+
+	for (i=0;i<NUMSCALEPICS;i++)
+		if (shapedirectory[i])
+			MM_SetPurge (&(memptr)shapedirectory[i],3);
+
+	for (i=0;i<NUMSCALEWALLS;i++)
+		if (walldirectory[i])
+			MM_SetPurge (&(memptr)walldirectory[i],3);
+}
+
+//==========================================================================
+
+#if 0
+
+/*
+==================
+=
+= DrawHighScores
+=
+==================
+*/
+
+void    DrawHighScores(void)
+{
+	char            buffer[16],*str;
+	word            i,j,
+				w,h,
+				x,y;
+	HighScore       *s;
+
+
+	CA_CacheGrChunk (HIGHSCORESPIC);
+	VWB_DrawPic (0,0,HIGHSCORESPIC);
+	MM_SetPurge (&grsegs[HIGHSCORESPIC],3);
+	UNMARKGRCHUNK(HIGHSCORESPIC);
+
+	for (i = 0,s = Scores;i < MaxScores;i++,s++)
+	{
+		PrintY = 68 + (16 * i);
+
+		//
+		// name
+		//
+		PrintX = 60;
+		US_Print(s->name);
+
+		//
+		// level
+		//
+		ultoa(s->completed,buffer,10);
+		for (str = buffer;*str;str++)
+			*str = *str + (129 - '0');      // Used fixed-width numbers (129...)
+		USL_MeasureString(buffer,&w,&h);
+		PrintX = (25 * 8) - 8 - w;
+		US_Print(buffer);
+
+		//
+		// score
+		//
+		ultoa(s->score,buffer,10);
+		for (str = buffer;*str;str++)
+			*str = *str + (129 - '0');      // Used fixed-width numbers (129...)
+		USL_MeasureString(buffer,&w,&h);
+		PrintX = (34 * 8) - 8 - w;
+		US_Print(buffer);
+	}
+
+	fontcolor = F_BLACK;
+}
+
+
+
+/*
+=======================
+=
+= CheckHighScore
+=
+=======================
+*/
+
+void    CheckHighScore (long score,word other)
+{
+	word            i,j;
+	int                     n;
+	HighScore       myscore;
+
+	strcpy(myscore.name,"");
+	myscore.score = score;
+	myscore.completed = other;
+
+	for (i = 0,n = -1;i < MaxScores;i++)
+	{
+		if
+		(
+			(myscore.score > Scores[i].score)
+		||      (
+				(myscore.score == Scores[i].score)
+			&&      (myscore.completed > Scores[i].completed)
+			)
+		)
+		{
+			for (j = MaxScores;--j > i;)
+				Scores[j] = Scores[j - 1];
+			Scores[i] = myscore;
+			n = i;
+			HighScoresDirty = true;
+			break;
+		}
+	}
+
+	if (n != -1)
+	{
+	//
+	// got a high score
+	//
+		DrawHighScores ();
+		PrintY = 68 + (16 * n);
+		PrintX = 60;
+		US_LineInput(PrintX,PrintY,Scores[n].name,nil,true,MaxHighName,100);
+	}
+}
+
+#endif
+
+
+//==========================================================================
+
+/*
+===================
+=
+= GameLoop
+=
+===================
+*/
+
+void GameLoop (void)
+{
+	boolean wait = false;
+	int i,xl,yl,xh,yh;
+	char num[20];
+#ifdef PROFILE
+	clock_t start,end;
+#endif
+
+	DrawPlayScreen ();
+	IN_ClearKeysDown();
+
+restart:
+	if (!loadedgame)
+	{
+		gamestate.difficulty = restartgame;
+		restartgame = gd_Continue;
+		DrawEnterScreen ();
+		if (gamestate.mapon != 8)
+			fizzlein = true;
+		wait = true;
+	}
+
+	do
+	{
+		playstate = gd_Continue;
+		if (!loadedgame)
+			SetupGameLevel ();
+		else
+			loadedgame = false;
+
+		FreeUpMemory();
+		LoadLatchMem();
+		CacheScaleds ();
+
+		if (EASYMODEON)
+			DisplaySMsg("*** NOVICE ***", NULL);
+		else
+			DisplaySMsg("*** WARRIOR ***", NULL);
+
+		status_delay = 250;
+
+		RedrawStatusWindow();
+		if (wait)
+		{
+			VW_WaitVBL(120);
+			wait = false;
+		}
+
+#ifdef PROFILE
+start = clock();
+while (start == clock());
+start++;
+#endif
+
+		PlayLoop ();
+
+#ifdef PROFILE
+end = clock();
+itoa(end-start,str,10);
+		Quit (str);
+#endif
+
+
+		switch (playstate)
+		{
+		case ex_abort:
+			FreeUpMemory ();
+			return;
+		case ex_resetgame:
+			NewGame();
+		case ex_loadedgame:
+		case ex_warped:
+			FreeUpMemory();
+			if (playstate != ex_resetgame)
+				DisplayMsg("                                      ", NULL);
+			DisplaySMsg("                  ", NULL);
+			goto restart;
+		case ex_victorious:
+			screenpage = 0;
+			bufferofs = 0;
+			status_flag = 0;
+			return;
+		}
+
+	} while (1);
+
+}
+
+
+#if 0
+//
+// make wall pictures purgable
+//
+	for (i=0;i<NUMSCALEWALLS;i++)
+		if (walldirectory[i])
+			MM_SetPurge (&(memptr)walldirectory[i],3);
+
+
+//
+// cache wall pictures back in
+//
+	for (i=1;i<NUMFLOORS;i++)
+		if (tileneeded[i])
+		{
+			SetupScaleWall (walllight1[i]);
+			SetupScaleWall (walllight2[i]);
+			SetupScaleWall (walldark1[i]);
+			SetupScaleWall (walldark2[i]);
+		}
+#endif
diff --git a/src/lib/hb/c6_main.c b/src/lib/hb/c6_main.c
new file mode 100755
index 00000000..9e284ee3
--- /dev/null
+++ b/src/lib/hb/c6_main.c
@@ -0,0 +1,1083 @@
+/* Catacomb Apocalypse Source Code
+ * Copyright (C) 1993-2014 Flat Rock Software
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// C3_MAIN.C
+
+#define CATALOG
+
+#include <time.h>
+#include <stdarg.h>
+
+#include "DEF.H"
+#include "GELIB.H"
+#pragma hdrstop
+#include <dir.h>
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+typedef enum demo_screens {sc_logo,sc_title,sc_credits1,sc_credits2,sc_credits3,sc_credits4,sc_end} demo_screens;
+struct Shape   shape,
+					SdLogoShp,
+					TitleShp,
+					CreditBKShp,
+					Credit1Shp,
+					Credit2Shp,
+					Credit3Shp,
+					Credit4Shp,
+					Credit5Shp,
+					Credit6Shp,
+					Credit7Shp,
+					Credit8Shp,
+					Credit9Shp,
+					Credit10Shp;
+
+
+PresenterInfo MainHelpText;
+
+GameDiff restartgame;
+boolean loadedgame,abortgame,ingame;
+
+
+memptr		scalesegs[NUMPICS];
+char		str[80],str2[20];
+unsigned	tedlevelnum;
+boolean		tedlevel;
+gametype	gamestate;
+exittype	playstate;
+char	SlowMode = 0;
+int starting_level;
+
+short NumGames=0;
+unsigned Flags=0;
+
+boolean LoadShapes = true;
+boolean EASYMODEON = false;
+
+void DisplayIntroText(void);
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+
+
+//===========================================================================
+
+#if 0
+// JAB Hack begin
+#define	MyInterrupt	0x60
+void interrupt (*intaddr)();
+void interrupt (*oldintaddr)();
+	char	*JHParmStrings[] = {"no386",nil};
+
+void
+jabhack(void)
+{
+extern void far jabhack2(void);
+extern int far	CheckIs386(void);
+
+	int	i;
+
+	oldintaddr = getvect(MyInterrupt);
+
+	for (i = 1;i < _argc;i++)
+		if (US_CheckParm(_argv[i],JHParmStrings) == 0)
+			return;
+
+	if (CheckIs386())
+	{
+		jabhack2();
+		setvect(MyInterrupt,intaddr);
+	}
+}
+
+void
+jabunhack(void)
+{
+	setvect(MyInterrupt,oldintaddr);
+}
+//	JAB Hack end
+#endif
+
+//===========================================================================
+
+/*
+=====================
+=
+= NewGame
+=
+= Set up new game to start from the beginning
+=
+=====================
+*/
+
+void NewGame (void)
+{
+	if (!loadedgame)
+	{
+		memset (&gamestate,0,sizeof(gamestate));
+		gamestate.mapon = starting_level;
+		gamestate.body = MAXBODY;
+	}
+
+	BGFLAGS = BGF_NOT_LIGHTNING;
+	Flags &= FL_CLEAR;
+
+	boltsleft = bolttimer = 0;
+
+//	memset (gamestate.levels,-1,sizeof(gamestate.levels));
+}
+
+//===========================================================================
+
+#define RLETAG	0xABCD
+
+/*
+==================
+=
+= SaveTheGame
+=
+==================
+*/
+
+boolean	SaveTheGame(int file)
+{
+	word	i,compressed,expanded;
+	objtype	*o;
+	memptr	bigbuffer;
+
+	// save the sky and ground colors
+	if (!CA_FarWrite(file,(void far *)&skycolor,sizeof(skycolor)))
+		return(false);
+	if (!CA_FarWrite(file,(void far *)&groundcolor,sizeof(groundcolor)))
+		return(false);
+
+	if (!CA_FarWrite(file,(void far *)&FreezeTime,sizeof(FreezeTime)))
+		return(false);
+
+	if (!CA_FarWrite(file,(void far *)&gamestate,sizeof(gamestate)))
+		return(false);
+
+	if (!CA_FarWrite(file,(void far *)&EASYMODEON,sizeof(EASYMODEON)))
+		return(false);
+
+	expanded = mapwidth * mapheight * 2;
+	MM_GetPtr (&bigbuffer,expanded);
+
+	for (i = 0;i < 3;i+=2)	// Write planes 0 and 2
+	{
+//
+// leave a word at start of compressed data for compressed length
+//
+		compressed = (unsigned)CA_RLEWCompress ((unsigned huge *)mapsegs[i]
+			,expanded,((unsigned huge *)bigbuffer)+1,RLETAG);
+
+		*(unsigned huge *)bigbuffer = compressed;
+
+		if (!CA_FarWrite(file,(void far *)bigbuffer,compressed+2) )
+		{
+			MM_FreePtr (&bigbuffer);
+			return(false);
+		}
+	}
+
+	for (o = player;o;o = o->next)
+		if (!CA_FarWrite(file,(void far *)o,sizeof(objtype)))
+		{
+			MM_FreePtr (&bigbuffer);
+			return(false);
+		}
+
+	MM_FreePtr (&bigbuffer);
+
+	return(true);
+}
+
+//===========================================================================
+
+
+/*
+==================
+=
+= LoadTheGame
+=
+==================
+*/
+
+boolean	LoadTheGame(int file)
+{
+	unsigned	i,x,y;
+	objtype		*obj,*prev,*next,*followed;
+	unsigned	compressed,expanded;
+	unsigned	far *map,tile;
+	memptr		bigbuffer;
+
+	screenpage = 0;
+	FreeUpMemory();
+
+	playstate = ex_loadedgame;
+	// load the sky and ground colors
+	if (!CA_FarRead(file,(void far *)&skycolor,sizeof(skycolor)))
+		return(false);
+	if (!CA_FarRead(file,(void far *)&groundcolor,sizeof(groundcolor)))
+		return(false);
+
+	if (!CA_FarRead(file,(void far *)&FreezeTime,sizeof(FreezeTime)))
+		return(false);
+
+	if (!CA_FarRead(file,(void far *)&gamestate,sizeof(gamestate)))
+		return(false);
+
+	if (!CA_FarRead(file,(void far *)&EASYMODEON,sizeof(EASYMODEON)))
+		return(false);
+
+	SetupGameLevel ();		// load in and cache the base old level
+
+	if (!FindFile(Filename,"SAVE GAME",-1))
+		Quit("Error: Can't find saved game file!");
+
+	expanded = mapwidth * mapheight * 2;
+	MM_GetPtr (&bigbuffer,expanded);
+
+	for (i = 0;i < 3;i+=2)	// Read planes 0 and 2
+	{
+		if (!CA_FarRead(file,(void far *)&compressed,sizeof(compressed)) )
+		{
+			MM_FreePtr (&bigbuffer);
+			return(false);
+		}
+
+		if (!CA_FarRead(file,(void far *)bigbuffer,compressed) )
+		{
+			MM_FreePtr (&bigbuffer);
+			return(false);
+		}
+
+		CA_RLEWexpand ((unsigned huge *)bigbuffer,
+			(unsigned huge *)mapsegs[i],expanded,RLETAG);
+	}
+
+	MM_FreePtr (&bigbuffer);
+//
+// copy the wall data to a data segment array again, to handle doors and
+// bomb walls that are allready opened
+//
+	memset (tilemap,0,sizeof(tilemap));
+	memset (actorat,0,sizeof(actorat));
+	map = mapsegs[0];
+	for (y=0;y<mapheight;y++)
+		for (x=0;x<mapwidth;x++)
+		{
+			tile = *map++;
+			if (tile<NUMFLOORS)
+			{
+				if (tile != INVISIBLEWALL)
+					tilemap[x][y] = tile;
+				if (tile>0)
+					(unsigned)actorat[x][y] = tile;
+			}
+		}
+
+
+	// Read the object list back in - assumes at least one object in list
+
+	InitObjList ();
+	new = player;
+	while (true)
+	{
+		prev = new->prev;
+		next = new->next;
+		if (!CA_FarRead(file,(void far *)new,sizeof(objtype)))
+			return(false);
+		followed = new->next;
+		new->prev = prev;
+		new->next = next;
+		actorat[new->tilex][new->tiley] = new;	// drop a new marker
+
+		if (followed)
+			GetNewObj (false);
+		else
+			break;
+	}
+
+	return(true);
+}
+
+//===========================================================================
+
+/*
+==================
+=
+= ResetGame
+=
+==================
+*/
+
+void ResetGame(void)
+{
+	NewGame ();
+
+	ca_levelnum--;
+	ca_levelbit>>=1;
+	CA_ClearMarks();
+	ca_levelbit<<=1;
+	ca_levelnum++;
+}
+
+//===========================================================================
+
+
+/*
+==========================
+=
+= ShutdownId
+=
+= Shuts down all ID_?? managers
+=
+==========================
+*/
+
+void ShutdownId (void)
+{
+  US_Shutdown ();
+#ifndef PROFILE
+  SD_Shutdown ();
+  IN_Shutdown ();
+#endif
+  VW_Shutdown ();
+  CA_Shutdown ();
+  MM_Shutdown ();
+}
+
+
+//===========================================================================
+
+/*
+==========================
+=
+= InitGame
+=
+= Load a few things right away
+=
+==========================
+*/
+
+void InitGame (void)
+{
+	unsigned	segstart,seglength;
+	int			i,x,y;
+	unsigned	*blockstart;
+
+//	US_TextScreen();
+
+	MM_Startup ();
+	VW_Startup ();
+#ifndef PROFILE
+	IN_Startup ();
+	SD_Startup ();
+#endif
+	US_Startup ();
+
+	CA_Startup ();
+
+	US_Setup ();
+
+	US_SetLoadSaveHooks(LoadTheGame,SaveTheGame,ResetGame);
+
+
+//
+// load in and lock down some basic chunks
+//
+
+	CA_ClearMarks ();
+
+	CA_MarkGrChunk(STARTFONT);
+	CA_MarkGrChunk(STARTTILE8);
+	CA_MarkGrChunk(STARTTILE8M);
+	CA_MarkGrChunk(HAND1PICM);
+
+	CA_MarkGrChunk(NORTHICONSPR);
+	CA_CacheMarks (NULL);
+
+	MM_SetLock (&grsegs[STARTFONT],true);
+	MM_SetLock (&grsegs[STARTTILE8],true);
+	MM_SetLock (&grsegs[STARTTILE8M],true);
+	MM_SetLock (&grsegs[HAND1PICM],true);
+
+	fontcolor = WHITE;
+
+
+//
+// build some tables
+//
+	for (i=0;i<MAPSIZE;i++)
+		nearmapylookup[i] = &tilemap[0][0]+MAPSIZE*i;
+
+	for (i=0;i<PORTTILESHIGH;i++)
+		uwidthtable[i] = UPDATEWIDE*i;
+
+	blockstart = &blockstarts[0];
+	for (y=0;y<UPDATEHIGH;y++)
+		for (x=0;x<UPDATEWIDE;x++)
+			*blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;
+
+	BuildTables ();			// 3-d tables
+
+	SetupScaling ();
+
+#ifndef PROFILE
+//	US_FinishTextScreen();
+#endif
+
+#if 0
+//
+// reclaim the memory from the linked in text screen
+//
+	segstart = FP_SEG(&introscn);
+	seglength = 4000/16;
+	if (FP_OFF(&introscn))
+	{
+		segstart++;
+		seglength--;
+	}
+	MML_UseSpace (segstart,seglength);
+#endif
+
+	VW_SetScreenMode (GRMODE);
+	ge_textmode = false;
+//	VW_ColorBorder (3);
+	VW_ClearVideo (BLACK);
+
+//
+// initialize variables
+//
+	updateptr = &update[0];
+	*(unsigned *)(updateptr + UPDATEWIDE*PORTTILESHIGH) = UPDATETERMINATE;
+	bufferofs = 0;
+	displayofs = 0;
+	VW_SetLineWidth(SCREENWIDTH);
+}
+
+//===========================================================================
+
+void clrscr (void);		// can't include CONIO.H because of name conflicts...
+
+/*
+==========================
+=
+= Quit
+=
+==========================
+*/
+
+void Quit (char *error, ...)
+{
+	short exit_code=0;
+	unsigned	finscreen;
+
+	va_list ap;
+
+	va_start(ap,error);
+
+#ifndef CATALOG
+	if (!error)
+	{
+		CA_SetAllPurge ();
+		CA_CacheGrChunk (PIRACY);
+		finscreen = (unsigned)grsegs[PIRACY];
+	}
+#endif
+
+	ShutdownId ();
+
+	if (error && *error)
+	{
+		vprintf(error,ap);
+		exit_code = 1;
+	}
+#ifndef CATALOG
+	else
+	if (!NoWait)
+	{
+		movedata (finscreen,0,0xb800,0,4000);
+		bioskey (0);
+	}
+#endif
+
+	va_end(ap);
+
+#ifndef CATALOG
+	if (!error)
+	{
+		_argc = 2;
+		_argv[1] = "LAST.SHL";
+		_argv[2] = "ENDSCN.SCN";
+		_argv[3] = NULL;
+		if (execv("LOADSCN.EXE", _argv) == -1)
+		{
+			clrscr();
+			puts("Couldn't find executable LOADSCN.EXE.\n");
+			exit(1);
+		}
+	}
+#endif
+
+	exit(exit_code);
+}
+
+//===========================================================================
+
+/*
+==================
+=
+= TEDDeath
+=
+==================
+*/
+
+void	TEDDeath(void)
+{
+	ShutdownId();
+	execlp("TED5.EXE","TED5.EXE","/LAUNCH",NULL);
+}
+
+//===========================================================================
+
+/*
+====================
+=
+= DisplayDepartment
+=
+====================
+*/
+void DisplayDepartment(char *text)
+{
+	short temp;
+
+//	bufferofs = 0;
+	PrintY = 5;
+	WindowX = 17;
+	WindowW = 168;
+
+	VW_Bar(WindowX,PrintY+1,WindowW,7,0);
+	temp = fontcolor;
+	fontcolor = 10;
+	US_CPrintLine (text);
+	fontcolor = temp;
+}
+
+
+
+/*
+=====================
+=
+= DemoLoop
+=
+=====================
+*/
+
+static	char *ParmStrings[] = {"easy","normal","hard",""};
+
+void	DemoLoop (void)
+{
+
+/////////////////////////////////////////////////////////////////////////////
+// main game cycle
+/////////////////////////////////////////////////////////////////////////////
+
+	displayofs = bufferofs = 0;
+	VW_Bar (0,0,320,200,0);
+	VW_SetScreen(0,0);
+
+//
+// Read in all the graphic images needed for the title sequence
+//
+		VW_WaitVBL(1);
+		IN_ReadControl(0,&control);
+
+//	set EASYMODE
+//
+	if (stricmp(_argv[2], "1") == 0)
+		EASYMODEON = true;
+	else
+		EASYMODEON = false;
+
+// restore game
+//
+	if (stricmp(_argv[3], "1") == 0)
+	{
+		VW_FadeOut();
+		bufferofs = displayofs = 0;
+		VW_Bar(0,0,320,200,0);
+		if (GE_LoadGame())
+		{
+			loadedgame = true;
+			playstate = ex_loadedgame;
+			Keyboard[sc_Enter] = true;
+			VW_Bar(0,0,320,200,0);
+			ColoredPalette();
+		}
+		VW_Bar(0,0,320,200,0);
+		VW_FadeIn();
+	}
+
+	// Play a game
+	//
+		restartgame = gd_Normal;
+		NewGame();
+		GameLoop();
+}
+
+//-------------------------------------------------------------------------
+// DisplayIntroText()
+//-------------------------------------------------------------------------
+void DisplayIntroText()
+{
+	PresenterInfo pi;
+
+#ifdef TEXT_PRESENTER
+	char *toptext = "You stand before the gate leading into the Towne "
+						 "of Morbidity.... "
+						 "^XX";
+
+	char *bottomtext = "Enter now boldly to defeat the evil Nemesis "
+							 "deep inside the catacombs."
+							 "
+							 "^XX";
+#endif
+
+	char oldfontcolor=fontcolor;
+
+	fontcolor = 14;
+
+
+#ifdef TEXT_PRESENTER
+	pi.xl = 0;
+	pi.yl = 0;
+	pi.xh = 319;
+	pi.yh = 1;
+	pi.bgcolor = 0;
+	pi.script[0] = (char far *)toptext;
+	Presenter(&pi);
+
+	pi.yl = 160;
+	pi.yh = 161;
+	pi.script[0] = (char far *)bottomtext;
+	Presenter(&pi);
+
+#else
+	PrintY = 1;
+	PrintX = 0;
+	WindowX = 0;
+	WindowW = 320;
+
+
+	US_Print ("      A chilling wind greets you at the entrance\n");
+	US_Print ("            to the Sanctuary of the Dead.\n");
+
+	PrintY = 180;
+
+	fontcolor = 9;
+	US_Print ("                   Shall you proceed as\n");
+	fontcolor = 14;
+	US_Print ("                  N");
+	fontcolor = 9;
+	US_Print ("ovice   or");
+	fontcolor = 14;
+	US_Print ("   W");
+	fontcolor = 9;
+	US_Print ("arrior ?");
+
+#endif
+
+	fontcolor = oldfontcolor;
+}
+
+#if 0
+boolean ChooseGameLevel()
+{
+	char choices[] = {sc_Escape,sc_E,sc_N,sc_H,0},ch;
+
+	CenterWindow(20,10);
+
+	US_Print("\n   Choose difficulty level:\n");
+	US_Print("       (E)asy\n");
+	US_Print("       (N)ormal\n");
+	US_Print("       (H)ard\n");
+	US_Print("\n      (ESC)ape aborts\n");
+
+//	VW_UpdateScreen();
+	if ((ch=GetKeyChoice(choices)) == sc_Escape)
+	{
+		while (Keyboard[sc_Escape]);
+		LastScan = 0;
+		return(false);
+	}
+
+	if (ch == sc_E)
+		restartgame = gd_Easy;
+	else
+	if (ch == sc_N)
+		restartgame = gd_Normal;
+	else
+		restartgame = gd_Hard;
+
+	return(true);
+}
+#endif
+
+
+//===========================================================================
+
+/*
+==========================
+=
+= SetupScalePic
+=
+==========================
+*/
+
+void SetupScalePic (unsigned picnum)
+{
+	unsigned	scnum;
+
+	if (picnum == 1)
+		return;
+
+	scnum = picnum-FIRSTSCALEPIC;
+
+	if (shapedirectory[scnum])
+	{
+		MM_SetPurge (&(memptr)shapedirectory[scnum],0);
+		return;					// allready in memory
+	}
+
+	CA_CacheGrChunk (picnum);
+	DeplanePic (picnum);
+	shapesize[scnum] = BuildCompShape (&shapedirectory[scnum]);
+	grneeded[picnum]&= ~ca_levelbit;
+	MM_FreePtr (&grsegs[picnum]);
+}
+
+//===========================================================================
+
+/*
+==========================
+=
+= SetupScaleWall
+=
+==========================
+*/
+
+void SetupScaleWall (unsigned picnum)
+{
+	int		x,y;
+	unsigned	scnum;
+	byte	far *dest;
+
+	if (picnum == 1)
+		return;
+
+	scnum = picnum-FIRSTWALLPIC;
+
+	if (walldirectory[scnum])
+	{
+		MM_SetPurge (&walldirectory[scnum],0);
+		return;					// allready in memory
+	}
+
+	CA_CacheGrChunk (picnum);
+	DeplanePic (picnum);
+	MM_GetPtr(&walldirectory[scnum],64*64);
+	dest = (byte far *)walldirectory[scnum];
+	for (x=0;x<64;x++)
+		for (y=0;y<64;y++)
+			*dest++ = spotvis[y][x];
+	grneeded[picnum]&= ~ca_levelbit;
+	MM_FreePtr (&grsegs[picnum]);
+}
+
+//===========================================================================
+
+/*
+==========================
+=
+= SetupScaling
+=
+==========================
+*/
+
+void SetupScaling (void)
+{
+	int		i,x,y;
+	byte	far *dest;
+
+//
+// build the compiled scalers
+//
+	for (i=1;i<=VIEWWIDTH/2;i++)
+		BuildCompScale (i*2,&scaledirectory[i]);
+}
+
+//===========================================================================
+
+int	showscorebox;
+
+void RF_FixOfs (void)
+{
+}
+
+void HelpScreens (void)
+{
+}
+
+
+#if 0
+/*
+==================
+=
+= CheckMemory
+=
+==================
+*/
+
+#define MINMEMORY	400000l
+
+void	CheckMemory(void)
+{
+	unsigned	finscreen;
+
+	if (Flags & FL_NOMEMCHECK)
+		return;
+
+	if (mminfo.nearheap+mminfo.farheap+mminfo.EMSmem+mminfo.XMSmem
+		>= MINMEMORY)
+		return;
+
+	CA_CacheGrChunk (OUTOFMEM);
+	finscreen = (unsigned)grsegs[OUTOFMEM];
+	ShutdownId ();
+	movedata (finscreen,7,0xb800,0,4000);
+	gotoxy (1,24);
+	exit(1);
+}
+#endif
+
+//===========================================================================
+
+
+/*
+==========================
+=
+= main
+=
+==========================
+*/
+
+char			*MainParmStrings[] = {"q","l","ver","nomemcheck","helptest",nil};
+
+void main (void)
+{
+	short i;
+
+	starting_level = 0;
+
+	for (i = 1;i < _argc;i++)
+	{
+		switch (US_CheckParm(_argv[i],MainParmStrings))
+		{
+			case 0:
+				Flags |= FL_QUICK;
+			break;
+
+			case 1:
+				starting_level = atoi(_argv[i]+1);
+				if ((starting_level < 0) || (starting_level > LASTMAP-1))
+					starting_level = 0;
+			break;
+
+			case 2:
+				printf("%s\n", GAMENAME);
+				printf("Copyright 1992-93 Softdisk Publishing\n");
+				printf("%s %s\n",VERSION,REVISION);
+				printf("\n");
+				exit(0);
+			break;
+
+			case 3:
+				Flags |= FL_NOMEMCHECK;
+			break;
+
+			case 4:
+				Flags |= (FL_HELPTEST|FL_QUICK);
+			break;
+
+		}
+	}
+
+	if (stricmp(_argv[1], "^(a@&r`"))
+		Quit("You must type CATAPOC to run CATACOMB APOCALYPSE\n");
+
+
+#if 0
+	MainHelpText.xl = 0;
+	MainHelpText.yl = 0;
+	MainHelpText.xh = 639;
+	MainHelpText.yh = 199;
+	MainHelpText.bgcolor = 7;
+	MainHelpText.ltcolor = 15;
+	MainHelpText.dkcolor = 8;
+#endif
+
+//	jabhack();
+
+	randomize();
+
+	InitGame ();
+//	CheckMemory ();
+	LoadLatchMem ();
+
+//	if (!LoadTextFile("MAINHELP."EXT,&MainHelpText))
+//		Quit("Can't load MAINHELP."EXT);
+
+#ifdef PROFILE
+	NewGame ();
+	GameLoop ();
+#endif
+
+	DemoLoop();
+	Quit(NULL);
+}
+
+//-------------------------------------------------------------------------
+// Display640()
+//-------------------------------------------------------------------------
+void Display640(void)
+{
+// Can you believe it takes all this just to change to 640 mode!!???!
+//
+	VW_ScreenToScreen(0,FREESTART-STATUSLEN,40,80);
+	VW_SetLineWidth(80);
+	MoveScreen(0,0);
+	VW_Bar (0,0,640,200,0);
+	VW_SetScreenMode(EGA640GR);
+	VW_SetLineWidth(80);
+	BlackPalette();
+}
+
+//-------------------------------------------------------------------------
+// Display320()
+//-------------------------------------------------------------------------
+void Display320(void)
+{
+// Can you believe it takes all this just to change to 320 mode!!???!
+//
+	VW_ColorBorder(0);
+	VW_FadeOut();
+	VW_SetLineWidth(40);
+	MoveScreen(0,0);
+	VW_Bar (0,0,320,200,0);
+	VW_SetScreenMode(EGA320GR);
+	VW_SetLineWidth(40);
+	BlackPalette();
+	VW_ScreenToScreen(FREESTART-STATUSLEN,0,40,80);
+}
+
+void PrintHelp(void)
+{
+	char oldfontcolor = fontcolor;
+	PrintY = 1;
+	WindowX = 135;
+	WindowW = 640;
+
+	VW_FadeOut();
+	bufferofs = displayofs = screenloc[0];
+	VW_Bar(0,0,320,200,0);
+
+	Display640();
+
+	VW_Bar(0, 0, 640, 200, 7);
+
+	fontcolor = (7 ^ 1);
+	US_Print ("\n\n                    SUMMARY OF GAME CONTROLS\n\n");
+
+	fontcolor = (7 ^ 4);
+	US_Print ("         ACTION\n\n");
+
+	US_Print ("Arrow keys, joystick, or mouse\n");
+	US_Print ("TAB or V while turning\n");
+	US_Print ("ALT or Button 2 while turning\n");
+	US_Print ("CTRL or Button 1\n");
+	US_Print ("Z\n");
+	US_Print ("X or Enter\n");
+	US_Print ("F1\n");
+	US_Print ("F2\n");
+	US_Print ("F3\n");
+	US_Print ("F4\n");
+	US_Print ("F5\n");
+	US_Print ("ESC\n\n");
+	fontcolor = (7 ^ 0);
+#ifndef CATALOG
+	US_Print ("          (See complete Instructions for more info)\n");
+#endif
+	US_Print ("\n           copyright (c) 1992-93 Softdisk Publishing\n");
+
+	fontcolor = (7 ^ 8);
+	PrintX = 400;
+	PrintY = 37;
+	WindowX = 400;
+	US_Print ("   REACTION\n\n");
+	US_Print ("Move and turn\n");
+	US_Print ("Turn quickly (Quick Turn)\n");
+	US_Print ("Move sideways\n");
+	US_Print ("Shoot a Missile\n");
+	US_Print ("Shoot a Zapper\n");
+	US_Print ("Shoot an Xterminator\n");
+	US_Print ("Help (this screen)\n");
+	US_Print ("Sound control\n");
+	US_Print ("Save game position\n");
+	US_Print ("Restore a saved game\n");
+	US_Print ("Joystick control\n");
+	US_Print ("System options\n\n\n");
+
+	VW_UpdateScreen();
+	VW_FadeIn();
+	VW_ColorBorder(8 | 56);
+	IN_Ack();
+	Display320();
+	fontcolor = oldfontcolor;
+}
\ No newline at end of file
diff --git a/src/lib/hb/c6_play.c b/src/lib/hb/c6_play.c
new file mode 100755
index 00000000..995e6c78
--- /dev/null
+++ b/src/lib/hb/c6_play.c
@@ -0,0 +1,1429 @@
+/* Catacomb Apocalypse Source Code
+ * Copyright (C) 1993-2014 Flat Rock Software
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// C3_PLAY.C
+
+#include "DEF.H"
+#include "gelib.h"
+#pragma hdrstop
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+#define POINTTICS	6
+#define PAUSE 300
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+byte bcolor;
+short skytimer=-1,skytimer_reset;
+short groundtimer=-1,groundtimer_reset;
+
+unsigned scolor,gcolor;
+unsigned *skycolor,*groundcolor,debug_sky,debug_gnd;
+
+unsigned nocolorchange=0xFFFF;
+byte BGFLAGS,				// global that holds all current flags
+	  bgflag;				// used by BG changer, this flag is set when done
+
+
+unsigned sky_daytonight[]={0x0909,0x0101,0x0808,0x0000,0xFFFF};
+//unsigned gnd_daytonight[]={0x0202,0xFFFF};
+
+unsigned sky_lightning[]={0x0101,0x0909,0x0f0f,0x0808,0x0000,0xFFFF};
+
+unsigned sky_colors[NUMLEVELS]={0x0000,0x0000,0x0000,0x0000,0x0808,
+										  0x0404,0x0000,0x0000,0x0000,0x0000,
+										  0x0000,0x0000,0x0000,0x0000,0x0606,
+										  0x0000,0x0000,0x0000,0x0000,0x0000,
+										  0x0000};
+unsigned gnd_colors[NUMLEVELS]={0x0202,0x0202,0x0606,0x0202,0x0707,
+										  0x0505,0x0808,0x0606,0x0101,0x0808,
+										  0x0606,0x0404,0x0808,0x0c0c,0x0e0e,
+										  0x0808,0x0808,0x0c0c,0x0000,0x0707,
+										  0x0808};
+
+
+ControlInfo	control;
+boolean		running=false; //,slowturn;
+
+int			bordertime;
+objtype objlist[MAXACTORS],*new,*obj,*player,*lastobj,*objfreelist;
+
+#if USE_INERT_LIST
+inertobjtype inertobjlist[MAXINERTOBJ],*inert;
+#endif
+
+unsigned	farmapylookup[MAPSIZE];
+byte		*nearmapylookup[MAPSIZE];
+
+boolean		singlestep,godmode;
+int			extravbls;
+status_flags    status_flag;
+int             status_delay;
+
+//
+// replacing refresh manager
+//
+unsigned	mapwidth,mapheight,tics,realtics;
+boolean		compatability;
+byte		*updateptr;
+unsigned	mapwidthtable[64];
+unsigned	uwidthtable[UPDATEHIGH];
+unsigned	blockstarts[UPDATEWIDE*UPDATEHIGH];
+#define	UPDATESCREENSIZE	(UPDATEWIDE*PORTTILESHIGH+2)
+#define	UPDATESPARESIZE		(UPDATEWIDE*2+4)
+#define UPDATESIZE			(UPDATESCREENSIZE+2*UPDATESPARESIZE)
+byte		update[UPDATESIZE];
+
+int		mousexmove,mouseymove;
+int		pointcount,pointsleft;
+
+short BeepTime = 0;
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+void CalcBounds (objtype *ob);
+void DrawPlayScreen (void);
+void PreFullDisplay(void);
+void PostFullDisplay(boolean draw_view);
+
+
+//
+// near data map array (wall values only, get text number from far data)
+//
+byte		tilemap[MAPSIZE][MAPSIZE];
+byte		spotvis[MAPSIZE][MAPSIZE];
+objtype		*actorat[MAPSIZE][MAPSIZE];
+
+objtype dummyobj;
+
+int bordertime;
+int	objectcount;
+
+void StopMusic(void);
+void StartMusic(void);
+
+void CalibrateJoystick(short joynum);
+
+//==========================================================================
+
+///////////////////////////////////////////////////////////////////////////
+//
+//	CenterWindow() - Generates a window of a given width & height in the
+//		middle of the screen
+//
+///////////////////////////////////////////////////////////////////////////
+
+#define MAXX	320
+#define MAXY	120
+
+void	CenterWindow(word w,word h)
+{
+	US_DrawWindow(((MAXX / 8) - w) / 2,((MAXY / 8) - h) / 2,w,h);
+}
+
+//===========================================================================
+
+
+/*
+=====================
+=
+= CheckKeys
+=
+=====================
+*/
+
+void CheckKeys (void)
+{
+	extern boolean autofire;
+
+	if (screenfaded)			// don't do anything with a faded screen
+		return;
+
+#if 0
+//
+// pause key wierdness can't be checked as a scan code
+//
+	if (Paused)
+	{
+		CenterWindow (8,3);
+		US_PrintCentered ("PAUSED");
+		VW_UpdateScreen ();
+//		SD_MusicOff();
+		IN_Ack();
+//		SD_MusicOn();
+		Paused = false;
+		if (MousePresent) Mouse(MDelta);	// Clear accumulated mouse movement
+	}
+	else
+	if (Keyboard[sc_Enter])			// P = pause with no screen disruptioon
+	{
+//		SD_MusicOff();
+		DisplaySMsg("PAUSED",NULL);
+		IN_Ack();
+//		SD_MusicOn();
+		if (MousePresent) Mouse(MDelta);	// Clear accumulated mouse movement
+	}
+	else
+	if (Keyboard[sc_S])
+	{
+		char *Text[] = {{"Slow Mode ON"},{"Slow Mode OFF"}};
+
+		SlowMode ^= 1;
+		extravbls = SlowMode << 3;
+		CenterWindow (8,3);
+		US_PrintCentered (Text[SlowMode]);
+		VW_UpdateScreen ();
+//		SD_MusicOff();
+		IN_Ack();
+//		SD_MusicOn();
+		if (MousePresent) Mouse(MDelta);	// Clear accumulated mouse movement
+	}
+#endif
+
+
+// F2 - SOUND OPTIONS
+//
+	if (Keyboard[sc_F2])
+	{
+		int height=7;
+		boolean ChoiceMade = false;
+
+		if (AdLibPresent)
+			height++;
+
+		VW_FixRefreshBuffer();
+		CenterWindow(22,height);
+		US_Print( "\n        1 )  NO SOUND \n");
+		US_Print(   "        2 )  PC  AUDIO \n");
+
+		if (AdLibPresent)
+			US_Print("        3 ) ADLIB AUDIO\n");
+
+		US_Print( "\n       ESC)    EXIT    ");
+		VW_UpdateScreen();
+
+		// Switch audio device ON/OFF & load sounds if there
+		// was a change in the device.
+
+		do {
+
+			if (Keyboard[1]) 								// ESC - Exit
+				ChoiceMade = true;
+			else
+			if (Keyboard[2]) 							 	// 1 - No Sound
+			{
+				SD_SetSoundMode(sdm_Off);
+				ChoiceMade = true;
+			}
+			else
+			if (Keyboard[3])  							// 2 - PC Audio
+			{
+				SD_SetSoundMode(sdm_PC);
+//				if (oldsoundmode != sdm_PC)
+					CA_LoadAllSounds();
+				ChoiceMade = true;
+			}
+			else
+			if ((Keyboard[4]) &&	AdLibPresent)		// 3 - AdLib Audio
+			{
+				SD_SetSoundMode(sdm_AdLib);
+//				if (oldsoundmode != sdm_AdLib)
+					CA_LoadAllSounds();
+				ChoiceMade = true;
+			}
+
+		} while (!ChoiceMade);
+		tics = realtics = 1;
+		IN_ClearKeysDown();
+	}
+
+// F5 - CALIBRATE JOYSTICK
+//
+	if (Keyboard[sc_F5])
+	{
+		CalibrateJoystick(0);
+		tics = realtics = 1;
+		IN_ClearKeysDown();
+	}
+
+deadloop:;
+// ESCAPE - quits game
+//
+	if ((Keyboard[sc_Escape]) || (Flags & FL_DEAD))
+	{
+		char ch;
+
+		DisplaySMsg("Options", NULL);
+		status_flag = S_NONE;
+
+
+		if (Flags & FL_DEAD)
+		{
+			char choices[] = {sc_Escape,sc_R,sc_N,sc_Q,0};
+			ch = DisplayMsg("Restore          New          Quit",choices);
+			DisplayMsg("                                      ", NULL);
+		}
+		else
+		{
+			char choices[] = {sc_Escape,sc_S,sc_R,sc_N,sc_Q,0};
+			ch = DisplayMsg("Save       Restore       New       Quit",choices);
+		}
+		DrawText(true);
+
+		switch (ch)
+		{
+			case sc_S:
+				if (!(Flags & FL_DEAD))
+					Keyboard[sc_F3] = true;
+			break;
+
+			case sc_R:
+				Keyboard[sc_F4] = true;
+			break;
+
+			case sc_N:
+				DisplaySMsg("Starting anew", NULL);
+				VW_WaitVBL(60);
+				playstate = ex_resetgame;
+				Flags &= ~FL_DEAD;
+			break;
+
+			case sc_Q:
+				DisplaySMsg("FARE THEE WELL!", NULL);
+				VW_WaitVBL(120);
+				if (!Flags & FL_QUICK)
+					VW_FadeOut();
+				NormalScreen();
+				FreeUpMemory();
+				Quit(NULL);
+			break;
+		}
+		tics = realtics = 1;
+	}
+
+// F1 - DISPLAY HELP
+//
+	if (Keyboard[sc_F1])
+	{
+		PrintHelp();
+
+#ifdef TEXT_PRESENTER
+
+		extern PresenterInfo MainHelpText;
+
+		VW_FadeOut();
+
+		FreeUpMemory();
+		if (!LoadPresenterScript("HELP.TXT",&MainHelpText))
+		{
+			VW_FadeIn();
+			CenterWindow(30,5);
+			US_CPrint("\nError loading HELP file.\n");
+			US_CPrint("Press any key.");
+			IN_Ack();
+			VW_FadeOut();
+		}
+		else
+		{
+			VW_SetSplitScreen(200);
+			bufferofs = displayofs = screenloc[0];
+			VW_Bar(0,0,320,200,0);
+
+			Display640();
+			Presenter(&MainHelpText);
+			Display320();
+		}
+		FreePresenterScript(&MainHelpText);
+#endif
+		VW_SetSplitScreen(120);
+		VW_SetScreen(screenloc[0],0);
+		screenpage = 0;
+		CacheScaleds();
+
+		bufferofs = 0;
+		RedrawStatusWindow();
+		ThreeDRefresh();
+		VW_FadeIn();
+		Keyboard[sc_F1] = false;
+		tics = realtics = 1;
+		IN_ClearKeysDown();
+	}
+
+// F3 - SAVE GAME
+//
+	if ((Keyboard[sc_F3]) && (!(Flags & FL_DEAD)))
+	{
+		PreFullDisplay();
+		GE_SaveGame();
+		PostFullDisplay(true);
+		tics = realtics = 1;
+		IN_ClearKeysDown();
+	}
+
+// F4 - LOAD GAME
+//
+	if (Keyboard[sc_F4])
+	{
+		PreFullDisplay();
+		if (GE_LoadGame())
+		{
+			loadedgame = true;
+			playstate = ex_loadedgame;
+			Flags &= ~FL_DEAD;
+			lasttext = -1;
+			PostFullDisplay(false);
+		}
+		else
+		if (playstate == ex_victorious)
+		{
+			PostFullDisplay(false);
+			Victory(false);
+			IN_Ack();
+			Quit(NULL);
+		}
+		else
+			PostFullDisplay(true);
+		Keyboard[sc_F5] = false;
+		tics = realtics = 1;
+		IN_ClearKeysDown();
+	}
+
+	if (Flags & FL_DEAD)
+		goto deadloop;
+
+//
+// F10-? debug keys
+//
+	if (Keyboard[sc_BackSpace])
+	{
+		DebugKeys();
+		if (MousePresent) Mouse(MDelta);	// Clear accumulated mouse movement
+		lasttimecount = TimeCount;
+	}
+}
+
+//-------------------------------------------------------------------------
+// PreFullDisplay()
+//-------------------------------------------------------------------------
+void PreFullDisplay()
+{
+	VW_FadeOut();
+	VW_SetSplitScreen(200);
+	bufferofs = displayofs = screenloc[0];
+	VW_Bar(0,0,320,200,0);
+}
+
+//-------------------------------------------------------------------------
+// PostFullDisplay()
+//-------------------------------------------------------------------------
+void PostFullDisplay(boolean draw_view)
+{
+	VW_SetSplitScreen(120);
+	bufferofs = 0;
+	RedrawStatusWindow();
+	if (draw_view)
+	{
+		ThreeDRefresh();
+		VW_FadeIn();
+	}
+}
+
+
+//===========================================================================
+
+/*
+#############################################################################
+
+				  The objlist data structure
+
+#############################################################################
+
+objlist containt structures for every actor currently playing.  The structure
+is accessed as a linked list starting at *player, ending when ob->next ==
+NULL.  GetNewObj inserts a new object at the end of the list, meaning that
+if an actor spawn another actor, the new one WILL get to think and react the
+same frame.  RemoveObj unlinks the given object and returns it to the free
+list, but does not damage the objects ->next pointer, so if the current object
+removes itself, a linked list following loop can still safely get to the
+next element.
+
+<backwardly linked free list>
+
+#############################################################################
+*/
+
+
+/*
+=========================
+=
+= InitObjList
+=
+= Call to clear out the entire object list, returning them all to the free
+= list.  Allocates a special spot for the player.
+=
+=========================
+*/
+
+void InitObjList (void)
+{
+	int	i;
+
+	for (i=0;i<MAXACTORS;i++)
+	{
+		objlist[i].prev = &objlist[i+1];
+		objlist[i].next = NULL;
+	}
+
+	objlist[MAXACTORS-1].prev = NULL;
+
+	objfreelist = &objlist[0];
+	lastobj = NULL;
+
+	objectcount = 0;
+
+//
+// give the player and score the first free spots
+//
+	GetNewObj (false);
+	player = new;
+
+#if USE_INERT_LIST
+	inert = inertobjlist;
+#endif
+
+}
+
+//===========================================================================
+
+/*
+=========================
+=
+= GetNewObj
+=
+= Sets the global variable new to point to a free spot in objlist.
+= The free spot is inserted at the end of the liked list
+=
+= When the object list is full, the caller can either have it bomb out ot
+= return a dummy object pointer that will never get used
+=
+=========================
+*/
+
+void GetNewObj (boolean usedummy)
+{
+	if (!objfreelist)
+	{
+		if (usedummy)
+		{
+			new = &dummyobj;
+			return;
+		}
+		Quit ("GetNewObj: No free spots in objlist!");
+	}
+
+	new = objfreelist;
+	objfreelist = new->prev;
+	memset (new,0,sizeof(*new));
+
+	if (lastobj)
+		lastobj->next = new;
+	new->prev = lastobj;	// new->next is allready NULL from memset
+
+	new->active = false;
+	lastobj = new;
+
+	objectcount++;
+}
+
+//===========================================================================
+
+/*
+=========================
+=
+= RemoveObj
+=
+= Add the given object back into the free list, and unlink it from it's
+= neighbors
+=
+=========================
+*/
+
+void RemoveObj (objtype *gone)
+{
+	objtype **spotat;
+
+	if (gone == player)
+		Quit ("RemoveObj: Tried to remove the player!");
+
+//
+// fix the next object's back link
+//
+	if (gone == lastobj)
+		lastobj = (objtype *)gone->prev;
+	else
+		gone->next->prev = gone->prev;
+
+//
+// fix the previous object's forward link
+//
+	gone->prev->next = gone->next;
+
+//
+// add it back in to the free list
+//
+	gone->prev = objfreelist;
+	objfreelist = gone;
+
+	objectcount--;
+}
+
+#if USE_INERT_LIST
+
+//--------------------------------------------------------------------------
+// MoveObjToInert()
+//--------------------------------------------------------------------------
+void MoveObjToInert(objtype *obj)
+{
+
+	if (inert == &inertobjlist[MAXINERTOBJ])
+		return;
+
+// Transfer info needed by inert objtype
+//
+	inert->x = obj->x;
+	inert->y = obj->y;
+	inert->size = obj->size;
+	inert->viewx = obj->viewx;
+	inert->tilex = obj->tilex;
+	inert->tiley = obj->tiley;
+	inert->state = obj->state;
+	inert->ticcount = obj->ticcount;
+
+// Setup links between inert objects
+//
+	if (inert != inertobjlist)
+		(inert-1)->next = inert;
+	inert->next = NULL;
+	inert++;
+
+// Free 'real' object from list.
+//
+	RemoveObj(obj);
+}
+
+#endif
+
+//==========================================================================
+
+/*
+===================
+=
+= PollControls
+=
+===================
+*/
+
+void PollControls (void)
+{
+	unsigned buttons;
+
+	IN_ReadControl(0,&control);
+
+	if (MousePresent)
+	{
+		Mouse(MButtons);
+		buttons = _BX;
+		Mouse(MDelta);
+		mousexmove = _CX;
+		mouseymove = _DX;
+
+		if (buttons&1)
+			control.button0 = 1;
+		if (buttons&2)
+			control.button1 = 1;
+
+	}
+
+	if (Keyboard[sc_V] || Keyboard[sc_Tab])
+		running = true;
+	else
+		running = false;
+}
+
+//==========================================================================
+
+#if 0
+/*
+=================
+=
+= StopMusic
+=
+=================
+*/
+
+void StopMusic(void)
+{
+	int	i;
+
+	SD_MusicOff();
+	for (i = 0;i < LASTMUSIC;i++)
+		if (audiosegs[STARTMUSIC + i])
+		{
+			MM_SetPurge(&((memptr)audiosegs[STARTMUSIC + i]),3);
+			MM_SetLock(&((memptr)audiosegs[STARTMUSIC + i]),false);
+		}
+}
+
+//==========================================================================
+
+
+/*
+=================
+=
+= StartMusic
+=
+=================
+*/
+
+// JAB - Cache & start the appropriate music for this level
+void StartMusic(void)
+{
+	musicnames	chunk;
+
+	SD_MusicOff();
+	chunk =	TOOHOT_MUS;
+//	if ((chunk == -1) || (MusicMode != smm_AdLib))
+//DEBUG control panel		return;
+
+	MM_BombOnError (false);
+	CA_CacheAudioChunk(STARTMUSIC + chunk);
+	MM_BombOnError (true);
+	if (mmerror)
+		mmerror = false;
+	else
+	{
+		MM_SetLock(&((memptr)audiosegs[STARTMUSIC + chunk]),true);
+		SD_StartMusic((MusicGroup far *)audiosegs[STARTMUSIC + chunk]);
+	}
+}
+#endif
+
+//==========================================================================
+
+
+/*
+===================
+=
+= PlayLoop
+=
+===================
+*/
+
+void PlayLoop (void)
+{
+	char shot_color[3] = {4,9,14};
+
+	int allgems[5]={GEM_DELAY_TIME,		// used for Q & D comparison
+						 GEM_DELAY_TIME,		// for having all gems...
+						 GEM_DELAY_TIME,		// the "allgems" declaration MUST
+						 GEM_DELAY_TIME,		// match the "gems" declaration in
+						 GEM_DELAY_TIME		// the gametype structure!
+						};
+
+//	int originx=0;
+//	int i=100;
+	signed long dx,dy,radius,psin,pcos,newx,newy;
+	int		give;
+	short objnum;
+	signed long ox,oy,xl,xh,yl,yh,px,py,norm_dx,norm_dy;
+	short o_radius;
+
+	void (*think)();
+
+	ingame = true;
+	playstate = TimeCount = 0;
+	gamestate.shotpower = handheight = 0;
+	pointcount = pointsleft = 0;
+
+	status_flag = S_NONE;
+
+#if 0
+	// setup sky/ground colors and effects (based on level)
+	//
+	switch (gamestate.mapon)
+	{
+		case 255:
+			if (!(BGFLAGS & BGF_NIGHT))
+			{
+				InitBgChange(3*60,sky_daytonight,-1,NULL,BGF_NIGHT);
+				groundcolor = &gnd_colors[0];
+			}
+			else
+			{
+				skycolor = &sky_colors[0];
+				groundcolor = &gnd_colors[0];
+			}
+		break;
+
+		default:
+			skycolor = &sky_colors[gamestate.mapon];
+			groundcolor = &gnd_colors[gamestate.mapon];
+			skytimer = groundtimer = -1;
+		break;
+	}
+#endif
+
+	BGFLAGS |= BGF_NOT_LIGHTNING;
+	skytimer = groundtimer = -1;
+
+	debug_gnd = *groundcolor;
+	debug_sky = *skycolor;
+	RedrawStatusWindow();
+	ThreeDRefresh();
+	if (screenfaded)
+		VW_FadeIn();
+
+#ifndef PROFILE
+	fizzlein = true;				// fizzle fade in the first refresh
+#endif
+	TimeCount = lasttimecount = lastnuke = 0;
+
+	PollControls ();				// center mouse
+//	StartMusic ();
+	do
+	{
+#ifndef PROFILE
+		PollControls();
+#else
+		control.xaxis = 1;
+		if (++TimeCount == 300)
+			return;
+#endif
+		DisplayStatus(&status_flag);
+
+		objnum=0;
+		for (obj = player;obj;obj = obj->next)
+		{
+			if ((obj->active >= yes) && (!(FreezeTime && (obj!=player))))
+			{
+				if (obj->ticcount)
+				{
+					obj->ticcount-=realtics;
+					while ( obj->ticcount <= 0)
+					{
+						think = obj->state->think;
+						if (think)
+						{
+							statetype *oldstate=obj->state;
+
+							think (obj);
+							if (!obj->state)
+							{
+								RemoveObj (obj);
+								goto nextactor;
+							}
+							if (obj->state != oldstate)
+								break;
+						}
+
+						obj->state = obj->state->next;
+						if (!obj->state)
+						{
+							RemoveObj (obj);
+							goto nextactor;
+						}
+						if (!obj->state->tictime)
+						{
+							obj->ticcount = 0;
+							goto nextactor;
+						}
+						if (obj->state->tictime>0)
+							obj->ticcount += obj->state->tictime;
+					}
+				}
+
+				think =	obj->state->think;
+				if (think)
+				{
+					think (obj);
+					if (!obj->state)
+						RemoveObj (obj);
+				}
+nextactor:;
+			}
+
+			// keep a list of objects around the player for radar updates
+			//
+				if (obj == player)
+				{
+					px = player->x;
+					py = player->y;
+					psin = sintable[player->angle];
+					pcos = costable[player->angle];
+					xl = px-((long)RADAR_WIDTH<<TILESHIFT)/2;
+					xh = px+((long)RADAR_WIDTH<<TILESHIFT)/2-1;
+					yl = py-((long)RADAR_HEIGHT<<TILESHIFT)/2;
+					yh = py+((long)RADAR_HEIGHT<<TILESHIFT)/2;
+				}
+
+				if (objnum > MAX_RADAR_BLIPS-2)
+					objnum = MAX_RADAR_BLIPS-2;
+
+				ox = obj->x;
+				oy = obj->y;
+
+
+				if ((ox >= xl) && (ox <= xh) && (oy >= yl) && (oy <= yh))
+				{
+					norm_dx = (dx = px-ox)>>TILESHIFT;
+					norm_dy = (dy = oy-py)>>TILESHIFT;
+
+					o_radius = IntSqrt((norm_dx * norm_dx) + (norm_dy * norm_dy));
+
+					if (o_radius < RADAR_RADIUS)
+					{
+						newx = FixedByFrac(dy,pcos)-FixedByFrac(dx,psin);
+						newy = FixedByFrac(dy,psin)+FixedByFrac(dx,pcos);
+
+						RadarXY[objnum][0]=newx>>TILESHIFT;
+						RadarXY[objnum][1]=newy>>TILESHIFT;
+
+						// Define color to use for this object...
+						//
+
+						switch (obj->obclass)
+						{
+			// NO GEM NEEDED
+			//
+					// THE WIZARD! (YOU)
+					//
+							case playerobj:
+								RadarXY[objnum++][2]=15;
+							break;
+
+					// WIZARD'S SHOTS
+					//
+							case bounceobj:
+							case pshotobj:
+							case bigpshotobj:
+								RadarXY[objnum++][2]=shot_color[screenpage];
+							break;
+
+			// RED GEM
+			//
+					// STOMPY										(DK RED)
+					//
+							case invisdudeobj:
+							case stompyobj:
+								if (gamestate.gems[B_RGEM-B_RGEM])
+									if (obj->active == always)
+										RadarXY[objnum++][2]=4;
+							break;
+
+					// BLOB											(LT RED)
+					//
+							case blobobj:
+								if (gamestate.gems[B_RGEM-B_RGEM])
+									if (obj->active == always)
+										RadarXY[objnum++][2]=12;
+							break;
+
+			// BLUE GEM
+			//
+					// ROBOTANK										(LT BLUE)
+					//
+							case robotankobj:
+							case fmageobj:
+								if (gamestate.gems[B_BGEM-B_RGEM])
+									if (obj->active == always)
+										RadarXY[objnum++][2]=9;
+							break;
+
+#if 1
+					// BLUE DEMON									(DK BLUE)
+					//
+							case demonobj:
+								if (gamestate.gems[B_GGEM-B_RGEM])
+									if (obj->active == always)
+										RadarXY[objnum++][2]=1;
+							break;
+#endif
+
+			// GREEN GEM
+			//
+					// WIZARD										(LT GREEN)
+					//
+							case wizardobj:
+								if (gamestate.gems[B_GGEM-B_RGEM])
+									if (obj->active == always)
+										RadarXY[objnum++][2]=10;
+							break;
+
+					// AQUA MAN										(DK GREEN)
+					//
+							case aquamanobj:
+								if (gamestate.gems[B_GGEM-B_RGEM])
+									if (obj->active == always)
+										RadarXY[objnum++][2]=2;
+							break;
+
+			// YELLOW GEM
+			//
+					// EQYPTIAN HEAD								(BROWN)
+					//
+							case headobj:
+								if (gamestate.gems[B_YGEM-B_RGEM])
+									if (obj->active == always)
+										RadarXY[objnum++][2]=6;
+							break;
+
+					//	RAMBONE										(YELLOW)
+					//	TROLL
+							case ramboneobj:
+							case trollobj:
+								if (gamestate.gems[B_YGEM-B_RGEM])
+									if (obj->active == always)
+										RadarXY[objnum++][2]=14;
+							break;
+
+					//	BUG											(LIGHT GRAY)
+							case bugobj:
+								if (gamestate.gems[B_YGEM-B_RGEM])
+									if (obj->active == always)
+										RadarXY[objnum++][2]=7;
+							break;
+
+					//	RAY											(DARK GRAY)
+							case rayobj:
+								if (gamestate.gems[B_YGEM-B_RGEM])
+									if (obj->active == always)
+										RadarXY[objnum++][2]=8;
+							break;
+
+			// PURPLE GEM
+			//
+					// MEC DEMON									(PURPLE)
+					//
+							case cyborgdemonobj:
+								if (gamestate.gems[B_PGEM-B_RGEM])
+									if (obj->active == always)
+										RadarXY[objnum++][2]=5;
+							break;
+
+					// EYE											(LT PURPLE)
+					//
+							case eyeobj:
+							case reyeobj:
+								if (gamestate.gems[B_PGEM-B_RGEM])
+									if (obj->active == always)
+										RadarXY[objnum++][2]=13;
+							break;
+
+			// ALL GEMS NEEDED
+			//
+					// NEMESIS
+					//
+							case grelmobj:
+								if (!memcmp(gamestate.gems,allgems,sizeof(gamestate.gems)))
+									if (obj->active == always)
+										RadarXY[objnum++][2]=15;
+							break;
+						}
+					}
+				}
+		}
+		RadarXY[objnum][2]=-1;		// Signals end of RadarXY list...
+
+#if USE_INERT_LIST
+		if (inert != inertobjlist)
+			for (obj=(objtype *)inertobjlist;obj;obj=obj->next)
+				if (obj->ticcount)
+				{
+					obj->ticcount-=realtics;
+					while ( obj->ticcount <= 0)
+					{
+						obj->state = obj->state->next;
+						if (!obj->state)
+							Quit("Removable object in INERT list.");
+
+						if (!obj->state->tictime)
+						{
+							obj->ticcount = 0;
+							goto nextactor;
+						}
+
+						if (obj->state->tictime>0)
+							obj->ticcount += obj->state->tictime;
+					}
+				}
+#endif
+
+		if (bordertime)
+		{
+			bordertime -= realtics;
+			if (bordertime<=0)
+			{
+				bordertime = 0;
+				VW_ColorBorder(0);
+			}
+		}
+
+#if 1
+// random lightning?
+//
+	if (BGFLAGS & (BGF_NOT_LIGHTNING))
+	{
+		if ((scolor & 0xe0) && (!(random(20-realtics))))
+		{
+			BGFLAGS &= ~BGF_NOT_LIGHTNING;
+			InitBgChange(1,sky_lightning,-1,NULL,BGF_NOT_LIGHTNING);
+		}
+	}
+
+// handle sky/ground color changes
+//
+		if (skytimer != -1)
+		{
+			skytimer -= realtics;
+			if (skytimer < 0)
+			{
+				skycolor++;
+				if (*skycolor == 0xffff)
+				{
+					skytimer = -1;
+//					skycolor--;
+					skycolor = &scolor;
+					if (groundtimer == -1)
+						BGFLAGS |= bgflag;
+				}
+				else
+					skytimer = skytimer_reset;
+			}
+		}
+
+		if (groundtimer != -1)
+		{
+			groundtimer -= realtics;
+			if (groundtimer < 0)
+			{
+				groundcolor++;
+				if (*groundcolor == 0xffff)
+				{
+					groundtimer = -1;
+//					groundcolor--;
+					groundcolor = &gcolor;
+					if (skytimer == -1)
+						BGFLAGS |= bgflag;
+				}
+				else
+					groundtimer = groundtimer_reset;
+			}
+		}
+#endif
+
+
+//
+//		Handle FreezeTime counter..
+//
+		if (FreezeTime)
+		{
+			if (FreezeTime<20*30)
+				if ((BeepTime+=realtics)>=60)
+				{
+					BeepTime -= 60;
+					SD_PlaySound(TICKSND);
+				}
+
+			if ((FreezeTime-=realtics)<=0)
+			{
+				FreezeTime=0;
+				SD_PlaySound(TIMERETURNSND);
+				DisplaySMsg(NULL,NULL);
+				status_flag = S_NONE;
+			}
+		}
+
+
+// refresh all
+//
+		ThreeDRefresh ();
+
+		if (Flags & FL_DEAD)
+		{
+			SD_PlaySound (GAMEOVERSND);
+			DisplaySMsg("DEAD",NULL);
+			DrawHealth();
+		}
+
+// check for win
+//
+		if (playstate == ex_victorious)
+		{
+			Victory(true);
+			IN_Ack();
+			Quit(NULL);
+		}
+
+		CheckKeys();
+
+	}while (!playstate);
+//	StopMusic ();
+
+	ingame = false;
+	if (bordertime)
+	{
+		bordertime = 0;
+		VW_ColorBorder(0);
+	}
+
+	if (abortgame)
+		abortgame = false;
+}
+
+//--------------------------------------------------------------------------
+// IntSqrt() - by Master Programmer, George Leritte!
+//--------------------------------------------------------------------------
+int IntSqrt(long va)
+{
+asm     mov     AX, word ptr va
+asm     mov     DX, word ptr va+2
+asm     mov     bx,dx           // {bx = integer square root of dx:ax}
+asm     or      bx,ax           // {if dx:ax=0 then return}
+asm     jz      isq01
+asm     mov     bx,dx
+asm     shl     bx,1
+asm     or      bl,ah
+asm     or      bl,al
+asm     dec     bx
+asm     add     bx,dx           // { initial guess}
+asm     jg      isq10
+asm     inc     bx              // { don't return zero}
+asm     jg      isq10
+asm     mov     bx,7fffh
+isq01:;
+		  goto    exitrout;
+
+isq10:;
+asm     push    ax
+asm     push    dx
+asm     div     bx
+asm     sub     ax,bx
+asm     cmp     ax,1
+asm     jbe     isq90
+asm     cmp     ax,-1
+asm     jae     isq90
+asm     sar     ax,1
+asm     add     bx,ax
+asm     pop     dx
+asm     pop     ax
+asm     jmp     isq10
+isq90:;
+asm     pop     dx
+asm     pop     ax
+exitrout:;
+asm     mov     ax,bx
+}
+
+//-------------------------------------------------------------------------
+// InitBgChange()
+//-------------------------------------------------------------------------
+void InitBgChange(short stimer, unsigned *scolors, short gtimer, unsigned *gcolors, byte flag)
+{
+	skytimer_reset = skytimer = stimer;
+	if (scolors)
+		skycolor = scolors;
+
+	groundtimer_reset = groundtimer = gtimer;
+	if (gcolors)
+		groundcolor = gcolors;
+
+	bgflag = flag;
+}
+
+////////////////////////////////////////////////////////
+//
+// DisplayStatus
+//
+//  Stat_Flag -  contains the type of status displayed
+//  -- also uses status_delay (global variable) will not
+//     change display until this variable is zero.
+//  -- heirarchy is determined by the series of if statements,
+//        to change it, rearrange th if statements.
+//
+////////////////////////////////////////////////////////
+
+#define MESSAGEDELAY  25
+void DisplayStatus (status_flags *stat_flag)
+{
+	status_flags temp_status;
+
+
+	if (*stat_flag == S_TIMESTOP)
+	  return;
+
+	if (status_delay > 0)
+	{
+		status_delay -= realtics;
+		return;
+	}
+	else
+		status_delay = 0;
+
+	// check for a change in status from previous call
+
+	temp_status = S_VIEWING;                             //precaution
+
+	if (Keyboard[sc_Control] || control.button0)
+		temp_status = S_MISSLE;
+
+	if (Keyboard[sc_Z] && !Keyboard[sc_F10])
+		temp_status = S_ZAPPER;
+
+	if ((Keyboard[sc_X] && !Keyboard[sc_F10]) || Keyboard[sc_Enter])
+		temp_status = S_XTER;
+
+	if (control.x)
+		temp_status = S_TURN;
+
+	if ((Keyboard[sc_V] || Keyboard[sc_Tab]) && control.x)
+		temp_status = S_QTURN;
+
+	if (Keyboard[sc_Alt] && control.x)
+		temp_status = S_SIDESTEP;
+
+	if (control.y < 0)
+		temp_status = S_ADVANCE;
+
+	if (control.y > 0)
+		temp_status = S_RETREAT;
+
+	if (Keyboard[sc_F5])
+		temp_status = S_JOYSTICK;
+
+	if (Keyboard[sc_F4])
+		temp_status = S_RESTORING;
+
+	if (Keyboard[sc_F3])
+		temp_status = S_SAVING;
+
+	if (Keyboard[sc_F2])
+		temp_status = S_SND;
+
+	if (Keyboard[sc_F1])
+		temp_status = S_HELP;
+
+	if (temp_status != *stat_flag)
+	{
+		*stat_flag = temp_status;
+
+
+		switch (*stat_flag)
+		{
+			case S_MISSLE:
+				DisplaySMsg("Magick Missile", NULL);
+				status_delay = MESSAGEDELAY;
+			break;
+
+			case S_ZAPPER:
+				if (gamestate.bolts)
+				{
+					DisplaySMsg("Zapper", NULL);
+					status_delay = MESSAGEDELAY+10;
+				}
+			break;
+
+			case S_XTER:
+				if (gamestate.nukes)
+				{
+					DisplaySMsg("Xterminator", NULL);
+					status_delay = MESSAGEDELAY+5;
+				}
+			break;
+
+			case S_TURN:
+				DisplaySMsg("Turning", NULL);
+				status_delay = MESSAGEDELAY;
+			break;
+
+			case S_QTURN:
+				DisplaySMsg("Quick Turning", NULL);
+				status_delay = MESSAGEDELAY;
+			break;
+
+			case S_SIDESTEP:
+				DisplaySMsg("Sidestepping", NULL);
+				status_delay = MESSAGEDELAY;
+			break;
+
+			case S_ADVANCE:
+				DisplaySMsg("Advancing", NULL);
+				status_delay = MESSAGEDELAY;
+			break;
+
+			case S_RETREAT:
+				DisplaySMsg("Retreating", NULL);
+				status_delay = MESSAGEDELAY;
+			break;
+
+			case S_JOYSTICK:
+				DisplaySMsg("Adjusting Joystick", NULL);
+			break;
+
+			case S_RESTORING:
+				DisplaySMsg("Restoring", NULL);
+			break;
+
+			case S_SAVING:
+				DisplaySMsg("Saving", NULL);
+			break;
+
+			case S_SND:
+				DisplaySMsg("Select Sound", NULL);
+			break;
+
+			case S_HELP:
+				DisplaySMsg("Getting Help", NULL);
+			break;
+
+			case S_VIEWING:
+				DisplaySMsg("Viewing", NULL);
+			break;
+		}
+		bufferofs = displayofs = screenloc[screenpage];
+
+	}
+}
diff --git a/src/lib/hb/c6_sca_a.asm b/src/lib/hb/c6_sca_a.asm
new file mode 100755
index 00000000..74160c32
--- /dev/null
+++ b/src/lib/hb/c6_sca_a.asm
@@ -0,0 +1,153 @@
+; Catacomb Apocalypse Source Code
+; Copyright (C) 1993-2014 Flat Rock Software
+;
+; 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.,
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+IDEAL
+MODEL	MEDIUM,C
+
+include "ID_ASM.EQU"
+
+;===========================================================================
+;
+;                    SCALING GRAPHICS
+;
+;===========================================================================
+
+
+
+MACRO	MAKELAB NUM
+
+lab&NUM:
+
+ENDM
+
+MACRO	MAKEREF NUM
+
+dw OFFSET lab&NUM
+
+ENDM
+
+
+;=========================================================================
+
+MAXSCALES equ 256
+
+	DATASEG
+
+EXTRN	screenseg:WORD
+EXTRN	linewidth:WORD
+
+LABEL endtable WORD
+labcount = 0
+REPT MAXSCALES
+MAKEREF %labcount
+labcount = labcount + 1
+ENDM
+
+
+	CODESEG
+
+;==================================================
+;
+; void scaleline (int scale, unsigned picseg, unsigned maskseg,
+;                 unsigned screen, unsigned width)
+;
+;==================================================
+
+PROC	ScaleLine pixels:word, scaleptr:dword, picptr:dword, screen:word
+USES	si,di
+PUBLIC	ScaleLine
+
+;
+; modify doline procedure for proper width
+;
+	mov    	bx,[pixels]
+	cmp	bx,MAXSCALES
+	jbe	@@scaleok
+	mov     bx,MAXSCALES
+@@scaleok:
+	shl	bx,1
+	mov	bx,[endtable+bx]
+	push	[cs:bx]			;save the code that will be modified over
+	mov	[WORD cs:bx],0d18eh	;mov ss,cx
+	push	[cs:bx+2]		;save the code that will be modified over
+	mov	[WORD cs:bx+2],90c3h	;ret / nop
+	push	bx
+
+	mov	dx,[linewidth]
+
+	mov	di,[WORD screen]
+	mov	es,[screenseg]
+
+	mov	si,[WORD scaleptr]
+	mov	ds,[WORD scaleptr+2]
+
+	mov	bx,[WORD picptr]
+	mov	ax,[WORD picptr+2]	;will be moved into ss after call
+
+	mov	bp,bx
+
+	cli
+	call	doline
+	sti
+;
+; restore doline to regular state
+;
+	pop	bx		;address of modified code
+	pop     [cs:bx+2]
+	pop     [cs:bx]
+
+	mov	ax,ss
+	mov	ds,ax
+	ret
+
+;================
+;
+; doline
+;
+; Big unwound scaling routine
+;
+; ds:si = scale table
+; ss:bx = pic data
+; es:di = screen location
+;
+;================
+
+doline:
+
+	mov	cx,ss
+	mov	ss,ax		;can't call a routine with ss used...
+
+labcount = 0
+
+REPT MAXSCALES
+
+MAKELAB %labcount
+labcount = labcount + 1
+
+	lodsb			; get scaled pixel number
+	xlat	[ss:bx]		; look it up in the picture
+	xchg	[es:di],al	; load latches and write pixel to screen
+	add	di,dx		; down to next line
+
+ENDM
+
+	mov	ss,cx
+	ret
+
+ENDP
+
+END
\ No newline at end of file
diff --git a/src/lib/hb/c6_scale.c b/src/lib/hb/c6_scale.c
new file mode 100755
index 00000000..bb824acf
--- /dev/null
+++ b/src/lib/hb/c6_scale.c
@@ -0,0 +1,697 @@
+/* Catacomb Apocalypse Source Code
+ * Copyright (C) 1993-2014 Flat Rock Software
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// C3_SCALE.C
+
+#include "DEF.H"
+#pragma hdrstop
+
+//const	unsigned	viewheight = 144;
+const	unsigned	screenbwide = 40;
+const	byte		BACKGROUNDPIX	=   5;
+
+unsigned		shapesize[NUMSCALEPICS];
+t_compscale _seg *scaledirectory[NUMSCALEPICS];
+t_compshape _seg *shapedirectory[NUMSCALEPICS];
+memptr			walldirectory[NUMSCALEWALLS];
+
+/*
+===========================
+=
+= DeplanePic
+=
+= Takes a raw bit map of width bytes by height and creates a scaleable shape
+=
+= Returns the length of the shape in bytes
+=
+= Fills in spotvis (a convenient 64*64 array) with the color values
+=
+===========================
+*/
+
+void DeplanePic (int picnum)
+{
+	byte		far *plane0,far *plane1,far *plane2,far *plane3;
+	byte		by0,by1,by2,by3;
+	unsigned	x,y,b,color,shift,width,height;
+	byte		*dest;
+
+//
+// convert ega pixels to byte color values in a temp buffer
+//
+	width = pictable[picnum-STARTPICS].width;
+	height = pictable[picnum-STARTPICS].height;
+
+	if (width>8 || height!=64)
+		Quit ("DePlanePic: Bad size shape");
+
+	memset (spotvis,BACKGROUNDPIX,sizeof(spotvis));
+
+	plane0 = (byte _seg *)grsegs[picnum];
+	plane1 = plane0 + width*height;
+	plane2 = plane1 + width*height;
+	plane3 = plane2 + width*height;
+
+	for (y=0;y<height;y++)
+	{
+		dest = &spotvis[y][0];
+		for (x=0;x<width;x++)
+		{
+			by0 = *plane0++;
+			by1 = *plane1++;
+			by2 = *plane2++;
+			by3 = *plane3++;
+
+			for (b=0;b<8;b++)
+			{
+				shift=8-b;
+
+				color = 0;
+				asm	mov	cl,[BYTE PTR shift]
+				asm	mov	al,[BYTE PTR by3]
+				asm	rcr	al,cl;
+				asm	rcl	[BYTE PTR color],1;
+
+				asm	mov	cl,[BYTE PTR shift]
+				asm	mov	al,[BYTE PTR by2]
+				asm	rcr	al,cl;
+				asm	rcl	[BYTE PTR color],1;
+
+				asm	mov	cl,[BYTE PTR shift]
+				asm	mov	al,[BYTE PTR by1]
+				asm	rcr	al,cl;
+				asm	rcl	[BYTE PTR color],1;
+
+				asm	mov	cl,[BYTE PTR shift]
+				asm	mov	al,[BYTE PTR by0]
+				asm	rcr	al,cl;
+				asm	rcl	[BYTE PTR color],1;
+
+				*dest++ = color;
+			}	// B
+		}		// X
+	}			// Y
+}
+
+
+
+
+/*
+========================
+=
+= BuildCompScale
+=
+= Builds a compiled scaler object that will scale a 64 tall object to
+= the given height (centered vertically on the screen)
+=
+= height should be even
+=
+= Call with
+= ---------
+= DS:SI		Source for scale
+= ES:DI		Dest for scale
+=
+= Calling the compiled scaler only destroys AL
+=
+========================
+*/
+
+unsigned BuildCompScale (int height, memptr *finalspot)
+{
+	t_compscale 	_seg *work;
+	byte		far *code;
+
+	int			i;
+	long		fix,step;
+	unsigned	src,totalscaled,totalsize;
+	int			startpix,endpix,toppix;
+
+
+	MM_GetPtr (&(memptr)work,20000);
+
+	step = ((long)height<<16) / 64;
+	code = &work->code[0];
+	toppix = (viewheight-height)/2;
+	fix = 0;
+
+	for (src=0;src<=64;src++)
+	{
+		startpix = fix>>16;
+		fix += step;
+		endpix = fix>>16;
+
+		work->start[src] = startpix;
+		if (endpix>startpix)
+			work->width[src] = endpix-startpix;
+		else
+			work->width[src] = 0;
+
+//
+// mark the start of the code
+//
+		work->codeofs[src] = FP_OFF(code);
+
+//
+// compile some code if the source pixel generates any screen pixels
+//
+		startpix+=toppix;
+		endpix+=toppix;
+
+		if (startpix == endpix || endpix < 0 || startpix >= VIEWHEIGHT || src == 64)
+			continue;
+
+	//
+	// mov al,[si+src]
+	//
+		*code++ = 0x8a;
+		*code++ = 0x44;
+		*code++ = src;
+
+		for (;startpix<endpix;startpix++)
+		{
+			if (startpix >= VIEWHEIGHT)
+				break;						// off the bottom of the view area
+			if (startpix < 0)
+				continue;					// not into the view area
+
+		//
+		// and [es:di+heightofs],al
+		//
+			*code++ = 0x26;
+			*code++ = 0x20;
+			*code++ = 0x85;
+			*((unsigned far *)code)++ = startpix*screenbwide;
+		}
+
+	}
+
+//
+// retf
+//
+	*code++ = 0xcb;
+
+	totalsize = FP_OFF(code);
+	MM_GetPtr (finalspot,totalsize);
+	_fmemcpy ((byte _seg *)(*finalspot),(byte _seg *)work,totalsize);
+	MM_FreePtr (&(memptr)work);
+
+	return totalsize;
+}
+
+
+
+
+/*
+========================
+=
+= BuildCompShape
+=
+= typedef struct
+= {
+=	unsigned	width;
+=	unsigned	codeofs[64];
+= }	t_compshape;
+=
+= Width is the number of compiled line draws in the shape.  The shape
+= drawing code will assume that the midpoint of the shape is in the
+= middle of the width.
+=
+= The non background pixel data will start at codeofs[width], so codeofs
+= greater than width will be invalid.
+=
+= Each code offset will draw one vertical line of the shape, consisting
+= of 0 or more segments of scaled pixels.
+=
+= The scaled shapes use ss:0-4 as a scratch variable for the far call to
+= the compiled scaler, so zero it back out after the shape is scaled, or
+= a "null pointer assignment" will result upon termination.
+=
+= Setup for a call to a compiled shape
+= -----------------------------------
+= ax	toast
+= bx	toast
+= cx	toast
+= dx	segment of compiled shape
+= si	toast
+= di	byte at top of view area to draw the line in
+= bp	0
+= ss:2 and ds  the segment of the compiled scaler to use
+= es	screenseg
+=
+= Upon return, ds IS NOT SET to the data segment.  Do:
+=	mov	ax,ss
+=	mov	ds,ax
+=
+=
+= GC_BITMASK	set to the pixels to be drawn in the row of bytes under DI
+= GC_MODE		read mode 1, write mode 2
+= GC_COLORDONTCARE  set to 0, so all reads from video memory return 0xff
+=
+=
+= Code generated for each segment
+= -------------------------------
+=	mov	bx,[(segend+1)*2]
+=	mov	cx,[bx]
+=	mov	[BYTE PTR bx],0xc8		// far return
+=	mov	ax,[segstart*2]
+=	mov	[ss:0],ax				// entry point into the compiled scaler
+=	mov	ds,dx                   // (mov ds,cs) the data is after the compiled code
+=	mov	si,ofs data
+=	call	[bp]				// scale some pixels
+=	mov	ds,[bp+2]
+=	mov	[bx],cx					// un patch return
+=
+= Code generated after all segments on a line
+= -------------------------------------------
+=	retf
+=
+========================
+*/
+
+unsigned BuildCompShape (t_compshape _seg **finalspot)
+{
+	t_compshape 	_seg *work;
+	byte		far *code;
+	int			firstline,lastline,x,y;
+	unsigned	firstpix,lastpix,width;
+	unsigned	totalsize,pixelofs;
+	unsigned	buff;
+
+
+//	MM_GetPtr (&(memptr)work,20000);
+	EGAWRITEMODE(0);
+	EGAREADMAP(0);		// use ega screen memory for temp buffer
+	EGAMAPMASK(1);
+	buff = screenloc[1];
+	work = (t_compshape _seg *)(0xa000+(buff+15)/16);
+
+//
+// find the width of the shape
+//
+	firstline = -1;
+	x=0;
+	do
+	{
+		for (y=0;y<64;y++)
+			if (spotvis[y][x] != BACKGROUNDPIX)
+			{
+				firstline = x;
+				break;
+			}
+		if (++x == 64)
+			Quit ("BuildCompShape: No shape data!");
+	} while (firstline == -1);
+
+	lastline = -1;
+	x=63;
+	do
+	{
+		for (y=0;y<64;y++)
+			if (spotvis[y][x] != BACKGROUNDPIX)
+			{
+				lastline = x;
+				break;
+			}
+		x--;
+	} while (lastline == -1);
+
+	width = lastline-firstline+1;
+
+	work->width = width;
+	code = (byte far *)&work->codeofs[width];
+
+//
+// copy all non background pixels to the work space
+//
+	pixelofs = FP_OFF(code);
+
+	for (x=firstline;x<=lastline;x++)
+		for (y=0;y<64;y++)
+			if (spotvis[y][x] != BACKGROUNDPIX)
+				*code++ = spotvis[y][x];
+
+//
+// start compiling the vertical lines
+//
+	for (x=firstline;x<=lastline;x++)
+	{
+		work->codeofs[x-firstline] = FP_OFF(code);
+
+		y=0;
+		do
+		{
+		//
+		// scan past black background pixels
+		//
+			while (spotvis[y][x] == BACKGROUNDPIX && y<64)
+				y++;
+
+			if (y>63)		// no more segments
+				break;
+
+			firstpix = y+1;		// +1 because width is before codeofs
+
+		//
+		// scan past scalable pixels
+		//
+			while (spotvis[y][x] != BACKGROUNDPIX && y<64)
+				y++;
+
+			if (y>63)
+				lastpix = 65;
+			else
+				lastpix = y+1;	// actually one pixel past the last displayed
+
+		//
+		// compile the scale call
+		//
+			*code++ = 0x8b;		// mov bx,[lastpix*2]
+			*code++ = 0x1e;
+			*((unsigned far *)code)++ = lastpix*2;
+
+			*code++ = 0x8b;		// mov cx,[bx]
+			*code++ = 0x0f;
+
+			*code++ = 0xc6;		// move [BYTE bx],0xcb
+			*code++ = 0x07;
+			*code++ = 0xcb;
+
+			*code++ = 0xa1;		// mov ax,[firstpix*2]	/*************
+			*((unsigned far *)code)++ = firstpix*2;
+
+			*code++ = 0x36;		// mov [ss:0],ax
+			*code++ = 0xa3;
+			*code++ = 0x00;
+			*code++ = 0x00;
+
+			*code++ = 0x8e;		// mov ds,dx	(mov ds,cs)
+			*code++ = 0xda;
+
+			*code++ = 0xbe;		// mov si,OFFSET pixelofs-firstpixel
+			*((unsigned far *)code)++ = pixelofs-firstpix;
+
+			*code++ = 0xff;		// call [DWORD bp]
+			*code++ = 0x5e;
+			*code++ = 0x00;
+
+			*code++ = 0x8e;		// mov ds,[bp+2]
+			*code++ = 0x5e;
+			*code++ = 0x02;
+
+			*code++ = 0x89;		// mov [bx],cx
+			*code++ = 0x0f;
+
+			pixelofs += (lastpix-firstpix);
+		} while (y<63);
+
+	//
+	// retf
+	//
+		*code++ = 0xcb;
+	}
+
+
+//
+// copy the final shape to a properly sized buffer
+//
+	totalsize = FP_OFF(code);
+
+	if (totalsize >= (PAGELEN*2))
+		Quit("BuildCompShape(): Shape is too complex!");
+
+	MM_GetPtr ((memptr *)finalspot,totalsize);
+	_fmemcpy ((byte _seg *)(*finalspot),(byte _seg *)work,totalsize);
+//	MM_FreePtr (&(memptr)work);
+
+	return totalsize;
+}
+
+
+
+/*
+=======================
+=
+= ScaleShape
+=
+= Draws a compiled shape at [scale] pixels high
+=
+= Setup for call
+= --------------
+= GC_MODE			read mode 1, write mode 2
+= GC_COLORDONTCARE  set to 0, so all reads from video memory return 0xff
+= GC_INDEX			pointing at GC_BITMASK
+=
+=======================
+*/
+
+static	long		longtemp;
+
+void ScaleShape (int xcenter, t_compshape _seg *compshape, unsigned scale)
+{
+	#define MAX_OBJ_SCALE (MAXSCALE)
+
+
+	t_compscale _seg *comptable;
+	unsigned	width,scalewidth;
+	int			x,pixel,lastpixel,pixwidth,min;
+	unsigned	far *codehandle, far *widthptr;
+	unsigned	badcodeptr;
+	int			rightclip;
+
+	if (!compshape)
+		Quit ("ScaleShape: NULL compshape ptr!");
+
+	scale = (scale+1)/2;
+	if (!scale)
+		return;								// too far away
+	if (scale>MAX_OBJ_SCALE)
+		scale = MAX_OBJ_SCALE;
+	comptable = scaledirectory[scale];
+
+	width = compshape->width;
+	scalewidth = comptable->start[width];
+
+	pixel = xcenter - scalewidth/2;
+	lastpixel = pixel+scalewidth-1;
+	if (pixel >= VIEWWIDTH || lastpixel < 0)
+		return;								// totally off screen
+
+//
+// scan backwards from the right edge until pixels are visable
+// rightclip is the first NON VISABLE pixel
+//
+	if (lastpixel>=VIEWWIDTH-1)
+		rightclip = VIEWWIDTH-1;
+	else
+		rightclip = lastpixel;
+
+	if (zbuffer[rightclip]>scale)
+	{
+		if (pixel>0)
+			min = pixel;
+		else
+			min = 0;
+		do
+		{
+			if (--rightclip < min)
+				return;							// totally covered or before 0
+			if (zbuffer[rightclip]<=scale)
+				break;
+		} while (1);
+	}
+	rightclip++;
+
+//
+// scan from the left until it is on screen, leaving
+// [pixel],[pixwidth],[codehandle],and [widthptr] set correctly
+//
+	*(((unsigned *)&longtemp)+1) = (unsigned)compshape;	// seg of shape
+	codehandle = &compshape->codeofs[0];
+	badcodeptr = compshape->codeofs[width];
+	widthptr = &comptable->width[0];
+	asm	mov	ax,[comptable]
+	asm	mov	WORD PTR [2],ax				// ds:0-4 is used as a far call pointer
+										// by the compiled shapes
+	pixwidth = *widthptr;				// scaled width of this pixel
+	while (!pixwidth)
+	{
+		pixwidth = *++widthptr;			// find the first visable pixel
+		codehandle++;
+	}
+
+	if (pixel<0)
+	{
+		do
+		{
+			if (pixel+pixwidth>0)
+			{
+				pixwidth += pixel;
+				pixel = 0;
+				break;
+			}
+			do
+			{
+				pixwidth = *++widthptr;
+				codehandle++;
+			} while (!pixwidth);
+			pixel+=pixwidth;
+		} while (1);
+	}
+
+//
+// scan until it is visable, leaving
+// [pixel],[pixwidth],[codehandle],and [widthptr] set correctly
+//
+	do
+	{
+		if (zbuffer[pixel] <= scale)
+			break;							// start drawing here
+		pixel++;
+		if (!--pixwidth)
+		{
+			do
+			{
+				pixwidth = *++widthptr;
+				codehandle++;
+			} while (!pixwidth);
+		}
+	} while (1);
+
+	if (pixel+pixwidth>rightclip)
+		pixwidth = rightclip-pixel;
+//
+// draw lines
+//
+	do		// while (1)
+	{
+	//
+	// scale a vertical segment [pixwidth] pixels wide at [pixel]
+	//
+		(unsigned)longtemp = *codehandle;	// offset of compiled code
+		if ((unsigned)longtemp == badcodeptr)
+			Quit ("ScaleShape: codehandle past end!");
+
+		asm	mov	bx,[pixel]
+		asm	mov	di,bx
+		asm	shr	di,1
+		asm	shr	di,1
+		asm	shr	di,1						// X in bytes
+		asm	add	di,[bufferofs]
+		asm	and	bx,7
+		asm	shl	bx,1
+		asm	shl	bx,1
+		asm	shl	bx,1
+		asm	add	bx,[pixwidth]				// bx = pixel*8+pixwidth-1
+		asm	dec	bx
+		asm	push	bx
+		asm	mov	al,BYTE PTR [bitmasks1+bx]
+		asm	mov	dx,GC_INDEX+1
+		asm	out	dx,al						// set bit mask register
+
+		asm	mov	es,[screenseg]
+		asm	push	si
+		asm	push	di
+		asm	push	bp
+		asm	xor	bp,bp
+		asm	mov	dx,[WORD PTR longtemp+2]
+		asm	mov	ds,[2]
+		asm	call ss:[DWORD PTR longtemp]		// scale the line of pixels
+		asm	mov	ax,ss
+		asm	mov	ds,ax
+		asm	pop		bp
+		asm	pop		di
+		asm	pop		si
+
+		asm	pop	bx
+		asm	mov	al,BYTE PTR [bitmasks2+bx]
+		asm	or	al,al
+		asm	jz	nosecond
+
+	//
+	// draw a second byte for vertical strips that cross two bytes
+	//
+		asm	inc	di
+		asm	mov	dx,GC_INDEX+1
+		asm	out	dx,al						// set bit mask register
+		asm	push	si
+		asm	push	di
+		asm	push	bp
+		asm	xor	bp,bp
+		asm	mov	dx,[WORD PTR longtemp+2]
+		asm	mov	ds,[2]
+		asm	call ss:[DWORD PTR longtemp]		// scale the line of pixels
+		asm	mov	ax,ss
+		asm	mov	ds,ax
+		asm	pop		bp
+		asm	pop		di
+		asm	pop		si
+
+
+	//
+	// advance to the next drawn line
+	//
+nosecond:;
+		if ( (pixel+=pixwidth) == rightclip )
+		{
+			asm	mov	WORD PTR [0],0
+			asm	mov	WORD PTR [2],0
+			return;							// all done!
+		}
+
+		do
+		{
+			pixwidth = *++widthptr;
+			codehandle++;
+		} while (!pixwidth);
+
+		if (pixel+pixwidth > rightclip)
+			pixwidth = rightclip-pixel;
+
+	} while (1);
+
+}
+
+//
+// bit mask tables for drawing scaled strips up to eight pixels wide
+//
+
+byte	bitmasks1[8][8] = {
+{0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff},
+{0x40,0x60,0x70,0x78,0x7c,0x7e,0x7f,0x7f},
+{0x20,0x30,0x38,0x3c,0x3e,0x3f,0x3f,0x3f},
+{0x10,0x18,0x1c,0x1e,0x1f,0x1f,0x1f,0x1f},
+{0x8,0xc,0xe,0xf,0xf,0xf,0xf,0xf},
+{0x4,0x6,0x7,0x7,0x7,0x7,0x7,0x7},
+{0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3},
+{0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1} };
+
+byte	bitmasks2[8][8] = {
+{0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0x80},
+{0,0,0,0,0,0,0x80,0xc0},
+{0,0,0,0,0,0x80,0xc0,0xe0},
+{0,0,0,0,0x80,0xc0,0xe0,0xf0},
+{0,0,0,0x80,0xc0,0xe0,0xf0,0xf8},
+{0,0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc},
+{0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe} };
+
+
+
+
+
+
diff --git a/src/lib/hb/c6_state.c b/src/lib/hb/c6_state.c
new file mode 100755
index 00000000..e17b77f4
--- /dev/null
+++ b/src/lib/hb/c6_state.c
@@ -0,0 +1,736 @@
+/* Catacomb Apocalypse Source Code
+ * Copyright (C) 1993-2014 Flat Rock Software
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// C3_STATE.C
+
+#include "DEF.H"
+#pragma hdrstop
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+
+dirtype opposite[9] =
+	{south,west,north,east,southwest,northwest,northeast,southeast,nodir};
+
+
+
+//===========================================================================
+
+
+/*
+===================
+=
+= Internal_SpawnNewObj
+=
+===================
+*/
+void Internal_SpawnNewObj (unsigned x, unsigned y, statetype *state, unsigned size, boolean UseDummy, boolean PutInActorat)
+{
+	extern objtype dummyobj;
+
+	GetNewObj(UseDummy);
+	new->size = size;
+	new->state = state;
+	new->ticcount = random (state->tictime)+1;
+
+	new->tilex = x;
+	new->tiley = y;
+	new->x = ((long)x<<TILESHIFT)+TILEGLOBAL/2;
+	new->y = ((long)y<<TILESHIFT)+TILEGLOBAL/2;
+	CalcBounds(new);
+	new->dir = nodir;
+	new->active = noalways;
+
+	if (new != &dummyobj && PutInActorat)
+		actorat[new->tilex][new->tiley] = new;
+}
+
+void Internal_SpawnNewObjFrac (long x, long y, statetype *state, unsigned size,boolean UseDummy)
+{
+	GetNewObj(UseDummy);
+	new->size = size;
+	new->state = state;
+	new->ticcount = random (state->tictime)+1;
+	new->active = noalways;
+
+	new->x = x;
+	new->y = y;
+	new->tilex = x>>TILESHIFT;
+	new->tiley = y>>TILESHIFT;
+	CalcBounds(new);
+	new->distance = 100;
+	new->dir = nodir;
+}
+
+
+
+
+/*
+===================
+=
+= CheckHandAttack
+=
+= If the object can move next to the player, it will return true
+=
+===================
+*/
+
+boolean CheckHandAttack (objtype *ob)
+{
+	long deltax,deltay,size;
+
+	size = (long)ob->size + player->size + ob->speed*tics + SIZE_TEST;
+	deltax = ob->x - player->x;
+	deltay = ob->y - player->y;
+
+	if (deltax > size || deltax < -size || deltay > size || deltay < -size)
+		return false;
+
+	return true;
+}
+
+
+/*
+===================
+=
+= T_DoDamage
+=
+= Attacks the player if still nearby, then immediately changes to next state
+=
+===================
+*/
+
+void T_DoDamage (objtype *ob)
+{
+	int	points;
+
+
+	if (CheckHandAttack(ob) && (!(ob->flags & of_damagedone)))
+	{
+		points = 0;
+
+		switch (ob->obclass)
+		{
+		case aquamanobj:
+			points = 7;
+		break;
+
+		case wizardobj:
+			points = 7;
+		break;
+
+		case trollobj:
+			points = 10;
+		break;
+
+		case invisdudeobj:
+			points = 10;
+		break;
+
+		case demonobj:
+		case cyborgdemonobj:
+			points = 15;
+		break;
+
+		}
+		points = EasyDoDamage(points);
+		TakeDamage (points);
+		ob->flags |= of_damagedone;
+	}
+}
+
+
+//==========================================================================
+
+/*
+==================================
+=
+= Walk
+=
+==================================
+*/
+
+boolean Walk (objtype *ob)
+{
+	switch (ob->dir)
+	{
+	case north:
+		if (actorat[ob->tilex][ob->tiley-1])
+			return false;
+		ob->tiley--;
+		ob->distance = TILEGLOBAL;
+		return true;
+
+	case northeast:
+		if (actorat[ob->tilex+1][ob->tiley-1])
+			return false;
+		ob->tilex++;
+		ob->tiley--;
+		ob->distance = TILEGLOBAL;
+		return true;
+
+	case east:
+		if (actorat[ob->tilex+1][ob->tiley])
+			return false;
+		ob->tilex++;
+		ob->distance = TILEGLOBAL;
+		return true;
+
+	case southeast:
+		if (actorat[ob->tilex+1][ob->tiley+1])
+			return false;
+		ob->tilex++;
+		ob->tiley++;
+		ob->distance = TILEGLOBAL;
+		return true;
+
+	case south:
+		if (actorat[ob->tilex][ob->tiley+1])
+			return false;
+		ob->tiley++;
+		ob->distance = TILEGLOBAL;
+		return true;
+
+	case southwest:
+		if (actorat[ob->tilex-1][ob->tiley+1])
+			return false;
+		ob->tilex--;
+		ob->tiley++;
+		ob->distance = TILEGLOBAL;
+		return true;
+
+	case west:
+		if (actorat[ob->tilex-1][ob->tiley])
+			return false;
+		ob->tilex--;
+		ob->distance = TILEGLOBAL;
+		return true;
+
+	case northwest:
+		if (actorat[ob->tilex-1][ob->tiley-1])
+			return false;
+		ob->tilex--;
+		ob->tiley--;
+		ob->distance = TILEGLOBAL;
+		return true;
+
+	case nodir:
+		return false;
+	}
+
+	Quit ("Walk: Bad dir");
+	return false;
+}
+
+
+
+/*
+==================================
+=
+= ChaseThink
+= have the current monster go after the player,
+= either diagonally or straight on
+=
+==================================
+*/
+
+void ChaseThink (objtype *obj, boolean diagonal)
+{
+	int deltax,deltay,i;
+	dirtype d[3];
+	dirtype tdir, olddir, turnaround;
+
+
+	olddir=obj->dir;
+	turnaround=opposite[olddir];
+
+	deltax=player->tilex - obj->tilex;
+	deltay=player->tiley - obj->tiley;
+
+	d[1]=nodir;
+	d[2]=nodir;
+
+	if (deltax>0)
+		d[1]= east;
+	if (deltax<0)
+		d[1]= west;
+	if (deltay>0)
+		d[2]=south;
+	if (deltay<0)
+		d[2]=north;
+
+	if (abs(deltay)>abs(deltax))
+	{
+		tdir=d[1];
+		d[1]=d[2];
+		d[2]=tdir;
+	}
+
+	if (d[1]==turnaround)
+		d[1]=nodir;
+	if (d[2]==turnaround)
+		d[2]=nodir;
+
+
+	if (diagonal)
+	{                           /*ramdiagonals try the best dir first*/
+		if (d[1]!=nodir)
+		{
+			obj->dir=d[1];
+			if (Walk(obj))
+				return;     /*either moved forward or attacked*/
+		}
+
+		if (d[2]!=nodir)
+		{
+			obj->dir=d[2];
+			if (Walk(obj))
+				return;
+		}
+	}
+	else
+	{                  /*ramstraights try the second best dir first*/
+
+		if (d[2]!=nodir)
+		{
+			obj->dir=d[2];
+			if (Walk(obj))
+				return;
+		}
+
+		if (d[1]!=nodir)
+		{
+			obj->dir=d[1];
+			if (Walk(obj))
+				return;
+		}
+	}
+
+	// Kluge to make the running eye stay in place if blocked, ie, not divert
+	// from path
+	if (obj->obclass == reyeobj)
+		return;
+
+
+/* there is no direct path to the player, so pick another direction */
+
+	obj->dir=olddir;
+	if (Walk(obj))
+		return;
+
+	if (US_RndT()>128) 	/*randomly determine direction of search*/
+	{
+		for (tdir=north;tdir<=west;tdir++)
+		{
+			if (tdir!=turnaround)
+			{
+				obj->dir=tdir;
+				if (Walk(obj))
+					return;
+			}
+		}
+	}
+	else
+	{
+		for (tdir=west;tdir>=north;tdir--)
+		{
+			if (tdir!=turnaround)
+			{
+			  obj->dir=tdir;
+			  if (Walk(obj))
+				return;
+			}
+		}
+	}
+
+	obj->dir=turnaround;
+	Walk(obj);		/*last chance, don't worry about returned value*/
+}
+
+
+/*
+=================
+=
+= MoveObj
+=
+=================
+*/
+
+void MoveObj (objtype *ob, long move)
+{
+	ob->distance -=move;
+
+	switch (ob->dir)
+	{
+	case north:
+		ob->y -= move;
+		return;
+	case northeast:
+		ob->x += move;
+		ob->y -= move;
+		return;
+	case east:
+		ob->x += move;
+		return;
+	case southeast:
+		ob->x += move;
+		ob->y += move;
+		return;
+	case south:
+		ob->y += move;
+		return;
+	case southwest:
+		ob->x -= move;
+		ob->y += move;
+		return;
+	case west:
+		ob->x -= move;
+		return;
+	case northwest:
+		ob->x -= move;
+		ob->y -= move;
+		return;
+
+	case nodir:
+		return;
+	}
+}
+
+
+/*
+=================
+=
+= Chase
+=
+= returns true if hand attack range
+=
+=================
+*/
+
+boolean Chase (objtype *ob, boolean diagonal)
+{
+	long move;
+	long deltax,deltay,size;
+
+	ob->flags &= ~of_damagedone;
+
+	move = ob->speed*tics;
+	size = (long)ob->size + player->size + move + SIZE_TEST;
+
+	while (move)
+	{
+		deltax = ob->x - player->x;
+		deltay = ob->y - player->y;
+
+		if (deltax <= size && deltax >= -size
+		&& deltay <= size && deltay >= -size)
+		{
+			CalcBounds (ob);
+			return true;
+		}
+
+		if (move < ob->distance)		//ob->distance - distance before you move
+		{                             //               over into next tile
+			MoveObj (ob,move);
+			break;
+		}
+		else
+			if (ob->obclass == reyeobj)	// Kludge for the "running eye"
+			{
+				if (ob->temp1 < 2)
+				{
+					MoveObj(ob, ob->distance/2);
+					ob->temp1 = 0;
+				}
+			}
+
+		actorat[ob->tilex][ob->tiley] = 0;	// pick up marker from goal
+		if (ob->dir == nodir)
+			ob->dir = north;
+
+		ob->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;
+		ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;
+		move -= ob->distance;
+
+		ChaseThink (ob, diagonal);
+		if (!ob->distance)
+			break;			// no possible move
+		actorat[ob->tilex][ob->tiley] = ob;	// set down a new goal marker
+	}
+	CalcBounds (ob);
+	return false;
+}
+
+//===========================================================================
+
+
+/*
+===================
+=
+= ShootActor
+=
+===================
+*/
+
+void ShootActor (objtype *ob, unsigned damage)
+{
+
+	ob->hitpoints -= damage;
+
+	if (ob->hitpoints<=0)
+	{
+		switch (ob->obclass)
+		{
+
+		case headobj:
+			ob->state = &s_pshot_exp1;
+			ob->obclass = expobj;
+			ob->ticcount = ob->state->tictime;
+			SpawnBigExplosion(ob->x,ob->y,12,(16l<<16L));
+		break;
+
+		case aquamanobj:
+			ob->state = &s_aqua_die1;
+			ob->temp1 = 10;
+		break;
+
+		case wizardobj:
+			ob->state = &s_wizard_die1;
+		break;
+
+		case trollobj:
+			ob->state = &s_trolldie1;
+		break;
+
+		case blobobj:
+			ob->state = &s_blob_die1;
+		break;
+
+		case rayobj:
+			ob->state = &s_ray_die1;
+		break;
+
+		case ramboneobj:
+			ob->state = &s_skel_die1;
+		break;
+
+		case fmageobj:
+			ob->state = &s_fmagedie1;
+		break;
+
+		case robotankobj:
+			ob->state = &s_robotank_death1;
+			ob->temp1 = 10;
+		break;
+
+		case stompyobj:
+			ob->state = &s_stompy_death1;
+		break;
+
+		case bugobj:
+			ob->state = &s_bug_death1;
+		break;
+
+		case demonobj:
+			ob->state = &s_demondie1;
+		break;
+
+		case cyborgdemonobj:
+			ob->state = &s_cyborg_demondie1;
+		break;
+
+		case invisdudeobj:
+			ob->state = &s_invis_death1;
+		break;
+
+		case grelmobj:
+			ob->state = &s_greldie1;
+		break;
+
+		case eyeobj:
+			ob->state = &s_eye_die1;
+		break;
+
+		case reyeobj:
+			ob->state = &s_reye_die1;
+		break;
+
+		case bounceobj:
+			ob->state = &s_pshot_exp1;
+			ob->obclass = expobj;
+			ob->ticcount = ob->state->tictime;
+			SpawnBigExplosion(ob->x,ob->y,12,(16l<<16L));
+		break;
+
+		case rshotobj:
+		case eshotobj:
+		case wshotobj:
+		case hshotobj:
+		case bshotobj:
+		case rbshotobj:
+		case fmshotobj:
+		case rtshotobj:
+		case syshotobj:
+		case bgshotobj:
+			ob->state = &s_bonus_die;
+#if USE_INERT_LIST
+			ob->obclass = solidobj;		// don't add these objs to inert list
+#endif
+		break;
+
+		case bonusobj:
+		case freezeobj:
+			switch (ob->temp1)
+			{
+				case B_POTION:
+				case B_OLDCHEST:
+				case B_CHEST:
+				case B_NUKE:
+				case B_BOLT:
+					ob->state = &s_pshot_exp1;
+					ob->obclass = expobj;
+					ob->ticcount = ob->state->tictime;
+					SpawnBigExplosion(ob->x,ob->y,12,(16l<<16L));
+					bordertime = FLASHTICS<<2;
+					bcolor = 14;
+					VW_ColorBorder(14 | 56);
+					DisplaySMsg("Item destroyed", NULL);
+					status_flag  = S_NONE;
+					status_delay = 80;
+				break;
+			}
+#if USE_INERT_LIST
+			ob->obclass = solidobj;		// don't add this obj to inert list
+#endif
+		break;
+		}
+
+		if (ob->obclass != solidobj && ob->obclass != realsolidobj)
+		{
+			ob->obclass = inertobj;
+			ob->flags &= ~of_shootable;
+			actorat[ob->tilex][ob->tiley] = NULL;
+#if USE_INERT_LIST
+			MoveObjToInert(ob);
+#endif
+		}
+		else
+		{
+			if (ob->flags & of_forcefield)
+			{
+				ob->state = &s_force_field_die;
+				ob->flags &= ~of_shootable;
+			}
+		}
+	}
+	else
+	{
+		switch (ob->obclass)
+		{
+		case wizardobj:
+			ob->state = &s_wizard_ouch;
+		break;
+
+		case trollobj:
+			if (!random(5))
+				ob->state = &s_trollouch;
+			else
+				return;
+		break;
+
+		case blobobj:
+			ob->state = &s_blob_ouch;
+		break;
+
+		case ramboneobj:
+			ob->state = &s_skel_ouch;
+		break;
+
+		case fmageobj:
+			ob->state = &s_fmageouch;
+		break;
+
+		case stompyobj:
+			ob->state = &s_stompy_ouch;
+		break;
+
+		case bugobj:
+			ob->state = &s_bug_ouch;
+		break;
+
+		case cyborgdemonobj:
+			if (!(random(8)))
+				ob->state = &s_cyborg_demonouch;
+			else
+				return;
+		break;
+
+		case demonobj:
+			if (!(random(8)))
+				ob->state = &s_demonouch;
+			else
+				return;
+		break;
+
+		case invisdudeobj:
+			ob->state = &s_invis_fizz1;
+		break;
+
+		case grelmobj:
+			ob->state = &s_grelouch;
+		break;
+
+		case eyeobj:
+			ob->state = &s_eye_ouch;
+		break;
+
+		case reyeobj:
+			ob->state = &s_reye_ouch;
+		break;
+		}
+	}
+
+	ob->ticcount = ob->state->tictime;
+}
+
+
diff --git a/src/lib/hb/c6_trace.c b/src/lib/hb/c6_trace.c
new file mode 100755
index 00000000..82ace1c0
--- /dev/null
+++ b/src/lib/hb/c6_trace.c
@@ -0,0 +1,872 @@
+/* Catacomb Apocalypse Source Code
+ * Copyright (C) 1993-2014 Flat Rock Software
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// C3_TRACE.C
+
+#include "DEF.H"
+#pragma hdrstop
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+
+//
+// TESTWALLVISABLE will set the global variable wallvisable to 1 or 0
+// depending on if tile.x,tile.y,wallon is visable from focal point
+//
+#define TESTWALLVISABLE {						\
+	if (tile.y<focal.y)                         \
+		voffset = 0;                            \
+	else if (tile.y==focal.y)                   \
+		voffset = 3;                            \
+	else                                        \
+		voffset = 6;                            \
+	if (tile.x==focal.x)                        \
+		voffset ++;                             \
+	else if (tile.x>focal.x)                    \
+		voffset += 2;                           \
+	wallvisable = visable[voffset][wallon]; }
+
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+boolean	aborttrace;
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+unsigned	wallvisable,voffset;
+
+
+fixed edgex,edgey;
+
+int wallon;
+int basecolor;
+
+walltype *oldwall;
+
+//
+// offsets from upper left corner of a tile to the left and right edges of
+// a given wall (NORTH-WEST)
+//
+fixed point1x[4] = {GLOBAL1,GLOBAL1,0      ,0       };
+fixed point1y[4] = {0      ,GLOBAL1,GLOBAL1,0       };
+
+fixed point2x[4] = {0      ,GLOBAL1,GLOBAL1,0       };
+fixed point2y[4] = {0     ,0	   ,GLOBAL1 ,GLOBAL1};
+
+
+//
+// offset from tile.x,tile.y of the tile that shares wallon side
+// (side is not visable if it is shared)
+//
+int sharex[4] = { 0, 1, 0,-1};
+int sharey[4] = {-1, 0, 1, 0};
+
+//
+// amount to move tile.x,tile.y to follow wallon to another tile
+//
+int followx[4] = {-1, 0, 1, 0};
+int followy[4] = { 0,-1, 0, 1};
+
+//
+// cornerwall gives the wall on the same tile to start following when the
+// wall ends at an empty tile (go around an edge on same tile)
+// turnwall gives the wall on tile.x+sharex,tile.y+sharey to start following
+// when the wall hits another tile (right angle corner)
+//
+int cornerwall[4] = {WEST,NORTH,EAST,SOUTH};
+int turnwall[4] = {EAST,SOUTH,WEST,NORTH};
+
+//
+// wall visabilities in reletive locations
+// -,- 0,- +,-
+// -,0 0,0 +,0
+// -,+ 0,+ +,+
+//
+int visable[9][4] =
+{
+ {0,1,1,0}, {0,0,1,0}, {0,0,1,1},
+ {0,1,0,0}, {0,0,0,0}, {0,0,0,1},
+ {1,1,0,0}, {1,0,0,0}, {1,0,0,1}
+};
+
+int startwall[9] =  {2,2,3, 1,0,3, 1,0,0};
+int backupwall[9] = {3,3,0, 2,0,0, 2,1,1};
+
+
+int	walllength;
+
+/*
+=============================================================================
+
+					 FUNCTIONS
+
+=============================================================================
+*/
+
+/*
+========================
+=
+= FollowTrace
+=
+========================
+*/
+
+int FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max)
+{
+	int tx,ty,otx,oty;
+	long absdx,absdy,xstep,ystep;
+
+	tx = tracex>>TILESHIFT;
+	ty = tracey>>TILESHIFT;
+
+	spotvis[tx][ty] = true;
+
+	absdx=LABS(deltax);
+	absdy=LABS(deltay);
+
+	if (absdx>absdy)
+	{
+		ystep = (deltay<<8)/(absdx>>8);
+
+		if (!ystep)
+			ystep = deltay>0 ? 1 : -1;
+
+		oty = (tracey+ystep)>>TILESHIFT;
+		if (deltax>0)
+		{
+//###############
+//
+// step x by +1
+//
+//###############
+			do
+			{
+				tx++;
+				spotvis[tx][ty] = true;
+				tracey+=ystep;
+				ty = tracey>>TILESHIFT;
+
+				if (ty!=oty)
+				{
+					if (tilemap[tx-1][ty])
+					{
+						tile.x = tx-1;
+						tile.y = ty;
+						return 1;
+					}
+					oty = ty;
+				}
+				if (tilemap[tx][ty])
+				{
+					tile.x = tx;
+					tile.y = ty;
+					return 1;
+				}
+			} while (--max);
+			return 0;
+		}
+		else
+		{
+//###############
+//
+// step x by -1
+//
+//###############
+			do
+			{
+				tx--;
+				spotvis[tx][ty] = true;
+				tracey+=ystep;
+				ty = tracey>>TILESHIFT;
+
+				if (ty!=oty)
+				{
+					if (tilemap[tx][oty])
+					{
+						tile.x = tx;
+						tile.y = oty;
+						return 1;
+					}
+					oty = ty;
+				}
+				if (tilemap[tx][ty])
+				{
+					tile.x = tx;
+					tile.y = ty;
+					return 1;
+				}
+			} while (--max);
+			return 0;
+
+		}
+	}
+	else
+	{
+		xstep = (deltax<<8)/(absdy>>8);
+		if (!xstep)
+			xstep = deltax>0 ? 1 : -1;
+
+
+		otx = (tracex+xstep)>>TILESHIFT;
+		if (deltay>0)
+		{
+//###############
+//
+// step y by +1
+//
+//###############
+			do
+			{
+				ty++;
+				spotvis[tx][ty] = true;
+				tracex+=xstep;
+				tx = tracex>>TILESHIFT;
+
+				if (tx!=otx)
+				{
+					if (tilemap[tx][ty-1])
+					{
+						tile.x = tx;
+						tile.y = ty-1;
+						return 1;
+					}
+					otx = tx;
+				}
+				if (tilemap[tx][ty])
+				{
+					tile.x = tx;
+					tile.y = ty;
+					return 1;
+				}
+			} while (--max);
+			return 0;
+		}
+		else
+		{
+//###############
+//
+// step y by -1
+//
+//###############
+			do
+			{
+				ty--;
+				spotvis[tx][ty] = true;
+				tracex+=xstep;
+				tx = tracex>>TILESHIFT;
+
+				if (tx!=otx)
+				{
+					if (tilemap[otx][ty])
+					{
+						tile.x = otx;
+						tile.y = ty;
+						return 1;
+					}
+					otx = tx;
+				}
+				if (tilemap[tx][ty])
+				{
+					tile.x = tx;
+					tile.y = ty;
+					return 1;
+				}
+			} while (--max);
+			return 0;
+		}
+
+	}
+
+}
+
+
+//===========================================================================
+
+
+/*
+=================
+=
+= BackTrace
+=
+= Traces backwards from edgex,edgey to viewx,viewy to see if a closer
+= tile obscures the given point.  If it does, it finishes the wall and
+= starts a new one.
+= Returns true if a tile is hit.
+= Call with a 1 to have it automatically finish the current wall
+=
+=================
+*/
+
+int BackTrace (int finish)
+{
+  fixed tracex,tracey;
+  long deltax,deltay,absdx,absdy;
+  int steps,otx,oty,testx,testheight,offset,wall;
+
+  deltax = viewx-edgex;
+  deltay = viewy-edgey;
+
+  absdx = LABS(deltax);
+  absdy = LABS(deltay);
+
+  if (absdx>absdy)
+    steps = ABS(focal.x-(edgex>>TILESHIFT))-1;
+  else
+    steps = ABS(focal.y-(edgey>>TILESHIFT))-1;
+
+  if (steps<=0)
+    return 0;
+
+  otx = tile.x;
+  oty = tile.y;
+  if (!FollowTrace(edgex,edgey,deltax,deltay,steps))
+    return 0;
+
+//
+// if the start wall is behind the focal point, the trace went too far back
+//
+  if (ABS(tile.x-focal.x)<2 && ABS(tile.y-focal.y)<2)	// too close
+  {
+    if (tile.x == focal.x && tile.y == focal.y)
+    {
+      tile.x = otx;
+      tile.y = oty;
+      return 0;
+    }
+
+    if (tile.x<focal.x)
+    {
+      if (tile.y<focal.y)
+	wall = SOUTH;
+      else
+	wall = EAST;
+    }
+    else if (tile.x==focal.x)
+    {
+	  if (tile.y<focal.y)
+	wall = SOUTH;
+      else
+	wall = NORTH;
+    }
+    else
+	{
+      if (tile.y<=focal.y)
+	wall = WEST;
+      else
+	wall = NORTH;
+    }
+
+    //
+    // rotate the X value to see if it is behind the view plane
+    //
+    if (TransformX (((long)tile.x<<16)+point1x[wall],
+		    ((long)tile.y<<16)+point1y[wall]) < FOCALLENGTH)
+    {
+      tile.x = otx;
+      tile.y = oty;
+      return 0;
+    }
+  }
+
+//
+// if the old wall is still behind a closer wall, ignore the back trace
+// and continue on (dealing with limited precision...)
+//
+  if (finish && !FinishWall ())	// the wall is still behind a forward wall
+  {
+    tile.x = otx;
+    tile.y = oty;
+    rightwall->x1 = oldwall->x2;		// common edge with last wall
+    rightwall->height1 = oldwall->height2;
+    return 0;
+  }
+
+
+//
+// back up along the intersecting face to find the rightmost wall
+//
+
+  if (tile.y<focal.y)
+    offset = 0;
+  else if (tile.y==focal.y)
+    offset = 3;
+  else
+    offset = 6;
+  if (tile.x==focal.x)
+    offset ++;
+  else if (tile.x>focal.x)
+    offset += 2;
+
+  wallon = backupwall[offset];
+
+  while (tilemap[tile.x][tile.y])
+  {
+    tile.x += followx[wallon];
+    tile.y += followy[wallon];
+  };
+
+  tile.x -= followx[wallon];
+  tile.y -= followy[wallon];
+
+  wallon = cornerwall[wallon];	// turn to first visable face
+
+  edgex = ((long)tile.x<<16);
+  edgey = ((long)tile.y<<16);
+
+  TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon],
+    &rightwall->x1,&rightwall->height1);
+
+  basecolor = tilemap[tile.x][tile.y];
+
+  return 1;
+}
+
+//===========================================================================
+
+
+/*
+=================
+=
+= ForwardTrace
+=
+= Traces forwards from edgex,edgey along the line from viewx,viewy until
+= a solid tile is hit.  Sets tile.x,tile.y
+=
+=================
+*/
+
+void ForwardTrace (void)
+{
+  int offset;
+  fixed tracex,tracey;
+  long deltax,deltay;
+
+  deltax = edgex-viewx;
+  deltay = edgey-viewy;
+
+  FollowTrace(edgex,edgey,deltax,deltay,0);
+
+  if (tile.y<focal.y)
+    offset = 0;
+  else if (tile.y==focal.y)
+    offset = 3;
+  else
+    offset = 6;
+  if (tile.x==focal.x)
+    offset ++;
+  else if (tile.x>focal.x)
+    offset += 2;
+
+  wallon = startwall[offset];
+
+//
+// start the new wall
+//
+  edgex = ((long)tile.x<<16);
+  edgey = ((long)tile.y<<16);
+
+//
+// if entire first wall is invisable, corner
+//
+  TransformPoint (edgex+point2x[wallon],edgey+point2y[wallon],
+    &rightwall->x2,&rightwall->height2);
+
+  if (tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]]
+  || rightwall->x2 < (rightwall-1)->x2 )
+    wallon = cornerwall [wallon];
+
+//
+// transform first point
+//
+
+  TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon],
+    &rightwall->x1,&rightwall->height1);
+
+  basecolor = tilemap[tile.x][tile.y];
+}
+
+
+//===========================================================================
+
+
+/*
+=================
+=
+= FinishWall
+=
+= Transforms edgex,edgey as the next point of the current wall
+= and sticks it in the wall list
+=
+=================
+*/
+
+int FinishWall (void)
+{
+  char num[20];
+
+  oldwall = rightwall;
+
+	rightwall->color  = basecolor;
+
+  TransformPoint (edgex,edgey,&rightwall->x2,&rightwall->height2);
+
+  if (rightwall->x2 <= (rightwall-1)->x2+2
+  && rightwall->height2 < (rightwall-1)->height2 )
+	return 0;
+
+  rightwall->walllength = walllength;
+
+  switch (wallon)
+  {
+  case north:
+  case south:
+	  rightwall->side = 0;
+	  rightwall->planecoord = edgey;
+	  break;
+
+  case west:
+  case east:
+	  rightwall->side = 1;
+	  rightwall->planecoord = edgex;
+	  break;
+  }
+
+  walllength = 1;
+
+  rightwall++;
+
+  return 1;
+}
+
+//===========================================================================
+
+
+/*
+=================
+=
+= InsideCorner
+=
+=================
+*/
+
+void InsideCorner (void)
+{
+  int offset;
+
+  //
+  // the wall turned -90 degrees, so draw what we have, move to the new tile,
+  // change wallon, change color, and continue following.
+  //
+  FinishWall ();
+
+  tile.x += sharex[wallon];
+  tile.y += sharey[wallon];
+
+  wallon = turnwall[wallon];
+
+  //
+  // if the new wall is visable, continue following it.  Otherwise
+  // follow it backwards until it turns
+  //
+  TESTWALLVISABLE;
+
+  if (wallvisable)
+  {
+  //
+  // just turn to the next wall and continue
+  //
+    rightwall->x1 = oldwall->x2;		// common edge with last wall
+    rightwall->height1 = oldwall->height2;
+    basecolor = tilemap[tile.x][tile.y];
+    return;			// continue from here
+  }
+
+  //
+  // back follow the invisable wall until it turns, then follow that
+  //
+  do
+  {
+	tile.x += followx[wallon];
+    tile.y += followy[wallon];
+  } while (tilemap[tile.x][tile.y]);
+
+  tile.x -= followx[wallon];
+  tile.y -= followy[wallon];
+
+  wallon = cornerwall[wallon];	// turn to first visable face
+
+  edgex = ((long)tile.x<<16)+point1x[wallon];
+  edgey = ((long)tile.y<<16)+point1y[wallon];
+
+  if (!BackTrace(0))		// backtrace without finishing a wall
+  {
+    TransformPoint (edgex,edgey,&rightwall->x1,&rightwall->height1);
+    basecolor = tilemap[tile.x][tile.y];
+  }
+}
+
+//===========================================================================
+
+
+/*
+=================
+=
+= OutsideCorner
+=
+=================
+*/
+
+void OutsideCorner (void)
+{
+  int offset;
+
+  //
+  // edge is the outside edge of a corner, so draw the current wall and
+  // turn the corner (+90 degrees)
+  //
+  FinishWall ();
+
+  tile.x -= followx[wallon];	// backup to the real tile
+  tile.y -= followy[wallon];
+  wallon = cornerwall[wallon];
+
+  //
+  // if the new wall is visable, continue following it.  Otherwise
+  // trace a ray from the corner to find a wall in the distance to
+  // follow
+  //
+  TESTWALLVISABLE;
+
+  if (wallvisable)
+  {
+  //
+  // the new wall is visable, so just continue on
+  //
+    rightwall->x1 = oldwall->x2;		// common edge with last wall
+    rightwall->height1 = oldwall->height2;
+    return;			// still on same tile, so color is ok
+  }
+
+//
+// start from a new tile further away
+//
+  ForwardTrace();		// find the next wall further back
+
+}
+
+
+//===========================================================================
+
+
+/*
+=================
+=
+= FollowWalls
+=
+= Starts a wall edge at the leftmost edge of tile.x,tile.y and follows it
+= until something else is seen or the entire view area is covered
+=
+=================
+*/
+
+void FollowWalls (void)
+{
+  int height,newcolor,offset,wall;
+
+//####################
+//
+// figure leftmost wall of new tile
+//
+//####################
+
+restart:
+
+  walllength = 1;
+
+  if (tile.y<focal.y)
+	offset = 0;
+  else if (tile.y==focal.y)
+	offset = 3;
+  else
+	offset = 6;
+  if (tile.x==focal.x)
+	offset ++;
+  else if (tile.x>focal.x)
+	offset += 2;
+
+  wallon = startwall[offset];
+
+//
+// if the start wall is inside a block, skip it by cornering to the second wall
+//
+  if ( tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]])
+	wallon = cornerwall [wallon];
+
+//
+// transform first edge to screen coordinates
+//
+  edgex = ((long)tile.x<<16);
+  edgey = ((long)tile.y<<16);
+
+  TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon],
+	&rightwall->x1,&rightwall->height1);
+
+  basecolor = tilemap[tile.x][tile.y];
+
+//##################
+//
+// follow the wall as long as possible
+//
+//##################
+
+advance:
+
+  do	// while ( tile.x != right.x || tile.y != right.y)
+  {
+//
+// check for conditions that shouldn't happed...
+//
+	if (rightwall->x1 > VIEWXH)	// somehow missed right tile...
+	  return;
+
+	if (rightwall == &walls[DANGERHIGH])
+	{
+  //
+  // somethiing got messed up!  Correct by thrusting ahead...
+  //
+//		VW_ColorBorder(6);
+		bordertime = 60;
+		Thrust(player->angle,TILEGLOBAL/4);
+		player->angle+=5;
+		if (player->angle>ANGLES)
+			player->angle-=ANGLES;
+		aborttrace = true;
+		return;
+
+#if 0
+	  strcpy (str,"Wall list overflow at LE:");
+	  itoa(mapon+1,str2,10);
+	  strcat (str,str2);
+	  strcat (str," X:");
+	  ltoa(objlist[0].x,str2,10);
+	  strcat (str,str2);
+	  strcat (str," Y:");
+	  ltoa(objlist[0].y,str2,10);
+	  strcat (str,str2);
+	  strcat (str," AN:");
+	  itoa(objlist[0].angle,str2,10);
+	  strcat (str,str2);
+
+	  Quit (str);
+#endif
+	}
+
+//
+// proceed along wall
+//
+
+	edgex = ((long)tile.x<<16)+point2x[wallon];
+	edgey = ((long)tile.y<<16)+point2y[wallon];
+
+	if (BackTrace(1))		// went behind a closer wall
+	  continue;
+
+	//
+	// advance to next tile along wall
+	//
+	tile.x += followx[wallon];
+	tile.y += followy[wallon];
+
+	if (tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]])
+	{
+	  InsideCorner ();		// turn at a corner
+	  continue;
+	}
+
+	newcolor = tilemap[tile.x][tile.y];
+
+	if (!newcolor)		// turn around an edge
+	{
+	  OutsideCorner ();
+	  continue;
+	}
+
+	if (newcolor != basecolor)
+	{
+	  //
+	  // wall changed color, so draw what we have and continue following
+	  //
+	  FinishWall ();
+	  rightwall->x1 = oldwall->x2;	// new wall shares this edge
+	  rightwall->height1 = oldwall->height2;
+	  basecolor = newcolor;
+
+	  continue;
+	}
+	walllength++;
+  } while (tile.x != right.x || tile.y != right.y);
+
+
+
+//######################
+//
+// draw the last tile
+//
+//######################
+
+  edgex = ((long)tile.x<<16)+point2x[wallon];
+  edgey = ((long)tile.y<<16)+point2y[wallon];
+  FinishWall();
+
+  wallon = cornerwall[wallon];
+
+  //
+  // if the corner wall is visable, draw it
+  //
+  TESTWALLVISABLE;
+
+  if (wallvisable)
+  {
+    rightwall->x1 = oldwall->x2;		// common edge with last wall
+    rightwall->height1 = oldwall->height2;
+    edgex = ((long)tile.x<<16)+point2x[wallon];
+    edgey = ((long)tile.y<<16)+point2y[wallon];
+    FinishWall();
+  }
+
+}
+
+//===========================================================================
diff --git a/src/lib/hb/c6_wiz.c b/src/lib/hb/c6_wiz.c
new file mode 100755
index 00000000..571ae3f7
--- /dev/null
+++ b/src/lib/hb/c6_wiz.c
@@ -0,0 +1,3349 @@
+/* Catacomb Apocalypse Source Code
+ * Copyright (C) 1993-2014 Flat Rock Software
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// C3_WIZ.C
+
+#include "DEF.H"
+#include "gelib.h"
+#pragma hdrstop
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+////////#define NUMSCROLLS	8
+
+#define	SHOWITEMS		9
+
+#define NUKETIME			40
+#define NUMBOLTS			10
+#define BOLTTICS			6
+
+#define STATUSCOLOR		1
+#define TEXTCOLOR			14
+
+#define SIDEBARWIDTH		5
+
+#define BODYLINE			8
+#define POWERLINE			80
+
+#define SPECTILESTART	0			// 18
+
+
+#define SHOTDAMAGE		1
+#define BIGSHOTDAMAGE	3
+
+
+#define PLAYERSPEED		5120
+#define RUNSPEED			(8192<<1)
+
+#define SHOTSPEED			10000
+
+//#define LASTWALLTILE		47
+//#define LASTSPECIALTILE	37
+
+#define LASTTILE		(LASTWALLPIC-FIRSTWALLPIC)							// 47
+
+#define FIRETIME		2
+
+#define HANDPAUSE		30
+
+#define	RIGHTEDGE 	205;
+#define	LEFTEDGE  	95;
+#define	PRNY			32;
+#define	WINX			10;
+#define	WINY   		32;
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+long		lastnuke,lasthand;
+int			lasttext;
+int			handheight;
+int			boltsleft,bolttimer;
+short RadarXY[MAX_RADAR_BLIPS][3]={-1,-1,-1};
+short radarx=RADARX,radary=RADARY,radar_xcenter=RADAR_XCENTER,radar_ycenter=RADAR_YCENTER;
+int key_x[4]={24,27,27,24},key_y[4]={30,57,30,57};
+
+boolean redraw_gems,button0down;
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+int			lastradar;
+unsigned	lastfiretime;
+
+int	strafeangle[9] = {0,90,180,270,45,135,225,315,0};
+
+short RotateAngle = -1;				// -1 == No Angle to turn to...
+short FreezeTime = 0;				// Stops all think (except player)
+short RotateSpeed;					// Speed (and dir) to rotate..
+
+
+//===========================================================================
+
+void CalcBounds(objtype *ob);
+boolean VerifyGateExit(void);
+void DrawNSEWIcons(void);
+void DrawGems(void);
+void DrawRadar (void);
+void DrawChar (unsigned x, unsigned y, unsigned tile);
+void RedrawStatusWindow (void);
+void GiveBolt (void);
+void TakeBolt (void);
+void GiveNuke (void);
+void TakeNuke (void);
+void GivePotion (void);
+void TakePotion (void);
+void GiveKey (int keytype);
+void TakeKey (int keytype);
+////////////void GiveScroll (int scrolltype,boolean show);
+////////////void ReadScroll (int scroll);
+////////////void DrawScrolls(void);
+
+void DrawNum(short x,short y,short value,short maxdigits);
+
+//----------
+
+void Shoot (void);
+void BigShoot (void);
+void CastBolt (void);
+void CastNuke (void);
+void DrinkPotion (void);
+
+//----------
+void DrawHealth(void);
+
+void SpawnPlayer (int tilex, int tiley, int dir);
+void Thrust (int angle, unsigned speed);
+void T_Player (objtype *ob);
+
+//void AddPoints (int points);
+
+void ClipMove (objtype *ob, long xmove, long ymove);
+boolean ShotClipMove (objtype *ob, long xmove, long ymove);
+
+//===========================================================================
+
+
+/*
+===============
+=
+= DrawChar
+=
+===============
+*/
+
+void DrawChar (unsigned x, unsigned y, unsigned tile)
+{
+	unsigned junk = latchpics[0];
+
+	EGAWRITEMODE(1);
+asm	mov	bx,[y]
+asm	shl	bx,1
+asm	mov	di,[WORD PTR ylookup+bx]
+asm	add	di,[x]
+asm	mov	si,[tile]
+asm	shl	si,1
+asm	shl	si,1
+asm	shl	si,1
+asm	add	si,[junk]		// the damn inline assembler won't reference latchpics
+asm	mov	ax,[screenseg]
+asm	mov	es,ax
+asm	mov	ds,ax
+asm	mov	dx,SCREENWIDTH-1
+asm	movsb
+asm	add	di,dx
+asm	movsb
+asm	add	di,dx
+asm	movsb
+asm	add	di,dx
+asm	movsb
+asm	add	di,dx
+asm	movsb
+asm	add	di,dx
+asm	movsb
+asm	add	di,dx
+asm	movsb
+asm	add	di,dx
+asm	movsb
+
+asm	mov	ax,ss
+asm	mov	ds,ax
+	EGAWRITEMODE(0);
+}
+
+
+//===========================================================================
+
+/*
+===============
+=
+= RedrawStatusWindow
+=
+===============
+*/
+
+void RedrawStatusWindow (void)
+{
+	short keytype;
+
+	EGABITMASK(0xff);
+	for (keytype=0; keytype<4; keytype++)
+		DrawNum(key_x[keytype],key_y[keytype],gamestate.keys[keytype],2);
+	DrawNum(20,54,gamestate.potions,2);
+	DrawNum(20,36,gamestate.nukes,2);
+	DrawNum(20,18,gamestate.bolts,2);
+
+	DrawHealth();
+	DrawRadar();
+	EGAWRITEMODE(0);
+	DrawGems();
+////////	DrawScrolls();
+	redraw_gems = false;
+}
+
+
+//===========================================================================
+
+/*
+===============
+=
+= GiveBolt
+=
+===============
+*/
+
+void GiveBolt (void)
+{
+	if (gamestate.bolts == 99)
+		return;
+
+	SD_PlaySound (GETBOLTSND);
+	DrawNum(20,18,++gamestate.bolts,2);
+}
+
+
+/*
+===============
+=
+= TakeBolt
+=
+===============
+*/
+
+void TakeBolt (void)
+{
+	SD_PlaySound (USEBOLTSND);
+	DrawNum(20,18,--gamestate.bolts,2);
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= GiveNuke
+=
+===============
+*/
+
+void GiveNuke (void)
+{
+	if (gamestate.nukes == 99)
+		return;
+
+	SD_PlaySound (GETNUKESND);
+	DrawNum(20,36,++gamestate.nukes,2);
+}
+
+
+/*
+===============
+=
+= TakeNuke
+=
+===============
+*/
+
+void TakeNuke (void)
+{
+	SD_PlaySound (USENUKESND);
+	DrawNum(20,36,--gamestate.nukes,2);
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= GivePotion
+=
+===============
+*/
+
+void GivePotion (void)
+{
+	if (gamestate.potions == 99)
+		return;
+
+	SD_PlaySound (GETPOTIONSND);
+	DrawNum(20,54,++gamestate.potions,2);
+}
+
+
+/*
+===============
+=
+= TakePotion
+=
+===============
+*/
+
+void TakePotion (void)
+{
+	SD_PlaySound (USEPOTIONSND);
+	DrawNum(20,54,--gamestate.potions,2);
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= GiveKey
+=
+===============
+*/
+
+void GiveKey (int keytype)
+{
+	int	i,j,x;
+
+	if (gamestate.keys[keytype] == 99)
+		return;
+
+	SD_PlaySound (GETKEYSND);
+	DrawNum(key_x[keytype],key_y[keytype],++gamestate.keys[keytype],2);
+}
+
+
+/*
+===============
+=
+= TakeKey
+=
+===============
+*/
+
+void TakeKey (int keytype)
+{
+	int	i,j,x;
+	char *key_colors[] = {"a RED key",
+								 "a YELLOW key",
+								 "a GREEN key",
+								 "a BLUE key"};
+
+
+	SD_PlaySound (USEKEYSND);
+	DrawNum(key_x[keytype],key_y[keytype],--gamestate.keys[keytype],2);
+	displayofs = bufferofs = screenloc[screenpage];
+	CenterWindow(20,5);
+	US_CPrint("\nYou use\n");
+	US_CPrint(key_colors[keytype]);
+	VW_UpdateScreen();
+	VW_WaitVBL(120);
+}
+
+
+//===========================================================================
+
+/*
+===============
+=
+= GiveGem
+=
+===============
+*/
+
+void GiveGem (int gemtype)
+{
+#if 0
+	int	i,j,x;
+
+	SD_PlaySound (GETKEYSND);
+	DrawNum(key_x[keytype],key_y[keytype],++gamestate.keys[keytype],2);
+#endif
+}
+
+
+/*
+===============
+=
+= TakeGem
+=
+===============
+*/
+
+void TakeGem (int gemtype)
+{
+#if 0
+	int	i,j,x;
+
+	SD_PlaySound (USEKEYSND);
+	DrawNum(key_x[keytype],key_y[keytype],--gamestate.keys[keytype],2);
+#endif
+}
+
+/*
+===============
+=
+= DrawGem
+=
+===============
+*/
+
+void DrawGems()
+{
+	short loop;
+
+	redraw_gems = false;
+
+	bufferofs = 0;
+	LatchDrawPic (31,51,RADAR_BOTTOMPIC);
+	for (loop=0; loop<5; loop++)
+		if (gamestate.gems[loop])
+			LatchDrawPic (32+loop,53,RADAR_RGEMPIC+loop);
+}
+
+//===========================================================================
+
+#if 0
+
+/*
+===============
+=
+= GiveScroll
+=
+===============
+*/
+
+void GiveScroll (int scrolltype,boolean show)
+{
+	int	i,j,x,y,scrollnum;
+
+	SD_PlaySound (GETSCROLLSND);
+	gamestate.scrolls[scrolltype] = true;
+
+	y = 30 + ((scrolltype > 3) * 10);
+	x = 26 + (scrolltype % 4);
+	DrawChar(x,y,SCROLLCHARS+scrolltype);
+
+	if (show)
+		ReadScroll(scrolltype);
+}
+
+/*
+===============
+=
+= DrawScrolls
+=
+= Force draw of all scrolls
+=
+===============
+*/
+void DrawScrolls()
+{
+	int loop,x,y;
+
+	VW_Bar(210,30,30,18,0xf);
+
+	for (loop=0;loop<8;loop++)
+		if (gamestate.scrolls[loop])
+		{
+			y = 30 + ((loop > 3) * 10);
+			x = 26 + (loop % 4);
+			DrawChar(x,y,SCROLLCHARS+loop);
+		}
+}
+#endif
+
+
+//===========================================================================
+
+#if 0
+/*
+===============
+=
+= GivePoints
+=
+===============
+*/
+
+void GivePoints (int points)
+{
+	pointcount = 1;
+	pointsleft += points;
+}
+#endif
+
+
+//===========================================================================
+
+#if 0
+/*
+===============
+=
+= AddPoints
+=
+===============
+*/
+
+void AddPoints (int points)
+{
+	char	str[10];
+	int		len,x,i;
+
+	gamestate.score += points;
+
+	ltoa (gamestate.score,str,10);
+	len = strlen (str);
+
+	x=24+(8-len);
+	for (i=0;i<len;i++)
+		DrawChar(x++,40,NUMBERCHARS+str[i]-'0');
+}
+#endif
+
+
+//===========================================================================
+
+/*
+===============
+=
+= DrawHealth
+=
+===============
+*/
+void DrawHealth()
+{
+	char picnum;
+	int percentage;
+
+	percentage = PERCENTAGE(100,MAXBODY,gamestate.body,9);
+
+	DrawNum(11,57,percentage,3);
+
+	if (percentage > 75)
+		picnum = FACE1PIC;
+	else
+	if (percentage > 50)
+		picnum = FACE2PIC;
+	else
+	if (percentage > 25)
+		picnum = FACE3PIC;
+	else
+	if (percentage)
+		picnum = FACE4PIC;
+	else
+	{
+		picnum = FACE5PIC;
+		CA_CacheGrChunk (picnum);
+	}
+
+	bufferofs = 0;
+	if (!percentage)
+	{
+		UNMARKGRCHUNK(picnum);
+//		VW_DrawPic(8,14,picnum);
+		VW_DrawPic(10,14,picnum);
+	}
+	else
+		LatchDrawPic(10,14,picnum);
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= DrawFreezeTime
+=
+===============
+*/
+void DrawFreezeTime()
+{
+	short temp =  fontcolor;
+	long percentage;
+	percentage = PERCENTAGE(100,MAXFREEZETIME,(long)FreezeTime,7);
+	fontcolor = 1 ^ 14;
+	DrawNum(23,70,percentage,3);
+	fontcolor = temp;
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= DrawNum
+=
+===============
+*/
+void DrawNum(short x,short y,short value,short maxdigits)
+{
+	char str[10],len,i;
+
+	itoa(value,str,10);
+	len=strlen(str);
+
+	for (i=len; i<maxdigits; i++)
+		DrawChar(x++,y,BLANKCHAR);
+
+	for (i=0;i<len;i++)
+		DrawChar(x++,y,NUMBERCHARS+str[i]-'0');
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= GiveChest
+=
+===============
+*/
+
+void GiveChest(void)
+{
+	char i;
+
+	for (i=0;i<random(4);i++)
+	{
+		GiveBolt();
+		SD_WaitSoundDone();
+	}
+
+	for (i=0;i<random(3);i++)
+	{
+		GiveNuke();
+		SD_WaitSoundDone();
+	}
+
+	for (i=0;i<random(2);i++)
+	{
+		GivePotion();
+		SD_WaitSoundDone();
+	}
+}
+
+
+//===========================================================================
+
+/*
+===============
+=
+= GiveGoal
+=
+===============
+*/
+
+void GiveGoal (void)
+{
+	SD_PlaySound (GETPOINTSSND);
+	playstate = ex_victorious;
+}
+
+
+//===========================================================================
+
+#if 0
+/*
+===============
+=
+= DrawLevelNumber
+=
+===============
+*/
+
+void DrawLevelNumber (int number)
+{
+	char	str[10];
+	int		len;
+	unsigned	temp;
+
+	bufferofs = 0;
+	if (number<9)
+		PrintX=13;
+	else
+		PrintX = 5;
+	PrintY = 4;
+	VW_Bar (5,4,16,9,STATUSCOLOR);
+	temp = fontcolor;
+	fontcolor = TEXTCOLOR^STATUSCOLOR;
+	US_PrintUnsigned (number+1);
+	fontcolor = temp;
+}
+#endif
+
+
+//===========================================================================
+
+/*
+===============
+=
+= DrawText
+=
+===============
+*/
+
+void DrawText (boolean draw_text_whether_it_needs_it_or_not)
+{
+	unsigned	number;
+	char		str[80];
+	char 		far *text;
+	unsigned	temp;
+
+	//
+	// draw a new text description if needed
+	//
+	number = *(mapsegs[0]+farmapylookup[player->tiley]+player->tilex)-NAMESTART;
+
+	if ( number>26 )
+		number = 0;
+
+	if ((number == lasttext) && (!draw_text_whether_it_needs_it_or_not))
+		return;
+
+	lasttext = number;
+
+	text = (char _seg *)grsegs[LEVEL1TEXT+mapon]+textstarts[number];
+
+	if (text[0] == '@')
+	{
+		bordertime = 20;//FLASHTICS;
+		bcolor = 15;
+		VW_ColorBorder (15 | 56);
+		text++;
+	}
+
+	_fmemcpy (str,text,80);
+	DisplayMsg(str,NULL);
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= DisplayMsg
+=
+===============
+*/
+
+char DisplayMsg(char *text,char *choices)
+{
+	char ch=true;
+	short temp;
+
+	bufferofs = 0;
+	PrintY = 1;
+	WindowX = 20;
+	WindowW = 270;
+
+	VW_Bar (WindowX,2,WindowW,8,STATUSCOLOR);
+	temp = fontcolor;
+	fontcolor = TEXTCOLOR^STATUSCOLOR;
+	US_CPrintLine (text);
+	fontcolor = temp;
+
+	if (choices)
+	{
+		ch=GetKeyChoice(choices,true);
+		LastScan = 0;
+	}
+
+	return(ch);
+}
+
+/*
+===============
+=
+= DisplaySMsg
+=
+===============
+*/
+char DisplaySMsg(char *text,char *choices)
+{
+	char ch=true;
+	short temp;
+
+	bufferofs = 0;
+	PrintY = 69;
+	WindowX = 98;
+	WindowW = 115;
+
+	VW_Bar(WindowX,PrintY+1,WindowW,8,STATUSCOLOR);
+	temp = fontcolor;
+	fontcolor = TEXTCOLOR^STATUSCOLOR;
+	US_CPrintLine (text);
+	fontcolor = temp;
+
+	if (choices)
+	{
+		ch=GetKeyChoice(choices,true);
+		LastScan = 0;
+	}
+
+	return(ch);
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= DrawRadar
+=
+===============
+*/
+
+void DrawRadar (void)
+{
+	int		angle,number;
+	short objnum;
+
+	bufferofs = 0;
+	LatchDrawPic (radarx,radary,RADAR_TOPPIC);
+
+	asm	cli
+	asm	mov	dx,GC_INDEX
+	asm	mov	ax,2*256+GC_MODE
+	asm	out	dx,ax						// write mode 2
+
+	asm	mov	ax,GC_DATAROTATE
+	asm	out	dx,ax                // no rotation / logical operation
+
+	asm	mov	dx,SC_INDEX
+	asm	mov	al,SC_MAPMASK
+	asm	mov	ah,15
+	asm	out	dx,ax						// write to all four planes
+	asm	sti
+
+	objnum = 0;
+	while (RadarXY[objnum][2] != -1)
+	{
+		RadarBlip(radar_xcenter+RadarXY[objnum][0],radar_ycenter+RadarXY[objnum][1],RadarXY[objnum][2]);
+		objnum++;
+	}
+
+	asm	cli
+	asm	mov	dx,GC_INDEX
+	asm	mov	ax,255*256+GC_BITMASK
+	asm	out	dx,ax						// reset bitmask to %11111111
+	asm	sti
+}
+
+//===========================================================================
+
+
+//--------------------------------------------------------------------------
+// DrawNSEWIcons(void)
+//--------------------------------------------------------------------------
+
+void DrawRadarObj(short dx, short dy, unsigned sprnum,signed long psin,signed long pcos);
+
+void DrawNSEWIcons()
+{
+	signed x,y;
+
+	x = -FixedByFrac(RADAR_X_IRADIUS,costable[player->angle]);
+	y = -FixedByFrac(RADAR_Y_IRADIUS,sintable[player->angle]);
+
+	VWB_DrawSprite(radar_xcenter+x-3,radar_ycenter+y-3,NORTHICONSPR);
+
+}
+
+#if 0
+/*
+===============
+=
+= DrawBars
+=
+===============
+*/
+
+void DrawBars (void)
+{
+	int			i;
+	unsigned	source,dest,topline;
+
+	for (i=0;i<3;i++)
+	{
+		bufferofs = screenloc[i];
+		VW_Bar (34*8,POWERLINE,40,MAXSHOTPOWER,1);
+	}
+	EGAWRITEMODE(1);
+	asm	mov	es,[screenseg]
+
+//
+// shot power
+//
+	if (gamestate.shotpower)
+	{
+		topline = MAXSHOTPOWER - gamestate.shotpower;
+
+		source = latchpics[SHOTPOWERPIC-FIRSTLATCHPIC]+topline*SIDEBARWIDTH;
+		dest = (POWERLINE+topline)*SCREENWIDTH+34;
+
+		asm	mov	si,[source]
+		asm	mov	di,[dest]
+
+		asm	mov	cx,[WORD PTR gamestate.shotpower]
+newline:
+	asm	mov	al,[es:si]
+	asm	mov	[es:di+PAGE1START],al
+	asm	mov	[es:di+PAGE2START],al
+	asm	mov	[es:di+PAGE3START],al
+	asm	mov	al,[es:si+1]
+	asm	mov	[es:di+1+PAGE1START],al
+	asm	mov	[es:di+1+PAGE2START],al
+	asm	mov	[es:di+1+PAGE3START],al
+	asm	mov	al,[es:si+2]
+	asm	mov	[es:di+2+PAGE1START],al
+	asm	mov	[es:di+2+PAGE2START],al
+	asm	mov	[es:di+2+PAGE3START],al
+	asm	mov	al,[es:si+3]
+	asm	mov	[es:di+3+PAGE1START],al
+	asm	mov	[es:di+3+PAGE2START],al
+	asm	mov	[es:di+3+PAGE3START],al
+	asm	mov	al,[es:si+4]
+	asm	mov	[es:di+4+PAGE1START],al
+	asm	mov	[es:di+4+PAGE2START],al
+	asm	mov	[es:di+4+PAGE3START],al
+
+	asm	add	di,SCREENWIDTH
+	asm	add	si,5
+
+		asm	loop	newline
+	}
+
+//
+// body
+//
+	if (gamestate.body)
+	{
+		source = latchpics[BODYPIC-FIRSTLATCHPIC];
+		dest = BODYLINE*SCREENWIDTH+34;
+
+		asm	mov	si,[source]
+		asm	mov	di,[dest]
+
+		asm	mov	cx,[WORD PTR gamestate.body]
+newline2:
+	asm	mov	al,[es:si]
+	asm	mov	[es:di+PAGE1START],al
+	asm	mov	[es:di+PAGE2START],al
+	asm	mov	[es:di+PAGE3START],al
+	asm	mov	al,[es:si+1]
+	asm	mov	[es:di+1+PAGE1START],al
+	asm	mov	[es:di+1+PAGE2START],al
+	asm	mov	[es:di+1+PAGE3START],al
+	asm	mov	al,[es:si+2]
+	asm	mov	[es:di+2+PAGE1START],al
+	asm	mov	[es:di+2+PAGE2START],al
+	asm	mov	[es:di+2+PAGE3START],al
+	asm	mov	al,[es:si+3]
+	asm	mov	[es:di+3+PAGE1START],al
+	asm	mov	[es:di+3+PAGE2START],al
+	asm	mov	[es:di+3+PAGE3START],al
+	asm	mov	al,[es:si+4]
+	asm	mov	[es:di+4+PAGE1START],al
+	asm	mov	[es:di+4+PAGE2START],al
+	asm	mov	[es:di+4+PAGE3START],al
+
+	asm	add	di,SCREENWIDTH
+	asm	add	si,5
+
+		asm	loop	newline2
+	}
+
+	if (gamestate.body != MAXBODY)
+	{
+		source = latchpics[NOBODYPIC-FIRSTLATCHPIC]+gamestate.body*SIDEBARWIDTH;
+		dest = (BODYLINE+gamestate.body)*SCREENWIDTH+34;
+		topline = MAXBODY-gamestate.body;
+
+		asm	mov	si,[source]
+		asm	mov	di,[dest]
+
+		asm	mov	cx,[WORD PTR topline]
+newline3:
+	asm	mov	al,[es:si]
+	asm	mov	[es:di+PAGE1START],al
+	asm	mov	[es:di+PAGE2START],al
+	asm	mov	[es:di+PAGE3START],al
+	asm	mov	al,[es:si+1]
+	asm	mov	[es:di+1+PAGE1START],al
+	asm	mov	[es:di+1+PAGE2START],al
+	asm	mov	[es:di+1+PAGE3START],al
+	asm	mov	al,[es:si+2]
+	asm	mov	[es:di+2+PAGE1START],al
+	asm	mov	[es:di+2+PAGE2START],al
+	asm	mov	[es:di+2+PAGE3START],al
+	asm	mov	al,[es:si+3]
+	asm	mov	[es:di+3+PAGE1START],al
+	asm	mov	[es:di+3+PAGE2START],al
+	asm	mov	[es:di+3+PAGE3START],al
+	asm	mov	al,[es:si+4]
+	asm	mov	[es:di+4+PAGE1START],al
+	asm	mov	[es:di+4+PAGE2START],al
+	asm	mov	[es:di+4+PAGE3START],al
+
+	asm	add	di,SCREENWIDTH
+	asm	add	si,5
+
+		asm	loop	newline3
+	}
+
+	EGAWRITEMODE(0);
+}
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+//
+//  Check the object and make sure it is a monster.  Used in making the sound
+//  of a monster being shot.
+//
+/////////////////////////////////////////////////////////////////////////////
+
+boolean PlayMonsterSound(classtype objclass)
+{
+	switch (objclass)
+	{
+		case solidobj:
+		case realsolidobj:
+			return false;
+		default:
+			return true;
+	}
+}
+
+
+/*
+=============================================================================
+
+							SHOTS
+
+=============================================================================
+*/
+
+void T_Pshot (objtype *ob);
+
+
+extern	statetype s_pshot1;
+extern	statetype s_pshot2;
+
+//extern	statetype s_bigpshot1;
+//extern	statetype s_bigpshot2;
+
+
+statetype s_pshot1 = {PSHOT1PIC,8,&T_Pshot,&s_pshot2};
+statetype s_pshot2 = {PSHOT2PIC,8,&T_Pshot,&s_pshot1};
+
+
+statetype s_pshot_exp1 = {PSHOT_EXP1PIC,7,NULL,&s_pshot_exp2};
+statetype s_pshot_exp2 = {PSHOT_EXP2PIC,7,NULL,&s_pshot_exp3};
+statetype s_pshot_exp3 = {PSHOT_EXP3PIC,7,NULL,NULL};
+
+
+//statetype s_shotexplode = {PSHOT2PIC,8,NULL,NULL};
+
+//statetype s_bigpshot1 = {BIGPSHOT1PIC,8,&T_Pshot,&s_bigpshot2};
+//statetype s_bigpshot2 = {BIGPSHOT2PIC,8,&T_Pshot,&s_bigpshot1};
+
+
+/*
+===================
+=
+= SpawnPShot
+=
+===================
+*/
+
+void SpawnPShot (void)
+{
+	DSpawnNewObjFrac (player->x,player->y,&s_pshot1,PIXRADIUS*2);
+	new->obclass = pshotobj;
+	new->speed = SHOTSPEED;
+	new->angle = player->angle;
+	new->active = always;
+}
+
+#if 0
+void SpawnBigPShot (void)
+{
+	SpawnNewObjFrac (player->x,player->y,&s_bigpshot1,24*PIXRADIUS);
+	new->obclass = bigpshotobj;
+	new->speed = SHOTSPEED;
+	new->angle = player->angle;
+}
+#endif
+
+
+/*
+===================
+=
+= JimsShotClipMove
+=
+= Only checks corners, so the object better be less than one tile wide!
+=
+===================
+*/
+boolean JimsShotClipMove (objtype *ob, long xmove, long ymove)
+{
+	int			xl,yl,xh,yh,tx,ty,nt1,nt2,x,y;
+	long		intersect,basex,basey,pointx,pointy;
+	unsigned	inside,total,tile;
+	objtype		*check;
+	boolean		moveok;
+
+//
+// move player and check to see if any corners are in solid tiles
+//
+//	basex = ob->x;
+//	basey = ob->y;
+
+//	ob->x += xmove;
+//	ob->y += ymove;
+
+//	CalcBounds (ob);
+
+	xl = ob->xl>>TILESHIFT;
+	yl = ob->yl>>TILESHIFT;
+
+	xh = ob->xh>>TILESHIFT;
+	yh = ob->yh>>TILESHIFT;
+
+	for (y=yl;y<=yh;y++)
+		for (x=xl;x<=xh;x++)
+		{
+			check = actorat[x][y];
+
+			if ((!check) || (check == player) || (!(check->flags & of_shootable)))
+				continue;
+
+			ob->x -= xmove;
+			ob->y -= ymove;
+
+			if (check->obclass != solidobj)
+			{
+				if (PlayMonsterSound(check->obclass))
+					SD_PlaySound (SHOOTMONSTERSND);
+				if (ob->obclass == bigpshotobj)
+					ShootActor (check,BIGSHOTDAMAGE);
+				else
+					ShootActor (check,SHOTDAMAGE);
+			}
+			else
+				if (check->obclass == solidobj && (check->flags & of_forcefield))
+				{
+					if (PlayMonsterSound(check->obclass))
+						SD_PlaySound (SHOOTMONSTERSND);
+					if (ob->obclass == bigpshotobj)
+						ShootActor (check,BIGSHOTDAMAGE);
+					else
+						ShootActor (check,SHOTDAMAGE);
+				}
+			ob->state = &s_pshot_exp1;
+			ob->ticcount = ob->state->tictime;
+			return(true);
+		}
+
+	return(false);		// move is OK!
+
+}
+
+
+/*
+===============
+=
+= T_Pshot
+=
+===============
+*/
+#if 0
+void T_Pshot (objtype *ob)
+{
+	objtype	*check;
+	long	xmove,ymove,speed;
+
+//
+// check current position for monsters having moved into it
+//
+	for (check = player->next; check; check=check->next)
+		if ((check->flags & of_shootable)
+		&& ob->xl <= check->xh
+		&& ob->xh >= check->xl
+		&& ob->yl <= check->yh
+		&& ob->yh >= check->yl)
+		{
+
+			if (check->obclass != solidobj)
+			{
+				if (PlayMonsterSound(check->obclass))
+					SD_PlaySound (SHOOTMONSTERSND);
+				if (ob->obclass == bigpshotobj)
+					ShootActor (check,BIGSHOTDAMAGE);
+				else
+					ShootActor (check,SHOTDAMAGE);
+			}
+
+			ob->state = &s_pshot_exp1;
+			ob->ticcount = ob->state->tictime;
+			return;
+		}
+
+
+//
+// move ahead, possibly hitting a wall
+//
+	speed = ob->speed*tics;
+
+	xmove = FixedByFrac(speed,costable[ob->angle]);
+	ymove = -FixedByFrac(speed,sintable[ob->angle]);
+
+	if (ShotClipMove(ob,xmove,ymove))
+	{
+		ob->state = &s_pshot_exp1;
+		ob->ticcount = ob->state->tictime;
+		return;
+	}
+
+	ob->tilex = ob->x >> TILESHIFT;
+	ob->tiley = ob->y >> TILESHIFT;
+
+//
+// check final position for monsters hit
+//
+	for (check = player->next; check; check=check->next)
+		if ((ob->flags & of_shootable)
+		&& ob->xl <= check->xh
+		&& ob->xh >= check->xl
+		&& ob->yl <= check->yh
+		&& ob->yh >= check->yl)
+		{
+			ShootActor (check,SHOTDAMAGE);
+			ob->state = &s_pshot_exp1;
+			ob->ticcount = ob->state->tictime;
+			return;
+		}
+}
+#endif
+
+
+
+void T_Pshot (objtype *ob)
+{
+	objtype	*check;
+	long	xmove,ymove,speed;
+	int			xl,yl,xh,yh,tx,ty,nt1,nt2,x,y;
+	long		intersect,basex,basey,pointx,pointy;
+	unsigned	inside,total,tile;
+	boolean		moveok;
+
+//
+// check current position for monsters having moved into it
+//
+	for (check = player->next; check; check=check->next)
+		if ((check->flags & of_shootable)
+		&& ob->xl <= check->xh
+		&& ob->xh >= check->xl
+		&& ob->yl <= check->yh
+		&& ob->yh >= check->yl)
+		{
+
+			if (check->obclass != solidobj)
+			{
+				if (PlayMonsterSound(check->obclass))
+					SD_PlaySound (SHOOTMONSTERSND);
+				if (ob->obclass == bigpshotobj)
+					ShootActor (check,BIGSHOTDAMAGE);
+				else
+					ShootActor (check,SHOTDAMAGE);
+			}
+
+			ob->state = &s_pshot_exp1;
+			ob->obclass = expobj;
+			ob->ticcount = ob->state->tictime;
+			return;
+		}
+
+
+//
+// move ahead, possibly hitting a wall
+//
+	speed = ob->speed*tics;
+
+	xmove = FixedByFrac(speed,costable[ob->angle]);
+	ymove = -FixedByFrac(speed,sintable[ob->angle]);
+
+	if (ShotClipMove(ob,xmove,ymove))
+	{
+		ob->state = &s_pshot_exp1;
+		ob->obclass = expobj;
+		ob->ticcount = ob->state->tictime;
+		return;
+	}
+
+	ob->tilex = ob->x >> TILESHIFT;
+	ob->tiley = ob->y >> TILESHIFT;
+
+//
+// check final position for monsters hit
+//
+
+	JimsShotClipMove(obj,xmove,ymove);
+
+}
+
+
+/*
+=============================================================================
+
+							PLAYER ACTIONS
+
+=============================================================================
+*/
+
+/*
+===============
+=
+= BuildShotPower
+=
+===============
+*/
+
+void BuildShotPower (void)
+{
+	int		newlines,topline;
+	long	i;
+	unsigned	source,dest;
+
+	if (gamestate.shotpower == MAXSHOTPOWER)
+		return;
+
+	newlines = 0;
+	for (i=lasttimecount-realtics;i<lasttimecount;i++)
+		newlines += (i&1);
+
+	gamestate.shotpower += newlines;
+
+	if (gamestate.shotpower > MAXSHOTPOWER)
+	{
+		newlines -= (gamestate.shotpower - MAXSHOTPOWER);
+		gamestate.shotpower = MAXSHOTPOWER;
+	}
+}
+
+
+//===========================================================================
+
+/*
+===============
+=
+= ClearShotPower
+=
+===============
+*/
+
+void ClearShotPower (void)
+{
+	unsigned	source,dest,topline;
+
+#if 0
+	topline = MAXSHOTPOWER - gamestate.shotpower;
+
+	source = latchpics[L_NOSHOT]+topline*SIDEBARWIDTH;
+	dest = (POWERLINE+topline)*SCREENWIDTH+34;
+
+	asm	mov	es,[screenseg]
+	asm	mov	si,[source]
+	asm	mov	di,[dest]
+
+	if (!gamestate.shotpower)
+		return;
+
+	EGAWRITEMODE(1);
+
+	asm	mov	cx,[WORD PTR gamestate.shotpower]
+newline:
+	asm	mov	al,[es:si]
+	asm	mov	[es:di+PAGE1START],al
+	asm	mov	[es:di+PAGE2START],al
+	asm	mov	[es:di+PAGE3START],al
+	asm	mov	al,[es:si+1]
+	asm	mov	[es:di+1+PAGE1START],al
+	asm	mov	[es:di+1+PAGE2START],al
+	asm	mov	[es:di+1+PAGE3START],al
+	asm	mov	al,[es:si+2]
+	asm	mov	[es:di+2+PAGE1START],al
+	asm	mov	[es:di+2+PAGE2START],al
+	asm	mov	[es:di+2+PAGE3START],al
+	asm	mov	al,[es:si+3]
+	asm	mov	[es:di+3+PAGE1START],al
+	asm	mov	[es:di+3+PAGE2START],al
+	asm	mov	[es:di+3+PAGE3START],al
+	asm	mov	al,[es:si+4]
+	asm	mov	[es:di+4+PAGE1START],al
+	asm	mov	[es:di+4+PAGE2START],al
+	asm	mov	[es:di+4+PAGE3START],al
+
+	asm	add	di,SCREENWIDTH
+	asm	add	si,5
+
+	asm	loop	newline
+
+	EGAWRITEMODE(0);
+#endif
+
+	gamestate.shotpower = 0;
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= Shoot
+=
+===============
+*/
+
+void Shoot (void)
+{
+	ClearShotPower ();
+	SD_PlaySound (SHOOTSND);
+	SpawnPShot ();
+}
+
+//===========================================================================
+
+#if 0
+/*
+===============
+=
+= BigShoot
+=
+===============
+*/
+
+void BigShoot (void)
+{
+	ClearShotPower ();
+	SD_PlaySound (BIGSHOOTSND);
+	SpawnBigPShot ();
+}
+#endif
+
+//===========================================================================
+
+/*
+===============
+=
+= CastBolt
+=
+===============
+*/
+
+void CastBolt (void)
+{
+	if (!gamestate.bolts)
+	{
+		SD_PlaySound (NOITEMSND);
+		return;
+	}
+
+	TakeBolt ();
+	boltsleft = NUMBOLTS;
+	bolttimer = BOLTTICS;
+	Shoot ();
+}
+
+
+/*
+===============
+=
+= ContinueBolt
+=
+===============
+*/
+
+void ContinueBolt (void)
+{
+	bolttimer-=realtics;
+	if (bolttimer<0)
+	{
+		boltsleft--;
+		bolttimer = BOLTTICS;
+		Shoot ();
+	}
+}
+
+
+//===========================================================================
+
+/*
+===============
+=
+= CastNuke
+=
+===============
+*/
+
+void CastNuke (void)
+{
+//	extern boolean autofire;
+
+	int	angle;
+
+	if (!gamestate.nukes)
+	{
+		SD_PlaySound (NOITEMSND);
+		return;
+	}
+
+//	if (!autofire)
+		TakeNuke ();
+	lastnuke = TimeCount;
+
+	for (angle = 0; angle < ANGLES; angle+= ANGLES/16)
+	{
+		DSpawnNewObjFrac (player->x,player->y,&s_pshot1,24*PIXRADIUS);
+		new->obclass = bigpshotobj;
+		new->speed = SHOTSPEED;
+		new->angle = angle;
+		new->active = always;
+	}
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= DrinkPotion
+=
+===============
+*/
+
+void DrinkPotion (void)
+{
+	unsigned	source,dest,topline;
+
+	if (!gamestate.potions)
+	{
+		SD_PlaySound (NOITEMSND);
+		return;
+	}
+
+	DisplaySMsg("Curing", NULL);
+	TakePotion ();
+	gamestate.body = MAXBODY;
+	VW_WaitVBL(30);
+	status_flag    = S_NONE;
+
+#if 0
+//
+// draw a full up bar
+//
+	source = latchpics[L_BODYBAR];
+	dest = BODYLINE*SCREENWIDTH+34;
+
+	asm	mov	es,[screenseg]
+	asm	mov	si,[source]
+	asm	mov	di,[dest]
+
+	EGAWRITEMODE(1);
+
+	asm	mov	cx,MAXBODY
+newline:
+	asm	mov	al,[es:si]
+	asm	mov	[es:di+PAGE1START],al
+	asm	mov	[es:di+PAGE2START],al
+	asm	mov	[es:di+PAGE3START],al
+	asm	mov	al,[es:si+1]
+	asm	mov	[es:di+1+PAGE1START],al
+	asm	mov	[es:di+1+PAGE2START],al
+	asm	mov	[es:di+1+PAGE3START],al
+	asm	mov	al,[es:si+2]
+	asm	mov	[es:di+2+PAGE1START],al
+	asm	mov	[es:di+2+PAGE2START],al
+	asm	mov	[es:di+2+PAGE3START],al
+	asm	mov	al,[es:si+3]
+	asm	mov	[es:di+3+PAGE1START],al
+	asm	mov	[es:di+3+PAGE2START],al
+	asm	mov	[es:di+3+PAGE3START],al
+	asm	mov	al,[es:si+4]
+	asm	mov	[es:di+4+PAGE1START],al
+	asm	mov	[es:di+4+PAGE2START],al
+	asm	mov	[es:di+4+PAGE3START],al
+	asm	add	di,SCREENWIDTH
+	asm	add	si,5
+
+	asm	loop	newline
+
+	EGAWRITEMODE(0);
+#endif
+}
+
+
+
+//===========================================================================
+
+#if 0
+
+////////////////////////////////////////////////////////////////////////////
+//
+//   GetScrollText
+//
+//   parms   - scroll -- the number of the scroll to display
+//   returns - a far pointer to the scroll text
+//
+////////////////////////////////////////////////////////////////////////////
+
+char far *GetScrollText (int scroll)
+{
+	boolean found;
+	int     i;
+	char far *txt;
+	unsigned ofset;
+
+	CA_CacheGrChunk(SCROLLTEXT);
+
+	found = false;
+	i     = 0;
+
+	txt = (char _seg *)grsegs[SCROLLTEXT];
+
+	while (!found)
+	{
+		while (*txt != '\n')
+		{
+			if (*txt == '\r')
+				*txt = 0;
+			txt++;
+		}
+		txt++;
+		if (i == scroll)
+		{
+			found   = true;
+			ofset = FP_OFF(txt);
+
+			while (*txt != '\n')
+			{
+				if (*txt == '\r')
+					*txt = 0;
+				txt++;
+			}
+		}
+		i++;
+	}
+	txt = (char _seg *)grsegs[SCROLLTEXT]+ofset;
+
+	UNMARKGRCHUNK(SCROLLTEXT);
+	return(txt);
+}  	//End of GetScrollText
+
+//===========================================================================
+
+/*
+===============
+=
+= ReadScroll
+=
+===============
+*/
+
+extern	boolean	tileneeded[NUMFLOORS];
+
+void ReadScroll (int scroll)
+{
+	PresenterInfo pi;
+	int	i;
+	unsigned *skytemp,*gndtemp,blackcolor=0;
+	char far *scrolltext;
+
+	DisplaySMsg("Reading Scroll", NULL);
+	bufferofs = displayofs = screenloc[screenpage];
+
+	if (status_flag != S_TIMESTOP)
+		status_flag = S_NONE;
+
+	FreeUpMemory();
+
+	CA_CacheGrChunk (SCROLLTOPPIC);
+	CA_CacheGrChunk (SCROLL1PIC);
+	CA_CacheGrChunk (SCROLLBOTTOMPIC);
+
+	skytemp = skycolor;
+	gndtemp = groundcolor;
+	skycolor = groundcolor = &blackcolor;
+
+	VW_Bar(0,0,VIEWWIDTH,VIEWHEIGHT,0);
+	VW_DrawPic (10,0,SCROLLTOPPIC);
+	VW_DrawPic (10,32,SCROLL1PIC);
+	VW_DrawPic (10,88,SCROLLBOTTOMPIC);
+
+	scrolltext = GetScrollText(scroll);
+
+	pi.xl = LEFTEDGE;
+	pi.yl = PRNY;
+	pi.xh = RIGHTEDGE;
+	pi.yh = PRNY+1;
+	pi.bgcolor = 7;
+	pi.script[0] = (char far *)scrolltext;
+	Presenter(&pi);
+
+	skycolor = skytemp;
+	groundcolor = gndtemp;
+
+	UNMARKGRCHUNK(SCROLL1PIC);
+	UNMARKGRCHUNK(SCROLLTOPPIC);
+	UNMARKGRCHUNK(SCROLLBOTTOMPIC);
+	MM_FreePtr (&grsegs[SCROLL1PIC]);
+	MM_FreePtr (&grsegs[SCROLLTOPPIC]);
+	MM_FreePtr (&grsegs[SCROLLBOTTOMPIC]);
+
+	CacheScaleds();
+
+	IN_ClearKeysDown ();
+	lasttext = -1;
+	DisplayMsg("Press ENTER or ESC to exit.",NULL);
+	while ((!Keyboard[sc_Escape]) && (!Keyboard[sc_Enter]));
+	IN_ClearKeysDown ();
+
+	if (status_flag == S_TIMESTOP)
+		DisplaySMsg("Time Stopped:     ",NULL);
+}
+
+#endif
+
+
+//===============
+//
+// StopTime()
+//
+//
+//===============
+void StopTime()
+{
+	FreezeTime = MAXFREEZETIME;
+	SD_PlaySound(FREEZETIMESND);
+	DisplaySMsg("Time Stopped:     ",NULL);
+	status_flag = S_TIMESTOP;
+}
+
+
+/*
+===============
+=
+= TakeDamage
+=
+===============
+*/
+
+void TakeDamage (int points)
+{
+	unsigned	source,dest,topline;
+
+	if (!gamestate.body || (bordertime && bcolor==FLASHCOLOR) || godmode)
+		return;
+
+	points = EasyDoDamage(points);
+
+	if (points >= gamestate.body)
+	{
+		points = gamestate.body;
+		Flags |= FL_DEAD;
+	}
+
+	bordertime = FLASHTICS<<2;
+	bcolor = FLASHCOLOR;
+	VW_ColorBorder (FLASHCOLOR);
+
+	DisplaySMsg("Damaging blows!", NULL);
+	status_flag  = S_NONE;
+	status_delay = 80;
+
+	if (gamestate.body<MAXBODY/3)
+		SD_PlaySound (TAKEDMGHURTSND);
+	else
+		SD_PlaySound (TAKEDAMAGESND);
+
+	gamestate.body -= points;
+}
+
+/*
+=============================================================================
+
+							INTERACTION
+
+=============================================================================
+*/
+
+
+#if 0
+/*
+==================
+=
+= OpenDoor
+=
+==================
+*/
+
+void OpenDoor (unsigned bx, unsigned by, unsigned doorbase)
+{
+	int x,y;
+	unsigned	far *map;
+
+	x=bx;
+	y=by;
+	map = mapsegs[0]+farmapylookup[y]+x;
+	while (tilemap[x][y]-doorbase<4)
+	{
+		tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;
+		map--;
+		x--;
+	}
+	x=bx+1;
+	map = mapsegs[0]+farmapylookup[y]+x;
+	while (tilemap[x][y]-doorbase<4)
+	{
+		tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;
+		map++;
+		x++;
+	}
+	x=bx;
+	y=by-1;
+	map = mapsegs[0]+farmapylookup[y]+x;
+	while (tilemap[x][y]-doorbase<4)
+	{
+		tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;
+		map-=mapwidth;
+		y--;
+	}
+	y=by+1;
+	map = mapsegs[0]+farmapylookup[y]+x;
+	while (tilemap[x][y]-doorbase<4)
+	{
+		tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;
+		map+=mapwidth;
+		y++;
+	}
+}
+#endif
+
+#if 0
+/*
+==================
+=
+= RemoveWalls - similar to OpenDoor(), but on a different plane
+=
+==================
+*/
+void RemoveWalls (unsigned bx, unsigned by, unsigned remove_code)
+{
+	int x,y;
+	unsigned	far *map,*p2;
+
+	x=bx;
+	y=by;
+	p2 = *(mapsegs[2]+farmapylookup[y]+x);
+	map = mapsegs[0]+farmapylookup[y]+x;
+	while (*p2 == remove_code)
+	{
+		tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;
+		map--;
+		p2--;
+		x--;
+	}
+	x=bx+1;
+	p2 = *(mapsegs[2]+farmapylookup[y]+x);
+	map = mapsegs[0]+farmapylookup[y]+x;
+	while (*p2 == remove_code)
+	{
+		tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;
+		map++;
+		p2++;
+		x++;
+	}
+	x=bx;
+	y=by-1;
+	p2 = *(mapsegs[2]+farmapylookup[y]+x);
+	map = mapsegs[0]+farmapylookup[y]+x;
+	while (*p2 == remove_code)
+	{
+		tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;
+		map-=mapwidth;
+		p2 -= mapwidth;
+		y--;
+	}
+	y=by+1;
+	p2 = *(mapsegs[2]+farmapylookup[y]+x);
+	map = mapsegs[0]+farmapylookup[y]+x;
+	while (*p2 == remove_code)
+	{
+		tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;
+		map+=mapwidth;
+		p2 += mapwidth;
+		y++;
+	}
+}
+#endif
+
+/*
+==================
+=
+= HitSpecialTile
+=
+= Returns true if the move is blocked
+=
+==================
+*/
+
+boolean HitSpecialTile (unsigned x, unsigned y, unsigned tile)
+{
+	objtype *check;
+	short keyspot;
+	unsigned	temp,spot,curmap=gamestate.mapon,newlevel;
+	char *key_colors[] = {"a RED key",
+					"a YELLOW key",
+					"a GREEN key",
+					"a BLUE key"};
+
+	switch (tile)
+	{
+		case 44:
+			playstate = ex_victorious;
+		break;
+
+		case 17:
+		case 30:
+		case 31:
+		case 35:
+		case 46:
+		case 47:
+		case 48:
+		case 49:
+		case 57:
+		case 58:
+		case 71:
+		case 85:
+		case 94:
+
+			if (!playstate && !FreezeTime)
+			{
+
+			// Is this an openable door? (Is "openable" a word?)
+			//
+				spot = (*(mapsegs[2]+farmapylookup[y]+x)) >> 8;
+				if (spot == CANT_OPEN_CODE)	// CAN'T EVER OPEN (it's just for looks)
+				{
+					CenterWindow(30,4);
+					US_CPrint("\nThis door is permanently blocked");
+					VW_UpdateScreen();
+					IN_ClearKeysDown();
+					IN_Ack();
+					return;
+				}
+
+				// make sure player has key to get into door
+				//
+
+				if (TILE_FLAGS(tile) & tf_EMBEDDED_KEY_COLOR)
+					keyspot = GATE_KEY_COLOR(tile);
+				else
+					keyspot = (*(mapsegs[2]+farmapylookup[y+1]+x)) >> 8;
+
+				if (keyspot--)
+					if (!gamestate.keys[keyspot])
+					{
+						SD_PlaySound(HIT_GATESND);
+						CenterWindow(20,5);
+						US_CPrint("\nYou need\n");
+						US_CPrint(key_colors[keyspot]);
+						VW_UpdateScreen();
+						IN_ClearKeysDown();
+						IN_Ack();
+						return;
+					}
+
+			//
+			// deal with this gate (warp? simply open? whatever...)
+			//
+				switch (spot)
+				{
+					case NEXT_LEVEL_CODE:		// WARP TO NEXT LEVEL
+						newlevel = gamestate.mapon+1;
+						playstate = ex_warped;
+					break;
+
+					case REMOVE_DOOR_CODE:		// REMOVE DOOR
+						(unsigned)actorat[x][y] = tilemap[x][y] =	*(mapsegs[0]+farmapylookup[y]+x) = 0;
+						*(mapsegs[2]+farmapylookup[y+1]+x) = 0;	// key no longer needed
+						if (keyspot>=0)
+							TakeKey(keyspot);
+					break;
+
+					default:			// WARP TO A LEVEL
+						newlevel = spot;
+						playstate = ex_warped;
+					break;
+				}
+
+				if (playstate == ex_warped)
+				{
+					SD_PlaySound(HIT_GATESND);
+//					levelinfo *li=&gamestate.levels[curmap];
+
+//					OldAngle = FaceDoor(x,y);
+
+					if (!VerifyGateExit())
+					{
+						IN_ClearKeysDown ();
+						playstate = ex_stillplaying;
+						break;
+					}
+
+//					FaceAngle(OldAngle);
+
+					if (keyspot>=0)
+						TakeKey(keyspot);
+					*(mapsegs[2]+farmapylookup[y+1]+x) = 0;	// key no longer needed
+
+					gamestate.mapon = newlevel;
+					SD_PlaySound(WARPUPSND);
+					IN_ClearKeysDown ();
+
+//					li->x = player->tilex;
+//					li->y = player->tiley;
+//					li->angle = player->angle+180;
+//					if (li->angle > 360)
+//						li->angle -= 360;
+				}
+			}
+		break;
+	}
+
+	return true;
+}
+
+//-------------------------------------------------------------------------
+// VerifyGateExit()
+//-------------------------------------------------------------------------
+boolean VerifyGateExit()
+{
+	char choices[] = {sc_Escape,sc_Y,sc_N,0},ch;
+
+	ch=DisplayMsg("Pass this way?      Y/N",choices);
+	DrawText(true);
+
+	return(ch == sc_Y);
+}
+
+
+/*
+==================
+=
+= TouchActor
+=
+= Returns true if the move is blocked
+=
+==================
+*/
+
+boolean TouchActor (objtype *ob, objtype *check)
+{
+	if (ob->xh < check->xl || ob->xl > check->xh ||
+		ob->yh < check->yl || ob->yl > check->yh)
+		return false;				// not quite touching
+
+	switch (check->obclass)
+	{
+		case bonusobj:
+			switch (check->temp1)
+			{
+				case B_BOLT:		GiveBolt ();		break;
+
+				case B_NUKE:		GiveNuke ();		break;
+
+				case B_POTION:		GivePotion ();		break;
+
+//				case B_RKEY2:		GiveKey(B_RKEY-B_RKEY);					break;
+
+				case B_RKEY:
+				case B_YKEY:
+				case B_GKEY:
+				case B_BKEY:		GiveKey (check->temp1-B_RKEY);		break;
+
+#if 0
+				case B_SCROLL1:
+				case B_SCROLL2:
+				case B_SCROLL3:
+				case B_SCROLL4:
+				case B_SCROLL5:
+				case B_SCROLL6:
+				case B_SCROLL7:
+				case B_SCROLL8:	GiveScroll (check->temp1-B_SCROLL1,true);	break;
+#endif
+
+				case B_OLDCHEST:
+				case B_CHEST:		GiveChest (); 		break;
+
+				case B_RGEM:
+				case B_YGEM:
+				case B_GGEM:
+				case B_BGEM:
+				case B_PGEM:
+					SD_PlaySound(GETGEMSND);
+					gamestate.gems[check->temp1-B_RGEM] = GEM_DELAY_TIME;
+					redraw_gems = true;
+				break;
+
+				default:
+					Quit("TouchActor(): INVALID BONUS");
+				break;
+			}
+
+			(unsigned)actorat[check->tilex][check->tiley] = 0;
+			RemoveObj (check);
+
+			return false;
+
+		case freezeobj:
+			StopTime();
+			(unsigned)actorat[check->tilex][check->tiley] = 0;
+			RemoveObj(check);
+			return(false);
+	}
+
+	return	true;
+}
+
+
+/*
+==================
+=
+= CalcBounds
+=
+==================
+*/
+
+void CalcBounds (objtype *ob)
+{
+//
+// calculate hit rect
+//
+  ob->xl = ob->x - ob->size;
+  ob->xh = ob->x + ob->size;
+  ob->yl = ob->y - ob->size;
+  ob->yh = ob->y + ob->size;
+}
+
+
+/*
+===================
+=
+= LocationInActor
+=
+===================
+*/
+
+boolean LocationInActor (objtype *ob)
+{
+	int	x,y,xmin,ymin,xmax,ymax;
+	objtype *check;
+
+	CalcBounds (ob);
+
+	xmin = (ob->x >> TILESHIFT)-2;
+	ymin = (ob->y >> TILESHIFT)-2;
+	xmax = xmin+5;
+	ymax = ymin+5;
+
+	for (x=xmin;x<xmax;x++)
+		for (y=ymin;y<ymax;y++)
+		{
+			check = actorat[x][y];
+			if (check>(objtype *)LASTTILE
+				&& (check->flags & of_shootable)
+				&&	(check->obclass != bonusobj)
+				&& (check->obclass != freezeobj)
+				&& (check->obclass != solidobj)
+				&& ob->xl-SIZE_TEST <= check->xh
+				&& ob->xh+SIZE_TEST >= check->xl
+				&& ob->yl-SIZE_TEST <= check->yh
+				&& ob->yh+SIZE_TEST >= check->yl)
+					return true;
+		}
+
+	return false;
+}
+
+/*
+===================
+=
+= ClipXMove
+=
+= Only checks corners, so the object better be less than one tile wide!
+=
+===================
+*/
+void ClipXMove (objtype *ob, long xmove)
+{
+	int			xl,yl,xh,yh,tx,ty,nt1,nt2,x,y;
+	long		intersect,basex,basey,pointx,pointy;
+	unsigned	inside,total,tile;
+	objtype		*check;
+	boolean		moveok;
+	boolean		invisible_present = false;
+
+//
+// move player and check to see if any corners are in solid tiles
+//
+	basex = ob->x;
+	basey = ob->y;
+
+	ob->x += xmove;
+
+	CalcBounds (ob);
+
+	xl = ob->xl>>TILESHIFT;
+	yl = ob->yl>>TILESHIFT;
+
+	xh = ob->xh>>TILESHIFT;
+	yh = ob->yh>>TILESHIFT;
+
+	for (y=yl;y<=yh;y++)
+		for (x=xl;x<=xh;x++)
+		{
+			check = actorat[x][y];
+
+			if (!check)
+				continue;		// blank floor, walk ok
+
+			if ((unsigned)check <= LASTTILE)
+			{
+				if (TILE_FLAGS((unsigned)check) & tf_SPECIAL)
+				{
+					HitSpecialTile(x,y,(unsigned)check-SPECTILESTART);
+					goto blockmove;
+				}
+
+				if (TILE_FLAGS((unsigned)check) & tf_INVISIBLE_WALL)
+				{
+					invisible_present = true;
+					goto blockmove;
+				}
+
+
+				if (TILE_FLAGS((unsigned)check) & tf_SOLID)
+				{
+					goto blockmove;			// solid wall
+				}
+			}
+
+			TouchActor(ob,check);		// pick up items
+		}
+
+//
+// check nearby actors
+//
+	if (LocationInActor(ob))
+	{
+		ob->x -= xmove;
+		if (LocationInActor(ob))
+		{
+			ob->x += xmove;
+			if (LocationInActor(ob))
+				ob->x -= xmove;
+		}
+	}
+	return;		// move is OK!
+
+
+blockmove:
+
+//	if (!SD_SoundPlaying())
+//		SD_PlaySound (HITWALLSND);
+
+	moveok = false;
+
+	do
+	{
+		xmove /= 2;
+		if (moveok)
+		{
+			ob->x += xmove;
+		}
+		else
+		{
+			ob->x -= xmove;
+		}
+		CalcBounds (ob);
+		xl = ob->xl>>TILESHIFT;
+		yl = ob->yl>>TILESHIFT;
+		xh = ob->xh>>TILESHIFT;
+		yh = ob->yh>>TILESHIFT;
+		if (tilemap[xl][yl] || tilemap[xh][yl]
+		|| tilemap[xh][yh] || tilemap[xl][yh] )
+		{
+			moveok = false;
+			if (xmove>=-2048 && xmove <=2048)
+			{
+				ob->x = basex;
+				ob->y = basey;
+				return;
+			}
+		}
+		else
+			if (invisible_present)
+			{
+				moveok = false;
+				if (xmove>=-2048 && xmove <=2048)
+				{
+					ob->x = basex;
+					ob->y = basey;
+					return;
+				}
+			}
+			else
+				if (xmove>=-2048 && xmove <=2048)
+					return;
+				moveok = true;
+	} while (1);
+}
+
+
+/*
+===================
+=
+= ClipYMove
+=
+= Only checks corners, so the object better be less than one tile wide!
+=
+===================
+*/
+void ClipYMove (objtype *ob, long ymove)
+{
+	int			xl,yl,xh,yh,tx,ty,nt1,nt2,x,y;
+	long		intersect,basex,basey,pointx,pointy;
+	unsigned	inside,total,tile;
+	objtype		*check;
+	boolean		moveok;
+	boolean		invisible_present = false;
+
+//
+// move player and check to see if any corners are in solid tiles
+//
+	basex = ob->x;
+	basey = ob->y;
+
+	ob->y += ymove;
+
+	CalcBounds (ob);
+
+	xl = ob->xl>>TILESHIFT;
+	yl = ob->yl>>TILESHIFT;
+
+	xh = ob->xh>>TILESHIFT;
+	yh = ob->yh>>TILESHIFT;
+
+	for (y=yl;y<=yh;y++)
+		for (x=xl;x<=xh;x++)
+		{
+			check = actorat[x][y];
+			if (!check)
+				continue;		// blank floor, walk ok
+
+			if ((unsigned)check <= LASTTILE)
+			{
+				if (TILE_FLAGS((unsigned)check) & tf_SPECIAL)		// <=LASTSPECIALTILE)
+				{
+					HitSpecialTile (x,y,(unsigned)check-SPECTILESTART);
+					goto blockmove;
+				}
+
+				if (TILE_FLAGS((unsigned)check) & tf_INVISIBLE_WALL)
+				{
+					invisible_present = true;
+					goto blockmove;
+				}
+
+
+				if (TILE_FLAGS((unsigned)check) & tf_SOLID)		// LASTWALLTILE)
+				{
+					goto blockmove;	// solid wall
+				}
+			}
+
+			TouchActor(ob,check);		// pick up items
+		}
+
+//
+// check nearby actors
+//
+	if (LocationInActor(ob))
+	{
+		if (LocationInActor(ob))
+		{
+			ob->y -= ymove;
+		}
+	}
+	return;		// move is OK!
+
+
+blockmove:
+
+//	if (!SD_SoundPlaying())
+//		SD_PlaySound (HITWALLSND);
+
+	moveok = false;
+
+	do
+	{
+		ymove /= 2;
+		if (moveok)
+		{
+			ob->y += ymove;
+		}
+		else
+		{
+			ob->y -= ymove;
+		}
+		CalcBounds (ob);
+		xl = ob->xl>>TILESHIFT;
+		yl = ob->yl>>TILESHIFT;
+		xh = ob->xh>>TILESHIFT;
+		yh = ob->yh>>TILESHIFT;
+		if (tilemap[xl][yl] || tilemap[xh][yl]
+		|| tilemap[xh][yh] || tilemap[xl][yh] )
+		{
+			moveok = false;
+			if (ymove>=-2048 && ymove <=2048)
+			{
+				ob->x = basex;
+				ob->y = basey;
+				return;
+			}
+		}
+		else
+			if (invisible_present)
+			{
+				moveok = false;
+				if (ymove>=-2048 && ymove <=2048)
+				{
+					ob->x = basex;
+					ob->y = basey;
+					return;
+				}
+			}
+			else
+				if (ymove>=-2048 && ymove <=2048)
+					return;
+				moveok = true;
+	} while (1);
+}
+
+
+//==========================================================================
+
+
+/*
+===================
+=
+= ShotClipMove
+=
+= Only checks corners, so the object better be less than one tile wide!
+=
+===================
+*/
+
+boolean ShotClipMove (objtype *ob, long xmove, long ymove)
+{
+	int			xl,yl,xh,yh,tx,ty,nt1,nt2,x,y;
+	long		intersect,basex,basey,pointx,pointy;
+	unsigned	inside,total,spot,tile;
+	objtype		*check;
+	boolean		moveok;
+
+//
+// move shot and check to see if any corners are in solid tiles
+//
+	basex = ob->x;
+	basey = ob->y;
+
+	ob->x += xmove;
+	ob->y += ymove;
+
+	CalcBounds (ob);
+
+	xl = ob->xl>>TILESHIFT;
+	yl = ob->yl>>TILESHIFT;
+
+	xh = ob->xh>>TILESHIFT;
+	yh = ob->yh>>TILESHIFT;
+
+	for (y=yl;y<=yh;y++)
+		for (x=xl;x<=xh;x++)
+		{
+			spot = (*(mapsegs[2]+farmapylookup[y]+x)) >> 8;
+			if (spot == EXP_WALL_CODE)
+				switch (ob->obclass)
+				{
+					case pshotobj:
+					case bigpshotobj:
+						ExplodeWall (x,y);
+						goto blockmove;
+//					break;
+				}
+
+			tile = *(mapsegs[0]+farmapylookup[y]+x);
+			if (TILE_FLAGS(tile) & tf_SOLID)
+				goto blockmove;
+		}
+	return false;		// move is OK!
+
+
+blockmove:
+
+	if (ob->obclass == pshotobj)
+		SD_PlaySound (SHOOTWALLSND);
+
+	moveok = false;
+
+	do
+	{
+		xmove /= 2;
+		ymove /= 2;
+		if (moveok)
+		{
+			ob->x += xmove;
+			ob->y += ymove;
+		}
+		else
+		{
+			ob->x -= xmove;
+			ob->y -= ymove;
+		}
+		CalcBounds (ob);
+		xl = ob->xl>>TILESHIFT;
+		yl = ob->yl>>TILESHIFT;
+		xh = ob->xh>>TILESHIFT;
+		yh = ob->yh>>TILESHIFT;
+		if (tilemap[xl][yl] || tilemap[xh][yl]
+		|| tilemap[xh][yh] || tilemap[xl][yh] )
+		{
+			moveok = false;
+			if (xmove>=-2048 && xmove <=2048 && ymove>=-2048 && ymove <=2048)
+			{
+				ob->x = basex;
+				ob->y = basey;
+				return true;
+			}
+		}
+		else
+		{
+			if (xmove>=-2048 && xmove <=2048 && ymove>=-2048 && ymove <=2048)
+				return true;
+			moveok = true;
+		}
+	} while (1);
+}
+
+
+
+/*
+=============================================================================
+
+							PLAYER CONTROL
+
+=============================================================================
+*/
+
+
+
+void	T_Player (objtype *ob);
+
+statetype s_player = {0,0,&T_Player,&s_player};
+
+/*
+===============
+=
+= SpawnPlayer
+=
+===============
+*/
+
+void SpawnPlayer (int tilex, int tiley, int dir)
+{
+#if 0
+	levelinfo *li=&gamestate.levels[gamestate.mapon];
+
+	if (li->x != -1)
+	{
+		tilex = li->x;
+		tiley = li->y;
+		player->angle = li->angle;
+	}
+	else
+		player->angle = (1-dir)*90;
+#endif
+
+	player->obclass = playerobj;
+	player->active = always;
+	player->tilex = tilex;
+	player->tiley = tiley;
+	player->x = ((long)tilex<<TILESHIFT)+TILEGLOBAL/2;
+	player->y = ((long)tiley<<TILESHIFT)+TILEGLOBAL/2;
+	player->state = &s_player;
+	player->size = MINDIST;
+	CalcBounds(player);
+	player->angle = (1-dir)*90;
+	if (player->angle<0)
+		player->angle += ANGLES;
+}
+
+
+/*
+===================
+=
+= Thrust
+=
+===================
+*/
+
+void Thrust (int angle, unsigned speed)
+{
+	long xmove,ymove;
+
+	if (lasttimecount>>5 != ((lasttimecount-tics)>>5) )
+	{
+	//
+	// walk sound
+	//
+		if (lasttimecount&32)
+			SD_PlaySound (WALK1SND);
+		else
+			SD_PlaySound (WALK2SND);
+	}
+
+	xmove = FixedByFrac(speed,costable[angle]);
+	ymove = -FixedByFrac(speed,sintable[angle]);
+
+	ClipXMove(player,xmove);
+	ClipYMove(player,ymove);
+	player->tilex = player->x >> TILESHIFT;
+	player->tiley = player->y >> TILESHIFT;
+}
+
+
+
+/*
+=======================
+=
+= ControlMovement
+=
+=======================
+*/
+
+void ControlMovement (objtype *ob)
+{
+	int	angle;
+	long	speed;
+
+
+	if (control.button1)
+	{
+	//
+	// strafing
+	//
+		//
+		// side to side move
+		//
+		if (!mousexmove)
+			speed = 0;
+		else if (mousexmove<0)
+			speed = -(long)mousexmove*300;
+		else
+			speed = -(long)mousexmove*300;
+
+		if (control.xaxis == -1)
+		{
+			speed += PLAYERSPEED*tics;
+		}
+		else if (control.xaxis == 1)
+		{
+			speed -= PLAYERSPEED*tics;
+		}
+
+		if (speed > 0)
+		{
+			if (speed >= TILEGLOBAL)
+				speed = TILEGLOBAL-1;
+			angle = ob->angle + ANGLES/4;
+			if (angle >= ANGLES)
+				angle -= ANGLES;
+			Thrust (angle,speed);				// move to left
+		}
+		else if (speed < 0)
+		{
+			if (speed <= -TILEGLOBAL)
+				speed = -TILEGLOBAL+1;
+			angle = ob->angle - ANGLES/4;
+			if (angle < 0)
+				angle += ANGLES;
+			Thrust (angle,-speed);				// move to right
+		}
+	}
+	else
+	{
+	//
+	// not strafing
+	//
+
+		//
+		// turning
+		//
+		if (control.xaxis == 1)
+		{
+			ob->angle -= tics;
+			if (running)				// fast turn
+				ob->angle -= (tics<<1);
+		}
+		else if (control.xaxis == -1)
+		{
+			ob->angle+= tics;
+			if (running)				// fast turn
+				ob->angle += (tics<<1);
+		}
+
+		ob->angle -= (mousexmove/10);
+
+		if (ob->angle >= ANGLES)
+			ob->angle -= ANGLES;
+		if (ob->angle < 0)
+			ob->angle += ANGLES;
+
+	}
+
+	//
+	// forward/backwards move
+	//
+	if (!mouseymove)
+		speed = 0;
+	else if (mouseymove<0)
+		speed = -(long)mouseymove*500;
+	else
+		speed = -(long)mouseymove*200;
+
+	if (control.yaxis == -1)
+	{
+		speed += PLAYERSPEED*tics;
+	}
+	else if (control.yaxis == 1)
+	{
+		speed -= PLAYERSPEED*tics;
+	}
+
+	if (speed > 0)
+	{
+		if (speed >= TILEGLOBAL)
+			speed = TILEGLOBAL-1;
+		Thrust (ob->angle,speed);			// move forwards
+	}
+	else if (speed < 0)
+	{
+		if (speed <= -TILEGLOBAL)
+			speed = -TILEGLOBAL+1;
+		angle = ob->angle + ANGLES/2;
+		if (angle >= ANGLES)
+			angle -= ANGLES;
+		Thrust (angle,-speed);				// move backwards
+	}
+}
+
+
+/*
+===============
+=
+= T_Player
+=
+===============
+*/
+
+void	T_Player (objtype *ob)
+{
+//	extern boolean autofire;
+
+	int	angle,speed,scroll,loop;
+	unsigned	text,tilex,tiley;
+	long	lspeed;
+
+//	boolean radar_moved=false;
+
+
+	ControlMovement (ob);
+
+
+	//
+	// firing
+	//
+	if (boltsleft)
+	{
+		handheight+=(realtics<<2);
+		if (handheight>MAXHANDHEIGHT)
+			handheight = MAXHANDHEIGHT;
+
+		ContinueBolt ();
+		lasthand = lasttimecount;
+	}
+	else
+	{
+		if (control.button0)
+		{
+			handheight+=(realtics<<2);
+			if (handheight>MAXHANDHEIGHT)
+				handheight = MAXHANDHEIGHT;
+			lasthand = lasttimecount;
+
+			if (!button0down)
+				Shoot();
+
+//			if (!autofire)
+				button0down=true;
+		}
+		else
+		{
+			if (lasttimecount > lasthand+HANDPAUSE)
+			{
+				handheight-=(realtics<<1);
+				if (handheight<0)
+					handheight = 0;
+			}
+
+			button0down = false;
+		}
+}
+
+#if 0
+		if (control.button0)
+		{
+			handheight+=(realtics<<2);
+			if (handheight>MAXHANDHEIGHT)
+				handheight = MAXHANDHEIGHT;
+
+			if ((unsigned)TimeCount/FIRETIME != lastfiretime)
+				BuildShotPower ();
+			lasthand = lasttimecount;
+		}
+		else
+		{
+			if (lasttimecount > lasthand+HANDPAUSE)
+			{
+				handheight-=(realtics<<1);
+				if (handheight<0)
+					handheight = 0;
+			}
+
+			if (gamestate.shotpower)
+			{
+				lastfiretime = (unsigned)TimeCount/FIRETIME;
+				Shoot ();
+			}
+		}
+	}
+#endif
+
+	//
+	// special actions
+	//
+
+	if ((Keyboard[sc_Space] || Keyboard[sc_C]) && gamestate.body != MAXBODY)
+		DrinkPotion ();
+
+	if (Keyboard[sc_Z] && !boltsleft)
+		CastBolt ();
+
+	if ( (Keyboard[sc_Enter] || Keyboard[sc_X]) && ((TimeCount-lastnuke > NUKETIME))) //|| (autofire)))
+		CastNuke ();
+
+#if 0
+	scroll = LastScan-2;
+	if ( scroll>=0 && scroll<NUMSCROLLS && gamestate.scrolls[scroll])
+		ReadScroll (scroll);
+#endif
+
+	DrawText(false);
+	DrawHealth();
+	if (FreezeTime)
+		DrawFreezeTime();
+	DrawRadar();
+	EGAWRITEMODE(0);
+	DrawNSEWIcons();
+
+	if (redraw_gems)
+		DrawGems();
+
+#if 0
+// gems fade out over time...
+//
+	for (loop=0; loop<5; loop++)
+		if (gamestate.gems[loop])
+		{
+			gamestate.gems[loop] -= realtics;
+			if (gamestate.gems[loop] < 0)
+			{
+				gamestate.gems[loop] = 0;
+				redraw_gems = true;
+			}
+		}
+#endif
+}
+
+#if 0
+//------------------------------------------------------------------------
+// FaceDir() -
+//
+// PARAMS : x,y - pixle coords to bring in to view.
+//
+// NOTE : Params CAN NOT be shifted fracs!
+//------------------------------------------------------------------------
+void FaceDir(short x,short y,boolean StopTime)
+{
+	short diff;
+
+	RotateAngle = CalcAngle(x-(player->x>>16l),(player->y>>16l)-y);
+	FreezeTime = StopTime;
+
+	diff = player->angle - RotateAngle;
+
+	if (((diff>0) && (diff<180)) || ((diff<0) && (diff>-180)))
+		RotateSpeed = -ROTATE_SPEED;
+	else
+		RotateSpeed = ROTATE_SPEED;
+}
+#endif
+
+#if 0
+//------------------------------------------------------------------------
+// CalcAngle() -
+//
+// DESC: Calculates the angle from a given dy & dx
+//------------------------------------------------------------------------
+short CalcAngle(short dx,short dy)
+{
+	#define degtorad				(180/PI)
+	float angle;
+	short diff;
+	float rad_angle;
+
+	if (dx)
+	{
+		angle = atan((float)dy/dx)* degtorad;
+		if (angle<=0)
+			angle += 180;
+		if (dy>0)
+			angle += 180;
+	}
+	else
+	{
+		// 90 Deg shift
+
+		if (dy < 0)
+			angle = 0 + 90; 			// Above player (NORTH)
+		else
+			angle = 180 + 90;			// Below player (SOUTH)
+	}
+
+	if (!angle)				// HACK
+		angle++;
+
+	return((short)abs(angle));
+}
+
+#endif
+
+#if 0
+
+//-------------------------------------------------------------------------
+// RotateView() -
+//
+// DESC : Rotates view (current view of game) to a dest angle.
+//-------------------------------------------------------------------------
+void RotateView()
+{
+	short LastPos;
+
+	// Store old angle position then change angle...
+	//
+
+	LastPos = player->angle;
+
+	player->angle += RotateSpeed;
+
+	// Check to see if we cranked past out dest angle...
+	//
+
+
+	if ((player->angle>ANGLES) || (!player->angle))
+		player->angle = 1;
+	else
+	if (player->angle<1)
+		player->angle = ANGLES;
+
+	// Check to see if we over shot our dest angle...
+	//
+
+	if (((LastPos < RotateAngle) && (player->angle > RotateAngle) && (RotateSpeed > 0)) ||
+		((LastPos > RotateAngle) && (player->angle < RotateAngle) && (RotateSpeed < 0)))
+		player->angle = RotateAngle;
+
+	// Check for ending force turn....
+	//
+
+	if (player->angle == RotateAngle)
+		RotateAngle = -1;
+
+}
+
+
+//--------------------------------------------------------------------------
+// InitRotate()
+//--------------------------------------------------------------------------
+void InitRotate(short DestAngle)
+{
+	if (player->angle != DestAngle)
+	{
+		RotateAngle = DestAngle;
+
+		if (player->angle > DestAngle)
+			RotateSpeed = -ROTATE_SPEED;
+		else
+			RotateSpeed = ROTATE_SPEED;
+
+		if (abs(player->angle - RotateAngle) > 180)
+			RotateSpeed =- RotateSpeed;
+	}
+}
+
+
+
+//------------------------------------------------------------------------
+// FaceAngle() -
+//
+// PARAMS : DestAngle - Destination angle to turn to
+//------------------------------------------------------------------------
+void FaceAngle(short DestAngle)
+{
+	signed long dx,dy,radius,psin,pcos,newx,newy;
+	int		give;
+	short objnum,LastPos;
+	signed long ox,oy,xl,xh,yl,yh,px,py,norm_dx,norm_dy;
+	short o_radius;
+	void (*think)();
+
+
+	// Calculate the direction we want to turn to...
+	//
+
+	InitRotate(DestAngle);
+
+	RedrawStatusWindow();
+
+	while (RotateAngle != -1)
+	{
+
+		RotateView();
+
+//		PollControls();
+
+		objnum=0;
+
+		for (obj = player;obj;obj = obj->next)
+		{
+			if (obj->active >= yes)
+			{
+
+			// keep a list of objects around the player for radar updates
+			//
+				if (obj == player)
+				{
+					px = player->x;
+					py = player->y;
+					psin = sintable[player->angle];
+					pcos = costable[player->angle];
+					xl = px-((long)RADAR_WIDTH<<TILESHIFT)/2;
+					xh = px+((long)RADAR_WIDTH<<TILESHIFT)/2-1;
+					yl = py-((long)RADAR_HEIGHT<<TILESHIFT)/2;
+					yh = py+((long)RADAR_HEIGHT<<TILESHIFT)/2;
+				}
+
+				if (objnum > MAX_RADAR_BLIPS-2)
+					objnum = MAX_RADAR_BLIPS-2;
+
+				ox = obj->x;
+				oy = obj->y;
+
+
+				if ((ox >= xl) && (ox <= xh) && (oy >= yl) && (oy <= yh))
+				{
+					norm_dx = (dx = px-ox)>>TILESHIFT;
+					norm_dy = (dy = oy-py)>>TILESHIFT;
+
+					o_radius = IntSqrt((norm_dx * norm_dx) + (norm_dy * norm_dy));
+
+					if (o_radius < RADAR_RADIUS)
+					{
+						newx = FixedByFrac(dy,pcos)-FixedByFrac(dx,psin);
+						newy = FixedByFrac(dy,psin)+FixedByFrac(dx,pcos);
+
+						RadarXY[objnum][0]=newx>>TILESHIFT;
+						RadarXY[objnum][1]=newy>>TILESHIFT;
+
+						// Define color to use for this object...
+						//
+
+						switch (obj->obclass)
+						{
+							case playerobj:
+								RadarXY[objnum++][2]=15;
+							break;
+
+						// RED GEM
+						//
+							// STOMPY										(DK RED)
+							//
+							case invisdudeobj:
+							case stompyobj:
+								RadarXY[objnum++][2]=4;
+							break;
+
+							// BLOB											(LT RED)
+							//
+							case blobobj:
+								RadarXY[objnum++][2]=12;
+							break;
+
+						// BLUE GEM
+						//
+							// ROBOTANK										(LT BLUE)
+							//
+							case robotankobj:
+							case fmageobj:
+								RadarXY[objnum++][2]=9;
+							break;
+
+#if 1
+							// BLUE DEMON									(DK BLUE)
+							//
+							case demonobj:
+								RadarXY[objnum++][2]=1;
+							break;
+#endif
+
+						// GREEN GEM
+						//
+							// WIZARD										(LT GREEN)
+							//
+							case wizardobj:
+								RadarXY[objnum++][2]=10;
+							break;
+
+							// AQUA MAN										(DK GREEN)
+							//
+							case aquamanobj:
+								RadarXY[objnum++][2]=2;
+							break;
+
+						// YELLOW GEM
+						//
+							// EQYPTIAN HEAD								(BROWN)
+							//
+							case headobj:
+								RadarXY[objnum++][2]=6;
+							break;
+
+							//	RAMBONE										(YELLOW)
+							//	TROLL
+							case ramboneobj:
+							case trollobj:
+								RadarXY[objnum++][2]=14;
+							break;
+
+							//	BUG											(LIGHT GRAY)
+							case bugobj:
+								RadarXY[objnum++][2]=7;
+							break;
+
+							//	RAY											(DARK GRAY)
+							case rayobj:
+								RadarXY[objnum++][2]=8;
+							break;
+
+						// PURPLE GEM
+						//
+							// MEC DEMON									(PURPLE)
+							//
+							case cyborgdemonobj:
+								RadarXY[objnum++][2]=5;
+							break;
+
+							// EYE											(LT PURPLE)
+							//
+							case eyeobj:
+							case reyeobj:
+								RadarXY[objnum++][2]=13;
+							break;
+						}
+					}
+				}
+			}
+		}
+
+		RadarXY[objnum][2]=-1;		// Signals end of RadarXY list...
+
+// refresh all
+//
+
+		ThreeDRefresh();
+		DrawRadar();
+		EGAWRITEMODE(0);
+		DrawNSEWIcons();
+
+//		CheckKeys();
+	}
+}
+
+
+//-------------------------------------------------------------------------
+// FaceDoor() - Turns the player to face a door (a tile) at a given TILE x & y
+//
+// RETURNS : Returns the orginal angle of the player.
+//------------------------------------------------------------------------
+short FaceDoor(short x, short y)
+{
+	short p_x,p_y,angle,old_angle;
+
+	old_angle = player->angle;
+
+	p_x = player->x>>16l;
+	p_y = player->y>>16l;
+
+	if (p_x != x)
+	{
+		if (p_x > x)
+			angle = 180;		// Face Left
+		else
+			angle = 1;			// Face Right
+	}
+
+	if (p_y != y)
+	{
+		if (p_y > y)
+			angle = 90;			// Face Up
+		else
+			angle = 270;		// Face Down
+	}
+
+	FaceAngle(angle);
+
+	return(old_angle);
+}
+
+
+#endif
+
+
+
+/*==========================================================================
+
+								EXPLOSION SPAWNING ROUTINES
+
+===========================================================================*/
+
+statetype s_explode = {0,1,T_ExpThink,&s_explode};
+
+//-------------------------------------------------------------------------
+// SpawnExplosion()
+//------------------------------------------------------------------------
+void SpawnExplosion(fixed x, fixed y, short Delay)
+{
+	DSpawnNewObjFrac(x,y,&s_explode,PIXRADIUS*7);
+	new->obclass = expobj;
+	new->active = always;
+	new->temp1 = Delay;
+}
+
+
+//---------------------------------------------------------------------------
+// T_ExpThink()
+//---------------------------------------------------------------------------
+void T_ExpThink(objtype *obj)
+{
+	if (obj->temp1)
+	{
+		if ((obj->temp1-=realtics) <= 0)
+			obj->temp1 = 0;
+	}
+	else
+	{
+		obj->state = &s_pshot_exp1;
+		obj->ticcount = obj->state->tictime;
+		SD_PlaySound(BOOMSND);
+	}
+}
+
+
+
+//-------------------------------------------------------------------------
+// SpawnBigExplosion()
+//------------------------------------------------------------------------
+void SpawnBigExplosion(fixed x, fixed y, short Delay, fixed Range)
+{
+	SpawnExplosion(x-random(Range),y+random(Range),random(Delay));
+	SpawnExplosion(x+random(Range),y-random(Range),random(Delay));
+	SpawnExplosion(x-random(Range),y-random(Range),random(Delay));
+	SpawnExplosion(x+random(Range),y+random(Range),random(Delay));
+}
+
diff --git a/src/lib/hb/demokd.c b/src/lib/hb/demokd.c
new file mode 100755
index 00000000..1b19e369
--- /dev/null
+++ b/src/lib/hb/demokd.c
@@ -0,0 +1,126 @@
+/*
+=====================
+=
+= DemoLoop
+=
+=====================
+*/
+
+void
+DemoLoop (void)
+{
+	char		*s;
+	word		move;
+	longword	lasttime;
+	char *FileName1;
+	struct Shape FileShape1;
+#if CREDITS
+	char *FileName2;
+	struct Shape FileShape2;
+#endif
+	struct ffblk ffblk;
+	WindowRec	mywin;
+	int bufsave	= bufferofs;
+	int dissave	= displayofs;
+
+
+#if FRILLS
+//
+// check for launch from ted
+//
+	if (tedlevel)
+	{
+		NewGame();
+		gamestate.mapon = tedlevelnum;
+		GameLoop();
+		TEDDeath();
+	}
+#endif
+
+//
+// demo loop
+//
+	US_SetLoadSaveHooks(LoadGame,SaveGame,ResetGame);
+	restartgame = gd_Continue;
+
+	if (findfirst("KDREAMS.CMP", &ffblk, 0) == -1)
+		Quit("Couldn't find KDREAMS.CMP");
+
+	while (true)
+	{
+
+		loadedgame = false;
+
+		FileName1 = "TITLESCR.LBM";
+		if (LoadLIBShape("KDREAMS.CMP", FileName1, &FileShape1))
+			Quit("Can't load TITLE SCREEN");
+#if CREDITS
+		FileName2 = "CREDITS.LBM";
+		if (LoadLIBShape("KDREAMS.CMP", FileName2, &FileShape2))
+			Quit("Can't load CREDITS SCREEN");
+#endif
+
+		while (!restartgame && !loadedgame)
+		{
+
+			VW_InitDoubleBuffer();
+			IN_ClearKeysDown();
+
+			while (true)
+			{
+
+				VW_SetScreen(0, 0);
+				MoveGfxDst(0, 200);
+				UnpackEGAShapeToScreen(&FileShape1, 0, 0);
+				VW_ScreenToScreen (64*200,0,40,200);
+
+#if CREDITS
+				if (IN_UserInput(TickBase * 8, false))
+					break;
+#else
+				if (IN_UserInput(TickBase * 4, false))
+					break;
+#endif
+
+#if CREDITS
+				MoveGfxDst(0, 200);
+				UnpackEGAShapeToScreen(&FileShape2, 0, 0);
+				VW_ScreenToScreen (64*200,0,40,200);
+
+				if (IN_UserInput(TickBase * 7, false))
+					break;
+#else
+				MoveGfxDst(0, 200);
+				UnpackEGAShapeToScreen(&FileShape1, 0, 0);
+				VW_ScreenToScreen (64*200,0,40,200);
+
+				if (IN_UserInput(TickBase * 3, false))
+					break;
+#endif
+
+				displayofs = 0;
+				VWB_Bar(0,0,320,200,FIRSTCOLOR);
+				US_DisplayHighScores(-1);
+
+				if (IN_UserInput(TickBase * 6, false))
+					break;
+
+			}
+
+			bufferofs = bufsave;
+			displayofs = dissave;
+
+			VW_FixRefreshBuffer();
+			US_ControlPanel ();
+		}
+
+		if (!loadedgame)
+			NewGame();
+
+		FreeShape(&FileShape1);
+#if CREDITS
+		FreeShape(&FileShape2);
+#endif
+		GameLoop();
+	}
+}
diff --git a/src/lib/hb/demowl.c b/src/lib/hb/demowl.c
new file mode 100755
index 00000000..85958eff
--- /dev/null
+++ b/src/lib/hb/demowl.c
@@ -0,0 +1,170 @@
+/*
+=====================
+=
+= DemoLoop
+=
+=====================
+*/
+
+static  char *ParmStrings[] = {"baby","easy","normal","hard",""};
+
+void    DemoLoop (void)
+{
+	static int LastDemo;
+	int     i,level;
+	long nsize;
+	memptr	nullblock;
+
+//
+// check for launch from ted
+//
+	if (tedlevel)
+	{
+		NoWait = true;
+		NewGame(1,0);
+
+		for (i = 1;i < _argc;i++)
+		{
+			if ( (level = US_CheckParm(_argv[i],ParmStrings)) != -1)
+			{
+			 gamestate.difficulty=level;
+			 break;
+			}
+		}
+
+#ifndef SPEAR
+		gamestate.episode = tedlevelnum/10;
+		gamestate.mapon = tedlevelnum%10;
+#else
+		gamestate.episode = 0;
+		gamestate.mapon = tedlevelnum;
+#endif
+		GameLoop();
+		Quit (NULL);
+	}
+
+
+//
+// main game cycle
+//
+
+
+//	nsize = (long)40*1024;
+//	MM_GetPtr(&nullblock,nsize);
+
+#ifndef DEMOTEST
+
+	#ifndef UPLOAD
+
+		#ifndef GOODTIMES
+		#ifndef SPEAR
+		#ifndef JAPAN
+		if (!NoWait)
+			NonShareware();
+		#endif
+		#else
+
+			#ifndef GOODTIMES
+			#ifndef SPEARDEMO
+			CopyProtection();
+			#endif
+			#endif
+
+		#endif
+		#endif
+	#endif
+
+	StartCPMusic(INTROSONG);
+
+#ifndef JAPAN
+	if (!NoWait)
+		PG13 ();
+#endif
+
+#endif
+
+	while (1)
+	{
+		while (!NoWait)
+		{
+//
+// title page
+//
+			MM_SortMem ();
+#ifndef DEMOTEST
+
+#ifdef SPEAR
+			CA_CacheGrChunk (TITLEPALETTE);
+
+			CA_CacheGrChunk (TITLE1PIC);
+			VWB_DrawPic (0,0,TITLE1PIC);
+			UNCACHEGRCHUNK (TITLE1PIC);
+
+			CA_CacheGrChunk (TITLE2PIC);
+			VWB_DrawPic (0,80,TITLE2PIC);
+			UNCACHEGRCHUNK (TITLE2PIC);
+			VW_UpdateScreen ();
+			VL_FadeIn(0,255,grsegs[TITLEPALETTE],30);
+
+			UNCACHEGRCHUNK (TITLEPALETTE);
+#else
+			CA_CacheScreen (TITLEPIC);
+			VW_UpdateScreen ();
+			VW_FadeIn();
+#endif
+			if (IN_UserInput(TickBase*15))
+				break;
+			VW_FadeOut();
+//
+// credits page
+//
+			CA_CacheScreen (CREDITSPIC);
+			VW_UpdateScreen();
+			VW_FadeIn ();
+			if (IN_UserInput(TickBase*10))
+				break;
+			VW_FadeOut ();
+//
+// high scores
+//
+			DrawHighScores ();
+			VW_UpdateScreen ();
+			VW_FadeIn ();
+
+			if (IN_UserInput(TickBase*10))
+				break;
+#endif
+//
+// demo
+//
+
+			#ifndef SPEARDEMO
+			PlayDemo (LastDemo++%4);
+			#else
+			PlayDemo (0);
+			#endif
+
+			if (playstate == ex_abort)
+				break;
+			StartCPMusic(INTROSONG);
+		}
+
+		VW_FadeOut ();
+
+#ifndef SPEAR
+		if (Keyboard[sc_Tab] && MS_CheckParm("goobers"))
+#else
+		if (Keyboard[sc_Tab] && MS_CheckParm("debugmode"))
+#endif
+			RecordDemo ();
+		else
+			US_ControlPanel (0);
+
+		if (startgame || loadedgame)
+		{
+			GameLoop ();
+			VW_FadeOut();
+			StartCPMusic(INTROSONG);
+		}
+	}
+}
diff --git a/src/lib/hb/kd_act1.c b/src/lib/hb/kd_act1.c
new file mode 100755
index 00000000..d2cf917c
--- /dev/null
+++ b/src/lib/hb/kd_act1.c
@@ -0,0 +1,1130 @@
+/* Keen Dreams Source Code
+ * Copyright (C) 2014 Javier M. Chavez
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// KD_ACT1.C
+#include "KD_DEF.H"
+#pragma hdrstop
+
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+
+#define PLACESPRITE RF_PlaceSprite (&ob->sprite,ob->x,ob->y,ob->shapenum, \
+	spritedraw,0);
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+int	flowertime[4] = {700,700,350,175};
+
+/*
+=============================================================================
+
+						MISC ACTOR STUFF
+
+=============================================================================
+*/
+
+/*
+==================
+=
+= DoGravity
+=
+= Changes speed and location
+=
+==================
+*/
+
+
+void DoGravity (objtype *ob)
+{
+	long	i;
+//
+// only accelerate on odd tics, because of limited precision
+//
+	for (i=lasttimecount-tics;i<lasttimecount;i++)
+	{
+		if (i&1)
+		{
+			if (ob->yspeed < 0 && ob->yspeed >= -ACCGRAVITY)
+			{
+			// stop at apex of jump
+				ob->ymove += ob->yspeed;
+				ob->yspeed = 0;
+				return;
+			}
+			ob->yspeed+=ACCGRAVITY;
+			if (ob->yspeed>SPDMAXY)
+			  ob->yspeed=SPDMAXY;
+		}
+		ob->ymove+=ob->yspeed;
+	}
+}
+
+
+/*
+===============
+=
+= AccelerateX
+=
+===============
+*/
+
+void AccelerateX (objtype *ob,int dir,int max)
+{
+	long	i;
+	unsigned	olddir;
+
+	olddir = ob->xspeed & 0x8000;
+//
+// only accelerate on odd tics, because of limited precision
+//
+	for (i=lasttimecount-tics;i<lasttimecount;i++)
+	{
+		if (i&1)
+		{
+			ob->xspeed+=dir;
+			if ( (ob->xspeed & 0x8000) != olddir)
+			{
+				olddir = ob->xspeed & 0x8000;
+				ob->xdir = olddir ? -1 : 1;
+			}
+			if (ob->xspeed>max)
+			  ob->xspeed=max;
+			else if (ob->xspeed<-max)
+			  ob->xspeed=-max;
+		}
+		ob->xmove+=ob->xspeed;
+	}
+}
+
+
+/*
+===============
+=
+= FrictionX
+=
+===============
+*/
+
+void FrictionX (objtype *ob)
+{
+	long	i;
+	int		dir;
+	unsigned	olddir;
+
+	olddir = ob->xspeed & 0x8000;
+
+	if (ob->xspeed > 0)
+		dir = -1;
+	else if (ob->xspeed < 0)
+		dir = 1;
+	else
+		dir = 0;
+//
+// only accelerate on odd tics, because of limited precision
+//
+	for (i=lasttimecount-tics;i<lasttimecount;i++)
+	{
+		if (i&1)
+		{
+			ob->xspeed+=dir;
+			if ( (ob->xspeed & 0x8000) != olddir)
+				ob->xspeed = 0;
+		}
+		ob->xmove+=ob->xspeed;
+	}
+}
+
+
+/*
+===============
+=
+= ProjectileThink
+=
+===============
+*/
+
+void ProjectileThink (objtype *ob)
+{
+	DoGravity (ob);
+	ob->xmove = tics*ob->xspeed;
+}
+
+/*
+===============
+=
+= VelocityThink
+=
+===============
+*/
+
+void VelocityThink (objtype *ob)
+{
+	ob->xmove = tics*ob->xspeed;
+	ob->ymove = tics*ob->yspeed;
+}
+
+/*
+===============
+=
+= DrawReact
+=
+===============
+*/
+
+void DrawReact (objtype *ob)
+{
+	RF_PlaceSprite (&ob->sprite,ob->x,ob->y,ob->shapenum,spritedraw,1);
+}
+
+void DrawReact2 (objtype *ob)
+{
+	RF_PlaceSprite (&ob->sprite,ob->x,ob->y,ob->shapenum,spritedraw,2);
+}
+
+void DrawReact3 (objtype *ob)
+{
+	RF_PlaceSprite (&ob->sprite,ob->x,ob->y,ob->shapenum,spritedraw,3);
+}
+
+/*
+==================
+=
+= ChangeState
+=
+==================
+*/
+
+void ChangeState (objtype *ob, statetype *state)
+{
+	ob->state = state;
+	ob->ticcount = 0;
+	if (state->rightshapenum)
+	{
+		if (ob->xdir>0)
+			ob->shapenum = state->rightshapenum;
+		else
+			ob->shapenum = state->leftshapenum;
+	}
+
+	ob->xmove = 0;
+	ob->ymove = 0;
+
+	ob->needtoreact = true;			// it will need to be redrawn this frame
+	ClipToWalls (ob);
+}
+
+
+/*
+====================
+=
+= WalkReact
+=
+====================
+*/
+
+void WalkReact (objtype *ob)
+{
+	if (ob->xdir == 1 && ob->hitwest)
+	{
+		ob->x -= ob->xmove;
+		ob->xdir = -1;
+		ob->nothink = US_RndT()>>5;
+		ChangeState (ob,ob->state);
+	}
+	else if (ob->xdir == -1 && ob->hiteast)
+	{
+		ob->x -= ob->xmove;
+		ob->xdir = 1;
+		ob->nothink = US_RndT()>>5;
+		ChangeState (ob,ob->state);
+	}
+	else if (!ob->hitnorth)
+	{
+		ob->x -= 2*ob->xmove;
+		ob->y -= ob->ymove;
+
+		ob->xdir = -ob->xdir;
+		ob->nothink = US_RndT()>>5;
+		ChangeState (ob,ob->state);
+	}
+
+	PLACESPRITE;
+}
+
+
+/*
+=============================================================================
+
+							DOOR
+
+=============================================================================
+*/
+
+void	DoorContact (objtype *ob, objtype *hit);
+
+extern	statetype s_door;
+extern	statetype s_doorraise;
+
+statetype s_door  	 = {DOORSPR,DOORSPR,think,false,
+	false,0, 0,0, NULL, DoorContact, DrawReact, &s_door};
+statetype s_doorraise = {DOORSPR,DOORSPR,slide,false,
+	false,24, 0,32, NULL, DoorContact, DrawReact, NULL};
+
+/*
+======================
+=
+= SpawnDoor
+=
+======================
+*/
+
+void	SpawnDoor (int tilex, int tiley)
+{
+	GetNewObj (false);
+
+	new->obclass = doorobj;
+	new->x = tilex<<G_T_SHIFT;
+	new->y = (tiley<<G_T_SHIFT)-2*BLOCKSIZE;
+	new->xdir = 1;
+	new->ydir = -1;
+	new->needtoclip = false;
+	NewState (new,&s_door);
+}
+
+/*
+======================
+=
+= DoorContact
+=
+======================
+*/
+
+void	DoorContact (objtype *ob, objtype *hit)
+{
+	ClipToSpriteSide (hit,ob);
+}
+
+/*
+=============================================================================
+
+							FLOWER
+
+temp1 = original class
+temp2 = original state
+temp3 = flower count
+
+=============================================================================
+*/
+
+void ChangeToFlower (objtype *ob);
+void FlowerThink (objtype *ob);
+void ChangeFromFlower (objtype *ob);
+
+extern	statetype s_flower1;
+extern	statetype s_flower2;
+extern	statetype s_flower3;
+extern	statetype s_flower4;
+extern	statetype s_flower5;
+extern	statetype s_flower6;
+
+extern	statetype s_poofto1;
+extern	statetype s_poofto2;
+extern	statetype s_poofto3;
+extern	statetype s_poofto4;
+
+extern	statetype s_pooffrom1;
+extern	statetype s_pooffrom2;
+extern	statetype s_pooffrom3;
+extern	statetype s_pooffrom4;
+extern	statetype s_pooffrom5;
+extern	statetype s_pooffrom6;
+extern	statetype s_pooffrom7;
+
+extern	statetype s_bonus1;
+
+#pragma warn -sus
+
+statetype s_flower1  	 = {FLOWER1SPR,FLOWER1SPR,stepthink,false,
+	false,20, 0,0, FlowerThink, NULL, DrawReact, &s_flower2};
+statetype s_flower2  	 = {FLOWER2SPR,FLOWER2SPR,stepthink,false,
+	false,20, 0,0, FlowerThink, NULL, DrawReact, &s_flower3};
+statetype s_flower3  	 = {FLOWER3SPR,FLOWER3SPR,stepthink,false,
+	false,20, 0,0, FlowerThink, NULL, DrawReact, &s_flower4};
+statetype s_flower4  	 = {FLOWER4SPR,FLOWER4SPR,stepthink,false,
+	false,20, 0,0, FlowerThink, NULL, DrawReact, &s_flower5};
+statetype s_flower5  	 = {FLOWER3SPR,FLOWER3SPR,stepthink,false,
+	false,20, 0,0, FlowerThink, NULL, DrawReact, &s_flower6};
+statetype s_flower6  	 = {FLOWER2SPR,FLOWER2SPR,stepthink,false,
+	false,20, 0,0, FlowerThink, NULL, DrawReact, &s_flower1};
+
+statetype s_poofto1	  	 = {POOF1SPR,POOF1SPR,step,false,
+	false,10, 0,0, NULL, NULL, DrawReact2, &s_poofto2};
+statetype s_poofto2	  	 = {POOF2SPR,POOF2SPR,step,false,
+	false,10, 0,0, NULL, NULL, DrawReact2, &s_poofto3};
+statetype s_poofto3	  	 = {POOF3SPR,POOF3SPR,step,false,
+	false,10, 0,0, NULL, NULL, DrawReact2, &s_poofto4};
+statetype s_poofto4	  	 = {POOF4SPR,POOF4SPR,step,false,
+	false,10, 0,0, NULL, NULL, DrawReact2, NULL};
+
+statetype s_pooffrom1  	 = {POOF4SPR,POOF4SPR,step,false,
+	false,10, 0,0, NULL, NULL, DrawReact2, &s_pooffrom2};
+statetype s_pooffrom2  	 = {POOF3SPR,POOF3SPR,step,false,
+	false,10, 0,0, NULL, NULL, DrawReact2, &s_pooffrom3};
+statetype s_pooffrom3  	 = {POOF2SPR,POOF2SPR,step,false,
+	false,10, 0,0, NULL, NULL, DrawReact2, &s_pooffrom4};
+statetype s_pooffrom4  	 = {POOF1SPR,POOF1SPR,step,false,
+	false,20, 0,0, ChangeFromFlower, NULL, DrawReact2, &s_pooffrom5};
+statetype s_pooffrom5  	 = {POOF2SPR,POOF2SPR,step,false,
+	false,10, 0,0, NULL, NULL, DrawReact2, &s_pooffrom6};
+statetype s_pooffrom6  	 = {POOF3SPR,POOF3SPR,step,false,
+	false,10, 0,0, NULL, NULL, DrawReact2, &s_pooffrom7};
+statetype s_pooffrom7  	 = {POOF4SPR,POOF4SPR,step,false,
+	false,10, 0,0, NULL, NULL, DrawReact2, NULL};
+
+
+#pragma warn +sus
+
+
+/*
+======================
+=
+= ChangeToFlower
+=
+======================
+*/
+
+void ChangeToFlower (objtype *ob)
+{
+	SD_PlaySound (FLOWERPOWERSND);
+	ob->y = ob->bottom-TILEGLOBAL*2;
+	ob->temp1 = (int)ob->obclass;
+	ob->temp2 = (int)ob->state;
+	ob->temp3 = 0;
+	ob->needtoclip = true;
+	ob->obclass = inertobj;
+	ob->xspeed = 0;
+	ChangeState (ob,&s_flower1);
+	ob->active = allways;			// flower never deactivated
+	GetNewObj (true);
+	new->x = ob->x;
+	new->y = ob->y;
+	NewState (new,&s_poofto1);
+	new->active = removable;
+}
+
+
+/*
+======================
+=
+= FlowerThink
+=
+======================
+*/
+
+void FlowerThink (objtype *ob)
+{
+	ProjectileThink (ob);
+	if ( (ob->temp3+=tics) >= flowertime[gamestate.difficulty])
+	{
+		GetNewObj (true);
+		new->active = allways;
+		new->temp1 = (int)ob;
+		new->x = ob->x;
+		new->y = ob->y;
+		NewState (new,&s_pooffrom1);
+		ob->temp3 = 0;
+	}
+}
+
+
+/*
+======================
+=
+= ChangeFromFlower
+=
+======================
+*/
+
+void ChangeFromFlower (objtype *ob)
+{
+	objtype *flower;
+	statetype *state;
+	unsigned	oldbottom;
+
+	SD_PlaySound (UNFLOWERPOWERSND);
+	flower = (objtype *)ob->temp1;
+
+	oldbottom = flower->bottom;
+	ChangeState (flower,(statetype *)flower->temp2);
+	flower->y += oldbottom - flower->bottom;
+
+	flower->obclass = flower->temp1;
+	flower->active = yes;			// allow it to unspawn now if off screen
+}
+
+
+/*
+=============================================================================
+
+							BONUS
+
+temp1 = bonus type
+temp2 = base shape number
+temp3 = last animated shape number +1
+
+=============================================================================
+*/
+
+void BonusThink (objtype *ob);
+
+extern	statetype s_bonus1;
+
+#pragma warn -sus
+
+statetype s_bonus  	 = {NULL,NULL,step,false,
+	false,20, 0,0, BonusThink, NULL, DrawReact2, &s_bonus};
+
+statetype s_bonusrise  	 = {NULL,NULL,slide,false,
+	false,40, 0,8, NULL, NULL, DrawReact3, NULL};
+
+#pragma warn +sus
+
+/*
+====================
+=
+= SpawnBonus
+=
+====================
+*/
+
+int bonusshape[12] = {PEPPERMINT1SPR,COOKIE1SPR,CANDYCANE1SPR,CANDYBAR1SPR,
+	LOLLIPOP1SPR,COTTONCANDY1SPR,EXTRAKEEN1SPR,SUPERBONUS1SPR,FLOWERPOWER1SPR,
+	FLOWERPOWERUP1SPR,BOOBUSBOMB1SPR,MAGICKEY1SPR};
+
+void SpawnBonus (int tilex, int tiley, int type)
+{
+	GetNewObj (false);
+
+	new->needtoclip = false;
+	new->obclass = bonusobj;
+	new->x = tilex<<G_T_SHIFT;
+	new->y = tiley<<G_T_SHIFT;
+
+	if (type == 9)
+		new->y -= 8*PIXGLOBAL;	// flower power up one block
+
+	new->ydir = -1;			// bonus stuff flies up when touched
+
+	new->temp1 = type;
+	new->temp2 = new->shapenum = bonusshape[type];
+	if (type != 7)
+		new->temp3 = new->temp2 + 2;
+	else
+		new->temp3 = new->temp2 + 4;	// super bonus is 4 stage animation
+
+	NewState (new,&s_bonus);
+}
+
+/*
+====================
+=
+= BonusThink
+=
+====================
+*/
+
+void BonusThink (objtype *ob)
+{
+	if (++ob->shapenum == ob->temp3)
+		ob->shapenum = ob->temp2;
+}
+
+
+
+/*
+=============================================================================
+
+						BROCCOLASH
+
+=============================================================================
+*/
+
+void BroccoThink (objtype *ob);
+void BroccoGetUp (objtype *ob);
+
+extern	statetype s_broccowalk1;
+extern	statetype s_broccowalk2;
+extern	statetype s_broccowalk3;
+extern	statetype s_broccowalk4;
+
+extern	statetype s_broccosmash1;
+extern	statetype s_broccosmash2;
+extern	statetype s_broccosmash3;
+extern	statetype s_broccosmash4;
+extern	statetype s_broccosmash5;
+extern	statetype s_broccosmash6;
+extern	statetype s_broccosmash7;
+extern	statetype s_broccosmash8;
+extern	statetype s_broccosmash9;
+
+#pragma warn -sus
+
+statetype s_broccowalk1	 = {BROCCOLASHRUNL1SPR,BROCCOLASHRUNR1SPR,step,false,
+	true,7, 128,0, BroccoThink, NULL, WalkReact, &s_broccowalk2};
+statetype s_broccowalk2	 = {BROCCOLASHRUNL2SPR,BROCCOLASHRUNR2SPR,step,false,
+	true,7, 128,0, BroccoThink, NULL, WalkReact, &s_broccowalk3};
+statetype s_broccowalk3	 = {BROCCOLASHRUNL3SPR,BROCCOLASHRUNR3SPR,step,false,
+	true,7, 128,0, BroccoThink, NULL, WalkReact, &s_broccowalk4};
+statetype s_broccowalk4	 = {BROCCOLASHRUNL4SPR,BROCCOLASHRUNR4SPR,step,false,
+	true,7, 128,0, BroccoThink, NULL, WalkReact, &s_broccowalk1};
+
+statetype s_broccosmash1 = {BROCCOLASHSMASHL1SPR,BROCCOLASHSMASHR1SPR,step,true,
+	false,3, 0,0, NULL, NULL, DrawReact, &s_broccosmash2};
+statetype s_broccosmash2 = {BROCCOLASHSMASHL2SPR,BROCCOLASHSMASHR2SPR,step,true,
+	false,3, 0,0, NULL, NULL, DrawReact, &s_broccosmash3};
+statetype s_broccosmash3 = {BROCCOLASHSMASHL3SPR,BROCCOLASHSMASHR3SPR,step,true,
+	false,3, 0,0, NULL, NULL, DrawReact, &s_broccosmash4};
+statetype s_broccosmash4 = {BROCCOLASHSMASHL4SPR,BROCCOLASHSMASHR4SPR,step,false,
+	false,7, 0,0, NULL, NULL, DrawReact, &s_broccosmash5};
+statetype s_broccosmash5 = {BROCCOLASHSMASHL3SPR,BROCCOLASHSMASHR3SPR,step,true,
+	false,6, 0,0, NULL, NULL, DrawReact, &s_broccosmash6};
+statetype s_broccosmash6 = {BROCCOLASHSMASHL2SPR,BROCCOLASHSMASHR2SPR,step,true,
+	false,6, 0,0, NULL, NULL, DrawReact, &s_broccosmash7};
+statetype s_broccosmash7 = {BROCCOLASHSMASHL1SPR,BROCCOLASHSMASHR1SPR,step,true,
+	false,6, 0,0, NULL, NULL, DrawReact, &s_broccosmash8};
+statetype s_broccosmash8 = {BROCCOLASHRUNL1SPR,BROCCOLASHRUNR1SPR,step,true,
+	false,6, 0,0, BroccoGetUp, NULL, DrawReact, &s_broccosmash9};
+statetype s_broccosmash9 = {BROCCOLASHRUNL1SPR,BROCCOLASHRUNR1SPR,step,true,
+	false,6, 128,0, NULL, NULL, WalkReact, &s_broccowalk1};
+
+#pragma warn +sus
+
+/*
+====================
+=
+= SpawnBrocco
+=
+====================
+*/
+
+void SpawnBrocco (int tilex, int tiley)
+{
+	GetNewObj (false);
+
+	new->obclass = broccoobj;
+	new->x = tilex<<G_T_SHIFT;
+	new->y = (tiley<<G_T_SHIFT)-2*BLOCKSIZE;
+	new->xdir = 1;
+	NewState (new,&s_broccowalk1);
+}
+
+/*
+====================
+=
+= BroccoGetUp
+=
+====================
+*/
+
+void BroccoGetUp (objtype *ob)
+{
+	ob->needtoclip = true;
+}
+
+
+/*
+====================
+=
+= BroccoThink
+=
+====================
+*/
+
+void BroccoThink (objtype *ob)
+{
+	int delta;
+
+	if (ob->top > player->bottom || ob->bottom < player->top)
+		return;
+
+	delta = player->x - ob->x;
+
+	if ( ob->xdir == -1 )
+	{
+		if (delta < -3*TILEGLOBAL)
+			return;
+		if (delta > TILEGLOBAL/2)
+		{
+			ob->xdir = 1;
+			return;
+		}
+		ob->state = &s_broccosmash1;
+		ob->needtoclip = false;
+		ob->xmove = 0;
+		return;
+	}
+	else
+	{
+		delta = player->left - ob->right;
+		if (delta > 3*TILEGLOBAL)
+			return;
+		if (delta < -TILEGLOBAL/2)
+		{
+			ob->xdir = -1;
+			return;
+		}
+		ob->state = &s_broccosmash1;
+		ob->needtoclip = false;
+		ob->xmove = 0;
+		return;
+	}
+}
+
+
+/*
+=============================================================================
+
+						 TOMATOOTH
+
+ob->temp1 = jumptime
+
+=============================================================================
+*/
+
+#define	SPDTOMATBOUNCE	30
+#define TICTOMATJUMP	10
+#define SPDTOMAT		16
+
+void TomatBounceThink (objtype *ob);
+void TomatReact (objtype *ob);
+
+extern	statetype s_tomatbounce;
+extern	statetype s_tomatbounce2;
+
+#pragma warn -sus
+
+statetype s_tomatbounce	 = {TOMATOOTHL1SPR,TOMATOOTHR1SPR,stepthink,false,
+	false,20, 0,0, TomatBounceThink, NULL, TomatReact, &s_tomatbounce2};
+statetype s_tomatbounce2 = {TOMATOOTHL2SPR,TOMATOOTHR2SPR,stepthink,false,
+	false,20, 0,0, TomatBounceThink, NULL, TomatReact, &s_tomatbounce};
+
+#pragma warn +sus
+
+/*
+====================
+=
+= SpawnTomat
+=
+====================
+*/
+
+void SpawnTomat (int tilex, int tiley)
+{
+	GetNewObj (false);
+
+	new->obclass = tomatobj;
+	new->x = tilex<<G_T_SHIFT;
+	new->y = (tiley<<G_T_SHIFT)-1*BLOCKSIZE;
+	new->xdir = 1;
+	NewState (new,&s_tomatbounce);
+}
+
+/*
+====================
+=
+= TomatBounceThink
+=
+====================
+*/
+
+void TomatBounceThink (objtype *ob)
+{
+	AccelerateX (ob,ob->x > player->x ? -1 : 1,SPDTOMAT);
+	if (ob->xspeed > 0)
+		ob->xdir = 1;
+	else
+		ob->xdir = -1;
+
+	if (ob->temp1)
+	{
+		if (ob->temp1<tics)
+		{
+			ob->ymove = ob->yspeed*ob->temp1;
+			ob->temp1 = 0;
+		}
+		else
+		{
+			ob->ymove = ob->yspeed*tics;
+			ob->temp1-=tics;
+		}
+	}
+	else
+		DoGravity(ob);
+
+}
+
+
+/*
+====================
+=
+= TomatReactThink
+=
+====================
+*/
+
+void TomatReact (objtype *ob)
+{
+	if (ob->hiteast || ob->hitwest)
+	{
+		ob->xdir = -ob->xdir;
+		ob->xspeed = -ob->xspeed;
+	}
+
+	if (ob->hitsouth)
+	{
+		if (ob->tileright >= originxtile
+		&& ob->tileleft <= originxtilemax
+		&& ob->tiletop >= originytile
+		&& ob->tilebottom <= originytilemax)
+			SD_PlaySound (BOUNCESND);
+		ob->yspeed = -ob->yspeed;
+	}
+
+	if (ob->hitnorth)
+	{
+		if (ob->tileright >= originxtile
+		&& ob->tileleft <= originxtilemax
+		&& ob->tiletop >= originytile
+		&& ob->tilebottom <= originytilemax)
+			SD_PlaySound (BOUNCESND);
+		ob->yspeed = -SPDTOMATBOUNCE-(US_RndT()>>4);
+		ob->temp1 = TICTOMATJUMP;
+	}
+
+	PLACESPRITE;
+}
+
+
+/*
+=============================================================================
+
+						 CARROT COURIER
+
+=============================================================================
+*/
+
+#define	SPDCARROTLEAPX	32
+#define SPDCARROTLEAPY  40
+
+void CarrotThink (objtype *ob);
+void CarrotReact (objtype *ob);
+void CarrotAirReact (objtype *ob);
+
+extern	statetype s_carrotwalk1;
+extern	statetype s_carrotwalk2;
+extern	statetype s_carrotwalk3;
+extern	statetype s_carrotwalk4;
+
+extern	statetype s_carrotleap;
+
+#pragma warn -sus
+
+statetype s_carrotwalk1	 = {CARROTRUNL1SPR,CARROTRUNR1SPR,step,false,
+	true,5, 128,0, NULL, NULL, CarrotReact, &s_carrotwalk2};
+statetype s_carrotwalk2	 = {CARROTRUNL2SPR,CARROTRUNR2SPR,step,false,
+	true,5, 128,0, NULL, NULL, CarrotReact, &s_carrotwalk3};
+statetype s_carrotwalk3	 = {CARROTRUNL3SPR,CARROTRUNR3SPR,step,false,
+	true,5, 128,0, NULL, NULL, CarrotReact, &s_carrotwalk4};
+statetype s_carrotwalk4	 = {CARROTRUNL4SPR,CARROTRUNR4SPR,step,false,
+	true,5, 128,0, NULL, NULL, CarrotReact, &s_carrotwalk1};
+
+statetype s_carrotleap	 = {CARROTLEAPL1SPR,CARROTLEAPR1SPR,think,false,
+	false,0, 0,0, ProjectileThink, NULL, CarrotAirReact, NULL};
+
+#pragma warn +sus
+
+/*
+====================
+=
+= SpawnCarrot
+=
+====================
+*/
+
+void SpawnCarrot (int tilex, int tiley)
+{
+	GetNewObj (false);
+
+	new->obclass = carrotobj;
+	new->x = tilex<<G_T_SHIFT;
+	new->y = (tiley<<G_T_SHIFT)-2*BLOCKSIZE;
+	new->xdir = 1;
+	new->ydir = 1;
+	NewState (new,&s_carrotwalk1);
+	new->hitnorth = 1;
+}
+
+
+/*
+====================
+=
+= CarrotReact
+=
+====================
+*/
+
+void CarrotReact (objtype *ob)
+{
+	unsigned x, width, bot, far *map;
+
+	if (ob->xdir == 1 && ob->hitwest)
+	{
+		ob->xdir = -1;
+	}
+	else if (ob->xdir == -1 && ob->hiteast)
+	{
+		ob->xdir = 1;
+	}
+	else if (!ob->hitnorth)
+	{
+		ob->x -= ob->xmove;
+		ob->y -= ob->ymove;
+
+		ob->yspeed = -SPDCARROTLEAPY;
+		ob->xspeed = SPDCARROTLEAPX*ob->xdir;
+		ChangeState (ob,&s_carrotleap);
+	}
+
+	PLACESPRITE;
+}
+
+
+/*
+====================
+=
+= CarrotAirReact
+=
+====================
+*/
+
+void CarrotAirReact (objtype *ob)
+{
+	if (ob->hitsouth)
+		ob->yspeed = 0;
+
+	if (ob->hitnorth)
+		ChangeState (ob,&s_carrotwalk1);
+
+	PLACESPRITE;
+}
+
+
+
+/*
+=============================================================================
+
+						   ASPARAGUSTO
+
+=============================================================================
+*/
+
+void AsparThink (objtype *ob);
+
+extern	statetype s_asparwalk1;
+extern	statetype s_asparwalk2;
+extern	statetype s_asparwalk3;
+extern	statetype s_asparwalk4;
+
+#pragma warn -sus
+
+statetype s_asparwalk1	 = {ASPARAGUSRUNL1SPR,ASPARAGUSRUNR1SPR,step,true,
+	true,3, 100,0, NULL, NULL, WalkReact, &s_asparwalk2};
+statetype s_asparwalk2	 = {ASPARAGUSRUNL2SPR,ASPARAGUSRUNR2SPR,step,false,
+	true,3, 100,0, NULL, NULL, WalkReact, &s_asparwalk3};
+statetype s_asparwalk3	 = {ASPARAGUSRUNL3SPR,ASPARAGUSRUNR3SPR,step,true,
+	true,3, 100,0, NULL, NULL, WalkReact, &s_asparwalk4};
+statetype s_asparwalk4	 = {ASPARAGUSRUNL4SPR,ASPARAGUSRUNR4SPR,step,false,
+	true,3, 100,0, NULL, NULL, WalkReact, &s_asparwalk1};
+
+#pragma warn +sus
+
+/*
+====================
+=
+= SpawnAspar
+=
+====================
+*/
+
+void SpawnAspar (int tilex, int tiley)
+{
+	GetNewObj (false);
+
+	new->obclass = asparobj;
+	new->x = tilex<<G_T_SHIFT;
+	new->y = tiley<<G_T_SHIFT;
+	new->xdir = 1;
+	NewState (new,&s_asparwalk1);
+}
+
+
+/*
+=============================================================================
+
+						   SOUR GRAPE
+
+=============================================================================
+*/
+
+void GrapeThink (objtype *ob);
+void GrapeRiseReact (objtype *ob);
+void GrapeFallReact (objtype *ob);
+
+extern	statetype s_grapewait;
+extern	statetype s_grapefall;
+extern	statetype s_grapesit;
+extern	statetype s_graperise;
+
+#pragma warn -sus
+
+statetype s_grapewait	= {GRAPEONVINESPR,GRAPEONVINESPR,think,false,
+	false,0, 0,0, GrapeThink, NULL, DrawReact, NULL};
+
+statetype s_grapefall	= {GRAPEFALLINGSPR,GRAPEFALLINGSPR,think,false,
+	false,0, 0,0, ProjectileThink, NULL, GrapeFallReact, NULL};
+
+statetype s_grapesit	= {GRAPEONVINESPR,GRAPEONVINESPR,step,false,
+	false,30, 0,0, NULL, NULL, DrawReact, &s_graperise};
+statetype s_graperise	= {GRAPEONVINESPR,GRAPEONVINESPR,slide,false,
+	false,0, 0,-16, NULL, NULL, GrapeRiseReact, NULL};
+
+#pragma warn +sus
+
+/*
+====================
+=
+= SpawnGrape
+=
+====================
+*/
+
+void SpawnGrape (int tilex, int tiley)
+{
+	GetNewObj (false);
+
+	new->obclass = grapeobj;
+	new->x = tilex<<G_T_SHIFT;
+	new->y = tiley<<G_T_SHIFT;
+	new->xdir = 1;
+	new->ydir = 1;
+	NewState (new,&s_grapewait);
+}
+
+
+/*
+====================
+=
+= GrapeThink
+=
+====================
+*/
+
+void GrapeThink (objtype *ob)
+{
+	unsigned y,starty,endy, far *map;
+
+	if (player->left > ob->right
+		|| player->right < ob->left
+		|| player->y < ob->y )
+		return;
+
+//
+// see if there are any walls between grape and player
+//
+	starty = ob->tilebottom;
+	endy = player->tiletop;
+
+	map = mapsegs[1] + mapbwidthtable[starty]/2 + ob->tilemidx;
+	for (y = starty ; y<endy ; y++,map+=mapwidth)
+		if (tinf[NORTHWALL+*map])
+			return;
+
+	ob->state = &s_grapefall;
+	SD_PlaySound (GRAPESCREAMSND);
+
+}
+
+
+/*
+====================
+=
+= GrapeRiseReact
+=
+====================
+*/
+
+void GrapeRiseReact (objtype *ob)
+{
+	if (ob->hitsouth)
+		ChangeState(ob,&s_grapewait);
+	PLACESPRITE;
+}
+
+
+/*
+====================
+=
+= GrapeFallReact
+=
+====================
+*/
+
+void GrapeFallReact (objtype *ob)
+{
+	if (ob->hitnorth)
+	{
+		SD_PlaySound (BOUNCESND);
+		ob->yspeed = -(ob->yspeed<<1)/3;
+		if (ob->yspeed > -32)
+			ChangeState(ob,&s_grapesit);
+	}
+	PLACESPRITE;
+}
+
diff --git a/src/lib/hb/kd_act2.c b/src/lib/hb/kd_act2.c
new file mode 100755
index 00000000..e1865404
--- /dev/null
+++ b/src/lib/hb/kd_act2.c
@@ -0,0 +1,1509 @@
+/* Keen Dreams Source Code
+ * Copyright (C) 2014 Javier M. Chavez
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// KD_ACT1.C
+#include "KD_DEF.H"
+#pragma hdrstop
+
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+#define PLACESPRITE RF_PlaceSprite (&ob->sprite,ob->x,ob->y,ob->shapenum, \
+	spritedraw,0);
+
+
+void	ProjectileReact (objtype *ob);
+
+/*
+=============================================================================
+
+						   TATER TROOPER
+
+temp1 = chasing player
+temp2 = nothink time
+
+=============================================================================
+*/
+
+void TaterThink (objtype *ob);
+void BackupReact (objtype *ob);
+
+extern	statetype s_taterwalk1;
+extern	statetype s_taterwalk2;
+extern	statetype s_taterwalk3;
+extern	statetype s_taterwalk4;
+
+
+extern	statetype s_taterattack1;
+extern	statetype s_taterattack2;
+extern	statetype s_taterattack3;
+
+#pragma warn -sus
+
+statetype s_taterwalk1	= {TATERTROOPWALKL1SPR,TATERTROOPWALKR1SPR,step,false,
+	true,10, 128,0, TaterThink, NULL, WalkReact, &s_taterwalk2};
+statetype s_taterwalk2	= {TATERTROOPWALKL2SPR,TATERTROOPWALKR2SPR,step,false,
+	true,10, 128,0, TaterThink, NULL, WalkReact, &s_taterwalk3};
+statetype s_taterwalk3	= {TATERTROOPWALKL3SPR,TATERTROOPWALKR3SPR,step,false,
+	true,10, 128,0, TaterThink, NULL, WalkReact, &s_taterwalk4};
+statetype s_taterwalk4	= {TATERTROOPWALKL4SPR,TATERTROOPWALKR4SPR,step,false,
+	true,10, 128,0, TaterThink, NULL, WalkReact, &s_taterwalk1};
+
+statetype s_taterattack1= {TATERTROOPLUNGEL1SPR,TATERTROOPLUNGER1SPR,step,false,
+	false,12, 0,0, NULL, NULL, BackupReact, &s_taterattack2};
+statetype s_taterattack2= {TATERTROOPLUNGEL2SPR,TATERTROOPLUNGER2SPR,step,false,
+	false,20, 0,0, NULL, NULL, DrawReact, &s_taterattack3};
+statetype s_taterattack3= {TATERTROOPLUNGEL1SPR,TATERTROOPLUNGER1SPR,step,false,
+	false,8, 0,0, NULL, NULL, DrawReact, &s_taterwalk1};
+
+#pragma warn +sus
+
+/*
+====================
+=
+= SpawnTater
+=
+====================
+*/
+
+void SpawnTater (int tilex, int tiley)
+{
+	GetNewObj (false);
+
+	new->obclass = taterobj;
+	new->x = tilex<<G_T_SHIFT;
+	new->y = (tiley<<G_T_SHIFT)-2*BLOCKSIZE + 15;
+	new->xdir = 1;
+	new->ydir = 1;
+	NewState (new,&s_taterwalk1);
+	new->hitnorth = 1;
+}
+
+/*
+====================
+=
+= TaterThink
+=
+====================
+*/
+
+void TaterThink (objtype *ob)
+{
+	int delta;
+
+	if (ob->top > player->bottom || ob->bottom < player->top)
+		return;
+
+	if ( ob->xdir == -1 )
+	{
+		delta = ob->left - player->right;
+		if (delta > TILEGLOBAL)
+			return;
+		if (delta < -8*PIXGLOBAL)
+		{
+			ob->xdir = 1;
+			return;
+		}
+		SD_PlaySound (TATERSWINGSND);
+		ob->state = &s_taterattack1;
+		return;
+	}
+	else
+	{
+		delta = player->left - ob->right;
+		if (delta > TILEGLOBAL)
+			return;
+		if (delta < -8*PIXGLOBAL)
+		{
+			ob->xdir = -1;
+			return;
+		}
+		SD_PlaySound (TATERSWINGSND);
+		ob->state = &s_taterattack1;
+		return;
+	}
+}
+
+
+/*
+====================
+=
+= BackupReact
+=
+====================
+*/
+
+void BackupReact (objtype *ob)
+{
+	if (!ob->hitnorth)
+	{
+		ob->x-=ob->xmove;
+		ob->y-=ob->ymove;
+	}
+
+	PLACESPRITE;
+}
+
+/*
+=============================================================================
+
+						   CANTELOUPE CART
+
+=============================================================================
+*/
+
+extern	statetype s_cartroll1;
+extern	statetype s_cartroll2;
+
+void CartReact (objtype *ob);
+
+#pragma warn -sus
+
+statetype s_cartroll1	= {CANTCARTL1SPR,CANTCARTL1SPR,slide,true,
+	false,5, 32,0, NULL, NULL, CartReact, &s_cartroll2};
+statetype s_cartroll2	= {CANTCARTL2SPR,CANTCARTL2SPR,slide,true,
+	false,5, 32,0, NULL, NULL, CartReact, &s_cartroll1};
+
+#pragma warn +sus
+
+/*
+====================
+=
+= SpawnCart
+=
+====================
+*/
+
+void SpawnCart (int tilex, int tiley)
+{
+	GetNewObj (false);
+
+	new->obclass = cartobj;
+	new->x = tilex<<G_T_SHIFT;
+	new->y = (tiley<<G_T_SHIFT)-3*PIXGLOBAL;
+	new->xdir = 1;
+	new->ydir = 1;
+	new->active = allways;
+	NewState (new,&s_cartroll1);
+}
+
+
+
+/*
+====================
+=
+= CartReact
+=
+====================
+*/
+
+void CartReact (objtype *ob)
+{
+	unsigned far *map;
+
+	if (ob->xdir == 1 && ob->hitwest)
+	{
+		ob->xdir = -1;
+	}
+	else if (ob->xdir == -1 && ob->hiteast)
+	{
+		ob->xdir = 1;
+	}
+
+	map = mapsegs[1] + mapbwidthtable[ob->tilebottom+1]/2;
+	if (ob->xdir == 1)
+		map += ob->tileright;
+	else
+		map += ob->tileleft;
+
+	if ( !tinf[NORTHWALL + *map] )
+		ob->xdir = -ob->xdir;
+
+	PLACESPRITE;
+}
+
+
+/*
+=============================================================================
+
+							FRENCHY
+
+=============================================================================
+*/
+
+#define FRYXSPEED	40
+#define FRYYSPEED	-20
+
+
+void FrenchyThink (objtype *ob);
+void FrenchyRunThink (objtype *ob);
+void FrenchyThrow (objtype *ob);
+
+extern	statetype s_frenchywalk1;
+extern	statetype s_frenchywalk2;
+extern	statetype s_frenchywalk3;
+extern	statetype s_frenchywalk4;
+
+extern	statetype s_frenchyrun1;
+extern	statetype s_frenchyrun2;
+extern	statetype s_frenchyrun3;
+extern	statetype s_frenchyrun4;
+
+extern	statetype s_frenchythrow1;
+extern	statetype s_frenchythrow2;
+extern	statetype s_frenchythrow3;
+
+extern	statetype s_fry1;
+extern	statetype s_fry2;
+
+#pragma warn -sus
+
+statetype s_frenchywalk1	= {FRENCHYRUNL1SPR,FRENCHYRUNR1SPR,step,false,
+	true,10, 128,0, FrenchyThink, NULL, WalkReact, &s_frenchywalk2};
+statetype s_frenchywalk2	= {FRENCHYRUNL2SPR,FRENCHYRUNR2SPR,step,false,
+	true,10, 128,0, FrenchyThink, NULL, WalkReact, &s_frenchywalk3};
+statetype s_frenchywalk3	= {FRENCHYRUNL3SPR,FRENCHYRUNR3SPR,step,false,
+	true,10, 128,0, FrenchyThink, NULL, WalkReact, &s_frenchywalk4};
+statetype s_frenchywalk4	= {FRENCHYRUNL4SPR,FRENCHYRUNR4SPR,step,false,
+	true,10, 128,0, FrenchyThink, NULL, WalkReact, &s_frenchywalk1};
+
+statetype s_frenchyrun1	= {FRENCHYRUNL1SPR,FRENCHYRUNR1SPR,step,true,
+	true,5, 128,0, FrenchyRunThink, NULL, WalkReact, &s_frenchyrun2};
+statetype s_frenchyrun2	= {FRENCHYRUNL2SPR,FRENCHYRUNR2SPR,step,true,
+	true,5, 128,0, FrenchyRunThink, NULL, WalkReact, &s_frenchyrun3};
+statetype s_frenchyrun3	= {FRENCHYRUNL3SPR,FRENCHYRUNR3SPR,step,true,
+	true,5, 128,0, FrenchyRunThink, NULL, WalkReact, &s_frenchyrun4};
+statetype s_frenchyrun4	= {FRENCHYRUNL4SPR,FRENCHYRUNR4SPR,step,true,
+	true,5, 128,0, FrenchyRunThink, NULL, WalkReact, &s_frenchyrun1};
+
+statetype s_frenchythrow1	= {FRENCHYTHROWL1SPR,FRENCHYTHROWR1SPR,step,false,
+	false,10, 0,0, NULL, NULL, DrawReact, &s_frenchythrow2};
+statetype s_frenchythrow2	= {FRENCHYTHROWL2SPR,FRENCHYTHROWR2SPR,step,false,
+	false,1, 0,0, FrenchyThrow, NULL, DrawReact, &s_frenchythrow3};
+statetype s_frenchythrow3	= {FRENCHYTHROWL2SPR,FRENCHYTHROWR2SPR,step,false,
+	false,10, -128,0, NULL, NULL, DrawReact, &s_frenchywalk1};
+
+statetype s_fry1		= {FRENCHFRY1SPR,FRENCHFRY1SPR,stepthink,false,
+	false,4, 0,0, ProjectileThink, NULL, ProjectileReact, &s_fry2};
+statetype s_fry2		= {FRENCHFRY2SPR,FRENCHFRY2SPR,stepthink,false,
+	false,4, 0,0, ProjectileThink, NULL, ProjectileReact, &s_fry1};
+
+
+#pragma warn +sus
+
+
+/*
+====================
+=
+= SpawnFrenchy
+=
+====================
+*/
+
+void SpawnFrenchy (int tilex, int tiley)
+{
+	GetNewObj (false);
+
+	new->obclass = frenchyobj;
+	new->x = tilex<<G_T_SHIFT;
+	new->y = (tiley<<G_T_SHIFT)-2*BLOCKSIZE;
+	new->xdir = 1;
+	new->ydir = 1;
+	NewState (new,&s_frenchywalk1);
+}
+
+
+/*
+====================
+=
+= FrenchyRunThink
+=
+====================
+*/
+
+void FrenchyRunThink (objtype *ob)
+{
+	ob->state = &s_frenchywalk1;
+}
+
+
+/*
+====================
+=
+= FrenchyThrow
+=
+====================
+*/
+
+void FrenchyThrow (objtype *ob)
+{
+	GetNewObj (true);
+	new->obclass = shotobj;
+	if (ob->xdir == 1)
+	{
+		new->x = ob->x+24*16;
+		new->y = ob->y+8*16;
+	}
+	else
+	{
+		new->x = ob->x;
+		new->y = ob->y+8*16;
+	}
+	new->xdir = ob->xdir;
+	new->ydir = 1;
+	new->xspeed = ob->xdir * FRYXSPEED-(US_RndT()>>4);
+	new->yspeed = FRYYSPEED;
+	new->active = removable;
+	NewState (new,&s_fry1);
+
+	ob->nothink = 2;
+}
+
+
+/*
+====================
+=
+= FrenchyThink
+=
+====================
+*/
+
+void FrenchyThink (objtype *ob)
+{
+	int delta;
+
+	if ( abs(ob->y - player->y) > 3*TILEGLOBAL )
+	{
+		if (US_RndT()<8)
+			ob->xdir = -ob->xdir;		// turn randomly
+		return;
+	}
+
+	delta = player->x - ob->x;
+
+	if (delta < -8*TILEGLOBAL)
+	{
+	// walk closer
+		ob->xdir = -1;
+	}
+	if (delta < -4*TILEGLOBAL)
+	{
+	// throw
+		ob->xdir = -1;
+		ob->state = &s_frenchythrow1;
+	}
+	else if (delta < 0)
+	{
+	// run away
+		ob->xdir = 1;
+		ob->state = &s_frenchyrun1;
+		ob->nothink = 8;
+	}
+	else if (delta < 4*TILEGLOBAL)
+	{
+	// run away
+		ob->xdir = -1;
+		ob->state = &s_frenchyrun1;
+		ob->nothink = 8;
+	}
+	else if (delta < 8*TILEGLOBAL)
+	{
+	// throw and walk closer
+		ob->xdir = 1;
+		ob->state = &s_frenchythrow1;
+	}
+	else
+	{
+	// walk closer
+		ob->xdir = 1;
+	}
+}
+
+
+/*
+=============================================================================
+
+						  MELON LIPS
+
+ob->temp1 = direction : 0 = left, 1 = right, 2 = down
+
+=============================================================================
+*/
+
+#define SPITXSPEED	48
+#define SPITYSPEED	-20
+
+void MelonSpitThink (objtype *ob);
+void	ProjectileReact (objtype *ob);
+
+extern	statetype s_melonside;
+extern	statetype s_melonsidespit;
+extern	statetype s_melonsidespit2;
+
+extern	statetype s_melondown;
+extern	statetype s_melondownspit;
+extern	statetype s_melondownspit2;
+
+extern	statetype s_melonseed1;
+extern	statetype s_melonseed2;
+
+extern	statetype s_melonseedd1;
+extern	statetype s_melonseedd2;
+
+#pragma warn -sus
+
+statetype s_melonside	= {MELONLIPSL1SPR,MELONLIPSR1SPR,step,false,
+	false,200, 0,0, NULL, NULL, DrawReact, &s_melonsidespit};
+statetype s_melonsidespit= {MELONLIPSL2SPR,MELONLIPSR2SPR,step,false,
+	false,6, 0,0, MelonSpitThink, NULL, DrawReact, &s_melonsidespit2};
+statetype s_melonsidespit2= {MELONLIPSL2SPR,MELONLIPSR2SPR,step,false,
+	false,6, 0,0, NULL, NULL, DrawReact, &s_melonside};
+
+statetype s_melondown	= {MELONLIPSD1SPR,MELONLIPSD1SPR,step,false,
+	false,200, 0,0, NULL, NULL, DrawReact, &s_melondownspit};
+statetype s_melondownspit	= {MELONLIPSD2SPR,MELONLIPSD2SPR,step,false,
+	false,6, 0,0, MelonSpitThink, NULL, DrawReact, &s_melondownspit2};
+statetype s_melondownspit2	= {MELONLIPSD2SPR,MELONLIPSD2SPR,step,false,
+	false,6, 0,0, NULL, NULL, DrawReact, &s_melondown};
+
+statetype s_melonseed1	= {MELONSEEDL1SPR,MELONSEEDR1SPR,think,false,
+	false,4, 0,0, ProjectileThink, NULL, ProjectileReact, &s_melonseed2};
+statetype s_melonseed2	= {MELONSEEDL2SPR,MELONSEEDR2SPR,think,false,
+	false,4, 0,0, ProjectileThink, NULL, ProjectileReact, &s_melonseed1};
+
+statetype s_melonseedd1	= {MELONSEEDD1SPR,MELONSEEDD1SPR,stepthink,false,
+	false,4, 0,0, ProjectileThink, NULL, ProjectileReact, &s_melonseedd2};
+statetype s_melonseedd2	= {MELONSEEDD2SPR,MELONSEEDD2SPR,stepthink,false,
+	false,4, 0,0, ProjectileThink, NULL, ProjectileReact, &s_melonseedd1};
+
+#pragma warn +sus
+
+/*
+====================
+=
+= SpawnMelon
+=
+====================
+*/
+
+void SpawnMelon (int tilex, int tiley,int dir)
+{
+	GetNewObj (false);
+
+	new->obclass = melonobj;
+	new->x = tilex<<G_T_SHIFT;
+	new->y = tiley<<G_T_SHIFT;
+	if (dir)
+		new->xdir = 1;
+	else
+		new->xdir = -1;
+	if (dir <2)
+		NewState (new,&s_melonside);
+	else
+		NewState (new,&s_melondown);
+
+	new->ticcount = US_RndT()>>1;
+	new->temp1 = dir;
+}
+
+
+/*
+====================
+=
+= MelonSpitThink
+=
+====================
+*/
+
+void MelonSpitThink (objtype *ob)
+{
+	GetNewObj (false);
+	new->obclass = shotobj;
+	switch (ob->temp1)
+	{
+	case 0:
+		new->x = ob->x+24*16;
+		new->y = ob->y+8*16;
+		new->xdir = ob->xdir;
+		new->ydir = 1;
+		new->xspeed = -SPITXSPEED-(US_RndT()>>4);
+		new->yspeed = SPITYSPEED;
+		NewState (new,&s_melonseed1);
+		break;
+	case 1:
+		new->x = ob->x;
+		new->y = ob->y+8*16;
+		new->xdir = ob->xdir;
+		new->ydir = 1;
+		new->xspeed = SPITXSPEED+(US_RndT()>>4);
+		new->yspeed = SPITYSPEED;
+		NewState (new,&s_melonseed1);
+		break;
+	case 2:
+		new->x = ob->x+8*16;
+		new->y = ob->y+24*16;
+		new->ydir = 1;
+		new->yspeed = -SPITYSPEED;
+		NewState (new,&s_melonseedd1);
+		break;
+	}
+
+	new->active = removable;
+}
+
+/*
+============================
+=
+= ProjectileReact
+=
+============================
+*/
+
+void	ProjectileReact (objtype *ob)
+{
+	unsigned wall,absx,absy,angle,newangle;
+	unsigned long speed;
+
+	PLACESPRITE;
+	if (ob->hiteast || ob->hitwest)
+		ob->xspeed= -ob->xspeed/2;
+
+	if (ob->hitsouth)
+		ob->yspeed= -ob->yspeed/2;
+
+	wall = ob->hitnorth;
+	if (wall)
+	{
+		if (ob->yspeed < 0)
+			ob->yspeed = 0;
+
+		absx = abs(ob->xspeed);
+		absy = ob->yspeed;
+		if (absx>absy)
+		{
+			if (absx>absy*2)	// 22 degrees
+			{
+				angle = 0;
+				speed = absx*286;	// x*sqrt(5)/2
+			}
+			else				// 45 degrees
+			{
+				angle = 1;
+				speed = absx*362;	// x*sqrt(2)
+			}
+		}
+		else
+		{
+			if (absy>absx*2)	// 90 degrees
+			{
+				angle = 3;
+				speed = absy*256;
+			}
+			else
+			{
+				angle = 2;		// 67 degrees
+				speed = absy*286;	// y*sqrt(5)/2
+			}
+		}
+		if (ob->xspeed > 0)
+			angle = 7-angle;
+
+		speed >>= 1;
+		newangle = bounceangle[ob->hitnorth][angle];
+		switch (newangle)
+		{
+		case 0:
+			ob->xspeed = speed / 286;
+			ob->yspeed = -ob->xspeed / 2;
+			break;
+		case 1:
+			ob->xspeed = speed / 362;
+			ob->yspeed = -ob->xspeed;
+			break;
+		case 2:
+			ob->yspeed = -(speed / 286);
+			ob->xspeed = -ob->yspeed / 2;
+			break;
+		case 3:
+
+		case 4:
+			ob->xspeed = 0;
+			ob->yspeed = -(speed / 256);
+			break;
+		case 5:
+			ob->yspeed = -(speed / 286);
+			ob->xspeed = ob->yspeed / 2;
+			break;
+		case 6:
+			ob->xspeed = ob->yspeed = -(speed / 362);
+			break;
+		case 7:
+			ob->xspeed = -(speed / 286);
+			ob->yspeed = ob->xspeed / 2;
+			break;
+
+		case 8:
+			ob->xspeed = -(speed / 286);
+			ob->yspeed = -ob->xspeed / 2;
+			break;
+		case 9:
+			ob->xspeed = -(speed / 362);
+			ob->yspeed = -ob->xspeed;
+			break;
+		case 10:
+			ob->yspeed = speed / 286;
+			ob->xspeed = -ob->yspeed / 2;
+			break;
+		case 11:
+
+		case 12:
+			ob->xspeed = 0;
+			ob->yspeed = -(speed / 256);
+			break;
+		case 13:
+			ob->yspeed = speed / 286;
+			ob->xspeed = ob->yspeed / 2;
+			break;
+		case 14:
+			ob->xspeed = speed / 362;
+			ob->yspeed = speed / 362;
+			break;
+		case 15:
+			ob->xspeed = speed / 286;
+			ob->yspeed = ob->xspeed / 2;
+			break;
+		}
+
+		if (speed < 256*16)
+			RemoveObj (ob);
+	}
+}
+
+/*
+=============================================================================
+
+							SQUASHER
+
+=============================================================================
+*/
+
+#define SPDSQUASHLEAPY  50
+
+
+void SquasherThink (objtype *ob);
+void SquasherJumpReact (objtype *ob);
+
+extern	statetype s_squasherwalk1;
+extern	statetype s_squasherwalk2;
+
+extern	statetype s_squasherjump1;
+extern	statetype s_squasherjump2;
+
+extern	statetype s_squasherwait;
+
+#pragma warn -sus
+
+statetype s_squasherwalk1	= {SQUASHERWALKL1SPR,SQUASHERWALKR1SPR,step,false,
+	true,10, 128,0, SquasherThink, NULL, WalkReact, &s_squasherwalk2};
+statetype s_squasherwalk2	= {SQUASHERWALKL2SPR,SQUASHERWALKR2SPR,step,false,
+	true,10, 128,0, SquasherThink, NULL, WalkReact, &s_squasherwalk1};
+
+statetype s_squasherjump1	= {SQUASHERJUMPL1SPR,SQUASHERJUMPR1SPR,stepthink,false,
+	false,20, 0,0, ProjectileThink, NULL, SquasherJumpReact, &s_squasherjump2};
+statetype s_squasherjump2	= {SQUASHERJUMPL2SPR,SQUASHERJUMPR2SPR,think,false,
+	false,0, 0,0, ProjectileThink, NULL, SquasherJumpReact, NULL};
+
+statetype s_squasherwait	= {SQUASHERJUMPL2SPR,SQUASHERJUMPR2SPR,step,false,
+	false,10, 0,0, ProjectileThink, NULL, DrawReact, &s_squasherwalk1};
+
+#pragma warn +sus
+
+/*
+====================
+=
+= SpawnSquasher
+=
+====================
+*/
+
+void SpawnSquasher (int tilex, int tiley)
+{
+	GetNewObj (false);
+
+	new->obclass = squashobj;
+	new->x = tilex<<G_T_SHIFT;
+	new->y = (tiley<<G_T_SHIFT)-2*BLOCKSIZE;
+	new->xdir = 1;
+	new->ydir = 1;
+	NewState (new,&s_squasherwalk1);
+}
+
+/*
+====================
+=
+= SquasherThink
+=
+====================
+*/
+
+void SquasherThink (objtype *ob)
+{
+	int delta;
+
+	if ( abs(ob->y - player->y) > 3*TILEGLOBAL )
+	{
+		if (US_RndT()<8)
+			ob->xdir = -ob->xdir;		// turn randomly
+		return;
+	}
+
+
+	delta = player->x - ob->x;
+
+	if ( ob->xdir == -1 )
+	{
+		if (delta < -6*TILEGLOBAL)
+			return;
+		if (delta > 8*PIXGLOBAL)
+		{
+			ob->xdir = 1;
+			return;
+		}
+	}
+	else
+	{
+		if (delta > 6*TILEGLOBAL)
+			return;
+		if (delta < -8*PIXGLOBAL)
+		{
+			ob->xdir = 1;
+			return;
+		}
+	}
+
+	ob->yspeed = -SPDSQUASHLEAPY;
+	ob->xspeed = delta/60;
+	ob->state = &s_squasherjump1;
+}
+
+/*
+====================
+=
+= SquasherJumpReact
+=
+====================
+*/
+
+void SquasherJumpReact (objtype *ob)
+{
+	if (ob->hitsouth)
+		ob->yspeed = 0;
+
+	if (ob->hitnorth)
+		ChangeState (ob,&s_squasherwait);
+
+	PLACESPRITE;
+}
+
+
+/*
+=============================================================================
+
+								APEL
+
+temp4 = pole x coordinate
+
+=============================================================================
+*/
+
+void ApelThink (objtype *ob);
+void ApelClimbThink (objtype *ob);
+void ApelSlideThink (objtype *ob);
+void ApelFallThink (objtype *ob);
+
+void ApelClimbReact (objtype *ob);
+void ApelSlideReact (objtype *ob);
+void ApelFallReact (objtype *ob);
+
+extern	statetype s_apelwalk1;
+extern	statetype s_apelwalk2;
+extern	statetype s_apelwalk3;
+
+extern	statetype s_apelclimb1;
+extern	statetype s_apelclimb2;
+
+extern	statetype s_apelslide1;
+extern	statetype s_apelslide2;
+extern	statetype s_apelslide3;
+extern	statetype s_apelslide4;
+
+extern	statetype s_apelfall;
+
+#pragma warn -sus
+
+statetype s_apelwalk1	= {APELWALKL1SPR,APELWALKR1SPR,step,false,
+	true,10, 128,0, ApelThink, NULL, WalkReact, &s_apelwalk2};
+statetype s_apelwalk2	= {APELWALKL2SPR,APELWALKR2SPR,step,false,
+	true,10, 128,0, ApelThink, NULL, WalkReact, &s_apelwalk3};
+statetype s_apelwalk3	= {APELWALKL3SPR,APELWALKR3SPR,step,false,
+	true,10, 128,0, ApelThink, NULL, WalkReact, &s_apelwalk1};
+
+statetype s_apelclimb1	= {APELSHINNY1SPR,APELSHINNY1SPR,slide,false,
+	false,6, 0,-16, ApelClimbThink, NULL, ApelClimbReact, &s_apelclimb2};
+statetype s_apelclimb2	= {APELSHINNY2SPR,APELSHINNY2SPR,slide,false,
+	false,6, 0,-16, ApelClimbThink, NULL, ApelClimbReact, &s_apelclimb1};
+
+statetype s_apelslide1	= {APELSLIDE1SPR,APELSLIDE1SPR,slide,false,
+	false,6, 0,16, ApelSlideThink, NULL, ApelFallReact, &s_apelslide2};
+statetype s_apelslide2	= {APELSLIDE2SPR,APELSLIDE2SPR,slide,false,
+	false,6, 0,16, ApelSlideThink, NULL, ApelFallReact, &s_apelslide3};
+statetype s_apelslide3	= {APELSLIDE3SPR,APELSLIDE3SPR,slide,false,
+	false,6, 0,16, ApelSlideThink, NULL, ApelFallReact, &s_apelslide4};
+statetype s_apelslide4	= {APELSLIDE4SPR,APELSLIDE4SPR,slide,false,
+	false,6, 0,16, ApelSlideThink, NULL, ApelFallReact, &s_apelslide1};
+
+statetype s_apelfall = {APELWALKL1SPR,APELWALKR1SPR,think,false,
+	false,0, 0,0, ProjectileThink, NULL, ApelFallReact, NULL};
+
+#pragma warn +sus
+
+
+/*
+====================
+=
+= SpawnApel
+=
+====================
+*/
+
+void SpawnApel (int tilex, int tiley)
+{
+	GetNewObj (false);
+
+	new->obclass = apelobj;
+	new->x = tilex<<G_T_SHIFT;
+	new->y = (tiley<<G_T_SHIFT)-2*BLOCKSIZE;
+	new->xdir = 1;
+	new->ydir = 1;
+	NewState (new,&s_apelwalk1);
+}
+
+
+/*
+====================
+=
+= ApelThink
+=
+====================
+*/
+
+void ApelThink (objtype *ob)
+{
+	int	x,y;
+	unsigned far *map;
+
+	if (ob->top > player->bottom || ob->bottom < player->top)
+	{
+	//
+	// try to climb a pole to reach player
+	//
+		if (ob->y < player->y)
+			y = ob->tilebottom;
+		else
+			y = ob->tiletop;
+
+		map = (unsigned _seg *)mapsegs[1]+
+			mapbwidthtable[y]/2 + ob->tilemidx;
+
+		if ((tinf[INTILE+*map]&0x7f) == 1)
+		{
+			ob->xmove = (ob->tilemidx<<G_T_SHIFT) - ob->x;
+			ob->ymove = 0;
+			ob->temp4 = ob->tilemidx;	// for future reference
+			ob->needtoclip = false;		// can climb through pole holes
+			if (ob->y < player->y)
+				ob->state = &s_apelslide1;
+			else
+				ob->state = &s_apelclimb1;
+			return;
+		}
+	}
+
+	if (US_RndT()>32)		// don't turn around all the time
+		return;
+
+	if (ob->x < player->x)
+		ob->xdir = 1;
+	else
+		ob->xdir = -1;
+
+}
+
+
+/*
+====================
+=
+= ApelClimbThink
+=
+====================
+*/
+
+void ApelClimbThink (objtype *ob)
+{
+	unsigned far *map;
+
+	map = (unsigned _seg *)mapsegs[1]+
+		mapbwidthtable[ob->tiletop]/2 + ob->temp4;
+
+	if ((tinf[INTILE+*map]&0x7f) != 1)
+	{
+		ob->needtoclip = true;
+		ob->state = &s_apelfall;
+	}
+}
+
+
+/*
+====================
+=
+= ApelSlideThink
+=
+====================
+*/
+
+void ApelSlideThink (objtype *ob)
+{
+	unsigned far *map;
+
+	map = (unsigned _seg *)mapsegs[1]+
+		mapbwidthtable[ob->tilebottom]/2 + ob->temp4;
+
+	if ((tinf[INTILE+*map]&0x7f) != 1)
+	{
+		ob->needtoclip = true;
+		ob->state = &s_apelfall;
+	}
+}
+
+
+/*
+====================
+=
+= ApelClimbReact
+=
+====================
+*/
+
+void ApelClimbReact (objtype *ob)
+{
+	if (ob->hitsouth)
+		ChangeState (ob,&s_apelfall);
+	PLACESPRITE;
+}
+
+
+/*
+====================
+=
+= ApelFallReact
+=
+====================
+*/
+
+void ApelFallReact (objtype *ob)
+{
+	if (ob->hitnorth)
+		ChangeState (ob,&s_apelwalk1);
+	PLACESPRITE;
+}
+
+
+/*
+=============================================================================
+
+								PEA BRAIN
+
+=============================================================================
+*/
+
+void PeaBrainThink (objtype *ob);
+void PeaFlyReact (objtype *ob);
+
+extern statetype s_peabrainfly;
+
+extern	statetype s_peabrainwalk1;
+extern	statetype s_peabrainwalk2;
+extern	statetype s_peabrainwalk3;
+extern	statetype s_peabrainwalk4;
+
+#pragma warn -sus
+
+statetype s_peabrainfly	= {PEABRAINWALKL1SPR,PEABRAINWALKR1SPR,think,false,
+	false,0, 0,0, ProjectileThink, NULL, PeaFlyReact, NULL};
+
+statetype s_peabrainwalk1	= {PEABRAINWALKL1SPR,PEABRAINWALKR1SPR,step,false,
+	true,10, 128,0, PeaBrainThink, NULL, WalkReact, &s_peabrainwalk2};
+statetype s_peabrainwalk2	= {PEABRAINWALKL2SPR,PEABRAINWALKR2SPR,step,false,
+	true,10, 128,0, PeaBrainThink, NULL, WalkReact, &s_peabrainwalk3};
+statetype s_peabrainwalk3	= {PEABRAINWALKL3SPR,PEABRAINWALKR3SPR,step,false,
+	true,10, 128,0, PeaBrainThink, NULL, WalkReact, &s_peabrainwalk4};
+statetype s_peabrainwalk4	= {PEABRAINWALKL4SPR,PEABRAINWALKR4SPR,step,false,
+	true,10, 128,0, PeaBrainThink, NULL, WalkReact, &s_peabrainwalk1};
+
+#pragma warn +sus
+
+/*
+====================
+=
+= SpawnPeaBrain
+=
+====================
+*/
+
+void SpawnPeaBrain (int tilex, int tiley)
+{
+	GetNewObj (false);
+
+	new->obclass = peabrainobj;
+	new->x = tilex<<G_T_SHIFT;
+	new->y = tiley<<G_T_SHIFT;
+	NewState (new,&s_peabrainwalk1);
+}
+
+/*
+====================
+=
+= PeaBrainThink
+=
+====================
+*/
+
+void PeaBrainThink (objtype *ob)
+{
+	ob++;
+}
+
+
+/*
+====================
+=
+= PeaFlyReact
+=
+====================
+*/
+
+void PeaFlyReact (objtype *ob)
+{
+	if (ob->hitnorth)
+		ChangeState (ob,&s_peabrainwalk1);
+
+	PLACESPRITE;
+}
+
+
+/*
+=============================================================================
+
+								PEA POD
+
+temp1 = number of peas spit
+
+=============================================================================
+*/
+
+#define MAXPEASPIT	4
+
+#define PEAXSPEED	48
+#define PEAYSPEED	-20
+
+
+void PeaPodThink (objtype *ob);
+void SpitPeaBrain (objtype *ob);
+
+extern	statetype s_peapodwalk1;
+extern	statetype s_peapodwalk2;
+extern	statetype s_peapodwalk3;
+extern	statetype s_peapodwalk4;
+
+extern	statetype s_peapodspit1;
+extern	statetype s_peapodspit2;
+
+#pragma warn -sus
+
+statetype s_peapodwalk1	= {PEAPODRUNL1SPR,PEAPODRUNR1SPR,step,false,
+	true,10, 128,0, PeaPodThink, NULL, WalkReact, &s_peapodwalk2};
+statetype s_peapodwalk2	= {PEAPODRUNL2SPR,PEAPODRUNR2SPR,step,false,
+	true,10, 128,0, PeaPodThink, NULL, WalkReact, &s_peapodwalk3};
+statetype s_peapodwalk3	= {PEAPODRUNL3SPR,PEAPODRUNR3SPR,step,false,
+	true,10, 128,0, PeaPodThink, NULL, WalkReact, &s_peapodwalk4};
+statetype s_peapodwalk4	= {PEAPODRUNL4SPR,PEAPODRUNR4SPR,step,false,
+	true,10, 128,0, PeaPodThink, NULL, WalkReact, &s_peapodwalk1};
+
+statetype s_peapodspit1	= {PEAPODSPITLSPR,PEAPODSPITRSPR,step,false,
+	true,30, 0,0, SpitPeaBrain, NULL, DrawReact, &s_peapodspit2};
+statetype s_peapodspit2	= {PEAPODSPITLSPR,PEAPODSPITRSPR,step,false,
+	true,30, 0,0, NULL, NULL, DrawReact, &s_peapodwalk1};
+
+#pragma warn +sus
+
+/*
+====================
+=
+= SpawnPeaPod
+=
+====================
+*/
+
+void SpawnPeaPod (int tilex, int tiley)
+{
+	GetNewObj (false);
+
+	new->obclass = peapodobj;
+	new->x = tilex<<G_T_SHIFT;
+	new->y = (tiley<<G_T_SHIFT)-2*BLOCKSIZE;
+	new->xdir = 1;
+	new->ydir = 1;
+	NewState (new,&s_peapodwalk1);
+}
+
+/*
+====================
+=
+= SpitPeaBrain
+=
+====================
+*/
+
+void SpitPeaBrain (objtype *ob)
+{
+	GetNewObj (true);
+	new->obclass = peabrainobj;
+	if (ob->xdir == 1)
+	{
+		new->x = ob->x+8*16;
+		new->y = ob->y+8*16;
+	}
+	else
+	{
+		new->x = ob->x;
+		new->y = ob->y+8*16;
+	}
+	new->xdir = ob->xdir;
+	new->ydir = 1;
+	new->xspeed = ob->xdir * PEAXSPEED-(US_RndT()>>4);
+	new->yspeed = PEAYSPEED;
+	NewState (new,&s_peabrainfly);
+}
+
+
+
+/*
+====================
+=
+= PeaPodThink
+=
+====================
+*/
+
+void PeaPodThink (objtype *ob)
+{
+	int delta;
+
+	if ( abs(ob->y - player->y) > 3*TILEGLOBAL )
+		return;
+
+	if (player->x < ob->x && ob->xdir == 1)
+		return;
+
+	if (player->x > ob->x && ob->xdir == -1)
+		return;
+
+	if (US_RndT()<8 && ob->temp1 < MAXPEASPIT)
+	{
+		ob->temp1 ++;
+		ob->state = &s_peapodspit1;
+		ob->xmove = 0;
+	}
+}
+
+
+/*
+=============================================================================
+
+							BOOBUS TUBOR
+
+temp4 = hit points
+
+=============================================================================
+*/
+
+#define PREFRAGTHINK	60
+#define POSTFRAGTHINK	60
+
+#define SPDBOOBUSJUMP	60
+#define SPDBOOBUSRUNJUMP 24
+
+void BoobusThink (objtype *ob);
+void FinishThink (objtype *ob);
+void FragThink (objtype *ob);
+void BoobusGroundReact (objtype *ob);
+void BoobusAirReact (objtype *ob);
+
+extern	statetype s_boobuswalk1;
+extern	statetype s_boobuswalk2;
+extern	statetype s_boobuswalk3;
+extern	statetype s_boobuswalk4;
+
+extern	statetype s_boobusjump;
+
+extern	statetype s_deathwait1;
+extern	statetype s_deathwait2;
+extern	statetype s_deathwait3;
+extern	statetype s_deathboom1;
+extern	statetype s_deathboom2;
+extern	statetype s_deathboom3;
+extern	statetype s_deathboom4;
+extern	statetype s_deathboom5;
+extern	statetype s_deathboom6;
+
+#pragma warn -sus
+
+statetype s_boobuswalk1	= {BOOBUSWALKL1SPR,BOOBUSWALKR1SPR,step,false,
+	true,10, 128,0, BoobusThink, NULL, BoobusGroundReact, &s_boobuswalk2};
+statetype s_boobuswalk2	= {BOOBUSWALKL2SPR,BOOBUSWALKR2SPR,step,false,
+	true,10, 128,0, BoobusThink, NULL, BoobusGroundReact, &s_boobuswalk3};
+statetype s_boobuswalk3	= {BOOBUSWALKL3SPR,BOOBUSWALKR3SPR,step,false,
+	true,10, 128,0, BoobusThink, NULL, BoobusGroundReact, &s_boobuswalk4};
+statetype s_boobuswalk4	= {BOOBUSWALKL4SPR,BOOBUSWALKR4SPR,step,false,
+	true,10, 128,0, BoobusThink, NULL, BoobusGroundReact, &s_boobuswalk1};
+
+statetype s_boobusjump	= {BOOBUSJUMPSPR,BOOBUSJUMPSPR,think,false,
+	false,0, 0,0, ProjectileThink, NULL, BoobusAirReact, NULL};
+
+statetype s_boobusdie	= {BOOBUSJUMPSPR,BOOBUSJUMPSPR,step,false,
+	false,4, 0,0, FragThink, NULL, DrawReact, &s_boobusdie};
+statetype s_boobusdie2	= {NULL,NULL,step,false,
+	false,4, 0,0, FragThink, NULL, NULL, &s_boobusdie2};
+statetype s_boobusdie3	= {NULL,NULL,step,false,
+	false,250, 0,0, FinishThink, NULL, NULL, NULL};
+
+statetype s_deathboom1	= {BOOBUSBOOM1SPR,BOOBUSBOOM1SPR,step,false,
+	false,20, 0,0, ProjectileThink, NULL, DrawReact3, &s_deathboom2};
+statetype s_deathboom2	= {BOOBUSBOOM2SPR,BOOBUSBOOM2SPR,step,false,
+	false,20, 0,0, ProjectileThink, NULL, DrawReact3, &s_deathboom3};
+statetype s_deathboom3	= {POOF1SPR,POOF1SPR,step,false,
+	false,40, 0,0, ProjectileThink, NULL, DrawReact3, &s_deathboom4};
+statetype s_deathboom4	= {POOF2SPR,POOF2SPR,step,false,
+	false,30, 0,0, ProjectileThink, NULL, DrawReact3, &s_deathboom5};
+statetype s_deathboom5	= {POOF3SPR,POOF3SPR,step,false,
+	false,30, 0,0, ProjectileThink, NULL, DrawReact3, &s_deathboom6};
+statetype s_deathboom6	= {POOF4SPR,POOF4SPR,step,false,
+	false,30, 0,0, ProjectileThink, NULL, DrawReact3, NULL};
+
+
+#pragma warn +sus
+
+/*
+====================
+=
+= SpawnBoobus
+=
+====================
+*/
+
+void SpawnBoobus (int tilex, int tiley)
+{
+	GetNewObj (false);
+
+	new->obclass = boobusobj;
+	new->x = tilex<<G_T_SHIFT;
+	new->y = (tiley<<G_T_SHIFT)-11*BLOCKSIZE;
+	new->xdir = -1;
+	NewState (new,&s_boobuswalk1);
+	new->temp4 = 12;			// hit points
+}
+
+
+/*
+===================
+=
+= FragThink
+=
+===================
+*/
+
+void FragThink (objtype *ob)
+{
+	if (++ob->temp1 == PREFRAGTHINK)
+		ob->state = &s_boobusdie2;
+	if (++ob->temp1 == POSTFRAGTHINK)
+	{
+		RF_RemoveSprite (&ob->sprite);
+		ob->state = &s_boobusdie3;
+	}
+
+	SD_PlaySound (BOMBBOOMSND);
+	GetNewObj (true);
+	new->x = ob->x-BLOCKSIZE + 5*US_RndT();
+	new->y = ob->y-BLOCKSIZE + 5*US_RndT();
+	new->xspeed = 0; //US_RndT()/4-32;
+	new->yspeed = 0; //US_RndT()/4-32;
+	US_RndT();					// keep rnd from even wrapping
+	ChangeState (new,&s_deathboom1);
+}
+
+
+/*
+===================
+=
+= FinishThink
+=
+===================
+*/
+
+void FinishThink (objtype *ob)
+{
+	playstate = victorious;
+	ob++;
+	SD_PlaySound (BOOBUSGONESND);
+}
+
+
+/*
+====================
+=
+= BoobusThink
+=
+====================
+*/
+
+void BoobusThink (objtype *ob)
+{
+	unsigned	move;
+	boolean	inline = false;
+
+	if (ob->left > player->right)
+		ob->xdir = -1;
+	else if (ob->right < player->left)
+		ob->xdir = 1;
+	else
+		inline = true;
+
+	if (player->top < ob->bottom && player->bottom > ob->top)
+	{
+	// on same level as player, so charge!
+
+	}
+	else
+	{
+	// above or below player, so get directly in line and jump
+		if (inline)
+		{
+			if (ob->y < player->y)
+			{
+			// drop down
+				move = PIXGLOBAL*8;
+				ob->tilebottom++;
+				ob->bottom += move;
+				ob->y += move;
+				ob->state = &s_boobusjump;
+				ob->yspeed = ob->xmove = ob->xspeed = ob->ymove = 0;
+			}
+			else
+			{
+			// jump up
+				ob->state = &s_boobusjump;
+				ob->yspeed = -SPDBOOBUSJUMP;
+				ob->xspeed = 0;
+			}
+
+		}
+	}
+}
+
+/*
+====================
+=
+= BoobusGroundReact
+=
+====================
+*/
+
+void BoobusGroundReact (objtype *ob)
+{
+	if (ob->xdir == 1 && ob->hitwest)
+	{
+		ob->x -= ob->xmove;
+		ob->xdir = -1;
+		ob->nothink = US_RndT()>>5;
+		ChangeState (ob,ob->state);
+	}
+	else if (ob->xdir == -1 && ob->hiteast)
+	{
+		ob->x -= ob->xmove;
+		ob->xdir = 1;
+		ob->nothink = US_RndT()>>5;
+		ChangeState (ob,ob->state);
+	}
+	else if (!ob->hitnorth)
+	{
+		if (ob->bottom >= player->bottom)
+		{
+		 // jump over
+			ob->x -= ob->xmove;
+			ob->y -= ob->ymove;
+			ob->yspeed = -SPDBOOBUSJUMP;
+			ob->xspeed = ob->xdir * SPDBOOBUSRUNJUMP;
+		}
+		else
+		{
+		// drop down
+			ob->xspeed = ob->yspeed = 0;
+		}
+		ChangeState (ob,&s_boobusjump);
+	}
+
+	PLACESPRITE;
+}
+
+
+
+/*
+====================
+=
+= BoobusAirReact
+=
+====================
+*/
+
+void BoobusAirReact (objtype *ob)
+{
+	if (ob->hitsouth)
+		ob->yspeed = 0;
+
+	if (ob->hitnorth)
+		ChangeState (ob,&s_boobuswalk1);
+
+	PLACESPRITE;
+}
diff --git a/src/lib/hb/kd_def.h b/src/lib/hb/kd_def.h
new file mode 100755
index 00000000..c2728a68
--- /dev/null
+++ b/src/lib/hb/kd_def.h
@@ -0,0 +1,366 @@
+/* Keen Dreams Source Code
+ * Copyright (C) 2014 Javier M. Chavez
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// KD_DEF.H
+
+#include "ID_HEADS.H"
+#include <BIOS.H>
+#include "SOFT.H"
+#include "SL_FILE.H"
+
+#define FRILLS	0			// Cut out frills for 360K - MIKE MAYNARD
+
+
+/*
+=============================================================================
+
+						GLOBAL CONSTANTS
+
+=============================================================================
+*/
+
+#define CREDITS 0
+
+#define	MAXACTORS	MAXSPRITES
+
+#define ACCGRAVITY	3
+#define SPDMAXY		80
+
+#define BLOCKSIZE	(8*PIXGLOBAL)		// for positioning starting actors
+
+//
+// movement scrolling edges
+//
+#define SCROLLEAST (TILEGLOBAL*11)
+#define SCROLLWEST (TILEGLOBAL*9)
+#define SCROLLSOUTH (TILEGLOBAL*8)
+#define SCROLLNORTH (TILEGLOBAL*4)
+
+#define CLIPMOVE	24					// speed to push out of a solid wall
+
+#define GAMELEVELS	17
+
+/*
+=============================================================================
+
+							 TYPES
+
+=============================================================================
+*/
+
+typedef	enum	{notdone,resetgame,levelcomplete,warptolevel,died,victorious}
+				exittype;
+
+typedef	enum	{nothing,keenobj,powerobj,doorobj,
+	bonusobj,broccoobj,tomatobj,carrotobj,celeryobj,asparobj,grapeobj,
+	taterobj,cartobj,frenchyobj,melonobj,turnipobj,cauliobj,brusselobj,
+	mushroomobj,squashobj,apelobj,peapodobj,peabrainobj,boobusobj,
+	shotobj,inertobj}	classtype;
+
+typedef struct
+{
+  int 		leftshapenum,rightshapenum;
+  enum		{step,slide,think,stepthink,slidethink} progress;
+  boolean	skippable;
+
+  boolean	pushtofloor;
+  int tictime;
+  int xmove;
+  int ymove;
+  void (*think) ();
+  void (*contact) ();
+  void (*react) ();
+  void *nextstate;
+} statetype;
+
+
+typedef	struct
+{
+	unsigned	worldx,worldy;
+	boolean	leveldone[GAMELEVELS];
+	long	score,nextextra;
+	int		flowerpowers;
+	int		boobusbombs,bombsthislevel;
+	int		keys;
+	int		mapon;
+	int		lives;
+	int		difficulty;
+} gametype;
+
+
+typedef struct	objstruct
+{
+	classtype	obclass;
+	enum		{no,yes,allways,removable} active;
+	boolean		needtoreact,needtoclip;
+	unsigned	nothink;
+	unsigned	x,y;
+
+	int			xdir,ydir;
+	int			xmove,ymove;
+	int			xspeed,yspeed;
+
+	int			ticcount,ticadjust;
+	statetype	*state;
+
+	unsigned	shapenum;
+
+	unsigned	left,top,right,bottom;	// hit rectangle
+	unsigned	midx;
+	unsigned	tileleft,tiletop,tileright,tilebottom;	// hit rect in tiles
+	unsigned	tilemidx;
+
+	int			hitnorth,hiteast,hitsouth,hitwest;	// wall numbers contacted
+
+	int			temp1,temp2,temp3,temp4;
+
+	void		*sprite;
+
+	struct	objstruct	*next,*prev;
+} objtype;
+
+
+struct BitMapHeader {
+	unsigned int	w,h,x,y;
+	unsigned char	d,trans,comp,pad;
+};
+
+struct BitMap {
+	unsigned int Width;
+	unsigned int Height;
+	unsigned int Depth;
+	unsigned int BytesPerRow;
+	char far *Planes[8];
+};
+
+struct Shape {
+	memptr Data;
+	long size;
+	unsigned int BPR;
+	struct BitMapHeader bmHdr;
+};
+
+typedef struct {
+	int handle;			// handle of file
+	memptr buffer;		// pointer to buffer
+	word offset;		// offset into buffer
+	word status;		// read/write status
+} BufferedIO;
+
+
+
+/*
+=============================================================================
+
+					 KD_MAIN.C DEFINITIONS
+
+=============================================================================
+*/
+
+extern	char	str[80],str2[20];
+extern	boolean	singlestep,jumpcheat,godmode,tedlevel;
+extern	unsigned	tedlevelnum;
+
+void	DebugMemory (void);
+void	TestSprites(void);
+int		DebugKeys (void);
+void	StartupId (void);
+void	ShutdownId (void);
+void	Quit (char *error);
+void	InitGame (void);
+void	main (void);
+
+
+/*
+=============================================================================
+
+					  KD_DEMO.C DEFINITIONS
+
+=============================================================================
+*/
+
+void	Finale (void);
+void	GameOver (void);
+void	DemoLoop (void);
+void	StatusWindow (void);
+void	NewGame (void);
+void	TEDDeath (void);
+
+boolean	LoadGame (int file);
+boolean	SaveGame (int file);
+void	ResetGame (void);
+
+/*
+=============================================================================
+
+					  KD_PLAY.C DEFINITIONS
+
+=============================================================================
+*/
+
+extern	gametype	gamestate;
+extern	exittype	playstate;
+extern	boolean		button0held,button1held;
+extern	unsigned	originxtilemax,originytilemax;
+extern	objtype		*new,*check,*player,*scoreobj;
+
+extern	ControlInfo	c;
+
+extern	objtype dummyobj;
+
+extern	char		*levelnames[21];
+
+void	CheckKeys (void);
+void	CalcInactivate (void);
+void 	InitObjArray (void);
+void 	GetNewObj (boolean usedummy);
+void	RemoveObj (objtype *gone);
+void 	ScanInfoPlane (void);
+void 	PatchWorldMap (void);
+void 	MarkTileGraphics (void);
+void 	FadeAndUnhook (void);
+void 	SetupGameLevel (boolean loadnow);
+void 	ScrollScreen (void);
+void 	MoveObjVert (objtype *ob, int ymove);
+void 	MoveObjHoriz (objtype *ob, int xmove);
+void 	GivePoints (unsigned points);
+void 	ClipToEnds (objtype *ob);
+void 	ClipToEastWalls (objtype *ob);
+void 	ClipToWestWalls (objtype *ob);
+void 	ClipToWalls (objtype *ob);
+void	ClipToSprite (objtype *push, objtype *solid, boolean squish);
+void	ClipToSpriteSide (objtype *push, objtype *solid);
+int 	DoActor (objtype *ob,int tics);
+void 	StateMachine (objtype *ob);
+void 	NewState (objtype *ob,statetype *state);
+void 	PlayLoop (void);
+void 	GameLoop (void);
+
+/*
+=============================================================================
+
+					  KD_KEEN.C DEFINITIONS
+
+=============================================================================
+*/
+
+void	CalcSingleGravity (void);
+
+void	ProjectileThink		(objtype *ob);
+void	VelocityThink		(objtype *ob);
+void	DrawReact			(objtype *ob);
+
+void	SpawnScore (void);
+void	FixScoreBox (void);
+void	SpawnWorldKeen (int tilex, int tiley);
+void	SpawnKeen (int tilex, int tiley, int dir);
+
+void 	KillKeen (void);
+
+extern	int	singlegravity;
+extern	unsigned	bounceangle[8][8];
+
+extern	statetype s_keendie1;
+
+/*
+=============================================================================
+
+					  KD_ACT1.C DEFINITIONS
+
+=============================================================================
+*/
+
+void WalkReact (objtype *ob);
+
+void 	DoGravity (objtype *ob);
+void	AccelerateX (objtype *ob,int dir,int max);
+void 	FrictionX (objtype *ob);
+
+void	ProjectileThink		(objtype *ob);
+void	VelocityThink		(objtype *ob);
+void	DrawReact			(objtype *ob);
+void	DrawReact2			(objtype *ob);
+void	DrawReact3			(objtype *ob);
+void	ChangeState (objtype *ob, statetype *state);
+
+void	ChangeToFlower (objtype *ob);
+
+void	SpawnBonus (int tilex, int tiley, int type);
+void	SpawnDoor (int tilex, int tiley);
+void 	SpawnBrocco (int tilex, int tiley);
+void 	SpawnTomat (int tilex, int tiley);
+void 	SpawnCarrot (int tilex, int tiley);
+void 	SpawnAspar (int tilex, int tiley);
+void 	SpawnGrape (int tilex, int tiley);
+
+extern	statetype s_doorraise;
+
+extern	statetype s_bonus;
+extern	statetype s_bonusrise;
+
+extern	statetype s_broccosmash3;
+extern	statetype s_broccosmash4;
+
+extern	statetype s_grapefall;
+
+/*
+=============================================================================
+
+					  KD_ACT2.C DEFINITIONS
+
+=============================================================================
+*/
+
+void SpawnTater (int tilex, int tiley);
+void SpawnCart (int tilex, int tiley);
+void SpawnFrenchy (int tilex, int tiley);
+void SpawnMelon (int tilex, int tiley,int dir);
+void SpawnSquasher (int tilex, int tiley);
+void SpawnApel (int tilex, int tiley);
+void SpawnPeaPod (int tilex, int tiley);
+void SpawnPeaBrain (int tilex, int tiley);
+void SpawnBoobus (int tilex, int tiley);
+
+extern	statetype s_taterattack2;
+extern	statetype s_squasherjump2;
+extern	statetype s_boobusdie;
+
+extern	statetype s_deathwait1;
+extern	statetype s_deathwait2;
+extern	statetype s_deathwait3;
+extern	statetype s_deathboom1;
+extern	statetype s_deathboom2;
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+//						GELIB.C DEFINITIONS
+//
+/////////////////////////////////////////////////////////////////////////////
+
+void FreeShape(struct Shape *shape);
+int UnpackEGAShapeToScreen(struct Shape *SHP,int startx,int starty);
+
+long Verify(char *filename);
+memptr InitBufferedIO(int handle, BufferedIO *bio);
+void FreeBufferedIO(BufferedIO *bio);
+byte bio_readch(BufferedIO *bio);
+void bio_fillbuffer(BufferedIO *bio);
+void SwapLong(long far *Var);
+void SwapWord(unsigned int far *Var);
+void MoveGfxDst(short x, short y);
\ No newline at end of file
diff --git a/src/lib/hb/kd_demo.c b/src/lib/hb/kd_demo.c
new file mode 100755
index 00000000..4469a924
--- /dev/null
+++ b/src/lib/hb/kd_demo.c
@@ -0,0 +1,545 @@
+/* Keen Dreams Source Code
+ * Copyright (C) 2014 Javier M. Chavez
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// KD_DEMO.C
+
+#include <dir.h>
+#include "KD_DEF.H"
+
+#pragma	hdrstop
+
+#define RLETAG	0xABCD
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+//===========================================================================
+
+/*
+=====================
+=
+= NewGame
+=
+= Set up new game to start from the beginning
+=
+=====================
+*/
+
+void NewGame (void)
+{
+	word	i;
+
+	gamestate.worldx = 0;		// spawn keen at starting spot
+
+	gamestate.mapon = 0;
+	gamestate.score = 0;
+	gamestate.nextextra = 20000;
+	gamestate.lives = 3;
+	gamestate.flowerpowers = gamestate.boobusbombs = 0;
+	for (i = 0;i < GAMELEVELS;i++)
+		gamestate.leveldone[i] = false;
+}
+
+//===========================================================================
+
+/*
+=====================
+=
+= WaitOrKey
+=
+=====================
+*/
+
+int WaitOrKey (int vbls)
+{
+	while (vbls--)
+	{
+		IN_ReadControl(0,&c);		// get player input
+		if (LastScan || c.button0 || c.button1)
+		{
+			IN_ClearKeysDown ();
+			return 1;
+		}
+		VW_WaitVBL(1);
+	}
+	return 0;
+}
+
+//===========================================================================
+
+/*
+=====================
+=
+= GameOver
+=
+=====================
+*/
+
+void
+GameOver (void)
+{
+	VW_InitDoubleBuffer ();
+	US_CenterWindow (16,3);
+
+	US_PrintCentered("Game Over!");
+
+	VW_UpdateScreen ();
+	IN_ClearKeysDown ();
+	IN_Ack ();
+
+}
+
+
+//===========================================================================
+
+/*
+==================
+=
+= StatusWindow
+=
+==================
+*/
+
+void StatusWindow (void)
+{
+	word	x;
+
+	// DEBUG - make this look better
+
+	US_CenterWindow(22,7);
+	US_CPrint("Status Window");
+
+	WindowX += 8;
+	WindowW -= 8;
+	WindowY += 20;
+	WindowH -= 20;
+	PrintX = WindowX;
+	PrintY = WindowY;
+
+	VWB_DrawTile8(PrintX,PrintY,26);
+	VWB_DrawTile8(PrintX + 8,PrintY,27);
+	PrintX += 24;
+	US_PrintUnsigned(gamestate.lives);
+	US_Print("\n");
+
+	VWB_DrawTile8(PrintX,PrintY,32);
+	VWB_DrawTile8(PrintX + 8,PrintY,33);
+	VWB_DrawTile8(PrintX,PrintY + 8,34);
+	VWB_DrawTile8(PrintX + 8,PrintY + 8,35);
+	PrintX += 24;
+	US_PrintUnsigned(gamestate.boobusbombs);
+	US_Print("\n");
+
+	WindowX += 50;
+	WindowW -= 50;
+	PrintX = WindowX;
+	PrintY = WindowY;
+
+	fontcolor = F_FIRSTCOLOR;
+	US_Print("Next ");
+	fontcolor = F_BLACK;
+	x = PrintX;
+	VWB_DrawTile8(PrintX,PrintY,26);
+	VWB_DrawTile8(PrintX + 8,PrintY,27);
+	PrintX += 24;
+	US_PrintUnsigned(gamestate.nextextra);
+	US_Print("\n");
+
+	PrintX = x;
+	VWB_DrawTile8(PrintX,PrintY,24);
+	VWB_DrawTile8(PrintX + 8,PrintY,25);
+	PrintX += 24;
+	US_PrintUnsigned(gamestate.keys);
+	US_Print("\n");
+
+	// DEBUG - add flower powers (#36)
+
+	VW_UpdateScreen();
+	IN_Ack();
+}
+
+boolean
+SaveGame(int file)
+{
+	word	i,size,compressed,expanded;
+	objtype	*o;
+	memptr	bigbuffer;
+
+	if (!CA_FarWrite(file,(void far *)&gamestate,sizeof(gamestate)))
+		return(false);
+
+	expanded = mapwidth * mapheight * 2;
+	MM_GetPtr (&bigbuffer,expanded);
+
+	for (i = 0;i < 3;i++)	// Write all three planes of the map
+	{
+//
+// leave a word at start of compressed data for compressed length
+//
+		compressed = CA_RLEWCompress ((unsigned huge *)mapsegs[i]
+			,expanded,((unsigned huge *)bigbuffer)+1,RLETAG);
+
+		*(unsigned huge *)bigbuffer = compressed;
+
+		if (!CA_FarWrite(file,(void far *)bigbuffer,compressed+2) )
+		{
+			MM_FreePtr (&bigbuffer);
+			return(false);
+		}
+	}
+
+	for (o = player;o;o = o->next)
+		if (!CA_FarWrite(file,(void far *)o,sizeof(objtype)))
+		{
+			MM_FreePtr (&bigbuffer);
+			return(false);
+		}
+
+	MM_FreePtr (&bigbuffer);
+	return(true);
+}
+
+
+boolean
+LoadGame(int file)
+{
+	word	i,j,size;
+	objtype	*o;
+	int orgx,orgy;
+	objtype		*prev,*next,*followed;
+	unsigned	compressed,expanded;
+	memptr	bigbuffer;
+
+	if (!CA_FarRead(file,(void far *)&gamestate,sizeof(gamestate)))
+		return(false);
+
+// drop down a cache level and mark everything, so when the option screen
+// is exited it will be cached
+
+	ca_levelbit >>= 1;
+	ca_levelnum--;
+
+	SetupGameLevel (false);		// load in and cache the base old level
+	titleptr[ca_levelnum] = levelnames[mapon];
+
+	ca_levelbit <<= 1;
+	ca_levelnum ++;
+
+	expanded = mapwidth * mapheight * 2;
+	MM_GetPtr (&bigbuffer,expanded);
+
+	for (i = 0;i < 3;i++)	// Read all three planes of the map
+	{
+		if (!CA_FarRead(file,(void far *)&compressed,sizeof(compressed)) )
+		{
+			MM_FreePtr (&bigbuffer);
+			return(false);
+		}
+
+		if (!CA_FarRead(file,(void far *)bigbuffer,compressed) )
+		{
+			MM_FreePtr (&bigbuffer);
+			return(false);
+		}
+
+		CA_RLEWexpand ((unsigned huge *)bigbuffer,
+			(unsigned huge *)mapsegs[i],compressed,RLETAG);
+	}
+
+	MM_FreePtr (&bigbuffer);
+
+	// Read the object list back in - assumes at least one object in list
+
+	InitObjArray ();
+	new = player;
+	prev = new->prev;
+	next = new->next;
+	if (!CA_FarRead(file,(void far *)new,sizeof(objtype)))
+		return(false);
+	new->prev = prev;
+	new->next = next;
+	new->needtoreact = true;
+	new->sprite = NULL;
+	new = scoreobj;
+	while (true)
+	{
+		prev = new->prev;
+		next = new->next;
+		if (!CA_FarRead(file,(void far *)new,sizeof(objtype)))
+			return(false);
+		followed = new->next;
+		new->prev = prev;
+		new->next = next;
+		new->needtoreact = true;
+		new->sprite = NULL;
+
+		if (followed)
+			GetNewObj (false);
+		else
+			break;
+	}
+
+	*((long *)&(scoreobj->temp1)) = -1;		// force score to be updated
+	scoreobj->temp3 = -1;			// and flower power
+	scoreobj->temp4 = -1;			// and lives
+
+	return(true);
+}
+
+void
+ResetGame(void)
+{
+	NewGame ();
+
+	ca_levelnum--;
+	CA_ClearMarks();
+	titleptr[ca_levelnum] = NULL;		// don't reload old level
+	ca_levelnum++;
+}
+
+#if FRILLS
+void
+TEDDeath(void)
+{
+	ShutdownId();
+	execlp("TED5.EXE","TED5.EXE","/LAUNCH","KDREAMS",NULL);
+}
+#endif
+
+static boolean
+MoveTitleTo(int offset)
+{
+	boolean		done;
+	int			dir,
+				chunk,
+				move;
+	longword	lasttime,delay;
+
+	if (offset < originxglobal)
+		dir = -1;
+	else
+		dir = +1;
+
+	chunk = dir * PIXGLOBAL;
+
+	done = false;
+	delay = 1;
+	while (!done)
+	{
+		lasttime = TimeCount;
+		move = delay * chunk;
+		if (chunk < 0)
+			done = originxglobal + move <= offset;
+		else
+			done = originxglobal + move >= offset;
+		if (!done)
+		{
+			RF_Scroll(move,0);
+			RF_Refresh();
+		}
+		if (IN_IsUserInput())
+			return(true);
+		delay = TimeCount - lasttime;
+	}
+	if (originxglobal != offset)
+	{
+		RF_Scroll(offset - originxglobal,0);
+		RF_Refresh();
+	}
+	return(false);
+}
+
+static boolean
+Wait(longword time)
+{
+	time += TimeCount;
+	while ((TimeCount < time) && (!IN_IsUserInput()))
+	{
+		if (!(TimeCount % MINTICS))
+			RF_Refresh();
+	}
+	return(IN_IsUserInput());
+}
+
+static boolean
+ShowText(int offset,WindowRec *wr,char *s)
+{
+	if (MoveTitleTo(offset))
+		return(true);
+
+	US_RestoreWindow(wr);
+	US_CPrint(s);
+	VW_UpdateScreen();
+
+	if (Wait(TickBase * 5))
+		return(true);
+
+	US_RestoreWindow(wr);
+	US_CPrint(s);
+	VW_UpdateScreen();
+	return(false);
+}
+
+/*
+=====================
+=
+= DemoLoop
+=
+=====================
+*/
+
+void
+DemoLoop (void)
+{
+	char		*s;
+	word		move;
+	longword	lasttime;
+	char *FileName1;
+	struct Shape FileShape1;
+#if CREDITS
+	char *FileName2;
+	struct Shape FileShape2;
+#endif
+	struct ffblk ffblk;
+	WindowRec	mywin;
+	int bufsave	= bufferofs;
+	int dissave	= displayofs;
+
+
+#if FRILLS
+//
+// check for launch from ted
+//
+	if (tedlevel)
+	{
+		NewGame();
+		gamestate.mapon = tedlevelnum;
+		GameLoop();
+		TEDDeath();
+	}
+#endif
+
+//
+// demo loop
+//
+	US_SetLoadSaveHooks(LoadGame,SaveGame,ResetGame);
+	restartgame = gd_Continue;
+
+	if (findfirst("KDREAMS.CMP", &ffblk, 0) == -1)
+		Quit("Couldn't find KDREAMS.CMP");
+
+	while (true)
+	{
+
+		loadedgame = false;
+
+		FileName1 = "TITLESCR.LBM";
+		if (LoadLIBShape("KDREAMS.CMP", FileName1, &FileShape1))
+			Quit("Can't load TITLE SCREEN");
+#if CREDITS
+		FileName2 = "CREDITS.LBM";
+		if (LoadLIBShape("KDREAMS.CMP", FileName2, &FileShape2))
+			Quit("Can't load CREDITS SCREEN");
+#endif
+
+		while (!restartgame && !loadedgame)
+		{
+
+			VW_InitDoubleBuffer();
+			IN_ClearKeysDown();
+
+			while (true)
+			{
+
+				VW_SetScreen(0, 0);
+				MoveGfxDst(0, 200);
+				UnpackEGAShapeToScreen(&FileShape1, 0, 0);
+				VW_ScreenToScreen (64*200,0,40,200);
+
+#if CREDITS
+				if (IN_UserInput(TickBase * 8, false))
+					break;
+#else
+				if (IN_UserInput(TickBase * 4, false))
+					break;
+#endif
+
+#if CREDITS
+				MoveGfxDst(0, 200);
+				UnpackEGAShapeToScreen(&FileShape2, 0, 0);
+				VW_ScreenToScreen (64*200,0,40,200);
+
+				if (IN_UserInput(TickBase * 7, false))
+					break;
+#else
+				MoveGfxDst(0, 200);
+				UnpackEGAShapeToScreen(&FileShape1, 0, 0);
+				VW_ScreenToScreen (64*200,0,40,200);
+
+				if (IN_UserInput(TickBase * 3, false))
+					break;
+#endif
+
+				displayofs = 0;
+				VWB_Bar(0,0,320,200,FIRSTCOLOR);
+				US_DisplayHighScores(-1);
+
+				if (IN_UserInput(TickBase * 6, false))
+					break;
+
+			}
+
+			bufferofs = bufsave;
+			displayofs = dissave;
+
+			VW_FixRefreshBuffer();
+			US_ControlPanel ();
+		}
+
+		if (!loadedgame)
+			NewGame();
+
+		FreeShape(&FileShape1);
+#if CREDITS
+		FreeShape(&FileShape2);
+#endif
+		GameLoop();
+	}
+}
diff --git a/src/lib/hb/kd_keen.c b/src/lib/hb/kd_keen.c
new file mode 100755
index 00000000..a1349317
--- /dev/null
+++ b/src/lib/hb/kd_keen.c
@@ -0,0 +1,2528 @@
+/* Keen Dreams Source Code
+ * Copyright (C) 2014 Javier M. Chavez
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// KD_KEEN.C
+
+#include "KD_DEF.H"
+#pragma hdrstop
+
+/*
+
+player->temp1 = pausetime / pointer to zees when sleeping
+player->temp2 = pausecount / stagecount
+player->temp3 =
+player->temp4 =
+
+
+*/
+
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+#define KEENPRIORITY	1
+
+#define PLACESPRITE RF_PlaceSprite (&ob->sprite,ob->x,ob->y,ob->shapenum, \
+	spritedraw,KEENPRIORITY);
+
+#define	MINPOLEJUMPTICS	19	// wait tics before allowing a pole regram
+
+#define SPDRUNJUMP		16
+#define SPDPOLESIDEJUMP	8
+#define WALKAIRSPEED	8
+#define DIVESPEED		32
+#define	JUMPTIME		16
+#define	POLEJUMPTIME	10
+#define	SPDJUMP			40
+#define	SPDDIVEUP		16
+#define	SPDPOLEJUMP		20
+
+#define SPDPOWERUP		-64
+#define SPDPOWER		64
+#define SPDPOWERY		-20
+#define POWERCOUNT		50
+
+#define MAXXSPEED   24
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+int	singlegravity;
+unsigned	bounceangle[8][8] =
+{
+{0,0,0,0,0,0,0,0},
+{7,6,5,4,3,2,1,0},
+{5,4,3,2,1,0,15,14},
+{5,4,3,2,1,0,15,14},
+{3,2,1,0,15,14,13,12},
+{9,8,7,6,5,4,3,2},
+{9,8,7,6,5,4,3,2},
+{11,10,9,8,7,6,5,4}
+};
+
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+int	jumptime;
+long	leavepoletime;		// TimeCount when jumped off pole
+
+/*
+=============================================================================
+
+						 SCORE BOX ROUTINES
+
+=============================================================================
+*/
+
+void	SpawnScore (void);
+void ScoreThink (objtype *ob);
+void ScoreReact (objtype *ob);
+
+void MemDrawChar (int char8,byte far *dest,unsigned width,unsigned planesize);
+
+statetype s_score	= {NULL,NULL,think,false,
+	false,0, 0,0, ScoreThink , NULL, ScoreReact, NULL};
+
+
+/*
+======================
+=
+= SpawnScore
+=
+======================
+*/
+
+void	SpawnScore (void)
+{
+	scoreobj->obclass = inertobj;
+	scoreobj->active = allways;
+	scoreobj->needtoclip = false;
+	*((long *)&(scoreobj->temp1)) = -1;		// force score to be updated
+	scoreobj->temp3 = -1;			// and flower power
+	scoreobj->temp4 = -1;			// and lives
+	NewState (scoreobj,&s_score);
+}
+
+
+void	FixScoreBox (void)
+{
+	unsigned	width, planesize;
+	unsigned smallplane,bigplane;
+	spritetype	_seg	*block;
+	byte	far	*dest;
+
+// draw boobus bomb if on level 15, else flower power
+	block = (spritetype _seg *)grsegs[SCOREBOXSPR];
+	width = block->width[0];
+	planesize = block->planesize[0];
+	dest = (byte far *)grsegs[SCOREBOXSPR]+block->sourceoffset[0]
+		+ planesize + width*16 + 4*CHARWIDTH;
+	if (mapon == 15)
+	{
+		MemDrawChar (20,dest,width,planesize);
+		MemDrawChar (21,dest+CHARWIDTH,width,planesize);
+		MemDrawChar (22,dest+width*8,width,planesize);
+		MemDrawChar (23,dest+width*8+CHARWIDTH,width,planesize);
+	}
+	else
+	{
+		MemDrawChar (28,dest,width,planesize);
+		MemDrawChar (29,dest+CHARWIDTH,width,planesize);
+		MemDrawChar (30,dest+width*8,width,planesize);
+		MemDrawChar (31,dest+width*8+CHARWIDTH,width,planesize);
+	}
+
+}
+
+
+/*
+======================
+=
+= MemDrawChar
+=
+======================
+*/
+
+#if GRMODE == EGAGR
+
+void MemDrawChar (int char8,byte far *dest,unsigned width,unsigned planesize)
+{
+asm	mov	si,[char8]
+asm	shl	si,1
+asm	shl	si,1
+asm	shl	si,1
+asm	shl	si,1
+asm	shl	si,1		// index into char 8 segment
+
+asm	mov	ds,[WORD PTR grsegs+STARTTILE8*2]
+asm	mov	es,[WORD PTR dest+2]
+
+asm	mov	cx,4		// draw four planes
+asm	mov	bx,[width]
+asm	dec	bx
+
+planeloop:
+
+asm	mov	di,[WORD PTR dest]
+
+asm	movsb
+asm	add	di,bx
+asm	movsb
+asm	add	di,bx
+asm	movsb
+asm	add	di,bx
+asm	movsb
+asm	add	di,bx
+asm	movsb
+asm	add	di,bx
+asm	movsb
+asm	add	di,bx
+asm	movsb
+asm	add	di,bx
+asm	movsb
+
+asm	mov	ax,[planesize]
+asm	add	[WORD PTR dest],ax
+
+asm	loop	planeloop
+
+asm	mov	ax,ss
+asm	mov	ds,ax
+
+}
+#endif
+
+#if GRMODE == CGAGR
+void MemDrawChar (int char8,byte far *dest,unsigned width,unsigned planesize)
+{
+asm	mov	si,[char8]
+asm	shl	si,1
+asm	shl	si,1
+asm	shl	si,1
+asm	shl	si,1		// index into char 8 segment
+
+asm	mov	ds,[WORD PTR grsegs+STARTTILE8*2]
+asm	mov	es,[WORD PTR dest+2]
+
+asm	mov	bx,[width]
+asm	sub	bx,2
+
+asm	mov	di,[WORD PTR dest]
+
+asm	movsw
+asm	add	di,bx
+asm	movsw
+asm	add	di,bx
+asm	movsw
+asm	add	di,bx
+asm	movsw
+asm	add	di,bx
+asm	movsw
+asm	add	di,bx
+asm	movsw
+asm	add	di,bx
+asm	movsw
+asm	add	di,bx
+asm	movsw
+
+asm	mov	ax,ss
+asm	mov	ds,ax
+
+	planesize++;		// shut the compiler up
+}
+#endif
+
+
+/*
+====================
+=
+= ShiftScore
+=
+====================
+*/
+#if GRMODE == EGAGR
+void ShiftScore (void)
+{
+	spritetabletype far *spr;
+	spritetype _seg *dest;
+
+	spr = &spritetable[SCOREBOXSPR-STARTSPRITES];
+	dest = (spritetype _seg *)grsegs[SCOREBOXSPR];
+
+	CAL_ShiftSprite (FP_SEG(dest),dest->sourceoffset[0],
+		dest->sourceoffset[1],spr->width,spr->height,2);
+
+	CAL_ShiftSprite (FP_SEG(dest),dest->sourceoffset[0],
+		dest->sourceoffset[2],spr->width,spr->height,4);
+
+	CAL_ShiftSprite (FP_SEG(dest),dest->sourceoffset[0],
+		dest->sourceoffset[3],spr->width,spr->height,6);
+}
+#endif
+
+/*
+===============
+=
+= ScoreThink
+=
+===============
+*/
+
+void ScoreThink (objtype *ob)
+{
+	char		str[10],*ch;
+	spritetype	_seg	*block;
+	byte		far *dest;
+	unsigned	i, length, width, planesize, number;
+
+//
+// score changed
+//
+	if ((gamestate.score>>16) != ob->temp1
+		|| (unsigned)gamestate.score != ob->temp2 )
+	{
+		block = (spritetype _seg *)grsegs[SCOREBOXSPR];
+		width = block->width[0];
+		planesize = block->planesize[0];
+		dest = (byte far *)grsegs[SCOREBOXSPR]+block->sourceoffset[0]
+			+ planesize + width*4 + 1*CHARWIDTH;
+
+		ltoa (gamestate.score,str,10);
+
+		// erase leading spaces
+		length = strlen(str);
+		for (i=6;i>length;i--)
+			MemDrawChar (0,dest+=CHARWIDTH,width,planesize);
+
+		// draw digits
+		ch = str;
+		while (*ch)
+			MemDrawChar (*ch++ - '0'+1,dest+=CHARWIDTH,width,planesize);
+
+#if GRMODE == EGAGR
+		ShiftScore ();
+#endif
+		ob->needtoreact = true;
+		ob->temp1 = gamestate.score>>16;
+		ob->temp2 = gamestate.score;
+	}
+
+//
+// flower power changed
+//
+	if (mapon == 15)
+		number = gamestate.boobusbombs;
+	else
+		number = gamestate.flowerpowers;
+	if (number != ob->temp3)
+	{
+		block = (spritetype _seg *)grsegs[SCOREBOXSPR];
+		width = block->width[0];
+		planesize = block->planesize[0];
+		dest = (byte far *)grsegs[SCOREBOXSPR]+block->sourceoffset[0]
+			+ planesize + width*20 + 5*CHARWIDTH;
+
+		if (number > 99)
+			strcpy (str,"99");
+		else
+			ltoa (number,str,10);
+
+		// erase leading spaces
+		length = strlen(str);
+		for (i=2;i>length;i--)
+			MemDrawChar (0,dest+=CHARWIDTH,width,planesize);
+
+		// draw digits
+		ch = str;
+		while (*ch)
+			MemDrawChar (*ch++ - '0'+1,dest+=CHARWIDTH,width,planesize);
+
+#if GRMODE == EGAGR
+		ShiftScore ();
+#endif
+		ob->needtoreact = true;
+		ob->temp3 = gamestate.flowerpowers;
+	}
+
+//
+// lives changed
+//
+	if (gamestate.lives != ob->temp4)
+	{
+		block = (spritetype _seg *)grsegs[SCOREBOXSPR];
+		width = block->width[0];
+		planesize = block->planesize[0];
+		dest = (byte far *)grsegs[SCOREBOXSPR]+block->sourceoffset[0]
+			+ planesize + width*20 + 2*CHARWIDTH;
+
+		if (gamestate.lives>9)
+			MemDrawChar ('9' - '0'+1,dest,width,planesize);
+		else
+			MemDrawChar (gamestate.lives +1,dest,width,planesize);
+
+#if GRMODE == EGAGR
+		ShiftScore ();
+#endif
+		ob->needtoreact = true;
+		ob->temp4 = gamestate.lives;
+	}
+
+	if (originxglobal != ob->x || originyglobal != ob->y)
+		ob->needtoreact = true;
+}
+
+
+/*
+===============
+=
+= ScoreReact
+=
+===============
+*/
+
+void ScoreReact (objtype *ob)
+{
+	ob->x = originxglobal;
+	ob->y = originyglobal;
+
+#if GRMODE == EGAGR
+	RF_PlaceSprite (&ob->sprite
+		,ob->x+4*PIXGLOBAL
+		,ob->y+4*PIXGLOBAL
+		,SCOREBOXSPR
+		,spritedraw
+		,PRIORITIES-1);
+#endif
+#if GRMODE == CGAGR
+	RF_PlaceSprite (&ob->sprite
+		,ob->x+8*PIXGLOBAL
+		,ob->y+8*PIXGLOBAL
+		,SCOREBOXSPR
+		,spritedraw
+		,PRIORITIES-1);
+#endif
+}
+
+
+/*
+=============================================================================
+
+						FLOWER POWER ROUTINES
+
+temp1 = 8, same as power bonus
+temp2 = initial direction
+
+=============================================================================
+*/
+
+void	PowerCount			(objtype *ob);
+void	PowerContact 		(objtype *ob, objtype *hit);
+void	PowerReact			(objtype *ob);
+
+extern	statetype s_flowerpower1;
+extern	statetype s_flowerpower2;
+
+extern	statetype s_boobusbomb1;
+extern	statetype s_boobusbomb2;
+
+extern	statetype s_bombexplode;
+extern	statetype s_bombexplode2;
+extern	statetype s_bombexplode3;
+extern	statetype s_bombexplode4;
+extern	statetype s_bombexplode5;
+extern	statetype s_bombexplode6;
+
+extern	statetype s_powerblink1;
+extern	statetype s_powerblink2;
+
+statetype s_flowerpower1	= {FLOWERPOWER1SPR,FLOWERPOWER1SPR,stepthink,false,
+	false,10, 0,0, ProjectileThink, PowerContact, PowerReact, &s_flowerpower2};
+statetype s_flowerpower2	= {FLOWERPOWER2SPR,FLOWERPOWER2SPR,stepthink,false,
+	false,10, 0,0, ProjectileThink, PowerContact, PowerReact, &s_flowerpower1};
+
+statetype s_boobusbomb1	= {BOOBUSBOMB1SPR,BOOBUSBOMB1SPR,stepthink,false,
+	false,5, 0,0, ProjectileThink, PowerContact, PowerReact, &s_boobusbomb2};
+statetype s_boobusbomb2	= {BOOBUSBOMB3SPR,BOOBUSBOMB3SPR,stepthink,false,
+	false,5, 0,0, ProjectileThink, PowerContact, PowerReact, &s_boobusbomb1};
+
+statetype s_bombexplode	= {BOOBUSBOOM1SPR,BOOBUSBOOM1SPR,step,false,
+	false,5, 0,0, NULL, NULL, DrawReact, &s_bombexplode2};
+statetype s_bombexplode2= {BOOBUSBOOM2SPR,BOOBUSBOOM2SPR,step,false,
+	false,5, 0,0, NULL, NULL, DrawReact, &s_bombexplode3};
+statetype s_bombexplode3= {BOOBUSBOOM1SPR,BOOBUSBOOM1SPR,step,false,
+	false,5, 0,0, NULL, NULL, DrawReact, &s_bombexplode4};
+statetype s_bombexplode4= {BOOBUSBOOM2SPR,BOOBUSBOOM2SPR,step,false,
+	false,5, 0,0, NULL, NULL, DrawReact, &s_bombexplode5};
+statetype s_bombexplode5= {BOOBUSBOOM1SPR,BOOBUSBOOM1SPR,step,false,
+	false,5, 0,0, NULL, NULL, DrawReact, &s_bombexplode6};
+statetype s_bombexplode6= {BOOBUSBOOM2SPR,BOOBUSBOOM2SPR,step,false,
+	false,5, 0,0, NULL, NULL, DrawReact, NULL};
+
+statetype s_powerblink1	= {FLOWERPOWER1SPR,FLOWERPOWER1SPR,step,false,
+	false,5, 0,0, PowerCount, NULL, DrawReact, &s_powerblink2};
+statetype s_powerblink2	= {-1,-1,step,false,
+	false,5, 0,0, PowerCount, NULL, DrawReact, &s_powerblink1};
+
+
+/*
+===============
+=
+= ThrowPower
+=
+===============
+*/
+
+void ThrowPower (unsigned x, unsigned y, int dir)
+{
+	statetype *startstate;
+
+	if (mapon == 15)
+	{
+		if (!gamestate.boobusbombs)
+		{
+			SD_PlaySound (NOWAYSND);
+			return;						// no bombs to throw
+		}
+		SD_PlaySound (THROWBOMBSND);
+		gamestate.bombsthislevel--;
+		gamestate.boobusbombs--;
+	}
+	else
+	{
+		if (!gamestate.flowerpowers)
+		{
+			SD_PlaySound (NOWAYSND);
+			return;						// no flower power to throw
+		}
+		SD_PlaySound (THROWSND);
+		gamestate.flowerpowers--;
+	}
+
+
+
+	GetNewObj (true);
+	new->obclass = powerobj;
+	new->temp2 = dir;
+	new->x = x;
+	new->y = y;
+	new->tileleft = new->tileright = x>>G_T_SHIFT;
+	new->tiletop = new->tilebottom = y>>G_T_SHIFT;
+	new->ydir = -1;
+
+	switch (dir)
+	{
+	case dir_North:
+		new->xspeed = 0;
+		new->yspeed = SPDPOWERUP;
+		break;
+	case dir_East:
+		new->xspeed = SPDPOWER;
+		new->yspeed = SPDPOWERY;
+		break;
+	case dir_South:
+		new->xspeed = 0;
+		new->yspeed = SPDPOWER;
+		break;
+	case dir_West:
+		new->xspeed = -SPDPOWER;
+		new->yspeed = SPDPOWERY;
+		break;
+	default:
+		Quit ("ThrowPower: Bad dir!");
+	}
+
+	if (mapon != 15)
+	{
+		new->temp1 = 8;  				// flower power bonus
+		startstate = &s_flowerpower1;
+	}
+	else
+	{
+		new->temp1 = 10;  				// boobus bomb bonus
+		startstate = &s_boobusbomb1;
+	}
+	new->active = removable;
+
+#if 0
+	new->x -= 5*new->xspeed;		// make sure they hit nearby walls
+	new->y -= 5*new->yspeed;
+#endif
+	NewState (new,startstate);
+#if 0
+	new->xmove = 5*new->xspeed;
+	new->ymove = 5*new->yspeed;
+	ClipToWalls (new);
+#endif
+}
+
+/*
+============================
+=
+= PowerCount
+=
+============================
+*/
+
+void	PowerCount (objtype *ob)
+{
+	ob->temp2+=tics;
+
+	ob->shapenum = 0;
+
+	if (ob->temp2 > POWERCOUNT)
+		RemoveObj(ob);
+}
+
+
+/*
+============================
+=
+= CalcSingleGravity
+=
+============================
+*/
+
+void	CalcSingleGravity (void)
+{
+	unsigned	speed;
+	long	i;
+//
+// only accelerate on odd tics, because of limited precision
+//
+	speed = 0;
+	singlegravity = 0;
+	for (i=lasttimecount-tics;i<lasttimecount;i++)
+	{
+		if (i&1)
+		{
+			speed+=ACCGRAVITY;
+			if (speed>SPDMAXY)
+			  speed=SPDMAXY;
+		}
+		singlegravity+=speed;
+	}
+
+	singlegravity/=2;
+}
+
+
+/*
+============================
+=
+= PowerContact
+=
+============================
+*/
+
+void	PowerContact (objtype *ob, objtype *hit)
+{
+	unsigned	x,y,yspot,xspot;
+
+	switch (hit->obclass)
+	{
+	case	broccoobj:
+	case	tomatobj:
+	case	carrotobj:
+	case	celeryobj:
+	case	asparobj:
+	case	taterobj:
+	case	frenchyobj:
+	case	squashobj:
+	case	apelobj:
+	case	peapodobj:
+	case	peabrainobj:
+		ChangeToFlower (hit);
+		RemoveObj (ob);
+		break;
+	case	boobusobj:
+		if (!--hit->temp4)
+		{
+		// killed boobus
+			GivePoints (50000);
+			GivePoints (50000);
+			hit->obclass = inertobj;
+			ChangeState (hit,&s_boobusdie);
+			hit->temp1 = 0;		// death count
+		}
+		SD_PlaySound (BOMBBOOMSND);
+		ChangeState (ob,&s_bombexplode);
+		break;
+	}
+}
+
+
+/*
+============================
+=
+= PowerReact
+=
+============================
+*/
+
+void	PowerReact (objtype *ob)
+{
+	unsigned wall,absx,absy,angle,newangle;
+	unsigned long speed;
+
+	PLACESPRITE;
+	if (ob->hiteast || ob->hitwest)
+	{
+		ob->xspeed= -ob->xspeed/2;
+		ob->obclass = bonusobj;
+	}
+
+	if (ob->hitsouth)
+	{
+		if ( ob->hitsouth == 17)	// go through pole holes
+		{
+			if (ob->temp2 != dir_North)
+				ob->obclass = bonusobj;
+			ob->top-=32;
+			ob->y-=32;
+			ob->xspeed = 0;
+			ob->x = ob->tilemidx*TILEGLOBAL + 4*PIXGLOBAL;
+		}
+		else
+		{
+			ob->yspeed= -ob->yspeed/2;
+		}
+		return;
+	}
+
+	wall = ob->hitnorth;
+	if ( wall == 17)	// go through pole holes
+	{
+		if (ob->temp2 != dir_South)
+			ob->obclass = bonusobj;
+		ob->bottom+=32;
+		ob->y+=32;
+		ob->xspeed = 0;
+		ob->x = ob->tilemidx*TILEGLOBAL + 4*PIXGLOBAL;
+	}
+	else if (wall)
+	{
+		ob->obclass = bonusobj;
+		if (ob->yspeed < 0)
+			ob->yspeed = 0;
+
+		absx = abs(ob->xspeed);
+		absy = ob->yspeed;
+		if (absx>absy)
+		{
+			if (absx>absy*2)	// 22 degrees
+			{
+				angle = 0;
+				speed = absx*286;	// x*sqrt(5)/2
+			}
+			else				// 45 degrees
+			{
+				angle = 1;
+				speed = absx*362;	// x*sqrt(2)
+			}
+		}
+		else
+		{
+			if (absy>absx*2)	// 90 degrees
+			{
+				angle = 3;
+				speed = absy*256;
+			}
+			else
+			{
+				angle = 2;		// 67 degrees
+				speed = absy*286;	// y*sqrt(5)/2
+			}
+		}
+		if (ob->xspeed > 0)
+			angle = 7-angle;
+
+		speed >>= 1;
+		newangle = bounceangle[ob->hitnorth][angle];
+		switch (newangle)
+		{
+		case 0:
+			ob->xspeed = speed / 286;
+			ob->yspeed = -ob->xspeed / 2;
+			break;
+		case 1:
+			ob->xspeed = speed / 362;
+			ob->yspeed = -ob->xspeed;
+			break;
+		case 2:
+			ob->yspeed = -(speed / 286);
+			ob->xspeed = -ob->yspeed / 2;
+			break;
+		case 3:
+
+		case 4:
+			ob->xspeed = 0;
+			ob->yspeed = -(speed / 256);
+			break;
+		case 5:
+			ob->yspeed = -(speed / 286);
+			ob->xspeed = ob->yspeed / 2;
+			break;
+		case 6:
+			ob->xspeed = ob->yspeed = -(speed / 362);
+			break;
+		case 7:
+			ob->xspeed = -(speed / 286);
+			ob->yspeed = ob->xspeed / 2;
+			break;
+
+		case 8:
+			ob->xspeed = -(speed / 286);
+			ob->yspeed = -ob->xspeed / 2;
+			break;
+		case 9:
+			ob->xspeed = -(speed / 362);
+			ob->yspeed = -ob->xspeed;
+			break;
+		case 10:
+			ob->yspeed = speed / 286;
+			ob->xspeed = -ob->yspeed / 2;
+			break;
+		case 11:
+
+		case 12:
+			ob->xspeed = 0;
+			ob->yspeed = -(speed / 256);
+			break;
+		case 13:
+			ob->yspeed = speed / 286;
+			ob->xspeed = ob->yspeed / 2;
+			break;
+		case 14:
+			ob->xspeed = speed / 362;
+			ob->yspeed = speed / 362;
+			break;
+		case 15:
+			ob->xspeed = speed / 286;
+			ob->yspeed = ob->xspeed / 2;
+			break;
+		}
+
+		if (speed < 256*16)
+		{
+			if (mapon == 15)
+			{
+				ob->ydir = -1;			// bonus stuff flies up when touched
+				ob->temp2 = ob->shapenum = BOOBUSBOMB1SPR;
+				ob->temp3 = ob->temp2 + 2;
+				ob->needtoclip = 0;
+				ChangeState (ob,&s_bonus);
+			}
+			else
+			{
+				ChangeState (ob,&s_powerblink1);
+				ob->needtoclip = 0;
+				ob->temp2 = 0;		// blink time
+			}
+		}
+	}
+}
+
+
+/*
+=============================================================================
+
+							   MINI KEEN
+
+player->temp1 = dir
+player->temp2 = animation stage
+
+#define WORLDKEENSLEEP1SPR		238
+#define WORLDKEENSLEEP2SPR		239
+#define WORLDKEENWAVE1SPR		240
+#define WORLDKEENWAVE2SPR		241
+=============================================================================
+*/
+
+void	SpawnWorldKeen (int tilex, int tiley);
+void	KeenWorldThink		(objtype *ob);
+void	KeenWorldWalk		(objtype *ob);
+
+extern	statetype s_worldkeen;
+
+extern	statetype s_worldkeenwave1;
+extern	statetype s_worldkeenwave2;
+extern	statetype s_worldkeenwave3;
+extern	statetype s_worldkeenwave4;
+extern	statetype s_worldkeenwave5;
+
+extern	statetype s_worldkeenwait;
+
+extern	statetype s_worldkeensleep1;
+extern	statetype s_worldkeensleep2;
+
+extern	statetype s_worldwalk;
+
+#pragma warn -sus
+
+statetype s_worldkeen	= {NULL,NULL,stepthink,false,
+	false,360, 0,0, KeenWorldThink, NULL, DrawReact, &s_worldkeenwave1};
+
+statetype s_worldkeenwave1= {WORLDKEENWAVE1SPR,WORLDKEENWAVE1SPR,stepthink,false,
+	false,20, 0,0, KeenWorldThink, NULL, DrawReact, &s_worldkeenwave2};
+statetype s_worldkeenwave2= {WORLDKEENWAVE2SPR,WORLDKEENWAVE2SPR,stepthink,false,
+	false,20, 0,0, KeenWorldThink, NULL, DrawReact, &s_worldkeenwave3};
+statetype s_worldkeenwave3= {WORLDKEENWAVE1SPR,WORLDKEENWAVE1SPR,stepthink,false,
+	false,20, 0,0, KeenWorldThink, NULL, DrawReact, &s_worldkeenwave4};
+statetype s_worldkeenwave4= {WORLDKEENWAVE2SPR,WORLDKEENWAVE2SPR,stepthink,false,
+	false,20, 0,0, KeenWorldThink, NULL, DrawReact, &s_worldkeenwave5};
+statetype s_worldkeenwave5= {WORLDKEENWAVE1SPR,WORLDKEENWAVE1SPR,stepthink,false,
+	false,20, 0,0, KeenWorldThink, NULL, DrawReact, &s_worldkeenwait};
+
+statetype s_worldkeenwait	= {WORLDKEEND3SPR,WORLDKEEND3SPR,stepthink,false,
+	false,400, 0,0, KeenWorldThink, NULL, DrawReact, &s_worldkeensleep1};
+
+statetype s_worldkeensleep1	= {WORLDKEENSLEEP1SPR,WORLDKEENSLEEP1SPR,stepthink,false,
+	false,30, 0,0, KeenWorldThink, NULL, DrawReact, &s_worldkeensleep2};
+statetype s_worldkeensleep2	= {WORLDKEENSLEEP2SPR,WORLDKEENSLEEP2SPR,stepthink,false,
+	false,90, 0,0, KeenWorldThink, NULL, DrawReact, &s_worldkeensleep2};
+
+statetype s_worldwalk	= {NULL,NULL,slide,false,
+	false,4, 16,16, KeenWorldWalk, NULL, DrawReact, &s_worldwalk};
+
+#pragma warn +sus
+
+/*
+======================
+=
+= SpawnWorldKeen
+=
+======================
+*/
+
+void	SpawnWorldKeen (int tilex, int tiley)
+{
+	player->obclass = keenobj;
+	if (!gamestate.worldx)
+	{
+		player->x = tilex<<G_T_SHIFT;
+		player->y = tiley<<G_T_SHIFT;
+	}
+	else
+	{
+		player->x = gamestate.worldx;
+		player->y = gamestate.worldy;
+	}
+
+	player->active = yes;
+	player->needtoclip = true;
+	player->xdir = 0;
+	player->ydir = 0;
+	player->temp1 = dir_West;
+	player->temp2 = 3;
+	player->shapenum = 3+WORLDKEENL1SPR-1;				// feet together
+	NewState (player,&s_worldkeen);
+}
+
+
+/*
+======================
+=
+= CheckEnterLevel
+=
+======================
+*/
+
+void	CheckEnterLevel (objtype *ob)
+{
+	int	x,y,tile;
+
+	for (y=ob->tiletop;y<=ob->tilebottom;y++)
+		for (x=ob->tileleft;x<=ob->tileright;x++)
+		{
+			tile = *((unsigned _seg *)mapsegs[2]+mapbwidthtable[y]/2+x);
+			if (tile >= 3 && tile <=18)
+			{
+			//
+			// enter level!
+			//
+				if (tile == 17)
+				{
+					if (gamestate.boobusbombs < 12)
+					{
+						VW_FixRefreshBuffer ();
+						US_CenterWindow (26,6);
+						PrintY+= 8;
+						US_CPrint ("You can't possibly\n"
+								   "defeat King Boobus Tuber\n"
+								   "with less than 12 bombs!");
+						VW_UpdateScreen ();
+						IN_Ack ();
+						RF_ForceRefresh ();
+						return;
+					}
+				}
+				gamestate.worldx = ob->x;
+				gamestate.worldy = ob->y;
+				gamestate.mapon = tile-2;
+				playstate = levelcomplete;
+				SD_PlaySound (ENTERLEVELSND);
+			}
+		}
+}
+
+
+/*
+======================
+=
+= KeenWorldThink
+=
+======================
+*/
+
+void	KeenWorldThink (objtype *ob)
+{
+	if (c.dir!=dir_None)
+	{
+		ob->state = &s_worldwalk;
+		ob->temp2 = 0;
+		KeenWorldWalk (ob);
+	}
+
+	if (c.button0 || c.button1)
+		CheckEnterLevel(ob);
+}
+
+
+/*
+======================
+=
+= KeenWorldWalk
+=
+======================
+*/
+
+int worldshapes[8] = {WORLDKEENU1SPR-1,WORLDKEENUR1SPR-1,WORLDKEENR1SPR-1,
+	WORLDKEENDR1SPR-1,WORLDKEEND1SPR-1,WORLDKEENDL1SPR-1,WORLDKEENL1SPR-1,
+	WORLDKEENUL1SPR-1};
+
+int worldanims[4] ={2,3,1,3};
+
+void	KeenWorldWalk (objtype *ob)
+{
+	ob->xdir = c.xaxis;
+	ob->ydir = c.yaxis;
+
+	if (++ob->temp2==4)
+		ob->temp2 = 0;
+
+	if (c.button0 || c.button1)
+		CheckEnterLevel(ob);
+
+	if (c.dir == dir_None)
+	{
+		ob->state = &s_worldkeen;
+		ob->shapenum = worldshapes[ob->temp1]+3;
+		return;
+	}
+
+	ob->temp1 = c.dir;
+	ob->shapenum = worldshapes[ob->temp1]+worldanims[ob->temp2];
+}
+
+
+/*
+=============================================================================
+
+								KEEN
+
+player->temp1 = pausetime / pointer to zees when sleeping
+player->temp2 = pausecount / stagecount
+player->temp3 =
+player->temp4 = pole x location
+
+=============================================================================
+*/
+
+void	KeenStandThink		(objtype *ob);
+void	KeenPauseThink		(objtype *ob);
+void	KeenGoSleepThink	(objtype *ob);
+void	KeenSleepThink		(objtype *ob);
+void	KeenDieThink		(objtype *ob);
+void	KeenDuckThink		(objtype *ob);
+void	KeenDropDownThink	(objtype *ob);
+void	KeenWalkThink		(objtype *ob);
+void	KeenAirThink		(objtype *ob);
+void	KeenPoleThink		(objtype *ob);
+void	KeenClimbThink		(objtype *ob);
+void	KeenDropThink		(objtype *ob);
+void	KeenDive			(objtype *ob);
+void	KeenThrow			(objtype *ob);
+
+void	KeenContact 		(objtype *ob, objtype *hit);
+void	KeenSimpleReact		(objtype *ob);
+void	KeenStandReact		(objtype *ob);
+void	KeenWalkReact		(objtype *ob);
+void	KeenPoleReact		(objtype *ob);
+void	KeenAirReact		(objtype *ob);
+void	KeenDiveReact		(objtype *ob);
+void	KeenSlideReact		(objtype *ob);
+
+//-------------------
+
+extern	statetype s_keenzee1;
+extern	statetype s_keenzee2;
+extern	statetype s_keenzee3;
+extern	statetype s_keenzee4;
+extern	statetype s_keenzee5;
+
+//-------------------
+
+extern	statetype	s_keenstand;
+extern	statetype s_keenpauselook;
+extern	statetype s_keenyawn1;
+extern	statetype s_keenyawn2;
+extern	statetype s_keenyawn3;
+extern	statetype s_keenyawn4;
+
+extern	statetype s_keenwait1;
+extern	statetype s_keenwait2;
+extern	statetype s_keenwait3;
+extern	statetype s_keenwait4;
+extern	statetype s_keenwait5;
+extern	statetype s_keenwait6;
+
+extern	statetype s_keenmoon1;
+extern	statetype s_keenmoon2;
+extern	statetype s_keenmoon3;
+
+extern	statetype s_keengosleep1;
+extern	statetype s_keengosleep2;
+extern	statetype s_keensleep1;
+extern	statetype s_keensleep2;
+
+extern	statetype s_keendie1;
+extern	statetype s_keendie2;
+extern	statetype s_keendie3;
+extern	statetype s_keendie4;
+
+extern	statetype	s_keenlookup;
+extern	statetype	s_keenduck;
+extern	statetype	s_keendrop;
+
+//-------------------
+
+extern	statetype s_keenreach;
+
+extern	statetype s_keenpole;
+
+extern	statetype s_keenclimb1;
+extern	statetype s_keenclimb2;
+extern	statetype s_keenclimb3;
+
+extern	statetype s_keenslide1;
+extern	statetype s_keenslide2;
+extern	statetype s_keenslide3;
+extern	statetype s_keenslide4;
+
+extern	statetype s_keenpolethrow1;
+extern	statetype s_keenpolethrow2;
+extern	statetype s_keenpolethrow3;
+
+extern	statetype s_keenpolethrowup1;
+extern	statetype s_keenpolethrowup2;
+extern	statetype s_keenpolethrowup3;
+
+extern	statetype s_keenpolethrowdown1;
+extern	statetype s_keenpolethrowdown2;
+extern	statetype s_keenpolethrowdown3;
+
+//-------------------
+
+extern	statetype	s_keenwalk1;
+extern	statetype	s_keenwalk2;
+extern	statetype	s_keenwalk3;
+extern	statetype	s_keenwalk4;
+
+extern	statetype	s_keenthrow1;
+extern	statetype	s_keenthrow2;
+extern	statetype	s_keenthrow3;
+extern	statetype	s_keenthrow4;
+
+extern	statetype	s_keenthrowup1;
+extern	statetype	s_keenthrowup2;
+extern	statetype	s_keenthrowup3;
+
+extern	statetype	s_keendive1;
+extern	statetype	s_keendive2;
+extern	statetype	s_keendive3;
+
+//-------------------
+
+extern	statetype s_keenjumpup1;
+extern	statetype s_keenjumpup2;
+extern	statetype s_keenjumpup3;
+
+extern	statetype s_keenjump1;
+extern	statetype s_keenjump2;
+extern	statetype s_keenjump3;
+
+extern	statetype s_keenairthrow1;
+extern	statetype s_keenairthrow2;
+extern	statetype s_keenairthrow3;
+
+extern	statetype s_keenairthrowup1;
+extern	statetype s_keenairthrowup2;
+extern	statetype s_keenairthrowup3;
+
+extern	statetype s_keenairthrowdown1;
+extern	statetype s_keenairthrowdown2;
+extern	statetype s_keenairthrowdown3;
+
+#pragma warn -sus
+
+//-------------------
+
+statetype s_keenzee1	= {KEENZEES1SPR,KEENZEES1SPR,step,false,
+	false,30, 0,0, NULL, NULL, DrawReact, &s_keenzee2};
+statetype s_keenzee2	= {KEENZEES2SPR,KEENZEES2SPR,step,false,
+	false,30, 0,0, NULL, NULL, DrawReact, &s_keenzee3};
+statetype s_keenzee3	= {KEENZEES3SPR,KEENZEES3SPR,step,false,
+	false,30, 0,0, NULL, NULL, DrawReact, &s_keenzee1};
+
+//-------------------
+
+statetype s_keenstand	= {KEENSTANDLSPR,KEENSTANDRSPR,stepthink,false,
+	true,4, 0,16, KeenPauseThink, KeenContact, KeenStandReact, &s_keenstand};
+statetype s_keenpauselook= {KEENLOOKULSPR,KEENLOOKURSPR,stepthink,false,
+	true,60, 0,0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenstand};
+statetype s_keenyawn1	= {KEENYAWN1SPR,KEENYAWN1SPR,stepthink,false,
+	true,40, 0,0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenyawn2};
+statetype s_keenyawn2	= {KEENYAWN2SPR,KEENYAWN2SPR,stepthink,false,
+	true,40, 0,0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenyawn3};
+statetype s_keenyawn3	= {KEENYAWN3SPR,KEENYAWN3SPR,stepthink,false,
+	true,40, 0,0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenyawn4};
+statetype s_keenyawn4	= {KEENYAWN4SPR,KEENYAWN4SPR,stepthink,false,
+	true,40, 0,0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenstand};
+
+statetype s_keenwait1	= {KEENWAITL2SPR,KEENWAITR2SPR,stepthink,false,
+	true,90, 0,0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenwait2};
+statetype s_keenwait2	= {KEENWAITL1SPR,KEENWAITR1SPR,stepthink,false,
+	true,10, 0,0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenwait3};
+statetype s_keenwait3	= {KEENWAITL2SPR,KEENWAITR2SPR,stepthink,false,
+	true,90, 0,0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenwait4};
+statetype s_keenwait4	= {KEENWAITL1SPR,KEENWAITR1SPR,stepthink,false,
+	true,10, 0,0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenwait5};
+statetype s_keenwait5	= {KEENWAITL2SPR,KEENWAITR2SPR,stepthink,false,
+	true,90, 0,0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenwait6};
+statetype s_keenwait6	= {KEENWAITL3SPR,KEENWAITR3SPR,stepthink,false,
+	true,70, 0,0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenstand};
+
+statetype s_keengosleep1= {KEENSLEEP1SPR,KEENSLEEP1SPR,stepthink,false,
+	true,20, 0,0, KeenPauseThink, KeenContact, KeenStandReact, &s_keengosleep2};
+statetype s_keengosleep2= {KEENSLEEP2SPR,KEENSLEEP2SPR,step,false,
+	true,20, 0,0, KeenGoSleepThink, KeenContact, KeenStandReact, &s_keensleep1};
+statetype s_keensleep1= {KEENSLEEP3SPR,KEENSLEEP3SPR,stepthink,false,
+	true,120, 0,0, KeenSleepThink, KeenContact, KeenStandReact, &s_keensleep2};
+statetype s_keensleep2= {KEENSLEEP4SPR,KEENSLEEP4SPR,stepthink,false,
+	true,120, 0,0, KeenSleepThink, KeenContact, KeenStandReact, &s_keensleep1};
+
+statetype s_keengetup	= {KEENGETUPLSPR,KEENGETUPRSPR,step,false,
+	true,15, 0,0, NULL, KeenContact, KeenSimpleReact, &s_keenstand};
+
+statetype s_keendie1		= {KEENDREAM1SPR,KEENDREAM1SPR,step,false,
+	false,20, 0,0, NULL, NULL, KeenSimpleReact, &s_keendie2};
+statetype s_keendie2		= {KEENDREAM2SPR,KEENDREAM2SPR,step,false,
+	false,20, 0,0, NULL, NULL, KeenSimpleReact, &s_keendie3};
+statetype s_keendie3		= {KEENDREAM3SPR,KEENDREAM3SPR,step,false,
+	false,120, 0,0, KeenDieThink, NULL, KeenSimpleReact, &s_keendie3};
+
+statetype s_keenlookup	= {KEENLOOKULSPR,KEENLOOKURSPR,think,false,
+	true,0, 0,0, KeenStandThink, KeenContact, KeenStandReact, &s_keenlookup};
+
+statetype s_keenduck	= {KEENDUCKLSPR,KEENDUCKRSPR,think,false,
+	true,0, 0,0, KeenDuckThink, KeenContact, KeenStandReact, &s_keenduck};
+
+statetype s_keendrop	= {KEENDUCKLSPR,KEENDUCKRSPR,step,false,
+	false,0, 0,0, KeenDropDownThink, KeenContact, KeenSimpleReact, &s_keenjumpup3};
+
+//-------------------
+
+statetype s_keenpole	= {KEENSHINNYL1SPR,KEENSHINNYR1SPR,think,false,
+	false,0, 0,0, KeenPoleThink, KeenContact, KeenSimpleReact, &s_keenpole};
+
+statetype s_keenclimb1	= {KEENSHINNYL1SPR,KEENSHINNYR1SPR,slidethink,false,
+	false,8, 0,8, KeenClimbThink, KeenContact, KeenSimpleReact, &s_keenclimb2};
+statetype s_keenclimb2	= {KEENSHINNYL2SPR,KEENSHINNYR2SPR,slidethink,false,
+	false,8, 0,8, KeenClimbThink, KeenContact, KeenSimpleReact, &s_keenclimb3};
+statetype s_keenclimb3	= {KEENSHINNYL3SPR,KEENSHINNYR3SPR,slidethink,false,
+	false,8, 0,8, KeenClimbThink, KeenContact, KeenSimpleReact, &s_keenclimb1};
+
+statetype s_keenslide1	= {KEENSLIDED1SPR,KEENSLIDED1SPR,slide,false,
+	false,8, 0,24, KeenDropThink, KeenContact, KeenSimpleReact, &s_keenslide2};
+statetype s_keenslide2	= {KEENSLIDED2SPR,KEENSLIDED2SPR,slide,false,
+	false,8, 0,24, KeenDropThink, KeenContact, KeenSimpleReact, &s_keenslide3};
+statetype s_keenslide3	= {KEENSLIDED3SPR,KEENSLIDED3SPR,slide,false,
+	false,8, 0,24, KeenDropThink, KeenContact, KeenSimpleReact, &s_keenslide4};
+statetype s_keenslide4	= {KEENSLIDED4SPR,KEENSLIDED4SPR,slide,false,
+	false,8, 0,24, KeenDropThink, KeenContact, KeenSimpleReact, &s_keenslide1};
+
+statetype s_keenpolethrow1	= {KEENPTHROWL1SPR,KEENPTHROWR1SPR,step,false,
+	false,8, 0,0, NULL, KeenContact, KeenSimpleReact, &s_keenpolethrow2};
+statetype s_keenpolethrow2	= {KEENPTHROWL2SPR,KEENPTHROWR2SPR,step,true,
+	false,1, 0,0, KeenThrow, KeenContact, KeenSimpleReact, &s_keenpolethrow3};
+statetype s_keenpolethrow3	= {KEENPTHROWL2SPR,KEENPTHROWR2SPR,step,false,
+	false,10, 0,0, NULL, KeenContact, KeenSimpleReact, &s_keenpole};
+
+statetype s_keenpolethrowup1 = {KEENPLTHROWU1SPR,KEENPRTHROWU1SPR,step,false,
+	false,8, 0,0, NULL, KeenContact, KeenSimpleReact, &s_keenpolethrowup2};
+statetype s_keenpolethrowup2 = {KEENPLTHROWU2SPR,KEENPRTHROWU2SPR,step,true,
+	false,1, 0,0, KeenThrow, KeenContact, KeenSimpleReact, &s_keenpolethrowup3};
+statetype s_keenpolethrowup3 = {KEENPLTHROWU2SPR,KEENPRTHROWU2SPR,step,false,
+	false,10, 0,0, NULL, KeenContact, KeenSimpleReact, &s_keenpole};
+
+statetype s_keenpolethrowdown1 = {KEENPLTHROWD1SPR,KEENPRTHROWD1SPR,step,false,
+	false,8, 0,0, NULL, KeenContact, KeenSimpleReact, &s_keenpolethrowdown2};
+statetype s_keenpolethrowdown2 = {KEENPLTHROWD2SPR,KEENPRTHROWD2SPR,step,true,
+	false,1, 0,0, KeenThrow, KeenContact, KeenSimpleReact, &s_keenpolethrowdown3};
+statetype s_keenpolethrowdown3 = {KEENPLTHROWD2SPR,KEENPRTHROWD2SPR,step,false,
+	false,10, 0,0, NULL, KeenContact, KeenSimpleReact, &s_keenpole};
+
+
+//-------------------
+
+statetype s_keenwalk1	= {KEENRUNL1SPR,KEENRUNR1SPR,slidethink,true,
+	true,6, 24,0, KeenWalkThink, KeenContact, KeenWalkReact, &s_keenwalk2};
+statetype s_keenwalk2	= {KEENRUNL2SPR,KEENRUNR2SPR,slidethink,true,
+	true,6, 24,0, KeenWalkThink, KeenContact, KeenWalkReact, &s_keenwalk3};
+statetype s_keenwalk3	= {KEENRUNL3SPR,KEENRUNR3SPR,slidethink,true,
+	true,6, 24,0, KeenWalkThink, KeenContact, KeenWalkReact, &s_keenwalk4};
+statetype s_keenwalk4	= {KEENRUNL4SPR,KEENRUNR4SPR,slidethink,true,
+	true,6, 24,0, KeenWalkThink, KeenContact, KeenWalkReact, &s_keenwalk1};
+
+statetype s_keenthrow1	= {KEENTHROWL1SPR,KEENTHROWR1SPR,step,true,
+	true,4, 0,0, NULL, KeenContact, KeenStandReact, &s_keenthrow2};
+statetype s_keenthrow2	= {KEENTHROWL2SPR,KEENTHROWR2SPR,step,false,
+	true,4, 0,0, NULL, KeenContact, KeenStandReact, &s_keenthrow3};
+statetype s_keenthrow3	= {KEENTHROWL3SPR,KEENTHROWR3SPR,step,true,
+	true,1, 0,0, KeenThrow, KeenContact, KeenStandReact, &s_keenthrow4};
+statetype s_keenthrow4	= {KEENTHROWL3SPR,KEENTHROWR3SPR,step,false,
+	true,10, 0,0, NULL, KeenContact, KeenStandReact, &s_keenstand};
+
+statetype s_keenthrowup1	= {KEENTHROWU1SPR,KEENTHROWU1SPR,step,false,
+	true,8, 0,0, NULL, KeenContact, KeenStandReact, &s_keenthrowup2};
+statetype s_keenthrowup2	= {KEENTHROWU2SPR,KEENTHROWU2SPR,step,true,
+	true,1, 0,0, KeenThrow, KeenContact, KeenStandReact, &s_keenthrowup3};
+statetype s_keenthrowup3	= {KEENTHROWU2SPR,KEENTHROWU2SPR,step,false,
+	true,10, 0,0, NULL, KeenContact, KeenStandReact, &s_keenstand};
+
+//-------------------
+
+statetype s_keenjumpup1	= {KEENJUMPUL1SPR,KEENJUMPUR1SPR,think,false,
+	false,0, 0,0, KeenAirThink, KeenContact, KeenAirReact, &s_keenjumpup2};
+statetype s_keenjumpup2	= {KEENJUMPUL2SPR,KEENJUMPUR2SPR,think,false,
+	false,0, 0,0, KeenAirThink, KeenContact, KeenAirReact, &s_keenjumpup3};
+statetype s_keenjumpup3	= {KEENJUMPUL1SPR,KEENJUMPUR1SPR,think,false,
+	false,0, 0,0, KeenAirThink, KeenContact, KeenAirReact, &s_keenstand};
+
+statetype s_keenjump1	= {KEENJUMPL1SPR,KEENJUMPR1SPR,think,false,
+	false,0, 0,0, KeenAirThink, KeenContact, KeenAirReact, &s_keenjump2};
+statetype s_keenjump2	= {KEENJUMPL2SPR,KEENJUMPR2SPR,think,false,
+	false,0, 0,0, KeenAirThink, KeenContact, KeenAirReact, &s_keenjump3};
+statetype s_keenjump3	= {KEENJUMPL3SPR,KEENJUMPR3SPR,think,false,
+	false,0, 0,0, KeenAirThink, KeenContact, KeenAirReact, &s_keenstand};
+
+statetype s_keenairthrow1= {KEENJLTHROWL1SPR,KEENJRTHROWR1SPR,stepthink,false,
+	false,8, 0,0, ProjectileThink, KeenContact, KeenAirReact, &s_keenairthrow2};
+statetype s_keenairthrow2= {KEENJLTHROWL2SPR,KEENJRTHROWR2SPR,step,true,
+	false,1, 0,0, KeenThrow, KeenContact, KeenAirReact, &s_keenairthrow3};
+statetype s_keenairthrow3= {KEENJLTHROWL2SPR,KEENJRTHROWR2SPR,stepthink,false,
+	false,10, 0,0, ProjectileThink, KeenContact, KeenAirReact, &s_keenjumpup3};
+
+statetype s_keenairthrowup1= {KEENJLTHROWU1SPR,KEENJRTHROWU1SPR,stepthink,false,
+	false,8, 0,0, ProjectileThink, KeenContact, KeenAirReact, &s_keenairthrowup2};
+statetype s_keenairthrowup2= {KEENJLTHROWU2SPR,KEENJRTHROWU2SPR,step,true,
+	false,1, 0,0, KeenThrow, KeenContact, KeenAirReact, &s_keenairthrowup3};
+statetype s_keenairthrowup3= {KEENJLTHROWU2SPR,KEENJRTHROWU2SPR,stepthink,false,
+	false,10, 0,0, ProjectileThink, KeenContact, KeenAirReact, &s_keenjumpup3};
+
+statetype s_keenairthrowdown1= {KEENJLTHROWD1SPR,KEENJRTHROWD1SPR,stepthink,false,
+	false,8, 0,0, ProjectileThink, KeenContact, KeenAirReact, &s_keenairthrowdown2};
+statetype s_keenairthrowdown2= {KEENJLTHROWD2SPR,KEENJRTHROWD2SPR,step,true,
+	false,1, 0,0, KeenThrow, KeenContact, KeenAirReact, &s_keenairthrowdown3};
+statetype s_keenairthrowdown3= {KEENJLTHROWD2SPR,KEENJRTHROWD2SPR,stepthink,false,
+	false,10, 0,0, ProjectileThink, KeenContact, KeenAirReact, &s_keenjumpup3};
+
+
+//===========================================================================
+
+#pragma warn +sus
+
+
+/*
+===============
+=
+= SpawnKeen
+=
+===============
+*/
+
+void SpawnKeen (int tilex, int tiley, int dir)
+{
+	player->obclass = keenobj;
+	player->active = yes;
+	player->needtoclip = true;
+	player->x = tilex<<G_T_SHIFT;
+	player->y = (tiley<<G_T_SHIFT) - BLOCKSIZE*2;
+
+	player->xdir = dir;
+	player->ydir = 1;
+	NewState (player,&s_keenstand);
+}
+
+//==========================================================================
+
+/*
+======================
+=
+= CheckGrabPole
+=
+======================
+*/
+
+boolean	CheckGrabPole (objtype *ob)
+{
+	int	x;
+	unsigned far *map;
+
+//
+// kludgy bit to not let you grab a pole the instant you jump off it
+//
+	if (TimeCount < leavepoletime)
+		leavepoletime = 0;
+	else if (TimeCount - leavepoletime < MINPOLEJUMPTICS)
+		return false;
+
+	if (c.yaxis == -1)
+		map = (unsigned _seg *)mapsegs[1]+
+			mapbwidthtable[(ob->top+6*PIXGLOBAL)/TILEGLOBAL]/2;
+	else
+		map = (unsigned _seg *)mapsegs[1]+
+			mapbwidthtable[ob->tilebottom]/2;
+
+	x = (ob->left + (ob->right - ob->left)/2) >>G_T_SHIFT;
+
+	map += x;
+
+	if ((tinf[INTILE+*map]&0x7f) == 1)
+	{
+		ob->xmove = ((x<<G_T_SHIFT)-8*PIXGLOBAL) - ob->x;
+		ob->ymove = c.yaxis*32;
+		ob->temp4 = x;				// for future reference
+		ob->needtoclip = false;		// can climb through pole holes
+		ob->state = &s_keenpole;
+		return true;
+	}
+
+	return false;
+}
+
+//==========================================================================
+
+
+/*
+============================
+=
+= KeenStandThink
+=
+============================
+*/
+
+void KeenStandThink (objtype *ob)
+{
+	if (c.xaxis)
+	{
+	// started walking
+		ob->xdir = c.xaxis;
+		ob->state = &s_keenwalk1;
+		ob->xmove = ob->xdir*s_keenwalk1.xmove*tics;
+		KeenWalkThink(ob);
+		return;
+	}
+
+	if (c.button0 && !button0held)
+	{
+	// jump straight up
+		SD_PlaySound (JUMPSND);
+		ob->xspeed = 0;
+		ob->yspeed = -SPDJUMP;
+		ob->xmove = 0;
+		ob->ymove = 0;		// ob->yspeed*tics;			// DEBUG
+		jumptime = JUMPTIME;	// -tics;
+		ob->state = &s_keenjumpup1;
+		button0held = true;
+		return;
+	}
+
+	if (c.button1 && !button1held)
+	{
+	// throw current xdir
+		if (c.yaxis == -1)
+			ob->state = &s_keenthrowup1;
+		else
+			ob->state = &s_keenthrow1;
+		button1held = true;
+		return;
+	}
+
+	switch (c.yaxis)
+	{
+	case -1:
+		if (!CheckGrabPole(ob))			// try to go up/down pole first
+			ob->state = &s_keenlookup;
+		return;
+	case 0:
+		ob->state = &s_keenstand;
+		return;
+	case 1:
+		if (!CheckGrabPole(ob))			// try to go up/down pole first
+			ob->state = &s_keenduck;
+		return;
+	}
+}
+
+//===========================================================================
+
+/*
+=======================
+=
+= KeenPauseThink
+=
+= Do special animations in time
+=
+=======================
+*/
+
+void KeenPauseThink (objtype *ob)
+{
+	if (c.dir != dir_None || c.button0 || c.button1)
+	{
+		ob->temp1 = ob->temp2 = 0;			// not paused any more
+		ob->state = &s_keenstand;
+		KeenStandThink(ob);
+		return;
+	}
+
+	if ((ob->hitnorth & ~7) != 24)	// if it is 0 now, keen is standing on a sprite
+		ob->temp1 += tics;
+
+	switch (ob->temp2)
+	{
+	case 0:
+		if (ob->temp1 > 200)
+		{
+			ob->temp2++;
+			ob->state = &s_keenpauselook;
+			ob->temp1 = 0;
+		}
+		break;
+	case 1:
+		if (ob->temp1 > 300)
+		{
+			ob->temp2++;
+			ob->state = &s_keenwait1;
+			ob->temp1 = 0;
+		}
+		break;
+	case 2:
+		if (ob->temp1 > 700)
+		{
+			ob->temp2++;
+			ob->state = &s_keenyawn1;
+			ob->temp1 = 0;
+		}
+		break;
+	case 3:
+		if (ob->temp1 > 400)
+		{
+			ob->temp2++;
+			ob->state = &s_keenpauselook;
+			ob->temp1 = 0;
+		}
+		break;
+	case 4:
+		if (ob->temp1 > 700)
+		{
+			ob->temp2++;
+			ob->state = &s_keenyawn1;
+			ob->temp1 = 0;
+		}
+		break;
+	case 5:
+		if (ob->temp1 > 400)
+		{
+			ob->temp2++;
+			ob->state = &s_keengosleep1;
+		}
+		break;
+	}
+}
+
+
+//===========================================================================
+
+/*
+=======================
+=
+= KeenGoSleepThink
+=
+=======================
+*/
+
+void KeenGoSleepThink (objtype *ob)
+{
+//
+// spawn the zees above keens head
+//
+	GetNewObj (true);
+
+	new->obclass = inertobj;
+	new->x = player->x;
+	new->y = player->y-4*PIXGLOBAL;
+	new->xdir = 1;
+	new->ydir = -1;
+	NewState (new,&s_keenzee1);
+
+	ob->temp1 = (int)new;				// so they can be removed later
+}
+
+
+//===========================================================================
+
+/*
+=======================
+=
+= KeenSleepThink
+=
+=======================
+*/
+
+void KeenSleepThink (objtype *ob)
+{
+	if (c.dir != dir_None || c.button0 || c.button1)
+	{
+		if (ob->temp1 != (unsigned)&dummyobj)
+			RemoveObj ((objtype *)ob->temp1);	// remove the zees
+		ob->temp1 = ob->temp2 = 0;			// not paused any more
+		ob->state = &s_keengetup;
+	}
+}
+
+
+//===========================================================================
+
+/*
+=======================
+=
+= KeenDieThink
+=
+=======================
+*/
+
+void KeenDieThink (objtype *ob)
+{
+	ob++;			// shut up compiler
+	playstate = died;
+}
+
+
+//===========================================================================
+
+/*
+=======================
+=
+= KeenDuckThink
+=
+=======================
+*/
+
+void KeenDuckThink (objtype *ob)
+{
+	unsigned far *map, tile;
+	int midtile,bottomtile,move;
+
+	if (c.yaxis != 1)
+	{
+		ob->state = &s_keenstand;
+		KeenStandThink(ob);
+		return;
+	}
+
+	if (c.xaxis)
+		ob->xdir = c.xaxis;
+
+	if (c.button0 && !button0held)
+	{
+	//
+	// drop down a level
+	//
+		button0held = true;
+
+		midtile = (ob->tileleft + ob->tileright) >> 1;
+		bottomtile = ob->tilebottom;
+		map = (unsigned far *)mapsegs[1] + mapbwidthtable[bottomtile]/2
+			+ midtile;
+		tile = *map;
+		if (tinf[WESTWALL+tile] || tinf[EASTWALL+tile]
+			|| tinf[SOUTHWALL+tile])
+			return;				// wall prevents drop down
+
+		map += mapwidth;
+		tile = *map;
+		if (tinf[WESTWALL+tile] || tinf[EASTWALL+tile]
+			|| tinf[SOUTHWALL+tile])
+			return;				// wall prevents drop down
+
+		move = PIXGLOBAL*(tics<4 ? 4: tics);
+		ob->bottom += move;
+		ob->y += move;
+		ob->xmove = ob->ymove = 0;
+		ob->state = &s_keenjumpup3;
+		ob->temp2 = 1;		// allready in last stage
+		ob->xspeed = ob->yspeed = 0;
+		SD_PlaySound (PLUMMETSND);
+	}
+}
+
+
+//===========================================================================
+
+/*
+=======================
+=
+= KeenDropDownThink
+=
+=======================
+*/
+
+void KeenDropDownThink (objtype *ob)
+{
+	ob->needtoclip = true;
+}
+
+
+//===========================================================================
+
+/*
+=======================
+=
+= KeenWalkThink
+=
+=======================
+*/
+
+unsigned slopespeed[8] = {0,0,4,4,8,-4,-4,-8};
+
+void KeenWalkThink (objtype *ob)
+{
+	int move;
+
+	if (!c.xaxis)
+	{
+	//
+	// stopped running
+	//
+		KeenStandThink (ob);
+		return;
+	}
+
+	ob->xdir = c.xaxis;
+
+	if (c.yaxis && CheckGrabPole(ob))		// try to go up/down pole
+		return;
+
+	if (c.button0 && !button0held)
+	{
+	//
+	// running jump
+	//
+		SD_PlaySound (JUMPSND);
+		ob->xspeed = ob->xdir * SPDRUNJUMP;
+		ob->yspeed = -SPDJUMP;
+		ob->xmove = 0;
+		ob->ymove = 0;					// ob->yspeed*tics;
+		button0held = true;
+		jumptime = JUMPTIME; //-tics;		// DEBUG
+		ob->state = &s_keenjump1;
+	}
+
+	if (c.button1 && !button1held)
+	{
+	//
+	// throw
+	//
+		ob->state = &s_keenthrow1;
+		button1held = true;
+		return;
+	}
+
+	//
+	// give speed for slopes
+	//
+	move = tics*slopespeed[ob->hitnorth&7];
+
+	ob->xmove += move;
+}
+
+//===========================================================================
+
+/*
+=======================
+=
+= KeenAirThink
+=
+=======================
+*/
+
+void	KeenAirThink		(objtype *ob)
+{
+	if (jumptime)
+	{
+		if (jumptime<tics)
+		{
+			ob->ymove = ob->yspeed*jumptime;
+			jumptime = 0;
+		}
+		else
+		{
+			ob->ymove = ob->yspeed*tics;
+			if (!jumpcheat)
+				jumptime-=tics;
+		}
+		if (!c.button0)
+			jumptime = 0;
+
+		if (!jumptime)
+		{
+			ob->temp2 = 0;
+			ob->state = ob->state->nextstate;	// switch to second jump stage
+		}
+	}
+	else
+	{
+		DoGravity(ob);
+
+		if (ob->yspeed>0 && !ob->temp2)
+		{
+			ob->state = ob->state->nextstate;	// switch to third jump stage
+			ob->temp2 = 1;
+		}
+	}
+
+//-------------
+
+	if (c.xaxis)
+	{
+		ob->xdir = c.xaxis;
+		AccelerateX(ob,c.xaxis*2,MAXXSPEED);
+	}
+	else
+		FrictionX(ob);
+
+	if (c.button1 && !button1held)
+	{
+		button1held = true;
+	//
+	// throw
+	//
+		switch (c.yaxis)
+		{
+		case -1:
+			ob->state = &s_keenairthrowup1;
+			return;
+		case 0:
+			ob->state = &s_keenairthrow1;
+			return;
+		case 1:
+			ob->state = &s_keenairthrowdown1;
+			return;
+		}
+	}
+
+	if (ob->hitsouth==17)		// going through a pole hole
+	{
+		ob->xspeed = ob->xmove = 0;
+	}
+
+
+	if (c.yaxis == -1)
+		CheckGrabPole (ob);
+
+}
+
+//===========================================================================
+
+/*
+=======================
+=
+= KeenPoleThink
+=
+=======================
+*/
+
+int	polexspeed[3] = {-SPDPOLESIDEJUMP,0,SPDPOLESIDEJUMP};
+
+void	PoleActions (objtype *ob)
+{
+	if (c.xaxis)
+		ob->xdir = c.xaxis;
+
+	if (c.button0 && !button0held)		// jump off the pole
+	{
+		SD_PlaySound (JUMPSND);
+		ob->xspeed = polexspeed[c.xaxis+1];
+		ob->yspeed = -SPDPOLEJUMP;
+		ob->needtoclip = true;
+		jumptime = POLEJUMPTIME;
+		ob->state = &s_keenjump1;
+		ob->ydir = 1;
+		button0held = true;
+		leavepoletime = TimeCount;
+		return;
+	}
+
+	if (c.button1 && !button1held)
+	{
+		button1held = true;
+
+		switch (c.yaxis)
+		{
+		case -1:
+			ob->state = &s_keenpolethrowup1;
+			break;
+		case 0:
+			ob->state = &s_keenpolethrow1;
+			break;
+		case 1:
+			ob->state = &s_keenpolethrowdown1;
+			break;
+		}
+	}
+}
+
+
+void	KeenPoleThink		(objtype *ob)
+{
+	unsigned far *map, tile;
+
+	switch (c.yaxis)
+	{
+	case -1:
+		ob->state = &s_keenclimb1;
+		ob->ydir = -1;
+		return;
+
+	case 1:
+		ob->state = &s_keenslide1;
+		ob->ydir = 1;
+		KeenDropThink (ob);
+		return;
+	}
+
+	if (c.xaxis)
+	{
+	//
+	// walk off pole if right next to ground
+	//
+		map = mapsegs[1] + (mapbwidthtable[ob->tilebottom+1]/2 + ob->tilemidx);
+		tile = *map;
+		if (tinf[NORTHWALL+tile])
+		{
+			ob->xspeed = 0;
+			ob->yspeed = 0;
+			ob->needtoclip = true;
+			jumptime = 0;
+			ob->state = &s_keenjump3;
+			ob->ydir = 1;
+			SD_PlaySound (PLUMMETSND);
+			return;
+		}
+	}
+
+	PoleActions (ob);
+}
+
+
+/*
+=======================
+=
+= KeenClimbThink
+=
+=======================
+*/
+
+void	KeenClimbThink		(objtype *ob)
+{
+	unsigned far *map;
+
+	map = (unsigned _seg *)mapsegs[1]+mapbwidthtable[ob->tiletop]/2+ob->temp4;
+
+	if ((tinf[INTILE+*map]&0x7f) != 1)
+	{
+		ob->ymove=0;
+		ob->state = &s_keenpole;		// ran out of pole
+		return;
+	}
+
+	switch (c.yaxis)
+	{
+	case 0:
+		ob->state = &s_keenpole;
+		ob->ydir = 0;
+		break;
+
+	case 1:
+		ob->state = &s_keenslide1;
+		ob->ydir = 1;
+		KeenDropThink (ob);
+		break;
+	}
+
+	PoleActions (ob);
+}
+
+
+/*
+=======================
+=
+= KeenDropThink
+=
+=======================
+*/
+
+void	KeenDropThink		(objtype *ob)
+{
+	unsigned far *map;
+
+	map = (unsigned _seg *)mapsegs[1]+mapbwidthtable[ob->tilebottom]/2+ob->temp4;
+
+	if ((tinf[INTILE+*map]&0x7f) != 1)
+	{
+		SD_PlaySound (PLUMMETSND);
+		ob->state = &s_keenjump3;		// ran out of pole
+		jumptime = 0;
+		ob->temp2 = 1;
+		ob->xspeed = polexspeed[c.xaxis+1];
+		ob->yspeed = 0;
+		ob->needtoclip = true;
+		ob->tilebottom--;
+		return;
+	}
+
+	switch (c.yaxis)
+	{
+	case -1:
+		ob->state = &s_keenclimb1;
+		ob->ydir = -1;
+		break;
+
+	case 0:
+		ob->state = &s_keenpole;
+		ob->ydir = 0;
+		break;
+	}
+
+	PoleActions (ob);
+}
+
+//===========================================================================
+
+/*
+=======================
+=
+= KeenThrow
+=
+=======================
+*/
+
+void	KeenThrow	(objtype *ob)
+{
+// can't use &<var> in a switch statement...
+
+	if (ob->state == &s_keenthrow3)
+	{
+		if (ob->xdir > 0)
+			ThrowPower (ob->midx-4*PIXGLOBAL,ob->y+8*PIXGLOBAL,dir_East);
+		else
+			ThrowPower (ob->midx-4*PIXGLOBAL,ob->y+8*PIXGLOBAL,dir_West);
+		return;
+	}
+
+	if (ob->state == &s_keenpolethrow2)
+	{
+		if (ob->xdir > 0)
+			ThrowPower (ob->x+24*PIXGLOBAL,ob->y,dir_East);
+		else
+			ThrowPower (ob->x-8*PIXGLOBAL,ob->y,dir_West);
+		return;
+	}
+
+	if (ob->state == &s_keenthrowup2)
+	{
+		ThrowPower (ob->x+4*PIXGLOBAL,ob->y-8*PIXGLOBAL,dir_North);
+		return;
+	}
+
+	if (ob->state == &s_keenpolethrowup2)
+	{
+		ThrowPower (ob->x+8*PIXGLOBAL,ob->y-8*PIXGLOBAL,dir_North);
+		return;
+	}
+
+	if (ob->state == &s_keenpolethrowdown2)
+	{
+		ThrowPower (ob->x+8*PIXGLOBAL,ob->y+8*PIXGLOBAL,dir_South);
+		return;
+	}
+
+	if (ob->state == &s_keenairthrow2)
+	{
+		if (ob->xdir > 0)
+			ThrowPower (ob->midx-4*PIXGLOBAL,ob->y+8*PIXGLOBAL,dir_East);
+		else
+			ThrowPower (ob->midx-4*PIXGLOBAL,ob->y+8*PIXGLOBAL,dir_West);
+#if 0
+		if (ob->xdir > 0)
+			ThrowPower (ob->x+32*PIXGLOBAL,ob->y+8*PIXGLOBAL,dir_East);
+		else
+			ThrowPower (ob->x-16*PIXGLOBAL,ob->y+8*PIXGLOBAL,dir_West);
+#endif
+
+		new->xspeed += ob->xspeed/2;
+		new->yspeed += ob->yspeed/2;
+		return;
+	}
+
+	if (ob->state == &s_keenairthrowup2)
+	{
+		if (ob->xdir > 0)
+			ThrowPower (ob->x+16*PIXGLOBAL,ob->y,dir_North);
+		else
+			ThrowPower (ob->x+4*PIXGLOBAL,ob->y,dir_North);
+		new->xspeed += ob->xspeed/2;
+		new->yspeed += ob->yspeed/2;
+		return;
+	}
+
+	if (ob->state == &s_keenairthrowdown2)
+	{
+		if (ob->xdir > 0)
+			ThrowPower (ob->x+3*PIXGLOBAL,ob->y+16*PIXGLOBAL,dir_South);
+		else
+			ThrowPower (ob->x+12*PIXGLOBAL,ob->y+16*PIXGLOBAL,dir_South);
+		new->xspeed += ob->xspeed/2;
+		new->yspeed += ob->yspeed/2;
+		return;
+	}
+
+	Quit ("KeenThrow: Bad state!");
+}
+
+
+/*
+=============================================================================
+
+						CONTACT ROUTINES
+
+=============================================================================
+*/
+
+/*
+============================
+=
+= KillKeen
+=
+============================
+*/
+
+void KillKeen (void)
+{
+	if (!godmode)
+	{
+		SD_PlaySound (WAKEUPSND);
+		player->needtoclip = false;
+		ChangeState (player,&s_keendie1);
+	}
+}
+
+
+
+/*
+============================
+=
+= KeenContact
+=
+============================
+*/
+
+unsigned bonuspoints[6] = {100,200,500,1000,2000,5000};
+
+void	KeenContact (objtype *ob, objtype *hit)
+{
+	switch (hit->obclass)
+	{
+	case	bonusobj:
+		hit->obclass = inertobj;
+		switch (hit->temp1)
+		{
+		case 0:
+		case 1:
+		case 2:
+		case 3:
+		case 4:
+		case 5:
+			SD_PlaySound (GETPOINTSSND);
+			hit->shapenum = BONUS100SPR+hit->temp1;
+			GivePoints (bonuspoints[hit->temp1]);
+			ChangeState (hit,&s_bonusrise);
+			break;
+		case 6:
+			SD_PlaySound (EXTRAKEENSND);
+			hit->shapenum = BONUS1UPSPR;
+			gamestate.lives++;
+			ChangeState (hit,&s_bonusrise);
+			break;
+		case 7:
+			SD_PlaySound (EXTRAKEENSND);
+			hit->shapenum = BONUSSUPERSPR;
+			gamestate.lives+=3;
+			gamestate.flowerpowers+=8;
+			GivePoints (10000);
+			ChangeState (hit,&s_bonusrise);
+			break;
+		case 8:
+			SD_PlaySound (GETPOWERSND);
+			hit->shapenum = BONUSFLOWERSPR;
+			gamestate.flowerpowers++;
+			ChangeState (hit,&s_bonusrise);
+			break;
+		case 9:
+			SD_PlaySound (GETPOWERSND);
+			hit->shapenum = BONUSFLOWERUPSPR;
+			gamestate.flowerpowers+=5;
+			ChangeState (hit,&s_bonusrise);
+			break;
+		case 10:
+			SD_PlaySound (GETBOMBSND);
+			hit->shapenum = BONUSBOMBSPR;
+			gamestate.boobusbombs++;
+			gamestate.bombsthislevel++;
+			ChangeState (hit,&s_bonusrise);
+			break;
+		case 11:
+			SD_PlaySound (GETKEYSND);
+			hit->shapenum = BONUSKEYSPR;
+			gamestate.keys++;
+			ChangeState (hit,&s_bonusrise);
+			break;
+		}
+		break;
+
+	case	doorobj:
+		if (gamestate.keys)
+		{
+			if (hit->state != &s_doorraise)
+			{
+				SD_PlaySound (OPENDOORSND);
+				gamestate.keys--;
+				ChangeState (hit,&s_doorraise);
+			}
+		}
+		else
+		{
+			SD_PlaySound (NOWAYSND);
+			ClipToSpriteSide (ob,hit);
+		}
+		break;
+	case	taterobj:
+		if (hit->state == &s_taterattack2)
+			KillKeen ();
+		break;
+	case	carrotobj:
+		ClipToSpriteSide (ob,hit);
+		if (!ob->needtoclip)		// got pushed off a pole
+		{
+			SD_PlaySound (PLUMMETSND);
+			ob->needtoclip = true;
+			ob->xspeed = ob->yspeed = 0;
+			ChangeState(ob,&s_keenjump3);
+			ob->temp2 = 1;
+			jumptime = 0;
+		}
+		break;
+	case	cartobj:
+		ClipToSprite (ob,hit,true);
+		break;
+	case	broccoobj:
+		if (hit->state == &s_broccosmash3 || hit->state == &s_broccosmash4)
+			KillKeen ();
+		break;
+	case	squashobj:
+		if (hit->state == &s_squasherjump2)
+			KillKeen ();
+		else
+		{
+			ClipToSpriteSide (ob,hit);
+			if (!ob->needtoclip)		// got pushed off a pole
+			{
+				SD_PlaySound (PLUMMETSND);
+				ob->needtoclip = true;
+				ob->xspeed = ob->yspeed = 0;
+				ChangeState(ob,&s_keenjump3);
+				ob->temp2 = 1;
+				jumptime = 0;
+			}
+		}
+		break;
+	case	grapeobj:
+		if (hit->state == &s_grapefall)
+			KillKeen ();
+		break;
+	case	tomatobj:
+	case	celeryobj:
+	case	asparobj:
+	case	turnipobj:
+	case	cauliobj:
+	case	brusselobj:
+	case	mushroomobj:
+	case	apelobj:
+	case	peabrainobj:
+	case	boobusobj:
+	case	shotobj:
+			KillKeen ();
+		break;
+
+	}
+}
+
+
+/*
+=============================================================================
+
+						 REACTION ROUTINES
+
+=============================================================================
+*/
+
+
+/*
+============================
+=
+= KeenSimpleReact
+=
+============================
+*/
+
+void	KeenSimpleReact (objtype *ob)
+{
+	PLACESPRITE;
+}
+
+
+/*
+============================
+=
+= KeenStandReact
+=
+============================
+*/
+
+void	KeenStandReact (objtype *ob)
+{
+	if (!ob->hitnorth)
+	{
+	//
+	// walked off an edge
+	//
+		SD_PlaySound (PLUMMETSND);
+		ob->xspeed = ob->xdir*WALKAIRSPEED;
+		ChangeState (ob,&s_keenjump3);
+		ob->temp2 = 1;
+		jumptime = 0;
+	}
+	else if ( (ob->hitnorth & ~7) == 8)	// deadly floor!
+	{
+		KillKeen ();
+	}
+
+	PLACESPRITE;
+}
+
+/*
+============================
+=
+= KeenWalkReact
+=
+============================
+*/
+
+void	KeenWalkReact (objtype *ob)
+{
+	if (!ob->hitnorth)
+	{
+	//
+	// walked off an edge
+	//
+		ob->xspeed = ob->xdir*WALKAIRSPEED;
+		ob->yspeed = 0;
+		ChangeState (ob,&s_keenjump3);
+		ob->temp2 = 1;
+		jumptime = 0;
+	}
+	else if ( (ob->hitnorth & ~7) == 8)	// deadly floor!
+	{
+		KillKeen ();
+		goto placeit;
+	}
+
+	if (ob->hiteast == 2)			// doors
+	{
+
+	}
+	else if (ob->hitwest == 2)		// doors
+	{
+
+	}
+	else if (ob->hiteast || ob->hitwest)
+	{
+	//
+	// ran into a wall
+	//
+		ob->ticcount = 0;
+		ob->state = &s_keenstand;
+		ob->shapenum = ob->xdir == 1 ? s_keenstand.rightshapenum :
+			s_keenstand.leftshapenum;
+	}
+placeit:
+
+	PLACESPRITE;
+}
+
+
+/*
+============================
+=
+= KeenAirReact
+=
+============================
+*/
+
+void	KeenAirReact (objtype *ob)
+{
+	int x,y;
+	unsigned far *map,mapextra;
+
+	if (ob->hiteast || ob->hitwest)
+		ob->xspeed = 0;
+
+	map = mapsegs[1] + (mapbwidthtable[ob->tiletop]/2) + ob->tileleft;
+	mapextra = mapwidth - (ob->tileright - ob->tileleft+1);
+	for (y=ob->tiletop;y<=ob->tilebottom;y++,map+=mapextra)
+		for (x=ob->tileleft;x<=ob->tileright;x++,map++)
+			if (tinf[SOUTHWALL+*map] == 17)	// jumping up through a pole hole
+			{
+				ob->xspeed = 0;
+				ob->x = ob->tilemidx*TILEGLOBAL - 2*PIXGLOBAL;
+				goto checknorth;
+			}
+
+	if (ob->hitsouth)
+	{
+		if (ob->hitsouth == 17)	// jumping up through a pole hole
+		{
+			ob->y -= 32;
+			ob->top -= 32;
+			ob->xspeed = 0;
+			ob->x = ob->tilemidx*TILEGLOBAL - 2*PIXGLOBAL;
+		}
+		else
+		{
+			SD_PlaySound (HITHEADSND);
+
+			if (ob->hitsouth > 1)
+			{
+				ob->yspeed += 16;
+				if (ob->yspeed<0)	// push away from slopes
+					ob->yspeed = 0;
+			}
+			else
+				ob->yspeed = 0;
+			jumptime = 0;
+		}
+	}
+
+checknorth:
+	if (ob->hitnorth)
+	{
+		if (!(ob->hitnorth == 25 && jumptime))	// KLUDGE to allow jumping off
+		{										// sprites
+			ob->temp1 = ob->temp2 = 0;
+			ChangeState (ob,&s_keenstand);
+			SD_PlaySound (LANDSND);
+		}
+	}
+
+	PLACESPRITE;
+}
+
+/*
+============================
+=
+= KeenSlideReact
+=
+============================
+*/
+
+void	KeenSlideReact (objtype *ob)
+{
+	unsigned far *map;
+
+	if (ob->hitnorth)			// friction slow down
+	{
+		map = mapsegs[2] + (mapbwidthtable[ob->tiletop]/2 + ob->tileleft);
+		if (!tinf[SOUTHWALL+*map] && !tinf[SOUTHWALL+*(map+1)])
+			FrictionX(ob);
+	}
+
+
+	if (ob->hitwest || ob->hiteast || !ob->xspeed)
+		ChangeState (ob,&s_keengetup);
+
+	PLACESPRITE;
+}
+
diff --git a/src/lib/hb/kd_main.c b/src/lib/hb/kd_main.c
new file mode 100755
index 00000000..69a6d3e9
--- /dev/null
+++ b/src/lib/hb/kd_main.c
@@ -0,0 +1,533 @@
+/* Keen Dreams Source Code
+ * Copyright (C) 2014 Javier M. Chavez
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// KD_MAIN.C
+/*
+=============================================================================
+
+							KEEN DREAMS
+
+					An Id Software production
+
+=============================================================================
+*/
+
+#include "mem.h"
+#include "string.h"
+
+#include "KD_DEF.H"
+#pragma hdrstop
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+char		str[80],str2[20];
+boolean		singlestep,jumpcheat,godmode,tedlevel;
+unsigned	tedlevelnum;
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+void	DebugMemory (void);
+void	TestSprites(void);
+int		DebugKeys (void);
+void	ShutdownId (void);
+void	Quit (char *error);
+void	InitGame (void);
+void	main (void);
+
+//===========================================================================
+
+#if FRILLS
+
+/*
+==================
+=
+= DebugMemory
+=
+==================
+*/
+
+void DebugMemory (void)
+{
+	VW_FixRefreshBuffer ();
+	US_CenterWindow (16,7);
+
+	US_CPrint ("Memory Usage");
+	US_CPrint ("------------");
+	US_Print ("Total     :");
+	US_PrintUnsigned (mminfo.mainmem/1024);
+	US_Print ("k\nFree      :");
+	US_PrintUnsigned (MM_UnusedMemory()/1024);
+	US_Print ("k\nWith purge:");
+	US_PrintUnsigned (MM_TotalFree()/1024);
+	US_Print ("k\n");
+	VW_UpdateScreen();
+	IN_Ack ();
+#if GRMODE == EGAGR
+	MM_ShowMemory ();
+#endif
+}
+
+/*
+===================
+=
+= TestSprites
+=
+===================
+*/
+
+#define DISPWIDTH	110
+#define	TEXTWIDTH   40
+void TestSprites(void)
+{
+	int hx,hy,sprite,oldsprite,bottomy,topx,shift;
+	spritetabletype far *spr;
+	spritetype _seg	*block;
+	unsigned	mem,scan;
+
+
+	VW_FixRefreshBuffer ();
+	US_CenterWindow (30,17);
+
+	US_CPrint ("Sprite Test");
+	US_CPrint ("-----------");
+
+	hy=PrintY;
+	hx=(PrintX+56)&(~7);
+	topx = hx+TEXTWIDTH;
+
+	US_Print ("Chunk:\nWidth:\nHeight:\nOrgx:\nOrgy:\nXl:\nYl:\nXh:\nYh:\n"
+			  "Shifts:\nMem:\n");
+
+	bottomy = PrintY;
+
+	sprite = STARTSPRITES;
+	shift = 0;
+
+	do
+	{
+		if (sprite>=STARTTILE8)
+			sprite = STARTTILE8-1;
+		else if (sprite<STARTSPRITES)
+			sprite = STARTSPRITES;
+
+		spr = &spritetable[sprite-STARTSPRITES];
+		block = (spritetype _seg *)grsegs[sprite];
+
+		VWB_Bar (hx,hy,TEXTWIDTH,bottomy-hy,WHITE);
+
+		PrintX=hx;
+		PrintY=hy;
+		US_PrintUnsigned (sprite);US_Print ("\n");PrintX=hx;
+		US_PrintUnsigned (spr->width);US_Print ("\n");PrintX=hx;
+		US_PrintUnsigned (spr->height);US_Print ("\n");PrintX=hx;
+		US_PrintSigned (spr->orgx);US_Print ("\n");PrintX=hx;
+		US_PrintSigned (spr->orgy);US_Print ("\n");PrintX=hx;
+		US_PrintSigned (spr->xl);US_Print ("\n");PrintX=hx;
+		US_PrintSigned (spr->yl);US_Print ("\n");PrintX=hx;
+		US_PrintSigned (spr->xh);US_Print ("\n");PrintX=hx;
+		US_PrintSigned (spr->yh);US_Print ("\n");PrintX=hx;
+		US_PrintSigned (spr->shifts);US_Print ("\n");PrintX=hx;
+		if (!block)
+		{
+			US_Print ("-----");
+		}
+		else
+		{
+			mem = block->sourceoffset[3]+5*block->planesize[3];
+			mem = (mem+15)&(~15);		// round to paragraphs
+			US_PrintUnsigned (mem);
+		}
+
+		oldsprite = sprite;
+		do
+		{
+		//
+		// draw the current shift, then wait for key
+		//
+			VWB_Bar(topx,hy,DISPWIDTH,bottomy-hy,WHITE);
+			if (block)
+			{
+				PrintX = topx;
+				PrintY = hy;
+				US_Print ("Shift:");
+				US_PrintUnsigned (shift);
+				US_Print ("\n");
+				VWB_DrawSprite (topx+16+shift*2,PrintY,sprite);
+			}
+
+			VW_UpdateScreen();
+
+			scan = IN_WaitForKey ();
+
+			switch (scan)
+			{
+			case sc_UpArrow:
+				sprite++;
+				break;
+			case sc_DownArrow:
+				sprite--;
+				break;
+			case sc_LeftArrow:
+				if (--shift == -1)
+					shift = 3;
+				break;
+			case sc_RightArrow:
+				if (++shift == 4)
+					shift = 0;
+				break;
+			case sc_Escape:
+				return;
+			}
+
+		} while (sprite == oldsprite);
+
+  } while (1);
+
+
+}
+
+#endif
+
+
+/*
+================
+=
+= DebugKeys
+=
+================
+*/
+int DebugKeys (void)
+{
+	boolean esc;
+	int level;
+
+#if FRILLS
+	if (Keyboard[0x12] && ingame)	// DEBUG: end + 'E' to quit level
+	{
+		if (tedlevel)
+			TEDDeath();
+		playstate = levelcomplete;
+	}
+#endif
+
+	if (Keyboard[0x22] && ingame)		// G = god mode
+	{
+		VW_FixRefreshBuffer ();
+		US_CenterWindow (12,2);
+		if (godmode)
+		  US_PrintCentered ("God mode OFF");
+		else
+		  US_PrintCentered ("God mode ON");
+		VW_UpdateScreen();
+		IN_Ack();
+		godmode ^= 1;
+		return 1;
+	}
+	else if (Keyboard[0x17])			// I = item cheat
+	{
+		VW_FixRefreshBuffer ();
+		US_CenterWindow (12,3);
+		US_PrintCentered ("Free items!");
+		gamestate.boobusbombs=99;
+		gamestate.flowerpowers=99;
+		gamestate.keys=99;
+		VW_UpdateScreen();
+		IN_Ack ();
+		return 1;
+	}
+	else if (Keyboard[0x24])			// J = jump cheat
+	{
+		jumpcheat^=1;
+		VW_FixRefreshBuffer ();
+		US_CenterWindow (18,3);
+		if (jumpcheat)
+			US_PrintCentered ("Jump cheat ON");
+		else
+			US_PrintCentered ("Jump cheat OFF");
+		VW_UpdateScreen();
+		IN_Ack ();
+		return 1;
+	}
+#if FRILLS
+	else if (Keyboard[0x32])			// M = memory info
+	{
+		DebugMemory();
+		return 1;
+	}
+#endif
+	else if (Keyboard[0x19])			// P = pause with no screen disruptioon
+	{
+		IN_Ack();
+	}
+	else if (Keyboard[0x1f] && ingame)	// S = slow motion
+	{
+		singlestep^=1;
+		VW_FixRefreshBuffer ();
+		US_CenterWindow (18,3);
+		if (singlestep)
+			US_PrintCentered ("Slow motion ON");
+		else
+			US_PrintCentered ("Slow motion OFF");
+		VW_UpdateScreen();
+		IN_Ack ();
+		return 1;
+	}
+#if FRILLS
+	else if (Keyboard[0x14])			// T = sprite test
+	{
+		TestSprites();
+		return 1;
+	}
+#endif
+	else if (Keyboard[0x11] && ingame)	// W = warp to level
+	{
+		VW_FixRefreshBuffer ();
+		US_CenterWindow(26,3);
+		PrintY+=6;
+		US_Print("  Warp to which level(0-16):");
+		VW_UpdateScreen();
+		esc = !US_LineInput (px,py,str,NULL,true,2,0);
+		if (!esc)
+		{
+			level = atoi (str);
+			if (level>=0 && level<=16)
+			{
+				gamestate.mapon = level;
+				playstate = warptolevel;
+			}
+		}
+		return 1;
+	}
+	return 0;
+}
+
+//===========================================================================
+
+/*
+==========================
+=
+= ShutdownId
+=
+= Shuts down all ID_?? managers
+=
+==========================
+*/
+
+void ShutdownId (void)
+{
+  US_Shutdown ();
+  SD_Shutdown ();
+  IN_Shutdown ();
+  RF_Shutdown ();
+  VW_Shutdown ();
+  CA_Shutdown ();
+  MM_Shutdown ();
+}
+
+//===========================================================================
+
+/*
+==========================
+=
+= Quit
+=
+==========================
+*/
+
+void Quit (char *error)
+{
+  ShutdownId ();
+  if (error && *error)
+  {
+	clrscr();
+	puts(error);
+	puts("\n");
+	exit(1);
+  }
+	exit (0);
+}
+
+//===========================================================================
+
+/*
+==========================
+=
+= InitGame
+=
+= Load a few things right away
+=
+==========================
+*/
+
+#if 0
+#include "piracy.h"
+#endif
+
+void InitGame (void)
+{
+	int i;
+
+	MM_Startup ();
+
+
+#if 0
+	// Handle piracy screen...
+	//
+	movedata(FP_SEG(PIRACY),(unsigned)PIRACY,0xb800,displayofs,4000);
+	while ((bioskey(0)>>8) != sc_Return);
+#endif
+
+
+#if GRMODE == EGAGR
+	if (mminfo.mainmem < 335l*1024)
+	{
+#pragma	warn	-pro
+#pragma	warn	-nod
+		clrscr();			// we can't include CONIO because of a name conflict
+#pragma	warn	+nod
+#pragma	warn	+pro
+		puts ("There is not enough memory available to play the game reliably.  You can");
+		puts ("play anyway, but an out of memory condition will eventually pop up.  The");
+		puts ("correct solution is to unload some TSRs or rename your CONFIG.SYS and");
+		puts ("AUTOEXEC.BAT to free up more memory.\n");
+		puts ("Do you want to (Q)uit, or (C)ontinue?");
+		i = bioskey (0);
+		if ( (i>>8) != sc_C)
+			Quit ("");
+	}
+#endif
+
+	US_TextScreen();
+
+	VW_Startup ();
+	RF_Startup ();
+	IN_Startup ();
+	SD_Startup ();
+	US_Startup ();
+
+//	US_UpdateTextScreen();
+
+	CA_Startup ();
+	US_Setup ();
+
+//
+// load in and lock down some basic chunks
+//
+
+	CA_ClearMarks ();
+
+	CA_MarkGrChunk(STARTFONT);
+	CA_MarkGrChunk(STARTFONTM);
+	CA_MarkGrChunk(STARTTILE8);
+	CA_MarkGrChunk(STARTTILE8M);
+	for (i=KEEN_LUMP_START;i<=KEEN_LUMP_END;i++)
+		CA_MarkGrChunk(i);
+
+	CA_CacheMarks (NULL, 0);
+
+	MM_SetLock (&grsegs[STARTFONT],true);
+	MM_SetLock (&grsegs[STARTFONTM],true);
+	MM_SetLock (&grsegs[STARTTILE8],true);
+	MM_SetLock (&grsegs[STARTTILE8M],true);
+	for (i=KEEN_LUMP_START;i<=KEEN_LUMP_END;i++)
+		MM_SetLock (&grsegs[i],true);
+
+	CA_LoadAllSounds ();
+
+	fontcolor = WHITE;
+
+	US_FinishTextScreen();
+
+	VW_SetScreenMode (GRMODE);
+	VW_ClearVideo (BLACK);
+}
+
+
+
+//===========================================================================
+
+/*
+==========================
+=
+= main
+=
+==========================
+*/
+
+void main (void)
+{
+	short i;
+
+	if (stricmp(_argv[1], "/VER") == 0)
+	{
+		printf("\nKeen Dreams version 1.93  (Rev 1)\n");
+		printf("developed for use with 100%% IBM compatibles\n");
+		printf("that have 640K memory, DOS version 3.3 or later,\n");
+		printf("and an EGA or VGA display adapter.\n");
+		printf("Copyright 1991-1993 Softdisk Publishing.\n");
+		printf("Commander Keen is a trademark of Id Software.\n");
+		exit(0);
+	}
+
+	if (stricmp(_argv[1], "/?") == 0)
+	{
+		printf("\nKeen Dreams version 1.93\n");
+		printf("Copyright 1991-1993 Softdisk Publishing.\n\n");
+		printf("Commander Keen is a trademark of Id Software.\n");
+		printf("Type KDREAMS from the DOS prompt to run.\n\n");
+		printf("KDREAMS /COMP for SVGA compatibility mode\n");
+		printf("KDREAMS /NODR stops program hang with the drive still on\n");
+		printf("KDREAMS /NOAL disables AdLib and Sound Blaster detection\n");
+		printf("KDREAMS /NOSB disables Sound Blaster detection\n");
+		printf("KDREAMS /NOJOYS ignores joystick\n");
+		printf("KDREAMS /NOMOUSE ignores mouse\n");
+		printf("KDREAMS /HIDDENCARD overrides video card detection\n");
+		printf("KDREAMS /VER  for version and compatibility information\n");
+		printf("KDREAMS /? for this help information\n");
+		exit(0);
+	}
+
+	textcolor(7);
+	textbackground(0);
+
+	InitGame();
+
+	DemoLoop();					// DemoLoop calls Quit when everything is done
+	Quit("Demo loop exited???");
+}
+
diff --git a/src/lib/hb/kd_play.c b/src/lib/hb/kd_play.c
new file mode 100755
index 00000000..02bdccdb
--- /dev/null
+++ b/src/lib/hb/kd_play.c
@@ -0,0 +1,1928 @@
+/* Keen Dreams Source Code
+ * Copyright (C) 2014 Javier M. Chavez
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+// KD_PLAY.C
+
+#include "KD_DEF.H"
+#pragma	hdrstop
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+#define	INACTIVATEDIST	10
+
+#define MAXMOVE	(TILEGLOBAL-17)
+
+#define NUMLUMPS	22
+
+
+#define CONTROLSLUMP	0
+#define KEENLUMP		1
+#define WORLDKEENLUMP	2
+#define BROCCOLUMP		3
+#define	TOMATLUMP       4
+#define	CARROTLUMP		5
+#define	ASPARLUMP		6
+#define	GRAPELUMP		7
+#define	TATERLUMP		8
+#define	CARTLUMP		9
+#define	FRENCHYLUMP		10
+#define	MELONLUMP		11
+#define	SQUASHLUMP		12
+#define	APELLUMP		13
+#define	PEALUMP			14
+#define	BOOBUSLUMP		15
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+exittype	playstate;
+gametype	gamestate;
+
+boolean		button0held,button1held;
+objtype		*new,*check,*player,*scoreobj;
+
+unsigned	originxtilemax,originytilemax;
+
+ControlInfo	c;
+
+objtype dummyobj;
+
+char		*levelnames[21] =
+{
+"The Land of Tuberia",
+"Horseradish Hill",
+"The Melon Mines",
+"Bridge Bottoms",
+"Rhubarb Rapids",
+"Parsnip Pass",
+"Level 6",
+"Spud City",
+"Level 8",
+"Apple Acres",
+"Grape Grove",
+"Level 11",
+"Brussels Sprout Bay",
+"Level 13",
+"Squash Swamp",
+"Boobus' Chamber",
+"Castle Tuberia",
+"",
+"Title Page"
+};
+
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+// for asm scaning of map planes
+unsigned	mapx,mapy,mapxcount,mapycount,maptile,mapspot;
+
+int			plummet;
+
+int			objectcount;
+
+objtype		objarray[MAXACTORS],*lastobj,*objfreelist;
+
+int			oldtileleft,oldtiletop,oldtileright,oldtilebottom,oldtilemidx;
+int			oldleft,oldtop,oldright,oldbottom,oldmidx;
+int			leftmoved,topmoved,rightmoved,bottommoved,midxmoved;
+
+int			topmove,bottommove,midxmove;
+
+int			inactivateleft,inactivateright,inactivatetop,inactivatebottom;
+
+int			fadecount;
+
+boolean		bombspresent;
+
+boolean		lumpneeded[NUMLUMPS];
+int			lumpstart[NUMLUMPS] =
+{
+CONTROLS_LUMP_START,
+KEEN_LUMP_START,
+WORLDKEEN_LUMP_START,
+BROCCOLASH_LUMP_START,
+TOMATO_LUMP_START,
+CARROT_LUMP_START,
+ASPAR_LUMP_START,
+GRAPE_LUMP_START,
+TATER_LUMP_START,
+CANTA_LUMP_START,
+FRENCHY_LUMP_START,
+MELONLIPS_LUMP_START,
+SQUASHER_LUMP_START,
+APEL_LUMP_START,
+PEAS_LUMP_START,
+BOOBUS_LUMP_START,
+};
+
+int			lumpend[NUMLUMPS] =
+{
+CONTROLS_LUMP_END,
+KEEN_LUMP_END,
+WORLDKEEN_LUMP_END,
+BROCCOLASH_LUMP_END,
+TOMATO_LUMP_END,
+CARROT_LUMP_END,
+ASPAR_LUMP_END,
+GRAPE_LUMP_END,
+TATER_LUMP_END,
+CANTA_LUMP_END,
+FRENCHY_LUMP_END,
+MELONLIPS_LUMP_END,
+SQUASHER_LUMP_END,
+APEL_LUMP_END,
+PEAS_LUMP_END,
+BOOBUS_LUMP_END,
+};
+
+
+void	CheckKeys (void);
+void	CalcInactivate (void);
+void 	InitObjArray (void);
+void 	GetNewObj (boolean usedummy);
+void	RemoveObj (objtype *gone);
+void 	ScanInfoPlane (void);
+void 	PatchWorldMap (void);
+void 	MarkTileGraphics (void);
+void 	FadeAndUnhook (void);
+void 	SetupGameLevel (boolean loadnow);
+void 	ScrollScreen (void);
+void 	MoveObjVert (objtype *ob, int ymove);
+void 	MoveObjHoriz (objtype *ob, int xmove);
+void 	GivePoints (unsigned points);
+void 	ClipToEnds (objtype *ob);
+void 	ClipToEastWalls (objtype *ob);
+void 	ClipToWestWalls (objtype *ob);
+void 	ClipToWalls (objtype *ob);
+void 	ClipToSpriteSide (objtype *push, objtype *solid);
+void 	ClipToSprite (objtype *push, objtype *solid, boolean squish);
+int 	DoActor (objtype *ob,int tics);
+void 	StateMachine (objtype *ob);
+void 	NewState (objtype *ob,statetype *state);
+void 	PlayLoop (void);
+void 	GameLoop (void);
+
+//===========================================================================
+
+/*
+=====================
+=
+= CheckKeys
+=
+=====================
+*/
+
+void CheckKeys (void)
+{
+	if (screenfaded)			// don't do anything with a faded screen
+		return;
+
+//
+// space for status screen
+//
+	if (Keyboard[sc_Space])
+	{
+		StatusWindow ();
+		IN_ClearKeysDown();
+		RF_ForceRefresh();
+		lasttimecount = TimeCount;
+	}
+
+//
+// pause key wierdness can't be checked as a scan code
+//
+	if (Paused)
+	{
+		VW_FixRefreshBuffer ();
+		US_CenterWindow (8,3);
+		US_PrintCentered ("PAUSED");
+		VW_UpdateScreen ();
+		IN_Ack();
+		RF_ForceRefresh ();
+		Paused = false;
+	}
+
+//
+// F1-F7/ESC to enter control panel
+//
+	if ( (LastScan >= sc_F1 && LastScan <= sc_F7) || LastScan == sc_Escape)
+	{
+		VW_FixRefreshBuffer ();
+		US_CenterWindow (20,8);
+		US_CPrint ("Loading");
+		VW_UpdateScreen ();
+		US_ControlPanel();
+		IN_ClearKeysDown();
+		if (restartgame)
+			playstate = resetgame;
+		else if (!loadedgame)
+			RF_ForceRefresh();		// don't refresh if loading a new game
+
+		lasttimecount = TimeCount;
+	}
+
+//
+// F10-? debug keys
+//
+	if (Keyboard[sc_F10] && DebugKeys() )
+	{
+		RF_ForceRefresh();
+		lasttimecount = TimeCount;
+	}
+
+}
+
+//===========================================================================
+
+
+/*
+=======================
+=
+= CalcInactivate
+=
+=======================
+*/
+
+void CalcInactivate (void)
+{
+	originxtilemax = originxtile+PORTTILESWIDE-1;
+	originytilemax = originytile+PORTTILESHIGH-1;
+
+	inactivateleft = originxtile-INACTIVATEDIST;
+	if (inactivateleft < 0)
+		inactivateleft = 0;
+	inactivateright = originxtilemax+INACTIVATEDIST;
+	inactivatetop = originytile-INACTIVATEDIST;
+	if (inactivatetop < 0)
+		inactivatetop = 0;
+	inactivatebottom = originytilemax+INACTIVATEDIST;
+}
+
+
+//===========================================================================
+
+
+/*
+#############################################################################
+
+				  The objarray data structure
+
+#############################################################################
+
+Objarray containt structures for every actor currently playing.  The structure
+is accessed as a linked list starting at *player, ending when ob->next ==
+NULL.  GetNewObj inserts a new object at the end of the list, meaning that
+if an actor spawn another actor, the new one WILL get to think and react the
+same frame.  RemoveObj unlinks the given object and returns it to the free
+list, but does not damage the objects ->next pointer, so if the current object
+removes itself, a linked list following loop can still safely get to the
+next element.
+
+<backwardly linked free list>
+
+#############################################################################
+*/
+
+
+/*
+=========================
+=
+= InitObjArray
+=
+= Call to clear out the entire object list, returning them all to the free
+= list.  Allocates a special spot for the player.
+=
+=========================
+*/
+
+void InitObjArray (void)
+{
+	int	i;
+
+	for (i=0;i<MAXACTORS;i++)
+	{
+		objarray[i].prev = &objarray[i+1];
+		objarray[i].next = NULL;
+	}
+
+	objarray[MAXACTORS-1].prev = NULL;
+
+	objfreelist = &objarray[0];
+	lastobj = NULL;
+
+	objectcount = 0;
+
+//
+// give the player and score the first free spots
+//
+	GetNewObj (false);
+	player = new;
+	GetNewObj (false);
+	scoreobj = new;
+}
+
+//===========================================================================
+
+/*
+=========================
+=
+= GetNewObj
+=
+= Sets the global variable new to point to a free spot in objarray.
+= The free spot is inserted at the end of the liked list
+=
+= When the object list is full, the caller can either have it bomb out ot
+= return a dummy object pointer that will never get used
+=
+=========================
+*/
+
+void GetNewObj (boolean usedummy)
+{
+	if (!objfreelist)
+	{
+		if (usedummy)
+		{
+			new = &dummyobj;
+			return;
+		}
+		Quit ("GetNewObj: No free spots in objarray!");
+	}
+
+	new = objfreelist;
+	objfreelist = new->prev;
+	memset (new,0,sizeof(*new));
+
+	if (lastobj)
+		lastobj->next = new;
+	new->prev = lastobj;	// new->next is allready NULL from memset
+
+	new->active = yes;
+	new->needtoclip = true;
+	lastobj = new;
+
+	objectcount++;
+}
+
+//===========================================================================
+
+/*
+=========================
+=
+= RemoveObj
+=
+= Add the given object back into the free list, and unlink it from it's
+= neighbors
+=
+=========================
+*/
+
+void RemoveObj (objtype *gone)
+{
+	if (gone == player)
+		Quit ("RemoveObj: Tried to remove the player!");
+
+//
+// erase it from the refresh manager
+//
+	RF_RemoveSprite (&gone->sprite);
+
+//
+// fix the next object's back link
+//
+	if (gone == lastobj)
+		lastobj = (objtype *)gone->prev;
+	else
+		gone->next->prev = gone->prev;
+
+//
+// fix the previous object's forward link
+//
+	gone->prev->next = gone->next;
+
+//
+// add it back in to the free list
+//
+	gone->prev = objfreelist;
+	objfreelist = gone;
+}
+
+//===========================================================================
+
+
+void near HandleInfo (void)
+{
+	switch (maptile)
+	{
+	case 1:
+		SpawnKeen(mapx,mapy,1);
+		break;
+	case 2:
+		SpawnKeen(mapx,mapy,-1);
+		break;
+	case 19:
+		SpawnWorldKeen(mapx,mapy);
+		lumpneeded[WORLDKEENLUMP] = true;
+		break;
+
+	case 31:
+		bombspresent = true;
+	case 21:
+	case 22:
+	case 23:
+	case 24:
+	case 25:
+	case 26:
+	case 27:
+	case 28:
+	case 29:
+	case 30:
+	case 32:
+		SpawnBonus(mapx,mapy,maptile-21);
+		new->active = false;
+		break;
+	case 33:
+		SpawnDoor(mapx,mapy);
+		new->active = false;
+		break;
+	case 41:
+		SpawnBrocco(mapx,mapy);
+		new->active = false;
+		lumpneeded[BROCCOLUMP] = true;
+		break;
+	case 42:
+		SpawnTomat(mapx,mapy);
+		new->active = false;
+		lumpneeded[TOMATLUMP] = true;
+		break;
+	case 43:
+		SpawnCarrot(mapx,mapy);
+		new->active = false;
+		lumpneeded[CARROTLUMP] = true;
+		break;
+	case 45:
+		SpawnAspar(mapx,mapy);
+		new->active = false;
+		lumpneeded[ASPARLUMP] = true;
+		break;
+	case 46:
+		SpawnGrape(mapx,mapy);
+		new->active = false;
+		lumpneeded[GRAPELUMP] = true;
+		break;
+	case 47:
+		SpawnTater(mapx,mapy);
+		new->active = false;
+		lumpneeded[TATERLUMP] = true;
+		break;
+	case 48:
+		SpawnCart(mapx,mapy);
+		lumpneeded[CARTLUMP] = true;
+		break;
+	case 49:
+		SpawnFrenchy(mapx,mapy);
+		new->active = false;
+		lumpneeded[FRENCHYLUMP] = true;
+		break;
+	case 50:
+	case 51:
+	case 52:
+		SpawnMelon(mapx,mapy,maptile-50);
+		new->active = false;
+		lumpneeded[MELONLUMP] = true;
+		break;
+	case 57:
+		SpawnSquasher(mapx,mapy);
+		new->active = false;
+		lumpneeded[SQUASHLUMP] = true;
+		break;
+	case 58:
+		SpawnApel(mapx,mapy);
+		new->active = false;
+		lumpneeded[APELLUMP] = true;
+		break;
+	case 59:
+		SpawnPeaPod(mapx,mapy);
+		new->active = false;
+		lumpneeded[PEALUMP] = true;
+		break;
+	case 60:
+		SpawnPeaBrain(mapx,mapy);
+		new->active = false;
+		lumpneeded[PEALUMP] = true;
+		break;
+	case 61:
+		SpawnBoobus(mapx,mapy);
+		lumpneeded[BOOBUSLUMP] = true;
+		break;
+	}
+
+	if (new->active != allways)
+		new->active = false;
+}
+
+/*
+==========================
+=
+= ScanInfoPlane
+=
+= Spawn all actors and mark down special places
+=
+==========================
+*/
+
+void ScanInfoPlane (void)
+{
+	unsigned	x,y,i,j;
+	int			tile;
+	unsigned	far	*start;
+
+	InitObjArray();			// start spawning things with a clean slate
+
+	memset (lumpneeded,0,sizeof(lumpneeded));
+
+#if 0
+	start = mapsegs[2];
+	for (y=0;y<mapheight;y++)
+		for (x=0;x<mapwidth;x++)
+		{
+			tile = *start++;
+			if (!tile)
+				continue;
+		}
+#endif
+
+//
+// This doesn't really need to be in asm.  I thought it was a bottleneck,
+// but I was wrong...
+//
+
+	asm	mov	es,[WORD PTR mapsegs+4]
+	asm	xor	si,si
+	asm	mov	[mapy],0
+	asm	mov	ax,[mapheight]
+	asm	mov	[mapycount],ax
+yloop:
+	asm	mov	[mapx],0
+	asm	mov	ax,[mapwidth]
+	asm	mov	[mapxcount],ax
+xloop:
+	asm	mov	ax,[es:si]
+	asm	or	ax,ax
+	asm	jz	nothing
+	asm	mov	[maptile],ax
+	HandleInfo ();						// si is saved
+	asm	mov	es,[WORD PTR mapsegs+4]
+nothing:
+	asm	inc	[mapx]
+	asm	add	si,2
+	asm	dec	[mapxcount]
+	asm	jnz	xloop
+	asm	inc	[mapy]
+	asm	dec	[mapycount]
+	asm	jnz	yloop
+
+	for (i=0;i<NUMLUMPS;i++)
+		if (lumpneeded[i])
+			for (j=lumpstart[i];j<=lumpend[i];j++)
+				CA_MarkGrChunk(j);
+}
+
+//===========================================================================
+
+
+/*
+==========================
+=
+= PatchWorldMap
+=
+= Takes out blocking squares and puts in dones
+=
+==========================
+*/
+
+void PatchWorldMap (void)
+{
+	unsigned	size,spot,info,foreground;
+
+	size = mapwidth*mapheight;
+	spot = 0;
+	do
+	{
+		info = *(mapsegs[2] + spot);
+		// finished a city here?
+		if (info>=3 && info<=18 && gamestate.leveldone[info-2])
+		{
+			*(mapsegs[2] + spot) = 0;
+			foreground = *(mapsegs[1] + spot);
+			if (foreground == 130)
+				*(mapsegs[1]+spot) = 0;	// not blocking now
+			else if (foreground == 90)
+			{
+			// plant done flag
+				*(mapsegs[1]+spot) = 133;
+				*(mapsegs[1]+(spot-mapwidth-1)) = 131;
+				*(mapsegs[1]+(spot-mapwidth)) = 132;
+			}
+		}
+		spot++;
+	} while (spot<size);
+}
+
+//===========================================================================
+
+/*
+==========================
+=
+= FadeAndUnhook
+=
+= Latch this onto the refresh so the screen only gets faded in after two
+= refreshes.  This lets all actors draw themselves to both pages before
+= fading the screen in.
+=
+==========================
+*/
+
+void FadeAndUnhook (void)
+{
+	if (++fadecount==2)
+	{
+		RF_ForceRefresh();
+		VW_FadeIn ();
+		RF_SetRefreshHook (NULL);
+		lasttimecount = TimeCount;	// don't adaptively time the fade
+	}
+}
+
+//===========================================================================
+
+
+/*
+==========================
+=
+= SetupGameLevel
+=
+= Load in map mapon and cache everything needed for it
+=
+==========================
+*/
+
+void 	SetupGameLevel (boolean loadnow)
+{
+	long	orgx,orgy;
+
+	bombspresent = false;
+//
+// load the level header and three map planes
+//
+	CA_CacheMap (gamestate.mapon);
+
+//
+// let the refresh manager set up some variables
+//
+	RF_NewMap ();
+
+//
+// decide which graphics are needed and spawn actors
+//
+	CA_ClearMarks ();
+
+	if (!mapon)
+		PatchWorldMap ();
+
+	if (mapon!=20)			// map 20 is the title screen
+		ScanInfoPlane ();
+	RF_MarkTileGraphics ();
+
+//
+// have the caching manager load and purge stuff to make sure all marks
+// are in memory
+//
+	if (loadnow)
+	{
+		if (bombspresent)
+		{
+			VW_FixRefreshBuffer ();
+			US_DrawWindow (10,1,20,2);
+			US_PrintCentered ("Boobus Bombs Near!");
+			VW_UpdateScreen ();
+		}
+		CA_CacheMarks (levelnames[mapon], 0);
+	}
+
+#if 0
+	VW_FixRefreshBuffer ();
+	US_CenterWindow (20,8);
+	US_Print ("\n\n\nObject count:");
+	itoa (objectcount,str,10);
+	US_Print (str);
+	VW_UpdateScreen ();
+	IN_Ack ();
+#endif
+
+	if (mapon!=20 && loadnow)			// map 20 is the title screen
+	{
+		VW_FadeOut ();
+		fadecount = 0;
+		RF_SetRefreshHook (&FadeAndUnhook);
+		SpawnScore ();
+
+//
+// start the initial view position to center the player
+//
+		orgx = (long)player->x - (150<<G_P_SHIFT);
+		orgy = (long)player->y-(84<<G_P_SHIFT);
+		if (orgx<0)
+			orgx=0;
+		if (orgy<0)
+			orgy=0;
+
+		RF_NewPosition (orgx,orgy);
+		CalcInactivate ();
+	}
+
+
+}
+
+//==========================================================================
+
+/*
+===============
+=
+= ScrollScreen
+=
+= Scroll if Keen is nearing an edge
+= Set playstate to levelcomplete
+=
+===============
+*/
+
+void ScrollScreen (void)
+{
+	int	xscroll,yscroll;
+
+//
+// walked off edge of map?
+//
+	if (player->left < originxmin
+	|| player->right > originxmax+20*TILEGLOBAL)
+	{
+		playstate = levelcomplete;
+		return;
+	}
+
+//
+// fallen off bottom of world?
+//
+	if (!plummet && player->bottom > originymax+13*TILEGLOBAL)
+	{
+		godmode = 0;
+		plummet = 1;
+		KillKeen ();
+		return;
+	}
+
+	if (player->x < originxglobal+SCROLLWEST)
+		xscroll = player->x - (originxglobal+SCROLLWEST);
+	else if (player->x > originxglobal+SCROLLEAST)
+		xscroll = player->x - (originxglobal+SCROLLEAST);
+	else
+		xscroll = 0;
+
+	if (player->y < originyglobal+SCROLLNORTH)
+		yscroll = player->y - (originyglobal+SCROLLNORTH);
+	else if (player->y > originyglobal+SCROLLSOUTH)
+		yscroll = player->y - (originyglobal+SCROLLSOUTH);
+	else yscroll = 0;
+
+	if (xscroll || yscroll)
+	{
+		RF_Scroll (xscroll,yscroll);
+		CalcInactivate ();
+		scoreobj->needtoreact = true;
+	}
+}
+
+//==========================================================================
+
+/*
+====================
+=
+= GivePoints
+=
+= Grants extra men at 20k,40k,80k,160k,320k
+=
+====================
+*/
+
+void GivePoints (unsigned points)
+{
+	gamestate.score += points;
+	if (gamestate.score >= gamestate.nextextra)
+	{
+		SD_PlaySound (EXTRAKEENSND);
+		gamestate.lives++;
+		gamestate.nextextra*=2;
+	}
+}
+
+
+//==========================================================================
+
+/*
+====================
+=
+= MoveObjVert
+=
+====================
+*/
+
+void MoveObjVert (objtype *ob, int ymove)
+{
+	ob->y += ymove;
+	ob->top += ymove;
+	ob->bottom += ymove;
+	ob->tiletop = ob->top >> G_T_SHIFT;
+	ob->tilebottom = ob->bottom >> G_T_SHIFT;
+}
+
+
+/*
+====================
+=
+= MoveObjHoriz
+=
+====================
+*/
+
+void MoveObjHoriz (objtype *ob, int xmove)
+{
+	ob->x += xmove;
+	ob->left += xmove;
+	ob->right += xmove;
+	ob->tileleft = ob->left >> G_T_SHIFT;
+	ob->tileright = ob->right >> G_T_SHIFT;
+}
+
+
+/*
+=============================================================================
+
+					Actor to tile clipping rouitnes
+
+=============================================================================
+*/
+
+// walltype / x coordinate (0-15)
+
+int	wallclip[8][16] = {			// the height of a given point in a tile
+{ 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256},
+{   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0},
+{   0,0x08,0x10,0x18,0x20,0x28,0x30,0x38,0x40,0x48,0x50,0x58,0x60,0x68,0x70,0x78},
+{0x80,0x88,0x90,0x98,0xa0,0xa8,0xb0,0xb8,0xc0,0xc8,0xd0,0xd8,0xe0,0xe8,0xf0,0xf8},
+{   0,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,0xa0,0xb0,0xc0,0xd0,0xe0,0xf0},
+{0x78,0x70,0x68,0x60,0x58,0x50,0x48,0x40,0x38,0x30,0x28,0x20,0x18,0x10,0x08,   0},
+{0xf8,0xf0,0xe8,0xe0,0xd8,0xd0,0xc8,0xc0,0xb8,0xb0,0xa8,0xa0,0x98,0x90,0x88,0x80},
+{0xf0,0xe0,0xd0,0xc0,0xb0,0xa0,0x90,0x80,0x70,0x60,0x50,0x40,0x30,0x20,0x10,   0}
+};
+
+// assignment within ifs are used heavily here, so turn off the warning
+#pragma warn -pia
+
+/*
+===========================
+=
+= ClipToEnds
+=
+===========================
+*/
+
+void ClipToEnds (objtype *ob)
+{
+	unsigned	far *map,tile,facetile,info,wall;
+	int	leftpix,rightpix,midtiles,toppix,bottompix;
+	int	x,y,clip,move,totalmove,maxmove,midxpix;
+
+	midxpix = (ob->midx&0xf0) >> 4;
+
+	maxmove = -abs(midxmoved) - bottommoved - 16;
+	map = (unsigned far *)mapsegs[1] +
+		mapbwidthtable[oldtilebottom-1]/2 + ob->tilemidx;
+	for (y=oldtilebottom-1 ; y<=ob->tilebottom ; y++,map+=mapwidth)
+	{
+		if (wall = tinf[NORTHWALL+*map])
+		{
+			clip = wallclip[wall&7][midxpix];
+			move = ( (y<<G_T_SHIFT)+clip - 1) - ob->bottom;
+			if (move<0 && move>=maxmove)
+			{
+				ob->hitnorth = wall;
+				MoveObjVert (ob,move);
+				return;
+			}
+		}
+	}
+
+	maxmove = abs(midxmoved) - topmoved + 16;
+	map = (unsigned far *)mapsegs[1] +
+		mapbwidthtable[oldtiletop+1]/2 + ob->tilemidx;
+	for (y=oldtiletop+1 ; y>=ob->tiletop ; y--,map-=mapwidth)
+	{
+		if (wall = tinf[SOUTHWALL+*map])
+		{
+			clip = wallclip[wall&7][midxpix];
+			move = ( ((y+1)<<G_T_SHIFT)-clip ) - ob->top;
+			if (move > 0 && move<=maxmove)
+			{
+				totalmove = ob->ymove + move;
+				if (totalmove < TILEGLOBAL && totalmove > -TILEGLOBAL)
+				{
+					ob->hitsouth = wall;
+					MoveObjVert (ob,move);
+				}
+			}
+		}
+	}
+}
+
+
+/*
+===========================
+=
+= ClipToEastWalls / ClipToWestWalls
+=
+===========================
+*/
+
+void ClipToEastWalls (objtype *ob)
+{
+	int			y,move,top,bottom;
+	unsigned	far *map,tile,info,wall;
+
+	// clip to east walls if moving west
+
+	top = ob->tiletop;
+	if (ob->hitsouth>1)
+		top++;			// on a slope inside a tile
+	bottom = ob->tilebottom;
+	if (ob->hitnorth>1)
+		bottom--;			// on a slope inside a tile
+
+	for (y=top;y<=bottom;y++)
+	{
+		map = (unsigned far *)mapsegs[1] +
+			mapbwidthtable[y]/2 + ob->tileleft;
+
+		if (ob->hiteast = tinf[EASTWALL+*map])
+		{
+			move = ( (ob->tileleft+1)<<G_T_SHIFT ) - ob->left;
+			MoveObjHoriz (ob,move);
+			return;
+		}
+	}
+}
+
+
+void ClipToWestWalls (objtype *ob)
+{
+	int			y,move,top,bottom;
+	unsigned	far *map,tile,info,wall;
+
+	// check west walls if moving east
+
+	top = ob->tiletop;
+	if (ob->hitsouth>1)
+		top++;			// on a slope inside a tile
+	bottom = ob->tilebottom;
+	if (ob->hitnorth>1)
+		bottom--;			// on a slope inside a tile
+
+	for (y=top;y<=bottom;y++)
+	{
+		map = (unsigned far *)mapsegs[1] +
+			mapbwidthtable[y]/2 + ob->tileright;
+
+		if (ob->hitwest = tinf[WESTWALL+*map])
+		{
+			move = ( (ob->tileright<<G_T_SHIFT ) -1) - ob->right;
+			MoveObjHoriz (ob,move);
+			return;
+		}
+	}
+}
+
+// turn 'possibly incorrect assignment' warnings back on
+#pragma warn +pia
+
+
+//==========================================================================
+
+/*
+================
+=
+= ClipToWalls
+=
+= Moves the current object xmove/ymove units, clipping to walls
+=
+================
+*/
+
+void ClipToWalls (objtype *ob)
+{
+	unsigned	x,y,tile;
+	spritetabletype	far *shape;
+	boolean	endfirst;
+
+//
+// make sure it stays in contact with a 45 degree slope
+//
+	if (ob->state->pushtofloor)
+	{
+		if (ob->xmove > 0)
+			ob->ymove = ob->xmove + 16;
+		else
+			ob->ymove = -ob->xmove + 16;
+	}
+
+//
+// move the shape
+//
+	if (ob->xmove > MAXMOVE)
+		ob->xmove = MAXMOVE;
+	else if (ob->xmove < -MAXMOVE)
+		ob->xmove = -MAXMOVE;
+
+	if (ob->ymove > MAXMOVE+16)			// +16 for push to floor
+		ob->ymove = MAXMOVE+16;
+	else if (ob->ymove < -MAXMOVE)
+		ob->ymove = -MAXMOVE;
+
+	ob->x += ob->xmove;
+	ob->y += ob->ymove;
+
+	ob->needtoreact = true;
+
+	if (!ob->shapenum)				// can't get a hit rect with no shape!
+		return;
+
+	shape = &spritetable[ob->shapenum-STARTSPRITES];
+
+	oldtileright = ob->tileright;
+	oldtiletop = ob->tiletop;
+	oldtileleft = ob->tileleft;
+	oldtilebottom = ob->tilebottom;
+	oldtilemidx = ob->tilemidx;
+
+	oldright = ob->right;
+	oldtop = ob->top;
+	oldleft = ob->left;
+	oldbottom = ob->bottom;
+	oldmidx = ob->midx;
+
+	ob->left = ob->x + shape->xl;
+	ob->right = ob->x + shape->xh;
+	ob->top = ob->y + shape->yl;
+	ob->bottom = ob->y + shape->yh;
+	ob->midx = ob->left + (ob->right - ob->left)/2;
+
+	ob->tileleft = ob->left >> G_T_SHIFT;
+	ob->tileright = ob->right >> G_T_SHIFT;
+	ob->tiletop = ob->top >> G_T_SHIFT;
+	ob->tilebottom = ob->bottom >> G_T_SHIFT;
+	ob->tilemidx = ob->midx >> G_T_SHIFT;
+
+	ob->hitnorth = ob->hiteast = ob->hitsouth = ob->hitwest = 0;
+
+	if (!ob->needtoclip)
+		return;
+
+	leftmoved = ob->left - oldleft;
+	rightmoved = ob->right - oldright;
+	topmoved = ob->top - oldtop;
+	bottommoved = ob->bottom - oldbottom;
+	midxmoved = ob->midx - oldmidx;
+
+//
+// clip it
+//
+
+	ClipToEnds(ob);
+
+	if (leftmoved < 0 || ob == player)	// make sure player gets cliped
+		ClipToEastWalls (ob);
+	if (rightmoved > 0 || ob == player)
+		ClipToWestWalls (ob);
+}
+
+//==========================================================================
+
+
+/*
+==================
+=
+= ClipToSpriteSide
+=
+= Clips push to solid
+=
+==================
+*/
+
+void ClipToSpriteSide (objtype *push, objtype *solid)
+{
+	int xmove,leftinto,rightinto;
+
+	//
+	// amount the push shape can be pushed
+	//
+	xmove = solid->xmove - push->xmove;
+
+	//
+	// amount it is inside
+	//
+	leftinto = solid->right - push->left;
+	rightinto = push->right - solid->left;
+
+	if (leftinto>0 && leftinto<= xmove)
+	{
+		push->xmove = leftinto;
+		if (push->state->pushtofloor)
+			push->ymove = leftinto+16;
+		ClipToWalls (push);
+		push->hiteast = 1;
+		return;
+	}
+
+	if (rightinto>0 && rightinto<= -xmove)
+	{
+		push->xmove = -rightinto;
+		if (push->state->pushtofloor)
+			push->ymove = rightinto+16;
+		ClipToWalls (push);
+		push->hitwest = 1;
+		return;
+	}
+
+}
+
+//==========================================================================
+
+
+/*
+==================
+=
+= ClipToSprite
+=
+= Clips push to solid
+=
+==================
+*/
+
+void ClipToSprite (objtype *push, objtype *solid, boolean squish)
+{
+	boolean temp;
+	int walltemp,xmove,leftinto,rightinto,topinto,bottominto;
+
+	xmove = solid->xmove - push->xmove;
+
+	push->xmove = push->ymove = 0;
+
+	//
+	// left / right
+	//
+	leftinto = solid->right - push->left;
+	rightinto = push->right - solid->left;
+
+	if (leftinto>0 && leftinto<=xmove)
+	{
+		push->xmove = leftinto;
+		walltemp = push->hitnorth;
+		ClipToWalls (push);
+		if (!push->hitnorth)
+			push->hitnorth = walltemp;
+		if (squish && push->hitwest)
+			KillKeen ();
+		push->hiteast = 1;
+		return;
+	}
+	else if (rightinto>0 && rightinto<=-xmove)
+	{
+		push->xmove = -rightinto;
+		walltemp = push->hitnorth;
+		ClipToWalls (push);
+		if (!push->hitnorth)
+			push->hitnorth = walltemp;
+		if (squish && push->hiteast)
+			KillKeen ();
+		push->hitwest = 1;
+		return;
+	}
+
+	//
+	// top / bottom
+	//
+	topinto = solid->bottom - push->top;
+	bottominto = push->bottom - solid->top;
+
+	if (bottominto>0)
+	{
+		push->ymove = -bottominto+16;
+		push->xmove = solid->xmove;
+		temp = push->state->pushtofloor;
+		push->state->pushtofloor = false;
+		walltemp = push->hitnorth;
+		ClipToWalls (push);
+		if (!push->hitnorth)
+			push->hitnorth = walltemp;
+		push->state->pushtofloor = temp;
+		push->hitnorth = 25;
+	}
+	else if (topinto>0)
+	{
+		push->ymove = topinto;
+		ClipToWalls (push);
+		push->hitsouth = 25;
+	}
+}
+
+//==========================================================================
+
+
+/*
+==================
+=
+= DoActor
+=
+= Moves an actor in its current state by a given number of tics.
+= If that time takes it into the next state, it changes the state
+= and returns the number of excess tics after the state change
+=
+==================
+*/
+
+int DoActor (objtype *ob,int tics)
+{
+	int	newtics,movetics,excesstics;
+	statetype *state;
+
+	state = ob->state;
+
+	if (state->progress == think)
+	{
+		if (state->think)
+		{
+			if (ob->nothink)
+				ob->nothink--;
+			else
+#pragma warn -pro
+				state->think(ob);
+#pragma warn +pro
+		}
+		return 0;
+	}
+
+	newtics = ob->ticcount+tics;
+
+	if (newtics < state->tictime || state->tictime == 0)
+	{
+		ob->ticcount = newtics;
+		if (state->progress == slide || state->progress == slidethink)
+		{
+			if (ob->xdir)
+				ob->xmove += ob->xdir == 1 ? tics*state->xmove
+				: -tics*state->xmove;
+			if (ob->ydir)
+				ob->ymove += ob->ydir == 1 ? tics*state->ymove
+				: -tics*state->ymove;
+		}
+		if (state->progress == slidethink || state->progress == stepthink)
+		{
+			if (state->think)
+			{
+				if (ob->nothink)
+					ob->nothink--;
+				else
+#pragma warn -pro
+					state->think(ob);
+#pragma warn +pro
+			}
+		}
+		return 0;
+	}
+	else
+	{
+		movetics = state->tictime - ob->ticcount;
+		excesstics = newtics - state->tictime;
+		ob->ticcount = 0;
+		if (state->progress == slide || state->progress == slidethink)
+		{
+			if (ob->xdir)
+				ob->xmove += ob->xdir == 1 ? movetics*state->xmove
+				: -movetics*state->xmove;
+			if (ob->ydir)
+				ob->ymove += ob->ydir == 1 ? movetics*state->ymove
+				: -movetics*state->ymove;
+		}
+		else
+		{
+			if (ob->xdir)
+				ob->xmove += ob->xdir == 1 ? state->xmove : -state->xmove;
+			if (ob->ydir)
+				ob->ymove += ob->ydir == 1 ? state->ymove : -state->ymove;
+		}
+
+		if (state->think)
+		{
+			if (ob->nothink)
+				ob->nothink--;
+			else
+#pragma warn -pro
+				state->think(ob);
+#pragma warn +pro
+		}
+
+		if (ob->state == state)
+			ob->state = state->nextstate;	// go to next state
+		else if (!ob->state)
+			return 0;			// object removed itself
+		return excesstics;
+	}
+}
+
+//==========================================================================
+
+
+/*
+====================
+=
+= StateMachine
+=
+= Change state and give directions
+=
+====================
+*/
+
+void StateMachine (objtype *ob)
+{
+	int excesstics,oldshapenum;
+	statetype *state;
+
+	ob->xmove = ob->ymove = 0;
+	oldshapenum = ob->shapenum;
+
+	state = ob->state;
+
+	excesstics = DoActor(ob,tics);
+	if (ob->state != state)
+	{
+		ob->ticcount = 0;		// start the new state at 0, then use excess
+		state = ob->state;
+	}
+
+	while (excesstics)
+	{
+	//
+	// passed through to next state
+	//
+		if (!state->skippable && excesstics >= state->tictime)
+			excesstics = DoActor(ob,state->tictime-1);
+		else
+			excesstics = DoActor(ob,excesstics);
+		if (ob->state != state)
+		{
+			ob->ticcount = 0;		// start the new state at 0, then use excess
+			state = ob->state;
+		}
+	}
+
+	if (!state)			// object removed itself
+	{
+		RemoveObj (ob);
+		return;
+	}
+
+
+	//
+	// if state->rightshapenum == NULL, the state does not have a standard
+	// shape (the think routine should have set it)
+	//
+	if (state->rightshapenum)
+	{
+		if (ob->xdir>0)
+			ob->shapenum = state->rightshapenum;
+		else
+			ob->shapenum = state->leftshapenum;
+	}
+	if (ob->shapenum == (unsigned)-1)
+		ob->shapenum = 0;		// make it invisable this time
+
+	if (ob->xmove || ob->ymove || ob->shapenum != oldshapenum)
+	{
+	//
+	// actor moved or changed shape
+	// make sure the movement is within limits (one tile)
+	//
+		ClipToWalls (ob);
+	}
+}
+
+//==========================================================================
+
+
+/*
+====================
+=
+= NewState
+=
+====================
+*/
+
+void NewState (objtype *ob,statetype *state)
+{
+	boolean temp;
+
+	ob->state = state;
+
+	if (state->rightshapenum)
+	{
+		if (ob->xdir>0)
+			ob->shapenum = state->rightshapenum;
+		else
+			ob->shapenum = state->leftshapenum;
+	}
+
+	temp = ob->needtoclip;
+
+	ob->needtoclip = false;
+
+	ClipToWalls (ob);					// just calculate values
+
+	ob->needtoclip = temp;
+
+	if (ob->needtoclip)
+		ClipToWalls (ob);
+
+}
+
+//==========================================================================
+
+/*
+============================
+=
+= PlayLoop
+=
+============================
+*/
+
+void PlayLoop (void)
+{
+	objtype	*obj, *check;
+	long	newtime;
+
+	button0held = button1held = false;
+
+	ingame = true;
+	playstate = 0;
+	plummet = 0;
+
+	FixScoreBox ();					// draw bomb/flower
+
+	do
+	{
+		CalcSingleGravity ();
+		IN_ReadControl(0,&c);		// get player input
+		if (!c.button0)
+			button0held = 0;
+		if (!c.button1)
+			button1held = 0;
+
+//
+// go through state changes and propose movements
+//
+		obj = player;
+		do
+		{
+			if (!obj->active
+			&& obj->tileright >= originxtile
+			&& obj->tileleft <= originxtilemax
+			&& obj->tiletop <= originytilemax
+			&& obj->tilebottom >= originytile)
+			{
+				obj->needtoreact = true;
+				obj->active = yes;
+			}
+
+			if (obj->active)
+				StateMachine(obj);
+
+			if ( (obj->active == true || obj->active == removable) &&
+			(  obj->tileright < inactivateleft
+			|| obj->tileleft > inactivateright
+			|| obj->tiletop > inactivatebottom
+			|| obj->tilebottom < inactivatetop) )
+			{
+				if (obj->active == removable)
+					RemoveObj (obj);				// temp thing (shots, etc)
+				else
+				{
+					if (US_RndT()<tics)				// let them get a random dist
+					{
+						RF_RemoveSprite (&obj->sprite);
+						obj->active = no;
+					}
+				}
+			}
+
+			obj = (objtype *)obj->next;
+		} while (obj);
+
+//
+// check for and handle collisions between objects
+//
+		obj = player;
+		do
+		{
+			if (obj->active)
+			{
+				check = (objtype *)obj->next;
+				while (check)
+				{
+					if ( check->active
+					&& obj->right > check->left
+					&& obj->left < check->right
+					&& obj->top < check->bottom
+					&& obj->bottom > check->top)
+					{
+#pragma warn -pro
+						if (obj->state->contact)
+							obj->state->contact(obj,check);
+						if (check->state->contact)
+							check->state->contact(check,obj);
+#pragma warn +pro
+						if (!obj->obclass)
+							break;				// contact removed object
+					}
+					check = (objtype *)check->next;
+				}
+			}
+			obj = (objtype *)obj->next;
+		} while (obj);
+
+
+		ScrollScreen();
+
+//
+// react to whatever happened, and post sprites to the refresh manager
+//
+		obj = player;
+		do
+		{
+			if (obj->needtoreact && obj->state->react)
+			{
+				obj->needtoreact = false;
+#pragma warn -pro
+				obj->state->react(obj);
+#pragma warn +pro
+			}
+			obj = (objtype *)obj->next;
+		} while (obj);
+
+
+//
+// update the screen and calculate the number of tics it took to execute
+// this cycle of events (for adaptive timing of next cycle)
+//
+		RF_Refresh();
+
+//
+// single step debug mode
+//
+		if (singlestep)
+		{
+			VW_WaitVBL(14);
+			lasttimecount = TimeCount;
+		}
+
+		CheckKeys();
+	} while (!loadedgame && !playstate);
+
+	ingame = false;
+}
+
+
+//==========================================================================
+
+/*
+==========================
+=
+= GameFinale
+=
+==========================
+*/
+
+void GameFinale (void)
+{
+struct date d;
+
+	VW_FixRefreshBuffer ();
+
+/* screen 1 of finale text (16 lines) */
+	US_CenterWindow (30,21);
+	PrintY += 4;
+	US_CPrint (
+"Yes!  Boobus Tuber's hash-brown-\n"
+"like remains rained down from\n"
+"the skies as Commander Keen\n"
+"walked up to the Dream Machine.\n"
+"He analyzed all the complex\n"
+"controls and readouts on it, then\n"
+"pulled down a huge red lever\n"
+"marked \"On/Off Switch.\"  The\n"
+"machine clanked and rattled,\n"
+"then went silent. He had freed\n"
+"all the children from their\n"
+"vegetable-enforced slavery!\n"
+"Everything around Keen wobbled\n"
+"in a disconcerting manner, his\n"
+"eyelids grew heavy, and he\n"
+"fell asleep....\n"
+	);
+	VW_UpdateScreen();
+	VW_WaitVBL(60);
+	SD_WaitSoundDone ();
+	IN_ClearKeysDown ();
+	IN_Ack();
+
+/* screen 2 of finale (15 lines) */
+	US_CenterWindow (30,21);
+	PrintY += 9;
+	US_CPrint (
+"Billy woke up, looking around the\n"
+"room, the early morning sun\n"
+"shining in his face.  Nothing.\n"
+"No vegetables to be seen.  Was it\n"
+"all just a dream?\n\n"
+"Billy's mom entered the room.\n\n"
+"\"Good morning, dear. I heard some\n"
+"news on TV that you'd be\n"
+"interested in,\" she said, sitting\n"
+"by him on the bed.\n\n"
+"\"What news?\" Billy asked,\n"
+"still groggy.\n\n"
+	);
+	VW_UpdateScreen();
+	VW_WaitVBL(60);
+	IN_ClearKeysDown ();
+	IN_Ack();
+
+/* screen 3 of finale (12 lines)*/
+	US_CenterWindow (30,21);
+	PrintY += 23;
+	US_CPrint (
+"\"The President declared today\n"
+"National 'I Hate Broccoli' Day.\n"
+"He said kids are allowed to pick\n"
+"one vegetable today, and they\n"
+"don't have to eat it.\"\n\n"
+"\"Aw, mom, I'm not afraid of any\n"
+"stupid vegetables,\" Billy said.\n"
+"\"But if it's okay with you, I'd\n"
+"rather not have any french fries\n"
+"for awhile.\"\n\n"
+"THE END"
+	);
+	VW_UpdateScreen();
+	VW_WaitVBL(60);
+	IN_ClearKeysDown ();
+	IN_Ack();
+
+}
+
+//==========================================================================
+
+/*
+==========================
+=
+= HandleDeath
+=
+==========================
+*/
+
+void HandleDeath (void)
+{
+	unsigned	top,bottom,selection,y,color;
+
+	gamestate.keys = 0;
+	gamestate.boobusbombs -= gamestate.bombsthislevel;
+	gamestate.lives--;
+	if (gamestate.lives < 0)
+		return;
+
+	VW_FixRefreshBuffer ();
+	US_CenterWindow (20,8);
+	PrintY += 4;
+	US_CPrint ("You didn't make it past");
+	US_CPrint (levelnames[mapon]);
+	PrintY += 8;
+	top = PrintY-2;
+	US_CPrint ("Try Again");
+	PrintY += 4;
+	bottom = PrintY-2;
+	US_CPrint ("Exit to Tuberia");
+
+	selection = 0;
+	do
+	{
+		if (selection)
+			y = bottom;
+		else
+			y = top;
+
+// draw select bar
+		if ( (TimeCount / 16)&1 )
+			color = SECONDCOLOR;
+		else
+			color = FIRSTCOLOR;
+
+		VWB_Hlin (WindowX+4,WindowX+WindowW-4,y,color);
+		VWB_Hlin (WindowX+4,WindowX+WindowW-4,y+1,color);
+		VWB_Hlin (WindowX+4,WindowX+WindowW-4,y+12,color);
+		VWB_Hlin (WindowX+4,WindowX+WindowW-4,y+13,color);
+		VWB_Vlin (y+1,y+11, WindowX+4,color);
+		VWB_Vlin (y+1,y+11, WindowX+5,color);
+		VWB_Vlin (y+1,y+11, WindowX+WindowW-4,color);
+		VWB_Vlin (y+1,y+11, WindowX+WindowW-5,color);
+
+		VW_UpdateScreen ();
+
+// erase select bar
+		VWB_Hlin (WindowX+4,WindowX+WindowW-4,y,WHITE);
+		VWB_Hlin (WindowX+4,WindowX+WindowW-4,y+1,WHITE);
+		VWB_Hlin (WindowX+4,WindowX+WindowW-4,y+12,WHITE);
+		VWB_Hlin (WindowX+4,WindowX+WindowW-4,y+13,WHITE);
+		VWB_Vlin (y+1,y+11, WindowX+4,WHITE);
+		VWB_Vlin (y+1,y+11, WindowX+5,WHITE);
+		VWB_Vlin (y+1,y+11, WindowX+WindowW-4,WHITE);
+		VWB_Vlin (y+1,y+11, WindowX+WindowW-5,WHITE);
+
+		if (LastScan == sc_Escape)
+		{
+			gamestate.mapon = 0;		// exit to tuberia
+			IN_ClearKeysDown ();
+			return;
+		}
+
+		IN_ReadControl(0,&c);		// get player input
+		if (c.button0 || c.button1 || LastScan == sc_Return
+		|| LastScan == sc_Space)
+		{
+			if (selection)
+				gamestate.mapon = 0;		// exit to tuberia
+			return;
+		}
+		if (c.yaxis == -1 || LastScan == sc_UpArrow)
+			selection = 0;
+		else if (c.yaxis == 1 || LastScan == sc_DownArrow)
+			selection = 1;
+	} while (1);
+
+}
+
+//==========================================================================
+
+/*
+============================
+=
+= GameLoop
+=
+= A game has just started (after the cinematic or load game)
+=
+============================
+*/
+
+void GameLoop (void)
+{
+	unsigned	cities,i;
+	long	orgx,orgy;
+
+	gamestate.difficulty = restartgame;
+	restartgame = gd_Continue;
+
+	do
+	{
+startlevel:
+		if (loadedgame)
+		{
+			loadedgame = false;
+			//
+			// start the initial view position to center the player
+			//
+			orgx = (long)player->x - (150<<G_P_SHIFT);
+			orgy = (long)player->y-(84<<G_P_SHIFT);
+			if (orgx<0)
+				orgx=0;
+			if (orgy<0)
+				orgy=0;
+
+			VW_FadeOut ();
+			fadecount = 0;
+			RF_SetRefreshHook (&FadeAndUnhook);
+			RF_NewPosition (orgx,orgy);
+			CalcInactivate ();
+		}
+		else
+		{
+			VW_FixRefreshBuffer ();
+			US_CenterWindow (20,8);
+			US_CPrint ("Loading");
+			VW_UpdateScreen ();
+			gamestate.bombsthislevel = 0;
+			SetupGameLevel (true);
+		}
+
+
+		PlayLoop ();
+
+#if FRILLS
+		if (tedlevel)
+		{
+			if (playstate == died)
+				goto startlevel;
+			else
+				TEDDeath ();
+		}
+#endif
+
+		if (loadedgame)
+			goto startlevel;
+
+		switch (playstate)
+		{
+		case warptolevel:
+			goto startlevel;
+
+		case died:
+			HandleDeath ();
+			break;
+
+		case levelcomplete:
+			if (mapon)
+				SD_PlaySound (LEVELDONESND);
+			gamestate.leveldone[mapon] = true;	// finished the level
+			if (mapon != 0)
+				gamestate.mapon = 0;
+			break;
+
+		case resetgame:
+			return;
+
+		case victorious:
+			GameFinale ();
+			goto done;
+		}
+
+
+	} while (gamestate.lives>-1 && playstate!=victorious);
+
+	GameOver ();
+
+done:
+	cities = 0;
+	for (i= 1; i<=16; i++)
+		if (gamestate.leveldone[i])
+			cities++;
+	US_CheckHighScore (gamestate.score,cities);
+	VW_ClearVideo (FIRSTCOLOR);
+}
+
diff --git a/src/lib/hb/wl_act1.c b/src/lib/hb/wl_act1.c
new file mode 100755
index 00000000..10d84e4e
--- /dev/null
+++ b/src/lib/hb/wl_act1.c
@@ -0,0 +1,900 @@
+// WL_ACT1.C
+
+#include "WL_DEF.H"
+#pragma hdrstop
+
+/*
+=============================================================================
+
+							STATICS
+
+=============================================================================
+*/
+
+
+statobj_t	statobjlist[MAXSTATS],*laststatobj;
+
+
+struct
+{
+	int		picnum;
+	stat_t	type;
+} statinfo[] =
+{
+{SPR_STAT_0},					// puddle          spr1v
+{SPR_STAT_1,block},				// Green Barrel    "
+{SPR_STAT_2,block},				// Table/chairs    "
+{SPR_STAT_3,block},				// Floor lamp      "
+{SPR_STAT_4},					// Chandelier      "
+{SPR_STAT_5,block},				// Hanged man      "
+{SPR_STAT_6,bo_alpo},			// Bad food        "
+{SPR_STAT_7,block},				// Red pillar      "
+//
+// NEW PAGE
+//
+{SPR_STAT_8,block},				// Tree            spr2v
+{SPR_STAT_9},					// Skeleton flat   "
+{SPR_STAT_10,block},			// Sink            " (SOD:gibs)
+{SPR_STAT_11,block},			// Potted plant    "
+{SPR_STAT_12,block},			// Urn             "
+{SPR_STAT_13,block},			// Bare table      "
+{SPR_STAT_14},					// Ceiling light   "
+#ifndef SPEAR
+{SPR_STAT_15},					// Kitchen stuff   "
+#else
+{SPR_STAT_15,block},			// Gibs!
+#endif
+//
+// NEW PAGE
+//
+{SPR_STAT_16,block},			// suit of armor   spr3v
+{SPR_STAT_17,block},			// Hanging cage    "
+{SPR_STAT_18,block},			// SkeletoninCage  "
+{SPR_STAT_19},					// Skeleton relax  "
+{SPR_STAT_20,bo_key1},			// Key 1           "
+{SPR_STAT_21,bo_key2},			// Key 2           "
+{SPR_STAT_22,block},			// stuff				(SOD:gibs)
+{SPR_STAT_23},					// stuff
+//
+// NEW PAGE
+//
+{SPR_STAT_24,bo_food}, 			// Good food       spr4v
+{SPR_STAT_25,bo_firstaid},		// First aid       "
+{SPR_STAT_26,bo_clip},			// Clip            "
+{SPR_STAT_27,bo_machinegun},	// Machine gun     "
+{SPR_STAT_28,bo_chaingun},		// Gatling gun     "
+{SPR_STAT_29,bo_cross},			// Cross           "
+{SPR_STAT_30,bo_chalice},		// Chalice         "
+{SPR_STAT_31,bo_bible},			// Bible           "
+//
+// NEW PAGE
+//
+{SPR_STAT_32,bo_crown},			// crown           spr5v
+{SPR_STAT_33,bo_fullheal},		// one up          "
+{SPR_STAT_34,bo_gibs},			// gibs            "
+{SPR_STAT_35,block},			// barrel          "
+{SPR_STAT_36,block},			// well            "
+{SPR_STAT_37,block},			// Empty well      "
+{SPR_STAT_38,bo_gibs},			// Gibs 2          "
+{SPR_STAT_39,block},			// flag				"
+//
+// NEW PAGE
+//
+#ifndef SPEAR
+{SPR_STAT_40,block},			// Call Apogee		spr7v
+#else
+{SPR_STAT_40},					// Red light
+#endif
+//
+// NEW PAGE
+//
+{SPR_STAT_41},					// junk            "
+{SPR_STAT_42},					// junk 		   "
+{SPR_STAT_43},					// junk            "
+#ifndef SPEAR
+{SPR_STAT_44},					// pots            "
+#else
+{SPR_STAT_44,block},			// Gibs!
+#endif
+{SPR_STAT_45,block},			// stove           " (SOD:gibs)
+{SPR_STAT_46,block},			// spears          " (SOD:gibs)
+{SPR_STAT_47},					// vines			"
+//
+// NEW PAGE
+//
+#ifdef SPEAR
+{SPR_STAT_48,block},			// marble pillar
+{SPR_STAT_49,bo_25clip},		// bonus 25 clip
+{SPR_STAT_50,block},			// truck
+{SPR_STAT_51,bo_spear},			// SPEAR OF DESTINY!
+#endif
+
+{SPR_STAT_26,bo_clip2},			// Clip            "
+{-1}							// terminator
+};
+
+/*
+===============
+=
+= InitStaticList
+=
+===============
+*/
+
+void InitStaticList (void)
+{
+	laststatobj = &statobjlist[0];
+}
+
+
+
+/*
+===============
+=
+= SpawnStatic
+=
+===============
+*/
+
+void SpawnStatic (int tilex, int tiley, int type)
+{
+	laststatobj->shapenum = statinfo[type].picnum;
+	laststatobj->tilex = tilex;
+	laststatobj->tiley = tiley;
+	laststatobj->visspot = &spotvis[tilex][tiley];
+
+	switch (statinfo[type].type)
+	{
+	case block:
+		(unsigned)actorat[tilex][tiley] = 1;		// consider it a blocking tile
+	case dressing:
+		laststatobj->flags = 0;
+		break;
+
+	case	bo_cross:
+	case	bo_chalice:
+	case	bo_bible:
+	case	bo_crown:
+	case	bo_fullheal:
+		if (!loadedgame)
+		  gamestate.treasuretotal++;
+
+	case	bo_firstaid:
+	case	bo_key1:
+	case	bo_key2:
+	case	bo_key3:
+	case	bo_key4:
+	case	bo_clip:
+	case	bo_25clip:
+	case	bo_machinegun:
+	case	bo_chaingun:
+	case	bo_food:
+	case	bo_alpo:
+	case	bo_gibs:
+	case	bo_spear:
+		laststatobj->flags = FL_BONUS;
+		laststatobj->itemnumber = statinfo[type].type;
+		break;
+	}
+
+	laststatobj++;
+
+	if (laststatobj == &statobjlist[MAXSTATS])
+		Quit ("Too many static objects!\n");
+}
+
+
+/*
+===============
+=
+= PlaceItemType
+=
+= Called during game play to drop actors' items.  It finds the proper
+= item number based on the item type (bo_???).  If there are no free item
+= spots, nothing is done.
+=
+===============
+*/
+
+void PlaceItemType (int itemtype, int tilex, int tiley)
+{
+	int			type;
+	statobj_t	*spot;
+
+//
+// find the item number
+//
+	for (type=0 ;  ; type++)
+	{
+		if (statinfo[type].picnum == -1)		// end of list
+			Quit ("PlaceItemType: couldn't find type!");
+		if (statinfo[type].type == itemtype)
+			break;
+	}
+
+//
+// find a spot in statobjlist to put it in
+//
+	for (spot=&statobjlist[0] ; ; spot++)
+	{
+		if (spot==laststatobj)
+		{
+			if (spot == &statobjlist[MAXSTATS])
+				return;							// no free spots
+			laststatobj++;						// space at end
+			break;
+		}
+
+		if (spot->shapenum == -1)				// -1 is a free spot
+			break;
+	}
+//
+// place it
+//
+	spot->shapenum = statinfo[type].picnum;
+	spot->tilex = tilex;
+	spot->tiley = tiley;
+	spot->visspot = &spotvis[tilex][tiley];
+	spot->flags = FL_BONUS;
+	spot->itemnumber = statinfo[type].type;
+}
+
+
+
+/*
+=============================================================================
+
+							DOORS
+
+doorobjlist[] holds most of the information for the doors
+
+doorposition[] holds the amount the door is open, ranging from 0 to 0xffff
+	this is directly accessed by AsmRefresh during rendering
+
+The number of doors is limited to 64 because a spot in tilemap holds the
+	door number in the low 6 bits, with the high bit meaning a door center
+	and bit 6 meaning a door side tile
+
+Open doors conect two areas, so sounds will travel between them and sight
+	will be checked when the player is in a connected area.
+
+Areaconnect is incremented/decremented by each door. If >0 they connect
+
+Every time a door opens or closes the areabyplayer matrix gets recalculated.
+	An area is true if it connects with the player's current spor.
+
+=============================================================================
+*/
+
+#define DOORWIDTH	0x7800
+#define OPENTICS	300
+
+doorobj_t	doorobjlist[MAXDOORS],*lastdoorobj;
+int			doornum;
+
+unsigned	doorposition[MAXDOORS];		// leading edge of door 0=closed
+										// 0xffff = fully open
+
+byte		far areaconnect[NUMAREAS][NUMAREAS];
+
+boolean		areabyplayer[NUMAREAS];
+
+
+/*
+==============
+=
+= ConnectAreas
+=
+= Scans outward from playerarea, marking all connected areas
+=
+==============
+*/
+
+void RecursiveConnect (int areanumber)
+{
+	int	i;
+
+	for (i=0;i<NUMAREAS;i++)
+	{
+		if (areaconnect[areanumber][i] && !areabyplayer[i])
+		{
+			areabyplayer[i] = true;
+			RecursiveConnect (i);
+		}
+	}
+}
+
+
+void ConnectAreas (void)
+{
+	memset (areabyplayer,0,sizeof(areabyplayer));
+	areabyplayer[player->areanumber] = true;
+	RecursiveConnect (player->areanumber);
+}
+
+
+void InitAreas (void)
+{
+	memset (areabyplayer,0,sizeof(areabyplayer));
+	areabyplayer[player->areanumber] = true;
+}
+
+
+
+/*
+===============
+=
+= InitDoorList
+=
+===============
+*/
+
+void InitDoorList (void)
+{
+	memset (areabyplayer,0,sizeof(areabyplayer));
+	_fmemset (areaconnect,0,sizeof(areaconnect));
+
+	lastdoorobj = &doorobjlist[0];
+	doornum = 0;
+}
+
+
+/*
+===============
+=
+= SpawnDoor
+=
+===============
+*/
+
+void SpawnDoor (int tilex, int tiley, boolean vertical, int lock)
+{
+	int	areanumber;
+	unsigned	far *map;
+
+	if (doornum==64)
+		Quit ("64+ doors on level!");
+
+	doorposition[doornum] = 0;		// doors start out fully closed
+	lastdoorobj->tilex = tilex;
+	lastdoorobj->tiley = tiley;
+	lastdoorobj->vertical = vertical;
+	lastdoorobj->lock = lock;
+	lastdoorobj->action = dr_closed;
+
+	(unsigned)actorat[tilex][tiley] = doornum | 0x80;	// consider it a solid wall
+
+//
+// make the door tile a special tile, and mark the adjacent tiles
+// for door sides
+//
+	tilemap[tilex][tiley] = doornum | 0x80;
+	map = mapsegs[0] + farmapylookup[tiley]+tilex;
+	if (vertical)
+	{
+		*map = *(map-1);                        // set area number
+		tilemap[tilex][tiley-1] |= 0x40;
+		tilemap[tilex][tiley+1] |= 0x40;
+	}
+	else
+	{
+		*map = *(map-mapwidth);					// set area number
+		tilemap[tilex-1][tiley] |= 0x40;
+		tilemap[tilex+1][tiley] |= 0x40;
+	}
+
+	doornum++;
+	lastdoorobj++;
+}
+
+//===========================================================================
+
+/*
+=====================
+=
+= OpenDoor
+=
+=====================
+*/
+
+void OpenDoor (int door)
+{
+	if (doorobjlist[door].action == dr_open)
+		doorobjlist[door].ticcount = 0;			// reset open time
+	else
+		doorobjlist[door].action = dr_opening;	// start it opening
+}
+
+
+/*
+=====================
+=
+= CloseDoor
+=
+=====================
+*/
+
+void CloseDoor (int door)
+{
+	int	tilex,tiley,area;
+	objtype *check;
+
+//
+// don't close on anything solid
+//
+	tilex = doorobjlist[door].tilex;
+	tiley = doorobjlist[door].tiley;
+
+	if (actorat[tilex][tiley])
+		return;
+
+	if (player->tilex == tilex && player->tiley == tiley)
+		return;
+
+	if (doorobjlist[door].vertical)
+	{
+		if ( player->tiley == tiley )
+		{
+			if ( ((player->x+MINDIST) >>TILESHIFT) == tilex )
+				return;
+			if ( ((player->x-MINDIST) >>TILESHIFT) == tilex )
+				return;
+		}
+		check = actorat[tilex-1][tiley];
+		if (check && ((check->x+MINDIST) >> TILESHIFT) == tilex )
+			return;
+		check = actorat[tilex+1][tiley];
+		if (check && ((check->x-MINDIST) >> TILESHIFT) == tilex )
+			return;
+	}
+	else if (!doorobjlist[door].vertical)
+	{
+		if (player->tilex == tilex)
+		{
+			if ( ((player->y+MINDIST) >>TILESHIFT) == tiley )
+				return;
+			if ( ((player->y-MINDIST) >>TILESHIFT) == tiley )
+				return;
+		}
+		check = actorat[tilex][tiley-1];
+		if (check && ((check->y+MINDIST) >> TILESHIFT) == tiley )
+			return;
+		check = actorat[tilex][tiley+1];
+		if (check && ((check->y-MINDIST) >> TILESHIFT) == tiley )
+			return;
+	}
+
+
+//
+// play door sound if in a connected area
+//
+	area = *(mapsegs[0] + farmapylookup[doorobjlist[door].tiley]
+			+doorobjlist[door].tilex)-AREATILE;
+	if (areabyplayer[area])
+	{
+		PlaySoundLocTile(CLOSEDOORSND,doorobjlist[door].tilex,doorobjlist[door].tiley);	// JAB
+	}
+
+	doorobjlist[door].action = dr_closing;
+//
+// make the door space solid
+//
+	(unsigned)actorat[tilex][tiley]
+		= door | 0x80;
+}
+
+
+
+/*
+=====================
+=
+= OperateDoor
+=
+= The player wants to change the door's direction
+=
+=====================
+*/
+
+void OperateDoor (int door)
+{
+	int	lock;
+
+	lock = doorobjlist[door].lock;
+	if (lock >= dr_lock1 && lock <= dr_lock4)
+	{
+		if ( ! (gamestate.keys & (1 << (lock-dr_lock1) ) ) )
+		{
+			SD_PlaySound (NOWAYSND);		// locked
+			return;
+		}
+	}
+
+	switch (doorobjlist[door].action)
+	{
+	case dr_closed:
+	case dr_closing:
+		OpenDoor (door);
+		break;
+	case dr_open:
+	case dr_opening:
+		CloseDoor (door);
+		break;
+	}
+}
+
+
+//===========================================================================
+
+/*
+===============
+=
+= DoorOpen
+=
+= Close the door after three seconds
+=
+===============
+*/
+
+void DoorOpen (int door)
+{
+	if ( (doorobjlist[door].ticcount += tics) >= OPENTICS)
+		CloseDoor (door);
+}
+
+
+
+/*
+===============
+=
+= DoorOpening
+=
+===============
+*/
+
+void DoorOpening (int door)
+{
+	int		area1,area2;
+	unsigned	far	*map;
+	long	position;
+
+	position = doorposition[door];
+	if (!position)
+	{
+	//
+	// door is just starting to open, so connect the areas
+	//
+		map = mapsegs[0] + farmapylookup[doorobjlist[door].tiley]
+			+doorobjlist[door].tilex;
+
+		if (doorobjlist[door].vertical)
+		{
+			area1 =	*(map+1);
+			area2 =	*(map-1);
+		}
+		else
+		{
+			area1 =	*(map-mapwidth);
+			area2 =	*(map+mapwidth);
+		}
+		area1 -= AREATILE;
+		area2 -= AREATILE;
+		areaconnect[area1][area2]++;
+		areaconnect[area2][area1]++;
+		ConnectAreas ();
+		if (areabyplayer[area1])
+		{
+			PlaySoundLocTile(OPENDOORSND,doorobjlist[door].tilex,doorobjlist[door].tiley);	// JAB
+		}
+	}
+
+//
+// slide the door by an adaptive amount
+//
+	position += tics<<10;
+	if (position >= 0xffff)
+	{
+	//
+	// door is all the way open
+	//
+		position = 0xffff;
+		doorobjlist[door].ticcount = 0;
+		doorobjlist[door].action = dr_open;
+		actorat[doorobjlist[door].tilex][doorobjlist[door].tiley] = 0;
+	}
+
+	doorposition[door] = position;
+}
+
+
+/*
+===============
+=
+= DoorClosing
+=
+===============
+*/
+
+void DoorClosing (int door)
+{
+	int		area1,area2,move;
+	unsigned	far	*map;
+	long	position;
+	int		tilex,tiley;
+
+	tilex = doorobjlist[door].tilex;
+	tiley = doorobjlist[door].tiley;
+
+	if ( ((unsigned)actorat[tilex][tiley] != (door | 0x80))
+	|| (player->tilex == tilex && player->tiley == tiley) )
+	{			// something got inside the door
+		OpenDoor (door);
+		return;
+	};
+
+	position = doorposition[door];
+
+//
+// slide the door by an adaptive amount
+//
+	position -= tics<<10;
+	if (position <= 0)
+	{
+	//
+	// door is closed all the way, so disconnect the areas
+	//
+		position = 0;
+
+		doorobjlist[door].action = dr_closed;
+
+		map = mapsegs[0] + farmapylookup[doorobjlist[door].tiley]
+			+doorobjlist[door].tilex;
+
+		if (doorobjlist[door].vertical)
+		{
+			area1 =	*(map+1);
+			area2 =	*(map-1);
+		}
+		else
+		{
+			area1 =	*(map-mapwidth);
+			area2 =	*(map+mapwidth);
+		}
+		area1 -= AREATILE;
+		area2 -= AREATILE;
+		areaconnect[area1][area2]--;
+		areaconnect[area2][area1]--;
+
+		ConnectAreas ();
+	}
+
+	doorposition[door] = position;
+}
+
+
+
+
+/*
+=====================
+=
+= MoveDoors
+=
+= Called from PlayLoop
+=
+=====================
+*/
+
+void MoveDoors (void)
+{
+	int		door;
+
+	if (gamestate.victoryflag)		// don't move door during victory sequence
+		return;
+
+	for (door = 0 ; door < doornum ; door++)
+		switch (doorobjlist[door].action)
+		{
+		case dr_open:
+			DoorOpen (door);
+			break;
+
+		case dr_opening:
+			DoorOpening(door);
+			break;
+
+		case dr_closing:
+			DoorClosing(door);
+			break;
+		}
+}
+
+
+/*
+=============================================================================
+
+						PUSHABLE WALLS
+
+=============================================================================
+*/
+
+unsigned	pwallstate;
+unsigned	pwallpos;			// amount a pushable wall has been moved (0-63)
+unsigned	pwallx,pwally;
+int			pwalldir;
+
+/*
+===============
+=
+= PushWall
+=
+===============
+*/
+
+void PushWall (int checkx, int checky, int dir)
+{
+	int		oldtile;
+
+	if (pwallstate)
+	  return;
+
+
+	oldtile = tilemap[checkx][checky];
+	if (!oldtile)
+		return;
+
+	switch (dir)
+	{
+	case di_north:
+		if (actorat[checkx][checky-1])
+		{
+			SD_PlaySound (NOWAYSND);
+			return;
+		}
+		(unsigned)actorat[checkx][checky-1] =
+		tilemap[checkx][checky-1] = oldtile;
+		break;
+
+	case di_east:
+		if (actorat[checkx+1][checky])
+		{
+			SD_PlaySound (NOWAYSND);
+			return;
+		}
+		(unsigned)actorat[checkx+1][checky] =
+		tilemap[checkx+1][checky] = oldtile;
+		break;
+
+	case di_south:
+		if (actorat[checkx][checky+1])
+		{
+			SD_PlaySound (NOWAYSND);
+			return;
+		}
+		(unsigned)actorat[checkx][checky+1] =
+		tilemap[checkx][checky+1] = oldtile;
+		break;
+
+	case di_west:
+		if (actorat[checkx-1][checky])
+		{
+			SD_PlaySound (NOWAYSND);
+			return;
+		}
+		(unsigned)actorat[checkx-1][checky] =
+		tilemap[checkx-1][checky] = oldtile;
+		break;
+	}
+
+	gamestate.secretcount++;
+	pwallx = checkx;
+	pwally = checky;
+	pwalldir = dir;
+	pwallstate = 1;
+	pwallpos = 0;
+	tilemap[pwallx][pwally] |= 0xc0;
+	*(mapsegs[1]+farmapylookup[pwally]+pwallx) = 0;	// remove P tile info
+
+	SD_PlaySound (PUSHWALLSND);
+}
+
+
+
+/*
+=================
+=
+= MovePWalls
+=
+=================
+*/
+
+void MovePWalls (void)
+{
+	int		oldblock,oldtile;
+
+	if (!pwallstate)
+		return;
+
+	oldblock = pwallstate/128;
+
+	pwallstate += tics;
+
+	if (pwallstate/128 != oldblock)
+	{
+	// block crossed into a new block
+		oldtile = tilemap[pwallx][pwally] & 63;
+
+		//
+		// the tile can now be walked into
+		//
+		tilemap[pwallx][pwally] = 0;
+		(unsigned)actorat[pwallx][pwally] = 0;
+		*(mapsegs[0]+farmapylookup[pwally]+pwallx) = player->areanumber+AREATILE;
+
+		//
+		// see if it should be pushed farther
+		//
+		if (pwallstate>256)
+		{
+		//
+		// the block has been pushed two tiles
+		//
+			pwallstate = 0;
+			return;
+		}
+		else
+		{
+			switch (pwalldir)
+			{
+			case di_north:
+				pwally--;
+				if (actorat[pwallx][pwally-1])
+				{
+					pwallstate = 0;
+					return;
+				}
+				(unsigned)actorat[pwallx][pwally-1] =
+				tilemap[pwallx][pwally-1] = oldtile;
+				break;
+
+			case di_east:
+				pwallx++;
+				if (actorat[pwallx+1][pwally])
+				{
+					pwallstate = 0;
+					return;
+				}
+				(unsigned)actorat[pwallx+1][pwally] =
+				tilemap[pwallx+1][pwally] = oldtile;
+				break;
+
+			case di_south:
+				pwally++;
+				if (actorat[pwallx][pwally+1])
+				{
+					pwallstate = 0;
+					return;
+				}
+				(unsigned)actorat[pwallx][pwally+1] =
+				tilemap[pwallx][pwally+1] = oldtile;
+				break;
+
+			case di_west:
+				pwallx--;
+				if (actorat[pwallx-1][pwally])
+				{
+					pwallstate = 0;
+					return;
+				}
+				(unsigned)actorat[pwallx-1][pwally] =
+				tilemap[pwallx-1][pwally] = oldtile;
+				break;
+			}
+
+			tilemap[pwallx][pwally] = oldtile | 0xc0;
+		}
+	}
+
+
+	pwallpos = (pwallstate/2)&63;
+
+}
+
diff --git a/src/lib/hb/wl_act2.c b/src/lib/hb/wl_act2.c
new file mode 100755
index 00000000..d9d99a0b
--- /dev/null
+++ b/src/lib/hb/wl_act2.c
@@ -0,0 +1,3872 @@
+// WL_ACT2.C
+
+#include "WL_DEF.H"
+#pragma hdrstop
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+#define PROJECTILESIZE	0xc000l
+
+#define BJRUNSPEED	2048
+#define BJJUMPSPEED	680
+
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+
+dirtype dirtable[9] = {northwest,north,northeast,west,nodir,east,
+	southwest,south,southeast};
+
+int	starthitpoints[4][NUMENEMIES] =
+	 //
+	 // BABY MODE
+	 //
+	 {
+	 {25,	// guards
+	  50,	// officer
+	  100,	// SS
+	  1,	// dogs
+	  850,	// Hans
+	  850,	// Schabbs
+	  200,	// fake hitler
+	  800,	// mecha hitler
+	  45,	// mutants
+	  25,	// ghosts
+	  25,	// ghosts
+	  25,	// ghosts
+	  25,	// ghosts
+
+	  850,	// Gretel
+	  850,	// Gift
+	  850,	// Fat
+	  5,	// en_spectre,
+	  1450,	// en_angel,
+	  850,	// en_trans,
+	  1050,	// en_uber,
+	  950,	// en_will,
+	  1250	// en_death
+	  },
+	 //
+	 // DON'T HURT ME MODE
+	 //
+	 {25,	// guards
+	  50,	// officer
+	  100,	// SS
+	  1,	// dogs
+	  950,	// Hans
+	  950,	// Schabbs
+	  300,	// fake hitler
+	  950,	// mecha hitler
+	  55,	// mutants
+	  25,	// ghosts
+	  25,	// ghosts
+	  25,	// ghosts
+	  25,	// ghosts
+
+	  950,	// Gretel
+	  950,	// Gift
+	  950,	// Fat
+	  10,	// en_spectre,
+	  1550,	// en_angel,
+	  950,	// en_trans,
+	  1150,	// en_uber,
+	  1050,	// en_will,
+	  1350	// en_death
+	  },
+	 //
+	 // BRING 'EM ON MODE
+	 //
+	 {25,	// guards
+	  50,	// officer
+	  100,	// SS
+	  1,	// dogs
+
+	  1050,	// Hans
+	  1550,	// Schabbs
+	  400,	// fake hitler
+	  1050,	// mecha hitler
+
+	  55,	// mutants
+	  25,	// ghosts
+	  25,	// ghosts
+	  25,	// ghosts
+	  25,	// ghosts
+
+	  1050,	// Gretel
+	  1050,	// Gift
+	  1050,	// Fat
+	  15,	// en_spectre,
+	  1650,	// en_angel,
+	  1050,	// en_trans,
+	  1250,	// en_uber,
+	  1150,	// en_will,
+	  1450	// en_death
+	  },
+	 //
+	 // DEATH INCARNATE MODE
+	 //
+	 {25,	// guards
+	  50,	// officer
+	  100,	// SS
+	  1,	// dogs
+
+	  1200,	// Hans
+	  2400,	// Schabbs
+	  500,	// fake hitler
+	  1200,	// mecha hitler
+
+	  65,	// mutants
+	  25,	// ghosts
+	  25,	// ghosts
+	  25,	// ghosts
+	  25,	// ghosts
+
+	  1200,	// Gretel
+	  1200,	// Gift
+	  1200,	// Fat
+	  25,	// en_spectre,
+	  2000,	// en_angel,
+	  1200,	// en_trans,
+	  1400,	// en_uber,
+	  1300,	// en_will,
+	  1600	// en_death
+	  }}
+	  ;
+
+void	A_StartDeathCam (objtype *ob);
+
+
+void	T_Path (objtype *ob);
+void	T_Shoot (objtype *ob);
+void	T_Bite (objtype *ob);
+void	T_DogChase (objtype *ob);
+void	T_Chase (objtype *ob);
+void	T_Projectile (objtype *ob);
+void	T_Stand (objtype *ob);
+
+void A_DeathScream (objtype *ob);
+
+extern	statetype s_rocket;
+extern	statetype s_smoke1;
+extern	statetype s_smoke2;
+extern	statetype s_smoke3;
+extern	statetype s_smoke4;
+extern	statetype s_boom2;
+extern	statetype s_boom3;
+
+void A_Smoke (objtype *ob);
+
+statetype s_rocket	 	= {true,SPR_ROCKET_1,3,T_Projectile,A_Smoke,&s_rocket};
+statetype s_smoke1	 	= {false,SPR_SMOKE_1,3,NULL,NULL,&s_smoke2};
+statetype s_smoke2	 	= {false,SPR_SMOKE_2,3,NULL,NULL,&s_smoke3};
+statetype s_smoke3	 	= {false,SPR_SMOKE_3,3,NULL,NULL,&s_smoke4};
+statetype s_smoke4	 	= {false,SPR_SMOKE_4,3,NULL,NULL,NULL};
+
+statetype s_boom1	 	= {false,SPR_BOOM_1,6,NULL,NULL,&s_boom2};
+statetype s_boom2	 	= {false,SPR_BOOM_2,6,NULL,NULL,&s_boom3};
+statetype s_boom3	 	= {false,SPR_BOOM_3,6,NULL,NULL,NULL};
+
+#ifdef SPEAR
+
+extern	statetype s_hrocket;
+extern	statetype s_hsmoke1;
+extern	statetype s_hsmoke2;
+extern	statetype s_hsmoke3;
+extern	statetype s_hsmoke4;
+extern	statetype s_hboom2;
+extern	statetype s_hboom3;
+
+void A_Smoke (objtype *ob);
+
+statetype s_hrocket	 	= {true,SPR_HROCKET_1,3,T_Projectile,A_Smoke,&s_hrocket};
+statetype s_hsmoke1	 	= {false,SPR_HSMOKE_1,3,NULL,NULL,&s_hsmoke2};
+statetype s_hsmoke2	 	= {false,SPR_HSMOKE_2,3,NULL,NULL,&s_hsmoke3};
+statetype s_hsmoke3	 	= {false,SPR_HSMOKE_3,3,NULL,NULL,&s_hsmoke4};
+statetype s_hsmoke4	 	= {false,SPR_HSMOKE_4,3,NULL,NULL,NULL};
+
+statetype s_hboom1	 	= {false,SPR_HBOOM_1,6,NULL,NULL,&s_hboom2};
+statetype s_hboom2	 	= {false,SPR_HBOOM_2,6,NULL,NULL,&s_hboom3};
+statetype s_hboom3	 	= {false,SPR_HBOOM_3,6,NULL,NULL,NULL};
+
+#endif
+
+void	T_Schabb (objtype *ob);
+void	T_SchabbThrow (objtype *ob);
+void	T_Fake (objtype *ob);
+void	T_FakeFire (objtype *ob);
+void	T_Ghosts (objtype *ob);
+
+void A_Slurpie (objtype *ob);
+void A_HitlerMorph (objtype *ob);
+void A_MechaSound (objtype *ob);
+
+/*
+=================
+=
+= A_Smoke
+=
+=================
+*/
+
+void A_Smoke (objtype *ob)
+{
+	GetNewActor ();
+#ifdef SPEAR
+	if (ob->obclass == hrocketobj)
+		new->state = &s_hsmoke1;
+	else
+#endif
+		new->state = &s_smoke1;
+	new->ticcount = 6;
+
+	new->tilex = ob->tilex;
+	new->tiley = ob->tiley;
+	new->x = ob->x;
+	new->y = ob->y;
+	new->obclass = inertobj;
+	new->active = true;
+
+	new->flags = FL_NEVERMARK;
+}
+
+
+/*
+===================
+=
+= ProjectileTryMove
+=
+= returns true if move ok
+===================
+*/
+
+#define PROJSIZE	0x2000
+
+boolean ProjectileTryMove (objtype *ob)
+{
+	int			xl,yl,xh,yh,x,y;
+	objtype		*check;
+	long		deltax,deltay;
+
+	xl = (ob->x-PROJSIZE) >>TILESHIFT;
+	yl = (ob->y-PROJSIZE) >>TILESHIFT;
+
+	xh = (ob->x+PROJSIZE) >>TILESHIFT;
+	yh = (ob->y+PROJSIZE) >>TILESHIFT;
+
+//
+// check for solid walls
+//
+	for (y=yl;y<=yh;y++)
+		for (x=xl;x<=xh;x++)
+		{
+			check = actorat[x][y];
+			if (check && check<objlist)
+				return false;
+		}
+
+	return true;
+}
+
+
+
+/*
+=================
+=
+= T_Projectile
+=
+=================
+*/
+
+void T_Projectile (objtype *ob)
+{
+	long	deltax,deltay;
+	int		damage;
+	long	speed;
+
+	speed = (long)ob->speed*tics;
+
+	deltax = FixedByFrac(speed,costable[ob->angle]);
+	deltay = -FixedByFrac(speed,sintable[ob->angle]);
+
+	if (deltax>0x10000l)
+		deltax = 0x10000l;
+	if (deltay>0x10000l)
+		deltay = 0x10000l;
+
+	ob->x += deltax;
+	ob->y += deltay;
+
+	deltax = LABS(ob->x - player->x);
+	deltay = LABS(ob->y - player->y);
+
+	if (!ProjectileTryMove (ob))
+	{
+		if (ob->obclass == rocketobj)
+		{
+			PlaySoundLocActor(MISSILEHITSND,ob);
+			ob->state = &s_boom1;
+		}
+#ifdef SPEAR
+		else if (ob->obclass == hrocketobj)
+		{
+			PlaySoundLocActor(MISSILEHITSND,ob);
+			ob->state = &s_hboom1;
+		}
+#endif
+		else
+			ob->state = NULL;		// mark for removal
+
+		return;
+	}
+
+	if (deltax < PROJECTILESIZE && deltay < PROJECTILESIZE)
+	{	// hit the player
+		switch (ob->obclass)
+		{
+		case needleobj:
+			damage = (US_RndT() >>3) + 20;
+			break;
+		case rocketobj:
+		case hrocketobj:
+		case sparkobj:
+			damage = (US_RndT() >>3) + 30;
+			break;
+		case fireobj:
+			damage = (US_RndT() >>3);
+			break;
+		}
+
+		TakeDamage (damage,ob);
+		ob->state = NULL;		// mark for removal
+		return;
+	}
+
+	ob->tilex = ob->x >> TILESHIFT;
+	ob->tiley = ob->y >> TILESHIFT;
+
+}
+
+
+
+
+/*
+=============================================================================
+
+							GUARD
+
+=============================================================================
+*/
+
+//
+// guards
+//
+
+extern	statetype s_grdstand;
+
+extern	statetype s_grdpath1;
+extern	statetype s_grdpath1s;
+extern	statetype s_grdpath2;
+extern	statetype s_grdpath3;
+extern	statetype s_grdpath3s;
+extern	statetype s_grdpath4;
+
+extern	statetype s_grdpain;
+extern	statetype s_grdpain1;
+
+extern	statetype s_grdgiveup;
+
+extern	statetype s_grdshoot1;
+extern	statetype s_grdshoot2;
+extern	statetype s_grdshoot3;
+extern	statetype s_grdshoot4;
+
+extern	statetype s_grdchase1;
+extern	statetype s_grdchase1s;
+extern	statetype s_grdchase2;
+extern	statetype s_grdchase3;
+extern	statetype s_grdchase3s;
+extern	statetype s_grdchase4;
+
+extern	statetype s_grddie1;
+extern	statetype s_grddie1d;
+extern	statetype s_grddie2;
+extern	statetype s_grddie3;
+extern	statetype s_grddie4;
+
+statetype s_grdstand	= {true,SPR_GRD_S_1,0,T_Stand,NULL,&s_grdstand};
+
+statetype s_grdpath1 	= {true,SPR_GRD_W1_1,20,T_Path,NULL,&s_grdpath1s};
+statetype s_grdpath1s 	= {true,SPR_GRD_W1_1,5,NULL,NULL,&s_grdpath2};
+statetype s_grdpath2 	= {true,SPR_GRD_W2_1,15,T_Path,NULL,&s_grdpath3};
+statetype s_grdpath3 	= {true,SPR_GRD_W3_1,20,T_Path,NULL,&s_grdpath3s};
+statetype s_grdpath3s 	= {true,SPR_GRD_W3_1,5,NULL,NULL,&s_grdpath4};
+statetype s_grdpath4 	= {true,SPR_GRD_W4_1,15,T_Path,NULL,&s_grdpath1};
+
+statetype s_grdpain 	= {2,SPR_GRD_PAIN_1,10,NULL,NULL,&s_grdchase1};
+statetype s_grdpain1 	= {2,SPR_GRD_PAIN_2,10,NULL,NULL,&s_grdchase1};
+
+statetype s_grdshoot1 	= {false,SPR_GRD_SHOOT1,20,NULL,NULL,&s_grdshoot2};
+statetype s_grdshoot2 	= {false,SPR_GRD_SHOOT2,20,NULL,T_Shoot,&s_grdshoot3};
+statetype s_grdshoot3 	= {false,SPR_GRD_SHOOT3,20,NULL,NULL,&s_grdchase1};
+
+statetype s_grdchase1 	= {true,SPR_GRD_W1_1,10,T_Chase,NULL,&s_grdchase1s};
+statetype s_grdchase1s 	= {true,SPR_GRD_W1_1,3,NULL,NULL,&s_grdchase2};
+statetype s_grdchase2 	= {true,SPR_GRD_W2_1,8,T_Chase,NULL,&s_grdchase3};
+statetype s_grdchase3 	= {true,SPR_GRD_W3_1,10,T_Chase,NULL,&s_grdchase3s};
+statetype s_grdchase3s 	= {true,SPR_GRD_W3_1,3,NULL,NULL,&s_grdchase4};
+statetype s_grdchase4 	= {true,SPR_GRD_W4_1,8,T_Chase,NULL,&s_grdchase1};
+
+statetype s_grddie1		= {false,SPR_GRD_DIE_1,15,NULL,A_DeathScream,&s_grddie2};
+statetype s_grddie2		= {false,SPR_GRD_DIE_2,15,NULL,NULL,&s_grddie3};
+statetype s_grddie3		= {false,SPR_GRD_DIE_3,15,NULL,NULL,&s_grddie4};
+statetype s_grddie4		= {false,SPR_GRD_DEAD,0,NULL,NULL,&s_grddie4};
+
+
+#ifndef SPEAR
+//
+// ghosts
+//
+extern	statetype s_blinkychase1;
+extern	statetype s_blinkychase2;
+extern	statetype s_inkychase1;
+extern	statetype s_inkychase2;
+extern	statetype s_pinkychase1;
+extern	statetype s_pinkychase2;
+extern	statetype s_clydechase1;
+extern	statetype s_clydechase2;
+
+statetype s_blinkychase1 	= {false,SPR_BLINKY_W1,10,T_Ghosts,NULL,&s_blinkychase2};
+statetype s_blinkychase2 	= {false,SPR_BLINKY_W2,10,T_Ghosts,NULL,&s_blinkychase1};
+
+statetype s_inkychase1 		= {false,SPR_INKY_W1,10,T_Ghosts,NULL,&s_inkychase2};
+statetype s_inkychase2 		= {false,SPR_INKY_W2,10,T_Ghosts,NULL,&s_inkychase1};
+
+statetype s_pinkychase1 	= {false,SPR_PINKY_W1,10,T_Ghosts,NULL,&s_pinkychase2};
+statetype s_pinkychase2 	= {false,SPR_PINKY_W2,10,T_Ghosts,NULL,&s_pinkychase1};
+
+statetype s_clydechase1 	= {false,SPR_CLYDE_W1,10,T_Ghosts,NULL,&s_clydechase2};
+statetype s_clydechase2 	= {false,SPR_CLYDE_W2,10,T_Ghosts,NULL,&s_clydechase1};
+#endif
+
+//
+// dogs
+//
+
+extern	statetype s_dogpath1;
+extern	statetype s_dogpath1s;
+extern	statetype s_dogpath2;
+extern	statetype s_dogpath3;
+extern	statetype s_dogpath3s;
+extern	statetype s_dogpath4;
+
+extern	statetype s_dogjump1;
+extern	statetype s_dogjump2;
+extern	statetype s_dogjump3;
+extern	statetype s_dogjump4;
+extern	statetype s_dogjump5;
+
+extern	statetype s_dogchase1;
+extern	statetype s_dogchase1s;
+extern	statetype s_dogchase2;
+extern	statetype s_dogchase3;
+extern	statetype s_dogchase3s;
+extern	statetype s_dogchase4;
+
+extern	statetype s_dogdie1;
+extern	statetype s_dogdie1d;
+extern	statetype s_dogdie2;
+extern	statetype s_dogdie3;
+extern	statetype s_dogdead;
+
+statetype s_dogpath1 	= {true,SPR_DOG_W1_1,20,T_Path,NULL,&s_dogpath1s};
+statetype s_dogpath1s 	= {true,SPR_DOG_W1_1,5,NULL,NULL,&s_dogpath2};
+statetype s_dogpath2 	= {true,SPR_DOG_W2_1,15,T_Path,NULL,&s_dogpath3};
+statetype s_dogpath3 	= {true,SPR_DOG_W3_1,20,T_Path,NULL,&s_dogpath3s};
+statetype s_dogpath3s 	= {true,SPR_DOG_W3_1,5,NULL,NULL,&s_dogpath4};
+statetype s_dogpath4 	= {true,SPR_DOG_W4_1,15,T_Path,NULL,&s_dogpath1};
+
+statetype s_dogjump1 	= {false,SPR_DOG_JUMP1,10,NULL,NULL,&s_dogjump2};
+statetype s_dogjump2 	= {false,SPR_DOG_JUMP2,10,NULL,T_Bite,&s_dogjump3};
+statetype s_dogjump3 	= {false,SPR_DOG_JUMP3,10,NULL,NULL,&s_dogjump4};
+statetype s_dogjump4 	= {false,SPR_DOG_JUMP1,10,NULL,NULL,&s_dogjump5};
+statetype s_dogjump5 	= {false,SPR_DOG_W1_1,10,NULL,NULL,&s_dogchase1};
+
+statetype s_dogchase1 	= {true,SPR_DOG_W1_1,10,T_DogChase,NULL,&s_dogchase1s};
+statetype s_dogchase1s 	= {true,SPR_DOG_W1_1,3,NULL,NULL,&s_dogchase2};
+statetype s_dogchase2 	= {true,SPR_DOG_W2_1,8,T_DogChase,NULL,&s_dogchase3};
+statetype s_dogchase3 	= {true,SPR_DOG_W3_1,10,T_DogChase,NULL,&s_dogchase3s};
+statetype s_dogchase3s 	= {true,SPR_DOG_W3_1,3,NULL,NULL,&s_dogchase4};
+statetype s_dogchase4 	= {true,SPR_DOG_W4_1,8,T_DogChase,NULL,&s_dogchase1};
+
+statetype s_dogdie1		= {false,SPR_DOG_DIE_1,15,NULL,A_DeathScream,&s_dogdie2};
+statetype s_dogdie2		= {false,SPR_DOG_DIE_2,15,NULL,NULL,&s_dogdie3};
+statetype s_dogdie3		= {false,SPR_DOG_DIE_3,15,NULL,NULL,&s_dogdead};
+statetype s_dogdead		= {false,SPR_DOG_DEAD,15,NULL,NULL,&s_dogdead};
+
+
+//
+// officers
+//
+
+extern	statetype s_ofcstand;
+
+extern	statetype s_ofcpath1;
+extern	statetype s_ofcpath1s;
+extern	statetype s_ofcpath2;
+extern	statetype s_ofcpath3;
+extern	statetype s_ofcpath3s;
+extern	statetype s_ofcpath4;
+
+extern	statetype s_ofcpain;
+extern	statetype s_ofcpain1;
+
+extern	statetype s_ofcgiveup;
+
+extern	statetype s_ofcshoot1;
+extern	statetype s_ofcshoot2;
+extern	statetype s_ofcshoot3;
+extern	statetype s_ofcshoot4;
+
+extern	statetype s_ofcchase1;
+extern	statetype s_ofcchase1s;
+extern	statetype s_ofcchase2;
+extern	statetype s_ofcchase3;
+extern	statetype s_ofcchase3s;
+extern	statetype s_ofcchase4;
+
+extern	statetype s_ofcdie1;
+extern	statetype s_ofcdie2;
+extern	statetype s_ofcdie3;
+extern	statetype s_ofcdie4;
+extern	statetype s_ofcdie5;
+
+statetype s_ofcstand	= {true,SPR_OFC_S_1,0,T_Stand,NULL,&s_ofcstand};
+
+statetype s_ofcpath1 	= {true,SPR_OFC_W1_1,20,T_Path,NULL,&s_ofcpath1s};
+statetype s_ofcpath1s 	= {true,SPR_OFC_W1_1,5,NULL,NULL,&s_ofcpath2};
+statetype s_ofcpath2 	= {true,SPR_OFC_W2_1,15,T_Path,NULL,&s_ofcpath3};
+statetype s_ofcpath3 	= {true,SPR_OFC_W3_1,20,T_Path,NULL,&s_ofcpath3s};
+statetype s_ofcpath3s 	= {true,SPR_OFC_W3_1,5,NULL,NULL,&s_ofcpath4};
+statetype s_ofcpath4 	= {true,SPR_OFC_W4_1,15,T_Path,NULL,&s_ofcpath1};
+
+statetype s_ofcpain 	= {2,SPR_OFC_PAIN_1,10,NULL,NULL,&s_ofcchase1};
+statetype s_ofcpain1 	= {2,SPR_OFC_PAIN_2,10,NULL,NULL,&s_ofcchase1};
+
+statetype s_ofcshoot1 	= {false,SPR_OFC_SHOOT1,6,NULL,NULL,&s_ofcshoot2};
+statetype s_ofcshoot2 	= {false,SPR_OFC_SHOOT2,20,NULL,T_Shoot,&s_ofcshoot3};
+statetype s_ofcshoot3 	= {false,SPR_OFC_SHOOT3,10,NULL,NULL,&s_ofcchase1};
+
+statetype s_ofcchase1 	= {true,SPR_OFC_W1_1,10,T_Chase,NULL,&s_ofcchase1s};
+statetype s_ofcchase1s 	= {true,SPR_OFC_W1_1,3,NULL,NULL,&s_ofcchase2};
+statetype s_ofcchase2 	= {true,SPR_OFC_W2_1,8,T_Chase,NULL,&s_ofcchase3};
+statetype s_ofcchase3 	= {true,SPR_OFC_W3_1,10,T_Chase,NULL,&s_ofcchase3s};
+statetype s_ofcchase3s 	= {true,SPR_OFC_W3_1,3,NULL,NULL,&s_ofcchase4};
+statetype s_ofcchase4 	= {true,SPR_OFC_W4_1,8,T_Chase,NULL,&s_ofcchase1};
+
+statetype s_ofcdie1		= {false,SPR_OFC_DIE_1,11,NULL,A_DeathScream,&s_ofcdie2};
+statetype s_ofcdie2		= {false,SPR_OFC_DIE_2,11,NULL,NULL,&s_ofcdie3};
+statetype s_ofcdie3		= {false,SPR_OFC_DIE_3,11,NULL,NULL,&s_ofcdie4};
+statetype s_ofcdie4		= {false,SPR_OFC_DIE_4,11,NULL,NULL,&s_ofcdie5};
+statetype s_ofcdie5		= {false,SPR_OFC_DEAD,0,NULL,NULL,&s_ofcdie5};
+
+
+//
+// mutant
+//
+
+extern	statetype s_mutstand;
+
+extern	statetype s_mutpath1;
+extern	statetype s_mutpath1s;
+extern	statetype s_mutpath2;
+extern	statetype s_mutpath3;
+extern	statetype s_mutpath3s;
+extern	statetype s_mutpath4;
+
+extern	statetype s_mutpain;
+extern	statetype s_mutpain1;
+
+extern	statetype s_mutgiveup;
+
+extern	statetype s_mutshoot1;
+extern	statetype s_mutshoot2;
+extern	statetype s_mutshoot3;
+extern	statetype s_mutshoot4;
+
+extern	statetype s_mutchase1;
+extern	statetype s_mutchase1s;
+extern	statetype s_mutchase2;
+extern	statetype s_mutchase3;
+extern	statetype s_mutchase3s;
+extern	statetype s_mutchase4;
+
+extern	statetype s_mutdie1;
+extern	statetype s_mutdie2;
+extern	statetype s_mutdie3;
+extern	statetype s_mutdie4;
+extern	statetype s_mutdie5;
+
+statetype s_mutstand	= {true,SPR_MUT_S_1,0,T_Stand,NULL,&s_mutstand};
+
+statetype s_mutpath1 	= {true,SPR_MUT_W1_1,20,T_Path,NULL,&s_mutpath1s};
+statetype s_mutpath1s 	= {true,SPR_MUT_W1_1,5,NULL,NULL,&s_mutpath2};
+statetype s_mutpath2 	= {true,SPR_MUT_W2_1,15,T_Path,NULL,&s_mutpath3};
+statetype s_mutpath3 	= {true,SPR_MUT_W3_1,20,T_Path,NULL,&s_mutpath3s};
+statetype s_mutpath3s 	= {true,SPR_MUT_W3_1,5,NULL,NULL,&s_mutpath4};
+statetype s_mutpath4 	= {true,SPR_MUT_W4_1,15,T_Path,NULL,&s_mutpath1};
+
+statetype s_mutpain 	= {2,SPR_MUT_PAIN_1,10,NULL,NULL,&s_mutchase1};
+statetype s_mutpain1 	= {2,SPR_MUT_PAIN_2,10,NULL,NULL,&s_mutchase1};
+
+statetype s_mutshoot1 	= {false,SPR_MUT_SHOOT1,6,NULL,T_Shoot,&s_mutshoot2};
+statetype s_mutshoot2 	= {false,SPR_MUT_SHOOT2,20,NULL,NULL,&s_mutshoot3};
+statetype s_mutshoot3 	= {false,SPR_MUT_SHOOT3,10,NULL,T_Shoot,&s_mutshoot4};
+statetype s_mutshoot4 	= {false,SPR_MUT_SHOOT4,20,NULL,NULL,&s_mutchase1};
+
+statetype s_mutchase1 	= {true,SPR_MUT_W1_1,10,T_Chase,NULL,&s_mutchase1s};
+statetype s_mutchase1s 	= {true,SPR_MUT_W1_1,3,NULL,NULL,&s_mutchase2};
+statetype s_mutchase2 	= {true,SPR_MUT_W2_1,8,T_Chase,NULL,&s_mutchase3};
+statetype s_mutchase3 	= {true,SPR_MUT_W3_1,10,T_Chase,NULL,&s_mutchase3s};
+statetype s_mutchase3s 	= {true,SPR_MUT_W3_1,3,NULL,NULL,&s_mutchase4};
+statetype s_mutchase4 	= {true,SPR_MUT_W4_1,8,T_Chase,NULL,&s_mutchase1};
+
+statetype s_mutdie1		= {false,SPR_MUT_DIE_1,7,NULL,A_DeathScream,&s_mutdie2};
+statetype s_mutdie2		= {false,SPR_MUT_DIE_2,7,NULL,NULL,&s_mutdie3};
+statetype s_mutdie3		= {false,SPR_MUT_DIE_3,7,NULL,NULL,&s_mutdie4};
+statetype s_mutdie4		= {false,SPR_MUT_DIE_4,7,NULL,NULL,&s_mutdie5};
+statetype s_mutdie5		= {false,SPR_MUT_DEAD,0,NULL,NULL,&s_mutdie5};
+
+
+//
+// SS
+//
+
+extern	statetype s_ssstand;
+
+extern	statetype s_sspath1;
+extern	statetype s_sspath1s;
+extern	statetype s_sspath2;
+extern	statetype s_sspath3;
+extern	statetype s_sspath3s;
+extern	statetype s_sspath4;
+
+extern	statetype s_sspain;
+extern	statetype s_sspain1;
+
+extern	statetype s_ssshoot1;
+extern	statetype s_ssshoot2;
+extern	statetype s_ssshoot3;
+extern	statetype s_ssshoot4;
+extern	statetype s_ssshoot5;
+extern	statetype s_ssshoot6;
+extern	statetype s_ssshoot7;
+extern	statetype s_ssshoot8;
+extern	statetype s_ssshoot9;
+
+extern	statetype s_sschase1;
+extern	statetype s_sschase1s;
+extern	statetype s_sschase2;
+extern	statetype s_sschase3;
+extern	statetype s_sschase3s;
+extern	statetype s_sschase4;
+
+extern	statetype s_ssdie1;
+extern	statetype s_ssdie2;
+extern	statetype s_ssdie3;
+extern	statetype s_ssdie4;
+
+statetype s_ssstand	= {true,SPR_SS_S_1,0,T_Stand,NULL,&s_ssstand};
+
+statetype s_sspath1 	= {true,SPR_SS_W1_1,20,T_Path,NULL,&s_sspath1s};
+statetype s_sspath1s 	= {true,SPR_SS_W1_1,5,NULL,NULL,&s_sspath2};
+statetype s_sspath2 	= {true,SPR_SS_W2_1,15,T_Path,NULL,&s_sspath3};
+statetype s_sspath3 	= {true,SPR_SS_W3_1,20,T_Path,NULL,&s_sspath3s};
+statetype s_sspath3s 	= {true,SPR_SS_W3_1,5,NULL,NULL,&s_sspath4};
+statetype s_sspath4 	= {true,SPR_SS_W4_1,15,T_Path,NULL,&s_sspath1};
+
+statetype s_sspain 		= {2,SPR_SS_PAIN_1,10,NULL,NULL,&s_sschase1};
+statetype s_sspain1 	= {2,SPR_SS_PAIN_2,10,NULL,NULL,&s_sschase1};
+
+statetype s_ssshoot1 	= {false,SPR_SS_SHOOT1,20,NULL,NULL,&s_ssshoot2};
+statetype s_ssshoot2 	= {false,SPR_SS_SHOOT2,20,NULL,T_Shoot,&s_ssshoot3};
+statetype s_ssshoot3 	= {false,SPR_SS_SHOOT3,10,NULL,NULL,&s_ssshoot4};
+statetype s_ssshoot4 	= {false,SPR_SS_SHOOT2,10,NULL,T_Shoot,&s_ssshoot5};
+statetype s_ssshoot5 	= {false,SPR_SS_SHOOT3,10,NULL,NULL,&s_ssshoot6};
+statetype s_ssshoot6 	= {false,SPR_SS_SHOOT2,10,NULL,T_Shoot,&s_ssshoot7};
+statetype s_ssshoot7  	= {false,SPR_SS_SHOOT3,10,NULL,NULL,&s_ssshoot8};
+statetype s_ssshoot8  	= {false,SPR_SS_SHOOT2,10,NULL,T_Shoot,&s_ssshoot9};
+statetype s_ssshoot9  	= {false,SPR_SS_SHOOT3,10,NULL,NULL,&s_sschase1};
+
+statetype s_sschase1 	= {true,SPR_SS_W1_1,10,T_Chase,NULL,&s_sschase1s};
+statetype s_sschase1s 	= {true,SPR_SS_W1_1,3,NULL,NULL,&s_sschase2};
+statetype s_sschase2 	= {true,SPR_SS_W2_1,8,T_Chase,NULL,&s_sschase3};
+statetype s_sschase3 	= {true,SPR_SS_W3_1,10,T_Chase,NULL,&s_sschase3s};
+statetype s_sschase3s 	= {true,SPR_SS_W3_1,3,NULL,NULL,&s_sschase4};
+statetype s_sschase4 	= {true,SPR_SS_W4_1,8,T_Chase,NULL,&s_sschase1};
+
+statetype s_ssdie1		= {false,SPR_SS_DIE_1,15,NULL,A_DeathScream,&s_ssdie2};
+statetype s_ssdie2		= {false,SPR_SS_DIE_2,15,NULL,NULL,&s_ssdie3};
+statetype s_ssdie3		= {false,SPR_SS_DIE_3,15,NULL,NULL,&s_ssdie4};
+statetype s_ssdie4		= {false,SPR_SS_DEAD,0,NULL,NULL,&s_ssdie4};
+
+
+#ifndef SPEAR
+//
+// hans
+//
+extern	statetype s_bossstand;
+
+extern	statetype s_bosschase1;
+extern	statetype s_bosschase1s;
+extern	statetype s_bosschase2;
+extern	statetype s_bosschase3;
+extern	statetype s_bosschase3s;
+extern	statetype s_bosschase4;
+
+extern	statetype s_bossdie1;
+extern	statetype s_bossdie2;
+extern	statetype s_bossdie3;
+extern	statetype s_bossdie4;
+
+extern	statetype s_bossshoot1;
+extern	statetype s_bossshoot2;
+extern	statetype s_bossshoot3;
+extern	statetype s_bossshoot4;
+extern	statetype s_bossshoot5;
+extern	statetype s_bossshoot6;
+extern	statetype s_bossshoot7;
+extern	statetype s_bossshoot8;
+
+
+statetype s_bossstand	= {false,SPR_BOSS_W1,0,T_Stand,NULL,&s_bossstand};
+
+statetype s_bosschase1 	= {false,SPR_BOSS_W1,10,T_Chase,NULL,&s_bosschase1s};
+statetype s_bosschase1s	= {false,SPR_BOSS_W1,3,NULL,NULL,&s_bosschase2};
+statetype s_bosschase2 	= {false,SPR_BOSS_W2,8,T_Chase,NULL,&s_bosschase3};
+statetype s_bosschase3 	= {false,SPR_BOSS_W3,10,T_Chase,NULL,&s_bosschase3s};
+statetype s_bosschase3s	= {false,SPR_BOSS_W3,3,NULL,NULL,&s_bosschase4};
+statetype s_bosschase4 	= {false,SPR_BOSS_W4,8,T_Chase,NULL,&s_bosschase1};
+
+statetype s_bossdie1	= {false,SPR_BOSS_DIE1,15,NULL,A_DeathScream,&s_bossdie2};
+statetype s_bossdie2	= {false,SPR_BOSS_DIE2,15,NULL,NULL,&s_bossdie3};
+statetype s_bossdie3	= {false,SPR_BOSS_DIE3,15,NULL,NULL,&s_bossdie4};
+statetype s_bossdie4	= {false,SPR_BOSS_DEAD,0,NULL,NULL,&s_bossdie4};
+
+statetype s_bossshoot1 	= {false,SPR_BOSS_SHOOT1,30,NULL,NULL,&s_bossshoot2};
+statetype s_bossshoot2 	= {false,SPR_BOSS_SHOOT2,10,NULL,T_Shoot,&s_bossshoot3};
+statetype s_bossshoot3 	= {false,SPR_BOSS_SHOOT3,10,NULL,T_Shoot,&s_bossshoot4};
+statetype s_bossshoot4 	= {false,SPR_BOSS_SHOOT2,10,NULL,T_Shoot,&s_bossshoot5};
+statetype s_bossshoot5 	= {false,SPR_BOSS_SHOOT3,10,NULL,T_Shoot,&s_bossshoot6};
+statetype s_bossshoot6 	= {false,SPR_BOSS_SHOOT2,10,NULL,T_Shoot,&s_bossshoot7};
+statetype s_bossshoot7 	= {false,SPR_BOSS_SHOOT3,10,NULL,T_Shoot,&s_bossshoot8};
+statetype s_bossshoot8 	= {false,SPR_BOSS_SHOOT1,10,NULL,NULL,&s_bosschase1};
+
+
+//
+// gretel
+//
+extern	statetype s_gretelstand;
+
+extern	statetype s_gretelchase1;
+extern	statetype s_gretelchase1s;
+extern	statetype s_gretelchase2;
+extern	statetype s_gretelchase3;
+extern	statetype s_gretelchase3s;
+extern	statetype s_gretelchase4;
+
+extern	statetype s_greteldie1;
+extern	statetype s_greteldie2;
+extern	statetype s_greteldie3;
+extern	statetype s_greteldie4;
+
+extern	statetype s_gretelshoot1;
+extern	statetype s_gretelshoot2;
+extern	statetype s_gretelshoot3;
+extern	statetype s_gretelshoot4;
+extern	statetype s_gretelshoot5;
+extern	statetype s_gretelshoot6;
+extern	statetype s_gretelshoot7;
+extern	statetype s_gretelshoot8;
+
+
+statetype s_gretelstand	= {false,SPR_GRETEL_W1,0,T_Stand,NULL,&s_gretelstand};
+
+statetype s_gretelchase1 	= {false,SPR_GRETEL_W1,10,T_Chase,NULL,&s_gretelchase1s};
+statetype s_gretelchase1s	= {false,SPR_GRETEL_W1,3,NULL,NULL,&s_gretelchase2};
+statetype s_gretelchase2 	= {false,SPR_GRETEL_W2,8,T_Chase,NULL,&s_gretelchase3};
+statetype s_gretelchase3 	= {false,SPR_GRETEL_W3,10,T_Chase,NULL,&s_gretelchase3s};
+statetype s_gretelchase3s	= {false,SPR_GRETEL_W3,3,NULL,NULL,&s_gretelchase4};
+statetype s_gretelchase4 	= {false,SPR_GRETEL_W4,8,T_Chase,NULL,&s_gretelchase1};
+
+statetype s_greteldie1	= {false,SPR_GRETEL_DIE1,15,NULL,A_DeathScream,&s_greteldie2};
+statetype s_greteldie2	= {false,SPR_GRETEL_DIE2,15,NULL,NULL,&s_greteldie3};
+statetype s_greteldie3	= {false,SPR_GRETEL_DIE3,15,NULL,NULL,&s_greteldie4};
+statetype s_greteldie4	= {false,SPR_GRETEL_DEAD,0,NULL,NULL,&s_greteldie4};
+
+statetype s_gretelshoot1 	= {false,SPR_GRETEL_SHOOT1,30,NULL,NULL,&s_gretelshoot2};
+statetype s_gretelshoot2 	= {false,SPR_GRETEL_SHOOT2,10,NULL,T_Shoot,&s_gretelshoot3};
+statetype s_gretelshoot3 	= {false,SPR_GRETEL_SHOOT3,10,NULL,T_Shoot,&s_gretelshoot4};
+statetype s_gretelshoot4 	= {false,SPR_GRETEL_SHOOT2,10,NULL,T_Shoot,&s_gretelshoot5};
+statetype s_gretelshoot5 	= {false,SPR_GRETEL_SHOOT3,10,NULL,T_Shoot,&s_gretelshoot6};
+statetype s_gretelshoot6 	= {false,SPR_GRETEL_SHOOT2,10,NULL,T_Shoot,&s_gretelshoot7};
+statetype s_gretelshoot7 	= {false,SPR_GRETEL_SHOOT3,10,NULL,T_Shoot,&s_gretelshoot8};
+statetype s_gretelshoot8 	= {false,SPR_GRETEL_SHOOT1,10,NULL,NULL,&s_gretelchase1};
+#endif
+
+
+/*
+===============
+=
+= SpawnStand
+=
+===============
+*/
+
+void SpawnStand (enemy_t which, int tilex, int tiley, int dir)
+{
+	unsigned	far *map,tile;
+
+	switch (which)
+	{
+	case en_guard:
+		SpawnNewObj (tilex,tiley,&s_grdstand);
+		new->speed = SPDPATROL;
+		if (!loadedgame)
+		  gamestate.killtotal++;
+		break;
+
+	case en_officer:
+		SpawnNewObj (tilex,tiley,&s_ofcstand);
+		new->speed = SPDPATROL;
+		if (!loadedgame)
+		  gamestate.killtotal++;
+		break;
+
+	case en_mutant:
+		SpawnNewObj (tilex,tiley,&s_mutstand);
+		new->speed = SPDPATROL;
+		if (!loadedgame)
+		  gamestate.killtotal++;
+		break;
+
+	case en_ss:
+		SpawnNewObj (tilex,tiley,&s_ssstand);
+		new->speed = SPDPATROL;
+		if (!loadedgame)
+		  gamestate.killtotal++;
+		break;
+	}
+
+
+	map = mapsegs[0]+farmapylookup[tiley]+tilex;
+	if (*map == AMBUSHTILE)
+	{
+		tilemap[tilex][tiley] = 0;
+
+		if (*(map+1) >= AREATILE)
+			tile = *(map+1);
+		if (*(map-mapwidth) >= AREATILE)
+			tile = *(map-mapwidth);
+		if (*(map+mapwidth) >= AREATILE)
+			tile = *(map+mapwidth);
+		if ( *(map-1) >= AREATILE)
+			tile = *(map-1);
+
+		*map = tile;
+		new->areanumber = tile-AREATILE;
+
+		new->flags |= FL_AMBUSH;
+	}
+
+	new->obclass = guardobj+which;
+	new->hitpoints = starthitpoints[gamestate.difficulty][which];
+	new->dir = dir*2;
+	new->flags |= FL_SHOOTABLE;
+}
+
+
+
+/*
+===============
+=
+= SpawnDeadGuard
+=
+===============
+*/
+
+void SpawnDeadGuard (int tilex, int tiley)
+{
+	SpawnNewObj (tilex,tiley,&s_grddie4);
+	new->obclass = inertobj;
+}
+
+
+
+#ifndef SPEAR
+/*
+===============
+=
+= SpawnBoss
+=
+===============
+*/
+
+void SpawnBoss (int tilex, int tiley)
+{
+	unsigned	far *map,tile;
+
+	SpawnNewObj (tilex,tiley,&s_bossstand);
+	new->speed = SPDPATROL;
+
+	new->obclass = bossobj;
+	new->hitpoints = starthitpoints[gamestate.difficulty][en_boss];
+	new->dir = south;
+	new->flags |= FL_SHOOTABLE|FL_AMBUSH;
+	if (!loadedgame)
+	  gamestate.killtotal++;
+}
+
+/*
+===============
+=
+= SpawnGretel
+=
+===============
+*/
+
+void SpawnGretel (int tilex, int tiley)
+{
+	unsigned	far *map,tile;
+
+	SpawnNewObj (tilex,tiley,&s_gretelstand);
+	new->speed = SPDPATROL;
+
+	new->obclass = gretelobj;
+	new->hitpoints = starthitpoints[gamestate.difficulty][en_gretel];
+	new->dir = north;
+	new->flags |= FL_SHOOTABLE|FL_AMBUSH;
+	if (!loadedgame)
+	  gamestate.killtotal++;
+}
+#endif
+
+/*
+===============
+=
+= SpawnPatrol
+=
+===============
+*/
+
+void SpawnPatrol (enemy_t which, int tilex, int tiley, int dir)
+{
+	switch (which)
+	{
+	case en_guard:
+		SpawnNewObj (tilex,tiley,&s_grdpath1);
+		new->speed = SPDPATROL;
+		if (!loadedgame)
+		  gamestate.killtotal++;
+		break;
+
+	case en_officer:
+		SpawnNewObj (tilex,tiley,&s_ofcpath1);
+		new->speed = SPDPATROL;
+		if (!loadedgame)
+		  gamestate.killtotal++;
+		break;
+
+	case en_ss:
+		SpawnNewObj (tilex,tiley,&s_sspath1);
+		new->speed = SPDPATROL;
+		if (!loadedgame)
+		  gamestate.killtotal++;
+		break;
+
+	case en_mutant:
+		SpawnNewObj (tilex,tiley,&s_mutpath1);
+		new->speed = SPDPATROL;
+		if (!loadedgame)
+		  gamestate.killtotal++;
+		break;
+
+	case en_dog:
+		SpawnNewObj (tilex,tiley,&s_dogpath1);
+		new->speed = SPDDOG;
+		if (!loadedgame)
+		  gamestate.killtotal++;
+		break;
+	}
+
+	new->obclass = guardobj+which;
+	new->dir = dir*2;
+	new->hitpoints = starthitpoints[gamestate.difficulty][which];
+	new->distance = tileglobal;
+	new->flags |= FL_SHOOTABLE;
+	new->active = true;
+
+	actorat[new->tilex][new->tiley] = NULL;		// don't use original spot
+
+	switch (dir)
+	{
+	case 0:
+		new->tilex++;
+		break;
+	case 1:
+		new->tiley--;
+		break;
+	case 2:
+		new->tilex--;
+		break;
+	case 3:
+		new->tiley++;
+		break;
+	}
+
+	actorat[new->tilex][new->tiley] = new;
+}
+
+
+
+/*
+==================
+=
+= A_DeathScream
+=
+==================
+*/
+
+void A_DeathScream (objtype *ob)
+{
+#ifndef UPLOAD
+#ifndef SPEAR
+	if (mapon==9 && !US_RndT())
+#else
+	if ((mapon==18 || mapon==19) && !US_RndT())
+#endif
+	{
+	 switch(ob->obclass)
+	 {
+	  case mutantobj:
+	  case guardobj:
+	  case officerobj:
+	  case ssobj:
+	  case dogobj:
+		PlaySoundLocActor(DEATHSCREAM6SND,ob);
+		return;
+	 }
+	}
+#endif
+
+	switch (ob->obclass)
+	{
+	case mutantobj:
+		PlaySoundLocActor(AHHHGSND,ob);
+		break;
+
+	case guardobj:
+		{
+		 int sounds[9]={ DEATHSCREAM1SND,
+				 DEATHSCREAM2SND,
+				 DEATHSCREAM3SND,
+				 DEATHSCREAM4SND,
+				 DEATHSCREAM5SND,
+				 DEATHSCREAM7SND,
+				 DEATHSCREAM8SND,
+				 DEATHSCREAM9SND
+				 };
+
+		 #ifndef UPLOAD
+		 PlaySoundLocActor(sounds[US_RndT()%8],ob);
+		 #else
+		 PlaySoundLocActor(sounds[US_RndT()%2],ob);
+		 #endif
+		}
+		break;
+	case officerobj:
+		PlaySoundLocActor(NEINSOVASSND,ob);
+		break;
+	case ssobj:
+		PlaySoundLocActor(LEBENSND,ob);	// JAB
+		break;
+	case dogobj:
+		PlaySoundLocActor(DOGDEATHSND,ob);	// JAB
+		break;
+#ifndef SPEAR
+	case bossobj:
+		SD_PlaySound(MUTTISND);				// JAB
+		break;
+	case schabbobj:
+		SD_PlaySound(MEINGOTTSND);
+		break;
+	case fakeobj:
+		SD_PlaySound(HITLERHASND);
+		break;
+	case mechahitlerobj:
+		SD_PlaySound(SCHEISTSND);
+		break;
+	case realhitlerobj:
+		SD_PlaySound(EVASND);
+		break;
+	case gretelobj:
+		SD_PlaySound(MEINSND);
+		break;
+	case giftobj:
+		SD_PlaySound(DONNERSND);
+		break;
+	case fatobj:
+		SD_PlaySound(ROSESND);
+		break;
+#else
+	case spectreobj:
+		SD_PlaySound(GHOSTFADESND);
+		break;
+	case angelobj:
+		SD_PlaySound(ANGELDEATHSND);
+		break;
+	case transobj:
+		SD_PlaySound(TRANSDEATHSND);
+		break;
+	case uberobj:
+		SD_PlaySound(UBERDEATHSND);
+		break;
+	case willobj:
+		SD_PlaySound(WILHELMDEATHSND);
+		break;
+	case deathobj:
+		SD_PlaySound(KNIGHTDEATHSND);
+		break;
+#endif
+	}
+}
+
+
+/*
+=============================================================================
+
+						 SPEAR ACTORS
+
+=============================================================================
+*/
+
+#ifdef SPEAR
+
+void T_Launch (objtype *ob);
+void T_Will (objtype *ob);
+
+extern	statetype s_angelshoot1;
+extern	statetype s_deathshoot1;
+extern	statetype s_spark1;
+
+//
+// trans
+//
+extern	statetype s_transstand;
+
+extern	statetype s_transchase1;
+extern	statetype s_transchase1s;
+extern	statetype s_transchase2;
+extern	statetype s_transchase3;
+extern	statetype s_transchase3s;
+extern	statetype s_transchase4;
+
+extern	statetype s_transdie0;
+extern	statetype s_transdie01;
+extern	statetype s_transdie1;
+extern	statetype s_transdie2;
+extern	statetype s_transdie3;
+extern	statetype s_transdie4;
+
+extern	statetype s_transshoot1;
+extern	statetype s_transshoot2;
+extern	statetype s_transshoot3;
+extern	statetype s_transshoot4;
+extern	statetype s_transshoot5;
+extern	statetype s_transshoot6;
+extern	statetype s_transshoot7;
+extern	statetype s_transshoot8;
+
+
+statetype s_transstand	= {false,SPR_TRANS_W1,0,T_Stand,NULL,&s_transstand};
+
+statetype s_transchase1 	= {false,SPR_TRANS_W1,10,T_Chase,NULL,&s_transchase1s};
+statetype s_transchase1s	= {false,SPR_TRANS_W1,3,NULL,NULL,&s_transchase2};
+statetype s_transchase2 	= {false,SPR_TRANS_W2,8,T_Chase,NULL,&s_transchase3};
+statetype s_transchase3 	= {false,SPR_TRANS_W3,10,T_Chase,NULL,&s_transchase3s};
+statetype s_transchase3s	= {false,SPR_TRANS_W3,3,NULL,NULL,&s_transchase4};
+statetype s_transchase4 	= {false,SPR_TRANS_W4,8,T_Chase,NULL,&s_transchase1};
+
+statetype s_transdie0	= {false,SPR_TRANS_W1,1,NULL,A_DeathScream,&s_transdie01};
+statetype s_transdie01	= {false,SPR_TRANS_W1,1,NULL,NULL,&s_transdie1};
+statetype s_transdie1	= {false,SPR_TRANS_DIE1,15,NULL,NULL,&s_transdie2};
+statetype s_transdie2	= {false,SPR_TRANS_DIE2,15,NULL,NULL,&s_transdie3};
+statetype s_transdie3	= {false,SPR_TRANS_DIE3,15,NULL,NULL,&s_transdie4};
+statetype s_transdie4	= {false,SPR_TRANS_DEAD,0,NULL,NULL,&s_transdie4};
+
+statetype s_transshoot1 	= {false,SPR_TRANS_SHOOT1,30,NULL,NULL,&s_transshoot2};
+statetype s_transshoot2 	= {false,SPR_TRANS_SHOOT2,10,NULL,T_Shoot,&s_transshoot3};
+statetype s_transshoot3 	= {false,SPR_TRANS_SHOOT3,10,NULL,T_Shoot,&s_transshoot4};
+statetype s_transshoot4 	= {false,SPR_TRANS_SHOOT2,10,NULL,T_Shoot,&s_transshoot5};
+statetype s_transshoot5 	= {false,SPR_TRANS_SHOOT3,10,NULL,T_Shoot,&s_transshoot6};
+statetype s_transshoot6 	= {false,SPR_TRANS_SHOOT2,10,NULL,T_Shoot,&s_transshoot7};
+statetype s_transshoot7 	= {false,SPR_TRANS_SHOOT3,10,NULL,T_Shoot,&s_transshoot8};
+statetype s_transshoot8 	= {false,SPR_TRANS_SHOOT1,10,NULL,NULL,&s_transchase1};
+
+
+/*
+===============
+=
+= SpawnTrans
+=
+===============
+*/
+
+void SpawnTrans (int tilex, int tiley)
+{
+	unsigned	far *map,tile;
+
+	if (SoundBlasterPresent && DigiMode != sds_Off)
+		s_transdie01.tictime = 105;
+
+	SpawnNewObj (tilex,tiley,&s_transstand);
+	new->obclass = transobj;
+	new->hitpoints = starthitpoints[gamestate.difficulty][en_trans];
+	new->flags |= FL_SHOOTABLE|FL_AMBUSH;
+	if (!loadedgame)
+	  gamestate.killtotal++;
+}
+
+
+//
+// uber
+//
+void T_UShoot (objtype *ob);
+
+extern	statetype s_uberstand;
+
+extern	statetype s_uberchase1;
+extern	statetype s_uberchase1s;
+extern	statetype s_uberchase2;
+extern	statetype s_uberchase3;
+extern	statetype s_uberchase3s;
+extern	statetype s_uberchase4;
+
+extern	statetype s_uberdie0;
+extern	statetype s_uberdie01;
+extern	statetype s_uberdie1;
+extern	statetype s_uberdie2;
+extern	statetype s_uberdie3;
+extern	statetype s_uberdie4;
+extern	statetype s_uberdie5;
+
+extern	statetype s_ubershoot1;
+extern	statetype s_ubershoot2;
+extern	statetype s_ubershoot3;
+extern	statetype s_ubershoot4;
+extern	statetype s_ubershoot5;
+extern	statetype s_ubershoot6;
+extern	statetype s_ubershoot7;
+
+
+statetype s_uberstand	= {false,SPR_UBER_W1,0,T_Stand,NULL,&s_uberstand};
+
+statetype s_uberchase1 	= {false,SPR_UBER_W1,10,T_Chase,NULL,&s_uberchase1s};
+statetype s_uberchase1s	= {false,SPR_UBER_W1,3,NULL,NULL,&s_uberchase2};
+statetype s_uberchase2 	= {false,SPR_UBER_W2,8,T_Chase,NULL,&s_uberchase3};
+statetype s_uberchase3 	= {false,SPR_UBER_W3,10,T_Chase,NULL,&s_uberchase3s};
+statetype s_uberchase3s	= {false,SPR_UBER_W3,3,NULL,NULL,&s_uberchase4};
+statetype s_uberchase4 	= {false,SPR_UBER_W4,8,T_Chase,NULL,&s_uberchase1};
+
+statetype s_uberdie0	= {false,SPR_UBER_W1,1,NULL,A_DeathScream,&s_uberdie01};
+statetype s_uberdie01	= {false,SPR_UBER_W1,1,NULL,NULL,&s_uberdie1};
+statetype s_uberdie1	= {false,SPR_UBER_DIE1,15,NULL,NULL,&s_uberdie2};
+statetype s_uberdie2	= {false,SPR_UBER_DIE2,15,NULL,NULL,&s_uberdie3};
+statetype s_uberdie3	= {false,SPR_UBER_DIE3,15,NULL,NULL,&s_uberdie4};
+statetype s_uberdie4	= {false,SPR_UBER_DIE4,15,NULL,NULL,&s_uberdie5};
+statetype s_uberdie5	= {false,SPR_UBER_DEAD,0,NULL,NULL,&s_uberdie5};
+
+statetype s_ubershoot1 	= {false,SPR_UBER_SHOOT1,30,NULL,NULL,&s_ubershoot2};
+statetype s_ubershoot2 	= {false,SPR_UBER_SHOOT2,12,NULL,T_UShoot,&s_ubershoot3};
+statetype s_ubershoot3 	= {false,SPR_UBER_SHOOT3,12,NULL,T_UShoot,&s_ubershoot4};
+statetype s_ubershoot4 	= {false,SPR_UBER_SHOOT4,12,NULL,T_UShoot,&s_ubershoot5};
+statetype s_ubershoot5 	= {false,SPR_UBER_SHOOT3,12,NULL,T_UShoot,&s_ubershoot6};
+statetype s_ubershoot6 	= {false,SPR_UBER_SHOOT2,12,NULL,T_UShoot,&s_ubershoot7};
+statetype s_ubershoot7 	= {false,SPR_UBER_SHOOT1,12,NULL,NULL,&s_uberchase1};
+
+
+/*
+===============
+=
+= SpawnUber
+=
+===============
+*/
+
+void SpawnUber (int tilex, int tiley)
+{
+	unsigned	far *map,tile;
+
+	if (SoundBlasterPresent && DigiMode != sds_Off)
+		s_uberdie01.tictime = 70;
+
+	SpawnNewObj (tilex,tiley,&s_uberstand);
+	new->obclass = uberobj;
+	new->hitpoints = starthitpoints[gamestate.difficulty][en_uber];
+	new->flags |= FL_SHOOTABLE|FL_AMBUSH;
+	if (!loadedgame)
+	  gamestate.killtotal++;
+}
+
+
+/*
+===============
+=
+= T_UShoot
+=
+===============
+*/
+
+void T_UShoot (objtype *ob)
+{
+	int	dx,dy,dist;
+
+	T_Shoot (ob);
+
+	dx = abs(ob->tilex - player->tilex);
+	dy = abs(ob->tiley - player->tiley);
+	dist = dx>dy ? dx : dy;
+	if (dist <= 1)
+		TakeDamage (10,ob);
+}
+
+
+//
+// will
+//
+extern	statetype s_willstand;
+
+extern	statetype s_willchase1;
+extern	statetype s_willchase1s;
+extern	statetype s_willchase2;
+extern	statetype s_willchase3;
+extern	statetype s_willchase3s;
+extern	statetype s_willchase4;
+
+extern	statetype s_willdie1;
+extern	statetype s_willdie2;
+extern	statetype s_willdie3;
+extern	statetype s_willdie4;
+extern	statetype s_willdie5;
+extern	statetype s_willdie6;
+
+extern	statetype s_willshoot1;
+extern	statetype s_willshoot2;
+extern	statetype s_willshoot3;
+extern	statetype s_willshoot4;
+extern	statetype s_willshoot5;
+extern	statetype s_willshoot6;
+
+
+statetype s_willstand	= {false,SPR_WILL_W1,0,T_Stand,NULL,&s_willstand};
+
+statetype s_willchase1 	= {false,SPR_WILL_W1,10,T_Will,NULL,&s_willchase1s};
+statetype s_willchase1s	= {false,SPR_WILL_W1,3,NULL,NULL,&s_willchase2};
+statetype s_willchase2 	= {false,SPR_WILL_W2,8,T_Will,NULL,&s_willchase3};
+statetype s_willchase3 	= {false,SPR_WILL_W3,10,T_Will,NULL,&s_willchase3s};
+statetype s_willchase3s	= {false,SPR_WILL_W3,3,NULL,NULL,&s_willchase4};
+statetype s_willchase4 	= {false,SPR_WILL_W4,8,T_Will,NULL,&s_willchase1};
+
+statetype s_willdeathcam	= {false,SPR_WILL_W1,1,NULL,NULL,&s_willdie1};
+
+statetype s_willdie1	= {false,SPR_WILL_W1,1,NULL,A_DeathScream,&s_willdie2};
+statetype s_willdie2	= {false,SPR_WILL_W1,10,NULL,NULL,&s_willdie3};
+statetype s_willdie3	= {false,SPR_WILL_DIE1,10,NULL,NULL,&s_willdie4};
+statetype s_willdie4	= {false,SPR_WILL_DIE2,10,NULL,NULL,&s_willdie5};
+statetype s_willdie5	= {false,SPR_WILL_DIE3,10,NULL,NULL,&s_willdie6};
+statetype s_willdie6	= {false,SPR_WILL_DEAD,20,NULL,NULL,&s_willdie6};
+
+statetype s_willshoot1 	= {false,SPR_WILL_SHOOT1,30,NULL,NULL,&s_willshoot2};
+statetype s_willshoot2 	= {false,SPR_WILL_SHOOT2,10,NULL,T_Launch,&s_willshoot3};
+statetype s_willshoot3 	= {false,SPR_WILL_SHOOT3,10,NULL,T_Shoot,&s_willshoot4};
+statetype s_willshoot4 	= {false,SPR_WILL_SHOOT4,10,NULL,T_Shoot,&s_willshoot5};
+statetype s_willshoot5 	= {false,SPR_WILL_SHOOT3,10,NULL,T_Shoot,&s_willshoot6};
+statetype s_willshoot6 	= {false,SPR_WILL_SHOOT4,10,NULL,T_Shoot,&s_willchase1};
+
+
+/*
+===============
+=
+= SpawnWill
+=
+===============
+*/
+
+void SpawnWill (int tilex, int tiley)
+{
+	unsigned	far *map,tile;
+
+	if (SoundBlasterPresent && DigiMode != sds_Off)
+		s_willdie2.tictime = 70;
+
+	SpawnNewObj (tilex,tiley,&s_willstand);
+	new->obclass = willobj;
+	new->hitpoints = starthitpoints[gamestate.difficulty][en_will];
+	new->flags |= FL_SHOOTABLE|FL_AMBUSH;
+	if (!loadedgame)
+	  gamestate.killtotal++;
+}
+
+
+/*
+================
+=
+= T_Will
+=
+================
+*/
+
+void T_Will (objtype *ob)
+{
+	long move;
+	int	dx,dy,dist;
+	boolean	dodge;
+
+	dodge = false;
+	dx = abs(ob->tilex - player->tilex);
+	dy = abs(ob->tiley - player->tiley);
+	dist = dx>dy ? dx : dy;
+
+	if (CheckLine(ob))						// got a shot at player?
+	{
+		if ( US_RndT() < (tics<<3) )
+		{
+		//
+		// go into attack frame
+		//
+			if (ob->obclass == willobj)
+				NewState (ob,&s_willshoot1);
+			else if (ob->obclass == angelobj)
+				NewState (ob,&s_angelshoot1);
+			else
+				NewState (ob,&s_deathshoot1);
+			return;
+		}
+		dodge = true;
+	}
+
+	if (ob->dir == nodir)
+	{
+		if (dodge)
+			SelectDodgeDir (ob);
+		else
+			SelectChaseDir (ob);
+		if (ob->dir == nodir)
+			return;							// object is blocked in
+	}
+
+	move = ob->speed*tics;
+
+	while (move)
+	{
+		if (ob->distance < 0)
+		{
+		//
+		// waiting for a door to open
+		//
+			OpenDoor (-ob->distance-1);
+			if (doorobjlist[-ob->distance-1].action != dr_open)
+				return;
+			ob->distance = TILEGLOBAL;	// go ahead, the door is now opoen
+		}
+
+		if (move < ob->distance)
+		{
+			MoveObj (ob,move);
+			break;
+		}
+
+		//
+		// reached goal tile, so select another one
+		//
+
+		//
+		// fix position to account for round off during moving
+		//
+		ob->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;
+		ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;
+
+		move -= ob->distance;
+
+		if (dist <4)
+			SelectRunDir (ob);
+		else if (dodge)
+			SelectDodgeDir (ob);
+		else
+			SelectChaseDir (ob);
+
+		if (ob->dir == nodir)
+			return;							// object is blocked in
+	}
+
+}
+
+
+//
+// death
+//
+extern	statetype s_deathstand;
+
+extern	statetype s_deathchase1;
+extern	statetype s_deathchase1s;
+extern	statetype s_deathchase2;
+extern	statetype s_deathchase3;
+extern	statetype s_deathchase3s;
+extern	statetype s_deathchase4;
+
+extern	statetype s_deathdie1;
+extern	statetype s_deathdie2;
+extern	statetype s_deathdie3;
+extern	statetype s_deathdie4;
+extern	statetype s_deathdie5;
+extern	statetype s_deathdie6;
+extern	statetype s_deathdie7;
+extern	statetype s_deathdie8;
+extern	statetype s_deathdie9;
+
+extern	statetype s_deathshoot1;
+extern	statetype s_deathshoot2;
+extern	statetype s_deathshoot3;
+extern	statetype s_deathshoot4;
+extern	statetype s_deathshoot5;
+
+
+statetype s_deathstand	= {false,SPR_DEATH_W1,0,T_Stand,NULL,&s_deathstand};
+
+statetype s_deathchase1 	= {false,SPR_DEATH_W1,10,T_Will,NULL,&s_deathchase1s};
+statetype s_deathchase1s	= {false,SPR_DEATH_W1,3,NULL,NULL,&s_deathchase2};
+statetype s_deathchase2 	= {false,SPR_DEATH_W2,8,T_Will,NULL,&s_deathchase3};
+statetype s_deathchase3 	= {false,SPR_DEATH_W3,10,T_Will,NULL,&s_deathchase3s};
+statetype s_deathchase3s	= {false,SPR_DEATH_W3,3,NULL,NULL,&s_deathchase4};
+statetype s_deathchase4 	= {false,SPR_DEATH_W4,8,T_Will,NULL,&s_deathchase1};
+
+statetype s_deathdeathcam	= {false,SPR_DEATH_W1,1,NULL,NULL,&s_deathdie1};
+
+statetype s_deathdie1	= {false,SPR_DEATH_W1,1,NULL,A_DeathScream,&s_deathdie2};
+statetype s_deathdie2	= {false,SPR_DEATH_W1,10,NULL,NULL,&s_deathdie3};
+statetype s_deathdie3	= {false,SPR_DEATH_DIE1,10,NULL,NULL,&s_deathdie4};
+statetype s_deathdie4	= {false,SPR_DEATH_DIE2,10,NULL,NULL,&s_deathdie5};
+statetype s_deathdie5	= {false,SPR_DEATH_DIE3,10,NULL,NULL,&s_deathdie6};
+statetype s_deathdie6	= {false,SPR_DEATH_DIE4,10,NULL,NULL,&s_deathdie7};
+statetype s_deathdie7	= {false,SPR_DEATH_DIE5,10,NULL,NULL,&s_deathdie8};
+statetype s_deathdie8	= {false,SPR_DEATH_DIE6,10,NULL,NULL,&s_deathdie9};
+statetype s_deathdie9	= {false,SPR_DEATH_DEAD,0,NULL,NULL,&s_deathdie9};
+
+statetype s_deathshoot1 	= {false,SPR_DEATH_SHOOT1,30,NULL,NULL,&s_deathshoot2};
+statetype s_deathshoot2 	= {false,SPR_DEATH_SHOOT2,10,NULL,T_Launch,&s_deathshoot3};
+statetype s_deathshoot3 	= {false,SPR_DEATH_SHOOT4,10,NULL,T_Shoot,&s_deathshoot4};
+statetype s_deathshoot4 	= {false,SPR_DEATH_SHOOT3,10,NULL,T_Launch,&s_deathshoot5};
+statetype s_deathshoot5 	= {false,SPR_DEATH_SHOOT4,10,NULL,T_Shoot,&s_deathchase1};
+
+
+/*
+===============
+=
+= SpawnDeath
+=
+===============
+*/
+
+void SpawnDeath (int tilex, int tiley)
+{
+	unsigned	far *map,tile;
+
+	if (SoundBlasterPresent && DigiMode != sds_Off)
+		s_deathdie2.tictime = 105;
+
+	SpawnNewObj (tilex,tiley,&s_deathstand);
+	new->obclass = deathobj;
+	new->hitpoints = starthitpoints[gamestate.difficulty][en_death];
+	new->flags |= FL_SHOOTABLE|FL_AMBUSH;
+	if (!loadedgame)
+	  gamestate.killtotal++;
+}
+
+/*
+===============
+=
+= T_Launch
+=
+===============
+*/
+
+void T_Launch (objtype *ob)
+{
+	long	deltax,deltay;
+	float	angle;
+	int		iangle;
+
+	deltax = player->x - ob->x;
+	deltay = ob->y - player->y;
+	angle = atan2 (deltay,deltax);
+	if (angle<0)
+		angle = M_PI*2+angle;
+	iangle = angle/(M_PI*2)*ANGLES;
+	if (ob->obclass == deathobj)
+	{
+		T_Shoot (ob);
+		if (ob->state == &s_deathshoot2)
+		{
+			iangle-=4;
+			if (iangle<0)
+				iangle+=ANGLES;
+		}
+		else
+		{
+			iangle+=4;
+			if (iangle>=ANGLES)
+				iangle-=ANGLES;
+		}
+	}
+
+	GetNewActor ();
+	new->state = &s_rocket;
+	new->ticcount = 1;
+
+	new->tilex = ob->tilex;
+	new->tiley = ob->tiley;
+	new->x = ob->x;
+	new->y = ob->y;
+	new->obclass = rocketobj;
+	switch(ob->obclass)
+	{
+	case deathobj:
+		new->state = &s_hrocket;
+		new->obclass = hrocketobj;
+		PlaySoundLocActor (KNIGHTMISSILESND,new);
+		break;
+	case angelobj:
+		new->state = &s_spark1;
+		new->obclass = sparkobj;
+		PlaySoundLocActor (ANGELFIRESND,new);
+		break;
+	default:
+		PlaySoundLocActor (MISSILEFIRESND,new);
+	}
+
+	new->dir = nodir;
+	new->angle = iangle;
+	new->speed = 0x2000l;
+	new->flags = FL_NONMARK;
+	new->active = true;
+}
+
+
+
+//
+// angel
+//
+void A_Relaunch (objtype *ob);
+void A_Victory (objtype *ob);
+void A_StartAttack (objtype *ob);
+void A_Breathing (objtype *ob);
+
+extern	statetype s_angelstand;
+
+extern	statetype s_angelchase1;
+extern	statetype s_angelchase1s;
+extern	statetype s_angelchase2;
+extern	statetype s_angelchase3;
+extern	statetype s_angelchase3s;
+extern	statetype s_angelchase4;
+
+extern	statetype s_angeldie1;
+extern	statetype s_angeldie11;
+extern	statetype s_angeldie2;
+extern	statetype s_angeldie3;
+extern	statetype s_angeldie4;
+extern	statetype s_angeldie5;
+extern	statetype s_angeldie6;
+extern	statetype s_angeldie7;
+extern	statetype s_angeldie8;
+extern	statetype s_angeldie9;
+
+extern	statetype s_angelshoot1;
+extern	statetype s_angelshoot2;
+extern	statetype s_angelshoot3;
+extern	statetype s_angelshoot4;
+extern	statetype s_angelshoot5;
+extern	statetype s_angelshoot6;
+
+extern	statetype s_angeltired;
+extern	statetype s_angeltired2;
+extern	statetype s_angeltired3;
+extern	statetype s_angeltired4;
+extern	statetype s_angeltired5;
+extern	statetype s_angeltired6;
+extern	statetype s_angeltired7;
+
+extern	statetype s_spark1;
+extern	statetype s_spark2;
+extern	statetype s_spark3;
+extern	statetype s_spark4;
+
+
+statetype s_angelstand	= {false,SPR_ANGEL_W1,0,T_Stand,NULL,&s_angelstand};
+
+statetype s_angelchase1 	= {false,SPR_ANGEL_W1,10,T_Will,NULL,&s_angelchase1s};
+statetype s_angelchase1s	= {false,SPR_ANGEL_W1,3,NULL,NULL,&s_angelchase2};
+statetype s_angelchase2 	= {false,SPR_ANGEL_W2,8,T_Will,NULL,&s_angelchase3};
+statetype s_angelchase3 	= {false,SPR_ANGEL_W3,10,T_Will,NULL,&s_angelchase3s};
+statetype s_angelchase3s	= {false,SPR_ANGEL_W3,3,NULL,NULL,&s_angelchase4};
+statetype s_angelchase4 	= {false,SPR_ANGEL_W4,8,T_Will,NULL,&s_angelchase1};
+
+statetype s_angeldie1	= {false,SPR_ANGEL_W1,1,NULL,A_DeathScream,&s_angeldie11};
+statetype s_angeldie11	= {false,SPR_ANGEL_W1,1,NULL,NULL,&s_angeldie2};
+statetype s_angeldie2	= {false,SPR_ANGEL_DIE1,10,NULL,A_Slurpie,&s_angeldie3};
+statetype s_angeldie3	= {false,SPR_ANGEL_DIE2,10,NULL,NULL,&s_angeldie4};
+statetype s_angeldie4	= {false,SPR_ANGEL_DIE3,10,NULL,NULL,&s_angeldie5};
+statetype s_angeldie5	= {false,SPR_ANGEL_DIE4,10,NULL,NULL,&s_angeldie6};
+statetype s_angeldie6	= {false,SPR_ANGEL_DIE5,10,NULL,NULL,&s_angeldie7};
+statetype s_angeldie7	= {false,SPR_ANGEL_DIE6,10,NULL,NULL,&s_angeldie8};
+statetype s_angeldie8	= {false,SPR_ANGEL_DIE7,10,NULL,NULL,&s_angeldie9};
+statetype s_angeldie9	= {false,SPR_ANGEL_DEAD,130,NULL,A_Victory,&s_angeldie9};
+
+statetype s_angelshoot1 	= {false,SPR_ANGEL_SHOOT1,10,NULL,A_StartAttack,&s_angelshoot2};
+statetype s_angelshoot2 	= {false,SPR_ANGEL_SHOOT2,20,NULL,T_Launch,&s_angelshoot3};
+statetype s_angelshoot3 	= {false,SPR_ANGEL_SHOOT1,10,NULL,A_Relaunch,&s_angelshoot2};
+
+statetype s_angeltired 	= {false,SPR_ANGEL_TIRED1,40,NULL,A_Breathing,&s_angeltired2};
+statetype s_angeltired2	= {false,SPR_ANGEL_TIRED2,40,NULL,NULL,&s_angeltired3};
+statetype s_angeltired3	= {false,SPR_ANGEL_TIRED1,40,NULL,A_Breathing,&s_angeltired4};
+statetype s_angeltired4	= {false,SPR_ANGEL_TIRED2,40,NULL,NULL,&s_angeltired5};
+statetype s_angeltired5	= {false,SPR_ANGEL_TIRED1,40,NULL,A_Breathing,&s_angeltired6};
+statetype s_angeltired6	= {false,SPR_ANGEL_TIRED2,40,NULL,NULL,&s_angeltired7};
+statetype s_angeltired7	= {false,SPR_ANGEL_TIRED1,40,NULL,A_Breathing,&s_angelchase1};
+
+statetype s_spark1 	= {false,SPR_SPARK1,6,T_Projectile,NULL,&s_spark2};
+statetype s_spark2 	= {false,SPR_SPARK2,6,T_Projectile,NULL,&s_spark3};
+statetype s_spark3 	= {false,SPR_SPARK3,6,T_Projectile,NULL,&s_spark4};
+statetype s_spark4 	= {false,SPR_SPARK4,6,T_Projectile,NULL,&s_spark1};
+
+
+#pragma argsused
+void A_Slurpie (objtype *ob)
+{
+ SD_PlaySound(SLURPIESND);
+}
+
+#pragma argsused
+void A_Breathing (objtype *ob)
+{
+ SD_PlaySound(ANGELTIREDSND);
+}
+
+/*
+===============
+=
+= SpawnAngel
+=
+===============
+*/
+
+void SpawnAngel (int tilex, int tiley)
+{
+	unsigned	far *map,tile;
+
+
+	if (SoundBlasterPresent && DigiMode != sds_Off)
+		s_angeldie11.tictime = 105;
+
+	SpawnNewObj (tilex,tiley,&s_angelstand);
+	new->obclass = angelobj;
+	new->hitpoints = starthitpoints[gamestate.difficulty][en_angel];
+	new->flags |= FL_SHOOTABLE|FL_AMBUSH;
+	if (!loadedgame)
+	  gamestate.killtotal++;
+}
+
+
+/*
+=================
+=
+= A_Victory
+=
+=================
+*/
+
+#pragma argsused
+void A_Victory (objtype *ob)
+{
+	playstate = ex_victorious;
+}
+
+
+/*
+=================
+=
+= A_StartAttack
+=
+=================
+*/
+
+void A_StartAttack (objtype *ob)
+{
+	ob->temp1 = 0;
+}
+
+
+/*
+=================
+=
+= A_Relaunch
+=
+=================
+*/
+
+void A_Relaunch (objtype *ob)
+{
+	if (++ob->temp1 == 3)
+	{
+		NewState (ob,&s_angeltired);
+		return;
+	}
+
+	if (US_RndT()&1)
+	{
+		NewState (ob,&s_angelchase1);
+		return;
+	}
+}
+
+
+
+
+//
+// spectre
+//
+void T_SpectreWait (objtype *ob);
+void A_Dormant (objtype *ob);
+
+extern	statetype s_spectrewait1;
+extern	statetype s_spectrewait2;
+extern	statetype s_spectrewait3;
+extern	statetype s_spectrewait4;
+
+extern	statetype s_spectrechase1;
+extern	statetype s_spectrechase2;
+extern	statetype s_spectrechase3;
+extern	statetype s_spectrechase4;
+
+extern	statetype s_spectredie1;
+extern	statetype s_spectredie2;
+extern	statetype s_spectredie3;
+extern	statetype s_spectredie4;
+
+extern	statetype s_spectrewake;
+
+statetype s_spectrewait1	= {false,SPR_SPECTRE_W1,10,T_Stand,NULL,&s_spectrewait2};
+statetype s_spectrewait2	= {false,SPR_SPECTRE_W2,10,T_Stand,NULL,&s_spectrewait3};
+statetype s_spectrewait3	= {false,SPR_SPECTRE_W3,10,T_Stand,NULL,&s_spectrewait4};
+statetype s_spectrewait4	= {false,SPR_SPECTRE_W4,10,T_Stand,NULL,&s_spectrewait1};
+
+statetype s_spectrechase1	= {false,SPR_SPECTRE_W1,10,T_Ghosts,NULL,&s_spectrechase2};
+statetype s_spectrechase2	= {false,SPR_SPECTRE_W2,10,T_Ghosts,NULL,&s_spectrechase3};
+statetype s_spectrechase3	= {false,SPR_SPECTRE_W3,10,T_Ghosts,NULL,&s_spectrechase4};
+statetype s_spectrechase4	= {false,SPR_SPECTRE_W4,10,T_Ghosts,NULL,&s_spectrechase1};
+
+statetype s_spectredie1	= {false,SPR_SPECTRE_F1,10,NULL,NULL,&s_spectredie2};
+statetype s_spectredie2	= {false,SPR_SPECTRE_F2,10,NULL,NULL,&s_spectredie3};
+statetype s_spectredie3	= {false,SPR_SPECTRE_F3,10,NULL,NULL,&s_spectredie4};
+statetype s_spectredie4	= {false,SPR_SPECTRE_F4,300,NULL,NULL,&s_spectrewake};
+statetype s_spectrewake	= {false,SPR_SPECTRE_F4,10,NULL,A_Dormant,&s_spectrewake};
+
+/*
+===============
+=
+= SpawnSpectre
+=
+===============
+*/
+
+void SpawnSpectre (int tilex, int tiley)
+{
+	unsigned	far *map,tile;
+
+	SpawnNewObj (tilex,tiley,&s_spectrewait1);
+	new->obclass = spectreobj;
+	new->hitpoints = starthitpoints[gamestate.difficulty][en_spectre];
+	new->flags |= FL_SHOOTABLE|FL_AMBUSH; // |FL_NEVERMARK|FL_NONMARK;
+	if (!loadedgame)
+	  gamestate.killtotal++;
+}
+
+
+/*
+===============
+=
+= A_Dormant
+=
+===============
+*/
+
+void A_Dormant (objtype *ob)
+{
+	long	deltax,deltay;
+	int	xl,xh,yl,yh;
+	int	x,y;
+	unsigned	tile;
+
+	deltax = ob->x - player->x;
+	if (deltax < -MINACTORDIST || deltax > MINACTORDIST)
+		goto moveok;
+	deltay = ob->y - player->y;
+	if (deltay < -MINACTORDIST || deltay > MINACTORDIST)
+		goto moveok;
+
+	return;
+moveok:
+
+	xl = (ob->x-MINDIST) >> TILESHIFT;
+	xh = (ob->x+MINDIST) >> TILESHIFT;
+	yl = (ob->y-MINDIST) >> TILESHIFT;
+	yh = (ob->y+MINDIST) >> TILESHIFT;
+
+	for (y=yl ; y<=yh ; y++)
+		for (x=xl ; x<=xh ; x++)
+		{
+			tile = actorat[x][y];
+			if (!tile)
+				continue;
+			if (tile<256)
+				return;
+			if (((objtype *)tile)->flags&FL_SHOOTABLE)
+				return;
+		}
+
+	ob->flags |= FL_AMBUSH | FL_SHOOTABLE;
+	ob->flags &= ~FL_ATTACKMODE;
+	ob->dir = nodir;
+	NewState (ob,&s_spectrewait1);
+}
+
+
+#endif
+
+/*
+=============================================================================
+
+						 SCHABBS / GIFT / FAT
+
+=============================================================================
+*/
+
+#ifndef SPEAR
+/*
+===============
+=
+= SpawnGhosts
+=
+===============
+*/
+
+void SpawnGhosts (int which, int tilex, int tiley)
+{
+	unsigned	far *map,tile;
+
+	switch(which)
+	{
+	 case en_blinky:
+	   SpawnNewObj (tilex,tiley,&s_blinkychase1);
+	   break;
+	 case en_clyde:
+	   SpawnNewObj (tilex,tiley,&s_clydechase1);
+	   break;
+	 case en_pinky:
+	   SpawnNewObj (tilex,tiley,&s_pinkychase1);
+	   break;
+	 case en_inky:
+	   SpawnNewObj (tilex,tiley,&s_inkychase1);
+	   break;
+	}
+
+	new->obclass = ghostobj;
+	new->speed = SPDDOG;
+
+	new->dir = east;
+	new->flags |= FL_AMBUSH;
+	if (!loadedgame)
+	  gamestate.killtotal++;
+}
+
+
+
+void	T_Gift (objtype *ob);
+void	T_GiftThrow (objtype *ob);
+
+void	T_Fat (objtype *ob);
+void	T_FatThrow (objtype *ob);
+
+//
+// schabb
+//
+extern	statetype s_schabbstand;
+
+extern	statetype s_schabbchase1;
+extern	statetype s_schabbchase1s;
+extern	statetype s_schabbchase2;
+extern	statetype s_schabbchase3;
+extern	statetype s_schabbchase3s;
+extern	statetype s_schabbchase4;
+
+extern	statetype s_schabbdie1;
+extern	statetype s_schabbdie2;
+extern	statetype s_schabbdie3;
+extern	statetype s_schabbdie4;
+extern	statetype s_schabbdie5;
+extern	statetype s_schabbdie6;
+
+extern	statetype s_schabbshoot1;
+extern	statetype s_schabbshoot2;
+
+extern	statetype s_needle1;
+extern	statetype s_needle2;
+extern	statetype s_needle3;
+extern	statetype s_needle4;
+
+extern	statetype s_schabbdeathcam;
+
+
+statetype s_schabbstand	= {false,SPR_SCHABB_W1,0,T_Stand,NULL,&s_schabbstand};
+
+statetype s_schabbchase1 	= {false,SPR_SCHABB_W1,10,T_Schabb,NULL,&s_schabbchase1s};
+statetype s_schabbchase1s	= {false,SPR_SCHABB_W1,3,NULL,NULL,&s_schabbchase2};
+statetype s_schabbchase2 	= {false,SPR_SCHABB_W2,8,T_Schabb,NULL,&s_schabbchase3};
+statetype s_schabbchase3 	= {false,SPR_SCHABB_W3,10,T_Schabb,NULL,&s_schabbchase3s};
+statetype s_schabbchase3s	= {false,SPR_SCHABB_W3,3,NULL,NULL,&s_schabbchase4};
+statetype s_schabbchase4 	= {false,SPR_SCHABB_W4,8,T_Schabb,NULL,&s_schabbchase1};
+
+statetype s_schabbdeathcam	= {false,SPR_SCHABB_W1,1,NULL,NULL,&s_schabbdie1};
+
+statetype s_schabbdie1	= {false,SPR_SCHABB_W1,10,NULL,A_DeathScream,&s_schabbdie2};
+statetype s_schabbdie2	= {false,SPR_SCHABB_W1,10,NULL,NULL,&s_schabbdie3};
+statetype s_schabbdie3	= {false,SPR_SCHABB_DIE1,10,NULL,NULL,&s_schabbdie4};
+statetype s_schabbdie4	= {false,SPR_SCHABB_DIE2,10,NULL,NULL,&s_schabbdie5};
+statetype s_schabbdie5	= {false,SPR_SCHABB_DIE3,10,NULL,NULL,&s_schabbdie6};
+statetype s_schabbdie6	= {false,SPR_SCHABB_DEAD,20,NULL,A_StartDeathCam,&s_schabbdie6};
+
+statetype s_schabbshoot1 	= {false,SPR_SCHABB_SHOOT1,30,NULL,NULL,&s_schabbshoot2};
+statetype s_schabbshoot2 	= {false,SPR_SCHABB_SHOOT2,10,NULL,T_SchabbThrow,&s_schabbchase1};
+
+statetype s_needle1 	= {false,SPR_HYPO1,6,T_Projectile,NULL,&s_needle2};
+statetype s_needle2 	= {false,SPR_HYPO2,6,T_Projectile,NULL,&s_needle3};
+statetype s_needle3 	= {false,SPR_HYPO3,6,T_Projectile,NULL,&s_needle4};
+statetype s_needle4 	= {false,SPR_HYPO4,6,T_Projectile,NULL,&s_needle1};
+
+
+//
+// gift
+//
+extern	statetype s_giftstand;
+
+extern	statetype s_giftchase1;
+extern	statetype s_giftchase1s;
+extern	statetype s_giftchase2;
+extern	statetype s_giftchase3;
+extern	statetype s_giftchase3s;
+extern	statetype s_giftchase4;
+
+extern	statetype s_giftdie1;
+extern	statetype s_giftdie2;
+extern	statetype s_giftdie3;
+extern	statetype s_giftdie4;
+extern	statetype s_giftdie5;
+extern	statetype s_giftdie6;
+
+extern	statetype s_giftshoot1;
+extern	statetype s_giftshoot2;
+
+extern	statetype s_needle1;
+extern	statetype s_needle2;
+extern	statetype s_needle3;
+extern	statetype s_needle4;
+
+extern	statetype s_giftdeathcam;
+
+extern	statetype s_boom1;
+extern	statetype s_boom2;
+extern	statetype s_boom3;
+
+
+statetype s_giftstand	= {false,SPR_GIFT_W1,0,T_Stand,NULL,&s_giftstand};
+
+statetype s_giftchase1 	= {false,SPR_GIFT_W1,10,T_Gift,NULL,&s_giftchase1s};
+statetype s_giftchase1s	= {false,SPR_GIFT_W1,3,NULL,NULL,&s_giftchase2};
+statetype s_giftchase2 	= {false,SPR_GIFT_W2,8,T_Gift,NULL,&s_giftchase3};
+statetype s_giftchase3 	= {false,SPR_GIFT_W3,10,T_Gift,NULL,&s_giftchase3s};
+statetype s_giftchase3s	= {false,SPR_GIFT_W3,3,NULL,NULL,&s_giftchase4};
+statetype s_giftchase4 	= {false,SPR_GIFT_W4,8,T_Gift,NULL,&s_giftchase1};
+
+statetype s_giftdeathcam	= {false,SPR_GIFT_W1,1,NULL,NULL,&s_giftdie1};
+
+statetype s_giftdie1	= {false,SPR_GIFT_W1,1,NULL,A_DeathScream,&s_giftdie2};
+statetype s_giftdie2	= {false,SPR_GIFT_W1,10,NULL,NULL,&s_giftdie3};
+statetype s_giftdie3	= {false,SPR_GIFT_DIE1,10,NULL,NULL,&s_giftdie4};
+statetype s_giftdie4	= {false,SPR_GIFT_DIE2,10,NULL,NULL,&s_giftdie5};
+statetype s_giftdie5	= {false,SPR_GIFT_DIE3,10,NULL,NULL,&s_giftdie6};
+statetype s_giftdie6	= {false,SPR_GIFT_DEAD,20,NULL,A_StartDeathCam,&s_giftdie6};
+
+statetype s_giftshoot1 	= {false,SPR_GIFT_SHOOT1,30,NULL,NULL,&s_giftshoot2};
+statetype s_giftshoot2 	= {false,SPR_GIFT_SHOOT2,10,NULL,T_GiftThrow,&s_giftchase1};
+
+
+//
+// fat
+//
+extern	statetype s_fatstand;
+
+extern	statetype s_fatchase1;
+extern	statetype s_fatchase1s;
+extern	statetype s_fatchase2;
+extern	statetype s_fatchase3;
+extern	statetype s_fatchase3s;
+extern	statetype s_fatchase4;
+
+extern	statetype s_fatdie1;
+extern	statetype s_fatdie2;
+extern	statetype s_fatdie3;
+extern	statetype s_fatdie4;
+extern	statetype s_fatdie5;
+extern	statetype s_fatdie6;
+
+extern	statetype s_fatshoot1;
+extern	statetype s_fatshoot2;
+extern	statetype s_fatshoot3;
+extern	statetype s_fatshoot4;
+extern	statetype s_fatshoot5;
+extern	statetype s_fatshoot6;
+
+extern	statetype s_needle1;
+extern	statetype s_needle2;
+extern	statetype s_needle3;
+extern	statetype s_needle4;
+
+extern	statetype s_fatdeathcam;
+
+
+statetype s_fatstand	= {false,SPR_FAT_W1,0,T_Stand,NULL,&s_fatstand};
+
+statetype s_fatchase1 	= {false,SPR_FAT_W1,10,T_Fat,NULL,&s_fatchase1s};
+statetype s_fatchase1s	= {false,SPR_FAT_W1,3,NULL,NULL,&s_fatchase2};
+statetype s_fatchase2 	= {false,SPR_FAT_W2,8,T_Fat,NULL,&s_fatchase3};
+statetype s_fatchase3 	= {false,SPR_FAT_W3,10,T_Fat,NULL,&s_fatchase3s};
+statetype s_fatchase3s	= {false,SPR_FAT_W3,3,NULL,NULL,&s_fatchase4};
+statetype s_fatchase4 	= {false,SPR_FAT_W4,8,T_Fat,NULL,&s_fatchase1};
+
+statetype s_fatdeathcam	= {false,SPR_FAT_W1,1,NULL,NULL,&s_fatdie1};
+
+statetype s_fatdie1	= {false,SPR_FAT_W1,1,NULL,A_DeathScream,&s_fatdie2};
+statetype s_fatdie2	= {false,SPR_FAT_W1,10,NULL,NULL,&s_fatdie3};
+statetype s_fatdie3	= {false,SPR_FAT_DIE1,10,NULL,NULL,&s_fatdie4};
+statetype s_fatdie4	= {false,SPR_FAT_DIE2,10,NULL,NULL,&s_fatdie5};
+statetype s_fatdie5	= {false,SPR_FAT_DIE3,10,NULL,NULL,&s_fatdie6};
+statetype s_fatdie6	= {false,SPR_FAT_DEAD,20,NULL,A_StartDeathCam,&s_fatdie6};
+
+statetype s_fatshoot1 	= {false,SPR_FAT_SHOOT1,30,NULL,NULL,&s_fatshoot2};
+statetype s_fatshoot2 	= {false,SPR_FAT_SHOOT2,10,NULL,T_GiftThrow,&s_fatshoot3};
+statetype s_fatshoot3 	= {false,SPR_FAT_SHOOT3,10,NULL,T_Shoot,&s_fatshoot4};
+statetype s_fatshoot4 	= {false,SPR_FAT_SHOOT4,10,NULL,T_Shoot,&s_fatshoot5};
+statetype s_fatshoot5 	= {false,SPR_FAT_SHOOT3,10,NULL,T_Shoot,&s_fatshoot6};
+statetype s_fatshoot6 	= {false,SPR_FAT_SHOOT4,10,NULL,T_Shoot,&s_fatchase1};
+
+
+/*
+===============
+=
+= SpawnSchabbs
+=
+===============
+*/
+
+void SpawnSchabbs (int tilex, int tiley)
+{
+	unsigned	far *map,tile;
+
+	if (DigiMode != sds_Off)
+		s_schabbdie2.tictime = 140;
+	else
+		s_schabbdie2.tictime = 5;
+
+	SpawnNewObj (tilex,tiley,&s_schabbstand);
+	new->speed = SPDPATROL;
+
+	new->obclass = schabbobj;
+	new->hitpoints = starthitpoints[gamestate.difficulty][en_schabbs];
+	new->dir = south;
+	new->flags |= FL_SHOOTABLE|FL_AMBUSH;
+	if (!loadedgame)
+	  gamestate.killtotal++;
+}
+
+
+/*
+===============
+=
+= SpawnGift
+=
+===============
+*/
+
+void SpawnGift (int tilex, int tiley)
+{
+	unsigned	far *map,tile;
+
+	if (DigiMode != sds_Off)
+	  s_giftdie2.tictime = 140;
+	else
+	  s_giftdie2.tictime = 5;
+
+	SpawnNewObj (tilex,tiley,&s_giftstand);
+	new->speed = SPDPATROL;
+
+	new->obclass = giftobj;
+	new->hitpoints = starthitpoints[gamestate.difficulty][en_gift];
+	new->dir = north;
+	new->flags |= FL_SHOOTABLE|FL_AMBUSH;
+	if (!loadedgame)
+	  gamestate.killtotal++;
+}
+
+
+/*
+===============
+=
+= SpawnFat
+=
+===============
+*/
+
+void SpawnFat (int tilex, int tiley)
+{
+	unsigned	far *map,tile;
+
+	if (DigiMode != sds_Off)
+	  s_fatdie2.tictime = 140;
+	else
+	  s_fatdie2.tictime = 5;
+
+	SpawnNewObj (tilex,tiley,&s_fatstand);
+	new->speed = SPDPATROL;
+
+	new->obclass = fatobj;
+	new->hitpoints = starthitpoints[gamestate.difficulty][en_fat];
+	new->dir = south;
+	new->flags |= FL_SHOOTABLE|FL_AMBUSH;
+	if (!loadedgame)
+	  gamestate.killtotal++;
+}
+
+
+/*
+=================
+=
+= T_SchabbThrow
+=
+=================
+*/
+
+void T_SchabbThrow (objtype *ob)
+{
+	long	deltax,deltay;
+	float	angle;
+	int		iangle;
+
+	deltax = player->x - ob->x;
+	deltay = ob->y - player->y;
+	angle = atan2 (deltay,deltax);
+	if (angle<0)
+		angle = M_PI*2+angle;
+	iangle = angle/(M_PI*2)*ANGLES;
+
+	GetNewActor ();
+	new->state = &s_needle1;
+	new->ticcount = 1;
+
+	new->tilex = ob->tilex;
+	new->tiley = ob->tiley;
+	new->x = ob->x;
+	new->y = ob->y;
+	new->obclass = needleobj;
+	new->dir = nodir;
+	new->angle = iangle;
+	new->speed = 0x2000l;
+
+	new->flags = FL_NONMARK;
+	new->active = true;
+
+	PlaySoundLocActor (SCHABBSTHROWSND,new);
+}
+
+/*
+=================
+=
+= T_GiftThrow
+=
+=================
+*/
+
+void T_GiftThrow (objtype *ob)
+{
+	long	deltax,deltay;
+	float	angle;
+	int		iangle;
+
+	deltax = player->x - ob->x;
+	deltay = ob->y - player->y;
+	angle = atan2 (deltay,deltax);
+	if (angle<0)
+		angle = M_PI*2+angle;
+	iangle = angle/(M_PI*2)*ANGLES;
+
+	GetNewActor ();
+	new->state = &s_rocket;
+	new->ticcount = 1;
+
+	new->tilex = ob->tilex;
+	new->tiley = ob->tiley;
+	new->x = ob->x;
+	new->y = ob->y;
+	new->obclass = rocketobj;
+	new->dir = nodir;
+	new->angle = iangle;
+	new->speed = 0x2000l;
+	new->flags = FL_NONMARK;
+	new->active = true;
+
+	PlaySoundLocActor (MISSILEFIRESND,new);
+}
+
+
+
+/*
+=================
+=
+= T_Schabb
+=
+=================
+*/
+
+void T_Schabb (objtype *ob)
+{
+	long move;
+	int	dx,dy,dist;
+	boolean	dodge;
+
+	dodge = false;
+	dx = abs(ob->tilex - player->tilex);
+	dy = abs(ob->tiley - player->tiley);
+	dist = dx>dy ? dx : dy;
+
+	if (CheckLine(ob))						// got a shot at player?
+	{
+
+		if ( US_RndT() < (tics<<3) )
+		{
+		//
+		// go into attack frame
+		//
+			NewState (ob,&s_schabbshoot1);
+			return;
+		}
+		dodge = true;
+	}
+
+	if (ob->dir == nodir)
+	{
+		if (dodge)
+			SelectDodgeDir (ob);
+		else
+			SelectChaseDir (ob);
+		if (ob->dir == nodir)
+			return;							// object is blocked in
+	}
+
+	move = ob->speed*tics;
+
+	while (move)
+	{
+		if (ob->distance < 0)
+		{
+		//
+		// waiting for a door to open
+		//
+			OpenDoor (-ob->distance-1);
+			if (doorobjlist[-ob->distance-1].action != dr_open)
+				return;
+			ob->distance = TILEGLOBAL;	// go ahead, the door is now opoen
+		}
+
+		if (move < ob->distance)
+		{
+			MoveObj (ob,move);
+			break;
+		}
+
+		//
+		// reached goal tile, so select another one
+		//
+
+		//
+		// fix position to account for round off during moving
+		//
+		ob->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;
+		ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;
+
+		move -= ob->distance;
+
+		if (dist <4)
+			SelectRunDir (ob);
+		else if (dodge)
+			SelectDodgeDir (ob);
+		else
+			SelectChaseDir (ob);
+
+		if (ob->dir == nodir)
+			return;							// object is blocked in
+	}
+
+}
+
+
+
+
+/*
+=================
+=
+= T_Gift
+=
+=================
+*/
+
+void T_Gift (objtype *ob)
+{
+	long move;
+	int	dx,dy,dist;
+	boolean	dodge;
+
+	dodge = false;
+	dx = abs(ob->tilex - player->tilex);
+	dy = abs(ob->tiley - player->tiley);
+	dist = dx>dy ? dx : dy;
+
+	if (CheckLine(ob))						// got a shot at player?
+	{
+
+		if ( US_RndT() < (tics<<3) )
+		{
+		//
+		// go into attack frame
+		//
+			NewState (ob,&s_giftshoot1);
+			return;
+		}
+		dodge = true;
+	}
+
+	if (ob->dir == nodir)
+	{
+		if (dodge)
+			SelectDodgeDir (ob);
+		else
+			SelectChaseDir (ob);
+		if (ob->dir == nodir)
+			return;							// object is blocked in
+	}
+
+	move = ob->speed*tics;
+
+	while (move)
+	{
+		if (ob->distance < 0)
+		{
+		//
+		// waiting for a door to open
+		//
+			OpenDoor (-ob->distance-1);
+			if (doorobjlist[-ob->distance-1].action != dr_open)
+				return;
+			ob->distance = TILEGLOBAL;	// go ahead, the door is now opoen
+		}
+
+		if (move < ob->distance)
+		{
+			MoveObj (ob,move);
+			break;
+		}
+
+		//
+		// reached goal tile, so select another one
+		//
+
+		//
+		// fix position to account for round off during moving
+		//
+		ob->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;
+		ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;
+
+		move -= ob->distance;
+
+		if (dist <4)
+			SelectRunDir (ob);
+		else if (dodge)
+			SelectDodgeDir (ob);
+		else
+			SelectChaseDir (ob);
+
+		if (ob->dir == nodir)
+			return;							// object is blocked in
+	}
+
+}
+
+
+
+
+/*
+=================
+=
+= T_Fat
+=
+=================
+*/
+
+void T_Fat (objtype *ob)
+{
+	long move;
+	int	dx,dy,dist;
+	boolean	dodge;
+
+	dodge = false;
+	dx = abs(ob->tilex - player->tilex);
+	dy = abs(ob->tiley - player->tiley);
+	dist = dx>dy ? dx : dy;
+
+	if (CheckLine(ob))						// got a shot at player?
+	{
+
+		if ( US_RndT() < (tics<<3) )
+		{
+		//
+		// go into attack frame
+		//
+			NewState (ob,&s_fatshoot1);
+			return;
+		}
+		dodge = true;
+	}
+
+	if (ob->dir == nodir)
+	{
+		if (dodge)
+			SelectDodgeDir (ob);
+		else
+			SelectChaseDir (ob);
+		if (ob->dir == nodir)
+			return;							// object is blocked in
+	}
+
+	move = ob->speed*tics;
+
+	while (move)
+	{
+		if (ob->distance < 0)
+		{
+		//
+		// waiting for a door to open
+		//
+			OpenDoor (-ob->distance-1);
+			if (doorobjlist[-ob->distance-1].action != dr_open)
+				return;
+			ob->distance = TILEGLOBAL;	// go ahead, the door is now opoen
+		}
+
+		if (move < ob->distance)
+		{
+			MoveObj (ob,move);
+			break;
+		}
+
+		//
+		// reached goal tile, so select another one
+		//
+
+		//
+		// fix position to account for round off during moving
+		//
+		ob->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;
+		ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;
+
+		move -= ob->distance;
+
+		if (dist <4)
+			SelectRunDir (ob);
+		else if (dodge)
+			SelectDodgeDir (ob);
+		else
+			SelectChaseDir (ob);
+
+		if (ob->dir == nodir)
+			return;							// object is blocked in
+	}
+
+}
+
+
+
+/*
+=============================================================================
+
+							HITLERS
+
+=============================================================================
+*/
+
+
+//
+// fake
+//
+extern	statetype s_fakestand;
+
+extern	statetype s_fakechase1;
+extern	statetype s_fakechase1s;
+extern	statetype s_fakechase2;
+extern	statetype s_fakechase3;
+extern	statetype s_fakechase3s;
+extern	statetype s_fakechase4;
+
+extern	statetype s_fakedie1;
+extern	statetype s_fakedie2;
+extern	statetype s_fakedie3;
+extern	statetype s_fakedie4;
+extern	statetype s_fakedie5;
+extern	statetype s_fakedie6;
+
+extern	statetype s_fakeshoot1;
+extern	statetype s_fakeshoot2;
+extern	statetype s_fakeshoot3;
+extern	statetype s_fakeshoot4;
+extern	statetype s_fakeshoot5;
+extern	statetype s_fakeshoot6;
+extern	statetype s_fakeshoot7;
+extern	statetype s_fakeshoot8;
+extern	statetype s_fakeshoot9;
+
+extern	statetype s_fire1;
+extern	statetype s_fire2;
+
+statetype s_fakestand	= {false,SPR_FAKE_W1,0,T_Stand,NULL,&s_fakestand};
+
+statetype s_fakechase1 	= {false,SPR_FAKE_W1,10,T_Fake,NULL,&s_fakechase1s};
+statetype s_fakechase1s	= {false,SPR_FAKE_W1,3,NULL,NULL,&s_fakechase2};
+statetype s_fakechase2 	= {false,SPR_FAKE_W2,8,T_Fake,NULL,&s_fakechase3};
+statetype s_fakechase3 	= {false,SPR_FAKE_W3,10,T_Fake,NULL,&s_fakechase3s};
+statetype s_fakechase3s	= {false,SPR_FAKE_W3,3,NULL,NULL,&s_fakechase4};
+statetype s_fakechase4 	= {false,SPR_FAKE_W4,8,T_Fake,NULL,&s_fakechase1};
+
+statetype s_fakedie1	= {false,SPR_FAKE_DIE1,10,NULL,A_DeathScream,&s_fakedie2};
+statetype s_fakedie2	= {false,SPR_FAKE_DIE2,10,NULL,NULL,&s_fakedie3};
+statetype s_fakedie3	= {false,SPR_FAKE_DIE3,10,NULL,NULL,&s_fakedie4};
+statetype s_fakedie4	= {false,SPR_FAKE_DIE4,10,NULL,NULL,&s_fakedie5};
+statetype s_fakedie5	= {false,SPR_FAKE_DIE5,10,NULL,NULL,&s_fakedie6};
+statetype s_fakedie6	= {false,SPR_FAKE_DEAD,0,NULL,NULL,&s_fakedie6};
+
+statetype s_fakeshoot1 	= {false,SPR_FAKE_SHOOT,8,NULL,T_FakeFire,&s_fakeshoot2};
+statetype s_fakeshoot2 	= {false,SPR_FAKE_SHOOT,8,NULL,T_FakeFire,&s_fakeshoot3};
+statetype s_fakeshoot3 	= {false,SPR_FAKE_SHOOT,8,NULL,T_FakeFire,&s_fakeshoot4};
+statetype s_fakeshoot4 	= {false,SPR_FAKE_SHOOT,8,NULL,T_FakeFire,&s_fakeshoot5};
+statetype s_fakeshoot5 	= {false,SPR_FAKE_SHOOT,8,NULL,T_FakeFire,&s_fakeshoot6};
+statetype s_fakeshoot6 	= {false,SPR_FAKE_SHOOT,8,NULL,T_FakeFire,&s_fakeshoot7};
+statetype s_fakeshoot7 	= {false,SPR_FAKE_SHOOT,8,NULL,T_FakeFire,&s_fakeshoot8};
+statetype s_fakeshoot8 	= {false,SPR_FAKE_SHOOT,8,NULL,T_FakeFire,&s_fakeshoot9};
+statetype s_fakeshoot9 	= {false,SPR_FAKE_SHOOT,8,NULL,NULL,&s_fakechase1};
+
+statetype s_fire1 	= {false,SPR_FIRE1,6,NULL,T_Projectile,&s_fire2};
+statetype s_fire2 	= {false,SPR_FIRE2,6,NULL,T_Projectile,&s_fire1};
+
+//
+// hitler
+//
+extern	statetype s_mechachase1;
+extern	statetype s_mechachase1s;
+extern	statetype s_mechachase2;
+extern	statetype s_mechachase3;
+extern	statetype s_mechachase3s;
+extern	statetype s_mechachase4;
+
+extern	statetype s_mechadie1;
+extern	statetype s_mechadie2;
+extern	statetype s_mechadie3;
+extern	statetype s_mechadie4;
+
+extern	statetype s_mechashoot1;
+extern	statetype s_mechashoot2;
+extern	statetype s_mechashoot3;
+extern	statetype s_mechashoot4;
+extern	statetype s_mechashoot5;
+extern	statetype s_mechashoot6;
+
+
+extern	statetype s_hitlerchase1;
+extern	statetype s_hitlerchase1s;
+extern	statetype s_hitlerchase2;
+extern	statetype s_hitlerchase3;
+extern	statetype s_hitlerchase3s;
+extern	statetype s_hitlerchase4;
+
+extern	statetype s_hitlerdie1;
+extern	statetype s_hitlerdie2;
+extern	statetype s_hitlerdie3;
+extern	statetype s_hitlerdie4;
+extern	statetype s_hitlerdie5;
+extern	statetype s_hitlerdie6;
+extern	statetype s_hitlerdie7;
+extern	statetype s_hitlerdie8;
+extern	statetype s_hitlerdie9;
+extern	statetype s_hitlerdie10;
+
+extern	statetype s_hitlershoot1;
+extern	statetype s_hitlershoot2;
+extern	statetype s_hitlershoot3;
+extern	statetype s_hitlershoot4;
+extern	statetype s_hitlershoot5;
+extern	statetype s_hitlershoot6;
+
+extern	statetype s_hitlerdeathcam;
+
+statetype s_mechastand	= {false,SPR_MECHA_W1,0,T_Stand,NULL,&s_mechastand};
+
+statetype s_mechachase1 	= {false,SPR_MECHA_W1,10,T_Chase,A_MechaSound,&s_mechachase1s};
+statetype s_mechachase1s	= {false,SPR_MECHA_W1,6,NULL,NULL,&s_mechachase2};
+statetype s_mechachase2 	= {false,SPR_MECHA_W2,8,T_Chase,NULL,&s_mechachase3};
+statetype s_mechachase3 	= {false,SPR_MECHA_W3,10,T_Chase,A_MechaSound,&s_mechachase3s};
+statetype s_mechachase3s	= {false,SPR_MECHA_W3,6,NULL,NULL,&s_mechachase4};
+statetype s_mechachase4 	= {false,SPR_MECHA_W4,8,T_Chase,NULL,&s_mechachase1};
+
+statetype s_mechadie1	= {false,SPR_MECHA_DIE1,10,NULL,A_DeathScream,&s_mechadie2};
+statetype s_mechadie2	= {false,SPR_MECHA_DIE2,10,NULL,NULL,&s_mechadie3};
+statetype s_mechadie3	= {false,SPR_MECHA_DIE3,10,NULL,A_HitlerMorph,&s_mechadie4};
+statetype s_mechadie4	= {false,SPR_MECHA_DEAD,0,NULL,NULL,&s_mechadie4};
+
+statetype s_mechashoot1 	= {false,SPR_MECHA_SHOOT1,30,NULL,NULL,&s_mechashoot2};
+statetype s_mechashoot2 	= {false,SPR_MECHA_SHOOT2,10,NULL,T_Shoot,&s_mechashoot3};
+statetype s_mechashoot3 	= {false,SPR_MECHA_SHOOT3,10,NULL,T_Shoot,&s_mechashoot4};
+statetype s_mechashoot4 	= {false,SPR_MECHA_SHOOT2,10,NULL,T_Shoot,&s_mechashoot5};
+statetype s_mechashoot5 	= {false,SPR_MECHA_SHOOT3,10,NULL,T_Shoot,&s_mechashoot6};
+statetype s_mechashoot6 	= {false,SPR_MECHA_SHOOT2,10,NULL,T_Shoot,&s_mechachase1};
+
+
+statetype s_hitlerchase1 	= {false,SPR_HITLER_W1,6,T_Chase,NULL,&s_hitlerchase1s};
+statetype s_hitlerchase1s	= {false,SPR_HITLER_W1,4,NULL,NULL,&s_hitlerchase2};
+statetype s_hitlerchase2 	= {false,SPR_HITLER_W2,2,T_Chase,NULL,&s_hitlerchase3};
+statetype s_hitlerchase3 	= {false,SPR_HITLER_W3,6,T_Chase,NULL,&s_hitlerchase3s};
+statetype s_hitlerchase3s	= {false,SPR_HITLER_W3,4,NULL,NULL,&s_hitlerchase4};
+statetype s_hitlerchase4 	= {false,SPR_HITLER_W4,2,T_Chase,NULL,&s_hitlerchase1};
+
+statetype s_hitlerdeathcam	= {false,SPR_HITLER_W1,10,NULL,NULL,&s_hitlerdie1};
+
+statetype s_hitlerdie1	= {false,SPR_HITLER_W1,1,NULL,A_DeathScream,&s_hitlerdie2};
+statetype s_hitlerdie2	= {false,SPR_HITLER_W1,10,NULL,NULL,&s_hitlerdie3};
+statetype s_hitlerdie3	= {false,SPR_HITLER_DIE1,10,NULL,A_Slurpie,&s_hitlerdie4};
+statetype s_hitlerdie4	= {false,SPR_HITLER_DIE2,10,NULL,NULL,&s_hitlerdie5};
+statetype s_hitlerdie5	= {false,SPR_HITLER_DIE3,10,NULL,NULL,&s_hitlerdie6};
+statetype s_hitlerdie6	= {false,SPR_HITLER_DIE4,10,NULL,NULL,&s_hitlerdie7};
+statetype s_hitlerdie7	= {false,SPR_HITLER_DIE5,10,NULL,NULL,&s_hitlerdie8};
+statetype s_hitlerdie8	= {false,SPR_HITLER_DIE6,10,NULL,NULL,&s_hitlerdie9};
+statetype s_hitlerdie9	= {false,SPR_HITLER_DIE7,10,NULL,NULL,&s_hitlerdie10};
+statetype s_hitlerdie10	= {false,SPR_HITLER_DEAD,20,NULL,A_StartDeathCam,&s_hitlerdie10};
+
+statetype s_hitlershoot1 	= {false,SPR_HITLER_SHOOT1,30,NULL,NULL,&s_hitlershoot2};
+statetype s_hitlershoot2 	= {false,SPR_HITLER_SHOOT2,10,NULL,T_Shoot,&s_hitlershoot3};
+statetype s_hitlershoot3 	= {false,SPR_HITLER_SHOOT3,10,NULL,T_Shoot,&s_hitlershoot4};
+statetype s_hitlershoot4 	= {false,SPR_HITLER_SHOOT2,10,NULL,T_Shoot,&s_hitlershoot5};
+statetype s_hitlershoot5 	= {false,SPR_HITLER_SHOOT3,10,NULL,T_Shoot,&s_hitlershoot6};
+statetype s_hitlershoot6 	= {false,SPR_HITLER_SHOOT2,10,NULL,T_Shoot,&s_hitlerchase1};
+
+
+
+/*
+===============
+=
+= SpawnFakeHitler
+=
+===============
+*/
+
+void SpawnFakeHitler (int tilex, int tiley)
+{
+	unsigned	far *map,tile;
+
+
+	if (DigiMode != sds_Off)
+	  s_hitlerdie2.tictime = 140;
+	else
+	  s_hitlerdie2.tictime = 5;
+
+	SpawnNewObj (tilex,tiley,&s_fakestand);
+	new->speed = SPDPATROL;
+
+	new->obclass = fakeobj;
+	new->hitpoints = starthitpoints[gamestate.difficulty][en_fake];
+	new->dir = north;
+	new->flags |= FL_SHOOTABLE|FL_AMBUSH;
+	if (!loadedgame)
+	  gamestate.killtotal++;
+}
+
+
+/*
+===============
+=
+= SpawnHitler
+=
+===============
+*/
+
+void SpawnHitler (int tilex, int tiley)
+{
+	unsigned	far *map,tile;
+
+	if (DigiMode != sds_Off)
+		s_hitlerdie2.tictime = 140;
+	else
+		s_hitlerdie2.tictime = 5;
+
+
+	SpawnNewObj (tilex,tiley,&s_mechastand);
+	new->speed = SPDPATROL;
+
+	new->obclass = mechahitlerobj;
+	new->hitpoints = starthitpoints[gamestate.difficulty][en_hitler];
+	new->dir = south;
+	new->flags |= FL_SHOOTABLE|FL_AMBUSH;
+	if (!loadedgame)
+	  gamestate.killtotal++;
+}
+
+
+/*
+===============
+=
+= A_HitlerMorph
+=
+===============
+*/
+
+void A_HitlerMorph (objtype *ob)
+{
+	unsigned	far *map,tile,hitpoints[4]={500,700,800,900};
+
+
+	SpawnNewObj (ob->tilex,ob->tiley,&s_hitlerchase1);
+	new->speed = SPDPATROL*5;
+
+	new->x = ob->x;
+	new->y = ob->y;
+
+	new->distance = ob->distance;
+	new->dir = ob->dir;
+	new->flags = ob->flags | FL_SHOOTABLE;
+
+	new->obclass = realhitlerobj;
+	new->hitpoints = hitpoints[gamestate.difficulty];
+}
+
+
+////////////////////////////////////////////////////////
+//
+// A_MechaSound
+// A_Slurpie
+//
+////////////////////////////////////////////////////////
+void A_MechaSound (objtype *ob)
+{
+	if (areabyplayer[ob->areanumber])
+		PlaySoundLocActor (MECHSTEPSND,ob);
+}
+
+
+#pragma argsused
+void A_Slurpie (objtype *ob)
+{
+ SD_PlaySound(SLURPIESND);
+}
+
+/*
+=================
+=
+= T_FakeFire
+=
+=================
+*/
+
+void T_FakeFire (objtype *ob)
+{
+	long	deltax,deltay;
+	float	angle;
+	int		iangle;
+
+	deltax = player->x - ob->x;
+	deltay = ob->y - player->y;
+	angle = atan2 (deltay,deltax);
+	if (angle<0)
+		angle = M_PI*2+angle;
+	iangle = angle/(M_PI*2)*ANGLES;
+
+	GetNewActor ();
+	new->state = &s_fire1;
+	new->ticcount = 1;
+
+	new->tilex = ob->tilex;
+	new->tiley = ob->tiley;
+	new->x = ob->x;
+	new->y = ob->y;
+	new->dir = nodir;
+	new->angle = iangle;
+	new->obclass = fireobj;
+	new->speed = 0x1200l;
+	new->flags = FL_NEVERMARK;
+	new->active = true;
+
+	PlaySoundLocActor (FLAMETHROWERSND,new);
+}
+
+
+
+/*
+=================
+=
+= T_Fake
+=
+=================
+*/
+
+void T_Fake (objtype *ob)
+{
+	long move;
+	int	dx,dy,dist;
+	boolean	dodge;
+
+	if (CheckLine(ob))			// got a shot at player?
+	{
+		if ( US_RndT() < (tics<<1) )
+		{
+		//
+		// go into attack frame
+		//
+			NewState (ob,&s_fakeshoot1);
+			return;
+		}
+	}
+
+	if (ob->dir == nodir)
+	{
+		SelectDodgeDir (ob);
+		if (ob->dir == nodir)
+			return;							// object is blocked in
+	}
+
+	move = ob->speed*tics;
+
+	while (move)
+	{
+		if (move < ob->distance)
+		{
+			MoveObj (ob,move);
+			break;
+		}
+
+		//
+		// reached goal tile, so select another one
+		//
+
+		//
+		// fix position to account for round off during moving
+		//
+		ob->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;
+		ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;
+
+		move -= ob->distance;
+
+		SelectDodgeDir (ob);
+
+		if (ob->dir == nodir)
+			return;							// object is blocked in
+	}
+
+}
+
+#endif
+/*
+============================================================================
+
+							STAND
+
+============================================================================
+*/
+
+
+/*
+===============
+=
+= T_Stand
+=
+===============
+*/
+
+void T_Stand (objtype *ob)
+{
+	SightPlayer (ob);
+}
+
+
+/*
+============================================================================
+
+								CHASE
+
+============================================================================
+*/
+
+/*
+=================
+=
+= T_Chase
+=
+=================
+*/
+
+void T_Chase (objtype *ob)
+{
+	long move;
+	int	dx,dy,dist,chance;
+	boolean	dodge;
+
+	if (gamestate.victoryflag)
+		return;
+
+	dodge = false;
+	if (CheckLine(ob))	// got a shot at player?
+	{
+		dx = abs(ob->tilex - player->tilex);
+		dy = abs(ob->tiley - player->tiley);
+		dist = dx>dy ? dx : dy;
+		if (!dist || (dist==1 && ob->distance<0x4000) )
+			chance = 300;
+		else
+			chance = (tics<<4)/dist;
+
+		if ( US_RndT()<chance)
+		{
+		//
+		// go into attack frame
+		//
+			switch (ob->obclass)
+			{
+			case guardobj:
+				NewState (ob,&s_grdshoot1);
+				break;
+			case officerobj:
+				NewState (ob,&s_ofcshoot1);
+				break;
+			case mutantobj:
+				NewState (ob,&s_mutshoot1);
+				break;
+			case ssobj:
+				NewState (ob,&s_ssshoot1);
+				break;
+#ifndef SPEAR
+			case bossobj:
+				NewState (ob,&s_bossshoot1);
+				break;
+			case gretelobj:
+				NewState (ob,&s_gretelshoot1);
+				break;
+			case mechahitlerobj:
+				NewState (ob,&s_mechashoot1);
+				break;
+			case realhitlerobj:
+				NewState (ob,&s_hitlershoot1);
+				break;
+#else
+			case angelobj:
+				NewState (ob,&s_angelshoot1);
+				break;
+			case transobj:
+				NewState (ob,&s_transshoot1);
+				break;
+			case uberobj:
+				NewState (ob,&s_ubershoot1);
+				break;
+			case willobj:
+				NewState (ob,&s_willshoot1);
+				break;
+			case deathobj:
+				NewState (ob,&s_deathshoot1);
+				break;
+#endif
+			}
+			return;
+		}
+		dodge = true;
+	}
+
+	if (ob->dir == nodir)
+	{
+		if (dodge)
+			SelectDodgeDir (ob);
+		else
+			SelectChaseDir (ob);
+		if (ob->dir == nodir)
+			return;							// object is blocked in
+	}
+
+	move = ob->speed*tics;
+
+	while (move)
+	{
+		if (ob->distance < 0)
+		{
+		//
+		// waiting for a door to open
+		//
+			OpenDoor (-ob->distance-1);
+			if (doorobjlist[-ob->distance-1].action != dr_open)
+				return;
+			ob->distance = TILEGLOBAL;	// go ahead, the door is now opoen
+		}
+
+		if (move < ob->distance)
+		{
+			MoveObj (ob,move);
+			break;
+		}
+
+		//
+		// reached goal tile, so select another one
+		//
+
+		//
+		// fix position to account for round off during moving
+		//
+		ob->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;
+		ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;
+
+		move -= ob->distance;
+
+		if (dodge)
+			SelectDodgeDir (ob);
+		else
+			SelectChaseDir (ob);
+
+		if (ob->dir == nodir)
+			return;							// object is blocked in
+	}
+
+}
+
+
+/*
+=================
+=
+= T_Ghosts
+=
+=================
+*/
+
+void T_Ghosts (objtype *ob)
+{
+	long move;
+
+
+	if (ob->dir == nodir)
+	{
+		SelectChaseDir (ob);
+		if (ob->dir == nodir)
+			return;							// object is blocked in
+	}
+
+	move = ob->speed*tics;
+
+	while (move)
+	{
+		if (move < ob->distance)
+		{
+			MoveObj (ob,move);
+			break;
+		}
+
+		//
+		// reached goal tile, so select another one
+		//
+
+		//
+		// fix position to account for round off during moving
+		//
+		ob->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;
+		ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;
+
+		move -= ob->distance;
+
+		SelectChaseDir (ob);
+
+		if (ob->dir == nodir)
+			return;							// object is blocked in
+	}
+
+}
+
+/*
+=================
+=
+= T_DogChase
+=
+=================
+*/
+
+void T_DogChase (objtype *ob)
+{
+	long 	move;
+	int		dist,chance;
+	long	dx,dy;
+
+
+	if (ob->dir == nodir)
+	{
+		SelectDodgeDir (ob);
+		if (ob->dir == nodir)
+			return;							// object is blocked in
+	}
+
+	move = ob->speed*tics;
+
+	while (move)
+	{
+	//
+	// check for byte range
+	//
+		dx = player->x - ob->x;
+		if (dx<0)
+			dx = -dx;
+		dx -= move;
+		if (dx <= MINACTORDIST)
+		{
+			dy = player->y - ob->y;
+			if (dy<0)
+				dy = -dy;
+			dy -= move;
+			if (dy <= MINACTORDIST)
+			{
+				NewState (ob,&s_dogjump1);
+				return;
+			}
+		}
+
+		if (move < ob->distance)
+		{
+			MoveObj (ob,move);
+			break;
+		}
+
+		//
+		// reached goal tile, so select another one
+		//
+
+		//
+		// fix position to account for round off during moving
+		//
+		ob->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;
+		ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;
+
+		move -= ob->distance;
+
+		SelectDodgeDir (ob);
+
+		if (ob->dir == nodir)
+			return;							// object is blocked in
+	}
+
+}
+
+
+
+/*
+============================================================================
+
+								PATH
+
+============================================================================
+*/
+
+
+/*
+===============
+=
+= SelectPathDir
+=
+===============
+*/
+
+void SelectPathDir (objtype *ob)
+{
+	unsigned spot;
+
+	spot = MAPSPOT(ob->tilex,ob->tiley,1)-ICONARROWS;
+
+	if (spot<8)
+	{
+	// new direction
+		ob->dir = spot;
+	}
+
+	ob->distance = TILEGLOBAL;
+
+	if (!TryWalk (ob))
+		ob->dir = nodir;
+}
+
+
+/*
+===============
+=
+= T_Path
+=
+===============
+*/
+
+void T_Path (objtype *ob)
+{
+	long 	move;
+	long 	deltax,deltay,size;
+
+	if (SightPlayer (ob))
+		return;
+
+	if (ob->dir == nodir)
+	{
+		SelectPathDir (ob);
+		if (ob->dir == nodir)
+			return;					// all movement is blocked
+	}
+
+
+	move = ob->speed*tics;
+
+	while (move)
+	{
+		if (ob->distance < 0)
+		{
+		//
+		// waiting for a door to open
+		//
+			OpenDoor (-ob->distance-1);
+			if (doorobjlist[-ob->distance-1].action != dr_open)
+				return;
+			ob->distance = TILEGLOBAL;	// go ahead, the door is now opoen
+		}
+
+		if (move < ob->distance)
+		{
+			MoveObj (ob,move);
+			break;
+		}
+
+		if (ob->tilex>MAPSIZE || ob->tiley>MAPSIZE)
+		{
+			sprintf (str,"T_Path hit a wall at %u,%u, dir %u"
+			,ob->tilex,ob->tiley,ob->dir);
+			Quit (str);
+		}
+
+
+
+		ob->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;
+		ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;
+		move -= ob->distance;
+
+		SelectPathDir (ob);
+
+		if (ob->dir == nodir)
+			return;					// all movement is blocked
+	}
+}
+
+
+/*
+=============================================================================
+
+								FIGHT
+
+=============================================================================
+*/
+
+
+/*
+===============
+=
+= T_Shoot
+=
+= Try to damage the player, based on skill level and player's speed
+=
+===============
+*/
+
+void T_Shoot (objtype *ob)
+{
+	int	dx,dy,dist;
+	int	hitchance,damage;
+
+	hitchance = 128;
+
+	if (!areabyplayer[ob->areanumber])
+		return;
+
+	if (!CheckLine (ob))			// player is behind a wall
+	  return;
+
+	dx = abs(ob->tilex - player->tilex);
+	dy = abs(ob->tiley - player->tiley);
+	dist = dx>dy ? dx:dy;
+
+	if (ob->obclass == ssobj || ob->obclass == bossobj)
+		dist = dist*2/3;					// ss are better shots
+
+	if (thrustspeed >= RUNSPEED)
+	{
+		if (ob->flags&FL_VISABLE)
+			hitchance = 160-dist*16;		// player can see to dodge
+		else
+			hitchance = 160-dist*8;
+	}
+	else
+	{
+		if (ob->flags&FL_VISABLE)
+			hitchance = 256-dist*16;		// player can see to dodge
+		else
+			hitchance = 256-dist*8;
+	}
+
+// see if the shot was a hit
+
+	if (US_RndT()<hitchance)
+	{
+		if (dist<2)
+			damage = US_RndT()>>2;
+		else if (dist<4)
+			damage = US_RndT()>>3;
+		else
+			damage = US_RndT()>>4;
+
+		TakeDamage (damage,ob);
+	}
+
+	switch(ob->obclass)
+	{
+	 case ssobj:
+	   PlaySoundLocActor(SSFIRESND,ob);
+	   break;
+#ifndef SPEAR
+	 case giftobj:
+	 case fatobj:
+	   PlaySoundLocActor(MISSILEFIRESND,ob);
+	   break;
+	 case mechahitlerobj:
+	 case realhitlerobj:
+	 case bossobj:
+	   PlaySoundLocActor(BOSSFIRESND,ob);
+	   break;
+	 case schabbobj:
+	   PlaySoundLocActor(SCHABBSTHROWSND,ob);
+	   break;
+	 case fakeobj:
+	   PlaySoundLocActor(FLAMETHROWERSND,ob);
+	   break;
+#endif
+	 default:
+	   PlaySoundLocActor(NAZIFIRESND,ob);
+	}
+
+}
+
+
+/*
+===============
+=
+= T_Bite
+=
+===============
+*/
+
+void T_Bite (objtype *ob)
+{
+	long	dx,dy;
+	int	hitchance,damage;
+
+
+	PlaySoundLocActor(DOGATTACKSND,ob);	// JAB
+
+	dx = player->x - ob->x;
+	if (dx<0)
+		dx = -dx;
+	dx -= TILEGLOBAL;
+	if (dx <= MINACTORDIST)
+	{
+		dy = player->y - ob->y;
+		if (dy<0)
+			dy = -dy;
+		dy -= TILEGLOBAL;
+		if (dy <= MINACTORDIST)
+		{
+		   if (US_RndT()<180)
+		   {
+			   TakeDamage (US_RndT()>>4,ob);
+			   return;
+		   }
+		}
+	}
+
+	return;
+}
+
+
+#ifndef SPEAR
+/*
+============================================================================
+
+							BJ VICTORY
+
+============================================================================
+*/
+
+
+//
+// BJ victory
+//
+
+void T_BJRun (objtype *ob);
+void T_BJJump (objtype *ob);
+void T_BJDone (objtype *ob);
+void T_BJYell (objtype *ob);
+
+void T_DeathCam (objtype *ob);
+
+extern	statetype s_bjrun1;
+extern	statetype s_bjrun1s;
+extern	statetype s_bjrun2;
+extern	statetype s_bjrun3;
+extern	statetype s_bjrun3s;
+extern	statetype s_bjrun4;
+
+extern	statetype s_bjjump1;
+extern	statetype s_bjjump2;
+extern	statetype s_bjjump3;
+extern	statetype s_bjjump4;
+
+
+statetype s_bjrun1 	= {false,SPR_BJ_W1,12,T_BJRun,NULL,&s_bjrun1s};
+statetype s_bjrun1s	= {false,SPR_BJ_W1,3, NULL,NULL,&s_bjrun2};
+statetype s_bjrun2 	= {false,SPR_BJ_W2,8,T_BJRun,NULL,&s_bjrun3};
+statetype s_bjrun3 	= {false,SPR_BJ_W3,12,T_BJRun,NULL,&s_bjrun3s};
+statetype s_bjrun3s	= {false,SPR_BJ_W3,3, NULL,NULL,&s_bjrun4};
+statetype s_bjrun4 	= {false,SPR_BJ_W4,8,T_BJRun,NULL,&s_bjrun1};
+
+
+statetype s_bjjump1	= {false,SPR_BJ_JUMP1,14,T_BJJump,NULL,&s_bjjump2};
+statetype s_bjjump2	= {false,SPR_BJ_JUMP2,14,T_BJJump,T_BJYell,&s_bjjump3};
+statetype s_bjjump3	= {false,SPR_BJ_JUMP3,14,T_BJJump,NULL,&s_bjjump4};
+statetype s_bjjump4	= {false,SPR_BJ_JUMP4,300,NULL,T_BJDone,&s_bjjump4};
+
+
+statetype s_deathcam = {false,0,0,NULL,NULL,NULL};
+
+
+/*
+===============
+=
+= SpawnBJVictory
+=
+===============
+*/
+
+void SpawnBJVictory (void)
+{
+	unsigned	far *map,tile;
+
+	SpawnNewObj (player->tilex,player->tiley+1,&s_bjrun1);
+	new->x = player->x;
+	new->y = player->y;
+	new->obclass = bjobj;
+	new->dir = north;
+	new->temp1 = 6;			// tiles to run forward
+}
+
+
+
+/*
+===============
+=
+= T_BJRun
+=
+===============
+*/
+
+void T_BJRun (objtype *ob)
+{
+	long 	move;
+
+	move = BJRUNSPEED*tics;
+
+	while (move)
+	{
+		if (move < ob->distance)
+		{
+			MoveObj (ob,move);
+			break;
+		}
+
+
+		ob->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;
+		ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;
+		move -= ob->distance;
+
+		SelectPathDir (ob);
+
+		if ( !(--ob->temp1) )
+		{
+			NewState (ob,&s_bjjump1);
+			return;
+		}
+	}
+}
+
+
+/*
+===============
+=
+= T_BJJump
+=
+===============
+*/
+
+void T_BJJump (objtype *ob)
+{
+	long 	move;
+
+	move = BJJUMPSPEED*tics;
+	MoveObj (ob,move);
+}
+
+
+/*
+===============
+=
+= T_BJYell
+=
+===============
+*/
+
+void T_BJYell (objtype *ob)
+{
+	PlaySoundLocActor(YEAHSND,ob);	// JAB
+}
+
+
+/*
+===============
+=
+= T_BJDone
+=
+===============
+*/
+
+#pragma argsused
+void T_BJDone (objtype *ob)
+{
+	playstate = ex_victorious;				// exit castle tile
+}
+
+
+
+//===========================================================================
+
+
+/*
+===============
+=
+= CheckPosition
+=
+===============
+*/
+
+boolean	CheckPosition (objtype *ob)
+{
+	int	x,y,xl,yl,xh,yh;
+	objtype *check;
+
+	xl = (ob->x-PLAYERSIZE) >>TILESHIFT;
+	yl = (ob->y-PLAYERSIZE) >>TILESHIFT;
+
+	xh = (ob->x+PLAYERSIZE) >>TILESHIFT;
+	yh = (ob->y+PLAYERSIZE) >>TILESHIFT;
+
+	//
+	// check for solid walls
+	//
+	for (y=yl;y<=yh;y++)
+		for (x=xl;x<=xh;x++)
+		{
+			check = actorat[x][y];
+			if (check && check<objlist)
+				return false;
+		}
+
+	return true;
+}
+
+
+/*
+===============
+=
+= A_StartDeathCam
+=
+===============
+*/
+
+void	A_StartDeathCam (objtype *ob)
+{
+	long	dx,dy;
+	float	fangle;
+	long    xmove,ymove;
+	long	dist;
+	int		temp,i;
+
+	FinishPaletteShifts ();
+
+	VW_WaitVBL (100);
+
+	if (gamestate.victoryflag)
+	{
+		playstate = ex_victorious;				// exit castle tile
+		return;
+	}
+
+	gamestate.victoryflag = true;
+	VW_Bar (0,0,320,200-STATUSLINES,127);
+	FizzleFade(bufferofs,displayofs,320,200-STATUSLINES,70,false);
+
+	PM_UnlockMainMem ();
+	CA_UpLevel ();
+	CacheLump(LEVELEND_LUMP_START,LEVELEND_LUMP_END);
+	#ifdef JAPAN
+	#ifndef JAPDEMO
+	CA_CacheScreen(C_LETSSEEPIC);
+	#endif
+	#else
+	Write(0,7,STR_SEEAGAIN);
+	#endif
+	CA_DownLevel ();
+	PM_CheckMainMem ();
+
+	VW_UpdateScreen ();
+
+	IN_UserInput(300);
+
+//
+// line angle up exactly
+//
+	NewState (player,&s_deathcam);
+
+	player->x = gamestate.killx;
+	player->y = gamestate.killy;
+
+	dx = ob->x - player->x;
+	dy = player->y - ob->y;
+
+	fangle = atan2(dy,dx);			// returns -pi to pi
+	if (fangle<0)
+		fangle = M_PI*2+fangle;
+
+	player->angle = fangle/(M_PI*2)*ANGLES;
+
+//
+// try to position as close as possible without being in a wall
+//
+	dist = 0x14000l;
+	do
+	{
+		xmove = FixedByFrac(dist,costable[player->angle]);
+		ymove = -FixedByFrac(dist,sintable[player->angle]);
+
+		player->x = ob->x - xmove;
+		player->y = ob->y - ymove;
+		dist += 0x1000;
+
+	} while (!CheckPosition (player));
+	plux = player->x >> UNSIGNEDSHIFT;			// scale to fit in unsigned
+	pluy = player->y >> UNSIGNEDSHIFT;
+	player->tilex = player->x >> TILESHIFT;		// scale to tile values
+	player->tiley = player->y >> TILESHIFT;
+
+//
+// go back to the game
+//
+	temp = bufferofs;
+	for (i=0;i<3;i++)
+	{
+		bufferofs = screenloc[i];
+		DrawPlayBorder ();
+	}
+	bufferofs = temp;
+
+	fizzlein = true;
+	switch (ob->obclass)
+	{
+#ifndef SPEAR
+	case schabbobj:
+		NewState (ob,&s_schabbdeathcam);
+		break;
+	case realhitlerobj:
+		NewState (ob,&s_hitlerdeathcam);
+		break;
+	case giftobj:
+		NewState (ob,&s_giftdeathcam);
+		break;
+	case fatobj:
+		NewState (ob,&s_fatdeathcam);
+		break;
+#endif
+	}
+
+}
+
+#endif
diff --git a/src/lib/hb/wl_agent.c b/src/lib/hb/wl_agent.c
new file mode 100755
index 00000000..631478d0
--- /dev/null
+++ b/src/lib/hb/wl_agent.c
@@ -0,0 +1,1421 @@
+// WL_AGENT.C
+
+#include "WL_DEF.H"
+#pragma hdrstop
+
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+#define MAXMOUSETURN	10
+
+
+#define MOVESCALE		150l
+#define BACKMOVESCALE	100l
+#define ANGLESCALE		20
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+
+
+//
+// player state info
+//
+boolean		running;
+long		thrustspeed;
+
+unsigned	plux,pluy;			// player coordinates scaled to unsigned
+
+int			anglefrac;
+int			gotgatgun;	// JR
+
+objtype		*LastAttacker;
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+
+void	T_Player (objtype *ob);
+void	T_Attack (objtype *ob);
+
+statetype s_player = {false,0,0,T_Player,NULL,NULL};
+statetype s_attack = {false,0,0,T_Attack,NULL,NULL};
+
+
+long	playerxmove,playerymove;
+
+struct atkinf
+{
+	char	tics,attack,frame;		// attack is 1 for gun, 2 for knife
+} attackinfo[4][14] =
+
+{
+{ {6,0,1},{6,2,2},{6,0,3},{6,-1,4} },
+{ {6,0,1},{6,1,2},{6,0,3},{6,-1,4} },
+{ {6,0,1},{6,1,2},{6,3,3},{6,-1,4} },
+{ {6,0,1},{6,1,2},{6,4,3},{6,-1,4} },
+};
+
+
+int	strafeangle[9] = {0,90,180,270,45,135,225,315,0};
+
+void DrawWeapon (void);
+void GiveWeapon (int weapon);
+void	GiveAmmo (int ammo);
+
+//===========================================================================
+
+//----------
+
+void Attack (void);
+void Use (void);
+void Search (objtype *ob);
+void SelectWeapon (void);
+void SelectItem (void);
+
+//----------
+
+boolean TryMove (objtype *ob);
+void T_Player (objtype *ob);
+
+void ClipMove (objtype *ob, long xmove, long ymove);
+
+/*
+=============================================================================
+
+						CONTROL STUFF
+
+=============================================================================
+*/
+
+/*
+======================
+=
+= CheckWeaponChange
+=
+= Keys 1-4 change weapons
+=
+======================
+*/
+
+void CheckWeaponChange (void)
+{
+	int	i,buttons;
+
+	if (!gamestate.ammo)		// must use knife with no ammo
+		return;
+
+	for (i=wp_knife ; i<=gamestate.bestweapon ; i++)
+		if (buttonstate[bt_readyknife+i-wp_knife])
+		{
+			gamestate.weapon = gamestate.chosenweapon = i;
+			DrawWeapon ();
+			return;
+		}
+}
+
+
+/*
+=======================
+=
+= ControlMovement
+=
+= Takes controlx,controly, and buttonstate[bt_strafe]
+=
+= Changes the player's angle and position
+=
+= There is an angle hack because when going 70 fps, the roundoff becomes
+= significant
+=
+=======================
+*/
+
+void ControlMovement (objtype *ob)
+{
+	long	oldx,oldy;
+	int		angle,maxxmove;
+	int		angleunits;
+	long	speed;
+
+	thrustspeed = 0;
+
+	oldx = player->x;
+	oldy = player->y;
+
+//
+// side to side move
+//
+	if (buttonstate[bt_strafe])
+	{
+	//
+	// strafing
+	//
+	//
+		if (controlx > 0)
+		{
+			angle = ob->angle - ANGLES/4;
+			if (angle < 0)
+				angle += ANGLES;
+			Thrust (angle,controlx*MOVESCALE);	// move to left
+		}
+		else if (controlx < 0)
+		{
+			angle = ob->angle + ANGLES/4;
+			if (angle >= ANGLES)
+				angle -= ANGLES;
+			Thrust (angle,-controlx*MOVESCALE);	// move to right
+		}
+	}
+	else
+	{
+	//
+	// not strafing
+	//
+		anglefrac += controlx;
+		angleunits = anglefrac/ANGLESCALE;
+		anglefrac -= angleunits*ANGLESCALE;
+		ob->angle -= angleunits;
+
+		if (ob->angle >= ANGLES)
+			ob->angle -= ANGLES;
+		if (ob->angle < 0)
+			ob->angle += ANGLES;
+
+	}
+
+//
+// forward/backwards move
+//
+	if (controly < 0)
+	{
+		Thrust (ob->angle,-controly*MOVESCALE);	// move forwards
+	}
+	else if (controly > 0)
+	{
+		angle = ob->angle + ANGLES/2;
+		if (angle >= ANGLES)
+			angle -= ANGLES;
+		Thrust (angle,controly*BACKMOVESCALE);		// move backwards
+	}
+
+	if (gamestate.victoryflag)		// watching the BJ actor
+		return;
+
+//
+// calculate total move
+//
+	playerxmove = player->x - oldx;
+	playerymove = player->y - oldy;
+}
+
+/*
+=============================================================================
+
+					STATUS WINDOW STUFF
+
+=============================================================================
+*/
+
+
+/*
+==================
+=
+= StatusDrawPic
+=
+==================
+*/
+
+void StatusDrawPic (unsigned x, unsigned y, unsigned picnum)
+{
+	unsigned	temp;
+
+	temp = bufferofs;
+	bufferofs = 0;
+
+	bufferofs = PAGE1START+(200-STATUSLINES)*SCREENWIDTH;
+	LatchDrawPic (x,y,picnum);
+	bufferofs = PAGE2START+(200-STATUSLINES)*SCREENWIDTH;
+	LatchDrawPic (x,y,picnum);
+	bufferofs = PAGE3START+(200-STATUSLINES)*SCREENWIDTH;
+	LatchDrawPic (x,y,picnum);
+
+	bufferofs = temp;
+}
+
+
+/*
+==================
+=
+= DrawFace
+=
+==================
+*/
+
+void DrawFace (void)
+{
+	if (gamestate.health)
+	{
+		#ifdef SPEAR
+		if (godmode)
+			StatusDrawPic (17,4,GODMODEFACE1PIC+gamestate.faceframe);
+		else
+		#endif
+		StatusDrawPic (17,4,FACE1APIC+3*((100-gamestate.health)/16)+gamestate.faceframe);
+	}
+	else
+	{
+#ifndef SPEAR
+	 if (LastAttacker->obclass == needleobj)
+	   StatusDrawPic (17,4,MUTANTBJPIC);
+	 else
+#endif
+	   StatusDrawPic (17,4,FACE8APIC);
+	}
+}
+
+
+/*
+===============
+=
+= UpdateFace
+=
+= Calls draw face if time to change
+=
+===============
+*/
+
+#define FACETICS	70
+
+int	facecount;
+
+void	UpdateFace (void)
+{
+
+	if (SD_SoundPlaying() == GETGATLINGSND)
+	  return;
+
+	facecount += tics;
+	if (facecount > US_RndT())
+	{
+		gamestate.faceframe = (US_RndT()>>6);
+		if (gamestate.faceframe==3)
+			gamestate.faceframe = 1;
+
+		facecount = 0;
+		DrawFace ();
+	}
+}
+
+
+
+/*
+===============
+=
+= LatchNumber
+=
+= right justifies and pads with blanks
+=
+===============
+*/
+
+void	LatchNumber (int x, int y, int width, long number)
+{
+	unsigned	length,c;
+	char	str[20];
+
+	ltoa (number,str,10);
+
+	length = strlen (str);
+
+	while (length<width)
+	{
+		StatusDrawPic (x,y,N_BLANKPIC);
+		x++;
+		width--;
+	}
+
+	c= length <= width ? 0 : length-width;
+
+	while (c<length)
+	{
+		StatusDrawPic (x,y,str[c]-'0'+ N_0PIC);
+		x++;
+		c++;
+	}
+}
+
+
+/*
+===============
+=
+= DrawHealth
+=
+===============
+*/
+
+void	DrawHealth (void)
+{
+	LatchNumber (21,16,3,gamestate.health);
+}
+
+
+/*
+===============
+=
+= TakeDamage
+=
+===============
+*/
+
+void	TakeDamage (int points,objtype *attacker)
+{
+	LastAttacker = attacker;
+
+	if (gamestate.victoryflag)
+		return;
+	if (gamestate.difficulty==gd_baby)
+	  points>>=2;
+
+	if (!godmode)
+		gamestate.health -= points;
+
+	if (gamestate.health<=0)
+	{
+		gamestate.health = 0;
+		playstate = ex_died;
+		killerobj = attacker;
+	}
+
+	StartDamageFlash (points);
+
+	gotgatgun=0;
+
+	DrawHealth ();
+	DrawFace ();
+
+	//
+	// MAKE BJ'S EYES BUG IF MAJOR DAMAGE!
+	//
+	#ifdef SPEAR
+	if (points > 30 && gamestate.health!=0 && !godmode)
+	{
+		StatusDrawPic (17,4,BJOUCHPIC);
+		facecount = 0;
+	}
+	#endif
+
+}
+
+
+/*
+===============
+=
+= HealSelf
+=
+===============
+*/
+
+void	HealSelf (int points)
+{
+	gamestate.health += points;
+	if (gamestate.health>100)
+		gamestate.health = 100;
+
+	DrawHealth ();
+	gotgatgun = 0;	// JR
+	DrawFace ();
+}
+
+
+//===========================================================================
+
+
+/*
+===============
+=
+= DrawLevel
+=
+===============
+*/
+
+void	DrawLevel (void)
+{
+#ifdef SPEAR
+	if (gamestate.mapon == 20)
+		LatchNumber (2,16,2,18);
+	else
+#endif
+	LatchNumber (2,16,2,gamestate.mapon+1);
+}
+
+//===========================================================================
+
+
+/*
+===============
+=
+= DrawLives
+=
+===============
+*/
+
+void	DrawLives (void)
+{
+	LatchNumber (14,16,1,gamestate.lives);
+}
+
+
+/*
+===============
+=
+= GiveExtraMan
+=
+===============
+*/
+
+void	GiveExtraMan (void)
+{
+	if (gamestate.lives<9)
+		gamestate.lives++;
+	DrawLives ();
+	SD_PlaySound (BONUS1UPSND);
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= DrawScore
+=
+===============
+*/
+
+void	DrawScore (void)
+{
+	LatchNumber (6,16,6,gamestate.score);
+}
+
+/*
+===============
+=
+= GivePoints
+=
+===============
+*/
+
+void	GivePoints (long points)
+{
+	gamestate.score += points;
+	while (gamestate.score >= gamestate.nextextra)
+	{
+		gamestate.nextextra += EXTRAPOINTS;
+		GiveExtraMan ();
+	}
+	DrawScore ();
+}
+
+//===========================================================================
+
+/*
+==================
+=
+= DrawWeapon
+=
+==================
+*/
+
+void DrawWeapon (void)
+{
+	StatusDrawPic (32,8,KNIFEPIC+gamestate.weapon);
+}
+
+
+/*
+==================
+=
+= DrawKeys
+=
+==================
+*/
+
+void DrawKeys (void)
+{
+	if (gamestate.keys & 1)
+		StatusDrawPic (30,4,GOLDKEYPIC);
+	else
+		StatusDrawPic (30,4,NOKEYPIC);
+
+	if (gamestate.keys & 2)
+		StatusDrawPic (30,20,SILVERKEYPIC);
+	else
+		StatusDrawPic (30,20,NOKEYPIC);
+}
+
+
+
+/*
+==================
+=
+= GiveWeapon
+=
+==================
+*/
+
+void GiveWeapon (int weapon)
+{
+	GiveAmmo (6);
+
+	if (gamestate.bestweapon<weapon)
+		gamestate.bestweapon = gamestate.weapon
+		= gamestate.chosenweapon = weapon;
+
+	DrawWeapon ();
+}
+
+
+//===========================================================================
+
+/*
+===============
+=
+= DrawAmmo
+=
+===============
+*/
+
+void	DrawAmmo (void)
+{
+	LatchNumber (27,16,2,gamestate.ammo);
+}
+
+
+/*
+===============
+=
+= GiveAmmo
+=
+===============
+*/
+
+void	GiveAmmo (int ammo)
+{
+	if (!gamestate.ammo)				// knife was out
+	{
+		if (!gamestate.attackframe)
+		{
+			gamestate.weapon = gamestate.chosenweapon;
+			DrawWeapon ();
+		}
+	}
+	gamestate.ammo += ammo;
+	if (gamestate.ammo > 99)
+		gamestate.ammo = 99;
+	DrawAmmo ();
+}
+
+//===========================================================================
+
+/*
+==================
+=
+= GiveKey
+=
+==================
+*/
+
+void GiveKey (int key)
+{
+	gamestate.keys |= (1<<key);
+	DrawKeys ();
+}
+
+
+
+/*
+=============================================================================
+
+							MOVEMENT
+
+=============================================================================
+*/
+
+
+/*
+===================
+=
+= GetBonus
+=
+===================
+*/
+void GetBonus (statobj_t *check)
+{
+	switch (check->itemnumber)
+	{
+	case	bo_firstaid:
+		if (gamestate.health == 100)
+			return;
+
+		SD_PlaySound (HEALTH2SND);
+		HealSelf (25);
+		break;
+
+	case	bo_key1:
+	case	bo_key2:
+	case	bo_key3:
+	case	bo_key4:
+		GiveKey (check->itemnumber - bo_key1);
+		SD_PlaySound (GETKEYSND);
+		break;
+
+	case	bo_cross:
+		SD_PlaySound (BONUS1SND);
+		GivePoints (100);
+		gamestate.treasurecount++;
+		break;
+	case	bo_chalice:
+		SD_PlaySound (BONUS2SND);
+		GivePoints (500);
+		gamestate.treasurecount++;
+		break;
+	case	bo_bible:
+		SD_PlaySound (BONUS3SND);
+		GivePoints (1000);
+		gamestate.treasurecount++;
+		break;
+	case	bo_crown:
+		SD_PlaySound (BONUS4SND);
+		GivePoints (5000);
+		gamestate.treasurecount++;
+		break;
+
+	case	bo_clip:
+		if (gamestate.ammo == 99)
+			return;
+
+		SD_PlaySound (GETAMMOSND);
+		GiveAmmo (8);
+		break;
+	case	bo_clip2:
+		if (gamestate.ammo == 99)
+			return;
+
+		SD_PlaySound (GETAMMOSND);
+		GiveAmmo (4);
+		break;
+
+#ifdef SPEAR
+	case	bo_25clip:
+		if (gamestate.ammo == 99)
+		  return;
+
+		SD_PlaySound (GETAMMOBOXSND);
+		GiveAmmo (25);
+		break;
+#endif
+
+	case	bo_machinegun:
+		SD_PlaySound (GETMACHINESND);
+		GiveWeapon (wp_machinegun);
+		break;
+	case	bo_chaingun:
+		SD_PlaySound (GETGATLINGSND);
+		GiveWeapon (wp_chaingun);
+
+		StatusDrawPic (17,4,GOTGATLINGPIC);
+		facecount = 0;
+		gotgatgun = 1;
+		break;
+
+	case	bo_fullheal:
+		SD_PlaySound (BONUS1UPSND);
+		HealSelf (99);
+		GiveAmmo (25);
+		GiveExtraMan ();
+		gamestate.treasurecount++;
+		break;
+
+	case	bo_food:
+		if (gamestate.health == 100)
+			return;
+
+		SD_PlaySound (HEALTH1SND);
+		HealSelf (10);
+		break;
+
+	case	bo_alpo:
+		if (gamestate.health == 100)
+			return;
+
+		SD_PlaySound (HEALTH1SND);
+		HealSelf (4);
+		break;
+
+	case	bo_gibs:
+		if (gamestate.health >10)
+			return;
+
+		SD_PlaySound (SLURPIESND);
+		HealSelf (1);
+		break;
+
+	case	bo_spear:
+		spearflag = true;
+		spearx = player->x;
+		speary = player->y;
+		spearangle = player->angle;
+		playstate = ex_completed;
+	}
+
+	StartBonusFlash ();
+	check->shapenum = -1;			// remove from list
+}
+
+
+/*
+===================
+=
+= TryMove
+=
+= returns true if move ok
+= debug: use pointers to optimize
+===================
+*/
+
+boolean TryMove (objtype *ob)
+{
+	int			xl,yl,xh,yh,x,y;
+	objtype		*check;
+	long		deltax,deltay;
+
+	xl = (ob->x-PLAYERSIZE) >>TILESHIFT;
+	yl = (ob->y-PLAYERSIZE) >>TILESHIFT;
+
+	xh = (ob->x+PLAYERSIZE) >>TILESHIFT;
+	yh = (ob->y+PLAYERSIZE) >>TILESHIFT;
+
+//
+// check for solid walls
+//
+	for (y=yl;y<=yh;y++)
+		for (x=xl;x<=xh;x++)
+		{
+			check = actorat[x][y];
+			if (check && check<objlist)
+				return false;
+		}
+
+//
+// check for actors
+//
+	if (yl>0)
+		yl--;
+	if (yh<MAPSIZE-1)
+		yh++;
+	if (xl>0)
+		xl--;
+	if (xh<MAPSIZE-1)
+		xh++;
+
+	for (y=yl;y<=yh;y++)
+		for (x=xl;x<=xh;x++)
+		{
+			check = actorat[x][y];
+			if (check > objlist
+			&& (check->flags & FL_SHOOTABLE) )
+			{
+				deltax = ob->x - check->x;
+				if (deltax < -MINACTORDIST || deltax > MINACTORDIST)
+					continue;
+				deltay = ob->y - check->y;
+				if (deltay < -MINACTORDIST || deltay > MINACTORDIST)
+					continue;
+
+				return false;
+			}
+		}
+
+	return true;
+}
+
+
+/*
+===================
+=
+= ClipMove
+=
+===================
+*/
+
+void ClipMove (objtype *ob, long xmove, long ymove)
+{
+	long	basex,basey;
+
+	basex = ob->x;
+	basey = ob->y;
+
+	ob->x = basex+xmove;
+	ob->y = basey+ymove;
+	if (TryMove (ob))
+		return;
+
+	if (noclip && ob->x > 2*TILEGLOBAL && ob->y > 2*TILEGLOBAL &&
+	ob->x < (((long)(mapwidth-1))<<TILESHIFT)
+	&& ob->y < (((long)(mapheight-1))<<TILESHIFT) )
+		return;		// walk through walls
+
+	if (!SD_SoundPlaying())
+		SD_PlaySound (HITWALLSND);
+
+	ob->x = basex+xmove;
+	ob->y = basey;
+	if (TryMove (ob))
+		return;
+
+	ob->x = basex;
+	ob->y = basey+ymove;
+	if (TryMove (ob))
+		return;
+
+	ob->x = basex;
+	ob->y = basey;
+}
+
+//==========================================================================
+
+/*
+===================
+=
+= VictoryTile
+=
+===================
+*/
+
+void VictoryTile (void)
+{
+#ifndef SPEAR
+	SpawnBJVictory ();
+#endif
+
+	gamestate.victoryflag = true;
+}
+
+
+/*
+===================
+=
+= Thrust
+=
+===================
+*/
+
+void Thrust (int angle, long speed)
+{
+	long xmove,ymove;
+	long	slowmax;
+	unsigned	offset;
+
+
+	//
+	// ZERO FUNNY COUNTER IF MOVED!
+	//
+	#ifdef SPEAR
+	if (speed)
+		funnyticount = 0;
+	#endif
+
+	thrustspeed += speed;
+//
+// moving bounds speed
+//
+	if (speed >= MINDIST*2)
+		speed = MINDIST*2-1;
+
+	xmove = FixedByFrac(speed,costable[angle]);
+	ymove = -FixedByFrac(speed,sintable[angle]);
+
+	ClipMove(player,xmove,ymove);
+
+	player->tilex = player->x >> TILESHIFT;		// scale to tile values
+	player->tiley = player->y >> TILESHIFT;
+
+	offset = farmapylookup[player->tiley]+player->tilex;
+	player->areanumber = *(mapsegs[0] + offset) -AREATILE;
+
+	if (*(mapsegs[1] + offset) == EXITTILE)
+		VictoryTile ();
+}
+
+
+/*
+=============================================================================
+
+								ACTIONS
+
+=============================================================================
+*/
+
+
+/*
+===============
+=
+= Cmd_Fire
+=
+===============
+*/
+
+void Cmd_Fire (void)
+{
+	buttonheld[bt_attack] = true;
+
+	gamestate.weaponframe = 0;
+
+	player->state = &s_attack;
+
+	gamestate.attackframe = 0;
+	gamestate.attackcount =
+		attackinfo[gamestate.weapon][gamestate.attackframe].tics;
+	gamestate.weaponframe =
+		attackinfo[gamestate.weapon][gamestate.attackframe].frame;
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= Cmd_Use
+=
+===============
+*/
+
+void Cmd_Use (void)
+{
+	objtype 	*check;
+	int			checkx,checky,doornum,dir;
+	boolean		elevatorok;
+
+
+//
+// find which cardinal direction the player is facing
+//
+	if (player->angle < ANGLES/8 || player->angle > 7*ANGLES/8)
+	{
+		checkx = player->tilex + 1;
+		checky = player->tiley;
+		dir = di_east;
+		elevatorok = true;
+	}
+	else if (player->angle < 3*ANGLES/8)
+	{
+		checkx = player->tilex;
+		checky = player->tiley-1;
+		dir = di_north;
+		elevatorok = false;
+	}
+	else if (player->angle < 5*ANGLES/8)
+	{
+		checkx = player->tilex - 1;
+		checky = player->tiley;
+		dir = di_west;
+		elevatorok = true;
+	}
+	else
+	{
+		checkx = player->tilex;
+		checky = player->tiley + 1;
+		dir = di_south;
+		elevatorok = false;
+	}
+
+	doornum = tilemap[checkx][checky];
+	if (*(mapsegs[1]+farmapylookup[checky]+checkx) == PUSHABLETILE)
+	{
+	//
+	// pushable wall
+	//
+
+		PushWall (checkx,checky,dir);
+		return;
+	}
+	if (!buttonheld[bt_use] && doornum == ELEVATORTILE && elevatorok)
+	{
+	//
+	// use elevator
+	//
+		buttonheld[bt_use] = true;
+
+		tilemap[checkx][checky]++;		// flip switch
+		if (*(mapsegs[0]+farmapylookup[player->tiley]+player->tilex) == ALTELEVATORTILE)
+			playstate = ex_secretlevel;
+		else
+			playstate = ex_completed;
+		SD_PlaySound (LEVELDONESND);
+		SD_WaitSoundDone();
+	}
+	else if (!buttonheld[bt_use] && doornum & 0x80)
+	{
+		buttonheld[bt_use] = true;
+		OperateDoor (doornum & ~0x80);
+	}
+	else
+		SD_PlaySound (DONOTHINGSND);
+
+}
+
+/*
+=============================================================================
+
+						   PLAYER CONTROL
+
+=============================================================================
+*/
+
+
+
+/*
+===============
+=
+= SpawnPlayer
+=
+===============
+*/
+
+void SpawnPlayer (int tilex, int tiley, int dir)
+{
+	player->obclass = playerobj;
+	player->active = true;
+	player->tilex = tilex;
+	player->tiley = tiley;
+	player->areanumber =
+		*(mapsegs[0] + farmapylookup[player->tiley]+player->tilex);
+	player->x = ((long)tilex<<TILESHIFT)+TILEGLOBAL/2;
+	player->y = ((long)tiley<<TILESHIFT)+TILEGLOBAL/2;
+	player->state = &s_player;
+	player->angle = (1-dir)*90;
+	if (player->angle<0)
+		player->angle += ANGLES;
+	player->flags = FL_NEVERMARK;
+	Thrust (0,0);				// set some variables
+
+	InitAreas ();
+}
+
+
+//===========================================================================
+
+/*
+===============
+=
+= T_KnifeAttack
+=
+= Update player hands, and try to do damage when the proper frame is reached
+=
+===============
+*/
+
+void	KnifeAttack (objtype *ob)
+{
+	objtype *check,*closest;
+	long	dist;
+
+	SD_PlaySound (ATKKNIFESND);
+// actually fire
+	dist = 0x7fffffff;
+	closest = NULL;
+	for (check=ob->next ; check ; check=check->next)
+		if ( (check->flags & FL_SHOOTABLE)
+		&& (check->flags & FL_VISABLE)
+		&& abs (check->viewx-centerx) < shootdelta
+		)
+		{
+			if (check->transx < dist)
+			{
+				dist = check->transx;
+				closest = check;
+			}
+		}
+
+	if (!closest || dist> 0x18000l)
+	{
+	// missed
+
+		return;
+	}
+
+// hit something
+	DamageActor (closest,US_RndT() >> 4);
+}
+
+
+
+void	GunAttack (objtype *ob)
+{
+	objtype *check,*closest,*oldclosest;
+	int		damage;
+	int		dx,dy,dist;
+	long	viewdist;
+
+	switch (gamestate.weapon)
+	{
+	case wp_pistol:
+		SD_PlaySound (ATKPISTOLSND);
+		break;
+	case wp_machinegun:
+		SD_PlaySound (ATKMACHINEGUNSND);
+		break;
+	case wp_chaingun:
+		SD_PlaySound (ATKGATLINGSND);
+		break;
+	}
+
+	madenoise = true;
+
+//
+// find potential targets
+//
+	viewdist = 0x7fffffffl;
+	closest = NULL;
+
+	while (1)
+	{
+		oldclosest = closest;
+
+		for (check=ob->next ; check ; check=check->next)
+			if ( (check->flags & FL_SHOOTABLE)
+			&& (check->flags & FL_VISABLE)
+			&& abs (check->viewx-centerx) < shootdelta
+			)
+			{
+				if (check->transx < viewdist)
+				{
+					viewdist = check->transx;
+					closest = check;
+				}
+			}
+
+		if (closest == oldclosest)
+			return;						// no more targets, all missed
+
+	//
+	// trace a line from player to enemey
+	//
+		if (CheckLine(closest))
+			break;
+
+	}
+
+//
+// hit something
+//
+	dx = abs(closest->tilex - player->tilex);
+	dy = abs(closest->tiley - player->tiley);
+	dist = dx>dy ? dx:dy;
+
+	if (dist<2)
+		damage = US_RndT() / 4;
+	else if (dist<4)
+		damage = US_RndT() / 6;
+	else
+	{
+		if ( (US_RndT() / 12) < dist)		// missed
+			return;
+		damage = US_RndT() / 6;
+	}
+
+	DamageActor (closest,damage);
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= VictorySpin
+=
+===============
+*/
+
+void VictorySpin (void)
+{
+	long	desty;
+
+	if (player->angle > 270)
+	{
+		player->angle -= tics * 3;
+		if (player->angle < 270)
+			player->angle = 270;
+	}
+	else if (player->angle < 270)
+	{
+		player->angle += tics * 3;
+		if (player->angle > 270)
+			player->angle = 270;
+	}
+
+	desty = (((long)player->tiley-5)<<TILESHIFT)-0x3000;
+
+	if (player->y > desty)
+	{
+		player->y -= tics*4096;
+		if (player->y < desty)
+			player->y = desty;
+	}
+}
+
+
+//===========================================================================
+
+/*
+===============
+=
+= T_Attack
+=
+===============
+*/
+
+void	T_Attack (objtype *ob)
+{
+	struct	atkinf	*cur;
+
+	UpdateFace ();
+
+	if (gamestate.victoryflag)		// watching the BJ actor
+	{
+		VictorySpin ();
+		return;
+	}
+
+	if ( buttonstate[bt_use] && !buttonheld[bt_use] )
+		buttonstate[bt_use] = false;
+
+	if ( buttonstate[bt_attack] && !buttonheld[bt_attack])
+		buttonstate[bt_attack] = false;
+
+	ControlMovement (ob);
+	if (gamestate.victoryflag)		// watching the BJ actor
+		return;
+
+	plux = player->x >> UNSIGNEDSHIFT;			// scale to fit in unsigned
+	pluy = player->y >> UNSIGNEDSHIFT;
+	player->tilex = player->x >> TILESHIFT;		// scale to tile values
+	player->tiley = player->y >> TILESHIFT;
+
+//
+// change frame and fire
+//
+	gamestate.attackcount -= tics;
+	while (gamestate.attackcount <= 0)
+	{
+		cur = &attackinfo[gamestate.weapon][gamestate.attackframe];
+		switch (cur->attack)
+		{
+		case -1:
+			ob->state = &s_player;
+			if (!gamestate.ammo)
+			{
+				gamestate.weapon = wp_knife;
+				DrawWeapon ();
+			}
+			else
+			{
+				if (gamestate.weapon != gamestate.chosenweapon)
+				{
+					gamestate.weapon = gamestate.chosenweapon;
+					DrawWeapon ();
+				}
+			};
+			gamestate.attackframe = gamestate.weaponframe = 0;
+			return;
+
+		case 4:
+			if (!gamestate.ammo)
+				break;
+			if (buttonstate[bt_attack])
+				gamestate.attackframe -= 2;
+		case 1:
+			if (!gamestate.ammo)
+			{	// can only happen with chain gun
+				gamestate.attackframe++;
+				break;
+			}
+			GunAttack (ob);
+			gamestate.ammo--;
+			DrawAmmo ();
+			break;
+
+		case 2:
+			KnifeAttack (ob);
+			break;
+
+		case 3:
+			if (gamestate.ammo && buttonstate[bt_attack])
+				gamestate.attackframe -= 2;
+			break;
+		}
+
+		gamestate.attackcount += cur->tics;
+		gamestate.attackframe++;
+		gamestate.weaponframe =
+			attackinfo[gamestate.weapon][gamestate.attackframe].frame;
+	}
+
+}
+
+
+
+//===========================================================================
+
+/*
+===============
+=
+= T_Player
+=
+===============
+*/
+
+void	T_Player (objtype *ob)
+{
+	if (gamestate.victoryflag)		// watching the BJ actor
+	{
+		VictorySpin ();
+		return;
+	}
+
+	UpdateFace ();
+	CheckWeaponChange ();
+
+	if ( buttonstate[bt_use] )
+		Cmd_Use ();
+
+	if ( buttonstate[bt_attack] && !buttonheld[bt_attack])
+		Cmd_Fire ();
+
+	ControlMovement (ob);
+	if (gamestate.victoryflag)		// watching the BJ actor
+		return;
+
+
+	plux = player->x >> UNSIGNEDSHIFT;			// scale to fit in unsigned
+	pluy = player->y >> UNSIGNEDSHIFT;
+	player->tilex = player->x >> TILESHIFT;		// scale to tile values
+	player->tiley = player->y >> TILESHIFT;
+}
+
+
diff --git a/src/lib/hb/wl_asm.asm b/src/lib/hb/wl_asm.asm
new file mode 100755
index 00000000..cca5d690
--- /dev/null
+++ b/src/lib/hb/wl_asm.asm
@@ -0,0 +1,69 @@
+; JABHACK.ASM
+
+.386C
+IDEAL
+MODEL	MEDIUM
+
+EXTRN	LDIV@:far
+
+;============================================================================
+
+DATASEG
+
+;============================================================================
+
+CODESEG
+
+;	Hacked up Juan Jimenez's code a bit to just return 386/not 386
+PROC	_CheckIs386
+PUBLIC	_CheckIs386
+
+;hack to never look for a 386, for benchmark comparisons of same code on all CPUs
+;	pushf			; Save flag registers, we use them here
+;	xor	ax,ax		; Clear AX and...
+;	push ax			; ...push it onto the stack
+;	popf			; Pop 0 into flag registers (all bits to 0),
+;	pushf			; attempting to set bits 12-15 of flags to 0's
+;	pop	ax			; Recover the save flags
+;	and	ax,08000h	; If bits 12-15 of flags are set to
+;	cmp	ax,08000h	; zero then it's 8088/86 or 80188/186
+;	jz	not386
+;
+;	mov	ax,07000h	; Try to set flag bits 12-14 to 1's
+;	push ax			; Push the test value onto the stack
+;	popf			; Pop it into the flag register
+;	pushf			; Push it back onto the stack
+;	pop	ax			; Pop it into AX for check
+;	and	ax,07000h	; if bits 12-14 are cleared then
+;	jz	not386		; the chip is an 80286
+;
+;	mov	ax,1		; We now assume it's a 80386 or better
+;	popf
+;	retf
+;end benchmark hack
+
+not386:
+	xor	ax,ax
+	popf
+	retf
+
+	ENDP
+
+
+PROC	_jabhack2
+PUBLIC	_jabhack2
+
+	push	es
+
+	mov	ax,seg LDIV@
+	mov	es,ax
+	mov	ax,9090h					;Two NOP's
+	mov	[WORD FAR es:LDIV@],ax		;Patch over XOR AX,AX
+	mov	[WORD FAR es:LDIV@+2],ax	;and over JMP SHORT COMMON
+
+	pop	es
+	retf
+
+	ENDP
+
+	END
diff --git a/src/lib/wl_debug.c b/src/lib/hb/wl_debug.c
similarity index 100%
rename from src/lib/wl_debug.c
rename to src/lib/hb/wl_debug.c
diff --git a/src/lib/hb/wl_def.h b/src/lib/hb/wl_def.h
new file mode 100755
index 00000000..d1bc0802
--- /dev/null
+++ b/src/lib/hb/wl_def.h
@@ -0,0 +1,1276 @@
+//#define BETA
+#define YEAR	1992
+#define MONTH	9
+#define DAY		30
+
+#include "ID_HEADS.H"
+#include <MATH.H>
+#include <VALUES.H>
+
+#include "WL_MENU.H"
+
+#ifdef SPANISH
+#include "SPANISH.H"
+#else
+#include "FOREIGN.H"
+#endif
+
+#ifdef SPEAR
+#include "F_SPEAR.H"
+#endif
+
+/*
+=============================================================================
+
+							MACROS
+
+=============================================================================
+*/
+
+
+#define COLORBORDER(color)		asm{mov	dx,STATUS_REGISTER_1;in al,dx;\
+	mov dx,ATR_INDEX;mov al,ATR_OVERSCAN;out dx,al;mov al,color;out	dx,al;\
+	mov	al,32;out dx,al};
+
+#define MAPSPOT(x,y,plane)		(*(mapsegs[plane]+farmapylookup[y]+x))
+
+#define SIGN(x) 	((x)>0?1:-1)
+#define ABS(x) 		((int)(x)>0?(x):-(x))
+#define LABS(x) 	((long)(x)>0?(x):-(x))
+
+/*
+=============================================================================
+
+						 GLOBAL CONSTANTS
+
+=============================================================================
+*/
+
+#define MAXACTORS		150				// max number of nazis, etc / map
+#define MAXSTATS		400				// max number of lamps, bonus, etc
+#define MAXDOORS		64				// max number of sliding doors
+#define MAXWALLTILES	64				// max number of wall tiles
+
+//
+// tile constants
+//
+
+#define	ICONARROWS		90
+#define PUSHABLETILE	98
+#define EXITTILE		99				// at end of castle
+#define AREATILE		107				// first of NUMAREAS floor tiles
+#define NUMAREAS		37
+#define ELEVATORTILE	21
+#define AMBUSHTILE		106
+#define	ALTELEVATORTILE	107
+
+#define NUMBERCHARS	9
+
+
+//----------------
+
+#define EXTRAPOINTS		40000
+
+#define PLAYERSPEED		3000
+#define RUNSPEED   		6000
+
+#define	SCREENSEG		0xa000
+
+#define SCREENBWIDE		80
+
+#define HEIGHTRATIO		0.50		// also defined in id_mm.c
+
+#define BORDERCOLOR	3
+#define FLASHCOLOR	5
+#define FLASHTICS	4
+
+
+#define PLAYERSIZE		MINDIST			// player radius
+#define MINACTORDIST	0x10000l		// minimum dist from player center
+										// to any actor center
+
+#define NUMLATCHPICS	100
+
+
+#define PI	3.141592657
+
+#define GLOBAL1		(1l<<16)
+#define TILEGLOBAL  GLOBAL1
+#define PIXGLOBAL	(GLOBAL1/64)
+#define TILESHIFT		16l
+#define UNSIGNEDSHIFT	8
+
+#define ANGLES		360					// must be divisable by 4
+#define ANGLEQUAD	(ANGLES/4)
+#define FINEANGLES	3600
+#define ANG90		(FINEANGLES/4)
+#define ANG180		(ANG90*2)
+#define ANG270		(ANG90*3)
+#define ANG360		(ANG90*4)
+#define VANG90		(ANGLES/4)
+#define VANG180		(VANG90*2)
+#define VANG270		(VANG90*3)
+#define VANG360		(VANG90*4)
+
+#define MINDIST		(0x5800l)
+
+
+#define	MAXSCALEHEIGHT	256				// largest scale on largest view
+
+#define MAXVIEWWIDTH		320
+
+#define MAPSIZE		64					// maps are 64*64 max
+#define NORTH	0
+#define EAST	1
+#define SOUTH	2
+#define WEST	3
+
+
+#define STATUSLINES		40
+
+#define SCREENSIZE		(SCREENBWIDE*208)
+#define PAGE1START		0
+#define PAGE2START		(SCREENSIZE)
+#define PAGE3START		(SCREENSIZE*2u)
+#define	FREESTART		(SCREENSIZE*3u)
+
+
+#define PIXRADIUS		512
+
+#define STARTAMMO		8
+
+
+// object flag values
+
+#define FL_SHOOTABLE	1
+#define FL_BONUS		2
+#define FL_NEVERMARK	4
+#define FL_VISABLE		8
+#define FL_ATTACKMODE	16
+#define FL_FIRSTATTACK	32
+#define FL_AMBUSH		64
+#define FL_NONMARK		128
+
+
+//
+// sprite constants
+//
+
+enum	{
+		SPR_DEMO,
+		SPR_DEATHCAM,
+//
+// static sprites
+//
+		SPR_STAT_0,SPR_STAT_1,SPR_STAT_2,SPR_STAT_3,
+		SPR_STAT_4,SPR_STAT_5,SPR_STAT_6,SPR_STAT_7,
+
+		SPR_STAT_8,SPR_STAT_9,SPR_STAT_10,SPR_STAT_11,
+		SPR_STAT_12,SPR_STAT_13,SPR_STAT_14,SPR_STAT_15,
+
+		SPR_STAT_16,SPR_STAT_17,SPR_STAT_18,SPR_STAT_19,
+		SPR_STAT_20,SPR_STAT_21,SPR_STAT_22,SPR_STAT_23,
+
+		SPR_STAT_24,SPR_STAT_25,SPR_STAT_26,SPR_STAT_27,
+		SPR_STAT_28,SPR_STAT_29,SPR_STAT_30,SPR_STAT_31,
+
+		SPR_STAT_32,SPR_STAT_33,SPR_STAT_34,SPR_STAT_35,
+		SPR_STAT_36,SPR_STAT_37,SPR_STAT_38,SPR_STAT_39,
+
+		SPR_STAT_40,SPR_STAT_41,SPR_STAT_42,SPR_STAT_43,
+		SPR_STAT_44,SPR_STAT_45,SPR_STAT_46,SPR_STAT_47,
+
+#ifdef SPEAR
+		SPR_STAT_48,SPR_STAT_49,SPR_STAT_50,SPR_STAT_51,
+#endif
+
+//
+// guard
+//
+		SPR_GRD_S_1,SPR_GRD_S_2,SPR_GRD_S_3,SPR_GRD_S_4,
+		SPR_GRD_S_5,SPR_GRD_S_6,SPR_GRD_S_7,SPR_GRD_S_8,
+
+		SPR_GRD_W1_1,SPR_GRD_W1_2,SPR_GRD_W1_3,SPR_GRD_W1_4,
+		SPR_GRD_W1_5,SPR_GRD_W1_6,SPR_GRD_W1_7,SPR_GRD_W1_8,
+
+		SPR_GRD_W2_1,SPR_GRD_W2_2,SPR_GRD_W2_3,SPR_GRD_W2_4,
+		SPR_GRD_W2_5,SPR_GRD_W2_6,SPR_GRD_W2_7,SPR_GRD_W2_8,
+
+		SPR_GRD_W3_1,SPR_GRD_W3_2,SPR_GRD_W3_3,SPR_GRD_W3_4,
+		SPR_GRD_W3_5,SPR_GRD_W3_6,SPR_GRD_W3_7,SPR_GRD_W3_8,
+
+		SPR_GRD_W4_1,SPR_GRD_W4_2,SPR_GRD_W4_3,SPR_GRD_W4_4,
+		SPR_GRD_W4_5,SPR_GRD_W4_6,SPR_GRD_W4_7,SPR_GRD_W4_8,
+
+		SPR_GRD_PAIN_1,SPR_GRD_DIE_1,SPR_GRD_DIE_2,SPR_GRD_DIE_3,
+		SPR_GRD_PAIN_2,SPR_GRD_DEAD,
+
+		SPR_GRD_SHOOT1,SPR_GRD_SHOOT2,SPR_GRD_SHOOT3,
+
+//
+// dogs
+//
+		SPR_DOG_W1_1,SPR_DOG_W1_2,SPR_DOG_W1_3,SPR_DOG_W1_4,
+		SPR_DOG_W1_5,SPR_DOG_W1_6,SPR_DOG_W1_7,SPR_DOG_W1_8,
+
+		SPR_DOG_W2_1,SPR_DOG_W2_2,SPR_DOG_W2_3,SPR_DOG_W2_4,
+		SPR_DOG_W2_5,SPR_DOG_W2_6,SPR_DOG_W2_7,SPR_DOG_W2_8,
+
+		SPR_DOG_W3_1,SPR_DOG_W3_2,SPR_DOG_W3_3,SPR_DOG_W3_4,
+		SPR_DOG_W3_5,SPR_DOG_W3_6,SPR_DOG_W3_7,SPR_DOG_W3_8,
+
+		SPR_DOG_W4_1,SPR_DOG_W4_2,SPR_DOG_W4_3,SPR_DOG_W4_4,
+		SPR_DOG_W4_5,SPR_DOG_W4_6,SPR_DOG_W4_7,SPR_DOG_W4_8,
+
+		SPR_DOG_DIE_1,SPR_DOG_DIE_2,SPR_DOG_DIE_3,SPR_DOG_DEAD,
+		SPR_DOG_JUMP1,SPR_DOG_JUMP2,SPR_DOG_JUMP3,
+
+
+
+//
+// ss
+//
+		SPR_SS_S_1,SPR_SS_S_2,SPR_SS_S_3,SPR_SS_S_4,
+		SPR_SS_S_5,SPR_SS_S_6,SPR_SS_S_7,SPR_SS_S_8,
+
+		SPR_SS_W1_1,SPR_SS_W1_2,SPR_SS_W1_3,SPR_SS_W1_4,
+		SPR_SS_W1_5,SPR_SS_W1_6,SPR_SS_W1_7,SPR_SS_W1_8,
+
+		SPR_SS_W2_1,SPR_SS_W2_2,SPR_SS_W2_3,SPR_SS_W2_4,
+		SPR_SS_W2_5,SPR_SS_W2_6,SPR_SS_W2_7,SPR_SS_W2_8,
+
+		SPR_SS_W3_1,SPR_SS_W3_2,SPR_SS_W3_3,SPR_SS_W3_4,
+		SPR_SS_W3_5,SPR_SS_W3_6,SPR_SS_W3_7,SPR_SS_W3_8,
+
+		SPR_SS_W4_1,SPR_SS_W4_2,SPR_SS_W4_3,SPR_SS_W4_4,
+		SPR_SS_W4_5,SPR_SS_W4_6,SPR_SS_W4_7,SPR_SS_W4_8,
+
+		SPR_SS_PAIN_1,SPR_SS_DIE_1,SPR_SS_DIE_2,SPR_SS_DIE_3,
+		SPR_SS_PAIN_2,SPR_SS_DEAD,
+
+		SPR_SS_SHOOT1,SPR_SS_SHOOT2,SPR_SS_SHOOT3,
+
+//
+// mutant
+//
+		SPR_MUT_S_1,SPR_MUT_S_2,SPR_MUT_S_3,SPR_MUT_S_4,
+		SPR_MUT_S_5,SPR_MUT_S_6,SPR_MUT_S_7,SPR_MUT_S_8,
+
+		SPR_MUT_W1_1,SPR_MUT_W1_2,SPR_MUT_W1_3,SPR_MUT_W1_4,
+		SPR_MUT_W1_5,SPR_MUT_W1_6,SPR_MUT_W1_7,SPR_MUT_W1_8,
+
+		SPR_MUT_W2_1,SPR_MUT_W2_2,SPR_MUT_W2_3,SPR_MUT_W2_4,
+		SPR_MUT_W2_5,SPR_MUT_W2_6,SPR_MUT_W2_7,SPR_MUT_W2_8,
+
+		SPR_MUT_W3_1,SPR_MUT_W3_2,SPR_MUT_W3_3,SPR_MUT_W3_4,
+		SPR_MUT_W3_5,SPR_MUT_W3_6,SPR_MUT_W3_7,SPR_MUT_W3_8,
+
+		SPR_MUT_W4_1,SPR_MUT_W4_2,SPR_MUT_W4_3,SPR_MUT_W4_4,
+		SPR_MUT_W4_5,SPR_MUT_W4_6,SPR_MUT_W4_7,SPR_MUT_W4_8,
+
+		SPR_MUT_PAIN_1,SPR_MUT_DIE_1,SPR_MUT_DIE_2,SPR_MUT_DIE_3,
+		SPR_MUT_PAIN_2,SPR_MUT_DIE_4,SPR_MUT_DEAD,
+
+		SPR_MUT_SHOOT1,SPR_MUT_SHOOT2,SPR_MUT_SHOOT3,SPR_MUT_SHOOT4,
+
+//
+// officer
+//
+		SPR_OFC_S_1,SPR_OFC_S_2,SPR_OFC_S_3,SPR_OFC_S_4,
+		SPR_OFC_S_5,SPR_OFC_S_6,SPR_OFC_S_7,SPR_OFC_S_8,
+
+		SPR_OFC_W1_1,SPR_OFC_W1_2,SPR_OFC_W1_3,SPR_OFC_W1_4,
+		SPR_OFC_W1_5,SPR_OFC_W1_6,SPR_OFC_W1_7,SPR_OFC_W1_8,
+
+		SPR_OFC_W2_1,SPR_OFC_W2_2,SPR_OFC_W2_3,SPR_OFC_W2_4,
+		SPR_OFC_W2_5,SPR_OFC_W2_6,SPR_OFC_W2_7,SPR_OFC_W2_8,
+
+		SPR_OFC_W3_1,SPR_OFC_W3_2,SPR_OFC_W3_3,SPR_OFC_W3_4,
+		SPR_OFC_W3_5,SPR_OFC_W3_6,SPR_OFC_W3_7,SPR_OFC_W3_8,
+
+		SPR_OFC_W4_1,SPR_OFC_W4_2,SPR_OFC_W4_3,SPR_OFC_W4_4,
+		SPR_OFC_W4_5,SPR_OFC_W4_6,SPR_OFC_W4_7,SPR_OFC_W4_8,
+
+		SPR_OFC_PAIN_1,SPR_OFC_DIE_1,SPR_OFC_DIE_2,SPR_OFC_DIE_3,
+		SPR_OFC_PAIN_2,SPR_OFC_DIE_4,SPR_OFC_DEAD,
+
+		SPR_OFC_SHOOT1,SPR_OFC_SHOOT2,SPR_OFC_SHOOT3,
+
+#ifndef SPEAR
+//
+// ghosts
+//
+		SPR_BLINKY_W1,SPR_BLINKY_W2,SPR_PINKY_W1,SPR_PINKY_W2,
+		SPR_CLYDE_W1,SPR_CLYDE_W2,SPR_INKY_W1,SPR_INKY_W2,
+
+//
+// hans
+//
+		SPR_BOSS_W1,SPR_BOSS_W2,SPR_BOSS_W3,SPR_BOSS_W4,
+		SPR_BOSS_SHOOT1,SPR_BOSS_SHOOT2,SPR_BOSS_SHOOT3,SPR_BOSS_DEAD,
+
+		SPR_BOSS_DIE1,SPR_BOSS_DIE2,SPR_BOSS_DIE3,
+
+//
+// schabbs
+//
+		SPR_SCHABB_W1,SPR_SCHABB_W2,SPR_SCHABB_W3,SPR_SCHABB_W4,
+		SPR_SCHABB_SHOOT1,SPR_SCHABB_SHOOT2,
+
+		SPR_SCHABB_DIE1,SPR_SCHABB_DIE2,SPR_SCHABB_DIE3,SPR_SCHABB_DEAD,
+		SPR_HYPO1,SPR_HYPO2,SPR_HYPO3,SPR_HYPO4,
+
+//
+// fake
+//
+		SPR_FAKE_W1,SPR_FAKE_W2,SPR_FAKE_W3,SPR_FAKE_W4,
+		SPR_FAKE_SHOOT,SPR_FIRE1,SPR_FIRE2,
+
+		SPR_FAKE_DIE1,SPR_FAKE_DIE2,SPR_FAKE_DIE3,SPR_FAKE_DIE4,
+		SPR_FAKE_DIE5,SPR_FAKE_DEAD,
+
+//
+// hitler
+//
+		SPR_MECHA_W1,SPR_MECHA_W2,SPR_MECHA_W3,SPR_MECHA_W4,
+		SPR_MECHA_SHOOT1,SPR_MECHA_SHOOT2,SPR_MECHA_SHOOT3,SPR_MECHA_DEAD,
+
+		SPR_MECHA_DIE1,SPR_MECHA_DIE2,SPR_MECHA_DIE3,
+
+		SPR_HITLER_W1,SPR_HITLER_W2,SPR_HITLER_W3,SPR_HITLER_W4,
+		SPR_HITLER_SHOOT1,SPR_HITLER_SHOOT2,SPR_HITLER_SHOOT3,SPR_HITLER_DEAD,
+
+		SPR_HITLER_DIE1,SPR_HITLER_DIE2,SPR_HITLER_DIE3,SPR_HITLER_DIE4,
+		SPR_HITLER_DIE5,SPR_HITLER_DIE6,SPR_HITLER_DIE7,
+
+//
+// giftmacher
+//
+		SPR_GIFT_W1,SPR_GIFT_W2,SPR_GIFT_W3,SPR_GIFT_W4,
+		SPR_GIFT_SHOOT1,SPR_GIFT_SHOOT2,
+
+		SPR_GIFT_DIE1,SPR_GIFT_DIE2,SPR_GIFT_DIE3,SPR_GIFT_DEAD,
+#endif
+//
+// Rocket, smoke and small explosion
+//
+		SPR_ROCKET_1,SPR_ROCKET_2,SPR_ROCKET_3,SPR_ROCKET_4,
+		SPR_ROCKET_5,SPR_ROCKET_6,SPR_ROCKET_7,SPR_ROCKET_8,
+
+		SPR_SMOKE_1,SPR_SMOKE_2,SPR_SMOKE_3,SPR_SMOKE_4,
+		SPR_BOOM_1,SPR_BOOM_2,SPR_BOOM_3,
+
+//
+// Angel of Death's DeathSparks(tm)
+//
+#ifdef SPEAR
+		SPR_HROCKET_1,SPR_HROCKET_2,SPR_HROCKET_3,SPR_HROCKET_4,
+		SPR_HROCKET_5,SPR_HROCKET_6,SPR_HROCKET_7,SPR_HROCKET_8,
+
+		SPR_HSMOKE_1,SPR_HSMOKE_2,SPR_HSMOKE_3,SPR_HSMOKE_4,
+		SPR_HBOOM_1,SPR_HBOOM_2,SPR_HBOOM_3,
+
+		SPR_SPARK1,SPR_SPARK2,SPR_SPARK3,SPR_SPARK4,
+#endif
+
+#ifndef SPEAR
+//
+// gretel
+//
+		SPR_GRETEL_W1,SPR_GRETEL_W2,SPR_GRETEL_W3,SPR_GRETEL_W4,
+		SPR_GRETEL_SHOOT1,SPR_GRETEL_SHOOT2,SPR_GRETEL_SHOOT3,SPR_GRETEL_DEAD,
+
+		SPR_GRETEL_DIE1,SPR_GRETEL_DIE2,SPR_GRETEL_DIE3,
+
+//
+// fat face
+//
+		SPR_FAT_W1,SPR_FAT_W2,SPR_FAT_W3,SPR_FAT_W4,
+		SPR_FAT_SHOOT1,SPR_FAT_SHOOT2,SPR_FAT_SHOOT3,SPR_FAT_SHOOT4,
+
+		SPR_FAT_DIE1,SPR_FAT_DIE2,SPR_FAT_DIE3,SPR_FAT_DEAD,
+
+//
+// bj
+//
+		SPR_BJ_W1,SPR_BJ_W2,SPR_BJ_W3,SPR_BJ_W4,
+		SPR_BJ_JUMP1,SPR_BJ_JUMP2,SPR_BJ_JUMP3,SPR_BJ_JUMP4,
+#else
+//
+// THESE ARE FOR 'SPEAR OF DESTINY'
+//
+
+//
+// Trans Grosse
+//
+		SPR_TRANS_W1,SPR_TRANS_W2,SPR_TRANS_W3,SPR_TRANS_W4,
+		SPR_TRANS_SHOOT1,SPR_TRANS_SHOOT2,SPR_TRANS_SHOOT3,SPR_TRANS_DEAD,
+
+		SPR_TRANS_DIE1,SPR_TRANS_DIE2,SPR_TRANS_DIE3,
+
+//
+// Wilhelm
+//
+		SPR_WILL_W1,SPR_WILL_W2,SPR_WILL_W3,SPR_WILL_W4,
+		SPR_WILL_SHOOT1,SPR_WILL_SHOOT2,SPR_WILL_SHOOT3,SPR_WILL_SHOOT4,
+
+		SPR_WILL_DIE1,SPR_WILL_DIE2,SPR_WILL_DIE3,SPR_WILL_DEAD,
+
+//
+// UberMutant
+//
+		SPR_UBER_W1,SPR_UBER_W2,SPR_UBER_W3,SPR_UBER_W4,
+		SPR_UBER_SHOOT1,SPR_UBER_SHOOT2,SPR_UBER_SHOOT3,SPR_UBER_SHOOT4,
+
+		SPR_UBER_DIE1,SPR_UBER_DIE2,SPR_UBER_DIE3,SPR_UBER_DIE4,
+		SPR_UBER_DEAD,
+
+//
+// Death Knight
+//
+		SPR_DEATH_W1,SPR_DEATH_W2,SPR_DEATH_W3,SPR_DEATH_W4,
+		SPR_DEATH_SHOOT1,SPR_DEATH_SHOOT2,SPR_DEATH_SHOOT3,SPR_DEATH_SHOOT4,
+
+		SPR_DEATH_DIE1,SPR_DEATH_DIE2,SPR_DEATH_DIE3,SPR_DEATH_DIE4,
+		SPR_DEATH_DIE5,SPR_DEATH_DIE6,SPR_DEATH_DEAD,
+
+//
+// Ghost
+//
+		SPR_SPECTRE_W1,SPR_SPECTRE_W2,SPR_SPECTRE_W3,SPR_SPECTRE_W4,
+		SPR_SPECTRE_F1,SPR_SPECTRE_F2,SPR_SPECTRE_F3,SPR_SPECTRE_F4,
+
+//
+// Angel of Death
+//
+		SPR_ANGEL_W1,SPR_ANGEL_W2,SPR_ANGEL_W3,SPR_ANGEL_W4,
+		SPR_ANGEL_SHOOT1,SPR_ANGEL_SHOOT2,SPR_ANGEL_TIRED1,SPR_ANGEL_TIRED2,
+
+		SPR_ANGEL_DIE1,SPR_ANGEL_DIE2,SPR_ANGEL_DIE3,SPR_ANGEL_DIE4,
+		SPR_ANGEL_DIE5,SPR_ANGEL_DIE6,SPR_ANGEL_DIE7,SPR_ANGEL_DEAD,
+
+#endif
+
+//
+// player attack frames
+//
+		SPR_KNIFEREADY,SPR_KNIFEATK1,SPR_KNIFEATK2,SPR_KNIFEATK3,
+		SPR_KNIFEATK4,
+
+		SPR_PISTOLREADY,SPR_PISTOLATK1,SPR_PISTOLATK2,SPR_PISTOLATK3,
+		SPR_PISTOLATK4,
+
+		SPR_MACHINEGUNREADY,SPR_MACHINEGUNATK1,SPR_MACHINEGUNATK2,MACHINEGUNATK3,
+		SPR_MACHINEGUNATK4,
+
+		SPR_CHAINREADY,SPR_CHAINATK1,SPR_CHAINATK2,SPR_CHAINATK3,
+		SPR_CHAINATK4,
+
+		};
+
+
+/*
+=============================================================================
+
+						   GLOBAL TYPES
+
+=============================================================================
+*/
+
+typedef long fixed;
+
+typedef enum {
+	di_north,
+	di_east,
+	di_south,
+	di_west
+} controldir_t;
+
+typedef enum {
+	dr_normal,
+	dr_lock1,
+	dr_lock2,
+	dr_lock3,
+	dr_lock4,
+	dr_elevator
+} door_t;
+
+typedef enum {
+	ac_badobject = -1,
+	ac_no,
+	ac_yes,
+	ac_allways
+} activetype;
+
+typedef enum {
+	nothing,
+	playerobj,
+	inertobj,
+	guardobj,
+	officerobj,
+	ssobj,
+	dogobj,
+	bossobj,
+	schabbobj,
+	fakeobj,
+	mechahitlerobj,
+	mutantobj,
+	needleobj,
+	fireobj,
+	bjobj,
+	ghostobj,
+	realhitlerobj,
+	gretelobj,
+	giftobj,
+	fatobj,
+	rocketobj,
+
+	spectreobj,
+	angelobj,
+	transobj,
+	uberobj,
+	willobj,
+	deathobj,
+	hrocketobj,
+	sparkobj
+} classtype;
+
+typedef enum {
+	dressing,
+	block,
+	bo_gibs,
+	bo_alpo,
+	bo_firstaid,
+	bo_key1,
+	bo_key2,
+	bo_key3,
+	bo_key4,
+	bo_cross,
+	bo_chalice,
+	bo_bible,
+	bo_crown,
+	bo_clip,
+	bo_clip2,
+	bo_machinegun,
+	bo_chaingun,
+	bo_food,
+	bo_fullheal,
+	bo_25clip,
+	bo_spear
+} stat_t;
+
+typedef enum {
+	east,
+	northeast,
+	north,
+	northwest,
+	west,
+	southwest,
+	south,
+	southeast,
+	nodir
+} dirtype;
+
+
+#define NUMENEMIES		22
+typedef enum {
+	en_guard,
+	en_officer,
+	en_ss,
+	en_dog,
+	en_boss,
+	en_schabbs,
+	en_fake,
+	en_hitler,
+	en_mutant,
+	en_blinky,
+	en_clyde,
+	en_pinky,
+	en_inky,
+	en_gretel,
+	en_gift,
+	en_fat,
+	en_spectre,
+	en_angel,
+	en_trans,
+	en_uber,
+	en_will,
+	en_death
+} enemy_t;
+
+
+typedef struct	statestruct
+{
+	boolean	rotate;
+	int		shapenum;			// a shapenum of -1 means get from ob->temp1
+	int		tictime;
+	void	(*think) (),(*action) ();
+	struct	statestruct	*next;
+} statetype;
+
+
+//---------------------
+//
+// trivial actor structure
+//
+//---------------------
+
+typedef struct statstruct
+{
+	byte	tilex,tiley;
+	byte	*visspot;
+	int		shapenum;			// if shapenum == -1 the obj has been removed
+	byte	flags;
+	byte	itemnumber;
+} statobj_t;
+
+
+//---------------------
+//
+// door actor structure
+//
+//---------------------
+
+typedef struct doorstruct
+{
+	byte	tilex,tiley;
+	boolean	vertical;
+	byte	lock;
+	enum	{dr_open,dr_closed,dr_opening,dr_closing}	action;
+	int		ticcount;
+} doorobj_t;
+
+
+//--------------------
+//
+// thinking actor structure
+//
+//--------------------
+
+typedef struct objstruct
+{
+	activetype	active;
+	int			ticcount;
+	classtype	obclass;
+	statetype	*state;
+
+	byte		flags;				//	FL_SHOOTABLE, etc
+
+	long		distance;			// if negative, wait for that door to open
+	dirtype		dir;
+
+	fixed 		x,y;
+	unsigned	tilex,tiley;
+	byte		areanumber;
+
+	int	 		viewx;
+	unsigned	viewheight;
+	fixed		transx,transy;		// in global coord
+
+	int 		angle;
+	int			hitpoints;
+	long		speed;
+
+	int			temp1,temp2,temp3;
+	struct		objstruct	*next,*prev;
+} objtype;
+
+
+#define NUMBUTTONS	8
+enum	{
+	bt_nobutton=-1,
+	bt_attack=0,
+	bt_strafe,
+	bt_run,
+	bt_use,
+	bt_readyknife,
+	bt_readypistol,
+	bt_readymachinegun,
+	bt_readychaingun
+};
+
+
+#define NUMWEAPONS	5
+typedef enum	{
+	wp_knife,
+	wp_pistol,
+	wp_machinegun,
+	wp_chaingun
+} weapontype;
+
+
+typedef enum	{
+	gd_baby,
+	gd_easy,
+	gd_medium,
+	gd_hard
+};
+
+//---------------
+//
+// gamestate structure
+//
+//---------------
+
+typedef	struct
+{
+	int			difficulty;
+	int			mapon;
+	long		oldscore,score,nextextra;
+	int			lives;
+	int			health;
+	int			ammo;
+	int			keys;
+	weapontype		bestweapon,weapon,chosenweapon;
+
+	int			faceframe;
+	int			attackframe,attackcount,weaponframe;
+
+	int			episode,secretcount,treasurecount,killcount,
+				secrettotal,treasuretotal,killtotal;
+	long		TimeCount;
+	long		killx,killy;
+	boolean		victoryflag;		// set during victory animations
+} gametype;
+
+
+typedef	enum	{
+	ex_stillplaying,
+	ex_completed,
+	ex_died,
+	ex_warped,
+	ex_resetgame,
+	ex_loadedgame,
+	ex_victorious,
+	ex_abort,
+	ex_demodone,
+	ex_secretlevel
+} exit_t;
+
+
+/*
+=============================================================================
+
+						 WL_MAIN DEFINITIONS
+
+=============================================================================
+*/
+
+extern	boolean		MS_CheckParm (char far *string);
+
+extern	char		str[80],str2[20];
+extern	int			tedlevelnum;
+extern	boolean		tedlevel;
+extern	boolean		nospr;
+extern	boolean		IsA386;
+
+extern	byte far	*scalermemory;
+
+extern	fixed		focallength;
+extern	unsigned	viewangles;
+extern	unsigned	screenofs;
+extern	int		    viewwidth;
+extern	int			viewheight;
+extern	int			centerx;
+extern	int			shootdelta;
+
+extern	int			dirangle[9];
+
+extern	boolean         startgame,loadedgame,virtualreality;
+extern	int		mouseadjustment;
+//
+// math tables
+//
+extern	int			pixelangle[MAXVIEWWIDTH];
+extern	long		far finetangent[FINEANGLES/4];
+extern	fixed 		far sintable[],far *costable;
+
+//
+// derived constants
+//
+extern	fixed 	scale,maxslope;
+extern	long	heightnumerator;
+extern	int		minheightdiv;
+
+extern	char	configname[13];
+
+
+
+void		HelpScreens (void);
+void		OrderingInfo (void);
+void		TEDDeath(void);
+void		Quit (char *error);
+void 		CalcProjection (long focal);
+boolean		SetViewSize (unsigned width, unsigned height);
+void		NewGame (int difficulty,int episode);
+void 		NewViewSize (int width);
+boolean 	LoadTheGame(int file,int x,int y);
+boolean		SaveTheGame(int file,int x,int y);
+void 		ShowViewSize (int width);
+void		ShutdownId (void);
+
+
+/*
+=============================================================================
+
+						 WL_GAME DEFINITIONS
+
+=============================================================================
+*/
+
+
+extern	boolean		ingame,fizzlein;
+extern	unsigned	latchpics[NUMLATCHPICS];
+extern	gametype	gamestate;
+extern	int			doornum;
+
+extern	char		demoname[13];
+
+extern	long		spearx,speary;
+extern	unsigned	spearangle;
+extern	boolean		spearflag;
+
+
+void 	DrawPlayBorder (void);
+void 	ScanInfoPlane (void);
+void	SetupGameLevel (void);
+void 	NormalScreen (void);
+void 	DrawPlayScreen (void);
+void 	FizzleOut (void);
+void 	GameLoop (void);
+void ClearMemory (void);
+void PlayDemo (int demonumber);
+void RecordDemo (void);
+void DrawAllPlayBorder (void);
+void	DrawHighScores(void);
+void DrawAllPlayBorderSides (void);
+
+
+// JAB
+#define	PlaySoundLocTile(s,tx,ty)	PlaySoundLocGlobal(s,(((long)(tx) << TILESHIFT) + (1L << (TILESHIFT - 1))),(((long)ty << TILESHIFT) + (1L << (TILESHIFT - 1))))
+#define	PlaySoundLocActor(s,ob)		PlaySoundLocGlobal(s,(ob)->x,(ob)->y)
+void	PlaySoundLocGlobal(word s,fixed gx,fixed gy);
+void UpdateSoundLoc(void);
+
+
+/*
+=============================================================================
+
+						 WL_PLAY DEFINITIONS
+
+=============================================================================
+*/
+
+#ifdef SPEAR
+extern	long		funnyticount;		// FOR FUNNY BJ FACE
+#endif
+
+extern	exit_t		playstate;
+
+extern	boolean		madenoise;
+
+extern	objtype 	objlist[MAXACTORS],*new,*obj,*player,*lastobj,
+					*objfreelist,*killerobj;
+extern	statobj_t	statobjlist[MAXSTATS],*laststatobj;
+extern	doorobj_t	doorobjlist[MAXDOORS],*lastdoorobj;
+
+extern	unsigned	farmapylookup[MAPSIZE];
+extern	byte		*nearmapylookup[MAPSIZE];
+
+extern	byte		tilemap[MAPSIZE][MAPSIZE];	// wall values only
+extern	byte		spotvis[MAPSIZE][MAPSIZE];
+extern	objtype		*actorat[MAPSIZE][MAPSIZE];
+
+#define UPDATESIZE			(UPDATEWIDE*UPDATEHIGH)
+extern	byte		update[UPDATESIZE];
+
+extern	boolean		singlestep,godmode,noclip;
+extern	int			extravbls;
+
+//
+// control info
+//
+extern	boolean		mouseenabled,joystickenabled,joypadenabled,joystickprogressive;
+extern	int			joystickport;
+extern	int			dirscan[4];
+extern	int			buttonscan[NUMBUTTONS];
+extern	int			buttonmouse[4];
+extern	int			buttonjoy[4];
+
+extern	boolean		buttonheld[NUMBUTTONS];
+
+extern	int			viewsize;
+
+//
+// curent user input
+//
+extern	int			controlx,controly;		// range from -100 to 100
+extern	boolean		buttonstate[NUMBUTTONS];
+
+extern	boolean		demorecord,demoplayback;
+extern	char		far *demoptr, far *lastdemoptr;
+extern	memptr		demobuffer;
+
+
+
+void	InitRedShifts (void);
+void 	FinishPaletteShifts (void);
+
+void	CenterWindow(word w,word h);
+void 	InitActorList (void);
+void 	GetNewActor (void);
+void 	RemoveObj (objtype *gone);
+void 	PollControls (void);
+void 	StopMusic(void);
+void 	StartMusic(void);
+void	PlayLoop (void);
+void StartDamageFlash (int damage);
+void StartBonusFlash (void);
+
+/*
+=============================================================================
+
+							WL_INTER
+
+=============================================================================
+*/
+
+void IntroScreen (void);
+void PreloadGraphics(void);
+void LevelCompleted (void);
+void	CheckHighScore (long score,word other);
+void Victory (void);
+void ClearSplitVWB (void);
+
+
+/*
+=============================================================================
+
+							WL_DEBUG
+
+=============================================================================
+*/
+
+int DebugKeys (void);
+void PicturePause (void);
+
+
+/*
+=============================================================================
+
+						 WL_DRAW DEFINITIONS
+
+=============================================================================
+*/
+
+extern	unsigned screenloc[3];
+extern	unsigned freelatch;
+
+extern	long 	lasttimecount;
+extern	long 	frameon;
+extern	boolean	fizzlein;
+
+extern	unsigned	wallheight[MAXVIEWWIDTH];
+
+extern	fixed	tileglobal;
+extern	fixed	focallength;
+extern	fixed	mindist;
+
+//
+// math tables
+//
+extern	int			pixelangle[MAXVIEWWIDTH];
+extern	long		far finetangent[FINEANGLES/4];
+extern	fixed 		far sintable[],far *costable;
+
+//
+// derived constants
+//
+extern	fixed 	scale;
+extern	long	heightnumerator,mindist;
+
+//
+// refresh variables
+//
+extern	fixed	viewx,viewy;			// the focal point
+extern	int		viewangle;
+extern	fixed	viewsin,viewcos;
+
+extern	long		postsource;
+extern	unsigned	postx;
+extern	unsigned	postwidth;
+
+
+extern	int		horizwall[],vertwall[];
+
+extern	unsigned	pwallpos;
+
+
+fixed	FixedByFrac (fixed a, fixed b);
+void	TransformActor (objtype *ob);
+void	BuildTables (void);
+void	ClearScreen (void);
+int		CalcRotate (objtype *ob);
+void	DrawScaleds (void);
+void	CalcTics (void);
+void	FixOfs (void);
+void	ThreeDRefresh (void);
+void  FarScalePost (void);
+
+/*
+=============================================================================
+
+						 WL_STATE DEFINITIONS
+
+=============================================================================
+*/
+#define TURNTICS	10
+#define SPDPATROL	512
+#define SPDDOG		1500
+
+
+extern	dirtype opposite[9];
+extern	dirtype diagonal[9][9];
+
+
+void	InitHitRect (objtype *ob, unsigned radius);
+void	SpawnNewObj (unsigned tilex, unsigned tiley, statetype *state);
+void	NewState (objtype *ob, statetype *state);
+
+boolean TryWalk (objtype *ob);
+void 	SelectChaseDir (objtype *ob);
+void 	SelectDodgeDir (objtype *ob);
+void	SelectRunDir (objtype *ob);
+void	MoveObj (objtype *ob, long move);
+boolean SightPlayer (objtype *ob);
+
+void	KillActor (objtype *ob);
+void	DamageActor (objtype *ob, unsigned damage);
+
+boolean CheckLine (objtype *ob);
+boolean	CheckSight (objtype *ob);
+
+
+/*
+=============================================================================
+
+						 WL_SCALE DEFINITIONS
+
+=============================================================================
+*/
+
+
+#define COMPSCALECODESTART	(65*4)		// offset to start of code in comp scaler
+
+typedef struct
+{
+	unsigned	codeofs[65];
+	unsigned	width[65];
+	byte		code[];
+}	t_compscale;
+
+typedef struct
+{
+	unsigned	leftpix,rightpix;
+	unsigned	dataofs[64];
+// table data after dataofs[rightpix-leftpix+1]
+}	t_compshape;
+
+
+extern	t_compscale _seg *scaledirectory[MAXSCALEHEIGHT+1];
+extern	long			fullscalefarcall[MAXSCALEHEIGHT+1];
+
+extern	byte		bitmasks1[8][8];
+extern	byte		bitmasks2[8][8];
+extern	unsigned	wordmasks[8][8];
+
+extern	byte		mapmasks1[4][8];
+extern	byte		mapmasks2[4][8];
+extern	byte		mapmasks3[4][8];
+
+extern	int			maxscale,maxscaleshl2;
+
+extern	boolean	insetupscaling;
+
+void SetupScaling (int maxscaleheight);
+void ScaleShape (int xcenter, int shapenum, unsigned height);
+void SimpleScaleShape (int xcenter, int shapenum, unsigned height);
+
+/*
+=============================================================================
+
+						 WL_AGENT DEFINITIONS
+
+=============================================================================
+*/
+
+//
+// player state info
+//
+extern	boolean		running;
+extern	long		thrustspeed;
+extern	unsigned	plux,pluy;		// player coordinates scaled to unsigned
+
+extern	int			anglefrac;
+extern	int			facecount;
+
+void	SpawnPlayer (int tilex, int tiley, int dir);
+void 	DrawFace (void);
+void	DrawHealth (void);
+void	TakeDamage (int points,objtype *attacker);
+void	HealSelf (int points);
+void	DrawLevel (void);
+void	DrawLives (void);
+void	GiveExtraMan (void);
+void	DrawScore (void);
+void	GivePoints (long points);
+void	DrawWeapon (void);
+void	DrawKeys (void);
+void	GiveWeapon (int weapon);
+void	DrawAmmo (void);
+void	GiveAmmo (int ammo);
+void	GiveKey (int key);
+void	GetBonus (statobj_t *check);
+
+void	Thrust (int angle, long speed);
+
+/*
+=============================================================================
+
+						 WL_ACT1 DEFINITIONS
+
+=============================================================================
+*/
+
+extern	doorobj_t	doorobjlist[MAXDOORS],*lastdoorobj;
+extern	int			doornum;
+
+extern	unsigned	doorposition[MAXDOORS],pwallstate;
+
+extern	byte		far areaconnect[NUMAREAS][NUMAREAS];
+
+extern	boolean		areabyplayer[NUMAREAS];
+
+extern unsigned	pwallstate;
+extern unsigned	pwallpos;			// amount a pushable wall has been moved (0-63)
+extern unsigned	pwallx,pwally;
+extern int			pwalldir;
+
+
+void InitDoorList (void);
+void InitStaticList (void);
+void SpawnStatic (int tilex, int tiley, int type);
+void SpawnDoor (int tilex, int tiley, boolean vertical, int lock);
+void MoveDoors (void);
+void MovePWalls (void);
+void OpenDoor (int door);
+void PlaceItemType (int itemtype, int tilex, int tiley);
+void PushWall (int checkx, int checky, int dir);
+void OperateDoor (int door);
+void InitAreas (void);
+
+/*
+=============================================================================
+
+						 WL_ACT2 DEFINITIONS
+
+=============================================================================
+*/
+
+#define s_nakedbody s_static10
+
+extern	statetype s_grddie1;
+extern	statetype s_dogdie1;
+extern	statetype s_ofcdie1;
+extern	statetype s_mutdie1;
+extern	statetype s_ssdie1;
+extern	statetype s_bossdie1;
+extern	statetype s_schabbdie1;
+extern	statetype s_fakedie1;
+extern	statetype s_mechadie1;
+extern	statetype s_hitlerdie1;
+extern	statetype s_greteldie1;
+extern	statetype s_giftdie1;
+extern	statetype s_fatdie1;
+
+extern	statetype s_spectredie1;
+extern	statetype s_angeldie1;
+extern	statetype s_transdie0;
+extern	statetype s_uberdie0;
+extern	statetype s_willdie1;
+extern	statetype s_deathdie1;
+
+
+extern	statetype s_grdchase1;
+extern	statetype s_dogchase1;
+extern	statetype s_ofcchase1;
+extern	statetype s_sschase1;
+extern	statetype s_mutchase1;
+extern	statetype s_bosschase1;
+extern	statetype s_schabbchase1;
+extern	statetype s_fakechase1;
+extern	statetype s_mechachase1;
+extern	statetype s_gretelchase1;
+extern	statetype s_giftchase1;
+extern	statetype s_fatchase1;
+
+extern	statetype s_spectrechase1;
+extern	statetype s_angelchase1;
+extern	statetype s_transchase1;
+extern	statetype s_uberchase1;
+extern	statetype s_willchase1;
+extern	statetype s_deathchase1;
+
+extern	statetype s_blinkychase1;
+extern	statetype s_hitlerchase1;
+
+extern	statetype s_grdpain;
+extern	statetype s_grdpain1;
+extern	statetype s_ofcpain;
+extern	statetype s_ofcpain1;
+extern	statetype s_sspain;
+extern	statetype s_sspain1;
+extern	statetype s_mutpain;
+extern	statetype s_mutpain1;
+
+extern	statetype s_deathcam;
+
+extern	statetype s_schabbdeathcam2;
+extern	statetype s_hitlerdeathcam2;
+extern	statetype s_giftdeathcam2;
+extern	statetype s_fatdeathcam2;
+
+void SpawnStand (enemy_t which, int tilex, int tiley, int dir);
+void SpawnPatrol (enemy_t which, int tilex, int tiley, int dir);
+void KillActor (objtype *ob);
+
+void	US_ControlPanel(byte);
+
+void SpawnDeadGuard (int tilex, int tiley);
+void SpawnBoss (int tilex, int tiley);
+void SpawnGretel (int tilex, int tiley);
+void SpawnTrans (int tilex, int tiley);
+void SpawnUber (int tilex, int tiley);
+void SpawnWill (int tilex, int tiley);
+void SpawnDeath (int tilex, int tiley);
+void SpawnAngel (int tilex, int tiley);
+void SpawnSpectre (int tilex, int tiley);
+void SpawnGhosts (int which, int tilex, int tiley);
+void SpawnSchabbs (int tilex, int tiley);
+void SpawnGift (int tilex, int tiley);
+void SpawnFat (int tilex, int tiley);
+void SpawnFakeHitler (int tilex, int tiley);
+void SpawnHitler (int tilex, int tiley);
+
+/*
+=============================================================================
+
+						 WL_TEXT DEFINITIONS
+
+=============================================================================
+*/
+
+extern	char	helpfilename[],endfilename[];
+
+extern	void	HelpScreens(void);
+extern	void	EndText(void);
diff --git a/src/lib/hb/wl_dr_a.asm b/src/lib/hb/wl_dr_a.asm
new file mode 100755
index 00000000..aec139a1
--- /dev/null
+++ b/src/lib/hb/wl_dr_a.asm
@@ -0,0 +1,795 @@
+	IDEAL
+	MODEL	MEDIUM,C
+	P286
+
+SCREENSEG	=	0a000h
+
+FINEANGLES	=	3600
+DEG90		=	900
+DEG180		=	1800
+DEG270		=	2700
+DEG360		=	3600
+
+OP_JLE		=	07eh
+OP_JGE		=	07dh
+
+EXTRN	finetangent:DWORD	; far array, starts at offset 0
+
+EXTRN	HitHorizWall:FAR
+EXTRN	HitVertWall:FAR
+EXTRN	HitHorizDoor:FAR
+EXTRN	HitVertDoor:FAR
+EXTRN	HitHorizPWall:FAR
+EXTRN	HitVertPWall:FAR
+
+
+DATASEG
+
+EXTRN	viewwidth:WORD
+
+EXTRN	tilemap:BYTE
+EXTRN	spotvis:BYTE
+EXTRN	pixelangle:WORD
+
+
+EXTRN	midangle:WORD
+EXTRN	angle:WORD
+
+EXTRN	focaltx:WORD
+EXTRN	focalty:WORD
+EXTRN	viewtx:WORD
+EXTRN	viewty:WORD
+EXTRN	viewx:DWORD
+EXTRN	viewy:DWORD
+
+EXTRN	xpartialup:WORD
+EXTRN	ypartialup:WORD
+EXTRN	xpartialdown:WORD
+EXTRN	ypartialdown:WORD
+
+EXTRN	tilehit:WORD
+EXTRN	pixx:WORD
+EXTRN	wallheight:WORD			; array of VIEWWIDTH entries
+
+EXTRN	xtile:WORD
+EXTRN	ytile:WORD
+EXTRN	xtilestep:WORD
+EXTRN	ytilestep:WORD
+EXTRN	xintercept:DWORD
+EXTRN	yintercept:DWORD
+EXTRN	xstep:DWORD
+EXTRN	ystep:DWORD
+
+EXTRN	doorposition:WORD		; table of door position values
+
+
+EXTRN	pwallpos:WORD			; amound a pushable wall has been moved
+
+CODESEG
+
+;-------------------
+;
+; xpartialbyystep
+;
+; multiplies long [ystep] (possibly negative), by word [xpartial] (in BX)
+;
+; returns dx:ax
+; trashes bx,cx,di
+;
+;-------------------
+
+PROC xpartialbyystep NEAR
+;
+; setup
+;
+	mov	ax,[WORD ystep]
+	mov	cx,[WORD ystep+2]
+	or	cx,cx               ; is ystep negatice?
+	jns	@@multpos
+;
+; multiply negative cx:ax by bx
+;
+	neg	cx
+	neg	ax
+	sbb	cx,0
+
+	mul	bx					; fraction*fraction
+	mov	di,dx				; di is low word of result
+	mov	ax,cx				;
+	mul	bx					; units*fraction
+	add	ax,di
+	adc	dx,0
+
+	neg	dx
+	neg	ax
+	sbb	dx,0
+	ret
+;
+; multiply positive cx:ax by bx
+;
+EVEN
+@@multpos:
+	mul	bx					; fraction*fraction
+	mov	di,dx				; di is low word of result
+	mov	ax,cx				;
+	mul	bx					; units*fraction
+	add	ax,di
+	adc	dx,0
+
+	ret
+
+ENDP
+
+
+
+;-------------------
+;
+; ypartialbyxstep
+;
+; multiplies long [xstep] (possibly negative), by word [ypartial] (in BP)
+;
+; returns dx:ax
+; trashes cx,di,bp
+;
+;-------------------
+
+PROC ypartialbyxstep NEAR
+;
+; setup
+;
+	mov	ax,[WORD xstep]
+	mov	cx,[WORD xstep+2]
+	or	cx,cx               ; is ystep negatice?
+	jns	@@multpos
+;
+; multiply negative cx:ax by bx
+;
+	neg	cx
+	neg	ax
+	sbb	cx,0
+
+	mul	bp					; fraction*fraction
+	mov	di,dx				; di is low word of result
+	mov	ax,cx				;
+	mul	bp					; units*fraction
+	add	ax,di
+	adc	dx,0
+
+	neg	dx
+	neg	ax
+	sbb	dx,0
+	ret
+;
+; multiply positive cx:ax by bx
+;
+EVEN
+@@multpos:
+	mul	bp					; fraction*fraction
+	mov	di,dx				; di is low word of result
+	mov	ax,cx				;
+	mul	bp					; units*fraction
+	add	ax,di
+	adc	dx,0
+	ret
+
+ENDP
+
+
+;============================
+;
+; AsmRefresh
+;
+;
+;============================
+
+PROC	AsmRefresh
+PUBLIC	AsmRefresh
+
+	push	si
+	push	di
+	push	bp
+
+	mov	[pixx],0
+;---------------------------------------------------------------------------
+;
+; Setup to trace a ray through pixx view pixel
+;
+; CX : angle of the ray through pixx
+; ES : points to segment of finetangent array for this block of code
+;
+; Upon entrance to initialize block
+;
+; BX : xpartial
+; BP : ypartial
+;
+;---------------------------------------------------------------------------
+	EVEN
+pixxloop:
+	mov	ax,SEG finetangent
+	mov	es,ax
+	mov	cx,[midangle]			; center of view area
+	mov	bx,[pixx]
+	shl	bx,1
+	add	cx,[pixelangle+bx]		; delta for this pixel
+	cmp	cx,0
+	jge	not0
+;----------
+;
+; -90 - -1 degree arc
+;
+;----------
+	add	cx,FINEANGLES			; -90 is the same as 270
+	jmp	entry360
+
+not0:
+	cmp	cx,DEG90
+	jge	not90
+;----------
+;
+; 0-89 degree arc
+;
+;----------
+entry90:
+	mov	[xtilestep],1			; xtilestep = 1
+	mov	[ytilestep],-1			; ytilestep = -1
+	mov	[BYTE cs:horizop],OP_JGE	; patch a jge in
+	mov	[BYTE cs:vertop],OP_JLE		; patch a jle in
+	mov	bx,DEG90-1
+	sub	bx,cx
+	;begin 8086 hack
+	;shl	bx,2
+	shl bx,1
+	shl bx,1
+	;end 8086 hack
+	mov	ax,[es:bx]
+	mov	dx,[es:bx+2]
+	mov	[WORD xstep],ax
+	mov	[WORD xstep+2],dx		; xstep = finetangent[DEG90-1-angle]
+	mov	bx,cx
+	;begin 8086 hack
+	;shl	bx,2
+	shl bx,1
+	shl bx,1
+	;end 8086 hack
+	mov	ax,[es:bx]
+	mov	dx,[es:bx+2]
+	neg	dx
+	neg	ax
+	sbb	dx,0
+	mov	[WORD ystep],ax
+	mov	[WORD ystep+2],dx		; ystep = -finetangent[angle]
+
+	mov	bx,[xpartialup]			; xpartial = xpartialup
+	mov	bp,[ypartialdown]		; ypartial = ypartialdown
+	jmp	initvars
+
+not90:
+	cmp	cx,DEG180
+	jge	not180
+;----------
+;
+; 90-179 degree arc
+;
+;----------
+	mov	ax,-1
+	mov	[xtilestep],ax			; xtilestep = -1
+	mov	[ytilestep],ax			; ytilestep = -1
+	mov	[BYTE cs:horizop],OP_JLE	; patch a jle in
+	mov	[BYTE cs:vertop],OP_JLE		; patch a jle in
+
+	mov	bx,cx
+	;begin 8086 hack
+	;shl	bx,2
+	shl bx,1
+	shl bx,1
+	;end 8086 hack
+	mov	ax,[es:bx-DEG90*4]
+	mov	dx,[es:bx+2-DEG90*4]
+	neg	dx
+	neg	ax
+	sbb	dx,0
+	mov	[WORD xstep],ax
+	mov	[WORD xstep+2],dx		; xstep = -finetangent[angle-DEG90]
+	mov	bx,DEG180-1
+	sub	bx,cx
+	;begin 8086 hack
+	;shl	bx,2
+	shl bx,1
+	shl bx,1
+	;end 8086 hack
+	mov	ax,[es:bx]
+	mov	dx,[es:bx+2]
+	neg	dx
+	neg	ax
+	sbb	dx,0
+	mov	[WORD ystep],ax
+	mov	[WORD ystep+2],dx		; ystep = -finetangent[DEG180-1-angle]
+
+	mov	bx,[xpartialdown]		; xpartial = xpartialdown
+	mov	bp,[ypartialdown]		; ypartial = ypartialdown
+	jmp	initvars
+
+not180:
+	cmp	cx,DEG270
+	jge	not270
+;----------
+;
+; 180-269 degree arc
+;
+;----------
+	mov	[xtilestep],-1			; xtilestep = -1
+	mov	[ytilestep],1			; ytilestep = 1
+	mov	[BYTE cs:horizop],OP_JLE	; patch a jle in
+	mov	[BYTE cs:vertop],OP_JGE		; patch a jge in
+
+	mov	bx,DEG270-1
+	sub	bx,cx
+	;begin 8086 hack
+	;shl	bx,2
+	shl bx,1
+	shl bx,1
+	;end 8086 hack
+	mov	ax,[es:bx]
+	mov	dx,[es:bx+2]
+	neg	dx
+	neg	ax
+	sbb	dx,0
+	mov	[WORD xstep],ax
+	mov	[WORD xstep+2],dx		; xstep = -finetangent[DEG270-1-angle]
+	mov	bx,cx
+	;begin 8086 hack
+	;shl	bx,2
+	shl bx,1
+	shl bx,1
+	;end 8086 hack
+	mov	ax,[es:bx-DEG180*4]
+	mov	dx,[es:bx+2-DEG180*4]
+	mov	[WORD ystep],ax
+	mov	[WORD ystep+2],dx		; ystep = finetangent[angle-DEG180]
+
+	mov	bx,[xpartialdown]		; xpartial = xpartialdown
+	mov	bp,[ypartialup]			; ypartial = ypartialup
+	jmp	initvars
+
+
+not270:
+	cmp	cx,DEG360
+	jge	not360
+;----------
+;
+; 270-359 degree arc
+;
+;----------
+entry360:
+	mov	ax,1
+	mov	[xtilestep],ax			; xtilestep = 1
+	mov	[ytilestep],ax			; ytilestep = 1
+	mov	[BYTE cs:horizop],OP_JGE	; patch a jge in
+	mov	[BYTE cs:vertop],OP_JGE		; patch a jge in
+
+	mov	bx,cx
+	;begin 8086 hack
+	;shl	bx,2
+	shl bx,1
+	shl bx,1
+	;end 8086 hack
+	mov	ax,[es:bx-DEG270*4]
+	mov	dx,[es:bx+2-DEG270*4]
+	mov	[WORD xstep],ax
+	mov	[WORD xstep+2],dx		; xstep = finetangent[angle-DEG270]
+	mov	bx,DEG360-1
+	sub	bx,cx
+	;begin 8086 hack
+	;shl	bx,2
+	shl bx,1
+	shl bx,1
+	;end 8086 hack
+	mov	ax,[es:bx]
+	mov	dx,[es:bx+2]
+	mov	[WORD ystep],ax
+	mov	[WORD ystep+2],dx		; ystep = finetangent[DEG360-1-angle]
+
+	mov	bx,[xpartialup]			; xpartial = xpartialup
+	mov	bp,[ypartialup]			; ypartial = ypartialup
+	jmp	initvars
+
+
+not360:
+;----------
+;
+; 360-449 degree arc
+;
+;----------
+	sub	cx,FINEANGLES			; -449 is the same as 89
+	jmp	entry90
+
+;---------------------------------------------------------------------------
+;
+; initialise variables for intersection testing
+;
+;---------------------------------------------------------------------------
+initvars:
+	call	NEAR xpartialbyystep	; xpartial is in BX
+	add	ax,[WORD viewy]
+	adc	dx,[WORD viewy+2]
+	mov	[WORD yintercept],ax
+	mov	[WORD yintercept+2],dx
+
+	mov	si,[focaltx]
+	add	si,[xtilestep]
+	mov	[xtile],si					; xtile = focaltx+xtilestep
+	;begin 8086 hack
+	;shl	si,6
+	push cx
+	mov cl,6
+	shl si,cl
+	pop cx
+	;end 8086 hack
+	add	si,dx						; xspot = (xtile<<6) + yinttile
+
+
+	call	NEAR ypartialbyxstep	; ypartial is in BP
+	add	ax,[WORD viewx]
+	adc	dx,[WORD viewx+2]
+	mov	[WORD xintercept],ax
+	mov	cx,dx
+
+	mov	bx,[focalty]
+	add	bx,[ytilestep]
+	mov	bp,bx						; ytile = focalty+ytilestep
+	mov	di,dx
+	;begin 8086 hack
+	;shl	di,6
+	push cx
+	mov cl,6
+	shl di,cl
+	pop cx
+	;end 8086 hack
+	add	di,bx						; yspot = (xinttile<<6) + ytile
+
+	mov	bx,[xtile]
+	mov	dx,[WORD yintercept+2]
+	mov	ax,SCREENSEG
+	mov	es,ax						; faster than mov es,[screenseg]
+
+
+;---------------------------------------------------------------------------
+;
+; trace along this angle until we hit a wall
+;
+; CORE LOOP!
+;
+; All variables are killed when a wall is hit
+;
+; AX : scratch
+; BX : xtile
+; CX : high word of xintercept
+; DX : high word of yintercept
+; SI : xspot (yinttile<<6)+xtile (index into tilemap and spotvis)
+; DI : yspot (xinttile<<6)+ytile (index into tilemap and spotvis)
+; BP : ytile
+; ES : screenseg
+;
+;---------------------------------------------------------------------------
+
+;-----------
+;
+; check intersections with vertical walls
+;
+;-----------
+
+	EVEN
+vertcheck:
+	cmp	dx,bp
+vertop:								; 0x7e = jle (ytilestep==-1)
+	jle	horizentry					; 0x7d = jge (ytilestep==1)
+vertentry:
+	test [BYTE tilemap+si],0ffh		; tilehit = *((byte *)tilemap+xspot);
+	jnz	hitvert
+passvert:
+	mov	[BYTE spotvis+si],1			; *((byte *)spotvis+xspot) = true;
+	add	bx,[xtilestep]				; xtile+=xtilestep
+	mov	ax,[WORD ystep]
+	add	[WORD yintercept],ax		; yintercept += ystep
+	adc	dx,[WORD ystep+2]
+	mov	si,bx
+	;begin 8086 hack
+	;shl	si,6
+	push cx
+	mov cl,6
+	shl si,cl
+	pop cx
+	;end 8086 hack
+	add	si,dx						; xspot = (xtile<<6)+yinttile
+	jmp	vertcheck
+
+	EVEN
+hitvert:
+	mov	al,[BYTE tilemap+si]		; tilehit = *((byte *)tilemap+xspot);
+	mov	[BYTE tilehit],al
+	or	al,al						; set flags
+	jns	notvertdoor
+	jmp	vertdoor
+notvertdoor:
+	mov	[WORD xintercept],0
+	mov	[WORD xintercept+2],bx
+	mov	[xtile],bx
+	mov	[WORD yintercept+2],dx
+	mov	[ytile],dx
+	call FAR HitVertWall
+	jmp nextpix
+
+
+;-----------
+;
+; check intersections with horizontal walls
+;
+;-----------
+	EVEN
+horizcheck:
+	cmp	cx,bx
+horizop:							; 0x7e = jle (xtilestep==-1)
+	jle	vertentry					; 0x7d = jge (xtilestep==1)
+horizentry:
+	test [BYTE tilemap+di],0ffh		; tilehit = *((byte *)tilemap+yspot);
+	jnz	hithoriz
+passhoriz:
+	mov	[BYTE spotvis+di],1			; *((byte *)spotvis+yspot) = true;
+	add	bp,[ytilestep]				; ytile+=ytilestep
+	mov	ax,[WORD xstep]
+	add	[WORD xintercept],ax		; xintercept += xstep
+	adc	cx,[WORD xstep+2]
+	mov	di,cx
+	;begin 8086 hack
+	;shl	di,6
+	push cx
+	mov cl,6
+	shl di,cl
+	pop cx
+	;end 8086 hack
+	add	di,bp						; yspot = (xinttile<<6)+ytile
+	jmp	horizcheck
+
+	EVEN
+hithoriz:
+	mov	al,[BYTE tilemap+di]		; tilehit = *((byte *)tilemap+yspot);
+	mov	[BYTE tilehit],al
+	or	al,al						; set flags
+	js	horizdoor
+	mov	[WORD xintercept+2],cx
+	mov	[xtile],cx
+	mov	[WORD yintercept],0
+	mov	[WORD yintercept+2],bp
+	mov	[ytile],bp
+	call FAR HitHorizWall
+	jmp nextpix
+
+;---------------------------------------------------------------------------
+;
+; next pixel over
+;
+;---------------------------------------------------------------------------
+
+nextpix:
+	mov	ax,[pixx]
+	inc	ax
+	mov	[pixx],ax
+	cmp	ax,[viewwidth]
+	jge	done
+	jmp	pixxloop
+done:
+	pop	bp
+	pop	di
+	pop	si
+	retf
+
+;===========================================================================
+
+;=============
+;
+; hit a special horizontal wall, so find which coordinate a door would be
+; intersected at, and check to see if the door is open past that point
+;
+;=============
+horizdoor:
+	mov	[xtile],bx					; save off live register variables
+	mov	[WORD yintercept+2],dx
+
+	test al,040h      				; both high bits set == pushable wall
+	jnz	horizpushwall
+
+	mov	bx,ax
+	and	bx,7fh						; strip high bit
+	shl	bx,1                        ; index into word width door table
+
+	mov	ax,[WORD xstep]
+	mov	dx,[WORD xstep+2]
+	sar	dx,1
+	rcr ax,1						; half a step gets to door position
+
+	add	ax,[WORD xintercept]		; add half step to current intercept pos
+	adc	dx,cx						; CX hold high word of xintercept
+
+	cmp	cx,dx						; is it still in the same tile?
+	je	hithmid
+;
+; midpoint is outside tile, so it hit the side of the wall before a door
+;
+continuehoriz:
+	mov	bx,[xtile]					; reload register variables
+	mov	dx,[WORD yintercept+2]
+	jmp	passhoriz					; continue tracing
+;
+; the trace hit the door plane at pixel position AX, see if the door is
+; closed that much
+;
+hithmid:
+	cmp	ax,[doorposition+bx]		; position of leading edge of door
+	jb	continuehoriz
+;
+; draw the door
+;
+	mov	[WORD xintercept],ax		; save pixel intercept position
+	mov	[WORD xintercept+2],cx
+
+	mov	[WORD yintercept],8000h		; intercept in middle of tile
+	mov	[WORD yintercept+2],bp
+
+	call	FAR HitHorizDoor
+	jmp	nextpix
+
+;============
+;
+; hit a sliding horizontal wall
+;
+;============
+
+horizpushwall:
+	mov	ax,[WORD xstep+2]			; multiply xstep by pwallmove (0-63)
+	mul	[pwallpos]
+	mov	bx,ax
+	mov	ax,[WORD xstep]
+	mul	[pwallpos]
+	add	dx,bx
+
+	sar	dx,1						; then divide by 64 to accomplish a
+	rcr ax,1						; fixed point multiplication
+	sar	dx,1
+	rcr ax,1
+	sar	dx,1
+	rcr ax,1
+	sar	dx,1
+	rcr ax,1
+	sar	dx,1
+	rcr ax,1
+	sar	dx,1
+	rcr ax,1
+
+	add	ax,[WORD xintercept]		; add partial step to current intercept
+	adc	dx,cx						; CX hold high word of xintercept
+
+	cmp	cx,dx						; is it still in the same tile?
+	jne	continuehoriz				; no, it hit the side
+
+;
+; draw the pushable wall at the new height
+;
+	mov	[WORD xintercept],ax		; save pixel intercept position
+	mov	[WORD xintercept+2],dx
+
+	mov	[WORD yintercept+2],bp
+	mov	[WORD yintercept],0
+
+	call	FAR HitHorizPWall
+	jmp	nextpix
+
+
+
+;===========================================================================
+
+;=============
+;
+; hit a special vertical wall, so find which coordinate a door would be
+; intersected at, and check to see if the door is open past that point
+;
+;=============
+vertdoor:
+	mov	[xtile],bx					; save off live register variables
+	mov	[WORD yintercept+2],dx
+
+	test al,040h      				; both high bits set == pushable wall
+	jnz	vertpushwall
+
+	mov	bx,ax
+	and	bx,7fh						; strip high bit
+	shl	bx,1                        ; index into word width doorposition
+
+	mov	ax,[WORD ystep]
+	mov	dx,[WORD ystep+2]
+	sar	dx,1
+	rcr ax,1						; half a step gets to door position
+
+	add	ax,[WORD yintercept]		; add half step to current intercept pos
+	adc	dx,[WORD yintercept+2]
+
+	cmp	[WORD yintercept+2],dx		; is it still in the same tile?
+	je	hitvmid
+;
+; midpoint is outside tile, so it hit the side of the wall before a door
+;
+continuevert:
+	mov	bx,[xtile]					; reload register variables
+	mov	dx,[WORD yintercept+2]
+	jmp	passvert					; continue tracing
+;
+; the trace hit the door plane at pixel position AX, see if the door is
+; closed that much
+;
+hitvmid:
+	cmp	ax,[doorposition+bx]		; position of leading edge of door
+	jb	continuevert
+;
+; draw the door
+;
+	mov	[WORD yintercept],ax		; save pixel intercept position
+	mov	[WORD xintercept],8000h		; intercept in middle of tile
+	mov	ax,[xtile]
+	mov	[WORD xintercept+2],ax
+
+	call	FAR HitVertDoor
+	jmp	nextpix
+
+;============
+;
+; hit a sliding vertical wall
+;
+;============
+
+vertpushwall:
+	mov	ax,[WORD ystep+2]			; multiply ystep by pwallmove (0-63)
+	mul	[pwallpos]
+	mov	bx,ax
+	mov	ax,[WORD ystep]
+	mul	[pwallpos]
+	add	dx,bx
+
+	sar	dx,1						; then divide by 64 to accomplish a
+	rcr ax,1						; fixed point multiplication
+	sar	dx,1
+	rcr ax,1
+	sar	dx,1
+	rcr ax,1
+	sar	dx,1
+	rcr ax,1
+	sar	dx,1
+	rcr ax,1
+	sar	dx,1
+	rcr ax,1
+
+	add	ax,[WORD yintercept]		; add partial step to current intercept
+	adc	dx,[WORD yintercept+2]
+
+	cmp	[WORD yintercept+2],dx		; is it still in the same tile?
+	jne	continuevert				; no, it hit the side
+
+;
+; draw the pushable wall at the new height
+;
+	mov	[WORD yintercept],ax		; save pixel intercept position
+	mov	[WORD yintercept+2],dx
+
+	mov	bx,[xtile]
+	mov	[WORD xintercept+2],bx
+	mov	[WORD xintercept],0
+
+	call	FAR HitVertPWall
+	jmp	nextpix
+
+
+
+ENDP
+
+
+END
+
+
diff --git a/src/lib/hb/wl_draw.c b/src/lib/hb/wl_draw.c
new file mode 100755
index 00000000..3c32e39e
--- /dev/null
+++ b/src/lib/hb/wl_draw.c
@@ -0,0 +1,1419 @@
+// WL_DRAW.C
+
+#include "WL_DEF.H"
+#include <DOS.H>
+#pragma hdrstop
+
+//#define DEBUGWALLS
+//#define DEBUGTICS
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+// the door is the last picture before the sprites
+#define DOORWALL	(PMSpriteStart-8)
+
+#define ACTORSIZE	0x4000
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+
+#ifdef DEBUGWALLS
+unsigned screenloc[3]= {0,0,0};
+#else
+unsigned screenloc[3]= {PAGE1START,PAGE2START,PAGE3START};
+#endif
+unsigned freelatch = FREESTART;
+
+long 	lasttimecount;
+long 	frameon;
+
+unsigned	wallheight[MAXVIEWWIDTH];
+
+fixed	tileglobal	= TILEGLOBAL;
+fixed	mindist		= MINDIST;
+
+
+//
+// math tables
+//
+int			pixelangle[MAXVIEWWIDTH];
+long		far finetangent[FINEANGLES/4];
+fixed 		far sintable[ANGLES+ANGLES/4],far *costable = sintable+(ANGLES/4);
+
+//
+// refresh variables
+//
+fixed	viewx,viewy;			// the focal point
+int		viewangle;
+fixed	viewsin,viewcos;
+
+
+
+fixed	FixedByFrac (fixed a, fixed b);
+void	TransformActor (objtype *ob);
+void	BuildTables (void);
+void	ClearScreen (void);
+int		CalcRotate (objtype *ob);
+void	DrawScaleds (void);
+void	CalcTics (void);
+void	FixOfs (void);
+void	ThreeDRefresh (void);
+
+
+
+//
+// wall optimization variables
+//
+int		lastside;		// true for vertical
+long	lastintercept;
+int		lasttilehit;
+
+
+//
+// ray tracing variables
+//
+int			focaltx,focalty,viewtx,viewty;
+
+int			midangle,angle;
+unsigned	xpartial,ypartial;
+unsigned	xpartialup,xpartialdown,ypartialup,ypartialdown;
+unsigned	xinttile,yinttile;
+
+unsigned	tilehit;
+unsigned	pixx;
+
+int		xtile,ytile;
+int		xtilestep,ytilestep;
+long	xintercept,yintercept;
+long	xstep,ystep;
+
+int		horizwall[MAXWALLTILES],vertwall[MAXWALLTILES];
+
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+
+void AsmRefresh (void);			// in WL_DR_A.ASM
+
+/*
+============================================================================
+
+			   3 - D  DEFINITIONS
+
+============================================================================
+*/
+
+
+//==========================================================================
+
+
+/*
+========================
+=
+= FixedByFrac
+=
+= multiply a 16/16 bit, 2's complement fixed point number by a 16 bit
+= fraction, passed as a signed magnitude 32 bit number
+=
+========================
+*/
+
+#pragma warn -rvl			// I stick the return value in with ASMs
+
+fixed FixedByFrac (fixed a, fixed b)
+{
+//
+// setup
+//
+asm	mov	si,[WORD PTR b+2]	// sign of result = sign of fraction
+
+asm	mov	ax,[WORD PTR a]
+asm	mov	cx,[WORD PTR a+2]
+
+asm	or	cx,cx
+asm	jns	aok:				// negative?
+asm	neg	cx
+asm	neg	ax
+asm	sbb	cx,0
+asm	xor	si,0x8000			// toggle sign of result
+aok:
+
+//
+// multiply  cx:ax by bx
+//
+asm	mov	bx,[WORD PTR b]
+asm	mul	bx					// fraction*fraction
+asm	mov	di,dx				// di is low word of result
+asm	mov	ax,cx				//
+asm	mul	bx					// units*fraction
+asm add	ax,di
+asm	adc	dx,0
+
+//
+// put result dx:ax in 2's complement
+//
+asm	test	si,0x8000		// is the result negative?
+asm	jz	ansok:
+asm	neg	dx
+asm	neg	ax
+asm	sbb	dx,0
+
+ansok:;
+
+}
+
+#pragma warn +rvl
+
+//==========================================================================
+
+/*
+========================
+=
+= TransformActor
+=
+= Takes paramaters:
+=   gx,gy		: globalx/globaly of point
+=
+= globals:
+=   viewx,viewy		: point of view
+=   viewcos,viewsin	: sin/cos of viewangle
+=   scale		: conversion from global value to screen value
+=
+= sets:
+=   screenx,transx,transy,screenheight: projected edge location and size
+=
+========================
+*/
+
+
+//
+// transform actor
+//
+void TransformActor (objtype *ob)
+{
+	int ratio;
+	fixed gx,gy,gxt,gyt,nx,ny;
+	long	temp;
+
+//
+// translate point to view centered coordinates
+//
+	gx = ob->x-viewx;
+	gy = ob->y-viewy;
+
+//
+// calculate newx
+//
+	gxt = FixedByFrac(gx,viewcos);
+	gyt = FixedByFrac(gy,viewsin);
+	nx = gxt-gyt-ACTORSIZE;		// fudge the shape forward a bit, because
+								// the midpoint could put parts of the shape
+								// into an adjacent wall
+
+//
+// calculate newy
+//
+	gxt = FixedByFrac(gx,viewsin);
+	gyt = FixedByFrac(gy,viewcos);
+	ny = gyt+gxt;
+
+//
+// calculate perspective ratio
+//
+	ob->transx = nx;
+	ob->transy = ny;
+
+	if (nx<mindist)			// too close, don't overflow the divide
+	{
+	  ob->viewheight = 0;
+	  return;
+	}
+
+	ob->viewx = centerx + ny*scale/nx;	// DEBUG: use assembly divide
+
+//
+// calculate height (heightnumerator/(nx>>8))
+//
+	asm	mov	ax,[WORD PTR heightnumerator]
+	asm	mov	dx,[WORD PTR heightnumerator+2]
+	asm	idiv	[WORD PTR nx+1]			// nx>>8
+	asm	mov	[WORD PTR temp],ax
+	asm	mov	[WORD PTR temp+2],dx
+
+	ob->viewheight = temp;
+}
+
+//==========================================================================
+
+/*
+========================
+=
+= TransformTile
+=
+= Takes paramaters:
+=   tx,ty		: tile the object is centered in
+=
+= globals:
+=   viewx,viewy		: point of view
+=   viewcos,viewsin	: sin/cos of viewangle
+=   scale		: conversion from global value to screen value
+=
+= sets:
+=   screenx,transx,transy,screenheight: projected edge location and size
+=
+= Returns true if the tile is withing getting distance
+=
+========================
+*/
+
+boolean TransformTile (int tx, int ty, int *dispx, int *dispheight)
+{
+	int ratio;
+	fixed gx,gy,gxt,gyt,nx,ny;
+	long	temp;
+
+//
+// translate point to view centered coordinates
+//
+	gx = ((long)tx<<TILESHIFT)+0x8000-viewx;
+	gy = ((long)ty<<TILESHIFT)+0x8000-viewy;
+
+//
+// calculate newx
+//
+	gxt = FixedByFrac(gx,viewcos);
+	gyt = FixedByFrac(gy,viewsin);
+	nx = gxt-gyt-0x2000;		// 0x2000 is size of object
+
+//
+// calculate newy
+//
+	gxt = FixedByFrac(gx,viewsin);
+	gyt = FixedByFrac(gy,viewcos);
+	ny = gyt+gxt;
+
+
+//
+// calculate perspective ratio
+//
+	if (nx<mindist)			// too close, don't overflow the divide
+	{
+		*dispheight = 0;
+		return false;
+	}
+
+	*dispx = centerx + ny*scale/nx;	// DEBUG: use assembly divide
+
+//
+// calculate height (heightnumerator/(nx>>8))
+//
+	asm	mov	ax,[WORD PTR heightnumerator]
+	asm	mov	dx,[WORD PTR heightnumerator+2]
+	asm	idiv	[WORD PTR nx+1]			// nx>>8
+	asm	mov	[WORD PTR temp],ax
+	asm	mov	[WORD PTR temp+2],dx
+
+	*dispheight = temp;
+
+//
+// see if it should be grabbed
+//
+	if (nx<TILEGLOBAL && ny>-TILEGLOBAL/2 && ny<TILEGLOBAL/2)
+		return true;
+	else
+		return false;
+}
+
+//==========================================================================
+
+/*
+====================
+=
+= CalcHeight
+=
+= Calculates the height of xintercept,yintercept from viewx,viewy
+=
+====================
+*/
+
+#pragma warn -rvl			// I stick the return value in with ASMs
+
+int	CalcHeight (void)
+{
+	int	transheight;
+	int ratio;
+	fixed gxt,gyt,nx,ny;
+	long	gx,gy;
+
+	gx = xintercept-viewx;
+	gxt = FixedByFrac(gx,viewcos);
+
+	gy = yintercept-viewy;
+	gyt = FixedByFrac(gy,viewsin);
+
+	nx = gxt-gyt;
+
+  //
+  // calculate perspective ratio (heightnumerator/(nx>>8))
+  //
+	if (nx<mindist)
+		nx=mindist;			// don't let divide overflow
+
+	asm	mov	ax,[WORD PTR heightnumerator]
+	asm	mov	dx,[WORD PTR heightnumerator+2]
+	asm	idiv	[WORD PTR nx+1]			// nx>>8
+}
+
+
+//==========================================================================
+
+/*
+===================
+=
+= ScalePost
+=
+===================
+*/
+
+long		postsource;
+unsigned	postx;
+unsigned	postwidth;
+
+void	near ScalePost (void)		// VGA version
+{
+	asm	mov	ax,SCREENSEG
+	asm	mov	es,ax
+
+	asm	mov	bx,[postx]
+	asm	shl	bx,1
+	asm	mov	bp,WORD PTR [wallheight+bx]		// fractional height (low 3 bits frac)
+	asm	and	bp,0xfff8				// bp = heightscaler*4
+	asm	shr	bp,1
+	asm	cmp	bp,[maxscaleshl2]
+	asm	jle	heightok
+	asm	mov	bp,[maxscaleshl2]
+heightok:
+	asm	add	bp,OFFSET fullscalefarcall
+	//
+	// scale a byte wide strip of wall
+	//
+	asm	mov	bx,[postx]
+	asm	mov	di,bx
+	asm	shr	di,1						// X in bytes
+	asm	shr	di,1
+	asm	add	di,[bufferofs]
+
+	asm	and	bx,3
+	/* begin 8086 hack
+	asm	shl	bx,3
+	*/
+	asm push cx
+	asm mov cl,3
+	asm shl bx,cl
+	asm pop cx
+	/* end 8086 hack */
+	asm	add	bx,[postwidth]
+
+	asm	mov	al,BYTE PTR [mapmasks1-1+bx]	// -1 because no widths of 0
+	asm	mov	dx,SC_INDEX+1
+	asm	out	dx,al						// set bit mask register
+	asm	lds	si,DWORD PTR [postsource]
+	asm	call DWORD PTR [bp]				// scale the line of pixels
+
+	asm	mov	al,BYTE PTR [ss:mapmasks2-1+bx]   // -1 because no widths of 0
+	asm	or	al,al
+	asm	jz	nomore
+
+	//
+	// draw a second byte for vertical strips that cross two bytes
+	//
+	asm	inc	di
+	asm	out	dx,al						// set bit mask register
+	asm	call DWORD PTR [bp]				// scale the line of pixels
+
+	asm	mov	al,BYTE PTR [ss:mapmasks3-1+bx]	// -1 because no widths of 0
+	asm	or	al,al
+	asm	jz	nomore
+	//
+	// draw a third byte for vertical strips that cross three bytes
+	//
+	asm	inc	di
+	asm	out	dx,al						// set bit mask register
+	asm	call DWORD PTR [bp]				// scale the line of pixels
+
+
+nomore:
+	asm	mov	ax,ss
+	asm	mov	ds,ax
+}
+
+void  FarScalePost (void)				// just so other files can call
+{
+	ScalePost ();
+}
+
+
+/*
+====================
+=
+= HitVertWall
+=
+= tilehit bit 7 is 0, because it's not a door tile
+= if bit 6 is 1 and the adjacent tile is a door tile, use door side pic
+=
+====================
+*/
+
+void HitVertWall (void)
+{
+	int			wallpic;
+	unsigned	texture;
+
+	texture = (yintercept>>4)&0xfc0;
+	if (xtilestep == -1)
+	{
+		texture = 0xfc0-texture;
+		xintercept += TILEGLOBAL;
+	}
+	wallheight[pixx] = CalcHeight();
+
+	if (lastside==1 && lastintercept == xtile && lasttilehit == tilehit)
+	{
+		// in the same wall type as last time, so check for optimized draw
+		if (texture == (unsigned)postsource)
+		{
+		// wide scale
+			postwidth++;
+			wallheight[pixx] = wallheight[pixx-1];
+			return;
+		}
+		else
+		{
+			ScalePost ();
+			(unsigned)postsource = texture;
+			postwidth = 1;
+			postx = pixx;
+		}
+	}
+	else
+	{
+	// new wall
+		if (lastside != -1)				// if not the first scaled post
+			ScalePost ();
+
+		lastside = true;
+		lastintercept = xtile;
+
+		lasttilehit = tilehit;
+		postx = pixx;
+		postwidth = 1;
+
+		if (tilehit & 0x40)
+		{								// check for adjacent doors
+			ytile = yintercept>>TILESHIFT;
+			if ( tilemap[xtile-xtilestep][ytile]&0x80 )
+				wallpic = DOORWALL+3;
+			else
+				wallpic = vertwall[tilehit & ~0x40];
+		}
+		else
+			wallpic = vertwall[tilehit];
+
+		*( ((unsigned *)&postsource)+1) = (unsigned)PM_GetPage(wallpic);
+		(unsigned)postsource = texture;
+
+	}
+}
+
+
+/*
+====================
+=
+= HitHorizWall
+=
+= tilehit bit 7 is 0, because it's not a door tile
+= if bit 6 is 1 and the adjacent tile is a door tile, use door side pic
+=
+====================
+*/
+
+void HitHorizWall (void)
+{
+	int			wallpic;
+	unsigned	texture;
+
+	texture = (xintercept>>4)&0xfc0;
+	if (ytilestep == -1)
+		yintercept += TILEGLOBAL;
+	else
+		texture = 0xfc0-texture;
+	wallheight[pixx] = CalcHeight();
+
+	if (lastside==0 && lastintercept == ytile && lasttilehit == tilehit)
+	{
+		// in the same wall type as last time, so check for optimized draw
+		if (texture == (unsigned)postsource)
+		{
+		// wide scale
+			postwidth++;
+			wallheight[pixx] = wallheight[pixx-1];
+			return;
+		}
+		else
+		{
+			ScalePost ();
+			(unsigned)postsource = texture;
+			postwidth = 1;
+			postx = pixx;
+		}
+	}
+	else
+	{
+	// new wall
+		if (lastside != -1)				// if not the first scaled post
+			ScalePost ();
+
+		lastside = 0;
+		lastintercept = ytile;
+
+		lasttilehit = tilehit;
+		postx = pixx;
+		postwidth = 1;
+
+		if (tilehit & 0x40)
+		{								// check for adjacent doors
+			xtile = xintercept>>TILESHIFT;
+			if ( tilemap[xtile][ytile-ytilestep]&0x80 )
+				wallpic = DOORWALL+2;
+			else
+				wallpic = horizwall[tilehit & ~0x40];
+		}
+		else
+			wallpic = horizwall[tilehit];
+
+		*( ((unsigned *)&postsource)+1) = (unsigned)PM_GetPage(wallpic);
+		(unsigned)postsource = texture;
+	}
+
+}
+
+//==========================================================================
+
+/*
+====================
+=
+= HitHorizDoor
+=
+====================
+*/
+
+void HitHorizDoor (void)
+{
+	unsigned	texture,doorpage,doornum;
+
+	doornum = tilehit&0x7f;
+	texture = ( (xintercept-doorposition[doornum]) >> 4) &0xfc0;
+
+	wallheight[pixx] = CalcHeight();
+
+	if (lasttilehit == tilehit)
+	{
+	// in the same door as last time, so check for optimized draw
+		if (texture == (unsigned)postsource)
+		{
+		// wide scale
+			postwidth++;
+			wallheight[pixx] = wallheight[pixx-1];
+			return;
+		}
+		else
+		{
+			ScalePost ();
+			(unsigned)postsource = texture;
+			postwidth = 1;
+			postx = pixx;
+		}
+	}
+	else
+	{
+		if (lastside != -1)				// if not the first scaled post
+			ScalePost ();			// draw last post
+	// first pixel in this door
+		lastside = 2;
+		lasttilehit = tilehit;
+		postx = pixx;
+		postwidth = 1;
+
+		switch (doorobjlist[doornum].lock)
+		{
+		case dr_normal:
+			doorpage = DOORWALL;
+			break;
+		case dr_lock1:
+		case dr_lock2:
+		case dr_lock3:
+		case dr_lock4:
+			doorpage = DOORWALL+6;
+			break;
+		case dr_elevator:
+			doorpage = DOORWALL+4;
+			break;
+		}
+
+		*( ((unsigned *)&postsource)+1) = (unsigned)PM_GetPage(doorpage);
+		(unsigned)postsource = texture;
+	}
+}
+
+//==========================================================================
+
+/*
+====================
+=
+= HitVertDoor
+=
+====================
+*/
+
+void HitVertDoor (void)
+{
+	unsigned	texture,doorpage,doornum;
+
+	doornum = tilehit&0x7f;
+	texture = ( (yintercept-doorposition[doornum]) >> 4) &0xfc0;
+
+	wallheight[pixx] = CalcHeight();
+
+	if (lasttilehit == tilehit)
+	{
+	// in the same door as last time, so check for optimized draw
+		if (texture == (unsigned)postsource)
+		{
+		// wide scale
+			postwidth++;
+			wallheight[pixx] = wallheight[pixx-1];
+			return;
+		}
+		else
+		{
+			ScalePost ();
+			(unsigned)postsource = texture;
+			postwidth = 1;
+			postx = pixx;
+		}
+	}
+	else
+	{
+		if (lastside != -1)				// if not the first scaled post
+			ScalePost ();			// draw last post
+	// first pixel in this door
+		lastside = 2;
+		lasttilehit = tilehit;
+		postx = pixx;
+		postwidth = 1;
+
+		switch (doorobjlist[doornum].lock)
+		{
+		case dr_normal:
+			doorpage = DOORWALL;
+			break;
+		case dr_lock1:
+		case dr_lock2:
+		case dr_lock3:
+		case dr_lock4:
+			doorpage = DOORWALL+6;
+			break;
+		case dr_elevator:
+			doorpage = DOORWALL+4;
+			break;
+		}
+
+		*( ((unsigned *)&postsource)+1) = (unsigned)PM_GetPage(doorpage+1);
+		(unsigned)postsource = texture;
+	}
+}
+
+//==========================================================================
+
+
+/*
+====================
+=
+= HitHorizPWall
+=
+= A pushable wall in action has been hit
+=
+====================
+*/
+
+void HitHorizPWall (void)
+{
+	int			wallpic;
+	unsigned	texture,offset;
+
+	texture = (xintercept>>4)&0xfc0;
+	offset = pwallpos<<10;
+	if (ytilestep == -1)
+		yintercept += TILEGLOBAL-offset;
+	else
+	{
+		texture = 0xfc0-texture;
+		yintercept += offset;
+	}
+
+	wallheight[pixx] = CalcHeight();
+
+	if (lasttilehit == tilehit)
+	{
+		// in the same wall type as last time, so check for optimized draw
+		if (texture == (unsigned)postsource)
+		{
+		// wide scale
+			postwidth++;
+			wallheight[pixx] = wallheight[pixx-1];
+			return;
+		}
+		else
+		{
+			ScalePost ();
+			(unsigned)postsource = texture;
+			postwidth = 1;
+			postx = pixx;
+		}
+	}
+	else
+	{
+	// new wall
+		if (lastside != -1)				// if not the first scaled post
+			ScalePost ();
+
+		lasttilehit = tilehit;
+		postx = pixx;
+		postwidth = 1;
+
+		wallpic = horizwall[tilehit&63];
+
+		*( ((unsigned *)&postsource)+1) = (unsigned)PM_GetPage(wallpic);
+		(unsigned)postsource = texture;
+	}
+
+}
+
+
+/*
+====================
+=
+= HitVertPWall
+=
+= A pushable wall in action has been hit
+=
+====================
+*/
+
+void HitVertPWall (void)
+{
+	int			wallpic;
+	unsigned	texture,offset;
+
+	texture = (yintercept>>4)&0xfc0;
+	offset = pwallpos<<10;
+	if (xtilestep == -1)
+	{
+		xintercept += TILEGLOBAL-offset;
+		texture = 0xfc0-texture;
+	}
+	else
+		xintercept += offset;
+
+	wallheight[pixx] = CalcHeight();
+
+	if (lasttilehit == tilehit)
+	{
+		// in the same wall type as last time, so check for optimized draw
+		if (texture == (unsigned)postsource)
+		{
+		// wide scale
+			postwidth++;
+			wallheight[pixx] = wallheight[pixx-1];
+			return;
+		}
+		else
+		{
+			ScalePost ();
+			(unsigned)postsource = texture;
+			postwidth = 1;
+			postx = pixx;
+		}
+	}
+	else
+	{
+	// new wall
+		if (lastside != -1)				// if not the first scaled post
+			ScalePost ();
+
+		lasttilehit = tilehit;
+		postx = pixx;
+		postwidth = 1;
+
+		wallpic = vertwall[tilehit&63];
+
+		*( ((unsigned *)&postsource)+1) = (unsigned)PM_GetPage(wallpic);
+		(unsigned)postsource = texture;
+	}
+
+}
+
+//==========================================================================
+
+//==========================================================================
+
+#if 0
+/*
+=====================
+=
+= ClearScreen
+=
+=====================
+*/
+
+void ClearScreen (void)
+{
+ unsigned floor=egaFloor[gamestate.episode*10+mapon],
+	  ceiling=egaCeiling[gamestate.episode*10+mapon];
+
+  //
+  // clear the screen
+  //
+asm	mov	dx,GC_INDEX
+asm	mov	ax,GC_MODE + 256*2		// read mode 0, write mode 2
+asm	out	dx,ax
+asm	mov	ax,GC_BITMASK + 255*256
+asm	out	dx,ax
+
+asm	mov	dx,40
+asm	mov	ax,[viewwidth]
+asm	shr	ax,1
+asm	shr	ax,1
+asm	shr	ax,1
+asm	sub	dx,ax					// dx = 40-viewwidth/8
+
+asm	mov	bx,[viewwidth]
+asm	shr	bx,1					// bl = viewwidth/16
+asm	shr	bx,1
+asm	shr	bx,1
+asm	shr	bx,1
+asm	mov	bh,BYTE PTR [viewheight]
+asm	shr	bh,1					// half height
+
+asm	mov	ax,[ceiling]
+asm	mov	es,[screenseg]
+asm	mov	di,[bufferofs]
+
+toploop:
+asm	mov	cl,bl
+asm	rep	stosw
+asm	add	di,dx
+asm	dec	bh
+asm	jnz	toploop
+
+asm	mov	bh,BYTE PTR [viewheight]
+asm	shr	bh,1					// half height
+asm	mov	ax,[floor]
+
+bottomloop:
+asm	mov	cl,bl
+asm	rep	stosw
+asm	add	di,dx
+asm	dec	bh
+asm	jnz	bottomloop
+
+
+asm	mov	dx,GC_INDEX
+asm	mov	ax,GC_MODE + 256*10		// read mode 1, write mode 2
+asm	out	dx,ax
+asm	mov	al,GC_BITMASK
+asm	out	dx,al
+
+}
+#endif
+//==========================================================================
+
+unsigned vgaCeiling[]=
+{
+#ifndef SPEAR
+ 0x1d1d,0x1d1d,0x1d1d,0x1d1d,0x1d1d,0x1d1d,0x1d1d,0x1d1d,0x1d1d,0xbfbf,
+ 0x4e4e,0x4e4e,0x4e4e,0x1d1d,0x8d8d,0x4e4e,0x1d1d,0x2d2d,0x1d1d,0x8d8d,
+ 0x1d1d,0x1d1d,0x1d1d,0x1d1d,0x1d1d,0x2d2d,0xdddd,0x1d1d,0x1d1d,0x9898,
+
+ 0x1d1d,0x9d9d,0x2d2d,0xdddd,0xdddd,0x9d9d,0x2d2d,0x4d4d,0x1d1d,0xdddd,
+ 0x7d7d,0x1d1d,0x2d2d,0x2d2d,0xdddd,0xd7d7,0x1d1d,0x1d1d,0x1d1d,0x2d2d,
+ 0x1d1d,0x1d1d,0x1d1d,0x1d1d,0xdddd,0xdddd,0x7d7d,0xdddd,0xdddd,0xdddd
+#else
+ 0x6f6f,0x4f4f,0x1d1d,0xdede,0xdfdf,0x2e2e,0x7f7f,0x9e9e,0xaeae,0x7f7f,
+ 0x1d1d,0xdede,0xdfdf,0xdede,0xdfdf,0xdede,0xe1e1,0xdcdc,0x2e2e,0x1d1d,0xdcdc
+#endif
+};
+
+/*
+=====================
+=
+= VGAClearScreen
+=
+=====================
+*/
+
+void VGAClearScreen (void)
+{
+ unsigned ceiling=vgaCeiling[gamestate.episode*10+mapon];
+
+  //
+  // clear the screen
+  //
+asm	mov	dx,SC_INDEX
+asm	mov	ax,SC_MAPMASK+15*256	// write through all planes
+asm	out	dx,ax
+
+asm	mov	dx,80
+asm	mov	ax,[viewwidth]
+asm	shr	ax,1
+asm	shr	ax,1
+asm	sub	dx,ax					// dx = 40-viewwidth/2
+
+asm	mov	bx,[viewwidth]
+asm	shr	bx,1					// bl = viewwidth/8
+asm	shr	bx,1
+asm	shr	bx,1
+asm	mov	bh,BYTE PTR [viewheight]
+asm	shr	bh,1					// half height
+
+asm	mov	es,[screenseg]
+asm	mov	di,[bufferofs]
+asm	mov	ax,[ceiling]
+
+toploop:
+asm	mov	cl,bl
+asm	rep	stosw
+asm	add	di,dx
+asm	dec	bh
+asm	jnz	toploop
+
+asm	mov	bh,BYTE PTR [viewheight]
+asm	shr	bh,1					// half height
+asm	mov	ax,0x1919
+
+bottomloop:
+asm	mov	cl,bl
+asm	rep	stosw
+asm	add	di,dx
+asm	dec	bh
+asm	jnz	bottomloop
+}
+
+//==========================================================================
+
+/*
+=====================
+=
+= CalcRotate
+=
+=====================
+*/
+
+int	CalcRotate (objtype *ob)
+{
+	int	angle,viewangle;
+
+	// this isn't exactly correct, as it should vary by a trig value,
+	// but it is close enough with only eight rotations
+
+	viewangle = player->angle + (centerx - ob->viewx)/8;
+
+	if (ob->obclass == rocketobj || ob->obclass == hrocketobj)
+		angle =  (viewangle-180)- ob->angle;
+	else
+		angle =  (viewangle-180)- dirangle[ob->dir];
+
+	angle+=ANGLES/16;
+	while (angle>=ANGLES)
+		angle-=ANGLES;
+	while (angle<0)
+		angle+=ANGLES;
+
+	if (ob->state->rotate == 2)             // 2 rotation pain frame
+		return 4*(angle/(ANGLES/2));        // seperated by 3 (art layout...)
+
+	return angle/(ANGLES/8);
+}
+
+
+/*
+=====================
+=
+= DrawScaleds
+=
+= Draws all objects that are visable
+=
+=====================
+*/
+
+#define MAXVISABLE	50
+
+typedef struct
+{
+	int	viewx,
+		viewheight,
+		shapenum;
+} visobj_t;
+
+visobj_t	vislist[MAXVISABLE],*visptr,*visstep,*farthest;
+
+void DrawScaleds (void)
+{
+	int 		i,j,least,numvisable,height;
+	memptr		shape;
+	byte		*tilespot,*visspot;
+	int			shapenum;
+	unsigned	spotloc;
+
+	statobj_t	*statptr;
+	objtype		*obj;
+
+	visptr = &vislist[0];
+
+//
+// place static objects
+//
+	for (statptr = &statobjlist[0] ; statptr !=laststatobj ; statptr++)
+	{
+		if ((visptr->shapenum = statptr->shapenum) == -1)
+			continue;						// object has been deleted
+
+		if (!*statptr->visspot)
+			continue;						// not visable
+
+		if (TransformTile (statptr->tilex,statptr->tiley
+			,&visptr->viewx,&visptr->viewheight) && statptr->flags & FL_BONUS)
+		{
+			GetBonus (statptr);
+			continue;
+		}
+
+		if (!visptr->viewheight)
+			continue;						// to close to the object
+
+		if (visptr < &vislist[MAXVISABLE-1])	// don't let it overflow
+			visptr++;
+	}
+
+//
+// place active objects
+//
+	for (obj = player->next;obj;obj=obj->next)
+	{
+		if (!(visptr->shapenum = obj->state->shapenum))
+			continue;						// no shape
+
+		spotloc = (obj->tilex<<6)+obj->tiley;	// optimize: keep in struct?
+		visspot = &spotvis[0][0]+spotloc;
+		tilespot = &tilemap[0][0]+spotloc;
+
+		//
+		// could be in any of the nine surrounding tiles
+		//
+		if (*visspot
+		|| ( *(visspot-1) && !*(tilespot-1) )
+		|| ( *(visspot+1) && !*(tilespot+1) )
+		|| ( *(visspot-65) && !*(tilespot-65) )
+		|| ( *(visspot-64) && !*(tilespot-64) )
+		|| ( *(visspot-63) && !*(tilespot-63) )
+		|| ( *(visspot+65) && !*(tilespot+65) )
+		|| ( *(visspot+64) && !*(tilespot+64) )
+		|| ( *(visspot+63) && !*(tilespot+63) ) )
+		{
+			obj->active = true;
+			TransformActor (obj);
+			if (!obj->viewheight)
+				continue;						// too close or far away
+
+			visptr->viewx = obj->viewx;
+			visptr->viewheight = obj->viewheight;
+			if (visptr->shapenum == -1)
+				visptr->shapenum = obj->temp1;	// special shape
+
+			if (obj->state->rotate)
+				visptr->shapenum += CalcRotate (obj);
+
+			if (visptr < &vislist[MAXVISABLE-1])	// don't let it overflow
+				visptr++;
+			obj->flags |= FL_VISABLE;
+		}
+		else
+			obj->flags &= ~FL_VISABLE;
+	}
+
+//
+// draw from back to front
+//
+	numvisable = visptr-&vislist[0];
+
+	if (!numvisable)
+		return;									// no visable objects
+
+	for (i = 0; i<numvisable; i++)
+	{
+		least = 32000;
+		for (visstep=&vislist[0] ; visstep<visptr ; visstep++)
+		{
+			height = visstep->viewheight;
+			if (height < least)
+			{
+				least = height;
+				farthest = visstep;
+			}
+		}
+		//
+		// draw farthest
+		//
+		ScaleShape(farthest->viewx,farthest->shapenum,farthest->viewheight);
+
+		farthest->viewheight = 32000;
+	}
+
+}
+
+//==========================================================================
+
+/*
+==============
+=
+= DrawPlayerWeapon
+=
+= Draw the player's hands
+=
+==============
+*/
+
+int	weaponscale[NUMWEAPONS] = {SPR_KNIFEREADY,SPR_PISTOLREADY
+	,SPR_MACHINEGUNREADY,SPR_CHAINREADY};
+
+void DrawPlayerWeapon (void)
+{
+	int	shapenum;
+
+#ifndef SPEAR
+	if (gamestate.victoryflag)
+	{
+		if (player->state == &s_deathcam && (TimeCount&32) )
+			SimpleScaleShape(viewwidth/2,SPR_DEATHCAM,viewheight+1);
+		return;
+	}
+#endif
+
+	if (gamestate.weapon != -1)
+	{
+		shapenum = weaponscale[gamestate.weapon]+gamestate.weaponframe;
+		SimpleScaleShape(viewwidth/2,shapenum,viewheight+1);
+	}
+
+	if (demorecord || demoplayback)
+		SimpleScaleShape(viewwidth/2,SPR_DEMO,viewheight+1);
+}
+
+
+//==========================================================================
+
+
+/*
+=====================
+=
+= CalcTics
+=
+=====================
+*/
+
+void CalcTics (void)
+{
+	long	newtime,oldtimecount;
+
+//
+// calculate tics since last refresh for adaptive timing
+//
+	if (lasttimecount > TimeCount)
+		TimeCount = lasttimecount;		// if the game was paused a LONG time
+
+	do
+	{
+		newtime = TimeCount;
+		tics = newtime-lasttimecount;
+	} while (!tics);			// make sure at least one tic passes
+
+	lasttimecount = newtime;
+
+#ifdef FILEPROFILE
+		strcpy (scratch,"\tTics:");
+		itoa (tics,str,10);
+		strcat (scratch,str);
+		strcat (scratch,"\n");
+		write (profilehandle,scratch,strlen(scratch));
+#endif
+
+	if (tics>MAXTICS)
+	{
+		TimeCount -= (tics-MAXTICS);
+		tics = MAXTICS;
+	}
+}
+
+
+//==========================================================================
+
+
+/*
+========================
+=
+= FixOfs
+=
+========================
+*/
+
+void	FixOfs (void)
+{
+	VW_ScreenToScreen (displayofs,bufferofs,viewwidth/8,viewheight);
+}
+
+
+//==========================================================================
+
+
+/*
+====================
+=
+= WallRefresh
+=
+====================
+*/
+
+void WallRefresh (void)
+{
+//
+// set up variables for this view
+//
+	viewangle = player->angle;
+	midangle = viewangle*(FINEANGLES/ANGLES);
+	viewsin = sintable[viewangle];
+	viewcos = costable[viewangle];
+	viewx = player->x - FixedByFrac(focallength,viewcos);
+	viewy = player->y + FixedByFrac(focallength,viewsin);
+
+	focaltx = viewx>>TILESHIFT;
+	focalty = viewy>>TILESHIFT;
+
+	viewtx = player->x >> TILESHIFT;
+	viewty = player->y >> TILESHIFT;
+
+	xpartialdown = viewx&(TILEGLOBAL-1);
+	xpartialup = TILEGLOBAL-xpartialdown;
+	ypartialdown = viewy&(TILEGLOBAL-1);
+	ypartialup = TILEGLOBAL-ypartialdown;
+
+	lastside = -1;			// the first pixel is on a new wall
+	AsmRefresh ();
+	ScalePost ();			// no more optimization on last post
+}
+
+//==========================================================================
+
+/*
+========================
+=
+= ThreeDRefresh
+=
+========================
+*/
+
+void	ThreeDRefresh (void)
+{
+	int tracedir;
+
+// this wouldn't need to be done except for my debugger/video wierdness
+	outportb (SC_INDEX,SC_MAPMASK);
+
+//
+// clear out the traced array
+//
+asm	mov	ax,ds
+asm	mov	es,ax
+asm	mov	di,OFFSET spotvis
+asm	xor	ax,ax
+asm	mov	cx,2048							// 64*64 / 2
+asm	rep stosw
+
+	bufferofs += screenofs;
+
+//
+// follow the walls from there to the right, drawwing as we go
+//
+	VGAClearScreen ();
+
+	WallRefresh ();
+
+//
+// draw all the scaled images
+//
+	DrawScaleds();			// draw scaled stuff
+	DrawPlayerWeapon ();	// draw player's hands
+
+//
+// show screen and time last cycle
+//
+	if (fizzlein)
+	{
+		FizzleFade(bufferofs,displayofs+screenofs,viewwidth,viewheight,20,false);
+		fizzlein = false;
+
+		lasttimecount = TimeCount = 0;		// don't make a big tic count
+
+	}
+
+	bufferofs -= screenofs;
+	displayofs = bufferofs;
+
+	asm	cli
+	asm	mov	cx,[displayofs]
+	asm	mov	dx,3d4h		// CRTC address register
+	asm	mov	al,0ch		// start address high register
+	asm	out	dx,al
+	asm	inc	dx
+	asm	mov	al,ch
+	asm	out	dx,al   	// set the high byte
+	asm	sti
+
+	bufferofs += SCREENSIZE;
+	if (bufferofs > PAGE3START)
+		bufferofs = PAGE1START;
+
+	frameon++;
+	PM_NextFrame();
+}
+
+
+//===========================================================================
+
diff --git a/src/lib/hb/wl_game.c b/src/lib/hb/wl_game.c
new file mode 100755
index 00000000..9f63d9fe
--- /dev/null
+++ b/src/lib/hb/wl_game.c
@@ -0,0 +1,1484 @@
+// WL_GAME.C
+
+#include "WL_DEF.H"
+#pragma hdrstop
+
+#ifdef MYPROFILE
+#include <TIME.H>
+#endif
+
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+boolean		ingame,fizzlein;
+unsigned	latchpics[NUMLATCHPICS];
+gametype	gamestate;
+
+long		spearx,speary;
+unsigned	spearangle;
+boolean		spearflag;
+
+//
+// ELEVATOR BACK MAPS - REMEMBER (-1)!!
+//
+int ElevatorBackTo[]={1,1,7,3,5,3};
+
+void ScanInfoPlane (void);
+void SetupGameLevel (void);
+void DrawPlayScreen (void);
+void LoadLatchMem (void);
+void GameLoop (void);
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+
+
+//===========================================================================
+//===========================================================================
+
+
+/*
+==========================
+=
+= SetSoundLoc - Given the location of an object (in terms of global
+=	coordinates, held in globalsoundx and globalsoundy), munges the values
+=	for an approximate distance from the left and right ear, and puts
+=	those values into leftchannel and rightchannel.
+=
+= JAB
+=
+==========================
+*/
+
+	fixed	globalsoundx,globalsoundy;
+	int		leftchannel,rightchannel;
+#define ATABLEMAX 15
+byte righttable[ATABLEMAX][ATABLEMAX * 2] = {
+{ 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 6, 0, 0, 0, 0, 0, 1, 3, 5, 8, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 6, 4, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 6, 6, 4, 1, 0, 0, 0, 1, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 6, 5, 4, 2, 1, 0, 1, 2, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 6, 5, 4, 3, 2, 2, 3, 3, 5, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 6, 6, 5, 4, 4, 4, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 6, 6, 5, 5, 5, 6, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}
+};
+byte lefttable[ATABLEMAX][ATABLEMAX * 2] = {
+{ 8, 8, 8, 8, 8, 8, 8, 8, 5, 3, 1, 0, 0, 0, 0, 0, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 8, 6, 4, 2, 0, 0, 0, 0, 0, 4, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 8, 6, 4, 2, 1, 0, 0, 0, 1, 4, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 8, 7, 5, 3, 2, 1, 0, 1, 2, 4, 5, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 6, 5, 3, 3, 2, 2, 3, 4, 5, 6, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 6, 5, 4, 4, 4, 4, 5, 6, 6, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 6, 6, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 6, 6, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
+{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}
+};
+
+void
+SetSoundLoc(fixed gx,fixed gy)
+{
+	fixed	xt,yt;
+	int		x,y;
+
+//
+// translate point to view centered coordinates
+//
+	gx -= viewx;
+	gy -= viewy;
+
+//
+// calculate newx
+//
+	xt = FixedByFrac(gx,viewcos);
+	yt = FixedByFrac(gy,viewsin);
+	x = (xt - yt) >> TILESHIFT;
+
+//
+// calculate newy
+//
+	xt = FixedByFrac(gx,viewsin);
+	yt = FixedByFrac(gy,viewcos);
+	y = (yt + xt) >> TILESHIFT;
+
+	if (y >= ATABLEMAX)
+		y = ATABLEMAX - 1;
+	else if (y <= -ATABLEMAX)
+		y = -ATABLEMAX;
+	if (x < 0)
+		x = -x;
+	if (x >= ATABLEMAX)
+		x = ATABLEMAX - 1;
+	leftchannel  =  lefttable[x][y + ATABLEMAX];
+	rightchannel = righttable[x][y + ATABLEMAX];
+
+#if 0
+	CenterWindow(8,1);
+	US_PrintSigned(leftchannel);
+	US_Print(",");
+	US_PrintSigned(rightchannel);
+	VW_UpdateScreen();
+#endif
+}
+
+/*
+==========================
+=
+= SetSoundLocGlobal - Sets up globalsoundx & globalsoundy and then calls
+=	UpdateSoundLoc() to transform that into relative channel volumes. Those
+=	values are then passed to the Sound Manager so that they'll be used for
+=	the next sound played (if possible).
+=
+= JAB
+=
+==========================
+*/
+void PlaySoundLocGlobal(word s,fixed gx,fixed gy)
+{
+	SetSoundLoc(gx,gy);
+	SD_PositionSound(leftchannel,rightchannel);
+	if (SD_PlaySound(s))
+	{
+		globalsoundx = gx;
+		globalsoundy = gy;
+	}
+}
+
+void UpdateSoundLoc(void)
+{
+	if (SoundPositioned)
+	{
+		SetSoundLoc(globalsoundx,globalsoundy);
+		SD_SetPosition(leftchannel,rightchannel);
+	}
+}
+
+/*
+**	JAB End
+*/
+
+
+/*
+==========================
+=
+= ClearMemory
+=
+==========================
+*/
+
+void ClearMemory (void)
+{
+	PM_UnlockMainMem();
+	SD_StopDigitized();
+	MM_SortMem ();
+}
+
+
+/*
+==========================
+=
+= ScanInfoPlane
+=
+= Spawn all actors and mark down special places
+=
+==========================
+*/
+
+void ScanInfoPlane (void)
+{
+	unsigned	x,y,i,j;
+	int			tile;
+	unsigned	far	*start;
+
+	start = mapsegs[1];
+	for (y=0;y<mapheight;y++)
+		for (x=0;x<mapwidth;x++)
+		{
+			tile = *start++;
+			if (!tile)
+				continue;
+
+			switch (tile)
+			{
+			case 19:
+			case 20:
+			case 21:
+			case 22:
+				SpawnPlayer(x,y,NORTH+tile-19);
+				break;
+
+			case 23:
+			case 24:
+			case 25:
+			case 26:
+			case 27:
+			case 28:
+			case 29:
+			case 30:
+
+			case 31:
+			case 32:
+			case 33:
+			case 34:
+			case 35:
+			case 36:
+			case 37:
+			case 38:
+
+			case 39:
+			case 40:
+			case 41:
+			case 42:
+			case 43:
+			case 44:
+			case 45:
+			case 46:
+
+			case 47:
+			case 48:
+			case 49:
+			case 50:
+			case 51:
+			case 52:
+			case 53:
+			case 54:
+
+			case 55:
+			case 56:
+			case 57:
+			case 58:
+			case 59:
+			case 60:
+			case 61:
+			case 62:
+
+			case 63:
+			case 64:
+			case 65:
+			case 66:
+			case 67:
+			case 68:
+			case 69:
+			case 70:
+			case 71:
+			case 72:
+			case 73:						// TRUCK AND SPEAR!
+			case 74:
+
+				SpawnStatic(x,y,tile-23);
+				break;
+
+//
+// P wall
+//
+			case 98:
+				if (!loadedgame)
+				  gamestate.secrettotal++;
+				break;
+
+//
+// guard
+//
+			case 180:
+			case 181:
+			case 182:
+			case 183:
+				if (gamestate.difficulty<gd_hard)
+					break;
+				tile -= 36;
+			case 144:
+			case 145:
+			case 146:
+			case 147:
+				if (gamestate.difficulty<gd_medium)
+					break;
+				tile -= 36;
+			case 108:
+			case 109:
+			case 110:
+			case 111:
+				SpawnStand(en_guard,x,y,tile-108);
+				break;
+
+
+			case 184:
+			case 185:
+			case 186:
+			case 187:
+				if (gamestate.difficulty<gd_hard)
+					break;
+				tile -= 36;
+			case 148:
+			case 149:
+			case 150:
+			case 151:
+				if (gamestate.difficulty<gd_medium)
+					break;
+				tile -= 36;
+			case 112:
+			case 113:
+			case 114:
+			case 115:
+				SpawnPatrol(en_guard,x,y,tile-112);
+				break;
+
+			case 124:
+				SpawnDeadGuard (x,y);
+				break;
+//
+// officer
+//
+			case 188:
+			case 189:
+			case 190:
+			case 191:
+				if (gamestate.difficulty<gd_hard)
+					break;
+				tile -= 36;
+			case 152:
+			case 153:
+			case 154:
+			case 155:
+				if (gamestate.difficulty<gd_medium)
+					break;
+				tile -= 36;
+			case 116:
+			case 117:
+			case 118:
+			case 119:
+				SpawnStand(en_officer,x,y,tile-116);
+				break;
+
+
+			case 192:
+			case 193:
+			case 194:
+			case 195:
+				if (gamestate.difficulty<gd_hard)
+					break;
+				tile -= 36;
+			case 156:
+			case 157:
+			case 158:
+			case 159:
+				if (gamestate.difficulty<gd_medium)
+					break;
+				tile -= 36;
+			case 120:
+			case 121:
+			case 122:
+			case 123:
+				SpawnPatrol(en_officer,x,y,tile-120);
+				break;
+
+
+//
+// ss
+//
+			case 198:
+			case 199:
+			case 200:
+			case 201:
+				if (gamestate.difficulty<gd_hard)
+					break;
+				tile -= 36;
+			case 162:
+			case 163:
+			case 164:
+			case 165:
+				if (gamestate.difficulty<gd_medium)
+					break;
+				tile -= 36;
+			case 126:
+			case 127:
+			case 128:
+			case 129:
+				SpawnStand(en_ss,x,y,tile-126);
+				break;
+
+
+			case 202:
+			case 203:
+			case 204:
+			case 205:
+				if (gamestate.difficulty<gd_hard)
+					break;
+				tile -= 36;
+			case 166:
+			case 167:
+			case 168:
+			case 169:
+				if (gamestate.difficulty<gd_medium)
+					break;
+				tile -= 36;
+			case 130:
+			case 131:
+			case 132:
+			case 133:
+				SpawnPatrol(en_ss,x,y,tile-130);
+				break;
+
+//
+// dogs
+//
+			case 206:
+			case 207:
+			case 208:
+			case 209:
+				if (gamestate.difficulty<gd_hard)
+					break;
+				tile -= 36;
+			case 170:
+			case 171:
+			case 172:
+			case 173:
+				if (gamestate.difficulty<gd_medium)
+					break;
+				tile -= 36;
+			case 134:
+			case 135:
+			case 136:
+			case 137:
+				SpawnStand(en_dog,x,y,tile-134);
+				break;
+
+
+			case 210:
+			case 211:
+			case 212:
+			case 213:
+				if (gamestate.difficulty<gd_hard)
+					break;
+				tile -= 36;
+			case 174:
+			case 175:
+			case 176:
+			case 177:
+				if (gamestate.difficulty<gd_medium)
+					break;
+				tile -= 36;
+			case 138:
+			case 139:
+			case 140:
+			case 141:
+				SpawnPatrol(en_dog,x,y,tile-138);
+				break;
+
+//
+// boss
+//
+#ifndef SPEAR
+			case 214:
+				SpawnBoss (x,y);
+				break;
+			case 197:
+				SpawnGretel (x,y);
+				break;
+			case 215:
+				SpawnGift (x,y);
+				break;
+			case 179:
+				SpawnFat (x,y);
+				break;
+			case 196:
+				SpawnSchabbs (x,y);
+				break;
+			case 160:
+				SpawnFakeHitler (x,y);
+				break;
+			case 178:
+				SpawnHitler (x,y);
+				break;
+#else
+			case 106:
+				SpawnSpectre (x,y);
+				break;
+			case 107:
+				SpawnAngel (x,y);
+				break;
+			case 125:
+				SpawnTrans (x,y);
+				break;
+			case 142:
+				SpawnUber (x,y);
+				break;
+			case 143:
+				SpawnWill (x,y);
+				break;
+			case 161:
+				SpawnDeath (x,y);
+				break;
+
+#endif
+
+//
+// mutants
+//
+			case 252:
+			case 253:
+			case 254:
+			case 255:
+				if (gamestate.difficulty<gd_hard)
+					break;
+				tile -= 18;
+			case 234:
+			case 235:
+			case 236:
+			case 237:
+				if (gamestate.difficulty<gd_medium)
+					break;
+				tile -= 18;
+			case 216:
+			case 217:
+			case 218:
+			case 219:
+				SpawnStand(en_mutant,x,y,tile-216);
+				break;
+
+			case 256:
+			case 257:
+			case 258:
+			case 259:
+				if (gamestate.difficulty<gd_hard)
+					break;
+				tile -= 18;
+			case 238:
+			case 239:
+			case 240:
+			case 241:
+				if (gamestate.difficulty<gd_medium)
+					break;
+				tile -= 18;
+			case 220:
+			case 221:
+			case 222:
+			case 223:
+				SpawnPatrol(en_mutant,x,y,tile-220);
+				break;
+
+//
+// ghosts
+//
+#ifndef SPEAR
+			case 224:
+				SpawnGhosts (en_blinky,x,y);
+				break;
+			case 225:
+				SpawnGhosts (en_clyde,x,y);
+				break;
+			case 226:
+				SpawnGhosts (en_pinky,x,y);
+				break;
+			case 227:
+				SpawnGhosts (en_inky,x,y);
+				break;
+#endif
+			}
+
+		}
+}
+
+//==========================================================================
+
+/*
+==================
+=
+= SetupGameLevel
+=
+==================
+*/
+
+void SetupGameLevel (void)
+{
+	int	x,y,i;
+	unsigned	far *map,tile,spot;
+
+
+	if (!loadedgame)
+	{
+	 gamestate.TimeCount=
+	 gamestate.secrettotal=
+	 gamestate.killtotal=
+	 gamestate.treasuretotal=
+	 gamestate.secretcount=
+	 gamestate.killcount=
+	 gamestate.treasurecount=0;
+	}
+
+	if (demoplayback || demorecord)
+		US_InitRndT (false);
+	else
+		US_InitRndT (true);
+
+//
+// load the level
+//
+	CA_CacheMap (gamestate.mapon+10*gamestate.episode);
+	mapon-=gamestate.episode*10;
+
+	mapwidth = mapheaderseg[mapon]->width;
+	mapheight = mapheaderseg[mapon]->height;
+
+	if (mapwidth != 64 || mapheight != 64)
+		Quit ("Map not 64*64!");
+
+
+//
+// copy the wall data to a data segment array
+//
+	memset (tilemap,0,sizeof(tilemap));
+	memset (actorat,0,sizeof(actorat));
+	map = mapsegs[0];
+	for (y=0;y<mapheight;y++)
+		for (x=0;x<mapwidth;x++)
+		{
+			tile = *map++;
+			if (tile<AREATILE)
+			{
+			// solid wall
+				tilemap[x][y] = tile;
+				(unsigned)actorat[x][y] = tile;
+			}
+			else
+			{
+			// area floor
+				tilemap[x][y] = 0;
+				(unsigned)actorat[x][y] = 0;
+			}
+		}
+
+//
+// spawn doors
+//
+	InitActorList ();			// start spawning things with a clean slate
+	InitDoorList ();
+	InitStaticList ();
+
+	map = mapsegs[0];
+	for (y=0;y<mapheight;y++)
+		for (x=0;x<mapwidth;x++)
+		{
+			tile = *map++;
+			if (tile >= 90 && tile <= 101)
+			{
+			// door
+				switch (tile)
+				{
+				case 90:
+				case 92:
+				case 94:
+				case 96:
+				case 98:
+				case 100:
+					SpawnDoor (x,y,1,(tile-90)/2);
+					break;
+				case 91:
+				case 93:
+				case 95:
+				case 97:
+				case 99:
+				case 101:
+					SpawnDoor (x,y,0,(tile-91)/2);
+					break;
+				}
+			}
+		}
+
+//
+// spawn actors
+//
+	ScanInfoPlane ();
+
+//
+// take out the ambush markers
+//
+	map = mapsegs[0];
+	for (y=0;y<mapheight;y++)
+		for (x=0;x<mapwidth;x++)
+		{
+			tile = *map++;
+			if (tile == AMBUSHTILE)
+			{
+				tilemap[x][y] = 0;
+				if ( (unsigned)actorat[x][y] == AMBUSHTILE)
+					actorat[x][y] = NULL;
+
+				if (*map >= AREATILE)
+					tile = *map;
+				if (*(map-1-mapwidth) >= AREATILE)
+					tile = *(map-1-mapwidth);
+				if (*(map-1+mapwidth) >= AREATILE)
+					tile = *(map-1+mapwidth);
+				if ( *(map-2) >= AREATILE)
+					tile = *(map-2);
+
+				*(map-1) = tile;
+			}
+		}
+
+
+
+//
+// have the caching manager load and purge stuff to make sure all marks
+// are in memory
+//
+	CA_LoadAllSounds ();
+
+}
+
+
+//==========================================================================
+
+
+/*
+===================
+=
+= DrawPlayBorderSides
+=
+= To fix window overwrites
+=
+===================
+*/
+
+void DrawPlayBorderSides (void)
+{
+	int	xl,yl;
+
+	xl = 160-viewwidth/2;
+	yl = (200-STATUSLINES-viewheight)/2;
+
+	VWB_Bar (0,0,xl-1,200-STATUSLINES,127);
+	VWB_Bar (xl+viewwidth+1,0,xl-2,200-STATUSLINES,127);
+
+	VWB_Vlin (yl-1,yl+viewheight,xl-1,0);
+	VWB_Vlin (yl-1,yl+viewheight,xl+viewwidth,125);
+}
+
+
+/*
+===================
+=
+= DrawAllPlayBorderSides
+=
+===================
+*/
+
+void DrawAllPlayBorderSides (void)
+{
+	unsigned	i,temp;
+
+	temp = bufferofs;
+	for (i=0;i<3;i++)
+	{
+		bufferofs = screenloc[i];
+		DrawPlayBorderSides ();
+	}
+	bufferofs = temp;
+}
+
+/*
+===================
+=
+= DrawPlayBorder
+=
+===================
+*/
+void DrawAllPlayBorder (void)
+{
+	unsigned	i,temp;
+
+	temp = bufferofs;
+	for (i=0;i<3;i++)
+	{
+		bufferofs = screenloc[i];
+		DrawPlayBorder ();
+	}
+	bufferofs = temp;
+}
+
+/*
+===================
+=
+= DrawPlayBorder
+=
+===================
+*/
+
+void DrawPlayBorder (void)
+{
+	int	xl,yl;
+
+	VWB_Bar (0,0,320,200-STATUSLINES,127);
+
+	xl = 160-viewwidth/2;
+	yl = (200-STATUSLINES-viewheight)/2;
+	VWB_Bar (xl,yl,viewwidth,viewheight,0);
+
+	VWB_Hlin (xl-1,xl+viewwidth,yl-1,0);
+	VWB_Hlin (xl-1,xl+viewwidth,yl+viewheight,125);
+	VWB_Vlin (yl-1,yl+viewheight,xl-1,0);
+	VWB_Vlin (yl-1,yl+viewheight,xl+viewwidth,125);
+	VWB_Plot (xl-1,yl+viewheight,124);
+}
+
+
+
+/*
+===================
+=
+= DrawPlayScreen
+=
+===================
+*/
+
+void DrawPlayScreen (void)
+{
+	int	i,j,p,m;
+	unsigned	temp;
+
+	VW_FadeOut ();
+
+	temp = bufferofs;
+
+	CA_CacheGrChunk (STATUSBARPIC);
+
+	for (i=0;i<3;i++)
+	{
+		bufferofs = screenloc[i];
+		DrawPlayBorder ();
+		VWB_DrawPic (0,200-STATUSLINES,STATUSBARPIC);
+	}
+
+	bufferofs = temp;
+
+	UNCACHEGRCHUNK (STATUSBARPIC);
+
+	DrawFace ();
+	DrawHealth ();
+	DrawLives ();
+	DrawLevel ();
+	DrawAmmo ();
+	DrawKeys ();
+	DrawWeapon ();
+	DrawScore ();
+}
+
+
+
+//==========================================================================
+
+/*
+==================
+=
+= StartDemoRecord
+=
+==================
+*/
+
+#define MAXDEMOSIZE	8192
+
+void StartDemoRecord (int levelnumber)
+{
+	MM_GetPtr (&demobuffer,MAXDEMOSIZE);
+	MM_SetLock (&demobuffer,true);
+	demoptr = (char far *)demobuffer;
+	lastdemoptr = demoptr+MAXDEMOSIZE;
+
+	*demoptr = levelnumber;
+	demoptr += 4;				// leave space for length
+	demorecord = true;
+}
+
+
+/*
+==================
+=
+= FinishDemoRecord
+=
+==================
+*/
+
+char	demoname[13] = "DEMO?.";
+
+void FinishDemoRecord (void)
+{
+	long	length,level;
+
+	demorecord = false;
+
+	length = demoptr - (char far *)demobuffer;
+
+	demoptr = ((char far *)demobuffer)+1;
+	*(unsigned far *)demoptr = length;
+
+	CenterWindow(24,3);
+	PrintY+=6;
+	US_Print(" Demo number (0-9):");
+	VW_UpdateScreen();
+
+	if (US_LineInput (px,py,str,NULL,true,2,0))
+	{
+		level = atoi (str);
+		if (level>=0 && level<=9)
+		{
+			demoname[4] = '0'+level;
+			CA_WriteFile (demoname,(void far *)demobuffer,length);
+		}
+	}
+
+
+	MM_FreePtr (&demobuffer);
+}
+
+//==========================================================================
+
+/*
+==================
+=
+= RecordDemo
+=
+= Fades the screen out, then starts a demo.  Exits with the screen faded
+=
+==================
+*/
+
+void RecordDemo (void)
+{
+	int level,esc;
+
+	CenterWindow(26,3);
+	PrintY+=6;
+	CA_CacheGrChunk(STARTFONT);
+	fontnumber=0;
+	US_Print("  Demo which level(1-10):");
+	VW_UpdateScreen();
+	VW_FadeIn ();
+	esc = !US_LineInput (px,py,str,NULL,true,2,0);
+	if (esc)
+		return;
+
+	level = atoi (str);
+	level--;
+
+	SETFONTCOLOR(0,15);
+	VW_FadeOut ();
+
+#ifndef SPEAR
+	NewGame (gd_hard,level/10);
+	gamestate.mapon = level%10;
+#else
+	NewGame (gd_hard,0);
+	gamestate.mapon = level;
+#endif
+
+	StartDemoRecord (level);
+
+	DrawPlayScreen ();
+	VW_FadeIn ();
+
+	startgame = false;
+	demorecord = true;
+
+	SetupGameLevel ();
+	StartMusic ();
+	PM_CheckMainMem ();
+	fizzlein = true;
+
+	PlayLoop ();
+
+	demoplayback = false;
+
+	StopMusic ();
+	VW_FadeOut ();
+	ClearMemory ();
+
+	FinishDemoRecord ();
+}
+
+//==========================================================================
+
+/*
+==================
+=
+= PlayDemo
+=
+= Fades the screen out, then starts a demo.  Exits with the screen faded
+=
+==================
+*/
+
+void PlayDemo (int demonumber)
+{
+	int length;
+
+#ifdef DEMOSEXTERN
+// debug: load chunk
+#ifndef SPEARDEMO
+	int dems[4]={T_DEMO0,T_DEMO1,T_DEMO2,T_DEMO3};
+#else
+	int dems[1]={T_DEMO0};
+#endif
+
+	CA_CacheGrChunk(dems[demonumber]);
+	demoptr = grsegs[dems[demonumber]];
+	MM_SetLock (&grsegs[dems[demonumber]],true);
+#else
+	demoname[4] = '0'+demonumber;
+	CA_LoadFile (demoname,&demobuffer);
+	MM_SetLock (&demobuffer,true);
+	demoptr = (char far *)demobuffer;
+#endif
+
+	NewGame (1,0);
+	gamestate.mapon = *demoptr++;
+	gamestate.difficulty = gd_hard;
+	length = *((unsigned far *)demoptr)++;
+	demoptr++;
+	lastdemoptr = demoptr-4+length;
+
+	VW_FadeOut ();
+
+	SETFONTCOLOR(0,15);
+	DrawPlayScreen ();
+	VW_FadeIn ();
+
+	startgame = false;
+	demoplayback = true;
+
+	SetupGameLevel ();
+	StartMusic ();
+	PM_CheckMainMem ();
+	fizzlein = true;
+
+	PlayLoop ();
+
+#ifdef DEMOSEXTERN
+	UNCACHEGRCHUNK(dems[demonumber]);
+#else
+	MM_FreePtr (&demobuffer);
+#endif
+
+	demoplayback = false;
+
+	StopMusic ();
+	VW_FadeOut ();
+	ClearMemory ();
+}
+
+//==========================================================================
+
+/*
+==================
+=
+= Died
+=
+==================
+*/
+
+#define DEATHROTATE 2
+
+void Died (void)
+{
+	float	fangle;
+	long	dx,dy;
+	int		iangle,curangle,clockwise,counter,change;
+
+	gamestate.weapon = -1;			// take away weapon
+	SD_PlaySound (PLAYERDEATHSND);
+//
+// swing around to face attacker
+//
+	dx = killerobj->x - player->x;
+	dy = player->y - killerobj->y;
+
+	fangle = atan2(dy,dx);			// returns -pi to pi
+	if (fangle<0)
+		fangle = M_PI*2+fangle;
+
+	iangle = fangle/(M_PI*2)*ANGLES;
+
+	if (player->angle > iangle)
+	{
+		counter = player->angle - iangle;
+		clockwise = ANGLES-player->angle + iangle;
+	}
+	else
+	{
+		clockwise = iangle - player->angle;
+		counter = player->angle + ANGLES-iangle;
+	}
+
+	curangle = player->angle;
+
+	if (clockwise<counter)
+	{
+	//
+	// rotate clockwise
+	//
+		if (curangle>iangle)
+			curangle -= ANGLES;
+		do
+		{
+			change = tics*DEATHROTATE;
+			if (curangle + change > iangle)
+				change = iangle-curangle;
+
+			curangle += change;
+			player->angle += change;
+			if (player->angle >= ANGLES)
+				player->angle -= ANGLES;
+
+			ThreeDRefresh ();
+			CalcTics ();
+		} while (curangle != iangle);
+	}
+	else
+	{
+	//
+	// rotate counterclockwise
+	//
+		if (curangle<iangle)
+			curangle += ANGLES;
+		do
+		{
+			change = -tics*DEATHROTATE;
+			if (curangle + change < iangle)
+				change = iangle-curangle;
+
+			curangle += change;
+			player->angle += change;
+			if (player->angle < 0)
+				player->angle += ANGLES;
+
+			ThreeDRefresh ();
+			CalcTics ();
+		} while (curangle != iangle);
+	}
+
+//
+// fade to red
+//
+	FinishPaletteShifts ();
+
+	bufferofs += screenofs;
+	VW_Bar (0,0,viewwidth,viewheight,4);
+	IN_ClearKeysDown ();
+	FizzleFade(bufferofs,displayofs+screenofs,viewwidth,viewheight,70,false);
+	bufferofs -= screenofs;
+	IN_UserInput(100);
+	SD_WaitSoundDone ();
+
+	if (tedlevel == false)	// SO'S YA DON'T GET KILLED WHILE LAUNCHING!
+	  gamestate.lives--;
+
+	if (gamestate.lives > -1)
+	{
+		gamestate.health = 100;
+		gamestate.weapon = gamestate.bestweapon
+			= gamestate.chosenweapon = wp_pistol;
+		gamestate.ammo = STARTAMMO;
+		gamestate.keys = 0;
+		gamestate.attackframe = gamestate.attackcount =
+		gamestate.weaponframe = 0;
+
+		DrawKeys ();
+		DrawWeapon ();
+		DrawAmmo ();
+		DrawHealth ();
+		DrawFace ();
+		DrawLives ();
+	}
+
+}
+
+//==========================================================================
+
+/*
+===================
+=
+= GameLoop
+=
+===================
+*/
+
+void GameLoop (void)
+{
+	int i,xl,yl,xh,yh;
+	char num[20];
+	boolean	died;
+#ifdef MYPROFILE
+	clock_t start,end;
+#endif
+
+restartgame:
+	ClearMemory ();
+	SETFONTCOLOR(0,15);
+	DrawPlayScreen ();
+	died = false;
+restart:
+	do
+	{
+		if (!loadedgame)
+		  gamestate.score = gamestate.oldscore;
+		DrawScore();
+
+		startgame = false;
+		if (loadedgame)
+			loadedgame = false;
+		else
+			SetupGameLevel ();
+
+#ifdef SPEAR
+		if (gamestate.mapon == 20)	// give them the key allways
+		{
+			gamestate.keys |= 1;
+			DrawKeys ();
+		}
+#endif
+
+		ingame = true;
+		StartMusic ();
+		PM_CheckMainMem ();
+		if (!died)
+			PreloadGraphics ();
+		else
+			died = false;
+
+		fizzlein = true;
+		DrawLevel ();
+
+startplayloop:
+		PlayLoop ();
+
+#ifdef SPEAR
+		if (spearflag)
+		{
+			SD_StopSound();
+			SD_PlaySound(GETSPEARSND);
+			if (DigiMode != sds_Off)
+			{
+				long lasttimecount = TimeCount;
+
+				while(TimeCount < lasttimecount+150)
+				//while(DigiPlaying!=false)
+					SD_Poll();
+			}
+			else
+				SD_WaitSoundDone();
+
+			ClearMemory ();
+			gamestate.oldscore = gamestate.score;
+			gamestate.mapon = 20;
+			SetupGameLevel ();
+			StartMusic ();
+			PM_CheckMainMem ();
+			player->x = spearx;
+			player->y = speary;
+			player->angle = spearangle;
+			spearflag = false;
+			Thrust (0,0);
+			goto startplayloop;
+		}
+#endif
+
+		StopMusic ();
+		ingame = false;
+
+		if (demorecord && playstate != ex_warped)
+			FinishDemoRecord ();
+
+		if (startgame || loadedgame)
+			goto restartgame;
+
+		switch (playstate)
+		{
+		case ex_completed:
+		case ex_secretlevel:
+			gamestate.keys = 0;
+			DrawKeys ();
+			VW_FadeOut ();
+
+			ClearMemory ();
+
+			LevelCompleted ();		// do the intermission
+#ifdef SPEARDEMO
+			if (gamestate.mapon == 1)
+			{
+				died = true;			// don't "get psyched!"
+
+				VW_FadeOut ();
+
+				ClearMemory ();
+
+				CheckHighScore (gamestate.score,gamestate.mapon+1);
+
+				#pragma warn -sus
+				#ifndef JAPAN
+				_fstrcpy(MainMenu[viewscores].string,STR_VS);
+				#endif
+				MainMenu[viewscores].routine = CP_ViewScores;
+				#pragma warn +sus
+
+				return;
+			}
+#endif
+
+#ifdef JAPDEMO
+			if (gamestate.mapon == 3)
+			{
+				died = true;			// don't "get psyched!"
+
+				VW_FadeOut ();
+
+				ClearMemory ();
+
+				CheckHighScore (gamestate.score,gamestate.mapon+1);
+
+				#pragma warn -sus
+				#ifndef JAPAN
+				_fstrcpy(MainMenu[viewscores].string,STR_VS);
+				#endif
+				MainMenu[viewscores].routine = CP_ViewScores;
+				#pragma warn +sus
+
+				return;
+			}
+#endif
+
+			gamestate.oldscore = gamestate.score;
+
+#ifndef SPEAR
+			//
+			// COMING BACK FROM SECRET LEVEL
+			//
+			if (gamestate.mapon == 9)
+				gamestate.mapon = ElevatorBackTo[gamestate.episode];	// back from secret
+			else
+			//
+			// GOING TO SECRET LEVEL
+			//
+			if (playstate == ex_secretlevel)
+				gamestate.mapon = 9;
+#else
+
+#define FROMSECRET1		3
+#define FROMSECRET2		11
+
+			//
+			// GOING TO SECRET LEVEL
+			//
+			if (playstate == ex_secretlevel)
+				switch(gamestate.mapon)
+				{
+				 case FROMSECRET1: gamestate.mapon = 18; break;
+				 case FROMSECRET2: gamestate.mapon = 19; break;
+				}
+			else
+			//
+			// COMING BACK FROM SECRET LEVEL
+			//
+			if (gamestate.mapon == 18 || gamestate.mapon == 19)
+				switch(gamestate.mapon)
+				{
+				 case 18: gamestate.mapon = FROMSECRET1+1; break;
+				 case 19: gamestate.mapon = FROMSECRET2+1; break;
+				}
+#endif
+			else
+			//
+			// GOING TO NEXT LEVEL
+			//
+				gamestate.mapon++;
+
+
+			break;
+
+		case ex_died:
+			Died ();
+			died = true;			// don't "get psyched!"
+
+			if (gamestate.lives > -1)
+				break;				// more lives left
+
+			VW_FadeOut ();
+
+			ClearMemory ();
+
+			CheckHighScore (gamestate.score,gamestate.mapon+1);
+
+			#pragma warn -sus
+			#ifndef JAPAN
+			_fstrcpy(MainMenu[viewscores].string,STR_VS);
+			#endif
+			MainMenu[viewscores].routine = CP_ViewScores;
+			#pragma warn +sus
+
+			return;
+
+		case ex_victorious:
+
+#ifndef SPEAR
+			VW_FadeOut ();
+#else
+			VL_FadeOut (0,255,0,17,17,300);
+#endif
+			ClearMemory ();
+
+			Victory ();
+
+			ClearMemory ();
+
+			CheckHighScore (gamestate.score,gamestate.mapon+1);
+
+			#pragma warn -sus
+			#ifndef JAPAN
+			_fstrcpy(MainMenu[viewscores].string,STR_VS);
+			#endif
+			MainMenu[viewscores].routine = CP_ViewScores;
+			#pragma warn +sus
+
+			return;
+
+		default:
+			ClearMemory ();
+			break;
+		}
+
+	} while (1);
+
+}
+
diff --git a/src/lib/hb/wl_inter.c b/src/lib/hb/wl_inter.c
new file mode 100755
index 00000000..c74cc9e6
--- /dev/null
+++ b/src/lib/hb/wl_inter.c
@@ -0,0 +1,1718 @@
+// WL_INTER.C
+
+#include "WL_DEF.H"
+#pragma hdrstop
+
+
+//==========================================================================
+
+/*
+==================
+=
+= CLearSplitVWB
+=
+==================
+*/
+
+void ClearSplitVWB (void)
+{
+	memset (update,0,sizeof(update));
+	WindowX = 0;
+	WindowY = 0;
+	WindowW = 320;
+	WindowH = 160;
+}
+
+
+//==========================================================================
+
+#ifdef SPEAR
+#ifndef SPEARDEMO
+////////////////////////////////////////////////////////
+//
+// End of Spear of Destiny
+//
+////////////////////////////////////////////////////////
+
+void EndScreen (int palette, int screen)
+{
+	CA_CacheScreen (screen);
+	VW_UpdateScreen ();
+	CA_CacheGrChunk (palette);
+	VL_FadeIn(0,255,grsegs[palette],30);
+	UNCACHEGRCHUNK (palette);
+	IN_ClearKeysDown ();
+	IN_Ack ();
+	VW_FadeOut ();
+}
+
+
+void EndSpear(void)
+{
+	EndScreen (END1PALETTE, ENDSCREEN11PIC);
+
+	CA_CacheScreen (ENDSCREEN3PIC);
+	VW_UpdateScreen ();
+	CA_CacheGrChunk (END3PALETTE);
+	VL_FadeIn(0,255,grsegs[END3PALETTE],30);
+	UNCACHEGRCHUNK (END3PALETTE);
+	fontnumber = 0;
+	fontcolor = 0xd0;
+	WindowX = 0;
+	WindowW = 320;
+	PrintX = 0;
+	PrintY = 180;
+	US_CPrint (STR_ENDGAME1"\n");
+	US_CPrint (STR_ENDGAME2);
+	VW_UpdateScreen ();
+	IN_StartAck ();
+	TimeCount = 0;
+	while (!IN_CheckAck () && TimeCount < 700);
+
+	PrintX = 0;
+	PrintY = 180;
+	VWB_Bar(0,180,320,20,0);
+	US_CPrint (STR_ENDGAME3"\n");
+	US_CPrint (STR_ENDGAME4);
+	VW_UpdateScreen ();
+	IN_StartAck ();
+	TimeCount = 0;
+	while (!IN_CheckAck () && TimeCount < 700);
+
+	VW_FadeOut ();
+
+	EndScreen (END4PALETTE, ENDSCREEN4PIC);
+	EndScreen (END5PALETTE, ENDSCREEN5PIC);
+	EndScreen (END6PALETTE, ENDSCREEN6PIC);
+	EndScreen (END7PALETTE, ENDSCREEN7PIC);
+	EndScreen (END8PALETTE, ENDSCREEN8PIC);
+	EndScreen (END9PALETTE, ENDSCREEN9PIC);
+
+	EndScreen (END2PALETTE, ENDSCREEN12PIC);
+
+	MainMenu[savegame].active = 0;
+}
+#endif
+#endif
+
+//==========================================================================
+
+/*
+==================
+=
+= Victory
+=
+==================
+*/
+
+void Victory (void)
+{
+#ifndef SPEARDEMO
+	long	sec;
+	int i,min,kr,sr,tr,x;
+	char tempstr[8];
+
+#define RATIOX	6
+#define RATIOY	14
+#define TIMEX	14
+#define TIMEY	8
+
+
+#ifdef SPEAR
+	StartCPMusic (XTHEEND_MUS);
+
+	CA_CacheGrChunk(BJCOLLAPSE1PIC);
+	CA_CacheGrChunk(BJCOLLAPSE2PIC);
+	CA_CacheGrChunk(BJCOLLAPSE3PIC);
+	CA_CacheGrChunk(BJCOLLAPSE4PIC);
+
+	VWB_Bar(0,0,320,200,VIEWCOLOR);
+	VWB_DrawPic (124,44,BJCOLLAPSE1PIC);
+	VW_UpdateScreen ();
+	VW_FadeIn ();
+	VW_WaitVBL(2*70);
+	VWB_DrawPic (124,44,BJCOLLAPSE2PIC);
+	VW_UpdateScreen ();
+	VW_WaitVBL(105);
+	VWB_DrawPic (124,44,BJCOLLAPSE3PIC);
+	VW_UpdateScreen ();
+	VW_WaitVBL(105);
+	VWB_DrawPic (124,44,BJCOLLAPSE4PIC);
+	VW_UpdateScreen ();
+	VW_WaitVBL(3*70);
+
+	UNCACHEGRCHUNK(BJCOLLAPSE1PIC);
+	UNCACHEGRCHUNK(BJCOLLAPSE2PIC);
+	UNCACHEGRCHUNK(BJCOLLAPSE3PIC);
+	UNCACHEGRCHUNK(BJCOLLAPSE4PIC);
+	VL_FadeOut (0,255,0,17,17,5);
+#endif
+
+	StartCPMusic (URAHERO_MUS);
+	ClearSplitVWB ();
+	CacheLump(LEVELEND_LUMP_START,LEVELEND_LUMP_END);
+	CA_CacheGrChunk(STARTFONT);
+
+#ifndef SPEAR
+	CA_CacheGrChunk(C_TIMECODEPIC);
+#endif
+
+
+	VWB_Bar (0,0,320,200-STATUSLINES,127);
+#ifdef JAPAN
+#ifndef JAPDEMO
+	CA_CacheGrChunk(C_ENDRATIOSPIC);
+	VWB_DrawPic(0,0,C_ENDRATIOSPIC);
+	UNCACHEGRCHUNK(C_ENDRATIOSPIC);
+#endif
+#else
+	Write(18,2,STR_YOUWIN);
+
+	Write(TIMEX,TIMEY-2,STR_TOTALTIME);
+
+	Write(12,RATIOY-2,"averages");
+
+	#ifdef SPANISH
+	Write(RATIOX+2,  RATIOY,      STR_RATKILL);
+	Write(RATIOX+2,  RATIOY+2,  STR_RATSECRET);
+	Write(RATIOX+2,  RATIOY+4,STR_RATTREASURE);
+	#else
+	Write(RATIOX+8,RATIOY,      STR_RATKILL);
+	Write(RATIOX+4,RATIOY+2,  STR_RATSECRET);
+	Write(RATIOX,  RATIOY+4,STR_RATTREASURE);
+	#endif
+
+#endif
+
+#ifndef JAPDEMO
+	VWB_DrawPic (8,4,L_BJWINSPIC);
+#endif
+
+
+#ifndef SPEAR
+	for (kr = sr = tr = sec = i = 0;i < 8;i++)
+#else
+	for (kr = sr = tr = sec = i = 0;i < 20;i++)
+#endif
+	{
+		sec += LevelRatios[i].time;
+		kr += LevelRatios[i].kill;
+		sr += LevelRatios[i].secret;
+		tr += LevelRatios[i].treasure;
+	}
+
+#ifndef SPEAR
+	kr /= 8;
+	sr /= 8;
+	tr /= 8;
+#else
+	kr /= 14;
+	sr /= 14;
+	tr /= 14;
+#endif
+
+	min = sec/60;
+	sec %= 60;
+
+	if (min > 99)
+		min = sec = 99;
+
+	i = TIMEX*8+1;
+	VWB_DrawPic(i,TIMEY*8,L_NUM0PIC+(min/10));
+	i += 2*8;
+	VWB_DrawPic(i,TIMEY*8,L_NUM0PIC+(min%10));
+	i += 2*8;
+	Write(i/8,TIMEY,":");
+	i += 1*8;
+	VWB_DrawPic(i,TIMEY*8,L_NUM0PIC+(sec/10));
+	i += 2*8;
+	VWB_DrawPic(i,TIMEY*8,L_NUM0PIC+(sec%10));
+	VW_UpdateScreen ();
+
+	itoa(kr,tempstr,10);
+	x=RATIOX+24-strlen(tempstr)*2;
+	Write(x,RATIOY,tempstr);
+
+	itoa(sr,tempstr,10);
+	x=RATIOX+24-strlen(tempstr)*2;
+	Write(x,RATIOY+2,tempstr);
+
+	itoa(tr,tempstr,10);
+	x=RATIOX+24-strlen(tempstr)*2;
+	Write(x,RATIOY+4,tempstr);
+
+
+#ifndef SPANISH
+#ifndef UPLOAD
+#ifndef SPEAR
+	//
+	// TOTAL TIME VERIFICATION CODE
+	//
+	if (gamestate.difficulty>=gd_medium)
+	{
+		VWB_DrawPic (30*8,TIMEY*8,C_TIMECODEPIC);
+		fontnumber = 0;
+		fontcolor = READHCOLOR;
+		PrintX = 30*8-3;
+		PrintY = TIMEY*8+8;
+		PrintX+=4;
+		tempstr[0] = (((min/10)^(min%10))^0xa)+'A';
+		tempstr[1] = (((sec/10)^(sec%10))^0xa)+'A';
+		tempstr[2] = (tempstr[0]^tempstr[1])+'A';
+		tempstr[3] = 0;
+		US_Print(tempstr);
+	}
+#endif
+#endif
+#endif
+
+
+	fontnumber = 1;
+
+	VW_UpdateScreen ();
+	VW_FadeIn ();
+
+	IN_Ack();
+
+	#ifndef SPEAR
+	if (Keyboard[sc_P] && MS_CheckParm("goobers"))
+		PicturePause();
+	#endif
+
+	VW_FadeOut ();
+
+#ifndef SPEAR
+	UNCACHEGRCHUNK(C_TIMECODEPIC);
+#endif
+	UnCacheLump(LEVELEND_LUMP_START,LEVELEND_LUMP_END);
+
+#ifndef SPEAR
+	EndText();
+#else
+	EndSpear();
+#endif
+
+#endif // SPEARDEMO
+}
+
+
+//==========================================================================
+
+#ifndef JAPAN
+/*
+==================
+=
+= PG13
+=
+==================
+*/
+
+void PG13 (void)
+{
+	VW_FadeOut();
+	VWB_Bar(0,0,320,200,0x82);			// background
+
+	CA_CacheGrChunk (PG13PIC);
+	VWB_DrawPic (216,110,PG13PIC);
+	VW_UpdateScreen ();
+
+	UNCACHEGRCHUNK (PG13PIC);
+
+	VW_FadeIn();
+	IN_UserInput(TickBase*7);
+
+	VW_FadeOut ();
+}
+#endif
+
+
+//==========================================================================
+
+void Write(int x,int y,char *string)
+{
+ int alpha[]={L_NUM0PIC,L_NUM1PIC,L_NUM2PIC,L_NUM3PIC,L_NUM4PIC,L_NUM5PIC,
+	L_NUM6PIC,L_NUM7PIC,L_NUM8PIC,L_NUM9PIC,L_COLONPIC,0,0,0,0,0,0,L_APIC,L_BPIC,
+	L_CPIC,L_DPIC,L_EPIC,L_FPIC,L_GPIC,L_HPIC,L_IPIC,L_JPIC,L_KPIC,
+	L_LPIC,L_MPIC,L_NPIC,L_OPIC,L_PPIC,L_QPIC,L_RPIC,L_SPIC,L_TPIC,
+	L_UPIC,L_VPIC,L_WPIC,L_XPIC,L_YPIC,L_ZPIC};
+
+ int i,ox,nx,ny;
+ char ch;
+
+
+ ox=nx=x*8;
+ ny=y*8;
+ for (i=0;i<strlen(string);i++)
+   if (string[i]=='\n')
+   {
+	nx=ox;
+	ny+=16;
+   }
+   else
+   {
+	ch=string[i];
+	if (ch>='a')
+	  ch-=('a'-'A');
+	ch-='0';
+
+	switch(string[i])
+	{
+	 case '!':
+	   VWB_DrawPic(nx,ny,L_EXPOINTPIC);
+	   nx+=8;
+	   continue;
+
+	 case '\'':
+	   VWB_DrawPic(nx,ny,L_APOSTROPHEPIC);
+	   nx+=8;
+	   continue;
+
+	 case ' ': break;
+	 case 0x3a:	// ':'
+
+	   VWB_DrawPic(nx,ny,L_COLONPIC);
+	   nx+=8;
+	   continue;
+
+	 case '%':
+	   VWB_DrawPic(nx,ny,L_PERCENTPIC);
+	   break;
+
+	 default:
+	   VWB_DrawPic(nx,ny,alpha[ch]);
+	}
+	nx+=16;
+   }
+}
+
+
+//
+// Breathe Mr. BJ!!!
+//
+void BJ_Breathe(void)
+{
+	static int which=0,max=10;
+	int pics[2]={L_GUYPIC,L_GUY2PIC};
+
+
+	if (TimeCount>max)
+	{
+		which^=1;
+		VWB_DrawPic(0,16,pics[which]);
+		VW_UpdateScreen();
+		TimeCount=0;
+		max=35;
+	}
+}
+
+
+
+/*
+==================
+=
+= LevelCompleted
+=
+= Entered with the screen faded out
+= Still in split screen mode with the status bar
+=
+= Exit with the screen faded out
+=
+==================
+*/
+
+#ifndef SPEAR
+LRstruct LevelRatios[8];
+#else
+LRstruct LevelRatios[20];
+#endif
+
+void LevelCompleted (void)
+{
+	#define VBLWAIT	30
+	#define PAR_AMOUNT	500
+	#define PERCENT100AMT	10000
+	typedef struct {
+			float time;
+			char timestr[6];
+			} times;
+
+	int	x,i,min,sec,ratio,kr,sr,tr;
+	unsigned	temp;
+	char tempstr[10];
+	long bonus,timeleft=0;
+	times parTimes[]=
+	{
+#ifndef SPEAR
+	 //
+	 // Episode One Par Times
+	 //
+	 {1.5,	"01:30"},
+	 {2,	"02:00"},
+	 {2,	"02:00"},
+	 {3.5,	"03:30"},
+	 {3,	"03:00"},
+	 {3,	"03:00"},
+	 {2.5,	"02:30"},
+	 {2.5,	"02:30"},
+	 {0,	"??:??"},	// Boss level
+	 {0,	"??:??"},	// Secret level
+
+	 //
+	 // Episode Two Par Times
+	 //
+	 {1.5,	"01:30"},
+	 {3.5,	"03:30"},
+	 {3,	"03:00"},
+	 {2,	"02:00"},
+	 {4,	"04:00"},
+	 {6,	"06:00"},
+	 {1,	"01:00"},
+	 {3,	"03:00"},
+	 {0,	"??:??"},
+	 {0,	"??:??"},
+
+	 //
+	 // Episode Three Par Times
+	 //
+	 {1.5,	"01:30"},
+	 {1.5,	"01:30"},
+	 {2.5,	"02:30"},
+	 {2.5,	"02:30"},
+	 {3.5,	"03:30"},
+	 {2.5,	"02:30"},
+	 {2,	"02:00"},
+	 {6,	"06:00"},
+	 {0,	"??:??"},
+	 {0,	"??:??"},
+
+	 //
+	 // Episode Four Par Times
+	 //
+	 {2,	"02:00"},
+	 {2,	"02:00"},
+	 {1.5,	"01:30"},
+	 {1,	"01:00"},
+	 {4.5,	"04:30"},
+	 {3.5,	"03:30"},
+	 {2,	"02:00"},
+	 {4.5,	"04:30"},
+	 {0,	"??:??"},
+	 {0,	"??:??"},
+
+	 //
+	 // Episode Five Par Times
+	 //
+	 {2.5,	"02:30"},
+	 {1.5,	"01:30"},
+	 {2.5,	"02:30"},
+	 {2.5,	"02:30"},
+	 {4,	"04:00"},
+	 {3,	"03:00"},
+	 {4.5,	"04:30"},
+	 {3.5,	"03:30"},
+	 {0,	"??:??"},
+	 {0,	"??:??"},
+
+	 //
+	 // Episode Six Par Times
+	 //
+	 {6.5,	"06:30"},
+	 {4,	"04:00"},
+	 {4.5,	"04:30"},
+	 {6,	"06:00"},
+	 {5,	"05:00"},
+	 {5.5,	"05:30"},
+	 {5.5,	"05:30"},
+	 {8.5,	"08:30"},
+	 {0,	"??:??"},
+	 {0,	"??:??"}
+#else
+	 //
+	 // SPEAR OF DESTINY TIMES
+	 //
+	 {1.5,	"01:30"},
+	 {3.5,	"03:30"},
+	 {2.75,	"02:45"},
+	 {3.5,	"03:30"},
+	 {0,	"??:??"},	// Boss 1
+	 {4.5,	"04:30"},
+	 {3.25,	"03:15"},
+	 {2.75,	"02:45"},
+	 {4.75,	"04:45"},
+	 {0,	"??:??"},	// Boss 2
+	 {6.5,	"06:30"},
+	 {4.5,	"04:30"},
+	 {2.75,	"02:45"},
+	 {4.5,	"04:30"},
+	 {6,	"06:00"},
+	 {0,	"??:??"},	// Boss 3
+	 {6,	"06:00"},
+	 {0,	"??:??"},	// Boss 4
+	 {0,	"??:??"},	// Secret level 1
+	 {0,	"??:??"},	// Secret level 2
+#endif
+	};
+
+
+
+	CacheLump(LEVELEND_LUMP_START,LEVELEND_LUMP_END);
+	ClearSplitVWB ();			// set up for double buffering in split screen
+	VWB_Bar (0,0,320,200-STATUSLINES,127);
+	StartCPMusic(ENDLEVEL_MUS);
+
+//
+// do the intermission
+//
+	IN_ClearKeysDown();
+	IN_StartAck();
+
+#ifdef JAPAN
+	CA_CacheGrChunk(C_INTERMISSIONPIC);
+	VWB_DrawPic(0,0,C_INTERMISSIONPIC);
+	UNCACHEGRCHUNK(C_INTERMISSIONPIC);
+#endif
+	VWB_DrawPic(0,16,L_GUYPIC);
+
+#ifndef SPEAR
+	if (mapon<8)
+#else
+	if (mapon != 4 &&
+		mapon != 9 &&
+		mapon != 15 &&
+		mapon < 17)
+#endif
+	{
+#ifndef JAPAN
+	 #ifdef SPANISH
+	 Write(14,2,"piso\ncompletado");
+	 #else
+	 Write(14,2,"floor\ncompleted");
+	 #endif
+
+	 Write(14,7,STR_BONUS"     0");
+	 Write(16,10,STR_TIME);
+	 Write(16,12,STR_PAR);
+
+	 #ifdef SPANISH
+	 Write(11,14,    STR_RAT2KILL);
+	 Write(11,16,  STR_RAT2SECRET);
+	 Write(11,18,STR_RAT2TREASURE);
+	 #else
+	 Write(9,14,    STR_RAT2KILL);
+	 Write(5,16,  STR_RAT2SECRET);
+	 Write(1,18,STR_RAT2TREASURE);
+	 #endif
+
+	 Write(26,2,itoa(gamestate.mapon+1,tempstr,10));
+#endif
+
+	 #ifdef SPANISH
+	 Write(30,12,parTimes[gamestate.episode*10+mapon].timestr);
+	 #else
+	 Write(26,12,parTimes[gamestate.episode*10+mapon].timestr);
+	 #endif
+
+	 //
+	 // PRINT TIME
+	 //
+	 sec=gamestate.TimeCount/70;
+
+	 if (sec > 99*60)		// 99 minutes max
+	   sec = 99*60;
+
+	 if (gamestate.TimeCount<parTimes[gamestate.episode*10+mapon].time*4200)
+		timeleft=(parTimes[gamestate.episode*10+mapon].time*4200)/70-sec;
+
+	 min=sec/60;
+	 sec%=60;
+
+	 #ifdef SPANISH
+	 i=30*8;
+	 #else
+	 i=26*8;
+	 #endif
+	 VWB_DrawPic(i,10*8,L_NUM0PIC+(min/10));
+	 i+=2*8;
+	 VWB_DrawPic(i,10*8,L_NUM0PIC+(min%10));
+	 i+=2*8;
+	 Write(i/8,10,":");
+	 i+=1*8;
+	 VWB_DrawPic(i,10*8,L_NUM0PIC+(sec/10));
+	 i+=2*8;
+	 VWB_DrawPic(i,10*8,L_NUM0PIC+(sec%10));
+
+	 VW_UpdateScreen ();
+	 VW_FadeIn ();
+
+
+	 //
+	 // FIGURE RATIOS OUT BEFOREHAND
+	 //
+	 kr = sr = tr = 0;
+	 if (gamestate.killtotal)
+		kr=(gamestate.killcount*100)/gamestate.killtotal;
+	 if (gamestate.secrettotal)
+		sr=(gamestate.secretcount*100)/gamestate.secrettotal;
+	 if (gamestate.treasuretotal)
+		tr=(gamestate.treasurecount*100)/gamestate.treasuretotal;
+
+
+	 //
+	 // PRINT TIME BONUS
+	 //
+	 bonus=timeleft*PAR_AMOUNT;
+	 if (bonus)
+	 {
+	  for (i=0;i<=timeleft;i++)
+	  {
+	   ltoa((long)i*PAR_AMOUNT,tempstr,10);
+	   x=36-strlen(tempstr)*2;
+	   Write(x,7,tempstr);
+	   if (!(i%(PAR_AMOUNT/10)))
+		 SD_PlaySound(ENDBONUS1SND);
+	   VW_UpdateScreen();
+	   while(SD_SoundPlaying())
+		 BJ_Breathe();
+	   if (IN_CheckAck())
+		 goto done;
+	  }
+
+	  VW_UpdateScreen();
+	  SD_PlaySound(ENDBONUS2SND);
+	  while(SD_SoundPlaying())
+		BJ_Breathe();
+	 }
+
+
+	 #ifdef SPANISH
+	 #define RATIOXX		33
+	 #else
+	 #define RATIOXX		37
+	 #endif
+	 //
+	 // KILL RATIO
+	 //
+	 ratio=kr;
+	 for (i=0;i<=ratio;i++)
+	 {
+	  itoa(i,tempstr,10);
+	  x=RATIOXX-strlen(tempstr)*2;
+	  Write(x,14,tempstr);
+	  if (!(i%10))
+		SD_PlaySound(ENDBONUS1SND);
+	  VW_UpdateScreen ();
+	  while(SD_SoundPlaying())
+		BJ_Breathe();
+
+	  if (IN_CheckAck())
+		goto done;
+	 }
+	 if (ratio==100)
+	 {
+	   VW_WaitVBL(VBLWAIT);
+	   SD_StopSound();
+	   bonus+=PERCENT100AMT;
+	   ltoa(bonus,tempstr,10);
+	   x=(RATIOXX-1)-strlen(tempstr)*2;
+	   Write(x,7,tempstr);
+	   VW_UpdateScreen();
+	   SD_PlaySound(PERCENT100SND);
+	 }
+	 else
+	 if (!ratio)
+	 {
+	   VW_WaitVBL(VBLWAIT);
+	   SD_StopSound();
+	   SD_PlaySound(NOBONUSSND);
+	 }
+	 else
+	 SD_PlaySound(ENDBONUS2SND);
+
+	 VW_UpdateScreen();
+	 while(SD_SoundPlaying())
+	   BJ_Breathe();
+
+
+	 //
+	 // SECRET RATIO
+	 //
+	 ratio=sr;
+	 for (i=0;i<=ratio;i++)
+	 {
+	  itoa(i,tempstr,10);
+	  x=RATIOXX-strlen(tempstr)*2;
+	  Write(x,16,tempstr);
+	  if (!(i%10))
+		SD_PlaySound(ENDBONUS1SND);
+	  VW_UpdateScreen ();
+	  while(SD_SoundPlaying())
+		BJ_Breathe();
+	  BJ_Breathe();
+
+	  if (IN_CheckAck())
+		goto done;
+	 }
+	 if (ratio==100)
+	 {
+	   VW_WaitVBL(VBLWAIT);
+	   SD_StopSound();
+	   bonus+=PERCENT100AMT;
+	   ltoa(bonus,tempstr,10);
+	   x=(RATIOXX-1)-strlen(tempstr)*2;
+	   Write(x,7,tempstr);
+	   VW_UpdateScreen();
+	   SD_PlaySound(PERCENT100SND);
+	 }
+	 else
+	 if (!ratio)
+	 {
+	   VW_WaitVBL(VBLWAIT);
+	   SD_StopSound();
+	   SD_PlaySound(NOBONUSSND);
+	 }
+	 else
+	   SD_PlaySound(ENDBONUS2SND);
+	 VW_UpdateScreen();
+	 while(SD_SoundPlaying())
+	   BJ_Breathe();
+
+
+	 //
+	 // TREASURE RATIO
+	 //
+	 ratio=tr;
+	 for (i=0;i<=ratio;i++)
+	 {
+	  itoa(i,tempstr,10);
+	  x=RATIOXX-strlen(tempstr)*2;
+	  Write(x,18,tempstr);
+	  if (!(i%10))
+		SD_PlaySound(ENDBONUS1SND);
+	  VW_UpdateScreen ();
+	  while(SD_SoundPlaying())
+		BJ_Breathe();
+	  if (IN_CheckAck())
+		goto done;
+	 }
+	 if (ratio==100)
+	 {
+	   VW_WaitVBL(VBLWAIT);
+	   SD_StopSound();
+	   bonus+=PERCENT100AMT;
+	   ltoa(bonus,tempstr,10);
+	   x=(RATIOXX-1)-strlen(tempstr)*2;
+	   Write(x,7,tempstr);
+	   VW_UpdateScreen();
+	   SD_PlaySound(PERCENT100SND);
+	 }
+	 else
+	 if (!ratio)
+	 {
+	   VW_WaitVBL(VBLWAIT);
+	   SD_StopSound();
+	   SD_PlaySound(NOBONUSSND);
+	 }
+	 else
+	 SD_PlaySound(ENDBONUS2SND);
+	 VW_UpdateScreen();
+	 while(SD_SoundPlaying())
+	   BJ_Breathe();
+
+
+	 //
+	 // JUMP STRAIGHT HERE IF KEY PRESSED
+	 //
+	 done:
+
+	 itoa(kr,tempstr,10);
+	 x=RATIOXX-strlen(tempstr)*2;
+	 Write(x,14,tempstr);
+
+	 itoa(sr,tempstr,10);
+	 x=RATIOXX-strlen(tempstr)*2;
+	 Write(x,16,tempstr);
+
+	 itoa(tr,tempstr,10);
+	 x=RATIOXX-strlen(tempstr)*2;
+	 Write(x,18,tempstr);
+
+	 bonus=(long)timeleft*PAR_AMOUNT+
+		   (PERCENT100AMT*(kr==100))+
+		   (PERCENT100AMT*(sr==100))+
+		   (PERCENT100AMT*(tr==100));
+
+	 GivePoints(bonus);
+	 ltoa(bonus,tempstr,10);
+	 x=36-strlen(tempstr)*2;
+	 Write(x,7,tempstr);
+
+	 //
+	 // SAVE RATIO INFORMATION FOR ENDGAME
+	 //
+	 LevelRatios[mapon].kill=kr;
+	 LevelRatios[mapon].secret=sr;
+	 LevelRatios[mapon].treasure=tr;
+	 LevelRatios[mapon].time=min*60+sec;
+	}
+	else
+	{
+#ifdef SPEAR
+#ifndef SPEARDEMO
+	  switch(mapon)
+	  {
+	   case 4: Write(14,4," trans\n"
+						  " grosse\n"
+						  STR_DEFEATED); break;
+	   case 9: Write(14,4,"barnacle\n"
+						  "wilhelm\n"
+						  STR_DEFEATED); break;
+	   case 15: Write(14,4,"ubermutant\n"
+						   STR_DEFEATED); break;
+	   case 17: Write(14,4," death\n"
+						   " knight\n"
+						   STR_DEFEATED); break;
+	   case 18: Write(13,4,"secret tunnel\n"
+						   "    area\n"
+						   "  completed!"); break;
+	   case 19: Write(13,4,"secret castle\n"
+						   "    area\n"
+						   "  completed!"); break;
+	  }
+#endif
+#else
+	  Write(14,4,"secret floor\n completed!");
+#endif
+
+	  Write(10,16,"15000 bonus!");
+
+	  VW_UpdateScreen();
+	  VW_FadeIn();
+
+	  GivePoints(15000);
+	}
+
+
+	DrawScore();
+	VW_UpdateScreen();
+
+	TimeCount=0;
+	IN_StartAck();
+	while(!IN_CheckAck())
+	  BJ_Breathe();
+
+//
+// done
+//
+#ifdef SPEARDEMO
+	if (gamestate.mapon == 1)
+	{
+		SD_PlaySound (BONUS1UPSND);
+
+		CA_CacheGrChunk (STARTFONT+1);
+		Message ("This concludes your demo\n"
+				 "of Spear of Destiny! Now,\n"
+				 "go to your local software\n"
+				 "store and buy it!");
+		UNCACHEGRCHUNK (STARTFONT+1);
+
+		IN_ClearKeysDown();
+		IN_Ack();
+	}
+#endif
+
+#ifdef JAPDEMO
+	if (gamestate.mapon == 3)
+	{
+		SD_PlaySound (BONUS1UPSND);
+
+		CA_CacheGrChunk (STARTFONT+1);
+		Message ("This concludes your demo\n"
+				 "of Wolfenstein 3-D! Now,\n"
+				 "go to your local software\n"
+				 "store and buy it!");
+		UNCACHEGRCHUNK (STARTFONT+1);
+
+		IN_ClearKeysDown();
+		IN_Ack();
+	}
+#endif
+
+	#ifndef SPEAR
+	if (Keyboard[sc_P] && MS_CheckParm("goobers"))
+		PicturePause();
+	#endif
+
+	VW_FadeOut ();
+	temp = bufferofs;
+	for (i=0;i<3;i++)
+	{
+		bufferofs = screenloc[i];
+		DrawPlayBorder ();
+	}
+	bufferofs = temp;
+
+	UnCacheLump(LEVELEND_LUMP_START,LEVELEND_LUMP_END);
+}
+
+
+
+//==========================================================================
+
+
+/*
+=================
+=
+= PreloadGraphics
+=
+= Fill the cache up
+=
+=================
+*/
+
+boolean PreloadUpdate(unsigned current, unsigned total)
+{
+	unsigned w = WindowW - 10;
+
+
+	VWB_Bar(WindowX + 5,WindowY + WindowH - 3,w,2,BLACK);
+	w = ((long)w * current) / total;
+	if (w)
+	{
+	 VWB_Bar(WindowX + 5,WindowY + WindowH - 3,w,2,0x37); //SECONDCOLOR);
+	 VWB_Bar(WindowX + 5,WindowY + WindowH - 3,w-1,1,0x32);
+
+	}
+	VW_UpdateScreen();
+//	if (LastScan == sc_Escape)
+//	{
+//		IN_ClearKeysDown();
+//		return(true);
+//	}
+//	else
+		return(false);
+}
+
+void PreloadGraphics(void)
+{
+	DrawLevel ();
+	ClearSplitVWB ();			// set up for double buffering in split screen
+
+	VWB_Bar (0,0,320,200-STATUSLINES,127);
+
+	LatchDrawPic (20-14,80-3*8,GETPSYCHEDPIC);
+
+	WindowX = 160-14*8;
+	WindowY = 80-3*8;
+	WindowW = 28*8;
+	WindowH = 48;
+	VW_UpdateScreen();
+	VW_FadeIn ();
+
+	PM_Preload (PreloadUpdate);
+	IN_UserInput (70);
+	VW_FadeOut ();
+
+	DrawPlayBorder ();
+	VW_UpdateScreen ();
+}
+
+
+//==========================================================================
+
+/*
+==================
+=
+= DrawHighScores
+=
+==================
+*/
+
+void	DrawHighScores(void)
+{
+	char		buffer[16],*str,buffer1[5];
+	byte		temp,temp1,temp2,temp3;
+	word		i,j,
+				w,h,
+				x,y;
+	HighScore	*s;
+
+
+	MM_SortMem ();
+
+#ifndef SPEAR
+//	CA_CacheGrChunk (C_CODEPIC);
+	CA_CacheGrChunk (HIGHSCORESPIC);
+	CA_CacheGrChunk (STARTFONT);
+	CA_CacheGrChunk (C_LEVELPIC);
+	CA_CacheGrChunk (C_SCOREPIC);
+	CA_CacheGrChunk (C_NAMEPIC);
+
+	ClearMScreen();
+	DrawStripes(10);
+
+	VWB_DrawPic(48,0,HIGHSCORESPIC);
+	UNCACHEGRCHUNK (HIGHSCORESPIC);
+
+	VWB_DrawPic(4*8,68,C_NAMEPIC);
+	VWB_DrawPic(20*8,68,C_LEVELPIC);
+	VWB_DrawPic(28*8,68,C_SCOREPIC);
+#ifndef UPLOAD
+//	VWB_DrawPic(35*8,68,C_CODEPIC);
+#endif
+	fontnumber=0;
+
+#else
+	CacheLump (BACKDROP_LUMP_START,BACKDROP_LUMP_END);
+	ClearMScreen();
+	DrawStripes(10);
+	UnCacheLump (BACKDROP_LUMP_START,BACKDROP_LUMP_END);
+
+	CacheLump (HIGHSCORES_LUMP_START,HIGHSCORES_LUMP_END);
+	CA_CacheGrChunk (STARTFONT+1);
+	VWB_DrawPic (0,0,HIGHSCORESPIC);
+
+	fontnumber = 1;
+#endif
+
+
+#ifndef SPEAR
+	SETFONTCOLOR(15,0x29);
+#else
+	SETFONTCOLOR(HIGHLIGHT,0x29);
+#endif
+
+	for (i = 0,s = Scores;i < MaxScores;i++,s++)
+	{
+		PrintY = 76 + (16 * i);
+
+		//
+		// name
+		//
+#ifndef SPEAR
+		PrintX = 4*8;
+#else
+		PrintX = 16;
+#endif
+		US_Print(s->name);
+
+		//
+		// level
+		//
+		ultoa(s->completed,buffer,10);
+#ifndef SPEAR
+		for (str = buffer;*str;str++)
+			*str = *str + (129 - '0');	// Used fixed-width numbers (129...)
+		USL_MeasureString(buffer,&w,&h);
+		PrintX = (22 * 8)-w;
+#else
+		USL_MeasureString(buffer,&w,&h);
+		PrintX = 194 - w;
+#endif
+
+#ifndef UPLOAD
+#ifndef SPEAR
+		PrintX -= 6;
+		itoa(s->episode+1,buffer1,10);
+		US_Print("E");
+		US_Print(buffer1);
+		US_Print("/L");
+#endif
+#endif
+
+#ifdef SPEAR
+		if (s->completed == 21)
+			VWB_DrawPic (PrintX+8,PrintY-1,C_WONSPEARPIC);
+		else
+#endif
+		US_Print(buffer);
+
+		//
+		// score
+		//
+		ultoa(s->score,buffer,10);
+#ifndef SPEAR
+		for (str = buffer;*str;str++)
+			*str = *str + (129 - '0');	// Used fixed-width numbers (129...)
+		USL_MeasureString(buffer,&w,&h);
+		PrintX = (34 * 8) - 8 - w;
+#else
+		USL_MeasureString(buffer,&w,&h);
+		PrintX = 292 - w;
+#endif
+		US_Print(buffer);
+
+		#if 0
+#ifndef UPLOAD
+#ifndef SPEAR
+		//
+		// verification #
+		//
+		if (!i)
+		{
+		 temp=(((s->score >> 28)& 0xf)^
+			  ((s->score >> 24)& 0xf))+'A';
+		 temp1=(((s->score >> 20)& 0xf)^
+			   ((s->score >> 16)& 0xf))+'A';
+		 temp2=(((s->score >> 12)& 0xf)^
+			   ((s->score >> 8)& 0xf))+'A';
+		 temp3=(((s->score >> 4)& 0xf)^
+			   ((s->score >> 0)& 0xf))+'A';
+
+		 SETFONTCOLOR(0x49,0x29);
+		 PrintX = 35*8;
+		 buffer[0]=temp;
+		 buffer[1]=temp1;
+		 buffer[2]=temp2;
+		 buffer[3]=temp3;
+		 buffer[4]=0;
+		 US_Print(buffer);
+		 SETFONTCOLOR(15,0x29);
+		}
+#endif
+#endif
+		#endif
+	}
+
+	VW_UpdateScreen ();
+
+#ifdef SPEAR
+	UnCacheLump (HIGHSCORES_LUMP_START,HIGHSCORES_LUMP_END);
+	fontnumber = 0;
+#endif
+}
+
+//===========================================================================
+
+
+/*
+=======================
+=
+= CheckHighScore
+=
+=======================
+*/
+
+void	CheckHighScore (long score,word other)
+{
+	word		i,j;
+	int			n;
+	HighScore	myscore;
+
+	strcpy(myscore.name,"");
+	myscore.score = score;
+	myscore.episode = gamestate.episode;
+	myscore.completed = other;
+
+	for (i = 0,n = -1;i < MaxScores;i++)
+	{
+		if
+		(
+			(myscore.score > Scores[i].score)
+		||	(
+				(myscore.score == Scores[i].score)
+			&& 	(myscore.completed > Scores[i].completed)
+			)
+		)
+		{
+			for (j = MaxScores;--j > i;)
+				Scores[j] = Scores[j - 1];
+			Scores[i] = myscore;
+			n = i;
+			break;
+		}
+	}
+
+#ifdef SPEAR
+	StartCPMusic (XAWARD_MUS);
+#else
+	StartCPMusic (ROSTER_MUS);
+#endif
+	DrawHighScores ();
+
+	VW_FadeIn ();
+
+	if (n != -1)
+	{
+	//
+	// got a high score
+	//
+		PrintY = 76 + (16 * n);
+#ifndef SPEAR
+		PrintX = 4*8;
+		backcolor = BORDCOLOR;
+		fontcolor = 15;
+		US_LineInput(PrintX,PrintY,Scores[n].name,nil,true,MaxHighName,100);
+#else
+		PrintX = 16;
+		fontnumber = 1;
+		VWB_Bar (PrintX-2,PrintY-2,145,15,0x9c);
+		VW_UpdateScreen ();
+		backcolor = 0x9c;
+		fontcolor = 15;
+		US_LineInput(PrintX,PrintY,Scores[n].name,nil,true,MaxHighName,130);
+#endif
+	}
+	else
+	{
+		IN_ClearKeysDown ();
+		IN_UserInput(500);
+	}
+
+}
+
+
+#ifndef UPLOAD
+#ifndef SPEAR
+#ifndef JAPAN
+////////////////////////////////////////////////////////
+//
+// NON-SHAREWARE NOTICE
+//
+////////////////////////////////////////////////////////
+void NonShareware(void)
+{
+	VW_FadeOut();
+
+	ClearMScreen();
+	DrawStripes(10);
+
+	CA_CacheGrChunk(STARTFONT+1);
+	fontnumber = 1;
+
+	SETFONTCOLOR(READHCOLOR,BKGDCOLOR);
+	PrintX=110;
+	PrintY=15;
+
+	#ifdef SPANISH
+	US_Print("Atencion");
+	#else
+	US_Print("Attention");
+	#endif
+
+	SETFONTCOLOR(HIGHLIGHT,BKGDCOLOR);
+	WindowX=PrintX=40;
+	PrintY=60;
+	#ifdef SPANISH
+	US_Print("Este juego NO es gratis y\n");
+	US_Print("NO es Shareware; favor de\n");
+	US_Print("no distribuirlo.\n\n");
+	#else
+	US_Print("This game is NOT shareware.\n");
+	US_Print("Please do not distribute it.\n");
+	US_Print("Thanks.\n\n");
+	#endif
+	US_Print("        Id Software\n");
+
+	VW_UpdateScreen ();
+	VW_FadeIn();
+	IN_Ack();
+}
+#endif
+#endif
+#endif
+
+#ifdef SPEAR
+#ifndef SPEARDEMO
+////////////////////////////////////////////////////////
+//
+// COPY PROTECTION FOR FormGen
+//
+////////////////////////////////////////////////////////
+char 	far CopyProFailedStrs[][100] = {
+			STR_COPY1,
+			STR_COPY2,
+
+			STR_COPY3,
+			STR_COPY4,
+
+			STR_COPY5,
+			STR_COPY6,
+
+			STR_COPY7,
+			STR_COPY8,
+
+			STR_COPY9,
+			"",
+
+			STR_COPY10,
+			STR_COPY11,
+
+			STR_COPY12,
+			"",
+
+			STR_COPY13,
+			"",
+
+			STR_COPY14,
+			""
+			},
+
+		far BackDoorStrs[5][16] = {
+			"a spoon?",
+			"bite me!",
+			"joshua",
+			"pelt",
+#ifdef BETA
+			"beta"
+#else
+			"snoops"
+#endif
+			},
+
+		far GoodBoyStrs[10][40] = {
+			"...is the CORRECT ANSWER!",
+			"",
+
+			"Consider yourself bitten, sir.",
+			"",
+
+			"Greetings Professor Falken, would you",
+			"like to play Spear of Destiny?",
+
+			"Do you have any gold spray paint?",
+			"",
+
+#ifdef BETA
+			"Beta testing approved.",
+#else
+			"I wish I had a 21\" monitor...",
+#endif
+			""
+			},
+
+		far bossstrs[4][24] = {
+			"DEATH KNIGHT",
+			"BARNACLE WILHELM",
+			"UBERMUTANTUBER MUTANT",
+			"TRANS GROSSE"
+			},
+
+		far WordStr[5][20] = {
+			"New Game",
+			"Sound...F4",
+			"Control...F6",
+			"Change View...F5",
+			"Quit...F10"},
+
+		far	WordCorrect[5][2] = {"3","4","4","5","5"},
+
+		far MemberStr[10][40] = {
+			STR_COPY15,
+			"",
+
+			STR_COPY16,
+			"",
+
+			STR_COPY17,
+			STR_COPY18,
+
+			STR_COPY19,
+			STR_COPY20,
+
+			STR_COPY21,
+			STR_COPY22},
+
+		far MemberCorrect[5][24] = {
+			"adrian carmack",
+			"john carmackjohn romero",
+			"tom hall",
+			"jay wilbur",
+			"kevin cloud"},
+
+		far DosMessages[9][80] = {
+			STR_NOPE1,
+			STR_NOPE2,
+			STR_NOPE3,
+			STR_NOPE4,
+			STR_NOPE5,
+			STR_NOPE6,
+			STR_NOPE7,
+			STR_NOPE8,
+			STR_NOPE9},
+
+		far MiscTitle[4][20] = {
+			"BLOOD TEST",
+			"STRAIGHT-LACED",
+			"QUITE SHAPELY",
+			"I AM WHAT I AMMO"
+			},
+
+		far MiscStr[12][40] = {
+			STR_MISC1,
+			STR_MISC2,
+			"",
+
+			STR_MISC3,
+			STR_MISC4,
+			"",
+
+			STR_MISC5,
+			STR_MISC6,
+			"",
+
+			STR_MISC7,
+			STR_MISC8,
+			STR_MISC9
+			},
+
+		far MiscCorrect[4][5] = {"ss","8",STR_STAR,"45"};
+
+
+int  BackDoor(char *s)
+{
+	int i;
+
+
+	strlwr(s);
+
+	for (i=0;i<5;i++)
+		if (!_fstrcmp(s,BackDoorStrs[i]))
+		{
+			SETFONTCOLOR(14,15);
+			fontnumber = 0;
+			PrintY = 175;
+			VWB_DrawPic (0,20*8,COPYPROTBOXPIC);
+			US_CPrint(GoodBoyStrs[i*2]);
+			US_CPrint(GoodBoyStrs[i*2+1]);
+			VW_UpdateScreen();
+			return 1;
+		}
+
+	return 0;
+}
+
+
+void CopyProtection(void)
+{
+#define TYPEBOX_Y		177
+#define TYPEBOX_BKGD	0x9c
+#define PRINTCOLOR		HIGHLIGHT
+
+	int	i,match,whichboss,bossnum,try,whichline,enemypicked[4]={0,0,0,0},
+		bosses[4] = { BOSSPIC1PIC,BOSSPIC2PIC,BOSSPIC3PIC,BOSSPIC4PIC },
+		whichone,whichpicked[4]={0,0,0,0},quiztype,whichmem,
+		memberpicked[5]={0,0,0,0,0},wordpicked[5]={0,0,0,0,0},whichword;
+
+	char	inputbuffer[20],
+			message[80];
+
+	enum
+	{
+		debriefing,
+		checkmanual,
+		staffquiz,
+		miscquiz,
+
+		totaltypes
+	};
+
+
+
+	try = 0;
+	VW_FadeOut();
+	CA_CacheGrChunk(C_BACKDROPPIC);
+	CacheLump(COPYPROT_LUMP_START,COPYPROT_LUMP_END);
+	CA_CacheGrChunk(STARTFONT+1);
+	CA_LoadAllSounds();
+	StartCPMusic(COPYPRO_MUS);
+	US_InitRndT(true);
+
+	while (try<3)
+	{
+		fontnumber = 1;
+		SETFONTCOLOR(PRINTCOLOR-2,15);
+		VWB_DrawPic (0,0,C_BACKDROPPIC);
+		VWB_DrawPic (0,0,COPYPROTTOPPIC);
+		VWB_DrawPic (0,20*8,COPYPROTBOXPIC);
+		WindowX = WindowY = 0;
+		WindowW = 320;
+		WindowH = 200;
+		PrintY = 65;
+
+		quiztype = US_RndT()%totaltypes;
+		switch(quiztype)
+		{
+			//
+			// BOSSES QUIZ
+			//
+			case debriefing:
+				PrintX = 0;
+				US_Print(STR_DEBRIEF);
+				SETFONTCOLOR(PRINTCOLOR,15);
+
+				while (enemypicked[whichboss = US_RndT()&3]);
+				enemypicked[whichboss] = 1;
+				bossnum = bosses[whichboss];
+				VWB_DrawPic(128,60,bossnum);
+				fontnumber = 0;
+				PrintY = 130;
+				US_CPrint(STR_ENEMY1"\n");
+				US_CPrint(STR_ENEMY2"\n\n");
+
+				VW_UpdateScreen();
+				VW_FadeIn();
+
+				PrintX = 100;
+				fontcolor = 15;
+				backcolor = TYPEBOX_BKGD;
+				inputbuffer[0] = 0;
+				PrintY = TYPEBOX_Y;
+				fontnumber = 1;
+				US_LineInput(PrintX,PrintY,inputbuffer,nil,true,20,100);
+
+				match = 0;
+				for (i=0;i<_fstrlen(bossstrs[whichboss]);i++)
+					if (!_fstrnicmp(inputbuffer,bossstrs[whichboss]+i,strlen(inputbuffer)) &&
+						strlen(inputbuffer)>3)
+						match = 1;
+
+				match += BackDoor(inputbuffer);
+				break;
+
+			//
+			// MANUAL CHECK
+			//
+			case checkmanual:
+				while (wordpicked[whichword = US_RndT()%5]);
+				wordpicked[whichword] = 1;
+				US_CPrint(STR_CHECKMAN);
+				SETFONTCOLOR(PRINTCOLOR,15);
+				PrintY += 25;
+				US_CPrint(STR_MAN1);
+				US_CPrint(STR_MAN2);
+				_fstrcpy(message,STR_MAN3" \"");
+				_fstrcat(message,WordStr[whichword]);
+				_fstrcat(message,"\" "STR_MAN4);
+				US_CPrint(message);
+				VW_UpdateScreen();
+				VW_FadeIn();
+
+				PrintX = 146;
+				fontcolor = 15;
+				backcolor = TYPEBOX_BKGD;
+				inputbuffer[0] = 0;
+				PrintY = TYPEBOX_Y;
+				US_LineInput(PrintX,PrintY,inputbuffer,nil,true,6,100);
+
+				strlwr(inputbuffer);
+				match = 1-(_fstrcmp(inputbuffer,WordCorrect[whichword])!=0);
+				match += BackDoor(inputbuffer);
+				break;
+
+			//
+			// STAFF QUIZ
+			//
+			case staffquiz:
+				while (memberpicked[whichmem = US_RndT()%5]);
+				memberpicked[whichmem] = 1;
+				US_CPrint(STR_ID1);
+				SETFONTCOLOR(PRINTCOLOR,15);
+				PrintY += 25;
+				US_CPrint(MemberStr[whichmem*2]);
+				US_CPrint(MemberStr[whichmem*2+1]);
+				VW_UpdateScreen();
+				VW_FadeIn();
+
+				PrintX = 100;
+				fontcolor = 15;
+				backcolor = TYPEBOX_BKGD;
+				inputbuffer[0] = 0;
+				PrintY = TYPEBOX_Y;
+				US_LineInput(PrintX,PrintY,inputbuffer,nil,true,20,120);
+
+				strlwr(inputbuffer);
+				match = 0;
+				for (i=0;i<_fstrlen(MemberCorrect[whichmem]);i++)
+					if (!_fstrnicmp(inputbuffer,MemberCorrect[whichmem]+i,strlen(inputbuffer)) &&
+						strlen(inputbuffer)>2)
+							match = 1;
+				match += BackDoor(inputbuffer);
+				break;
+
+			//
+			// MISCELLANEOUS QUESTIONS
+			//
+			case miscquiz:
+				while (whichpicked[whichone = US_RndT()&3]);
+				whichpicked[whichone] = 1;
+				US_CPrint(MiscTitle[whichone]);
+				SETFONTCOLOR(PRINTCOLOR,15);
+				PrintY += 25;
+				US_CPrint(MiscStr[whichone*3]);
+				US_CPrint(MiscStr[whichone*3+1]);
+				US_CPrint(MiscStr[whichone*3+2]);
+				VW_UpdateScreen();
+				VW_FadeIn();
+
+				PrintX = 146;
+				fontcolor = 15;
+				backcolor = TYPEBOX_BKGD;
+				inputbuffer[0] = 0;
+				PrintY = TYPEBOX_Y;
+				US_LineInput(PrintX,PrintY,inputbuffer,nil,true,6,100);
+
+				strlwr(inputbuffer);
+				match = 1-(_fstrcmp(inputbuffer,MiscCorrect[whichone])!=0);
+				match += BackDoor(inputbuffer);
+				break;
+			}
+
+		//
+		// IF NO MATCH, WE'VE GOT A (MINOR) PROBLEM!
+		//
+
+		if (!match)
+		{
+			whichline = 2*(US_RndT()%9);
+			SETFONTCOLOR(14,15);
+			fontnumber = 0;
+			PrintY = 175;
+			VWB_DrawPic (0,20*8,COPYPROTBOXPIC);
+			US_CPrint(CopyProFailedStrs[whichline]);
+			US_CPrint(CopyProFailedStrs[whichline+1]);
+
+			VW_UpdateScreen();
+			SD_PlaySound(NOWAYSND);
+			IN_UserInput(TickBase*3);
+			VW_FadeOut();
+			try++;
+		}
+		else
+		{
+			int start;
+
+
+			SD_PlaySound(BONUS1UPSND);
+			SD_WaitSoundDone();
+			UNCACHEGRCHUNK (STARTFONT+1);
+			UNCACHEGRCHUNK (C_BACKDROPPIC);
+			UnCacheLump (COPYPROT_LUMP_START,COPYPROT_LUMP_END);
+
+			switch(SoundMode)
+			{
+				case sdm_Off: return;
+				case sdm_PC: start = STARTPCSOUNDS; break;
+				case sdm_AdLib: start = STARTADLIBSOUNDS;
+			}
+
+			for (i=0;i<NUMSOUNDS;i++,start++)
+				MM_FreePtr ((memptr *)&audiosegs[start]);
+			return;
+		}
+	}
+
+	ClearMemory();
+	ShutdownId();
+
+	_fstrcpy(message,DosMessages[US_RndT()%9]);
+
+	_AX = 3;
+	geninterrupt(0x10);
+
+	printf("%s\n",message);
+	exit(1);
+}
+
+#endif // SPEARDEMO
+#endif // SPEAR
+//===========================================================================
diff --git a/src/lib/hb/wl_main.c b/src/lib/hb/wl_main.c
new file mode 100755
index 00000000..7fec7290
--- /dev/null
+++ b/src/lib/hb/wl_main.c
@@ -0,0 +1,1616 @@
+// WL_MAIN.C
+
+#include <conio.h>
+#include "WL_DEF.H"
+#pragma hdrstop
+
+
+/*
+=============================================================================
+
+						   WOLFENSTEIN 3-D
+
+					  An Id Software production
+
+						   by John Carmack
+
+=============================================================================
+*/
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+
+#define FOCALLENGTH     (0x5700l)               // in global coordinates
+#define VIEWGLOBAL      0x10000                 // globals visable flush to wall
+
+#define VIEWWIDTH       256                     // size of view window
+#define VIEWHEIGHT      144
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+char            str[80],str2[20];
+int				tedlevelnum;
+boolean         tedlevel;
+boolean         nospr;
+boolean         IsA386;
+int                     dirangle[9] = {0,ANGLES/8,2*ANGLES/8,3*ANGLES/8,4*ANGLES/8,
+	5*ANGLES/8,6*ANGLES/8,7*ANGLES/8,ANGLES};
+
+//
+// proejection variables
+//
+fixed           focallength;
+unsigned        screenofs;
+int             viewwidth;
+int             viewheight;
+int             centerx;
+int             shootdelta;                     // pixels away from centerx a target can be
+fixed           scale,maxslope;
+long            heightnumerator;
+int                     minheightdiv;
+
+
+void            Quit (char *error);
+
+boolean         startgame,loadedgame,virtualreality;
+int             mouseadjustment;
+
+char	configname[13]="CONFIG.";
+
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+
+/*
+====================
+=
+= ReadConfig
+=
+====================
+*/
+
+void ReadConfig(void)
+{
+	int                     file;
+	SDMode          sd;
+	SMMode          sm;
+	SDSMode         sds;
+
+
+	if ( (file = open(configname,O_BINARY | O_RDONLY)) != -1)
+	{
+	//
+	// valid config file
+	//
+		read(file,Scores,sizeof(HighScore) * MaxScores);
+
+		read(file,&sd,sizeof(sd));
+		read(file,&sm,sizeof(sm));
+		read(file,&sds,sizeof(sds));
+
+		read(file,&mouseenabled,sizeof(mouseenabled));
+		read(file,&joystickenabled,sizeof(joystickenabled));
+		read(file,&joypadenabled,sizeof(joypadenabled));
+		read(file,&joystickprogressive,sizeof(joystickprogressive));
+		read(file,&joystickport,sizeof(joystickport));
+
+		read(file,&dirscan,sizeof(dirscan));
+		read(file,&buttonscan,sizeof(buttonscan));
+		read(file,&buttonmouse,sizeof(buttonmouse));
+		read(file,&buttonjoy,sizeof(buttonjoy));
+
+		read(file,&viewsize,sizeof(viewsize));
+		read(file,&mouseadjustment,sizeof(mouseadjustment));
+
+		close(file);
+
+		if (sd == sdm_AdLib && !AdLibPresent && !SoundBlasterPresent)
+		{
+			sd = sdm_PC;
+			sd = smm_Off;
+		}
+
+		if ((sds == sds_SoundBlaster && !SoundBlasterPresent) ||
+			(sds == sds_SoundSource && !SoundSourcePresent))
+			sds = sds_Off;
+
+		if (!MousePresent)
+			mouseenabled = false;
+		if (!JoysPresent[joystickport])
+			joystickenabled = false;
+
+		MainMenu[6].active=1;
+		MainItems.curpos=0;
+	}
+	else
+	{
+	//
+	// no config file, so select by hardware
+	//
+		if (SoundBlasterPresent || AdLibPresent)
+		{
+			sd = sdm_AdLib;
+			sm = smm_AdLib;
+		}
+		else
+		{
+			sd = sdm_PC;
+			sm = smm_Off;
+		}
+
+		if (SoundBlasterPresent)
+			sds = sds_SoundBlaster;
+		else if (SoundSourcePresent)
+			sds = sds_SoundSource;
+		else
+			sds = sds_Off;
+
+		if (MousePresent)
+			mouseenabled = true;
+
+		joystickenabled = false;
+		joypadenabled = false;
+		joystickport = 0;
+		joystickprogressive = false;
+
+		viewsize = 15;
+		mouseadjustment=5;
+	}
+
+	SD_SetMusicMode (sm);
+	SD_SetSoundMode (sd);
+	SD_SetDigiDevice (sds);
+
+}
+
+
+/*
+====================
+=
+= WriteConfig
+=
+====================
+*/
+
+void WriteConfig(void)
+{
+	int                     file;
+
+	file = open(configname,O_CREAT | O_BINARY | O_WRONLY,
+				S_IREAD | S_IWRITE | S_IFREG);
+
+	if (file != -1)
+	{
+		write(file,Scores,sizeof(HighScore) * MaxScores);
+
+		write(file,&SoundMode,sizeof(SoundMode));
+		write(file,&MusicMode,sizeof(MusicMode));
+		write(file,&DigiMode,sizeof(DigiMode));
+
+		write(file,&mouseenabled,sizeof(mouseenabled));
+		write(file,&joystickenabled,sizeof(joystickenabled));
+		write(file,&joypadenabled,sizeof(joypadenabled));
+		write(file,&joystickprogressive,sizeof(joystickprogressive));
+		write(file,&joystickport,sizeof(joystickport));
+
+		write(file,&dirscan,sizeof(dirscan));
+		write(file,&buttonscan,sizeof(buttonscan));
+		write(file,&buttonmouse,sizeof(buttonmouse));
+		write(file,&buttonjoy,sizeof(buttonjoy));
+
+		write(file,&viewsize,sizeof(viewsize));
+		write(file,&mouseadjustment,sizeof(mouseadjustment));
+
+		close(file);
+	}
+}
+
+
+//===========================================================================
+
+
+/*
+========================
+=
+= Patch386
+=
+= Patch ldiv to use 32 bit instructions
+=
+========================
+*/
+
+char    *JHParmStrings[] = {"no386",nil};
+void Patch386 (void)
+{
+extern void far jabhack2(void);
+extern int far  CheckIs386(void);
+
+	int     i;
+
+	for (i = 1;i < _argc;i++)
+		if (US_CheckParm(_argv[i],JHParmStrings) == 0)
+		{
+			IsA386 = false;
+			return;
+		}
+
+	if (CheckIs386())
+	{
+		IsA386 = true;
+		jabhack2();
+	}
+	else
+		IsA386 = false;
+}
+
+//===========================================================================
+
+/*
+=====================
+=
+= NewGame
+=
+= Set up new game to start from the beginning
+=
+=====================
+*/
+
+void NewGame (int difficulty,int episode)
+{
+	memset (&gamestate,0,sizeof(gamestate));
+	gamestate.difficulty = difficulty;
+	gamestate.weapon = gamestate.bestweapon
+		= gamestate.chosenweapon = wp_pistol;
+	gamestate.health = 100;
+	gamestate.ammo = STARTAMMO;
+	gamestate.lives = 3;
+	gamestate.nextextra = EXTRAPOINTS;
+	gamestate.episode=episode;
+
+	startgame = true;
+}
+
+//===========================================================================
+
+void DiskFlopAnim(int x,int y)
+{
+ static char which=0;
+ if (!x && !y)
+   return;
+ VWB_DrawPic(x,y,C_DISKLOADING1PIC+which);
+ VW_UpdateScreen();
+ which^=1;
+}
+
+
+long DoChecksum(byte far *source,unsigned size,long checksum)
+{
+ unsigned i;
+
+ for (i=0;i<size-1;i++)
+   checksum += source[i]^source[i+1];
+
+ return checksum;
+}
+
+
+/*
+==================
+=
+= SaveTheGame
+=
+==================
+*/
+
+boolean SaveTheGame(int file,int x,int y)
+{
+	struct diskfree_t dfree;
+	long avail,size,checksum;
+	objtype *ob,nullobj;
+
+
+	if (_dos_getdiskfree(0,&dfree))
+	  Quit("Error in _dos_getdiskfree call");
+
+	avail = (long)dfree.avail_clusters *
+			dfree.bytes_per_sector *
+			dfree.sectors_per_cluster;
+
+	size = 0;
+	for (ob = player; ob ; ob=ob->next)
+	  size += sizeof(*ob);
+	size += sizeof(nullobj);
+
+	size += sizeof(gamestate) +
+			sizeof(LRstruct)*8 +
+			sizeof(tilemap) +
+			sizeof(actorat) +
+			sizeof(laststatobj) +
+			sizeof(statobjlist) +
+			sizeof(doorposition) +
+			sizeof(pwallstate) +
+			sizeof(pwallx) +
+			sizeof(pwally) +
+			sizeof(pwalldir) +
+			sizeof(pwallpos);
+
+	if (avail < size)
+	{
+	 Message(STR_NOSPACE1"\n"
+			 STR_NOSPACE2);
+	 return false;
+	}
+
+	checksum = 0;
+
+
+	DiskFlopAnim(x,y);
+	CA_FarWrite (file,(void far *)&gamestate,sizeof(gamestate));
+	checksum = DoChecksum((byte far *)&gamestate,sizeof(gamestate),checksum);
+
+	DiskFlopAnim(x,y);
+#ifdef SPEAR
+	CA_FarWrite (file,(void far *)&LevelRatios[0],sizeof(LRstruct)*20);
+	checksum = DoChecksum((byte far *)&LevelRatios[0],sizeof(LRstruct)*20,checksum);
+#else
+	CA_FarWrite (file,(void far *)&LevelRatios[0],sizeof(LRstruct)*8);
+	checksum = DoChecksum((byte far *)&LevelRatios[0],sizeof(LRstruct)*8,checksum);
+#endif
+
+	DiskFlopAnim(x,y);
+	CA_FarWrite (file,(void far *)tilemap,sizeof(tilemap));
+	checksum = DoChecksum((byte far *)tilemap,sizeof(tilemap),checksum);
+	DiskFlopAnim(x,y);
+	CA_FarWrite (file,(void far *)actorat,sizeof(actorat));
+	checksum = DoChecksum((byte far *)actorat,sizeof(actorat),checksum);
+
+	CA_FarWrite (file,(void far *)areaconnect,sizeof(areaconnect));
+	CA_FarWrite (file,(void far *)areabyplayer,sizeof(areabyplayer));
+
+	for (ob = player ; ob ; ob=ob->next)
+	{
+	 DiskFlopAnim(x,y);
+	 CA_FarWrite (file,(void far *)ob,sizeof(*ob));
+	}
+	nullobj.active = ac_badobject;          // end of file marker
+	DiskFlopAnim(x,y);
+	CA_FarWrite (file,(void far *)&nullobj,sizeof(nullobj));
+
+
+
+	DiskFlopAnim(x,y);
+	CA_FarWrite (file,(void far *)&laststatobj,sizeof(laststatobj));
+	checksum = DoChecksum((byte far *)&laststatobj,sizeof(laststatobj),checksum);
+	DiskFlopAnim(x,y);
+	CA_FarWrite (file,(void far *)statobjlist,sizeof(statobjlist));
+	checksum = DoChecksum((byte far *)statobjlist,sizeof(statobjlist),checksum);
+
+	DiskFlopAnim(x,y);
+	CA_FarWrite (file,(void far *)doorposition,sizeof(doorposition));
+	checksum = DoChecksum((byte far *)doorposition,sizeof(doorposition),checksum);
+	DiskFlopAnim(x,y);
+	CA_FarWrite (file,(void far *)doorobjlist,sizeof(doorobjlist));
+	checksum = DoChecksum((byte far *)doorobjlist,sizeof(doorobjlist),checksum);
+
+	DiskFlopAnim(x,y);
+	CA_FarWrite (file,(void far *)&pwallstate,sizeof(pwallstate));
+	checksum = DoChecksum((byte far *)&pwallstate,sizeof(pwallstate),checksum);
+	CA_FarWrite (file,(void far *)&pwallx,sizeof(pwallx));
+	checksum = DoChecksum((byte far *)&pwallx,sizeof(pwallx),checksum);
+	CA_FarWrite (file,(void far *)&pwally,sizeof(pwally));
+	checksum = DoChecksum((byte far *)&pwally,sizeof(pwally),checksum);
+	CA_FarWrite (file,(void far *)&pwalldir,sizeof(pwalldir));
+	checksum = DoChecksum((byte far *)&pwalldir,sizeof(pwalldir),checksum);
+	CA_FarWrite (file,(void far *)&pwallpos,sizeof(pwallpos));
+	checksum = DoChecksum((byte far *)&pwallpos,sizeof(pwallpos),checksum);
+
+	//
+	// WRITE OUT CHECKSUM
+	//
+	CA_FarWrite (file,(void far *)&checksum,sizeof(checksum));
+
+	return(true);
+}
+
+//===========================================================================
+
+/*
+==================
+=
+= LoadTheGame
+=
+==================
+*/
+
+boolean LoadTheGame(int file,int x,int y)
+{
+	long checksum,oldchecksum;
+	objtype *ob,nullobj;
+
+
+	checksum = 0;
+
+	DiskFlopAnim(x,y);
+	CA_FarRead (file,(void far *)&gamestate,sizeof(gamestate));
+	checksum = DoChecksum((byte far *)&gamestate,sizeof(gamestate),checksum);
+
+	DiskFlopAnim(x,y);
+#ifdef SPEAR
+	CA_FarRead (file,(void far *)&LevelRatios[0],sizeof(LRstruct)*20);
+	checksum = DoChecksum((byte far *)&LevelRatios[0],sizeof(LRstruct)*20,checksum);
+#else
+	CA_FarRead (file,(void far *)&LevelRatios[0],sizeof(LRstruct)*8);
+	checksum = DoChecksum((byte far *)&LevelRatios[0],sizeof(LRstruct)*8,checksum);
+#endif
+
+	DiskFlopAnim(x,y);
+	SetupGameLevel ();
+
+	DiskFlopAnim(x,y);
+	CA_FarRead (file,(void far *)tilemap,sizeof(tilemap));
+	checksum = DoChecksum((byte far *)tilemap,sizeof(tilemap),checksum);
+	DiskFlopAnim(x,y);
+	CA_FarRead (file,(void far *)actorat,sizeof(actorat));
+	checksum = DoChecksum((byte far *)actorat,sizeof(actorat),checksum);
+
+	CA_FarRead (file,(void far *)areaconnect,sizeof(areaconnect));
+	CA_FarRead (file,(void far *)areabyplayer,sizeof(areabyplayer));
+
+
+
+	InitActorList ();
+	DiskFlopAnim(x,y);
+	CA_FarRead (file,(void far *)player,sizeof(*player));
+
+	while (1)
+	{
+	 DiskFlopAnim(x,y);
+		CA_FarRead (file,(void far *)&nullobj,sizeof(nullobj));
+		if (nullobj.active == ac_badobject)
+			break;
+		GetNewActor ();
+	 // don't copy over the links
+		memcpy (new,&nullobj,sizeof(nullobj)-4);
+	}
+
+
+
+	DiskFlopAnim(x,y);
+	CA_FarRead (file,(void far *)&laststatobj,sizeof(laststatobj));
+	checksum = DoChecksum((byte far *)&laststatobj,sizeof(laststatobj),checksum);
+	DiskFlopAnim(x,y);
+	CA_FarRead (file,(void far *)statobjlist,sizeof(statobjlist));
+	checksum = DoChecksum((byte far *)statobjlist,sizeof(statobjlist),checksum);
+
+	DiskFlopAnim(x,y);
+	CA_FarRead (file,(void far *)doorposition,sizeof(doorposition));
+	checksum = DoChecksum((byte far *)doorposition,sizeof(doorposition),checksum);
+	DiskFlopAnim(x,y);
+	CA_FarRead (file,(void far *)doorobjlist,sizeof(doorobjlist));
+	checksum = DoChecksum((byte far *)doorobjlist,sizeof(doorobjlist),checksum);
+
+	DiskFlopAnim(x,y);
+	CA_FarRead (file,(void far *)&pwallstate,sizeof(pwallstate));
+	checksum = DoChecksum((byte far *)&pwallstate,sizeof(pwallstate),checksum);
+	CA_FarRead (file,(void far *)&pwallx,sizeof(pwallx));
+	checksum = DoChecksum((byte far *)&pwallx,sizeof(pwallx),checksum);
+	CA_FarRead (file,(void far *)&pwally,sizeof(pwally));
+	checksum = DoChecksum((byte far *)&pwally,sizeof(pwally),checksum);
+	CA_FarRead (file,(void far *)&pwalldir,sizeof(pwalldir));
+	checksum = DoChecksum((byte far *)&pwalldir,sizeof(pwalldir),checksum);
+	CA_FarRead (file,(void far *)&pwallpos,sizeof(pwallpos));
+	checksum = DoChecksum((byte far *)&pwallpos,sizeof(pwallpos),checksum);
+
+	CA_FarRead (file,(void far *)&oldchecksum,sizeof(oldchecksum));
+
+	if (oldchecksum != checksum)
+	{
+	 Message(STR_SAVECHT1"\n"
+			 STR_SAVECHT2"\n"
+			 STR_SAVECHT3"\n"
+			 STR_SAVECHT4);
+
+	 IN_ClearKeysDown();
+	 IN_Ack();
+
+	 gamestate.score = 0;
+	 gamestate.lives = 1;
+	 gamestate.weapon =
+	   gamestate.chosenweapon =
+	   gamestate.bestweapon = wp_pistol;
+	 gamestate.ammo = 8;
+	}
+
+	return true;
+}
+
+//===========================================================================
+
+/*
+==========================
+=
+= ShutdownId
+=
+= Shuts down all ID_?? managers
+=
+==========================
+*/
+
+void ShutdownId (void)
+{
+	US_Shutdown ();
+	SD_Shutdown ();
+	PM_Shutdown ();
+	IN_Shutdown ();
+	VW_Shutdown ();
+	CA_Shutdown ();
+	MM_Shutdown ();
+}
+
+
+//===========================================================================
+
+/*
+==================
+=
+= BuildTables
+=
+= Calculates:
+=
+= scale                 projection constant
+= sintable/costable     overlapping fractional tables
+=
+==================
+*/
+
+const   float   radtoint = (float)FINEANGLES/2/PI;
+
+void BuildTables (void)
+{
+  int           i;
+  float         angle,anglestep;
+  double        tang;
+  fixed         value;
+
+
+//
+// calculate fine tangents
+//
+
+	for (i=0;i<FINEANGLES/8;i++)
+	{
+		tang = tan( (i+0.5)/radtoint);
+		finetangent[i] = tang*TILEGLOBAL;
+		finetangent[FINEANGLES/4-1-i] = 1/tang*TILEGLOBAL;
+	}
+
+//
+// costable overlays sintable with a quarter phase shift
+// ANGLES is assumed to be divisable by four
+//
+// The low word of the value is the fraction, the high bit is the sign bit,
+// bits 16-30 should be 0
+//
+
+  angle = 0;
+  anglestep = PI/2/ANGLEQUAD;
+  for (i=0;i<=ANGLEQUAD;i++)
+  {
+	value=GLOBAL1*sin(angle);
+	sintable[i]=
+	  sintable[i+ANGLES]=
+	  sintable[ANGLES/2-i] = value;
+	sintable[ANGLES-i]=
+	  sintable[ANGLES/2+i] = value | 0x80000000l;
+	angle += anglestep;
+  }
+
+}
+
+//===========================================================================
+
+
+/*
+====================
+=
+= CalcProjection
+=
+= Uses focallength
+=
+====================
+*/
+
+void CalcProjection (long focal)
+{
+	int             i;
+	long            intang;
+	float   angle;
+	double  tang;
+	double  planedist;
+	double  globinhalf;
+	int             halfview;
+	double  halfangle,facedist;
+
+
+	focallength = focal;
+	facedist = focal+MINDIST;
+	halfview = viewwidth/2;                                 // half view in pixels
+
+//
+// calculate scale value for vertical height calculations
+// and sprite x calculations
+//
+	scale = halfview*facedist/(VIEWGLOBAL/2);
+
+//
+// divide heightnumerator by a posts distance to get the posts height for
+// the heightbuffer.  The pixel height is height>>2
+//
+	heightnumerator = (TILEGLOBAL*scale)>>6;
+	minheightdiv = heightnumerator/0x7fff +1;
+
+//
+// calculate the angle offset from view angle of each pixel's ray
+//
+
+	for (i=0;i<halfview;i++)
+	{
+	// start 1/2 pixel over, so viewangle bisects two middle pixels
+		tang = (long)i*VIEWGLOBAL/viewwidth/facedist;
+		angle = atan(tang);
+		intang = angle*radtoint;
+		pixelangle[halfview-1-i] = intang;
+		pixelangle[halfview+i] = -intang;
+	}
+
+//
+// if a point's abs(y/x) is greater than maxslope, the point is outside
+// the view area
+//
+	maxslope = finetangent[pixelangle[0]];
+	maxslope >>= 8;
+}
+
+
+
+//===========================================================================
+
+/*
+===================
+=
+= SetupWalls
+=
+= Map tile values to scaled pics
+=
+===================
+*/
+
+void SetupWalls (void)
+{
+	int     i;
+
+	for (i=1;i<MAXWALLTILES;i++)
+	{
+		horizwall[i]=(i-1)*2;
+		vertwall[i]=(i-1)*2+1;
+	}
+}
+
+//===========================================================================
+
+/*
+==========================
+=
+= SignonScreen
+=
+==========================
+*/
+
+void SignonScreen (void)                        // VGA version
+{
+	unsigned        segstart,seglength;
+
+	VL_SetVGAPlaneMode ();
+	VL_TestPaletteSet ();
+	VL_SetPalette (&gamepal);
+
+	if (!virtualreality)
+	{
+		VW_SetScreen(0x8000,0);
+		VL_MungePic (&introscn,320,200);
+		VL_MemToScreen (&introscn,320,200,0,0);
+		VW_SetScreen(0,0);
+	}
+
+//
+// reclaim the memory from the linked in signon screen
+//
+	segstart = FP_SEG(&introscn);
+	seglength = 64000/16;
+	if (FP_OFF(&introscn))
+	{
+		segstart++;
+		seglength--;
+	}
+	MML_UseSpace (segstart,seglength);
+}
+
+
+/*
+==========================
+=
+= FinishSignon
+=
+==========================
+*/
+
+void FinishSignon (void)
+{
+
+#ifndef SPEAR
+	VW_Bar (0,189,300,11,peekb(0xa000,0));
+	WindowX = 0;
+	WindowW = 320;
+	PrintY = 190;
+
+	#ifndef JAPAN
+	SETFONTCOLOR(14,4);
+
+	#ifdef SPANISH
+	US_CPrint ("Oprima una tecla");
+	#else
+	US_CPrint ("Press a key");
+	#endif
+
+	#endif
+
+	if (!NoWait)
+		IN_Ack ();
+
+	#ifndef JAPAN
+	VW_Bar (0,189,300,11,peekb(0xa000,0));
+
+	PrintY = 190;
+	SETFONTCOLOR(10,4);
+
+	#ifdef SPANISH
+	US_CPrint ("pensando...");
+	#else
+	US_CPrint ("Working...");
+	#endif
+
+	#endif
+
+	SETFONTCOLOR(0,15);
+#else
+	if (!NoWait)
+		VW_WaitVBL(3*70);
+#endif
+}
+
+//===========================================================================
+
+/*
+=================
+=
+= MS_CheckParm
+=
+=================
+*/
+
+boolean MS_CheckParm (char far *check)
+{
+	int             i;
+	char    *parm;
+
+	for (i = 1;i<_argc;i++)
+	{
+		parm = _argv[i];
+
+		while ( !isalpha(*parm) )       // skip - / \ etc.. in front of parm
+			if (!*parm++)
+				break;                          // hit end of string without an alphanum
+
+		if ( !_fstricmp(check,parm) )
+			return true;
+	}
+
+	return false;
+}
+
+//===========================================================================
+
+/*
+=====================
+=
+= InitDigiMap
+=
+=====================
+*/
+
+static  int     wolfdigimap[] =
+		{
+			// These first sounds are in the upload version
+#ifndef SPEAR
+			HALTSND,                0,
+			DOGBARKSND,             1,
+			CLOSEDOORSND,           2,
+			OPENDOORSND,            3,
+			ATKMACHINEGUNSND,       4,
+			ATKPISTOLSND,           5,
+			ATKGATLINGSND,          6,
+			SCHUTZADSND,            7,
+			GUTENTAGSND,            8,
+			MUTTISND,               9,
+			BOSSFIRESND,            10,
+			SSFIRESND,              11,
+			DEATHSCREAM1SND,        12,
+			DEATHSCREAM2SND,        13,
+			DEATHSCREAM3SND,        13,
+			TAKEDAMAGESND,          14,
+			PUSHWALLSND,            15,
+
+			LEBENSND,               20,
+			NAZIFIRESND,            21,
+			SLURPIESND,             22,
+
+			YEAHSND,				32,
+
+#ifndef UPLOAD
+			// These are in all other episodes
+			DOGDEATHSND,            16,
+			AHHHGSND,               17,
+			DIESND,                 18,
+			EVASND,                 19,
+
+			TOT_HUNDSND,            23,
+			MEINGOTTSND,            24,
+			SCHABBSHASND,           25,
+			HITLERHASND,            26,
+			SPIONSND,               27,
+			NEINSOVASSND,           28,
+			DOGATTACKSND,           29,
+			LEVELDONESND,           30,
+			MECHSTEPSND,			31,
+
+			SCHEISTSND,				33,
+			DEATHSCREAM4SND,		34,		// AIIEEE
+			DEATHSCREAM5SND,		35,		// DEE-DEE
+			DONNERSND,				36,		// EPISODE 4 BOSS DIE
+			EINESND,				37,		// EPISODE 4 BOSS SIGHTING
+			ERLAUBENSND,			38,		// EPISODE 6 BOSS SIGHTING
+			DEATHSCREAM6SND,		39,		// FART
+			DEATHSCREAM7SND,		40,		// GASP
+			DEATHSCREAM8SND,		41,		// GUH-BOY!
+			DEATHSCREAM9SND,		42,		// AH GEEZ!
+			KEINSND,				43,		// EPISODE 5 BOSS SIGHTING
+			MEINSND,				44,		// EPISODE 6 BOSS DIE
+			ROSESND,				45,		// EPISODE 5 BOSS DIE
+
+#endif
+#else
+//
+// SPEAR OF DESTINY DIGISOUNDS
+//
+			HALTSND,                0,
+			CLOSEDOORSND,           2,
+			OPENDOORSND,            3,
+			ATKMACHINEGUNSND,       4,
+			ATKPISTOLSND,           5,
+			ATKGATLINGSND,          6,
+			SCHUTZADSND,            7,
+			BOSSFIRESND,            8,
+			SSFIRESND,              9,
+			DEATHSCREAM1SND,        10,
+			DEATHSCREAM2SND,        11,
+			TAKEDAMAGESND,          12,
+			PUSHWALLSND,            13,
+			AHHHGSND,               15,
+			LEBENSND,               16,
+			NAZIFIRESND,            17,
+			SLURPIESND,             18,
+			LEVELDONESND,           22,
+			DEATHSCREAM4SND,		23,		// AIIEEE
+			DEATHSCREAM3SND,        23,		// DOUBLY-MAPPED!!!
+			DEATHSCREAM5SND,		24,		// DEE-DEE
+			DEATHSCREAM6SND,		25,		// FART
+			DEATHSCREAM7SND,		26,		// GASP
+			DEATHSCREAM8SND,		27,		// GUH-BOY!
+			DEATHSCREAM9SND,		28,		// AH GEEZ!
+			GETGATLINGSND,			38,		// Got Gat replacement
+
+#ifndef SPEARDEMO
+			DOGBARKSND,             1,
+			DOGDEATHSND,            14,
+			SPIONSND,               19,
+			NEINSOVASSND,           20,
+			DOGATTACKSND,           21,
+			TRANSSIGHTSND,			29,		// Trans Sight
+			TRANSDEATHSND,			30,		// Trans Death
+			WILHELMSIGHTSND,		31,		// Wilhelm Sight
+			WILHELMDEATHSND,		32,		// Wilhelm Death
+			UBERDEATHSND,			33,		// Uber Death
+			KNIGHTSIGHTSND,			34,		// Death Knight Sight
+			KNIGHTDEATHSND,			35,		// Death Knight Death
+			ANGELSIGHTSND,			36,		// Angel Sight
+			ANGELDEATHSND,			37,		// Angel Death
+			GETSPEARSND,			39,		// Got Spear replacement
+#endif
+#endif
+			LASTSOUND
+		};
+
+
+void InitDigiMap (void)
+{
+	int                     *map;
+
+	for (map = wolfdigimap;*map != LASTSOUND;map += 2)
+		DigiMap[map[0]] = map[1];
+
+
+}
+
+
+#ifndef SPEAR
+CP_iteminfo	MusicItems={CTL_X,CTL_Y,6,0,32};
+CP_itemtype far MusicMenu[]=
+	{
+		{1,"Get Them!",0},
+		{1,"Searching",0},
+		{1,"P.O.W.",0},
+		{1,"Suspense",0},
+		{1,"War March",0},
+		{1,"Around The Corner!",0},
+
+		{1,"Nazi Anthem",0},
+		{1,"Lurking...",0},
+		{1,"Going After Hitler",0},
+		{1,"Pounding Headache",0},
+		{1,"Into the Dungeons",0},
+		{1,"Ultimate Conquest",0},
+
+		{1,"Kill the S.O.B.",0},
+		{1,"The Nazi Rap",0},
+		{1,"Twelfth Hour",0},
+		{1,"Zero Hour",0},
+		{1,"Ultimate Conquest",0},
+		{1,"Wolfpack",0}
+	};
+#else
+CP_iteminfo MusicItems={CTL_X,CTL_Y-20,9,0,32};
+CP_itemtype far MusicMenu[]=
+   {
+		{1,"Funky Colonel Bill",0},
+		{1,"Death To The Nazis",0},
+		{1,"Tiptoeing Around",0},
+		{1,"Is This THE END?",0},
+		{1,"Evil Incarnate",0},
+		{1,"Jazzin' Them Nazis",0},
+		{1,"Puttin' It To The Enemy",0},
+		{1,"The SS Gonna Get You",0},
+		{1,"Towering Above",0}
+	};
+#endif
+
+#ifndef SPEARDEMO
+void DoJukebox(void)
+{
+	int which,lastsong=-1;
+	unsigned start,songs[]=
+		{
+#ifndef SPEAR
+			GETTHEM_MUS,
+			SEARCHN_MUS,
+			POW_MUS,
+			SUSPENSE_MUS,
+			WARMARCH_MUS,
+			CORNER_MUS,
+
+			NAZI_OMI_MUS,
+			PREGNANT_MUS,
+			GOINGAFT_MUS,
+			HEADACHE_MUS,
+			DUNGEON_MUS,
+			ULTIMATE_MUS,
+
+			INTROCW3_MUS,
+			NAZI_RAP_MUS,
+			TWELFTH_MUS,
+			ZEROHOUR_MUS,
+			ULTIMATE_MUS,
+			PACMAN_MUS
+#else
+			XFUNKIE_MUS,             // 0
+			XDEATH_MUS,              // 2
+			XTIPTOE_MUS,             // 4
+			XTHEEND_MUS,             // 7
+			XEVIL_MUS,               // 17
+			XJAZNAZI_MUS,            // 18
+			XPUTIT_MUS,              // 21
+			XGETYOU_MUS,             // 22
+			XTOWER2_MUS              // 23
+#endif
+		};
+	struct dostime_t time;
+
+
+
+	IN_ClearKeysDown();
+	if (!AdLibPresent && !SoundBlasterPresent)
+		return;
+
+
+	MenuFadeOut();
+
+#ifndef SPEAR
+#ifndef UPLOAD
+	_dos_gettime(&time);
+	start = (time.hsecond%3)*6;
+#else
+	start = 0;
+#endif
+#else
+	start = 0;
+#endif
+
+
+	CA_CacheGrChunk (STARTFONT+1);
+#ifdef SPEAR
+	CacheLump (BACKDROP_LUMP_START,BACKDROP_LUMP_END);
+#else
+	CacheLump (CONTROLS_LUMP_START,CONTROLS_LUMP_END);
+#endif
+	CA_LoadAllSounds ();
+
+	fontnumber=1;
+	ClearMScreen ();
+	VWB_DrawPic(112,184,C_MOUSELBACKPIC);
+	DrawStripes (10);
+	SETFONTCOLOR (TEXTCOLOR,BKGDCOLOR);
+
+#ifndef SPEAR
+	DrawWindow (CTL_X-2,CTL_Y-6,280,13*7,BKGDCOLOR);
+#else
+	DrawWindow (CTL_X-2,CTL_Y-26,280,13*10,BKGDCOLOR);
+#endif
+
+	DrawMenu (&MusicItems,&MusicMenu[start]);
+
+	SETFONTCOLOR (READHCOLOR,BKGDCOLOR);
+	PrintY=15;
+	WindowX = 0;
+	WindowY = 320;
+	US_CPrint ("Robert's Jukebox");
+
+	SETFONTCOLOR (TEXTCOLOR,BKGDCOLOR);
+	VW_UpdateScreen();
+	MenuFadeIn();
+
+	do
+	{
+		which = HandleMenu(&MusicItems,&MusicMenu[start],NULL);
+		if (which>=0)
+		{
+			if (lastsong >= 0)
+				MusicMenu[start+lastsong].active = 1;
+
+			StartCPMusic(songs[start + which]);
+			MusicMenu[start+which].active = 2;
+			DrawMenu (&MusicItems,&MusicMenu[start]);
+			VW_UpdateScreen();
+			lastsong = which;
+		}
+	} while(which>=0);
+
+	MenuFadeOut();
+	IN_ClearKeysDown();
+#ifdef SPEAR
+	UnCacheLump (BACKDROP_LUMP_START,BACKDROP_LUMP_END);
+#else
+	UnCacheLump (CONTROLS_LUMP_START,CONTROLS_LUMP_END);
+#endif
+}
+#endif
+
+
+/*
+==========================
+=
+= InitGame
+=
+= Load a few things right away
+=
+==========================
+*/
+
+void InitGame (void)
+{
+	int                     i,x,y;
+	unsigned        *blockstart;
+
+	if (MS_CheckParm ("virtual"))
+		virtualreality = true;
+	else
+		virtualreality = false;
+
+	MM_Startup ();                  // so the signon screen can be freed
+
+	SignonScreen ();
+
+	VW_Startup ();
+	IN_Startup ();
+	PM_Startup ();
+	PM_UnlockMainMem ();
+	SD_Startup ();
+	CA_Startup ();
+	US_Startup ();
+
+
+#ifndef SPEAR
+	if (mminfo.mainmem < 235000L)
+#else
+	if (mminfo.mainmem < 257000L && !MS_CheckParm("debugmode"))
+#endif
+	{
+		memptr screen;
+
+		CA_CacheGrChunk (ERRORSCREEN);
+		screen = grsegs[ERRORSCREEN];
+		ShutdownId();
+		movedata ((unsigned)screen,7+7*160,0xb800,0,17*160);
+		gotoxy (1,23);
+		exit(1);
+	}
+
+
+//
+// build some tables
+//
+	InitDigiMap ();
+
+	for (i=0;i<MAPSIZE;i++)
+	{
+		nearmapylookup[i] = &tilemap[0][0]+MAPSIZE*i;
+		farmapylookup[i] = i*64;
+	}
+
+	for (i=0;i<PORTTILESHIGH;i++)
+		uwidthtable[i] = UPDATEWIDE*i;
+
+	blockstart = &blockstarts[0];
+	for (y=0;y<UPDATEHIGH;y++)
+		for (x=0;x<UPDATEWIDE;x++)
+			*blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;
+
+	updateptr = &update[0];
+
+	bufferofs = 0;
+	displayofs = 0;
+	ReadConfig ();
+
+
+//
+// HOLDING DOWN 'M' KEY?
+//
+#ifndef SPEARDEMO
+	if (Keyboard[sc_M])
+	  DoJukebox();
+	else
+#endif
+//
+// draw intro screen stuff
+//
+	if (!virtualreality)
+		IntroScreen ();
+
+//
+// load in and lock down some basic chunks
+//
+
+	CA_CacheGrChunk(STARTFONT);
+	MM_SetLock (&grsegs[STARTFONT],true);
+
+	LoadLatchMem ();
+	BuildTables ();          // trig tables
+	SetupWalls ();
+
+#if 0
+{
+int temp,i;
+temp = viewsize;
+	profilehandle = open("SCALERS.TXT", O_CREAT | O_WRONLY | O_TEXT);
+for (i=1;i<20;i++)
+	NewViewSize(i);
+viewsize = temp;
+close(profilehandle);
+}
+#endif
+
+	NewViewSize (viewsize);
+
+
+//
+// initialize variables
+//
+	InitRedShifts ();
+	if (!virtualreality)
+		FinishSignon();
+
+	displayofs = PAGE1START;
+	bufferofs = PAGE2START;
+
+	if (virtualreality)
+	{
+		NoWait = true;
+		geninterrupt(0x60);
+	}
+}
+
+//===========================================================================
+
+/*
+==========================
+=
+= SetViewSize
+=
+==========================
+*/
+
+boolean SetViewSize (unsigned width, unsigned height)
+{
+	viewwidth = width&~15;                  // must be divisable by 16
+	viewheight = height&~1;                 // must be even
+	centerx = viewwidth/2-1;
+	shootdelta = viewwidth/10;
+	screenofs = ((200-STATUSLINES-viewheight)/2*SCREENWIDTH+(320-viewwidth)/8);
+
+//
+// calculate trace angles and projection constants
+//
+	CalcProjection (FOCALLENGTH);
+
+//
+// build all needed compiled scalers
+//
+//	MM_BombOnError (false);
+	SetupScaling (viewwidth*1.5);
+#if 0
+	MM_BombOnError (true);
+	if (mmerror)
+	{
+		Quit ("Can't build scalers!");
+		mmerror = false;
+		return false;
+	}
+#endif
+	return true;
+}
+
+
+void ShowViewSize (int width)
+{
+	int     oldwidth,oldheight;
+
+	oldwidth = viewwidth;
+	oldheight = viewheight;
+
+	viewwidth = width*16;
+	viewheight = width*16*HEIGHTRATIO;
+	DrawPlayBorder ();
+
+	viewheight = oldheight;
+	viewwidth = oldwidth;
+}
+
+
+void NewViewSize (int width)
+{
+	CA_UpLevel ();
+	MM_SortMem ();
+	viewsize = width;
+	SetViewSize (width*16,width*16*HEIGHTRATIO);
+	CA_DownLevel ();
+}
+
+
+
+//===========================================================================
+
+/*
+==========================
+=
+= Quit
+=
+==========================
+*/
+
+void Quit (char *error)
+{
+	unsigned        finscreen;
+	memptr	screen;
+
+	if (virtualreality)
+		geninterrupt(0x61);
+
+	ClearMemory ();
+	if (!*error)
+	{
+	 #ifndef JAPAN
+	 CA_CacheGrChunk (ORDERSCREEN);
+	 screen = grsegs[ORDERSCREEN];
+	 #endif
+	 WriteConfig ();
+	}
+	else
+	{
+	 CA_CacheGrChunk (ERRORSCREEN);
+	 screen = grsegs[ERRORSCREEN];
+	}
+
+	ShutdownId ();
+
+	if (error && *error)
+	{
+	  movedata ((unsigned)screen,7,0xb800,0,7*160);
+	  gotoxy (10,4);
+	  puts(error);
+	  gotoxy (1,8);
+	  exit(1);
+	}
+	else
+	if (!error || !(*error))
+	{
+		clrscr();
+		#ifndef JAPAN
+		movedata ((unsigned)screen,7,0xb800,0,4000);
+		gotoxy(1,24);
+		#endif
+//asm	mov	bh,0
+//asm	mov	dh,23	// row
+//asm	mov	dl,0	// collumn
+//asm	mov ah,2
+//asm	int	0x10
+	}
+
+	exit(0);
+}
+
+//===========================================================================
+
+
+
+/*
+=====================
+=
+= DemoLoop
+=
+=====================
+*/
+
+static  char *ParmStrings[] = {"baby","easy","normal","hard",""};
+
+void    DemoLoop (void)
+{
+	static int LastDemo;
+	int     i,level;
+	long nsize;
+	memptr	nullblock;
+
+//
+// check for launch from ted
+//
+	if (tedlevel)
+	{
+		NoWait = true;
+		NewGame(1,0);
+
+		for (i = 1;i < _argc;i++)
+		{
+			if ( (level = US_CheckParm(_argv[i],ParmStrings)) != -1)
+			{
+			 gamestate.difficulty=level;
+			 break;
+			}
+		}
+
+#ifndef SPEAR
+		gamestate.episode = tedlevelnum/10;
+		gamestate.mapon = tedlevelnum%10;
+#else
+		gamestate.episode = 0;
+		gamestate.mapon = tedlevelnum;
+#endif
+		GameLoop();
+		Quit (NULL);
+	}
+
+
+//
+// main game cycle
+//
+
+
+//	nsize = (long)40*1024;
+//	MM_GetPtr(&nullblock,nsize);
+
+#ifndef DEMOTEST
+
+	#ifndef UPLOAD
+
+		#ifndef GOODTIMES
+		#ifndef SPEAR
+		#ifndef JAPAN
+		if (!NoWait)
+			NonShareware();
+		#endif
+		#else
+
+			#ifndef GOODTIMES
+			#ifndef SPEARDEMO
+			CopyProtection();
+			#endif
+			#endif
+
+		#endif
+		#endif
+	#endif
+
+	StartCPMusic(INTROSONG);
+
+#ifndef JAPAN
+	if (!NoWait)
+		PG13 ();
+#endif
+
+#endif
+
+	while (1)
+	{
+		while (!NoWait)
+		{
+//
+// title page
+//
+			MM_SortMem ();
+#ifndef DEMOTEST
+
+#ifdef SPEAR
+			CA_CacheGrChunk (TITLEPALETTE);
+
+			CA_CacheGrChunk (TITLE1PIC);
+			VWB_DrawPic (0,0,TITLE1PIC);
+			UNCACHEGRCHUNK (TITLE1PIC);
+
+			CA_CacheGrChunk (TITLE2PIC);
+			VWB_DrawPic (0,80,TITLE2PIC);
+			UNCACHEGRCHUNK (TITLE2PIC);
+			VW_UpdateScreen ();
+			VL_FadeIn(0,255,grsegs[TITLEPALETTE],30);
+
+			UNCACHEGRCHUNK (TITLEPALETTE);
+#else
+			CA_CacheScreen (TITLEPIC);
+			VW_UpdateScreen ();
+			VW_FadeIn();
+#endif
+			if (IN_UserInput(TickBase*15))
+				break;
+			VW_FadeOut();
+//
+// credits page
+//
+			CA_CacheScreen (CREDITSPIC);
+			VW_UpdateScreen();
+			VW_FadeIn ();
+			if (IN_UserInput(TickBase*10))
+				break;
+			VW_FadeOut ();
+//
+// high scores
+//
+			DrawHighScores ();
+			VW_UpdateScreen ();
+			VW_FadeIn ();
+
+			if (IN_UserInput(TickBase*10))
+				break;
+#endif
+//
+// demo
+//
+
+			#ifndef SPEARDEMO
+			PlayDemo (LastDemo++%4);
+			#else
+			PlayDemo (0);
+			#endif
+
+			if (playstate == ex_abort)
+				break;
+			StartCPMusic(INTROSONG);
+		}
+
+		VW_FadeOut ();
+
+#ifndef SPEAR
+		if (Keyboard[sc_Tab] && MS_CheckParm("goobers"))
+#else
+		if (Keyboard[sc_Tab] && MS_CheckParm("debugmode"))
+#endif
+			RecordDemo ();
+		else
+			US_ControlPanel (0);
+
+		if (startgame || loadedgame)
+		{
+			GameLoop ();
+			VW_FadeOut();
+			StartCPMusic(INTROSONG);
+		}
+	}
+}
+
+
+//===========================================================================
+
+
+/*
+==========================
+=
+= main
+=
+==========================
+*/
+
+char    *nosprtxt[] = {"nospr",nil};
+
+void main (void)
+{
+	int     i;
+
+
+#ifdef BETA
+	//
+	// THIS IS FOR BETA ONLY!
+	//
+	struct dosdate_t d;
+
+	_dos_getdate(&d);
+	if (d.year > YEAR ||
+		(d.month >= MONTH && d.day >= DAY))
+	{
+	 printf("Sorry, BETA-TESTING is over. Thanks for you help.\n");
+	 exit(1);
+	}
+#endif
+
+	CheckForEpisodes();
+
+	Patch386 ();
+
+	InitGame ();
+
+	DemoLoop();
+
+	Quit("Demo loop exited???");
+}
+
diff --git a/src/lib/hb/wl_menu.c b/src/lib/hb/wl_menu.c
new file mode 100755
index 00000000..7d1521cb
--- /dev/null
+++ b/src/lib/hb/wl_menu.c
@@ -0,0 +1,3986 @@
+////////////////////////////////////////////////////////////////////
+//
+// WL_MENU.C
+// by John Romero (C) 1992 Id Software, Inc.
+//
+////////////////////////////////////////////////////////////////////
+#include "wl_def.h"
+#pragma hdrstop
+
+//
+// PRIVATE PROTOTYPES
+//
+void CP_ReadThis(void);
+
+#ifdef SPEAR
+#define STARTITEM	newgame
+
+#else
+#ifdef GOODTIMES
+#define STARTITEM	newgame
+
+#else
+#define STARTITEM	readthis
+#endif
+#endif
+
+char far endStrings[9][80]=
+{
+#ifndef SPEAR
+	{"Dost thou wish to\nleave with such hasty\nabandon?"},
+	{"Chickening out...\nalready?"},
+	{"Press N for more carnage.\nPress Y to be a weenie."},
+	{"So, you think you can\nquit this easily, huh?"},
+	{"Press N to save the world.\nPress Y to abandon it in\nits hour of need."},
+	{"Press N if you are brave.\nPress Y to cower in shame."},
+	{"Heroes, press N.\nWimps, press Y."},
+	{"You are at an intersection.\nA sign says, 'Press Y to quit.'\n>"},
+	{"For guns and glory, press N.\nFor work and worry, press Y."}
+#else
+	ENDSTR1,
+	ENDSTR2,
+	ENDSTR3,
+	ENDSTR4,
+	ENDSTR5,
+	ENDSTR6,
+	ENDSTR7,
+	ENDSTR8,
+	ENDSTR9
+#endif
+};
+
+CP_iteminfo
+	MainItems={MENU_X,MENU_Y,10,STARTITEM,24},
+	SndItems={SM_X,SM_Y1,12,0,52},
+	LSItems={LSM_X,LSM_Y,10,0,24},
+	CtlItems={CTL_X,CTL_Y,6,-1,56},
+	CusItems={8,CST_Y+13*2,9,-1,0},
+	NewEitems={NE_X,NE_Y,11,0,88},
+	NewItems={NM_X,NM_Y,4,2,24};
+
+#pragma warn -sus
+CP_itemtype far
+MainMenu[]=
+{
+#ifdef JAPAN
+	{1,"",CP_NewGame},
+	{1,"",CP_Sound},
+	{1,"",CP_Control},
+	{1,"",CP_LoadGame},
+	{0,"",CP_SaveGame},
+	{1,"",CP_ChangeView},
+	{2,"",CP_ReadThis},
+	{1,"",CP_ViewScores},
+	{1,"",0},
+	{1,"",0}
+#else
+
+	{1,STR_NG,CP_NewGame},
+	{1,STR_SD,CP_Sound},
+	{1,STR_CL,CP_Control},
+	{1,STR_LG,CP_LoadGame},
+	{0,STR_SG,CP_SaveGame},
+	{1,STR_CV,CP_ChangeView},
+
+#ifndef GOODTIMES
+#ifndef SPEAR
+
+	#ifdef SPANISH
+	{2,"Ve esto!",CP_ReadThis},
+	#else
+	{2,"Read This!",CP_ReadThis},
+	#endif
+
+#endif
+#endif
+
+	{1,STR_VS,CP_ViewScores},
+	{1,STR_BD,0},
+	{1,STR_QT,0}
+#endif
+},
+
+far SndMenu[]=
+{
+#ifdef JAPAN
+	{1,"",0},
+	{1,"",0},
+	{1,"",0},
+	{0,"",0},
+	{0,"",0},
+	{1,"",0},
+	{1,"",0},
+	{1,"",0},
+	{0,"",0},
+	{0,"",0},
+	{1,"",0},
+	{1,"",0},
+#else
+	{1,STR_NONE,0},
+	{1,STR_PC,0},
+	{1,STR_ALSB,0},
+	{0,"",0},
+	{0,"",0},
+	{1,STR_NONE,0},
+	{1,STR_DISNEY,0},
+	{1,STR_SB,0},
+	{0,"",0},
+	{0,"",0},
+	{1,STR_NONE,0},
+	{1,STR_ALSB,0}
+#endif
+},
+
+far CtlMenu[]=
+{
+#ifdef JAPAN
+	{0,"",0},
+	{0,"",0},
+	{0,"",0},
+	{0,"",0},
+	{0,"",MouseSensitivity},
+	{1,"",CustomControls}
+#else
+	{0,STR_MOUSEEN,0},
+	{0,STR_JOYEN,0},
+	{0,STR_PORT2,0},
+	{0,STR_GAMEPAD,0},
+	{0,STR_SENS,MouseSensitivity},
+	{1,STR_CUSTOM,CustomControls}
+#endif
+},
+
+#pragma warn +sus
+
+#ifndef SPEAR
+far NewEmenu[]=
+{
+#ifdef JAPAN
+#ifdef JAPDEMO
+	{1,"",0},
+	{0,"",0},
+	{0,"",0},
+	{0,"",0},
+	{0,"",0},
+	{0,"",0},
+	{0,"",0},
+	{0,"",0},
+	{0,"",0},
+	{0,"",0},
+	{0,"",0},
+	{0,"",0},
+#else
+	{1,"",0},
+	{0,"",0},
+	{1,"",0},
+	{0,"",0},
+	{1,"",0},
+	{0,"",0},
+	{1,"",0},
+	{0,"",0},
+	{1,"",0},
+	{0,"",0},
+	{1,"",0},
+	{0,"",0}
+#endif
+#else
+	#ifdef SPANISH
+	{1,"Episodio 1\n"
+	   "Fuga desde Wolfenstein",0},
+	{0,"",0},
+	{3,"Episodio 2\n"
+		   "Operacion Eisenfaust",0},
+	{0,"",0},
+	{3,"Episodio 3\n"
+		   "Muere, Fuhrer, Muere!",0},
+	{0,"",0},
+	{3,"Episodio 4\n"
+		  "Un Negro Secreto",0},
+	{0,"",0},
+	{3,"Episodio 5\n"
+		  "Huellas del Loco",0},
+	{0,"",0},
+	{3,"Episodio 6\n"
+		  "Confrontacion",0}
+	#else
+	{1,"Episode 1\n"
+	   "Escape from Wolfenstein",0},
+	{0,"",0},
+	{3,"Episode 2\n"
+		   "Operation: Eisenfaust",0},
+	{0,"",0},
+	{3,"Episode 3\n"
+		   "Die, Fuhrer, Die!",0},
+	{0,"",0},
+	{3,"Episode 4\n"
+		  "A Dark Secret",0},
+	{0,"",0},
+	{3,"Episode 5\n"
+		  "Trail of the Madman",0},
+	{0,"",0},
+	{3,"Episode 6\n"
+		  "Confrontation",0}
+	#endif
+#endif
+},
+#endif
+
+
+far NewMenu[]=
+{
+#ifdef JAPAN
+	{1,"",0},
+	{1,"",0},
+	{1,"",0},
+	{1,"",0}
+#else
+	{1,STR_DADDY,0},
+	{1,STR_HURTME,0},
+	{1,STR_BRINGEM,0},
+	{1,STR_DEATH,0}
+#endif
+},
+
+far LSMenu[]=
+{
+	{1,"",0},
+	{1,"",0},
+	{1,"",0},
+	{1,"",0},
+	{1,"",0},
+	{1,"",0},
+	{1,"",0},
+	{1,"",0},
+	{1,"",0},
+	{1,"",0}
+},
+
+far CusMenu[]=
+{
+	{1,"",0},
+	{0,"",0},
+	{0,"",0},
+	{1,"",0},
+	{0,"",0},
+	{0,"",0},
+	{1,"",0},
+	{0,"",0},
+	{1,"",0}
+}
+;
+
+
+int color_hlite[]={
+   DEACTIVE,
+   HIGHLIGHT,
+   READHCOLOR,
+   0x67
+   },
+
+   color_norml[]={
+   DEACTIVE,
+   TEXTCOLOR,
+   READCOLOR,
+   0x6b
+   };
+
+int EpisodeSelect[6]={1};
+
+
+int SaveGamesAvail[10],StartGame,SoundStatus=1,pickquick;
+char SaveGameNames[10][32],SaveName[13]="SAVEGAM?.";
+
+
+////////////////////////////////////////////////////////////////////
+//
+// INPUT MANAGER SCANCODE TABLES
+//
+////////////////////////////////////////////////////////////////////
+static byte
+					*ScanNames[] =		// Scan code names with single chars
+					{
+	"?","?","1","2","3","4","5","6","7","8","9","0","-","+","?","?",
+	"Q","W","E","R","T","Y","U","I","O","P","[","]","|","?","A","S",
+	"D","F","G","H","J","K","L",";","\"","?","?","?","Z","X","C","V",
+	"B","N","M",",",".","/","?","?","?","?","?","?","?","?","?","?",
+	"?","?","?","?","?","?","?","?","\xf","?","-","\x15","5","\x11","+","?",
+	"\x13","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?",
+	"?","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?",
+	"?","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?"
+					},	// DEBUG - consolidate these
+					far ExtScanCodes[] =	// Scan codes with >1 char names
+					{
+	1,0xe,0xf,0x1d,0x2a,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,
+	0x3f,0x40,0x41,0x42,0x43,0x44,0x57,0x59,0x46,0x1c,0x36,
+	0x37,0x38,0x47,0x49,0x4f,0x51,0x52,0x53,0x45,0x48,
+	0x50,0x4b,0x4d,0x00
+					},
+					*ExtScanNames[] =	// Names corresponding to ExtScanCodes
+					{
+	"Esc","BkSp","Tab","Ctrl","LShft","Space","CapsLk","F1","F2","F3","F4",
+	"F5","F6","F7","F8","F9","F10","F11","F12","ScrlLk","Enter","RShft",
+	"PrtSc","Alt","Home","PgUp","End","PgDn","Ins","Del","NumLk","Up",
+	"Down","Left","Right",""
+					};
+
+
+////////////////////////////////////////////////////////////////////
+//
+// Wolfenstein Control Panel!  Ta Da!
+//
+////////////////////////////////////////////////////////////////////
+void US_ControlPanel(byte scancode)
+{
+	int which,i,start;
+
+
+	if (ingame)
+		if (CP_CheckQuick(scancode))
+			return;
+
+	StartCPMusic(MENUSONG);
+	SetupControlPanel();
+
+	//
+	// F-KEYS FROM WITHIN GAME
+	//
+	switch(scancode)
+	{
+		case sc_F1:
+			#ifdef SPEAR
+			BossKey();
+			#else
+			#ifdef GOODTIMES
+			BossKey();
+			#else
+			HelpScreens();
+			#endif
+			#endif
+			goto finishup;
+
+		case sc_F2:
+			CP_SaveGame(0);
+			goto finishup;
+
+		case sc_F3:
+			CP_LoadGame(0);
+			goto finishup;
+
+		case sc_F4:
+			CP_Sound();
+			goto finishup;
+
+		case sc_F5:
+			CP_ChangeView();
+			goto finishup;
+
+		case sc_F6:
+			CP_Control();
+			goto finishup;
+
+		finishup:
+			CleanupControlPanel();
+			#ifdef SPEAR
+			UnCacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END);
+			#endif
+			return;
+	}
+
+#ifdef SPEAR
+	CacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END);
+#endif
+
+	DrawMainMenu();
+	MenuFadeIn();
+	StartGame=0;
+
+	//
+	// MAIN MENU LOOP
+	//
+	do
+	{
+		which=HandleMenu(&MainItems,&MainMenu[0],NULL);
+
+		#ifdef SPEAR
+		#ifndef SPEARDEMO
+		//
+		// EASTER EGG FOR SPEAR OF DESTINY!
+		//
+		if (Keyboard[sc_I] && Keyboard[sc_D])
+		{
+			VW_FadeOut();
+			StartCPMusic (XJAZNAZI_MUS);
+			UnCacheLump(OPTIONS_LUMP_START,OPTIONS_LUMP_END);
+			UnCacheLump(BACKDROP_LUMP_START,BACKDROP_LUMP_END);
+			MM_SortMem ();
+			ClearMemory ();
+
+
+			CA_CacheGrChunk (IDGUYS1PIC);
+			VWB_DrawPic(0,0,IDGUYS1PIC);
+			UNCACHEGRCHUNK(IDGUYS1PIC);
+
+			CA_CacheGrChunk (IDGUYS2PIC);
+			VWB_DrawPic(0,80,IDGUYS2PIC);
+			UNCACHEGRCHUNK(IDGUYS2PIC);
+
+			VW_UpdateScreen();
+
+			CA_CacheGrChunk (IDGUYSPALETTE);
+			VL_FadeIn(0,255,grsegs[IDGUYSPALETTE],30);
+			UNCACHEGRCHUNK(IDGUYSPALETTE);
+
+			while (Keyboard[sc_I] || Keyboard[sc_D]);
+			IN_ClearKeysDown();
+			IN_Ack();
+
+			VW_FadeOut();
+
+			CacheLump(BACKDROP_LUMP_START,BACKDROP_LUMP_END);
+			CacheLump(OPTIONS_LUMP_START,OPTIONS_LUMP_END);
+			DrawMainMenu();
+			StartCPMusic (MENUSONG);
+			MenuFadeIn();
+		}
+		#endif
+		#endif
+
+		switch(which)
+		{
+			case viewscores:
+				if (MainMenu[viewscores].routine == NULL)
+					if (CP_EndGame())
+						StartGame=1;
+
+				DrawMainMenu();
+				MenuFadeIn();
+				break;
+
+			case backtodemo:
+				#ifdef SPEAR
+				if (!ingame)
+				{
+					//
+					// DEALLOCATE ALL SOUNDS!
+					//
+					switch (SoundMode)
+					{
+						case sdm_PC:
+							start = STARTPCSOUNDS;
+							break;
+						case sdm_AdLib:
+							start = STARTADLIBSOUNDS;
+							break;
+					}
+
+					if (SoundMode != sdm_Off)
+						for (i=0;i<NUMSOUNDS;i++,start++)
+							if (audiosegs[start])
+								MM_SetPurge (&(memptr)audiosegs[start],3);		// make purgable
+				}
+				#endif
+
+				MM_SortMem();
+				StartGame=1;
+				if (!ingame)
+					StartCPMusic(INTROSONG);
+				VL_FadeOut(0,255,0,0,0,10);
+				break;
+
+			case -1:
+			case quit:
+				CP_Quit();
+				break;
+
+			default:
+				if (!StartGame)
+				{
+					DrawMainMenu();
+					MenuFadeIn();
+				}
+		}
+
+	//
+	// "EXIT OPTIONS" OR "NEW GAME" EXITS
+	//
+	} while(!StartGame);
+
+	//
+	// DEALLOCATE EVERYTHING
+	//
+	CleanupControlPanel();
+
+	//
+	// CHANGE MAINMENU ITEM
+	//
+	if (startgame || loadedgame)
+	{
+		#pragma warn -sus
+		MainMenu[viewscores].routine = NULL;
+		#ifndef JAPAN
+		_fstrcpy(MainMenu[viewscores].string,STR_EG);
+		#endif
+		#pragma warn +sus
+	}
+
+	// RETURN/START GAME EXECUTION
+
+#ifdef SPEAR
+	UnCacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END);
+	MM_SortMem ();
+#endif
+}
+
+
+////////////////////////
+//
+// DRAW MAIN MENU SCREEN
+//
+void DrawMainMenu(void)
+{
+#ifdef JAPAN
+	CA_CacheScreen(S_OPTIONSPIC);
+#else
+	ClearMScreen();
+
+	VWB_DrawPic(112,184,C_MOUSELBACKPIC);
+	DrawStripes(10);
+	VWB_DrawPic(84,0,C_OPTIONSPIC);
+
+	#ifdef SPANISH
+	DrawWindow(MENU_X-8,MENU_Y-3,MENU_W+8,MENU_H,BKGDCOLOR);
+	#else
+	DrawWindow(MENU_X-8,MENU_Y-3,MENU_W,MENU_H,BKGDCOLOR);
+	#endif
+#endif
+
+	//
+	// CHANGE "GAME" AND "DEMO"
+	//
+	if (ingame)
+	{
+		#ifndef JAPAN
+
+		#ifdef SPANISH
+		_fstrcpy(&MainMenu[backtodemo].string,STR_GAME);
+		#else
+		_fstrcpy(&MainMenu[backtodemo].string[8],STR_GAME);
+		#endif
+
+		#else
+		CA_CacheGrChunk(C_MRETGAMEPIC);
+		VWB_DrawPic(12*8,20*8,C_MRETGAMEPIC);
+		UNCACHEGRCHUNK(C_MRETGAMEPIC);
+		CA_CacheGrChunk(C_MENDGAMEPIC);
+		VWB_DrawPic(12*8,18*8,C_MENDGAMEPIC);
+		UNCACHEGRCHUNK(C_MENDGAMEPIC);
+		#endif
+		MainMenu[backtodemo].active=2;
+	}
+	else
+	{
+		#ifndef JAPAN
+		#ifdef SPANISH
+		_fstrcpy(&MainMenu[backtodemo].string,STR_BD);
+		#else
+		_fstrcpy(&MainMenu[backtodemo].string[8],STR_DEMO);
+		#endif
+		#else
+		CA_CacheGrChunk(C_MRETDEMOPIC);
+		VWB_DrawPic(12*8,20*8,C_MRETDEMOPIC);
+		UNCACHEGRCHUNK(C_MRETDEMOPIC);
+		CA_CacheGrChunk(C_MSCORESPIC);
+		VWB_DrawPic(12*8,18*8,C_MSCORESPIC);
+		UNCACHEGRCHUNK(C_MSCORESPIC);
+		#endif
+		MainMenu[backtodemo].active=1;
+	}
+
+	DrawMenu(&MainItems,&MainMenu[0]);
+	VW_UpdateScreen();
+}
+
+#ifndef GOODTIMES
+#ifndef SPEAR
+////////////////////////////////////////////////////////////////////
+//
+// READ THIS!
+//
+////////////////////////////////////////////////////////////////////
+void CP_ReadThis(void)
+{
+	StartCPMusic(CORNER_MUS);
+	HelpScreens();
+	StartCPMusic(MENUSONG);
+}
+#endif
+#endif
+
+#ifndef SPEAR
+#ifndef GOODTIMES
+#else
+////////////////////////////////////////////////////////////////////
+//
+// BOSS KEY
+//
+////////////////////////////////////////////////////////////////////
+void BossKey(void)
+{
+	SD_MusicOff();
+	_AX = 3;
+	geninterrupt(0x10);
+	printf("C>");
+	while (!Keyboard[sc_Escape])
+	IN_ClearKeysDown();
+
+	SD_MusicOn();
+	VL_SetVGAPlaneMode ();
+	VL_TestPaletteSet ();
+	VL_SetPalette (&gamepal);
+	LoadLatchMem();
+}
+#endif
+#endif
+
+////////////////////////////////////////////////////////////////////
+//
+// CHECK QUICK-KEYS & QUIT (WHILE IN A GAME)
+//
+////////////////////////////////////////////////////////////////////
+int CP_CheckQuick(unsigned scancode)
+{
+	switch(scancode)
+	{
+		//
+		// END GAME
+		//
+		case sc_F7:
+			CA_CacheGrChunk(STARTFONT+1);
+
+			WindowH=160;
+			#ifdef JAPAN
+			if (GetYorN(7,8,C_JAPQUITPIC))
+			#else
+			if (Confirm(ENDGAMESTR))
+			#endif
+			{
+				playstate = ex_died;
+				pickquick = gamestate.lives = 0;
+			}
+
+			DrawAllPlayBorder();
+			WindowH=200;
+			fontnumber=0;
+			MainMenu[savegame].active = 0;
+			return 1;
+
+		//
+		// QUICKSAVE
+		//
+		case sc_F8:
+			if (SaveGamesAvail[LSItems.curpos] && pickquick)
+			{
+				CA_CacheGrChunk(STARTFONT+1);
+				fontnumber = 1;
+				Message(STR_SAVING"...");
+				CP_SaveGame(1);
+				fontnumber=0;
+			}
+			else
+			{
+				#ifndef SPEAR
+				CA_CacheGrChunk(STARTFONT+1);
+				CA_CacheGrChunk(C_CURSOR1PIC);
+				CA_CacheGrChunk(C_CURSOR2PIC);
+				CA_CacheGrChunk(C_DISKLOADING1PIC);
+				CA_CacheGrChunk(C_DISKLOADING2PIC);
+				CA_CacheGrChunk(C_SAVEGAMEPIC);
+				CA_CacheGrChunk(C_MOUSELBACKPIC);
+				#else
+				CacheLump (BACKDROP_LUMP_START,BACKDROP_LUMP_END);
+				CA_CacheGrChunk(C_CURSOR1PIC);
+				#endif
+
+				VW_FadeOut ();
+
+				StartCPMusic(MENUSONG);
+				pickquick=CP_SaveGame(0);
+
+				SETFONTCOLOR(0,15);
+				IN_ClearKeysDown();
+				DrawPlayScreen ();
+
+				if (!startgame && !loadedgame)
+				{
+					VW_FadeIn ();
+					StartMusic ();
+				}
+
+				if (loadedgame)
+					playstate = ex_abort;
+				lasttimecount = TimeCount;
+
+				if (MousePresent)
+					Mouse(MDelta);	// Clear accumulated mouse movement
+
+				PM_CheckMainMem ();
+
+				#ifndef SPEAR
+				UNCACHEGRCHUNK(C_CURSOR1PIC);
+				UNCACHEGRCHUNK(C_CURSOR2PIC);
+				UNCACHEGRCHUNK(C_DISKLOADING1PIC);
+				UNCACHEGRCHUNK(C_DISKLOADING2PIC);
+				UNCACHEGRCHUNK(C_SAVEGAMEPIC);
+				UNCACHEGRCHUNK(C_MOUSELBACKPIC);
+				#else
+				UnCacheLump (BACKDROP_LUMP_START,BACKDROP_LUMP_END);
+				#endif
+			}
+			return 1;
+
+		//
+		// QUICKLOAD
+		//
+		case sc_F9:
+			if (SaveGamesAvail[LSItems.curpos] && pickquick)
+			{
+				char string[100]=STR_LGC;
+
+
+				CA_CacheGrChunk(STARTFONT+1);
+				fontnumber = 1;
+
+				strcat(string,SaveGameNames[LSItems.curpos]);
+				strcat(string,"\"?");
+
+				if (Confirm(string))
+					CP_LoadGame(1);
+
+				DrawAllPlayBorder();
+				fontnumber=0;
+			}
+			else
+			{
+				#ifndef SPEAR
+				CA_CacheGrChunk(STARTFONT+1);
+				CA_CacheGrChunk(C_CURSOR1PIC);
+				CA_CacheGrChunk(C_CURSOR2PIC);
+				CA_CacheGrChunk(C_DISKLOADING1PIC);
+				CA_CacheGrChunk(C_DISKLOADING2PIC);
+				CA_CacheGrChunk(C_LOADGAMEPIC);
+				CA_CacheGrChunk(C_MOUSELBACKPIC);
+				#else
+				CA_CacheGrChunk(C_CURSOR1PIC);
+				CacheLump (BACKDROP_LUMP_START,BACKDROP_LUMP_END);
+				#endif
+
+				VW_FadeOut ();
+
+				StartCPMusic(MENUSONG);
+				pickquick=CP_LoadGame(0);
+
+				SETFONTCOLOR(0,15);
+				IN_ClearKeysDown();
+				DrawPlayScreen ();
+
+				if (!startgame && !loadedgame)
+				{
+					VW_FadeIn ();
+					StartMusic ();
+				}
+
+				if (loadedgame)
+					playstate = ex_abort;
+
+				lasttimecount = TimeCount;
+
+				if (MousePresent)
+					Mouse(MDelta);	// Clear accumulated mouse movement
+				PM_CheckMainMem ();
+
+				#ifndef SPEAR
+				UNCACHEGRCHUNK(C_CURSOR1PIC);
+				UNCACHEGRCHUNK(C_CURSOR2PIC);
+				UNCACHEGRCHUNK(C_DISKLOADING1PIC);
+				UNCACHEGRCHUNK(C_DISKLOADING2PIC);
+				UNCACHEGRCHUNK(C_LOADGAMEPIC);
+				UNCACHEGRCHUNK(C_MOUSELBACKPIC);
+				#else
+				UnCacheLump (BACKDROP_LUMP_START,BACKDROP_LUMP_END);
+				#endif
+			}
+			return 1;
+
+		//
+		// QUIT
+		//
+		case sc_F10:
+			CA_CacheGrChunk(STARTFONT+1);
+
+			WindowX=WindowY=0;
+			WindowW=320;
+			WindowH=160;
+			#ifdef JAPAN
+			if (GetYorN(7,8,C_QUITMSGPIC))
+			#else
+				#ifdef SPANISH
+			if (Confirm(ENDGAMESTR))
+				#else
+			if (Confirm(endStrings[US_RndT()&0x7+(US_RndT()&1)]))
+				#endif
+			#endif
+			{
+				int i;
+
+
+				VW_UpdateScreen();
+				SD_MusicOff();
+				SD_StopSound();
+				MenuFadeOut();
+
+				//
+				// SHUT-UP THE ADLIB
+				//
+				for (i=1;i<=0xf5;i++)
+					alOut(i,0);
+				Quit(NULL);
+			}
+
+			DrawAllPlayBorder();
+			WindowH=200;
+			fontnumber=0;
+			return 1;
+		}
+
+	return 0;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//
+// END THE CURRENT GAME
+//
+////////////////////////////////////////////////////////////////////
+int CP_EndGame(void)
+{
+#ifdef JAPAN
+	if (!GetYorN(7,8,C_JAPQUITPIC))
+#else
+	if (!Confirm(ENDGAMESTR))
+#endif
+		return 0;
+
+	pickquick = gamestate.lives = 0;
+	playstate = ex_died;
+
+	#pragma warn -sus
+	MainMenu[savegame].active = 0;
+	MainMenu[viewscores].routine=CP_ViewScores;
+	#ifndef JAPAN
+	_fstrcpy(MainMenu[viewscores].string,STR_VS);
+	#endif
+	#pragma warn +sus
+
+	return 1;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//
+// VIEW THE HIGH SCORES
+//
+////////////////////////////////////////////////////////////////////
+void CP_ViewScores(void)
+{
+	fontnumber=0;
+
+#ifdef SPEAR
+	UnCacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END);
+	StartCPMusic (XAWARD_MUS);
+#else
+	StartCPMusic (ROSTER_MUS);
+#endif
+
+	DrawHighScores ();
+	VW_UpdateScreen ();
+	MenuFadeIn();
+	fontnumber=1;
+
+	IN_Ack();
+
+	StartCPMusic(MENUSONG);
+	MenuFadeOut();
+
+#ifdef SPEAR
+	CacheLump (BACKDROP_LUMP_START,BACKDROP_LUMP_END);
+	CacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END);
+#endif
+}
+
+
+////////////////////////////////////////////////////////////////////
+//
+// START A NEW GAME
+//
+////////////////////////////////////////////////////////////////////
+void CP_NewGame(void)
+{
+	int which,episode;
+
+#ifdef SPEAR
+	UnCacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END);
+#endif
+
+
+#ifndef SPEAR
+firstpart:
+
+	DrawNewEpisode();
+	do
+	{
+		which=HandleMenu(&NewEitems,&NewEmenu[0],NULL);
+		switch(which)
+		{
+			case -1:
+				MenuFadeOut();
+				return;
+
+			default:
+				if (!EpisodeSelect[which/2])
+				{
+					SD_PlaySound (NOWAYSND);
+					Message("Please select \"Read This!\"\n"
+							"from the Options menu to\n"
+							"find out how to order this\n"
+							"episode from Apogee.");
+					IN_ClearKeysDown();
+					IN_Ack();
+					DrawNewEpisode();
+					which = 0;
+				}
+				else
+				{
+					episode = which/2;
+					which = 1;
+				}
+				break;
+		}
+
+	} while (!which);
+
+	ShootSnd();
+
+	//
+	// ALREADY IN A GAME?
+	//
+	if (ingame)
+		#ifdef JAPAN
+		if (!GetYorN(7,8,C_JAPNEWGAMEPIC))
+		#else
+		if (!Confirm(CURGAME))
+		#endif
+		{
+			MenuFadeOut();
+			return;
+		}
+
+	MenuFadeOut();
+
+#else
+	episode = 0;
+
+	//
+	// ALREADY IN A GAME?
+	//
+	CacheLump (NEWGAME_LUMP_START,NEWGAME_LUMP_END);
+	DrawNewGame();
+	if (ingame)
+		if (!Confirm(CURGAME))
+		{
+			MenuFadeOut();
+			UnCacheLump (NEWGAME_LUMP_START,NEWGAME_LUMP_END);
+			CacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END);
+			return;
+		}
+
+#endif
+
+	DrawNewGame();
+	which=HandleMenu(&NewItems,&NewMenu[0],DrawNewGameDiff);
+	if (which<0)
+	{
+		MenuFadeOut();
+		#ifndef SPEAR
+		goto firstpart;
+		#else
+		UnCacheLump (NEWGAME_LUMP_START,NEWGAME_LUMP_END);
+		CacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END);
+		return;
+		#endif
+	}
+
+	ShootSnd();
+	NewGame(which,episode);
+	StartGame=1;
+	MenuFadeOut();
+
+	//
+	// CHANGE "READ THIS!" TO NORMAL COLOR
+	//
+	#ifndef SPEAR
+	#ifndef GOODTIMES
+	MainMenu[readthis].active=1;
+	#endif
+	#endif
+
+	pickquick = 0;
+
+#ifdef SPEAR
+	UnCacheLump (NEWGAME_LUMP_START,NEWGAME_LUMP_END);
+	CacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END);
+#endif
+}
+
+
+#ifndef SPEAR
+/////////////////////
+//
+// DRAW NEW EPISODE MENU
+//
+void DrawNewEpisode(void)
+{
+	int i;
+
+#ifdef JAPAN
+	CA_CacheScreen(S_EPISODEPIC);
+#else
+	ClearMScreen();
+	VWB_DrawPic(112,184,C_MOUSELBACKPIC);
+
+	DrawWindow(NE_X-4,NE_Y-4,NE_W+8,NE_H+8,BKGDCOLOR);
+	SETFONTCOLOR(READHCOLOR,BKGDCOLOR);
+	PrintY=2;
+	WindowX=0;
+	#ifdef SPANISH
+	US_CPrint("Cual episodio jugar?");
+	#else
+	US_CPrint("Which episode to play?");
+	#endif
+#endif
+
+	SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR);
+	DrawMenu(&NewEitems,&NewEmenu[0]);
+
+	for (i=0;i<6;i++)
+		VWB_DrawPic(NE_X+32,NE_Y+i*26,C_EPISODE1PIC+i);
+
+	VW_UpdateScreen();
+	MenuFadeIn();
+	WaitKeyUp();
+}
+#endif
+
+/////////////////////
+//
+// DRAW NEW GAME MENU
+//
+void DrawNewGame(void)
+{
+#ifdef JAPAN
+	CA_CacheScreen(S_SKILLPIC);
+#else
+	ClearMScreen();
+	VWB_DrawPic(112,184,C_MOUSELBACKPIC);
+
+	SETFONTCOLOR(READHCOLOR,BKGDCOLOR);
+	PrintX=NM_X+20;
+	PrintY=NM_Y-32;
+
+#ifndef SPEAR
+	#ifdef SPANISH
+	US_Print("Eres macho?");
+	#else
+	US_Print("How tough are you?");
+	#endif
+#else
+	VWB_DrawPic (PrintX,PrintY,C_HOWTOUGHPIC);
+#endif
+
+	DrawWindow(NM_X-5,NM_Y-10,NM_W,NM_H,BKGDCOLOR);
+#endif
+
+	DrawMenu(&NewItems,&NewMenu[0]);
+	DrawNewGameDiff(NewItems.curpos);
+	VW_UpdateScreen();
+	MenuFadeIn();
+	WaitKeyUp();
+}
+
+
+////////////////////////
+//
+// DRAW NEW GAME GRAPHIC
+//
+void DrawNewGameDiff(int w)
+{
+	VWB_DrawPic(NM_X+185,NM_Y+7,w+C_BABYMODEPIC);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//
+// HANDLE SOUND MENU
+//
+////////////////////////////////////////////////////////////////////
+void CP_Sound(void)
+{
+	int which,i;
+
+
+#ifdef SPEAR
+	UnCacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END);
+	CacheLump (SOUND_LUMP_START,SOUND_LUMP_END);
+#endif
+
+	DrawSoundMenu();
+	MenuFadeIn();
+	WaitKeyUp();
+
+	do
+	{
+		which=HandleMenu(&SndItems,&SndMenu[0],NULL);
+		//
+		// HANDLE MENU CHOICES
+		//
+		switch(which)
+		{
+			//
+			// SOUND EFFECTS
+			//
+			case 0:
+				if (SoundMode!=sdm_Off)
+				{
+					SD_WaitSoundDone();
+					SD_SetSoundMode(sdm_Off);
+					DrawSoundMenu();
+				}
+				break;
+			case 1:
+				if (SoundMode!=sdm_PC)
+				{
+					SD_WaitSoundDone();
+					SD_SetSoundMode(sdm_PC);
+					CA_LoadAllSounds();
+					DrawSoundMenu();
+					ShootSnd();
+				}
+				break;
+			case 2:
+				if (SoundMode!=sdm_AdLib)
+				{
+					SD_WaitSoundDone();
+					SD_SetSoundMode(sdm_AdLib);
+					CA_LoadAllSounds();
+					DrawSoundMenu();
+					ShootSnd();
+				}
+				break;
+
+			//
+			// DIGITIZED SOUND
+			//
+			case 5:
+				if (DigiMode!=sds_Off)
+				{
+					SD_SetDigiDevice(sds_Off);
+					DrawSoundMenu();
+				}
+				break;
+			case 6:
+				if (DigiMode!=sds_SoundSource)
+				{
+					SD_SetDigiDevice(sds_SoundSource);
+					DrawSoundMenu();
+					ShootSnd();
+				}
+				break;
+			case 7:
+				if (DigiMode!=sds_SoundBlaster)
+				{
+					SD_SetDigiDevice(sds_SoundBlaster);
+					DrawSoundMenu();
+					ShootSnd();
+				}
+				break;
+
+			//
+			// MUSIC
+			//
+			case 10:
+				if (MusicMode!=smm_Off)
+				{
+					SD_SetMusicMode(smm_Off);
+					DrawSoundMenu();
+					ShootSnd();
+				}
+				break;
+			case 11:
+				if (MusicMode!=smm_AdLib)
+				{
+					SD_SetMusicMode(smm_AdLib);
+					DrawSoundMenu();
+					ShootSnd();
+					StartCPMusic(MENUSONG);
+				}
+				break;
+		}
+	} while(which>=0);
+
+	MenuFadeOut();
+
+#ifdef SPEAR
+	UnCacheLump (SOUND_LUMP_START,SOUND_LUMP_END);
+	CacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END);
+#endif
+}
+
+
+//////////////////////
+//
+// DRAW THE SOUND MENU
+//
+void DrawSoundMenu(void)
+{
+	int i,on;
+
+
+#ifdef JAPAN
+	CA_CacheScreen(S_SOUNDPIC);
+#else
+	//
+	// DRAW SOUND MENU
+	//
+	ClearMScreen();
+	VWB_DrawPic(112,184,C_MOUSELBACKPIC);
+
+	DrawWindow(SM_X-8,SM_Y1-3,SM_W,SM_H1,BKGDCOLOR);
+	DrawWindow(SM_X-8,SM_Y2-3,SM_W,SM_H2,BKGDCOLOR);
+	DrawWindow(SM_X-8,SM_Y3-3,SM_W,SM_H3,BKGDCOLOR);
+#endif
+
+	//
+	// IF NO ADLIB, NON-CHOOSENESS!
+	//
+	if (!AdLibPresent && !SoundBlasterPresent)
+	{
+		SndMenu[2].active=SndMenu[10].active=SndMenu[11].active=0;
+	}
+
+	if (!SoundSourcePresent)
+		SndMenu[6].active=0;
+
+	if (!SoundBlasterPresent)
+		SndMenu[7].active=0;
+
+	if (!SoundSourcePresent && !SoundBlasterPresent)
+		SndMenu[5].active=0;
+
+	DrawMenu(&SndItems,&SndMenu[0]);
+#ifndef JAPAN
+	VWB_DrawPic(100,SM_Y1-20,C_FXTITLEPIC);
+	VWB_DrawPic(100,SM_Y2-20,C_DIGITITLEPIC);
+	VWB_DrawPic(100,SM_Y3-20,C_MUSICTITLEPIC);
+#endif
+
+	for (i=0;i<SndItems.amount;i++)
+#ifdef JAPAN
+		if (i!=3 && i!=4 && i!=8 && i!=9)
+#else
+		if (SndMenu[i].string[0])
+#endif
+		{
+			//
+			// DRAW SELECTED/NOT SELECTED GRAPHIC BUTTONS
+			//
+			on=0;
+			switch(i)
+			{
+				//
+				// SOUND EFFECTS
+				//
+				case 0: if (SoundMode==sdm_Off) on=1; break;
+				case 1: if (SoundMode==sdm_PC) on=1; break;
+				case 2: if (SoundMode==sdm_AdLib) on=1; break;
+
+				//
+				// DIGITIZED SOUND
+				//
+				case 5: if (DigiMode==sds_Off) on=1; break;
+				case 6: if (DigiMode==sds_SoundSource) on=1; break;
+				case 7: if (DigiMode==sds_SoundBlaster) on=1; break;
+
+				//
+				// MUSIC
+				//
+				case 10: if (MusicMode==smm_Off) on=1; break;
+				case 11: if (MusicMode==smm_AdLib) on=1; break;
+			}
+
+			if (on)
+				VWB_DrawPic(SM_X+24,SM_Y1+i*13+2,C_SELECTEDPIC);
+			else
+				VWB_DrawPic(SM_X+24,SM_Y1+i*13+2,C_NOTSELECTEDPIC);
+		}
+
+	DrawMenuGun(&SndItems);
+	VW_UpdateScreen();
+}
+
+
+//
+// DRAW LOAD/SAVE IN PROGRESS
+//
+void DrawLSAction(int which)
+{
+	#define LSA_X	96
+	#define LSA_Y	80
+	#define LSA_W	130
+	#define LSA_H	42
+
+	DrawWindow(LSA_X,LSA_Y,LSA_W,LSA_H,TEXTCOLOR);
+	DrawOutline(LSA_X,LSA_Y,LSA_W,LSA_H,0,HIGHLIGHT);
+	VWB_DrawPic(LSA_X+8,LSA_Y+5,C_DISKLOADING1PIC);
+
+	fontnumber=1;
+	SETFONTCOLOR(0,TEXTCOLOR);
+	PrintX=LSA_X+46;
+	PrintY=LSA_Y+13;
+
+	if (!which)
+		US_Print(STR_LOADING"...");
+	else
+		US_Print(STR_SAVING"...");
+
+	VW_UpdateScreen();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//
+// LOAD SAVED GAMES
+//
+////////////////////////////////////////////////////////////////////
+int CP_LoadGame(int quick)
+{
+	int handle,which,exit=0;
+	char name[13];
+
+
+	strcpy(name,SaveName);
+
+	//
+	// QUICKLOAD?
+	//
+	if (quick)
+	{
+		which=LSItems.curpos;
+
+		if (SaveGamesAvail[which])
+		{
+			name[7]=which+'0';
+			handle=open(name,O_BINARY);
+			lseek(handle,32,SEEK_SET);
+			loadedgame=true;
+			LoadTheGame(handle,0,0);
+			loadedgame=false;
+			close(handle);
+
+			DrawFace ();
+			DrawHealth ();
+			DrawLives ();
+			DrawLevel ();
+			DrawAmmo ();
+			DrawKeys ();
+			DrawWeapon ();
+			DrawScore ();
+			return 1;
+		}
+	}
+
+
+#ifdef SPEAR
+	UnCacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END);
+	CacheLump (LOADSAVE_LUMP_START,LOADSAVE_LUMP_END);
+#endif
+
+	DrawLoadSaveScreen(0);
+
+	do
+	{
+		which=HandleMenu(&LSItems,&LSMenu[0],TrackWhichGame);
+		if (which>=0 && SaveGamesAvail[which])
+		{
+			ShootSnd();
+			name[7]=which+'0';
+
+			handle=open(name,O_BINARY);
+			lseek(handle,32,SEEK_SET);
+
+			DrawLSAction(0);
+			loadedgame=true;
+
+			LoadTheGame(handle,LSA_X+8,LSA_Y+5);
+			close(handle);
+
+			StartGame=1;
+			ShootSnd();
+			//
+			// CHANGE "READ THIS!" TO NORMAL COLOR
+			//
+
+			#ifndef SPEAR
+			#ifndef GOODTIMES
+			MainMenu[readthis].active=1;
+			#endif
+			#endif
+
+			exit=1;
+			break;
+		}
+
+	} while(which>=0);
+
+	MenuFadeOut();
+
+#ifdef SPEAR
+	UnCacheLump (LOADSAVE_LUMP_START,LOADSAVE_LUMP_END);
+	CacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END);
+#endif
+
+	return exit;
+}
+
+
+///////////////////////////////////
+//
+// HIGHLIGHT CURRENT SELECTED ENTRY
+//
+void TrackWhichGame(int w)
+{
+	static int lastgameon=0;
+
+	PrintLSEntry(lastgameon,TEXTCOLOR);
+	PrintLSEntry(w,HIGHLIGHT);
+
+	lastgameon=w;
+}
+
+
+////////////////////////////
+//
+// DRAW THE LOAD/SAVE SCREEN
+//
+void DrawLoadSaveScreen(int loadsave)
+{
+	#define DISKX	100
+	#define DISKY	0
+
+	int i;
+
+
+	ClearMScreen();
+	fontnumber=1;
+	VWB_DrawPic(112,184,C_MOUSELBACKPIC);
+	DrawWindow(LSM_X-10,LSM_Y-5,LSM_W,LSM_H,BKGDCOLOR);
+	DrawStripes(10);
+
+	if (!loadsave)
+		VWB_DrawPic(60,0,C_LOADGAMEPIC);
+	else
+		VWB_DrawPic(60,0,C_SAVEGAMEPIC);
+
+	for (i=0;i<10;i++)
+		PrintLSEntry(i,TEXTCOLOR);
+
+	DrawMenu(&LSItems,&LSMenu[0]);
+	VW_UpdateScreen();
+	MenuFadeIn();
+	WaitKeyUp();
+}
+
+
+///////////////////////////////////////////
+//
+// PRINT LOAD/SAVE GAME ENTRY W/BOX OUTLINE
+//
+void PrintLSEntry(int w,int color)
+{
+	SETFONTCOLOR(color,BKGDCOLOR);
+	DrawOutline(LSM_X+LSItems.indent,LSM_Y+w*13,LSM_W-LSItems.indent-15,11,color,color);
+	PrintX=LSM_X+LSItems.indent+2;
+	PrintY=LSM_Y+w*13+1;
+	fontnumber=0;
+
+	if (SaveGamesAvail[w])
+		US_Print(SaveGameNames[w]);
+	else
+		US_Print("      - "STR_EMPTY" -");
+
+	fontnumber=1;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//
+// SAVE CURRENT GAME
+//
+////////////////////////////////////////////////////////////////////
+int CP_SaveGame(int quick)
+{
+	int handle,which,exit=0;
+	unsigned nwritten;
+	char name[13],input[32];
+
+
+	strcpy(name,SaveName);
+
+	//
+	// QUICKSAVE?
+	//
+	if (quick)
+	{
+		which=LSItems.curpos;
+
+		if (SaveGamesAvail[which])
+		{
+			name[7]=which+'0';
+			unlink(name);
+			handle=creat(name,S_IREAD|S_IWRITE);
+
+			strcpy(input,&SaveGameNames[which][0]);
+
+			_dos_write(handle,(void far *)input,32,&nwritten);
+			lseek(handle,32,SEEK_SET);
+			SaveTheGame(handle,0,0);
+			close(handle);
+
+			return 1;
+		}
+	}
+
+
+#ifdef SPEAR
+	UnCacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END);
+	CacheLump (LOADSAVE_LUMP_START,LOADSAVE_LUMP_END);
+#endif
+
+	DrawLoadSaveScreen(1);
+
+	do
+	{
+		which=HandleMenu(&LSItems,&LSMenu[0],TrackWhichGame);
+		if (which>=0)
+		{
+			//
+			// OVERWRITE EXISTING SAVEGAME?
+			//
+			if (SaveGamesAvail[which])
+				#ifdef JAPAN
+				if (!GetYorN(7,8,C_JAPSAVEOVERPIC))
+				#else
+				if (!Confirm(GAMESVD))
+				#endif
+				{
+					DrawLoadSaveScreen(1);
+					continue;
+				}
+				else
+				{
+					DrawLoadSaveScreen(1);
+					PrintLSEntry(which,HIGHLIGHT);
+					VW_UpdateScreen();
+				}
+
+			ShootSnd();
+
+			strcpy(input,&SaveGameNames[which][0]);
+			name[7]=which+'0';
+
+			fontnumber=0;
+			if (!SaveGamesAvail[which])
+				VWB_Bar(LSM_X+LSItems.indent+1,LSM_Y+which*13+1,LSM_W-LSItems.indent-16,10,BKGDCOLOR);
+			VW_UpdateScreen();
+
+			if (US_LineInput(LSM_X+LSItems.indent+2,LSM_Y+which*13+1,input,input,true,31,LSM_W-LSItems.indent-30))
+			{
+				SaveGamesAvail[which]=1;
+				strcpy(&SaveGameNames[which][0],input);
+
+				unlink(name);
+				handle=creat(name,S_IREAD|S_IWRITE);
+				_dos_write(handle,(void far *)input,32,&nwritten);
+				lseek(handle,32,SEEK_SET);
+
+				DrawLSAction(1);
+				SaveTheGame(handle,LSA_X+8,LSA_Y+5);
+
+				close(handle);
+
+				ShootSnd();
+				exit=1;
+			}
+			else
+			{
+				VWB_Bar(LSM_X+LSItems.indent+1,LSM_Y+which*13+1,LSM_W-LSItems.indent-16,10,BKGDCOLOR);
+				PrintLSEntry(which,HIGHLIGHT);
+				VW_UpdateScreen();
+				SD_PlaySound(ESCPRESSEDSND);
+				continue;
+			}
+
+			fontnumber=1;
+			break;
+		}
+
+	} while(which>=0);
+
+	MenuFadeOut();
+
+#ifdef SPEAR
+	UnCacheLump (LOADSAVE_LUMP_START,LOADSAVE_LUMP_END);
+	CacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END);
+#endif
+
+	return exit;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//
+// CALIBRATE JOYSTICK
+//
+////////////////////////////////////////////////////////////////////
+int CalibrateJoystick(void)
+{
+	#define CALX	85
+	#define CALY	40
+	#define CALW	158
+	#define CALH	140
+
+	unsigned xmin,ymin,xmax,ymax,jb;
+
+
+
+	#ifdef JAPAN
+	VWB_DrawPic(CALX,CALY,C_JOY0PIC);
+	#else
+	DrawWindow(CALX-5,CALY-5,CALW,CALH,TEXTCOLOR);
+	DrawOutline(CALX-5,CALY-5,CALW,CALH,0,HIGHLIGHT);
+	SETFONTCOLOR(0,TEXTCOLOR);
+
+	WindowX = PrintX = CALX;
+	WindowW = CALW;
+	WindowH = CALH;
+	WindowY = PrintY = CALY;
+	US_Print("    "STR_CALIB"\n    "STR_JOYST"\n");
+	VWB_DrawPic(CALX+40,CALY+30,C_JOY1PIC);
+	PrintY = CALY+80;
+	US_Print(STR_MOVEJOY);
+	SETFONTCOLOR(BKGDCOLOR,TEXTCOLOR);
+	US_Print("   "STR_ESCEXIT);
+	#endif
+	VW_UpdateScreen();
+
+	do
+	{
+		jb=IN_JoyButtons();
+		if (Keyboard[sc_Escape])
+			return 0;
+		#ifndef SPEAR
+		if (Keyboard[sc_Tab] && Keyboard[sc_P] && MS_CheckParm("goobers"))
+			PicturePause();
+		#endif
+
+	} while(!(jb&1));
+
+	SD_PlaySound(SHOOTSND);
+	IN_GetJoyAbs(joystickport,&xmin,&ymin);
+
+
+	#ifdef JAPAN
+	VWB_DrawPic(CALX,CALY,C_JOY1PIC);
+	#else
+	DrawWindow(CALX-5,CALY-5,CALW,CALH,TEXTCOLOR);
+	DrawOutline(CALX-5,CALY-5,CALW,CALH,0,HIGHLIGHT);
+	SETFONTCOLOR(0,TEXTCOLOR);
+
+	PrintX = CALX;
+	PrintY = CALY;
+	US_Print("    "STR_CALIB"\n    "STR_JOYST"\n");
+	VWB_DrawPic(CALX+40,CALY+30,C_JOY2PIC);
+	PrintY = CALY+80;
+	US_Print(STR_MOVEJOY2);
+	SETFONTCOLOR(BKGDCOLOR,TEXTCOLOR);
+	US_Print("   "STR_ESCEXIT);
+	#endif
+	VW_UpdateScreen();
+
+	do
+	{
+		jb=IN_JoyButtons();
+		if (Keyboard[sc_Escape])
+			return 0;
+		#ifndef SPEAR
+		if (Keyboard[sc_Tab] && Keyboard[sc_P] && MS_CheckParm("goobers"))
+			PicturePause();
+		#endif
+	} while(!(jb&2));
+
+	IN_GetJoyAbs(joystickport,&xmax,&ymax);
+	SD_PlaySound(SHOOTSND);
+
+	while (IN_JoyButtons());
+
+	//
+	// ASSIGN ACTUAL VALUES HERE
+	//
+	if ((xmin != xmax) && (ymin != ymax))
+		IN_SetupJoy(joystickport,xmin,xmax,ymin,ymax);
+	else
+		return 0;
+
+	return 1;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//
+// DEFINE CONTROLS
+//
+////////////////////////////////////////////////////////////////////
+void CP_Control(void)
+{
+	#define CTL_SPC	70
+	enum {MOUSEENABLE,JOYENABLE,USEPORT2,PADENABLE,MOUSESENS,CUSTOMIZE};
+	int i,which;
+
+
+#ifdef SPEAR
+	UnCacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END);
+	CacheLump (CONTROL_LUMP_START,CONTROL_LUMP_END);
+#endif
+
+	DrawCtlScreen();
+	MenuFadeIn();
+	WaitKeyUp();
+
+	do
+	{
+		which=HandleMenu(&CtlItems,&CtlMenu[0],NULL);
+		switch(which)
+		{
+			case MOUSEENABLE:
+				mouseenabled^=1;
+				_CX=_DX=CENTER;
+				Mouse(4);
+				DrawCtlScreen();
+				CusItems.curpos=-1;
+				ShootSnd();
+				break;
+
+			case JOYENABLE:
+				joystickenabled^=1;
+				if (joystickenabled)
+					if (!CalibrateJoystick())
+						joystickenabled = 0;
+				DrawCtlScreen();
+				CusItems.curpos=-1;
+				ShootSnd();
+				break;
+
+			case USEPORT2:
+				joystickport^=1;
+				DrawCtlScreen();
+				ShootSnd();
+				break;
+
+			case PADENABLE:
+				joypadenabled^=1;
+				DrawCtlScreen();
+				ShootSnd();
+				break;
+
+			case MOUSESENS:
+			case CUSTOMIZE:
+				DrawCtlScreen();
+				MenuFadeIn();
+				WaitKeyUp();
+				break;
+		}
+	} while(which>=0);
+
+	MenuFadeOut();
+
+#ifdef SPEAR
+	UnCacheLump (CONTROL_LUMP_START,CONTROL_LUMP_END);
+	CacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END);
+#endif
+}
+
+
+////////////////////////////////
+//
+// DRAW MOUSE SENSITIVITY SCREEN
+//
+void DrawMouseSens(void)
+{
+#ifdef JAPAN
+	CA_CacheScreen(S_MOUSESENSPIC);
+#else
+	ClearMScreen();
+	VWB_DrawPic(112,184,C_MOUSELBACKPIC);
+	#ifdef SPANISH
+	DrawWindow(10,80,300,43,BKGDCOLOR);
+	#else
+	DrawWindow(10,80,300,30,BKGDCOLOR);
+	#endif
+
+	WindowX=0;
+	WindowW=320;
+	PrintY=82;
+	SETFONTCOLOR(READCOLOR,BKGDCOLOR);
+	US_CPrint(STR_MOUSEADJ);
+
+	SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR);
+	#ifdef SPANISH
+	PrintX=14;
+	PrintY=95+13;
+	US_Print(STR_SLOW);
+	PrintX=252;
+	US_Print(STR_FAST);
+	#else
+	PrintX=14;
+	PrintY=95;
+	US_Print(STR_SLOW);
+	PrintX=269;
+	US_Print(STR_FAST);
+	#endif
+#endif
+
+	VWB_Bar(60,97,200,10,TEXTCOLOR);
+	DrawOutline(60,97,200,10,0,HIGHLIGHT);
+	DrawOutline(60+20*mouseadjustment,97,20,10,0,READCOLOR);
+	VWB_Bar(61+20*mouseadjustment,98,19,9,READHCOLOR);
+
+	VW_UpdateScreen();
+	MenuFadeIn();
+}
+
+
+///////////////////////////
+//
+// ADJUST MOUSE SENSITIVITY
+//
+void MouseSensitivity(void)
+{
+	ControlInfo ci;
+	int exit=0,oldMA;
+
+
+	oldMA=mouseadjustment;
+	DrawMouseSens();
+	do
+	{
+		ReadAnyControl(&ci);
+		switch(ci.dir)
+		{
+			case dir_North:
+			case dir_West:
+				if (mouseadjustment)
+				{
+					mouseadjustment--;
+					VWB_Bar(60,97,200,10,TEXTCOLOR);
+					DrawOutline(60,97,200,10,0,HIGHLIGHT);
+					DrawOutline(60+20*mouseadjustment,97,20,10,0,READCOLOR);
+					VWB_Bar(61+20*mouseadjustment,98,19,9,READHCOLOR);
+					VW_UpdateScreen();
+					SD_PlaySound(MOVEGUN1SND);
+					while(Keyboard[sc_LeftArrow]);
+					WaitKeyUp();
+				}
+				break;
+
+			case dir_South:
+			case dir_East:
+				if (mouseadjustment<9)
+				{
+					mouseadjustment++;
+					VWB_Bar(60,97,200,10,TEXTCOLOR);
+					DrawOutline(60,97,200,10,0,HIGHLIGHT);
+					DrawOutline(60+20*mouseadjustment,97,20,10,0,READCOLOR);
+					VWB_Bar(61+20*mouseadjustment,98,19,9,READHCOLOR);
+					VW_UpdateScreen();
+					SD_PlaySound(MOVEGUN1SND);
+					while(Keyboard[sc_RightArrow]);
+					WaitKeyUp();
+				}
+				break;
+		}
+
+		#ifndef SPEAR
+		if (Keyboard[sc_Tab] && Keyboard[sc_P] && MS_CheckParm("goobers"))
+		#else
+		if (Keyboard[sc_Tab] && Keyboard[sc_P] && MS_CheckParm("debugmode"))
+		#endif
+			PicturePause();
+
+		if (ci.button0 || Keyboard[sc_Space] || Keyboard[sc_Enter])
+			exit=1;
+		else
+		if (ci.button1 || Keyboard[sc_Escape])
+			exit=2;
+
+	} while(!exit);
+
+	if (exit==2)
+	{
+		mouseadjustment=oldMA;
+		SD_PlaySound(ESCPRESSEDSND);
+	}
+	else
+		SD_PlaySound(SHOOTSND);
+
+	WaitKeyUp();
+	MenuFadeOut();
+}
+
+
+///////////////////////////
+//
+// DRAW CONTROL MENU SCREEN
+//
+void DrawCtlScreen(void)
+{
+ int i,x,y;
+
+
+#ifdef JAPAN
+	CA_CacheScreen(S_CONTROLPIC);
+#else
+ ClearMScreen();
+ DrawStripes(10);
+ VWB_DrawPic(80,0,C_CONTROLPIC);
+ VWB_DrawPic(112,184,C_MOUSELBACKPIC);
+ DrawWindow(CTL_X-8,CTL_Y-5,CTL_W,CTL_H,BKGDCOLOR);
+#endif
+ WindowX=0;
+ WindowW=320;
+ SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR);
+
+ if (JoysPresent[0])
+   CtlMenu[1].active=
+   CtlMenu[2].active=
+   CtlMenu[3].active=1;
+
+ CtlMenu[2].active=CtlMenu[3].active=joystickenabled;
+
+ if (MousePresent)
+ {
+  CtlMenu[4].active=
+  CtlMenu[0].active=1;
+ }
+
+ CtlMenu[4].active=mouseenabled;
+
+
+ DrawMenu(&CtlItems,&CtlMenu[0]);
+
+
+ x=CTL_X+CtlItems.indent-24;
+ y=CTL_Y+3;
+ if (mouseenabled)
+   VWB_DrawPic(x,y,C_SELECTEDPIC);
+ else
+   VWB_DrawPic(x,y,C_NOTSELECTEDPIC);
+
+ y=CTL_Y+16;
+ if (joystickenabled)
+   VWB_DrawPic(x,y,C_SELECTEDPIC);
+ else
+   VWB_DrawPic(x,y,C_NOTSELECTEDPIC);
+
+ y=CTL_Y+29;
+ if (joystickport)
+   VWB_DrawPic(x,y,C_SELECTEDPIC);
+ else
+   VWB_DrawPic(x,y,C_NOTSELECTEDPIC);
+
+ y=CTL_Y+42;
+ if (joypadenabled)
+   VWB_DrawPic(x,y,C_SELECTEDPIC);
+ else
+   VWB_DrawPic(x,y,C_NOTSELECTEDPIC);
+
+ //
+ // PICK FIRST AVAILABLE SPOT
+ //
+ if (CtlItems.curpos<0 || !CtlMenu[CtlItems.curpos].active)
+   for (i=0;i<6;i++)
+	 if (CtlMenu[i].active)
+	 {
+	  CtlItems.curpos=i;
+	  break;
+	 }
+
+ DrawMenuGun(&CtlItems);
+ VW_UpdateScreen();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//
+// CUSTOMIZE CONTROLS
+//
+////////////////////////////////////////////////////////////////////
+enum {FIRE,STRAFE,RUN,OPEN};
+char mbarray[4][3]={"b0","b1","b2","b3"},
+	   order[4]={RUN,OPEN,FIRE,STRAFE};
+
+
+void CustomControls(void)
+{
+ int which;
+
+
+ DrawCustomScreen();
+ do
+ {
+  which=HandleMenu(&CusItems,&CusMenu[0],FixupCustom);
+  switch(which)
+  {
+   case 0:
+	 DefineMouseBtns();
+	 DrawCustMouse(1);
+	 break;
+   case 3:
+	 DefineJoyBtns();
+	 DrawCustJoy(0);
+	 break;
+   case 6:
+	 DefineKeyBtns();
+	 DrawCustKeybd(0);
+	 break;
+   case 8:
+	 DefineKeyMove();
+	 DrawCustKeys(0);
+  }
+ } while(which>=0);
+
+
+
+ MenuFadeOut();
+}
+
+
+////////////////////////
+//
+// DEFINE THE MOUSE BUTTONS
+//
+void DefineMouseBtns(void)
+{
+ CustomCtrls mouseallowed={0,1,1,1};
+ EnterCtrlData(2,&mouseallowed,DrawCustMouse,PrintCustMouse,MOUSE);
+}
+
+
+////////////////////////
+//
+// DEFINE THE JOYSTICK BUTTONS
+//
+void DefineJoyBtns(void)
+{
+ CustomCtrls joyallowed={1,1,1,1};
+ EnterCtrlData(5,&joyallowed,DrawCustJoy,PrintCustJoy,JOYSTICK);
+}
+
+
+////////////////////////
+//
+// DEFINE THE KEYBOARD BUTTONS
+//
+void DefineKeyBtns(void)
+{
+ CustomCtrls keyallowed={1,1,1,1};
+ EnterCtrlData(8,&keyallowed,DrawCustKeybd,PrintCustKeybd,KEYBOARDBTNS);
+}
+
+
+////////////////////////
+//
+// DEFINE THE KEYBOARD BUTTONS
+//
+void DefineKeyMove(void)
+{
+	CustomCtrls keyallowed={1,1,1,1};
+	EnterCtrlData(10,&keyallowed,DrawCustKeys,PrintCustKeys,KEYBOARDMOVE);
+}
+
+
+////////////////////////
+//
+// ENTER CONTROL DATA FOR ANY TYPE OF CONTROL
+//
+enum {FWRD,RIGHT,BKWD,LEFT};
+int moveorder[4]={LEFT,RIGHT,FWRD,BKWD};
+
+void EnterCtrlData(int index,CustomCtrls *cust,void (*DrawRtn)(int),void (*PrintRtn)(int),int type)
+{
+ int j,exit,tick,redraw,which,x,picked;
+ ControlInfo ci;
+
+
+ ShootSnd();
+ PrintY=CST_Y+13*index;
+ IN_ClearKeysDown();
+ exit=0;
+ redraw=1;
+ //
+ // FIND FIRST SPOT IN ALLOWED ARRAY
+ //
+ for (j=0;j<4;j++)
+   if (cust->allowed[j])
+   {
+	which=j;
+	break;
+   }
+
+ do
+ {
+  if (redraw)
+  {
+   x=CST_START+CST_SPC*which;
+   DrawWindow(5,PrintY-1,310,13,BKGDCOLOR);
+
+   DrawRtn(1);
+   DrawWindow(x-2,PrintY,CST_SPC,11,TEXTCOLOR);
+   DrawOutline(x-2,PrintY,CST_SPC,11,0,HIGHLIGHT);
+   SETFONTCOLOR(0,TEXTCOLOR);
+   PrintRtn(which);
+   PrintX=x;
+   SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR);
+   VW_UpdateScreen();
+   WaitKeyUp();
+   redraw=0;
+  }
+
+  ReadAnyControl(&ci);
+
+  if (type==MOUSE || type==JOYSTICK)
+	if (IN_KeyDown(sc_Enter)||IN_KeyDown(sc_Control)||IN_KeyDown(sc_Alt))
+	{
+	 IN_ClearKeysDown();
+	 ci.button0=ci.button1=false;
+	}
+
+  //
+  // CHANGE BUTTON VALUE?
+  //
+  if ((ci.button0|ci.button1|ci.button2|ci.button3)||
+	  ((type==KEYBOARDBTNS||type==KEYBOARDMOVE) && LastScan==sc_Enter))
+  {
+   tick=TimeCount=picked=0;
+   SETFONTCOLOR(0,TEXTCOLOR);
+
+   do
+   {
+	int button,result=0;
+
+
+	if (type==KEYBOARDBTNS||type==KEYBOARDMOVE)
+	  IN_ClearKeysDown();
+
+	//
+	// FLASH CURSOR
+	//
+	if (TimeCount>10)
+	{
+	 switch(tick)
+	 {
+	  case 0:
+	VWB_Bar(x,PrintY+1,CST_SPC-2,10,TEXTCOLOR);
+	break;
+	  case 1:
+	PrintX=x;
+	US_Print("?");
+	SD_PlaySound(HITWALLSND);
+	 }
+	 tick^=1;
+	 TimeCount=0;
+	 VW_UpdateScreen();
+	}
+
+	//
+	// WHICH TYPE OF INPUT DO WE PROCESS?
+	//
+	switch(type)
+	{
+	 case MOUSE:
+	   Mouse(3);
+	   button=_BX;
+	   switch(button)
+	   {
+	case 1: result=1; break;
+	case 2: result=2; break;
+	case 4: result=3; break;
+	   }
+
+	   if (result)
+	   {
+	int z;
+
+
+	for (z=0;z<4;z++)
+	  if (order[which]==buttonmouse[z])
+	  {
+	   buttonmouse[z]=bt_nobutton;
+	   break;
+	  }
+
+	buttonmouse[result-1]=order[which];
+	picked=1;
+	SD_PlaySound(SHOOTDOORSND);
+	   }
+	   break;
+
+	 case JOYSTICK:
+	   if (ci.button0) result=1;
+	   else
+	   if (ci.button1) result=2;
+	   else
+	   if (ci.button2) result=3;
+	   else
+	   if (ci.button3) result=4;
+
+	   if (result)
+	   {
+	int z;
+
+
+	for (z=0;z<4;z++)
+	  if (order[which]==buttonjoy[z])
+	  {
+	   buttonjoy[z]=bt_nobutton;
+	   break;
+	  }
+
+	buttonjoy[result-1]=order[which];
+	picked=1;
+	SD_PlaySound(SHOOTDOORSND);
+	   }
+	   break;
+
+	 case KEYBOARDBTNS:
+	   if (LastScan)
+	   {
+	buttonscan[order[which]]=LastScan;
+	picked=1;
+	ShootSnd();
+	IN_ClearKeysDown();
+	   }
+	   break;
+
+	 case KEYBOARDMOVE:
+	   if (LastScan)
+	   {
+	dirscan[moveorder[which]]=LastScan;
+	picked=1;
+	ShootSnd();
+	IN_ClearKeysDown();
+	   }
+	   break;
+	}
+
+	//
+	// EXIT INPUT?
+	//
+	if (IN_KeyDown(sc_Escape))
+	{
+	 picked=1;
+	 continue;
+	}
+
+   } while(!picked);
+
+   SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR);
+   redraw=1;
+   WaitKeyUp();
+   continue;
+  }
+
+  if (ci.button1 || IN_KeyDown(sc_Escape))
+	exit=1;
+
+  //
+  // MOVE TO ANOTHER SPOT?
+  //
+  switch(ci.dir)
+  {
+   case dir_West:
+	 do
+	 {
+	  which--;
+	  if (which<0)
+	which=3;
+	 } while(!cust->allowed[which]);
+	 redraw=1;
+	 SD_PlaySound(MOVEGUN1SND);
+	 while(ReadAnyControl(&ci),ci.dir!=dir_None);
+	 IN_ClearKeysDown();
+	 break;
+
+   case dir_East:
+	 do
+	 {
+	  which++;
+	  if (which>3)
+	which=0;
+	 } while(!cust->allowed[which]);
+	 redraw=1;
+	 SD_PlaySound(MOVEGUN1SND);
+	 while(ReadAnyControl(&ci),ci.dir!=dir_None);
+	 IN_ClearKeysDown();
+	 break;
+   case dir_North:
+   case dir_South:
+	 exit=1;
+  }
+ } while(!exit);
+
+ SD_PlaySound(ESCPRESSEDSND);
+ WaitKeyUp();
+ DrawWindow(5,PrintY-1,310,13,BKGDCOLOR);
+}
+
+
+////////////////////////
+//
+// FIXUP GUN CURSOR OVERDRAW SHIT
+//
+void FixupCustom(int w)
+{
+	static int lastwhich=-1;
+	int y=CST_Y+26+w*13;
+
+
+	VWB_Hlin(7,32,y-1,DEACTIVE);
+	VWB_Hlin(7,32,y+12,BORD2COLOR);
+#ifndef SPEAR
+	VWB_Hlin(7,32,y-2,BORDCOLOR);
+	VWB_Hlin(7,32,y+13,BORDCOLOR);
+#else
+	VWB_Hlin(7,32,y-2,BORD2COLOR);
+	VWB_Hlin(7,32,y+13,BORD2COLOR);
+#endif
+
+	switch(w)
+	{
+		case 0: DrawCustMouse(1); break;
+		case 3: DrawCustJoy(1); break;
+		case 6: DrawCustKeybd(1); break;
+		case 8: DrawCustKeys(1);
+	}
+
+
+	if (lastwhich>=0)
+	{
+		y=CST_Y+26+lastwhich*13;
+		VWB_Hlin(7,32,y-1,DEACTIVE);
+		VWB_Hlin(7,32,y+12,BORD2COLOR);
+#ifndef SPEAR
+		VWB_Hlin(7,32,y-2,BORDCOLOR);
+		VWB_Hlin(7,32,y+13,BORDCOLOR);
+#else
+		VWB_Hlin(7,32,y-2,BORD2COLOR);
+		VWB_Hlin(7,32,y+13,BORD2COLOR);
+#endif
+
+		if (lastwhich!=w)
+			switch(lastwhich)
+			{
+				case 0: DrawCustMouse(0); break;
+				case 3: DrawCustJoy(0); break;
+				case 6: DrawCustKeybd(0); break;
+				case 8: DrawCustKeys(0);
+			}
+	}
+
+	lastwhich=w;
+}
+
+
+////////////////////////
+//
+// DRAW CUSTOMIZE SCREEN
+//
+void DrawCustomScreen(void)
+{
+	int i;
+
+
+#ifdef JAPAN
+	CA_CacheScreen(S_CUSTOMPIC);
+	fontnumber=1;
+
+	PrintX=CST_START;
+	PrintY = CST_Y+26;
+	DrawCustMouse(0);
+
+	PrintX=CST_START;
+	US_Print("\n\n\n");
+	DrawCustJoy(0);
+
+	PrintX=CST_START;
+	US_Print("\n\n\n");
+	DrawCustKeybd(0);
+
+	PrintX=CST_START;
+	US_Print("\n\n\n");
+	DrawCustKeys(0);
+#else
+	ClearMScreen();
+	WindowX=0;
+	WindowW=320;
+	VWB_DrawPic(112,184,C_MOUSELBACKPIC);
+	DrawStripes(10);
+	VWB_DrawPic(80,0,C_CUSTOMIZEPIC);
+
+	//
+	// MOUSE
+	//
+	SETFONTCOLOR(READCOLOR,BKGDCOLOR);
+	WindowX=0;
+	WindowW=320;
+
+#ifndef SPEAR
+	PrintY=CST_Y;
+	US_CPrint("Mouse\n");
+#else
+	PrintY = CST_Y+13;
+	VWB_DrawPic (128,48,C_MOUSEPIC);
+#endif
+
+	SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR);
+	#ifdef SPANISH
+	PrintX=CST_START-16;
+	US_Print(STR_CRUN);
+	PrintX=CST_START-16+CST_SPC*1;
+	US_Print(STR_COPEN);
+	PrintX=CST_START-16+CST_SPC*2;
+	US_Print(STR_CFIRE);
+	PrintX=CST_START-16+CST_SPC*3;
+	US_Print(STR_CSTRAFE"\n");
+	#else
+	PrintX=CST_START;
+	US_Print(STR_CRUN);
+	PrintX=CST_START+CST_SPC*1;
+	US_Print(STR_COPEN);
+	PrintX=CST_START+CST_SPC*2;
+	US_Print(STR_CFIRE);
+	PrintX=CST_START+CST_SPC*3;
+	US_Print(STR_CSTRAFE"\n");
+	#endif
+
+	DrawWindow(5,PrintY-1,310,13,BKGDCOLOR);
+	DrawCustMouse(0);
+	US_Print("\n");
+
+
+	//
+	// JOYSTICK/PAD
+	//
+#ifndef SPEAR
+	SETFONTCOLOR(READCOLOR,BKGDCOLOR);
+	US_CPrint("Joystick/Gravis GamePad\n");
+#else
+	PrintY += 13;
+	VWB_DrawPic (40,88,C_JOYSTICKPIC);
+#endif
+
+#ifdef SPEAR
+	VWB_DrawPic (112,120,C_KEYBOARDPIC);
+#endif
+
+	SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR);
+	#ifdef SPANISH
+	PrintX=CST_START-16;
+	US_Print(STR_CRUN);
+	PrintX=CST_START-16+CST_SPC*1;
+	US_Print(STR_COPEN);
+	PrintX=CST_START-16+CST_SPC*2;
+	US_Print(STR_CFIRE);
+	PrintX=CST_START-16+CST_SPC*3;
+	US_Print(STR_CSTRAFE"\n");
+	#else
+	PrintX=CST_START;
+	US_Print(STR_CRUN);
+	PrintX=CST_START+CST_SPC*1;
+	US_Print(STR_COPEN);
+	PrintX=CST_START+CST_SPC*2;
+	US_Print(STR_CFIRE);
+	PrintX=CST_START+CST_SPC*3;
+	US_Print(STR_CSTRAFE"\n");
+	#endif
+	DrawWindow(5,PrintY-1,310,13,BKGDCOLOR);
+	DrawCustJoy(0);
+	US_Print("\n");
+
+
+	//
+	// KEYBOARD
+	//
+#ifndef SPEAR
+	SETFONTCOLOR(READCOLOR,BKGDCOLOR);
+	US_CPrint("Keyboard\n");
+#else
+	PrintY += 13;
+#endif
+	SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR);
+	#ifdef SPANISH
+	PrintX=CST_START-16;
+	US_Print(STR_CRUN);
+	PrintX=CST_START-16+CST_SPC*1;
+	US_Print(STR_COPEN);
+	PrintX=CST_START-16+CST_SPC*2;
+	US_Print(STR_CFIRE);
+	PrintX=CST_START-16+CST_SPC*3;
+	US_Print(STR_CSTRAFE"\n");
+	#else
+	PrintX=CST_START;
+	US_Print(STR_CRUN);
+	PrintX=CST_START+CST_SPC*1;
+	US_Print(STR_COPEN);
+	PrintX=CST_START+CST_SPC*2;
+	US_Print(STR_CFIRE);
+	PrintX=CST_START+CST_SPC*3;
+	US_Print(STR_CSTRAFE"\n");
+	#endif
+	DrawWindow(5,PrintY-1,310,13,BKGDCOLOR);
+	DrawCustKeybd(0);
+	US_Print("\n");
+
+
+	//
+	// KEYBOARD MOVE KEYS
+	//
+	SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR);
+	#ifdef SPANISH
+	PrintX=4;
+	US_Print(STR_LEFT);
+	US_Print("/");
+	US_Print(STR_RIGHT);
+	US_Print("/");
+	US_Print(STR_FRWD);
+	US_Print("/");
+	US_Print(STR_BKWD"\n");
+	#else
+	PrintX=CST_START;
+	US_Print(STR_LEFT);
+	PrintX=CST_START+CST_SPC*1;
+	US_Print(STR_RIGHT);
+	PrintX=CST_START+CST_SPC*2;
+	US_Print(STR_FRWD);
+	PrintX=CST_START+CST_SPC*3;
+	US_Print(STR_BKWD"\n");
+	#endif
+	DrawWindow(5,PrintY-1,310,13,BKGDCOLOR);
+	DrawCustKeys(0);
+#endif
+	//
+	// PICK STARTING POINT IN MENU
+	//
+	if (CusItems.curpos<0)
+		for (i=0;i<CusItems.amount;i++)
+			if (CusMenu[i].active)
+			{
+				CusItems.curpos=i;
+				break;
+			}
+
+
+	VW_UpdateScreen();
+	MenuFadeIn();
+}
+
+
+void PrintCustMouse(int i)
+{
+	int j;
+
+	for (j=0;j<4;j++)
+		if (order[i]==buttonmouse[j])
+		{
+			PrintX=CST_START+CST_SPC*i;
+			US_Print(mbarray[j]);
+			break;
+		}
+}
+
+void DrawCustMouse(int hilight)
+{
+	int i,color;
+
+
+	color=TEXTCOLOR;
+	if (hilight)
+		color=HIGHLIGHT;
+	SETFONTCOLOR(color,BKGDCOLOR);
+
+	if (!mouseenabled)
+	{
+		SETFONTCOLOR(DEACTIVE,BKGDCOLOR);
+		CusMenu[0].active=0;
+	}
+	else
+		CusMenu[0].active=1;
+
+	PrintY=CST_Y+13*2;
+	for (i=0;i<4;i++)
+		PrintCustMouse(i);
+}
+
+void PrintCustJoy(int i)
+{
+	int j;
+
+	for (j=0;j<4;j++)
+		if (order[i]==buttonjoy[j])
+		{
+			PrintX=CST_START+CST_SPC*i;
+			US_Print(mbarray[j]);
+			break;
+		}
+}
+
+void DrawCustJoy(int hilight)
+{
+	int i,color;
+
+
+	color=TEXTCOLOR;
+	if (hilight)
+		color=HIGHLIGHT;
+	SETFONTCOLOR(color,BKGDCOLOR);
+
+	if (!joystickenabled)
+	{
+		SETFONTCOLOR(DEACTIVE,BKGDCOLOR);
+		CusMenu[3].active=0;
+	}
+	else
+		CusMenu[3].active=1;
+
+	PrintY=CST_Y+13*5;
+	for (i=0;i<4;i++)
+		PrintCustJoy(i);
+}
+
+
+void PrintCustKeybd(int i)
+{
+	PrintX=CST_START+CST_SPC*i;
+	US_Print(IN_GetScanName(buttonscan[order[i]]));
+}
+
+void DrawCustKeybd(int hilight)
+{
+	int i,color;
+
+
+	color=TEXTCOLOR;
+	if (hilight)
+		color=HIGHLIGHT;
+	SETFONTCOLOR(color,BKGDCOLOR);
+
+	PrintY=CST_Y+13*8;
+	for (i=0;i<4;i++)
+		PrintCustKeybd(i);
+}
+
+void PrintCustKeys(int i)
+{
+	PrintX=CST_START+CST_SPC*i;
+	US_Print(IN_GetScanName(dirscan[moveorder[i]]));
+}
+
+void DrawCustKeys(int hilight)
+{
+	int i,color;
+
+
+	color=TEXTCOLOR;
+	if (hilight)
+		color=HIGHLIGHT;
+	SETFONTCOLOR(color,BKGDCOLOR);
+
+	PrintY=CST_Y+13*10;
+	for (i=0;i<4;i++)
+		PrintCustKeys(i);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//
+// CHANGE SCREEN VIEWING SIZE
+//
+////////////////////////////////////////////////////////////////////
+void CP_ChangeView(void)
+{
+	int exit=0,oldview,newview;
+	ControlInfo ci;
+
+
+	WindowX=WindowY=0;
+	WindowW=320;
+	WindowH=200;
+	newview=oldview=viewwidth/16;
+	DrawChangeView(oldview);
+
+	do
+	{
+		CheckPause();
+		ReadAnyControl(&ci);
+		switch(ci.dir)
+		{
+		case dir_South:
+		case dir_West:
+			newview--;
+			if (newview<4)
+				newview=4;
+			ShowViewSize(newview);
+			VW_UpdateScreen();
+			SD_PlaySound(HITWALLSND);
+			TicDelay(10);
+			break;
+
+		case dir_North:
+		case dir_East:
+			newview++;
+			if (newview>19)
+				newview=19;
+			ShowViewSize(newview);
+			VW_UpdateScreen();
+			SD_PlaySound(HITWALLSND);
+			TicDelay(10);
+			break;
+		}
+
+		#ifndef SPEAR
+		if (Keyboard[sc_Tab] && Keyboard[sc_P] && MS_CheckParm("goobers"))
+		#else
+		if (Keyboard[sc_Tab] && Keyboard[sc_P] && MS_CheckParm("debugmode"))
+		#endif
+			PicturePause();
+
+		if (ci.button0 || Keyboard[sc_Enter])
+			exit=1;
+		else
+		if (ci.button1 || Keyboard[sc_Escape])
+		{
+			viewwidth=oldview*16;
+			SD_PlaySound(ESCPRESSEDSND);
+			MenuFadeOut();
+			return;
+		}
+
+	} while(!exit);
+
+
+	if (oldview!=newview)
+	{
+		SD_PlaySound (SHOOTSND);
+		Message(STR_THINK"...");
+		NewViewSize(newview);
+	}
+
+	ShootSnd();
+	MenuFadeOut();
+}
+
+
+/////////////////////////////
+//
+// DRAW THE CHANGEVIEW SCREEN
+//
+void DrawChangeView(int view)
+{
+#ifdef JAPAN
+	CA_CacheScreen(S_CHANGEPIC);
+
+	ShowViewSize(view);
+#else
+	VWB_Bar(0,160,320,40,VIEWCOLOR);
+	ShowViewSize(view);
+
+	PrintY=161;
+	WindowX=0;
+	WindowY=320;
+	SETFONTCOLOR(HIGHLIGHT,BKGDCOLOR);
+
+	US_CPrint(STR_SIZE1"\n");
+	US_CPrint(STR_SIZE2"\n");
+	US_CPrint(STR_SIZE3);
+#endif
+	VW_UpdateScreen();
+
+	MenuFadeIn();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//
+// QUIT THIS INFERNAL GAME!
+//
+////////////////////////////////////////////////////////////////////
+void CP_Quit(void)
+{
+	int i;
+
+
+	#ifdef JAPAN
+	if (GetYorN(7,11,C_QUITMSGPIC))
+	#else
+
+	#ifdef SPANISH
+	if (Confirm(ENDGAMESTR))
+	#else
+	if (Confirm(endStrings[US_RndT()&0x7+(US_RndT()&1)]))
+	#endif
+
+	#endif
+	{
+		VW_UpdateScreen();
+		SD_MusicOff();
+		SD_StopSound();
+		MenuFadeOut();
+		//
+		// SHUT-UP THE ADLIB
+		//
+		for (i=1;i<=0xf5;i++)
+			alOut(i,0);
+		Quit(NULL);
+	}
+
+	DrawMainMenu();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//
+// HANDLE INTRO SCREEN (SYSTEM CONFIG)
+//
+////////////////////////////////////////////////////////////////////
+void IntroScreen(void)
+{
+#ifdef SPEAR
+
+#define MAINCOLOR	0x4f
+#define EMSCOLOR	0x4f
+#define XMSCOLOR	0x4f
+
+#else
+
+#define MAINCOLOR	0x6c
+#define EMSCOLOR	0x6c
+#define XMSCOLOR	0x6c
+
+#endif
+#define FILLCOLOR	14
+
+	long memory,emshere,xmshere;
+	int i,num,ems[10]={100,200,300,400,500,600,700,800,900,1000},
+		xms[10]={100,200,300,400,500,600,700,800,900,1000},
+		main[10]={32,64,96,128,160,192,224,256,288,320};
+
+
+	//
+	// DRAW MAIN MEMORY
+	//
+	memory=(1023l+mminfo.nearheap+mminfo.farheap)/1024l;
+	for (i=0;i<10;i++)
+		if (memory>=main[i])
+			VWB_Bar(49,163-8*i,6,5,MAINCOLOR-i);
+
+
+	//
+	// DRAW EMS MEMORY
+	//
+	if (EMSPresent)
+	{
+		emshere=4l*EMSPagesAvail;
+		for (i=0;i<10;i++)
+			if (emshere>=ems[i])
+				VWB_Bar(89,163-8*i,6,5,EMSCOLOR-i);
+	}
+
+	//
+	// DRAW XMS MEMORY
+	//
+	if (XMSPresent)
+	{
+		xmshere=4l*XMSPagesAvail;
+		for (i=0;i<10;i++)
+			if (xmshere>=xms[i])
+				VWB_Bar(129,163-8*i,6,5,XMSCOLOR-i);
+	}
+
+	//
+	// FILL BOXES
+	//
+	if (MousePresent)
+		VWB_Bar(164,82,12,2,FILLCOLOR);
+
+	if (JoysPresent[0] || JoysPresent[1])
+		VWB_Bar(164,105,12,2,FILLCOLOR);
+
+	if (AdLibPresent && !SoundBlasterPresent)
+		VWB_Bar(164,128,12,2,FILLCOLOR);
+
+	if (SoundBlasterPresent)
+		VWB_Bar(164,151,12,2,FILLCOLOR);
+
+	if (SoundSourcePresent)
+		VWB_Bar(164,174,12,2,FILLCOLOR);
+}
+
+
+////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////
+//
+// SUPPORT ROUTINES
+//
+////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////
+//
+// Clear Menu screens to dark red
+//
+////////////////////////////////////////////////////////////////////
+void ClearMScreen(void)
+{
+#ifndef SPEAR
+	VWB_Bar(0,0,320,200,BORDCOLOR);
+#else
+	VWB_DrawPic(0,0,C_BACKDROPPIC);
+#endif
+}
+
+
+////////////////////////////////////////////////////////////////////
+//
+// Un/Cache a LUMP of graphics
+//
+////////////////////////////////////////////////////////////////////
+void CacheLump(int lumpstart,int lumpend)
+{
+ int i;
+
+ for (i=lumpstart;i<=lumpend;i++)
+   CA_CacheGrChunk(i);
+}
+
+
+void UnCacheLump(int lumpstart,int lumpend)
+{
+ int i;
+
+ for (i=lumpstart;i<=lumpend;i++)
+	if (grsegs[i])
+		UNCACHEGRCHUNK(i);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//
+// Draw a window for a menu
+//
+////////////////////////////////////////////////////////////////////
+void DrawWindow(int x,int y,int w,int h,int wcolor)
+{
+	VWB_Bar(x,y,w,h,wcolor);
+	DrawOutline(x,y,w,h,BORD2COLOR,DEACTIVE);
+}
+
+
+void DrawOutline(int x,int y,int w,int h,int color1,int color2)
+{
+	VWB_Hlin(x,x+w,y,color2);
+	VWB_Vlin(y,y+h,x,color2);
+	VWB_Hlin(x,x+w,y+h,color1);
+	VWB_Vlin(y,y+h,x+w,color1);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//
+// Setup Control Panel stuff - graphics, etc.
+//
+////////////////////////////////////////////////////////////////////
+void SetupControlPanel(void)
+{
+	struct ffblk f;
+	char name[13];
+	int which,i;
+
+
+	//
+	// CACHE GRAPHICS & SOUNDS
+	//
+	CA_CacheGrChunk(STARTFONT+1);
+#ifndef SPEAR
+	CacheLump(CONTROLS_LUMP_START,CONTROLS_LUMP_END);
+#else
+	CacheLump(BACKDROP_LUMP_START,BACKDROP_LUMP_END);
+#endif
+
+	SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR);
+	fontnumber=1;
+	WindowH=200;
+
+	if (!ingame)
+		CA_LoadAllSounds();
+	else
+		MainMenu[savegame].active=1;
+
+	//
+	// SEE WHICH SAVE GAME FILES ARE AVAILABLE & READ STRING IN
+	//
+	strcpy(name,SaveName);
+	if (!findfirst(name,&f,0))
+		do
+		{
+			which=f.ff_name[7]-'0';
+			if (which<10)
+			{
+				int handle;
+				char temp[32];
+
+				SaveGamesAvail[which]=1;
+				handle=open(f.ff_name,O_BINARY);
+				read(handle,temp,32);
+				close(handle);
+				strcpy(&SaveGameNames[which][0],temp);
+			}
+		} while(!findnext(&f));
+
+	//
+	// CENTER MOUSE
+	//
+	_CX=_DX=CENTER;
+	Mouse(4);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//
+// Clean up all the Control Panel stuff
+//
+////////////////////////////////////////////////////////////////////
+void CleanupControlPanel(void)
+{
+#ifndef SPEAR
+	UnCacheLump(CONTROLS_LUMP_START,CONTROLS_LUMP_END);
+#else
+	UnCacheLump (BACKDROP_LUMP_START,BACKDROP_LUMP_END);
+#endif
+
+	fontnumber = 0;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//
+// Handle moving gun around a menu
+//
+////////////////////////////////////////////////////////////////////
+int HandleMenu(CP_iteminfo *item_i,CP_itemtype far *items,void (*routine)(int w))
+{
+	char key;
+	static int redrawitem=1,lastitem=-1;
+	int i,x,y,basey,exit,which,shape,timer;
+	ControlInfo ci;
+
+
+	which=item_i->curpos;
+	x=item_i->x&-8;
+	basey=item_i->y-2;
+	y=basey+which*13;
+
+	VWB_DrawPic(x,y,C_CURSOR1PIC);
+	SetTextColor(items+which,1);
+	if (redrawitem)
+	{
+		PrintX=item_i->x+item_i->indent;
+		PrintY=item_i->y+which*13;
+		US_Print((items+which)->string);
+	}
+	//
+	// CALL CUSTOM ROUTINE IF IT IS NEEDED
+	//
+	if (routine)
+		routine(which);
+	VW_UpdateScreen();
+
+	shape=C_CURSOR1PIC;
+	timer=8;
+	exit=0;
+	TimeCount=0;
+	IN_ClearKeysDown();
+
+
+	do
+	{
+		//
+		// CHANGE GUN SHAPE
+		//
+		if (TimeCount>timer)
+		{
+			TimeCount=0;
+			if (shape==C_CURSOR1PIC)
+			{
+				shape=C_CURSOR2PIC;
+				timer=8;
+			}
+			else
+			{
+				shape=C_CURSOR1PIC;
+				timer=70;
+			}
+			VWB_DrawPic(x,y,shape);
+			if (routine)
+				routine(which);
+			VW_UpdateScreen();
+		}
+
+		CheckPause();
+
+		//
+		// SEE IF ANY KEYS ARE PRESSED FOR INITIAL CHAR FINDING
+		//
+		key=LastASCII;
+		if (key)
+		{
+			int ok=0;
+
+			//
+			// CHECK FOR SCREEN CAPTURE
+			//
+			#ifndef SPEAR
+			if (Keyboard[sc_Tab] && Keyboard[sc_P] && MS_CheckParm("goobers"))
+			#else
+			if (Keyboard[sc_Tab] && Keyboard[sc_P] && MS_CheckParm("debugmode"))
+			#endif
+				PicturePause();
+
+
+			if (key>='a')
+				key-='a'-'A';
+
+			for (i=which+1;i<item_i->amount;i++)
+				if ((items+i)->active && (items+i)->string[0]==key)
+				{
+					EraseGun(item_i,items,x,y,which);
+					which=i;
+					DrawGun(item_i,items,x,&y,which,basey,routine);
+					ok=1;
+					IN_ClearKeysDown();
+					break;
+				}
+
+			//
+			// DIDN'T FIND A MATCH FIRST TIME THRU. CHECK AGAIN.
+			//
+			if (!ok)
+			{
+				for (i=0;i<which;i++)
+					if ((items+i)->active && (items+i)->string[0]==key)
+					{
+						EraseGun(item_i,items,x,y,which);
+						which=i;
+						DrawGun(item_i,items,x,&y,which,basey,routine);
+						IN_ClearKeysDown();
+						break;
+					}
+			}
+		}
+
+		//
+		// GET INPUT
+		//
+		ReadAnyControl(&ci);
+		switch(ci.dir)
+		{
+			////////////////////////////////////////////////
+			//
+			// MOVE UP
+			//
+			case dir_North:
+
+			EraseGun(item_i,items,x,y,which);
+
+			//
+			// ANIMATE HALF-STEP
+			//
+			if (which && (items+which-1)->active)
+			{
+				y-=6;
+				DrawHalfStep(x,y);
+			}
+
+			//
+			// MOVE TO NEXT AVAILABLE SPOT
+			//
+			do
+			{
+				if (!which)
+					which=item_i->amount-1;
+				else
+					which--;
+			} while(!(items+which)->active);
+
+			DrawGun(item_i,items,x,&y,which,basey,routine);
+			//
+			// WAIT FOR BUTTON-UP OR DELAY NEXT MOVE
+			//
+			TicDelay(20);
+			break;
+
+			////////////////////////////////////////////////
+			//
+			// MOVE DOWN
+			//
+			case dir_South:
+
+			EraseGun(item_i,items,x,y,which);
+			//
+			// ANIMATE HALF-STEP
+			//
+			if (which!=item_i->amount-1 && (items+which+1)->active)
+			{
+				y+=6;
+				DrawHalfStep(x,y);
+			}
+
+			do
+			{
+				if (which==item_i->amount-1)
+					which=0;
+				else
+					which++;
+			} while(!(items+which)->active);
+
+			DrawGun(item_i,items,x,&y,which,basey,routine);
+
+			//
+			// WAIT FOR BUTTON-UP OR DELAY NEXT MOVE
+			//
+			TicDelay(20);
+			break;
+		}
+
+		if (ci.button0 ||
+			Keyboard[sc_Space] ||
+			Keyboard[sc_Enter])
+				exit=1;
+
+		if (ci.button1 ||
+			Keyboard[sc_Escape])
+				exit=2;
+
+	} while(!exit);
+
+
+	IN_ClearKeysDown();
+
+	//
+	// ERASE EVERYTHING
+	//
+	if (lastitem!=which)
+	{
+		VWB_Bar(x-1,y,25,16,BKGDCOLOR);
+		PrintX=item_i->x+item_i->indent;
+		PrintY=item_i->y+which*13;
+		US_Print((items+which)->string);
+		redrawitem=1;
+	}
+	else
+		redrawitem=0;
+
+	if (routine)
+		routine(which);
+	VW_UpdateScreen();
+
+	item_i->curpos=which;
+
+	lastitem=which;
+	switch(exit)
+	{
+		case 1:
+			//
+			// CALL THE ROUTINE
+			//
+			if ((items+which)->routine!=NULL)
+			{
+				ShootSnd();
+				MenuFadeOut();
+				(items+which)->routine(0);
+			}
+			return which;
+
+		case 2:
+			SD_PlaySound(ESCPRESSEDSND);
+			return -1;
+	}
+
+	return 0; // JUST TO SHUT UP THE ERROR MESSAGES!
+}
+
+
+//
+// ERASE GUN & DE-HIGHLIGHT STRING
+//
+void EraseGun(CP_iteminfo *item_i,CP_itemtype far *items,int x,int y,int which)
+{
+	VWB_Bar(x-1,y,25,16,BKGDCOLOR);
+	SetTextColor(items+which,0);
+
+	PrintX=item_i->x+item_i->indent;
+	PrintY=item_i->y+which*13;
+	US_Print((items+which)->string);
+	VW_UpdateScreen();
+}
+
+
+//
+// DRAW HALF STEP OF GUN TO NEXT POSITION
+//
+void DrawHalfStep(int x,int y)
+{
+	VWB_DrawPic(x,y,C_CURSOR1PIC);
+	VW_UpdateScreen();
+	SD_PlaySound(MOVEGUN1SND);
+	TimeCount=0;
+	while(TimeCount<8);
+}
+
+
+//
+// DRAW GUN AT NEW POSITION
+//
+void DrawGun(CP_iteminfo *item_i,CP_itemtype far *items,int x,int *y,int which,int basey,void (*routine)(int w))
+{
+	VWB_Bar(x-1,*y,25,16,BKGDCOLOR);
+	*y=basey+which*13;
+	VWB_DrawPic(x,*y,C_CURSOR1PIC);
+	SetTextColor(items+which,1);
+
+	PrintX=item_i->x+item_i->indent;
+	PrintY=item_i->y+which*13;
+	US_Print((items+which)->string);
+
+	//
+	// CALL CUSTOM ROUTINE IF IT IS NEEDED
+	//
+	if (routine)
+		routine(which);
+	VW_UpdateScreen();
+	SD_PlaySound(MOVEGUN2SND);
+}
+
+////////////////////////////////////////////////////////////////////
+//
+// DELAY FOR AN AMOUNT OF TICS OR UNTIL CONTROLS ARE INACTIVE
+//
+////////////////////////////////////////////////////////////////////
+void TicDelay(int count)
+{
+	ControlInfo ci;
+
+
+	TimeCount=0;
+	do
+	{
+		ReadAnyControl(&ci);
+	} while(TimeCount<count && ci.dir!=dir_None);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//
+// Draw a menu
+//
+////////////////////////////////////////////////////////////////////
+void DrawMenu(CP_iteminfo *item_i,CP_itemtype far *items)
+{
+	int i,which=item_i->curpos;
+
+
+	WindowX=PrintX=item_i->x+item_i->indent;
+	WindowY=PrintY=item_i->y;
+	WindowW=320;
+	WindowH=200;
+
+	for (i=0;i<item_i->amount;i++)
+	{
+		SetTextColor(items+i,which==i);
+
+		PrintY=item_i->y+i*13;
+		if ((items+i)->active)
+			US_Print((items+i)->string);
+		else
+		{
+			SETFONTCOLOR(DEACTIVE,BKGDCOLOR);
+			US_Print((items+i)->string);
+			SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR);
+		}
+
+		US_Print("\n");
+	}
+}
+
+
+////////////////////////////////////////////////////////////////////
+//
+// SET TEXT COLOR (HIGHLIGHT OR NO)
+//
+////////////////////////////////////////////////////////////////////
+void SetTextColor(CP_itemtype far *items,int hlight)
+{
+	if (hlight)
+		{SETFONTCOLOR(color_hlite[items->active],BKGDCOLOR);}
+	else
+		{SETFONTCOLOR(color_norml[items->active],BKGDCOLOR);}
+}
+
+
+////////////////////////////////////////////////////////////////////
+//
+// WAIT FOR CTRLKEY-UP OR BUTTON-UP
+//
+////////////////////////////////////////////////////////////////////
+void WaitKeyUp(void)
+{
+	ControlInfo ci;
+	while(ReadAnyControl(&ci),	ci.button0|
+								ci.button1|
+								ci.button2|
+								ci.button3|
+								Keyboard[sc_Space]|
+								Keyboard[sc_Enter]|
+								Keyboard[sc_Escape]);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//
+// READ KEYBOARD, JOYSTICK AND MOUSE FOR INPUT
+//
+////////////////////////////////////////////////////////////////////
+void ReadAnyControl(ControlInfo *ci)
+{
+	int mouseactive=0;
+
+
+	IN_ReadControl(0,ci);
+
+	if (mouseenabled)
+	{
+		int mousey,mousex;
+
+
+		// READ MOUSE MOTION COUNTERS
+		// RETURN DIRECTION
+		// HOME MOUSE
+		// CHECK MOUSE BUTTONS
+
+		Mouse(3);
+		mousex=_CX;
+		mousey=_DX;
+
+		if (mousey<CENTER-SENSITIVE)
+		{
+			ci->dir=dir_North;
+			_CX=_DX=CENTER;
+			Mouse(4);
+			mouseactive=1;
+		}
+		else
+		if (mousey>CENTER+SENSITIVE)
+		{
+			ci->dir=dir_South;
+			_CX=_DX=CENTER;
+			Mouse(4);
+			mouseactive=1;
+		}
+
+		if (mousex<CENTER-SENSITIVE)
+		{
+			ci->dir=dir_West;
+			_CX=_DX=CENTER;
+			Mouse(4);
+			mouseactive=1;
+		}
+		else
+		if (mousex>CENTER+SENSITIVE)
+		{
+			ci->dir=dir_East;
+			_CX=_DX=CENTER;
+			Mouse(4);
+			mouseactive=1;
+		}
+
+		if (IN_MouseButtons())
+		{
+			ci->button0=IN_MouseButtons()&1;
+			ci->button1=IN_MouseButtons()&2;
+			ci->button2=IN_MouseButtons()&4;
+			ci->button3=false;
+			mouseactive=1;
+		}
+	}
+
+	if (joystickenabled && !mouseactive)
+	{
+		int jx,jy,jb;
+
+
+		INL_GetJoyDelta(joystickport,&jx,&jy);
+		if (jy<-SENSITIVE)
+			ci->dir=dir_North;
+		else
+		if (jy>SENSITIVE)
+			ci->dir=dir_South;
+
+		if (jx<-SENSITIVE)
+			ci->dir=dir_West;
+		else
+		if (jx>SENSITIVE)
+			ci->dir=dir_East;
+
+		jb=IN_JoyButtons();
+		if (jb)
+		{
+			ci->button0=jb&1;
+			ci->button1=jb&2;
+			if (joypadenabled)
+			{
+				ci->button2=jb&4;
+				ci->button3=jb&8;
+			}
+			else
+				ci->button2=ci->button3=false;
+		}
+	}
+}
+
+
+////////////////////////////////////////////////////////////////////
+//
+// DRAW DIALOG AND CONFIRM YES OR NO TO QUESTION
+//
+////////////////////////////////////////////////////////////////////
+int Confirm(char far *string)
+{
+	int xit=0,i,x,y,tick=0,time,whichsnd[2]={ESCPRESSEDSND,SHOOTSND};
+
+
+	Message(string);
+	IN_ClearKeysDown();
+
+	//
+	// BLINK CURSOR
+	//
+	x=PrintX;
+	y=PrintY;
+	TimeCount=0;
+
+	do
+	{
+		if (TimeCount>=10)
+		{
+			switch(tick)
+			{
+				case 0:
+					VWB_Bar(x,y,8,13,TEXTCOLOR);
+					break;
+				case 1:
+					PrintX=x;
+					PrintY=y;
+					US_Print("_");
+			}
+			VW_UpdateScreen();
+			tick^=1;
+			TimeCount=0;
+		}
+
+		#ifndef SPEAR
+		if (Keyboard[sc_Tab] && Keyboard[sc_P] && MS_CheckParm("goobers"))
+			PicturePause();
+		#endif
+
+	#ifdef SPANISH
+	} while(!Keyboard[sc_S] && !Keyboard[sc_N] && !Keyboard[sc_Escape]);
+	#else
+	} while(!Keyboard[sc_Y] && !Keyboard[sc_N] && !Keyboard[sc_Escape]);
+	#endif
+
+	#ifdef SPANISH
+	if (Keyboard[sc_S])
+	{
+		xit=1;
+		ShootSnd();
+	}
+
+	while(Keyboard[sc_S] || Keyboard[sc_N] || Keyboard[sc_Escape]);
+
+	#else
+
+	if (Keyboard[sc_Y])
+	{
+		xit=1;
+		ShootSnd();
+	}
+
+	while(Keyboard[sc_Y] || Keyboard[sc_N] || Keyboard[sc_Escape]);
+	#endif
+
+	IN_ClearKeysDown();
+	SD_PlaySound(whichsnd[xit]);
+	return xit;
+}
+
+#ifdef JAPAN
+////////////////////////////////////////////////////////////////////
+//
+// DRAW MESSAGE & GET Y OR N
+//
+////////////////////////////////////////////////////////////////////
+int GetYorN(int x,int y,int pic)
+{
+	int xit=0,whichsnd[2]={ESCPRESSEDSND,SHOOTSND};
+
+
+	CA_CacheGrChunk(pic);
+	VWB_DrawPic(x * 8,y * 8,pic);
+	UNCACHEGRCHUNK(pic);
+	VW_UpdateScreen();
+	IN_ClearKeysDown();
+
+	do
+	{
+		#ifndef SPEAR
+		if (Keyboard[sc_Tab] && Keyboard[sc_P] && MS_CheckParm("goobers"))
+			PicturePause();
+		#endif
+
+	#ifdef SPANISH
+	} while(!Keyboard[sc_S] && !Keyboard[sc_N] && !Keyboard[sc_Escape]);
+	#else
+	} while(!Keyboard[sc_Y] && !Keyboard[sc_N] && !Keyboard[sc_Escape]);
+	#endif
+
+	#ifdef SPANISH
+	if (Keyboard[sc_S])
+	{
+		xit=1;
+		ShootSnd();
+	}
+
+	while(Keyboard[sc_S] || Keyboard[sc_N] || Keyboard[sc_Escape]);
+
+	#else
+
+	if (Keyboard[sc_Y])
+	{
+		xit=1;
+		ShootSnd();
+	}
+
+	while(Keyboard[sc_Y] || Keyboard[sc_N] || Keyboard[sc_Escape]);
+	#endif
+
+	IN_ClearKeysDown();
+	SD_PlaySound(whichsnd[xit]);
+	return xit;
+}
+#endif
+
+
+////////////////////////////////////////////////////////////////////
+//
+// PRINT A MESSAGE IN A WINDOW
+//
+////////////////////////////////////////////////////////////////////
+void Message(char far *string)
+{
+	int h=0,w=0,mw=0,i,x,y,time;
+	fontstruct _seg *font;
+
+
+	CA_CacheGrChunk (STARTFONT+1);
+	fontnumber=1;
+	font=grsegs[STARTFONT+fontnumber];
+	h=font->height;
+	for (i=0;i<_fstrlen(string);i++)
+		if (string[i]=='\n')
+		{
+			if (w>mw)
+				mw=w;
+			w=0;
+			h+=font->height;
+		}
+		else
+			w+=font->width[string[i]];
+
+	if (w+10>mw)
+		mw=w+10;
+
+	PrintY=(WindowH/2)-h/2;
+	PrintX=WindowX=160-mw/2;
+
+	DrawWindow(WindowX-5,PrintY-5,mw+10,h+10,TEXTCOLOR);
+	DrawOutline(WindowX-5,PrintY-5,mw+10,h+10,0,HIGHLIGHT);
+	SETFONTCOLOR(0,TEXTCOLOR);
+	US_Print(string);
+	VW_UpdateScreen();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//
+// THIS MAY BE FIXED A LITTLE LATER...
+//
+////////////////////////////////////////////////////////////////////
+static	int	lastmusic;
+
+void StartCPMusic(int song)
+{
+	musicnames	chunk;
+
+	if (audiosegs[STARTMUSIC + lastmusic])	// JDC
+		MM_FreePtr ((memptr *)&audiosegs[STARTMUSIC + lastmusic]);
+	lastmusic = song;
+
+	SD_MusicOff();
+	chunk =	song;
+
+	MM_BombOnError (false);
+	CA_CacheAudioChunk(STARTMUSIC + chunk);
+	MM_BombOnError (true);
+	if (mmerror)
+		mmerror = false;
+	else
+	{
+		MM_SetLock(&((memptr)audiosegs[STARTMUSIC + chunk]),true);
+		SD_StartMusic((MusicGroup far *)audiosegs[STARTMUSIC + chunk]);
+	}
+}
+
+void FreeMusic (void)
+{
+	if (audiosegs[STARTMUSIC + lastmusic])	// JDC
+		MM_FreePtr ((memptr *)&audiosegs[STARTMUSIC + lastmusic]);
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+//
+//	IN_GetScanName() - Returns a string containing the name of the
+//		specified scan code
+//
+///////////////////////////////////////////////////////////////////////////
+byte *
+IN_GetScanName(ScanCode scan)
+{
+	byte		**p;
+	ScanCode	far *s;
+
+	for (s = ExtScanCodes,p = ExtScanNames;*s;p++,s++)
+		if (*s == scan)
+			return(*p);
+
+	return(ScanNames[scan]);
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+//
+// CHECK FOR PAUSE KEY (FOR MUSIC ONLY)
+//
+///////////////////////////////////////////////////////////////////////////
+void CheckPause(void)
+{
+	if (Paused)
+	{
+		switch(SoundStatus)
+		{
+			case 0: SD_MusicOn(); break;
+			case 1: SD_MusicOff(); break;
+		}
+
+		SoundStatus^=1;
+		VW_WaitVBL(3);
+		IN_ClearKeysDown();
+		Paused=false;
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+//
+// DRAW GUN CURSOR AT CORRECT POSITION IN MENU
+//
+///////////////////////////////////////////////////////////////////////////
+void DrawMenuGun(CP_iteminfo *iteminfo)
+{
+	int x,y;
+
+
+	x=iteminfo->x;
+	y=iteminfo->y+iteminfo->curpos*13-2;
+	VWB_DrawPic(x,y,C_CURSOR1PIC);
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+//
+// DRAW SCREEN TITLE STRIPES
+//
+///////////////////////////////////////////////////////////////////////////
+void DrawStripes(int y)
+{
+#ifndef SPEAR
+	VWB_Bar(0,y,320,24,0);
+	VWB_Hlin(0,319,y+22,STRIPE);
+#else
+	VWB_Bar(0,y,320,22,0);
+	VWB_Hlin(0,319,y+23,0);
+#endif
+}
+
+void ShootSnd(void)
+{
+	SD_PlaySound(SHOOTSND);
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+//
+// CHECK FOR EPISODES
+//
+///////////////////////////////////////////////////////////////////////////
+void CheckForEpisodes(void)
+{
+	struct ffblk f;
+
+//
+// JAPANESE VERSION
+//
+#ifdef JAPAN
+#ifdef JAPDEMO
+	if (!findfirst("*.WJ1",&f,FA_ARCH))
+	{
+		strcpy(extension,"WJ1");
+#else
+	if (!findfirst("*.WJ6",&f,FA_ARCH))
+	{
+		strcpy(extension,"WJ6");
+#endif
+		strcat(configname,extension);
+		strcat(SaveName,extension);
+		strcat(PageFileName,extension);
+		strcat(audioname,extension);
+		strcat(demoname,extension);
+		EpisodeSelect[1] =
+		EpisodeSelect[2] =
+		EpisodeSelect[3] =
+		EpisodeSelect[4] =
+		EpisodeSelect[5] = 1;
+	}
+	else
+		Quit("NO JAPANESE WOLFENSTEIN 3-D DATA FILES to be found!");
+#else
+
+//
+// ENGLISH
+//
+#ifndef UPLOAD
+#ifndef SPEAR
+	if (!findfirst("*.WL6",&f,FA_ARCH))
+	{
+		strcpy(extension,"WL6");
+		NewEmenu[2].active =
+		NewEmenu[4].active =
+		NewEmenu[6].active =
+		NewEmenu[8].active =
+		NewEmenu[10].active =
+		EpisodeSelect[1] =
+		EpisodeSelect[2] =
+		EpisodeSelect[3] =
+		EpisodeSelect[4] =
+		EpisodeSelect[5] = 1;
+	}
+	else
+	if (!findfirst("*.WL3",&f,FA_ARCH))
+	{
+		strcpy(extension,"WL3");
+		NewEmenu[2].active =
+		NewEmenu[4].active =
+		EpisodeSelect[1] =
+		EpisodeSelect[2] = 1;
+	}
+	else
+#endif
+#endif
+
+
+
+#ifdef SPEAR
+#ifndef SPEARDEMO
+	if (!findfirst("*.SOD",&f,FA_ARCH))
+	{
+		strcpy(extension,"SOD");
+	}
+	else
+		Quit("NO SPEAR OF DESTINY DATA FILES TO BE FOUND!");
+#else
+	if (!findfirst("*.SDM",&f,FA_ARCH))
+	{
+		strcpy(extension,"SDM");
+	}
+	else
+		Quit("NO SPEAR OF DESTINY DEMO DATA FILES TO BE FOUND!");
+#endif
+
+#else
+	if (!findfirst("*.WL1",&f,FA_ARCH))
+	{
+		strcpy(extension,"WL1");
+	}
+	else
+		Quit("NO WOLFENSTEIN 3-D DATA FILES to be found!");
+#endif
+
+	strcat(configname,extension);
+	strcat(SaveName,extension);
+	strcat(PageFileName,extension);
+	strcat(audioname,extension);
+	strcat(demoname,extension);
+#ifndef SPEAR
+#ifndef GOODTIMES
+	strcat(helpfilename,extension);
+#endif
+	strcat(endfilename,extension);
+#endif
+#endif
+}
diff --git a/src/lib/hb/wl_menu.h b/src/lib/hb/wl_menu.h
new file mode 100755
index 00000000..dc03400c
--- /dev/null
+++ b/src/lib/hb/wl_menu.h
@@ -0,0 +1,234 @@
+//
+// WL_MENU.H
+//
+#ifdef SPEAR
+
+#define BORDCOLOR	0x99
+#define BORD2COLOR	0x93
+#define DEACTIVE	0x9b
+#define BKGDCOLOR	0x9d
+//#define STRIPE		0x9c
+
+#define MenuFadeOut()	VL_FadeOut(0,255,0,0,51,10)
+
+#else
+
+#define BORDCOLOR	0x29
+#define BORD2COLOR	0x23
+#define DEACTIVE	0x2b
+#define BKGDCOLOR	0x2d
+#define STRIPE		0x2c
+
+#define MenuFadeOut()	VL_FadeOut(0,255,43,0,0,10)
+
+#endif
+
+#define READCOLOR	0x4a
+#define READHCOLOR	0x47
+#define VIEWCOLOR	0x7f
+#define TEXTCOLOR	0x17
+#define HIGHLIGHT	0x13
+#define MenuFadeIn()	VL_FadeIn(0,255,&gamepal,10)
+
+
+#define MENUSONG	WONDERIN_MUS
+
+#ifndef SPEAR
+#define INTROSONG	NAZI_NOR_MUS
+#else
+#define INTROSONG	XTOWER2_MUS
+#endif
+
+#define SENSITIVE	60
+#define CENTER		SENSITIVE*2
+
+#define MENU_X	76
+#define MENU_Y	55
+#define MENU_W	178
+#ifndef SPEAR
+#define MENU_H	13*10+6
+#else
+#define MENU_H	13*9+6
+#endif
+
+#define SM_X	48
+#define SM_W	250
+
+#define SM_Y1	20
+#define SM_H1	4*13-7
+#define SM_Y2	SM_Y1+5*13
+#define SM_H2	4*13-7
+#define SM_Y3	SM_Y2+5*13
+#define SM_H3	3*13-7
+
+#define CTL_X	24
+#define CTL_Y	70
+#define CTL_W	284
+#define CTL_H	13*7-7
+
+#define LSM_X	85
+#define LSM_Y	55
+#define LSM_W	175
+#define LSM_H	10*13+10
+
+#define NM_X	50
+#define NM_Y	100
+#define NM_W	225
+#define NM_H	13*4+15
+
+#define NE_X	10
+#define NE_Y	23
+#define NE_W	320-NE_X*2
+#define NE_H	200-NE_Y*2
+
+#define CST_X		20
+#define CST_Y		48
+#define CST_START	60
+#define CST_SPC	60
+
+
+//
+// TYPEDEFS
+//
+typedef struct {
+		int x,y,amount,curpos,indent;
+		} CP_iteminfo;
+
+typedef struct {
+		int active;
+		char string[36];
+		void (* routine)(int temp1);
+		} CP_itemtype;
+
+typedef struct {
+		int allowed[4];
+		} CustomCtrls;
+
+extern CP_itemtype far MainMenu[],far NewEMenu[];
+extern CP_iteminfo MainItems;
+
+//
+// FUNCTION PROTOTYPES
+//
+void SetupControlPanel(void);
+void CleanupControlPanel(void);
+
+void DrawMenu(CP_iteminfo *item_i,CP_itemtype far *items);
+int  HandleMenu(CP_iteminfo *item_i,
+		CP_itemtype far *items,
+		void (*routine)(int w));
+void ClearMScreen(void);
+void DrawWindow(int x,int y,int w,int h,int wcolor);
+void DrawOutline(int x,int y,int w,int h,int color1,int color2);
+void WaitKeyUp(void);
+void ReadAnyControl(ControlInfo *ci);
+void TicDelay(int count);
+void CacheLump(int lumpstart,int lumpend);
+void UnCacheLump(int lumpstart,int lumpend);
+void StartCPMusic(int song);
+int  Confirm(char far *string);
+void Message(char far *string);
+void CheckPause(void);
+void ShootSnd(void);
+void CheckSecretMissions(void);
+void BossKey(void);
+
+void DrawGun(CP_iteminfo *item_i,CP_itemtype far *items,int x,int *y,int which,int basey,void (*routine)(int w));
+void DrawHalfStep(int x,int y);
+void EraseGun(CP_iteminfo *item_i,CP_itemtype far *items,int x,int y,int which);
+void SetTextColor(CP_itemtype far *items,int hlight);
+void DrawMenuGun(CP_iteminfo *iteminfo);
+void DrawStripes(int y);
+
+void DefineMouseBtns(void);
+void DefineJoyBtns(void);
+void DefineKeyBtns(void);
+void DefineKeyMove(void);
+void EnterCtrlData(int index,CustomCtrls *cust,void (*DrawRtn)(int),void (*PrintRtn)(int),int type);
+
+void DrawMainMenu(void);
+void DrawSoundMenu(void);
+void DrawLoadSaveScreen(int loadsave);
+void DrawNewEpisode(void);
+void DrawNewGame(void);
+void DrawChangeView(int view);
+void DrawMouseSens(void);
+void DrawCtlScreen(void);
+void DrawCustomScreen(void);
+void DrawLSAction(int which);
+void DrawCustMouse(int hilight);
+void DrawCustJoy(int hilight);
+void DrawCustKeybd(int hilight);
+void DrawCustKeys(int hilight);
+void PrintCustMouse(int i);
+void PrintCustJoy(int i);
+void PrintCustKeybd(int i);
+void PrintCustKeys(int i);
+
+void PrintLSEntry(int w,int color);
+void TrackWhichGame(int w);
+void DrawNewGameDiff(int w);
+void FixupCustom(int w);
+
+void CP_NewGame(void);
+void CP_Sound(void);
+int  CP_LoadGame(int quick);
+int  CP_SaveGame(int quick);
+void CP_Control(void);
+void CP_ChangeView(void);
+void CP_ExitOptions(void);
+void CP_Quit(void);
+void CP_ViewScores(void);
+int  CP_EndGame(void);
+int  CP_CheckQuick(unsigned scancode);
+void CustomControls(void);
+void MouseSensitivity(void);
+
+void CheckForEpisodes(void);
+
+//
+// VARIABLES
+//
+extern int SaveGamesAvail[10],StartGame,SoundStatus;
+extern char SaveGameNames[10][32],SaveName[13];
+
+enum {MOUSE,JOYSTICK,KEYBOARDBTNS,KEYBOARDMOVE};	// FOR INPUT TYPES
+
+#ifndef USO_FIX1
+extern
+#endif
+enum
+{
+	newgame,
+	soundmenu,
+	control,
+	loadgame,
+	savegame,
+	changeview,
+
+#ifndef GOODTIMES
+#ifndef SPEAR
+	readthis,
+#endif
+#endif
+
+	viewscores,
+	backtodemo,
+	quit
+} menuitems;
+
+//
+// WL_INTER
+//
+typedef struct {
+		int kill,secret,treasure;
+		long time;
+		} LRstruct;
+
+extern LRstruct LevelRatios[];
+
+void Write (int x,int y,char *string);
+void NonShareware(void);
+int GetYorN(int x,int y,int pic);
+
+
diff --git a/src/lib/hb/wl_play.c b/src/lib/hb/wl_play.c
new file mode 100755
index 00000000..447c8937
--- /dev/null
+++ b/src/lib/hb/wl_play.c
@@ -0,0 +1,1473 @@
+// WL_PLAY.C
+
+#include "WL_DEF.H"
+#pragma hdrstop
+
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+#define sc_Question	0x35
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+boolean		madenoise;					// true when shooting or screaming
+
+exit_t		playstate;
+
+int			DebugOk;
+
+objtype 	objlist[MAXACTORS],*new,*obj,*player,*lastobj,
+			*objfreelist,*killerobj;
+
+unsigned	farmapylookup[MAPSIZE];
+byte		*nearmapylookup[MAPSIZE];
+
+boolean		singlestep,godmode,noclip;
+int			extravbls;
+
+byte		tilemap[MAPSIZE][MAPSIZE];	// wall values only
+byte		spotvis[MAPSIZE][MAPSIZE];
+objtype		*actorat[MAPSIZE][MAPSIZE];
+
+//
+// replacing refresh manager
+//
+unsigned	mapwidth,mapheight,tics;
+boolean		compatability;
+byte		*updateptr;
+unsigned	mapwidthtable[64];
+unsigned	uwidthtable[UPDATEHIGH];
+unsigned	blockstarts[UPDATEWIDE*UPDATEHIGH];
+//uso: replace: byte            update[UPDATESIZE];
+//uso: is needed? byte    update[UPDATEHIGH][UPDATEWIDE];
+
+//
+// control info
+//
+boolean		mouseenabled,joystickenabled,joypadenabled,joystickprogressive;
+int			joystickport;
+int			dirscan[4] = {sc_UpArrow,sc_RightArrow,sc_DownArrow,sc_LeftArrow};
+int			buttonscan[NUMBUTTONS] =
+			{sc_Control,sc_Alt,sc_RShift,sc_Space,sc_1,sc_2,sc_3,sc_4};
+int			buttonmouse[4]={bt_attack,bt_strafe,bt_use,bt_nobutton};
+int			buttonjoy[4]={bt_attack,bt_strafe,bt_use,bt_run};
+
+int			viewsize;
+
+boolean		buttonheld[NUMBUTTONS];
+
+boolean		demorecord,demoplayback;
+char		far *demoptr, far *lastdemoptr;
+memptr		demobuffer;
+
+//
+// curent user input
+//
+int			controlx,controly;		// range from -100 to 100 per tic
+boolean		buttonstate[NUMBUTTONS];
+
+
+
+//===========================================================================
+
+
+void	CenterWindow(word w,word h);
+void 	InitObjList (void);
+void 	RemoveObj (objtype *gone);
+void 	PollControls (void);
+void 	StopMusic(void);
+void 	StartMusic(void);
+void	PlayLoop (void);
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+
+objtype dummyobj;
+
+//
+// LIST OF SONGS FOR EACH VERSION
+//
+int songs[]=
+{
+#ifndef SPEAR
+ //
+ // Episode One
+ //
+ GETTHEM_MUS,
+ SEARCHN_MUS,
+ POW_MUS,
+ SUSPENSE_MUS,
+ GETTHEM_MUS,
+ SEARCHN_MUS,
+ POW_MUS,
+ SUSPENSE_MUS,
+
+ WARMARCH_MUS,	// Boss level
+ CORNER_MUS,	// Secret level
+
+ //
+ // Episode Two
+ //
+ NAZI_OMI_MUS,
+ PREGNANT_MUS,
+ GOINGAFT_MUS,
+ HEADACHE_MUS,
+ NAZI_OMI_MUS,
+ PREGNANT_MUS,
+ HEADACHE_MUS,
+ GOINGAFT_MUS,
+
+ WARMARCH_MUS,	// Boss level
+ DUNGEON_MUS,	// Secret level
+
+ //
+ // Episode Three
+ //
+ INTROCW3_MUS,
+ NAZI_RAP_MUS,
+ TWELFTH_MUS,
+ ZEROHOUR_MUS,
+ INTROCW3_MUS,
+ NAZI_RAP_MUS,
+ TWELFTH_MUS,
+ ZEROHOUR_MUS,
+
+ ULTIMATE_MUS,	// Boss level
+ PACMAN_MUS,	// Secret level
+
+ //
+ // Episode Four
+ //
+ GETTHEM_MUS,
+ SEARCHN_MUS,
+ POW_MUS,
+ SUSPENSE_MUS,
+ GETTHEM_MUS,
+ SEARCHN_MUS,
+ POW_MUS,
+ SUSPENSE_MUS,
+
+ WARMARCH_MUS,	// Boss level
+ CORNER_MUS,	// Secret level
+
+ //
+ // Episode Five
+ //
+ NAZI_OMI_MUS,
+ PREGNANT_MUS,
+ GOINGAFT_MUS,
+ HEADACHE_MUS,
+ NAZI_OMI_MUS,
+ PREGNANT_MUS,
+ HEADACHE_MUS,
+ GOINGAFT_MUS,
+
+ WARMARCH_MUS,	// Boss level
+ DUNGEON_MUS,	// Secret level
+
+ //
+ // Episode Six
+ //
+ INTROCW3_MUS,
+ NAZI_RAP_MUS,
+ TWELFTH_MUS,
+ ZEROHOUR_MUS,
+ INTROCW3_MUS,
+ NAZI_RAP_MUS,
+ TWELFTH_MUS,
+ ZEROHOUR_MUS,
+
+ ULTIMATE_MUS,	// Boss level
+ FUNKYOU_MUS		// Secret level
+#else
+
+ //////////////////////////////////////////////////////////////
+ //
+ // SPEAR OF DESTINY TRACKS
+ //
+ //////////////////////////////////////////////////////////////
+ XTIPTOE_MUS,
+ XFUNKIE_MUS,
+ XDEATH_MUS,
+ XGETYOU_MUS,		// DON'T KNOW
+ ULTIMATE_MUS,	// Trans Gr”sse
+
+ DUNGEON_MUS,
+ GOINGAFT_MUS,
+ POW_MUS,
+ TWELFTH_MUS,
+ ULTIMATE_MUS,	// Barnacle Wilhelm BOSS
+
+ NAZI_OMI_MUS,
+ GETTHEM_MUS,
+ SUSPENSE_MUS,
+ SEARCHN_MUS,
+ ZEROHOUR_MUS,
+ ULTIMATE_MUS,	// Super Mutant BOSS
+
+ XPUTIT_MUS,
+ ULTIMATE_MUS,	// Death Knight BOSS
+
+ XJAZNAZI_MUS,	// Secret level
+ XFUNKIE_MUS,	// Secret level (DON'T KNOW)
+
+ XEVIL_MUS		// Angel of Death BOSS
+
+#endif
+};
+
+
+/*
+=============================================================================
+
+						  USER CONTROL
+
+=============================================================================
+*/
+
+
+#define BASEMOVE		35
+#define RUNMOVE			70
+#define BASETURN		35
+#define RUNTURN			70
+
+#define JOYSCALE		2
+
+/*
+===================
+=
+= PollKeyboardButtons
+=
+===================
+*/
+
+void PollKeyboardButtons (void)
+{
+	int		i;
+
+	for (i=0;i<NUMBUTTONS;i++)
+		if (Keyboard[buttonscan[i]])
+			buttonstate[i] = true;
+}
+
+
+/*
+===================
+=
+= PollMouseButtons
+=
+===================
+*/
+
+void PollMouseButtons (void)
+{
+	int	buttons;
+
+	buttons = IN_MouseButtons ();
+
+	if (buttons&1)
+		buttonstate[buttonmouse[0]] = true;
+	if (buttons&2)
+		buttonstate[buttonmouse[1]] = true;
+	if (buttons&4)
+		buttonstate[buttonmouse[2]] = true;
+}
+
+
+
+/*
+===================
+=
+= PollJoystickButtons
+=
+===================
+*/
+
+void PollJoystickButtons (void)
+{
+	int	buttons;
+
+	buttons = IN_JoyButtons ();
+
+	if (joystickport && !joypadenabled)
+	{
+		if (buttons&4)
+			buttonstate[buttonjoy[0]] = true;
+		if (buttons&8)
+			buttonstate[buttonjoy[1]] = true;
+	}
+	else
+	{
+		if (buttons&1)
+			buttonstate[buttonjoy[0]] = true;
+		if (buttons&2)
+			buttonstate[buttonjoy[1]] = true;
+		if (joypadenabled)
+		{
+			if (buttons&4)
+				buttonstate[buttonjoy[2]] = true;
+			if (buttons&8)
+				buttonstate[buttonjoy[3]] = true;
+		}
+	}
+}
+
+
+/*
+===================
+=
+= PollKeyboardMove
+=
+===================
+*/
+
+void PollKeyboardMove (void)
+{
+	if (buttonstate[bt_run])
+	{
+		if (Keyboard[dirscan[di_north]])
+			controly -= RUNMOVE*tics;
+		if (Keyboard[dirscan[di_south]])
+			controly += RUNMOVE*tics;
+		if (Keyboard[dirscan[di_west]])
+			controlx -= RUNMOVE*tics;
+		if (Keyboard[dirscan[di_east]])
+			controlx += RUNMOVE*tics;
+	}
+	else
+	{
+		if (Keyboard[dirscan[di_north]])
+			controly -= BASEMOVE*tics;
+		if (Keyboard[dirscan[di_south]])
+			controly += BASEMOVE*tics;
+		if (Keyboard[dirscan[di_west]])
+			controlx -= BASEMOVE*tics;
+		if (Keyboard[dirscan[di_east]])
+			controlx += BASEMOVE*tics;
+	}
+}
+
+
+/*
+===================
+=
+= PollMouseMove
+=
+===================
+*/
+
+void PollMouseMove (void)
+{
+	int	mousexmove,mouseymove;
+
+	Mouse(MDelta);
+	mousexmove = _CX;
+	mouseymove = _DX;
+
+	controlx += mousexmove*10/(13-mouseadjustment);
+	controly += mouseymove*20/(13-mouseadjustment);
+}
+
+
+
+/*
+===================
+=
+= PollJoystickMove
+=
+===================
+*/
+
+void PollJoystickMove (void)
+{
+	int	joyx,joyy;
+
+	INL_GetJoyDelta(joystickport,&joyx,&joyy);
+
+	if (joystickprogressive)
+	{
+		if (joyx > 64)
+			controlx += (joyx-64)*JOYSCALE*tics;
+		else if (joyx < -64)
+			controlx -= (-joyx-64)*JOYSCALE*tics;
+		if (joyy > 64)
+			controlx += (joyy-64)*JOYSCALE*tics;
+		else if (joyy < -64)
+			controly -= (-joyy-64)*JOYSCALE*tics;
+	}
+	else if (buttonstate[bt_run])
+	{
+		if (joyx > 64)
+			controlx += RUNMOVE*tics;
+		else if (joyx < -64)
+			controlx -= RUNMOVE*tics;
+		if (joyy > 64)
+			controly += RUNMOVE*tics;
+		else if (joyy < -64)
+			controly -= RUNMOVE*tics;
+	}
+	else
+	{
+		if (joyx > 64)
+			controlx += BASEMOVE*tics;
+		else if (joyx < -64)
+			controlx -= BASEMOVE*tics;
+		if (joyy > 64)
+			controly += BASEMOVE*tics;
+		else if (joyy < -64)
+			controly -= BASEMOVE*tics;
+	}
+}
+
+
+/*
+===================
+=
+= PollControls
+=
+= Gets user or demo input, call once each frame
+=
+= controlx		set between -100 and 100 per tic
+= controly
+= buttonheld[]	the state of the buttons LAST frame
+= buttonstate[]	the state of the buttons THIS frame
+=
+===================
+*/
+
+void PollControls (void)
+{
+	int		max,min,i;
+	byte	buttonbits;
+
+//
+// get timing info for last frame
+//
+	if (demoplayback)
+	{
+		while (TimeCount<lasttimecount+DEMOTICS)
+		;
+		TimeCount = lasttimecount + DEMOTICS;
+		lasttimecount += DEMOTICS;
+		tics = DEMOTICS;
+	}
+	else if (demorecord)			// demo recording and playback needs
+	{								// to be constant
+//
+// take DEMOTICS or more tics, and modify Timecount to reflect time taken
+//
+		while (TimeCount<lasttimecount+DEMOTICS)
+		;
+		TimeCount = lasttimecount + DEMOTICS;
+		lasttimecount += DEMOTICS;
+		tics = DEMOTICS;
+	}
+	else
+		CalcTics ();
+
+	controlx = 0;
+	controly = 0;
+	memcpy (buttonheld,buttonstate,sizeof(buttonstate));
+	memset (buttonstate,0,sizeof(buttonstate));
+
+	if (demoplayback)
+	{
+	//
+	// read commands from demo buffer
+	//
+		buttonbits = *demoptr++;
+		for (i=0;i<NUMBUTTONS;i++)
+		{
+			buttonstate[i] = buttonbits&1;
+			buttonbits >>= 1;
+		}
+
+		controlx = *demoptr++;
+		controly = *demoptr++;
+
+		if (demoptr == lastdemoptr)
+			playstate = ex_completed;		// demo is done
+
+		controlx *= (int)tics;
+		controly *= (int)tics;
+
+		return;
+	}
+
+
+//
+// get button states
+//
+	PollKeyboardButtons ();
+
+	if (mouseenabled)
+		PollMouseButtons ();
+
+	if (joystickenabled)
+		PollJoystickButtons ();
+
+//
+// get movements
+//
+	PollKeyboardMove ();
+
+	if (mouseenabled)
+		PollMouseMove ();
+
+	if (joystickenabled)
+		PollJoystickMove ();
+
+//
+// bound movement to a maximum
+//
+	max = 100*tics;
+	min = -max;
+	if (controlx > max)
+		controlx = max;
+	else if (controlx < min)
+		controlx = min;
+
+	if (controly > max)
+		controly = max;
+	else if (controly < min)
+		controly = min;
+
+	if (demorecord)
+	{
+	//
+	// save info out to demo buffer
+	//
+		controlx /= (int)tics;
+		controly /= (int)tics;
+
+		buttonbits = 0;
+
+		for (i=NUMBUTTONS-1;i>=0;i--)
+		{
+			buttonbits <<= 1;
+			if (buttonstate[i])
+				buttonbits |= 1;
+		}
+
+		*demoptr++ = buttonbits;
+		*demoptr++ = controlx;
+		*demoptr++ = controly;
+
+		if (demoptr >= lastdemoptr)
+			Quit ("Demo buffer overflowed!");
+
+		controlx *= (int)tics;
+		controly *= (int)tics;
+	}
+}
+
+
+
+//==========================================================================
+
+
+
+///////////////////////////////////////////////////////////////////////////
+//
+//	CenterWindow() - Generates a window of a given width & height in the
+//		middle of the screen
+//
+///////////////////////////////////////////////////////////////////////////
+
+#define MAXX	320
+#define MAXY	160
+
+void	CenterWindow(word w,word h)
+{
+	FixOfs ();
+	US_DrawWindow(((MAXX / 8) - w) / 2,((MAXY / 8) - h) / 2,w,h);
+}
+
+//===========================================================================
+
+
+/*
+=====================
+=
+= CheckKeys
+=
+=====================
+*/
+
+void CheckKeys (void)
+{
+	int		i;
+	byte	scan;
+	unsigned	temp;
+
+
+	if (screenfaded || demoplayback)	// don't do anything with a faded screen
+		return;
+
+	scan = LastScan;
+
+
+	#ifdef SPEAR
+	//
+	// SECRET CHEAT CODE: TAB-G-F10
+	//
+	if (Keyboard[sc_Tab] &&
+		Keyboard[sc_G] &&
+		Keyboard[sc_F10])
+	{
+		WindowH = 160;
+		if (godmode)
+		{
+			Message ("God mode OFF");
+			SD_PlaySound (NOBONUSSND);
+		}
+		else
+		{
+			Message ("God mode ON");
+			SD_PlaySound (ENDBONUS2SND);
+		}
+
+		IN_Ack();
+		godmode ^= 1;
+		DrawAllPlayBorderSides ();
+		IN_ClearKeysDown();
+		return;
+	}
+	#endif
+
+
+	//
+	// SECRET CHEAT CODE: 'MLI'
+	//
+	if (Keyboard[sc_M] &&
+		Keyboard[sc_L] &&
+		Keyboard[sc_I])
+	{
+		gamestate.health = 100;
+		gamestate.ammo = 99;
+		gamestate.keys = 3;
+		gamestate.score = 0;
+		gamestate.TimeCount += 42000L;
+		GiveWeapon (wp_chaingun);
+
+		DrawWeapon();
+		DrawHealth();
+		DrawKeys();
+		DrawAmmo();
+		DrawScore();
+
+		ClearMemory ();
+		CA_CacheGrChunk (STARTFONT+1);
+		ClearSplitVWB ();
+		VW_ScreenToScreen (displayofs,bufferofs,80,160);
+
+		Message(STR_CHEATER1"\n"
+				STR_CHEATER2"\n\n"
+				STR_CHEATER3"\n"
+				STR_CHEATER4"\n"
+				STR_CHEATER5);
+
+		UNCACHEGRCHUNK(STARTFONT+1);
+		PM_CheckMainMem ();
+		IN_ClearKeysDown();
+		IN_Ack();
+
+		DrawAllPlayBorder ();
+	}
+
+	//
+	// OPEN UP DEBUG KEYS
+	//
+#ifndef SPEAR
+	if (Keyboard[sc_BackSpace] &&
+		Keyboard[sc_LShift] &&
+		Keyboard[sc_Alt] &&
+		MS_CheckParm("goobers"))
+#else
+	if (Keyboard[sc_BackSpace] &&
+		Keyboard[sc_LShift] &&
+		Keyboard[sc_Alt] &&
+		MS_CheckParm("debugmode"))
+#endif
+	{
+	 ClearMemory ();
+	 CA_CacheGrChunk (STARTFONT+1);
+	 ClearSplitVWB ();
+	 VW_ScreenToScreen (displayofs,bufferofs,80,160);
+
+	 Message("Debugging keys are\nnow available!");
+	 UNCACHEGRCHUNK(STARTFONT+1);
+	 PM_CheckMainMem ();
+	 IN_ClearKeysDown();
+	 IN_Ack();
+
+	 DrawAllPlayBorderSides ();
+	 DebugOk=1;
+	}
+
+	//
+	// TRYING THE KEEN CHEAT CODE!
+	//
+	if (Keyboard[sc_B] &&
+		Keyboard[sc_A] &&
+		Keyboard[sc_T])
+	{
+	 ClearMemory ();
+	 CA_CacheGrChunk (STARTFONT+1);
+	 ClearSplitVWB ();
+	 VW_ScreenToScreen (displayofs,bufferofs,80,160);
+
+	 Message("Commander Keen is also\n"
+			 "available from Apogee, but\n"
+			 "then, you already know\n"
+			 "that - right, Cheatmeister?!");
+
+	 UNCACHEGRCHUNK(STARTFONT+1);
+	 PM_CheckMainMem ();
+	 IN_ClearKeysDown();
+	 IN_Ack();
+
+	 DrawAllPlayBorder ();
+	}
+
+//
+// pause key weirdness can't be checked as a scan code
+//
+	if (Paused)
+	{
+		bufferofs = displayofs;
+		LatchDrawPic (20-4,80-2*8,PAUSEDPIC);
+		SD_MusicOff();
+		IN_Ack();
+		IN_ClearKeysDown ();
+		SD_MusicOn();
+		Paused = false;
+		if (MousePresent)
+			Mouse(MDelta);	// Clear accumulated mouse movement
+		return;
+	}
+
+
+//
+// F1-F7/ESC to enter control panel
+//
+	if (
+#ifndef DEBCHECK
+		scan == sc_F10 ||
+#endif
+		scan == sc_F9 ||
+		scan == sc_F7 ||
+		scan == sc_F8)			// pop up quit dialog
+	{
+		ClearMemory ();
+		ClearSplitVWB ();
+		VW_ScreenToScreen (displayofs,bufferofs,80,160);
+		US_ControlPanel(scan);
+
+		 DrawAllPlayBorderSides ();
+
+		if (scan == sc_F9)
+		  StartMusic ();
+
+		PM_CheckMainMem ();
+		SETFONTCOLOR(0,15);
+		IN_ClearKeysDown();
+		return;
+	}
+
+	if ( (scan >= sc_F1 && scan <= sc_F9) || scan == sc_Escape)
+	{
+		StopMusic ();
+		ClearMemory ();
+		VW_FadeOut ();
+
+		US_ControlPanel(scan);
+
+		SETFONTCOLOR(0,15);
+		IN_ClearKeysDown();
+		DrawPlayScreen ();
+		if (!startgame && !loadedgame)
+		{
+			VW_FadeIn ();
+			StartMusic ();
+		}
+		if (loadedgame)
+			playstate = ex_abort;
+		lasttimecount = TimeCount;
+		if (MousePresent)
+			Mouse(MDelta);	// Clear accumulated mouse movement
+		PM_CheckMainMem ();
+		return;
+	}
+
+//
+// TAB-? debug keys
+//
+	if (Keyboard[sc_Tab] && DebugOk)
+	{
+		CA_CacheGrChunk (STARTFONT);
+		fontnumber=0;
+		SETFONTCOLOR(0,15);
+		DebugKeys();
+		if (MousePresent)
+			Mouse(MDelta);	// Clear accumulated mouse movement
+		lasttimecount = TimeCount;
+		return;
+	}
+
+}
+
+
+//===========================================================================
+
+/*
+#############################################################################
+
+				  The objlist data structure
+
+#############################################################################
+
+objlist containt structures for every actor currently playing.  The structure
+is accessed as a linked list starting at *player, ending when ob->next ==
+NULL.  GetNewObj inserts a new object at the end of the list, meaning that
+if an actor spawn another actor, the new one WILL get to think and react the
+same frame.  RemoveObj unlinks the given object and returns it to the free
+list, but does not damage the objects ->next pointer, so if the current object
+removes itself, a linked list following loop can still safely get to the
+next element.
+
+<backwardly linked free list>
+
+#############################################################################
+*/
+
+
+/*
+=========================
+=
+= InitActorList
+=
+= Call to clear out the actor object lists returning them all to the free
+= list.  Allocates a special spot for the player.
+=
+=========================
+*/
+
+int	objcount;
+
+void InitActorList (void)
+{
+	int	i;
+
+//
+// init the actor lists
+//
+	for (i=0;i<MAXACTORS;i++)
+	{
+		objlist[i].prev = &objlist[i+1];
+		objlist[i].next = NULL;
+	}
+
+	objlist[MAXACTORS-1].prev = NULL;
+
+	objfreelist = &objlist[0];
+	lastobj = NULL;
+
+	objcount = 0;
+
+//
+// give the player the first free spots
+//
+	GetNewActor ();
+	player = new;
+
+}
+
+//===========================================================================
+
+/*
+=========================
+=
+= GetNewActor
+=
+= Sets the global variable new to point to a free spot in objlist.
+= The free spot is inserted at the end of the liked list
+=
+= When the object list is full, the caller can either have it bomb out ot
+= return a dummy object pointer that will never get used
+=
+=========================
+*/
+
+void GetNewActor (void)
+{
+	if (!objfreelist)
+		Quit ("GetNewActor: No free spots in objlist!");
+
+	new = objfreelist;
+	objfreelist = new->prev;
+	memset (new,0,sizeof(*new));
+
+	if (lastobj)
+		lastobj->next = new;
+	new->prev = lastobj;	// new->next is allready NULL from memset
+
+	new->active = false;
+	lastobj = new;
+
+	objcount++;
+}
+
+//===========================================================================
+
+/*
+=========================
+=
+= RemoveObj
+=
+= Add the given object back into the free list, and unlink it from it's
+= neighbors
+=
+=========================
+*/
+
+void RemoveObj (objtype *gone)
+{
+	objtype **spotat;
+
+	if (gone == player)
+		Quit ("RemoveObj: Tried to remove the player!");
+
+	gone->state = NULL;
+
+//
+// fix the next object's back link
+//
+	if (gone == lastobj)
+		lastobj = (objtype *)gone->prev;
+	else
+		gone->next->prev = gone->prev;
+
+//
+// fix the previous object's forward link
+//
+	gone->prev->next = gone->next;
+
+//
+// add it back in to the free list
+//
+	gone->prev = objfreelist;
+	objfreelist = gone;
+
+	objcount--;
+}
+
+/*
+=============================================================================
+
+						MUSIC STUFF
+
+=============================================================================
+*/
+
+
+/*
+=================
+=
+= StopMusic
+=
+=================
+*/
+
+void StopMusic(void)
+{
+	int	i;
+
+	SD_MusicOff();
+	for (i = 0;i < LASTMUSIC;i++)
+		if (audiosegs[STARTMUSIC + i])
+		{
+			MM_SetPurge(&((memptr)audiosegs[STARTMUSIC + i]),3);
+			MM_SetLock(&((memptr)audiosegs[STARTMUSIC + i]),false);
+		}
+}
+
+//==========================================================================
+
+
+/*
+=================
+=
+= StartMusic
+=
+=================
+*/
+
+void StartMusic(void)
+{
+	musicnames	chunk;
+
+	SD_MusicOff();
+	chunk = songs[gamestate.mapon+gamestate.episode*10];
+
+//	if ((chunk == -1) || (MusicMode != smm_AdLib))
+//DEBUG control panel		return;
+
+	MM_BombOnError (false);
+	CA_CacheAudioChunk(STARTMUSIC + chunk);
+	MM_BombOnError (true);
+	if (mmerror)
+		mmerror = false;
+	else
+	{
+		MM_SetLock(&((memptr)audiosegs[STARTMUSIC + chunk]),true);
+		SD_StartMusic((MusicGroup far *)audiosegs[STARTMUSIC + chunk]);
+	}
+}
+
+
+/*
+=============================================================================
+
+					PALETTE SHIFTING STUFF
+
+=============================================================================
+*/
+
+#define NUMREDSHIFTS	6
+#define REDSTEPS		8
+
+#define NUMWHITESHIFTS	3
+#define WHITESTEPS		20
+#define WHITETICS		6
+
+
+byte	far redshifts[NUMREDSHIFTS][768];
+byte	far whiteshifts[NUMREDSHIFTS][768];
+
+int		damagecount,bonuscount;
+boolean	palshifted;
+
+extern 	byte	far	gamepal;
+
+/*
+=====================
+=
+= InitRedShifts
+=
+=====================
+*/
+
+void InitRedShifts (void)
+{
+	byte	far *workptr, far *baseptr;
+	int		i,j,delta;
+
+
+//
+// fade through intermediate frames
+//
+	for (i=1;i<=NUMREDSHIFTS;i++)
+	{
+		workptr = (byte far *)&redshifts[i-1][0];
+		baseptr = &gamepal;
+
+		for (j=0;j<=255;j++)
+		{
+			delta = 64-*baseptr;
+			*workptr++ = *baseptr++ + delta * i / REDSTEPS;
+			delta = -*baseptr;
+			*workptr++ = *baseptr++ + delta * i / REDSTEPS;
+			delta = -*baseptr;
+			*workptr++ = *baseptr++ + delta * i / REDSTEPS;
+		}
+	}
+
+	for (i=1;i<=NUMWHITESHIFTS;i++)
+	{
+		workptr = (byte far *)&whiteshifts[i-1][0];
+		baseptr = &gamepal;
+
+		for (j=0;j<=255;j++)
+		{
+			delta = 64-*baseptr;
+			*workptr++ = *baseptr++ + delta * i / WHITESTEPS;
+			delta = 62-*baseptr;
+			*workptr++ = *baseptr++ + delta * i / WHITESTEPS;
+			delta = 0-*baseptr;
+			*workptr++ = *baseptr++ + delta * i / WHITESTEPS;
+		}
+	}
+}
+
+
+/*
+=====================
+=
+= ClearPaletteShifts
+=
+=====================
+*/
+
+void ClearPaletteShifts (void)
+{
+	bonuscount = damagecount = 0;
+}
+
+
+/*
+=====================
+=
+= StartBonusFlash
+=
+=====================
+*/
+
+void StartBonusFlash (void)
+{
+	bonuscount = NUMWHITESHIFTS*WHITETICS;		// white shift palette
+}
+
+
+/*
+=====================
+=
+= StartDamageFlash
+=
+=====================
+*/
+
+void StartDamageFlash (int damage)
+{
+	damagecount += damage;
+}
+
+
+/*
+=====================
+=
+= UpdatePaletteShifts
+=
+=====================
+*/
+
+void UpdatePaletteShifts (void)
+{
+	int	red,white;
+
+	if (bonuscount)
+	{
+		white = bonuscount/WHITETICS +1;
+		if (white>NUMWHITESHIFTS)
+			white = NUMWHITESHIFTS;
+		bonuscount -= tics;
+		if (bonuscount < 0)
+			bonuscount = 0;
+	}
+	else
+		white = 0;
+
+
+	if (damagecount)
+	{
+		red = damagecount/10 +1;
+		if (red>NUMREDSHIFTS)
+			red = NUMREDSHIFTS;
+
+		damagecount -= tics;
+		if (damagecount < 0)
+			damagecount = 0;
+	}
+	else
+		red = 0;
+
+	if (red)
+	{
+		VW_WaitVBL(1);
+		VL_SetPalette (redshifts[red-1]);
+		palshifted = true;
+	}
+	else if (white)
+	{
+		VW_WaitVBL(1);
+		VL_SetPalette (whiteshifts[white-1]);
+		palshifted = true;
+	}
+	else if (palshifted)
+	{
+		VW_WaitVBL(1);
+		VL_SetPalette (&gamepal);		// back to normal
+		palshifted = false;
+	}
+}
+
+
+/*
+=====================
+=
+= FinishPaletteShifts
+=
+= Resets palette to normal if needed
+=
+=====================
+*/
+
+void FinishPaletteShifts (void)
+{
+	if (palshifted)
+	{
+		palshifted = 0;
+		VW_WaitVBL(1);
+		VL_SetPalette (&gamepal);
+	}
+}
+
+
+/*
+=============================================================================
+
+						CORE PLAYLOOP
+
+=============================================================================
+*/
+
+
+/*
+=====================
+=
+= DoActor
+=
+=====================
+*/
+
+void DoActor (objtype *ob)
+{
+	void (*think)(objtype *);
+
+	if (!ob->active && !areabyplayer[ob->areanumber])
+		return;
+
+	if (!(ob->flags&(FL_NONMARK|FL_NEVERMARK)) )
+		actorat[ob->tilex][ob->tiley] = NULL;
+
+//
+// non transitional object
+//
+
+	if (!ob->ticcount)
+	{
+		think =	ob->state->think;
+		if (think)
+		{
+			think (ob);
+			if (!ob->state)
+			{
+				RemoveObj (ob);
+				return;
+			}
+		}
+
+		if (ob->flags&FL_NEVERMARK)
+			return;
+
+		if ( (ob->flags&FL_NONMARK) && actorat[ob->tilex][ob->tiley])
+			return;
+
+		actorat[ob->tilex][ob->tiley] = ob;
+		return;
+	}
+
+//
+// transitional object
+//
+	ob->ticcount-=tics;
+	while ( ob->ticcount <= 0)
+	{
+		think = ob->state->action;			// end of state action
+		if (think)
+		{
+			think (ob);
+			if (!ob->state)
+			{
+				RemoveObj (ob);
+				return;
+			}
+		}
+
+		ob->state = ob->state->next;
+
+		if (!ob->state)
+		{
+			RemoveObj (ob);
+			return;
+		}
+
+		if (!ob->state->tictime)
+		{
+			ob->ticcount = 0;
+			goto think;
+		}
+
+		ob->ticcount += ob->state->tictime;
+	}
+
+think:
+	//
+	// think
+	//
+	think =	ob->state->think;
+	if (think)
+	{
+		think (ob);
+		if (!ob->state)
+		{
+			RemoveObj (ob);
+			return;
+		}
+	}
+
+	if (ob->flags&FL_NEVERMARK)
+		return;
+
+	if ( (ob->flags&FL_NONMARK) && actorat[ob->tilex][ob->tiley])
+		return;
+
+	actorat[ob->tilex][ob->tiley] = ob;
+}
+
+//==========================================================================
+
+
+/*
+===================
+=
+= PlayLoop
+=
+===================
+*/
+long funnyticount;
+
+
+void PlayLoop (void)
+{
+	int		give;
+	int	helmetangle;
+
+	playstate = TimeCount = lasttimecount = 0;
+	frameon = 0;
+	running = false;
+	anglefrac = 0;
+	facecount = 0;
+	funnyticount = 0;
+	memset (buttonstate,0,sizeof(buttonstate));
+	ClearPaletteShifts ();
+
+	if (MousePresent)
+		Mouse(MDelta);	// Clear accumulated mouse movement
+
+	if (demoplayback)
+		IN_StartAck ();
+
+	do
+	{
+		if (virtualreality)
+		{
+			helmetangle = peek (0x40,0xf0);
+			player->angle += helmetangle;
+			if (player->angle >= ANGLES)
+				player->angle -= ANGLES;
+		}
+
+
+		PollControls();
+
+//
+// actor thinking
+//
+		madenoise = false;
+
+		MoveDoors ();
+		MovePWalls ();
+
+		for (obj = player;obj;obj = obj->next)
+			DoActor (obj);
+
+		UpdatePaletteShifts ();
+
+		ThreeDRefresh ();
+
+		//
+		// MAKE FUNNY FACE IF BJ DOESN'T MOVE FOR AWHILE
+		//
+		#ifdef SPEAR
+		funnyticount += tics;
+		if (funnyticount > 30l*70)
+		{
+			funnyticount = 0;
+			StatusDrawPic (17,4,BJWAITING1PIC+(US_RndT()&1));
+			facecount = 0;
+		}
+		#endif
+
+		gamestate.TimeCount+=tics;
+
+		SD_Poll ();
+		UpdateSoundLoc();	// JAB
+
+		if (screenfaded)
+			VW_FadeIn ();
+
+		CheckKeys();
+
+//
+// debug aids
+//
+		if (singlestep)
+		{
+			VW_WaitVBL(14);
+			lasttimecount = TimeCount;
+		}
+		if (extravbls)
+			VW_WaitVBL(extravbls);
+
+		if (demoplayback)
+		{
+			if (IN_CheckAck ())
+			{
+				IN_ClearKeysDown ();
+				playstate = ex_abort;
+			}
+		}
+
+
+		if (virtualreality)
+		{
+			player->angle -= helmetangle;
+			if (player->angle < 0)
+				player->angle += ANGLES;
+		}
+
+	}while (!playstate && !startgame);
+
+	if (playstate != ex_died)
+		FinishPaletteShifts ();
+}
+
diff --git a/src/lib/hb/wl_scale.c b/src/lib/hb/wl_scale.c
new file mode 100755
index 00000000..9c1e102f
--- /dev/null
+++ b/src/lib/hb/wl_scale.c
@@ -0,0 +1,741 @@
+// WL_SCALE.C
+
+#include "WL_DEF.H"
+#pragma hdrstop
+
+#define OP_RETF	0xcb
+
+/*
+=============================================================================
+
+						  GLOBALS
+
+=============================================================================
+*/
+
+t_compscale _seg *scaledirectory[MAXSCALEHEIGHT+1];
+long			fullscalefarcall[MAXSCALEHEIGHT+1];
+
+int			maxscale,maxscaleshl2;
+
+boolean	insetupscaling;
+
+/*
+=============================================================================
+
+						  LOCALS
+
+=============================================================================
+*/
+
+t_compscale 	_seg *work;
+unsigned BuildCompScale (int height, memptr *finalspot);
+
+int			stepbytwo;
+
+//===========================================================================
+
+/*
+==============
+=
+= BadScale
+=
+==============
+*/
+
+void far BadScale (void)
+{
+	Quit ("BadScale called!");
+}
+
+
+/*
+==========================
+=
+= SetupScaling
+=
+==========================
+*/
+
+void SetupScaling (int maxscaleheight)
+{
+	int		i,x,y;
+	byte	far *dest;
+
+	insetupscaling = true;
+
+	maxscaleheight/=2;			// one scaler every two pixels
+
+	maxscale = maxscaleheight-1;
+	maxscaleshl2 = maxscale<<2;
+
+//
+// free up old scalers
+//
+	for (i=1;i<MAXSCALEHEIGHT;i++)
+	{
+		if (scaledirectory[i])
+			MM_FreePtr (&(memptr)scaledirectory[i]);
+		if (i>=stepbytwo)
+			i += 2;
+	}
+	memset (scaledirectory,0,sizeof(scaledirectory));
+
+	MM_SortMem ();
+
+//
+// build the compiled scalers
+//
+	stepbytwo = viewheight/2;	// save space by double stepping
+	MM_GetPtr (&(memptr)work,20000);
+
+	for (i=1;i<=maxscaleheight;i++)
+	{
+		BuildCompScale (i*2,&(memptr)scaledirectory[i]);
+		if (i>=stepbytwo)
+			i+= 2;
+	}
+	MM_FreePtr (&(memptr)work);
+
+//
+// compact memory and lock down scalers
+//
+	MM_SortMem ();
+	for (i=1;i<=maxscaleheight;i++)
+	{
+		MM_SetLock (&(memptr)scaledirectory[i],true);
+		fullscalefarcall[i] = (unsigned)scaledirectory[i];
+		fullscalefarcall[i] <<=16;
+		fullscalefarcall[i] += scaledirectory[i]->codeofs[0];
+		if (i>=stepbytwo)
+		{
+			scaledirectory[i+1] = scaledirectory[i];
+			fullscalefarcall[i+1] = fullscalefarcall[i];
+			scaledirectory[i+2] = scaledirectory[i];
+			fullscalefarcall[i+2] = fullscalefarcall[i];
+			i+=2;
+		}
+	}
+	scaledirectory[0] = scaledirectory[1];
+	fullscalefarcall[0] = fullscalefarcall[1];
+
+//
+// check for oversize wall drawing
+//
+	for (i=maxscaleheight;i<MAXSCALEHEIGHT;i++)
+		fullscalefarcall[i] = (long)BadScale;
+
+	insetupscaling = false;
+}
+
+//===========================================================================
+
+/*
+========================
+=
+= BuildCompScale
+=
+= Builds a compiled scaler object that will scale a 64 tall object to
+= the given height (centered vertically on the screen)
+=
+= height should be even
+=
+= Call with
+= ---------
+= DS:SI		Source for scale
+= ES:DI		Dest for scale
+=
+= Calling the compiled scaler only destroys AL
+=
+========================
+*/
+
+unsigned BuildCompScale (int height, memptr *finalspot)
+{
+	byte		far *code;
+
+	int			i;
+	long		fix,step;
+	unsigned	src,totalscaled,totalsize;
+	int			startpix,endpix,toppix;
+
+
+	step = ((long)height<<16) / 64;
+	code = &work->code[0];
+	toppix = (viewheight-height)/2;
+	fix = 0;
+
+	for (src=0;src<=64;src++)
+	{
+		startpix = fix>>16;
+		fix += step;
+		endpix = fix>>16;
+
+		if (endpix>startpix)
+			work->width[src] = endpix-startpix;
+		else
+			work->width[src] = 0;
+
+//
+// mark the start of the code
+//
+		work->codeofs[src] = FP_OFF(code);
+
+//
+// compile some code if the source pixel generates any screen pixels
+//
+		startpix+=toppix;
+		endpix+=toppix;
+
+		if (startpix == endpix || endpix < 0 || startpix >= viewheight || src == 64)
+			continue;
+
+	//
+	// mov al,[si+src]
+	//
+		*code++ = 0x8a;
+		*code++ = 0x44;
+		*code++ = src;
+
+		for (;startpix<endpix;startpix++)
+		{
+			if (startpix >= viewheight)
+				break;						// off the bottom of the view area
+			if (startpix < 0)
+				continue;					// not into the view area
+
+		//
+		// mov [es:di+heightofs],al
+		//
+			*code++ = 0x26;
+			*code++ = 0x88;
+			*code++ = 0x85;
+			*((unsigned far *)code)++ = startpix*SCREENBWIDE;
+		}
+
+	}
+
+//
+// retf
+//
+	*code++ = 0xcb;
+
+	totalsize = FP_OFF(code);
+	MM_GetPtr (finalspot,totalsize);
+	_fmemcpy ((byte _seg *)(*finalspot),(byte _seg *)work,totalsize);
+
+	return totalsize;
+}
+
+
+/*
+=======================
+=
+= ScaleLine
+=
+= linescale should have the high word set to the segment of the scaler
+=
+=======================
+*/
+
+extern	int			slinex,slinewidth;
+extern	unsigned	far *linecmds;
+extern	long		linescale;
+extern	unsigned	maskword;
+
+byte	mask1,mask2,mask3;
+
+
+void near ScaleLine (void)
+{
+asm	mov	cx,WORD PTR [linescale+2]
+asm	mov	es,cx						// segment of scaler
+
+asm	mov bp,WORD PTR [linecmds]
+asm	mov	dx,SC_INDEX+1				// to set SC_MAPMASK
+
+asm	mov	bx,[slinex]
+asm	mov	di,bx
+asm	shr	di,1						// X in bytes
+asm	shr	di,1
+asm	add	di,[bufferofs]
+asm	and	bx,3
+/* begin 8086 hack
+asm	shl	bx,3
+*/
+asm push cx
+asm mov cl,3
+asm shl bx,cl
+asm pop cx
+/* end 8086 hack */
+asm	add	bx,[slinewidth]				// bx = (pixel*8+pixwidth)
+asm	mov	al,BYTE [mapmasks3-1+bx]	// -1 because pixwidth of 1 is first
+asm	mov	ds,WORD PTR [linecmds+2]
+asm	or	al,al
+asm	jz	notthreebyte				// scale across three bytes
+asm	jmp	threebyte
+notthreebyte:
+asm	mov	al,BYTE PTR ss:[mapmasks2-1+bx]	// -1 because pixwidth of 1 is first
+asm	or	al,al
+asm	jnz	twobyte						// scale across two bytes
+
+//
+// one byte scaling
+//
+asm	mov	al,BYTE PTR ss:[mapmasks1-1+bx]	// -1 because pixwidth of 1 is first
+asm	out	dx,al						// set map mask register
+
+scalesingle:
+
+asm	mov	bx,[ds:bp]					// table location of rtl to patch
+asm	or	bx,bx
+asm	jz	linedone					// 0 signals end of segment list
+asm	mov	bx,[es:bx]
+asm	mov	dl,[es:bx]					// save old value
+asm	mov	BYTE PTR es:[bx],OP_RETF	// patch a RETF in
+asm	mov	si,[ds:bp+4]				// table location of entry spot
+asm	mov	ax,[es:si]
+asm	mov	WORD PTR ss:[linescale],ax	// call here to start scaling
+asm	mov	si,[ds:bp+2]				// corrected top of shape for this segment
+asm	add	bp,6						// next segment list
+
+asm	mov	ax,SCREENSEG
+asm	mov	es,ax
+asm	call ss:[linescale]				// scale the segment of pixels
+
+asm	mov	es,cx						// segment of scaler
+asm	mov	BYTE PTR es:[bx],dl			// unpatch the RETF
+asm	jmp	scalesingle					// do the next segment
+
+
+//
+// done
+//
+linedone:
+asm	mov	ax,ss
+asm	mov	ds,ax
+return;
+
+//
+// two byte scaling
+//
+twobyte:
+asm	mov	ss:[mask2],al
+asm	mov	al,BYTE PTR ss:[mapmasks1-1+bx]	// -1 because pixwidth of 1 is first
+asm	mov	ss:[mask1],al
+
+scaledouble:
+
+asm	mov	bx,[ds:bp]					// table location of rtl to patch
+asm	or	bx,bx
+asm	jz	linedone					// 0 signals end of segment list
+asm	mov	bx,[es:bx]
+asm	mov	cl,[es:bx]					// save old value
+asm	mov	BYTE PTR es:[bx],OP_RETF	// patch a RETF in
+asm	mov	si,[ds:bp+4]				// table location of entry spot
+asm	mov	ax,[es:si]
+asm	mov	WORD PTR ss:[linescale],ax	// call here to start scaling
+asm	mov	si,[ds:bp+2]				// corrected top of shape for this segment
+asm	add	bp,6						// next segment list
+
+asm	mov	ax,SCREENSEG
+asm	mov	es,ax
+asm	mov	al,ss:[mask1]
+asm	out	dx,al						// set map mask register
+asm	call ss:[linescale]				// scale the segment of pixels
+asm	inc	di
+asm	mov	al,ss:[mask2]
+asm	out	dx,al						// set map mask register
+asm	call ss:[linescale]				// scale the segment of pixels
+asm	dec	di
+
+asm	mov	es,WORD PTR ss:[linescale+2] // segment of scaler
+asm	mov	BYTE PTR es:[bx],cl			// unpatch the RETF
+asm	jmp	scaledouble					// do the next segment
+
+
+//
+// three byte scaling
+//
+threebyte:
+asm	mov	ss:[mask3],al
+asm	mov	al,BYTE PTR ss:[mapmasks2-1+bx]	// -1 because pixwidth of 1 is first
+asm	mov	ss:[mask2],al
+asm	mov	al,BYTE PTR ss:[mapmasks1-1+bx]	// -1 because pixwidth of 1 is first
+asm	mov	ss:[mask1],al
+
+scaletriple:
+
+asm	mov	bx,[ds:bp]					// table location of rtl to patch
+asm	or	bx,bx
+asm	jz	linedone					// 0 signals end of segment list
+asm	mov	bx,[es:bx]
+asm	mov	cl,[es:bx]					// save old value
+asm	mov	BYTE PTR es:[bx],OP_RETF	// patch a RETF in
+asm	mov	si,[ds:bp+4]				// table location of entry spot
+asm	mov	ax,[es:si]
+asm	mov	WORD PTR ss:[linescale],ax	// call here to start scaling
+asm	mov	si,[ds:bp+2]				// corrected top of shape for this segment
+asm	add	bp,6						// next segment list
+
+asm	mov	ax,SCREENSEG
+asm	mov	es,ax
+asm	mov	al,ss:[mask1]
+asm	out	dx,al						// set map mask register
+asm	call ss:[linescale]				// scale the segment of pixels
+asm	inc	di
+asm	mov	al,ss:[mask2]
+asm	out	dx,al						// set map mask register
+asm	call ss:[linescale]				// scale the segment of pixels
+asm	inc	di
+asm	mov	al,ss:[mask3]
+asm	out	dx,al						// set map mask register
+asm	call ss:[linescale]				// scale the segment of pixels
+asm	dec	di
+asm	dec	di
+
+asm	mov	es,WORD PTR ss:[linescale+2] // segment of scaler
+asm	mov	BYTE PTR es:[bx],cl			// unpatch the RETF
+asm	jmp	scaletriple					// do the next segment
+
+
+}
+
+
+/*
+=======================
+=
+= ScaleShape
+=
+= Draws a compiled shape at [scale] pixels high
+=
+= each vertical line of the shape has a pointer to segment data:
+= 	end of segment pixel*2 (0 terminates line) used to patch rtl in scaler
+= 	top of virtual line with segment in proper place
+=	start of segment pixel*2, used to jsl into compiled scaler
+=	<repeat>
+=
+= Setup for call
+= --------------
+= GC_MODE			read mode 1, write mode 2
+= GC_COLORDONTCARE  set to 0, so all reads from video memory return 0xff
+= GC_INDEX			pointing at GC_BITMASK
+=
+=======================
+*/
+
+static	long		longtemp;
+
+void ScaleShape (int xcenter, int shapenum, unsigned height)
+{
+	t_compshape	_seg *shape;
+	t_compscale _seg *comptable;
+	unsigned	scale,srcx,stopx,tempx;
+	int			t;
+	unsigned	far *cmdptr;
+	boolean		leftvis,rightvis;
+
+
+	shape = PM_GetSpritePage (shapenum);
+
+	scale = height>>3;						// low three bits are fractional
+	if (!scale || scale>maxscale)
+		return;								// too close or far away
+	comptable = scaledirectory[scale];
+
+	*(((unsigned *)&linescale)+1)=(unsigned)comptable;	// seg of far call
+	*(((unsigned *)&linecmds)+1)=(unsigned)shape;		// seg of shape
+
+//
+// scale to the left (from pixel 31 to shape->leftpix)
+//
+	srcx = 32;
+	slinex = xcenter;
+	stopx = shape->leftpix;
+	cmdptr = &shape->dataofs[31-stopx];
+
+	while ( --srcx >=stopx && slinex>0)
+	{
+		(unsigned)linecmds = *cmdptr--;
+		if ( !(slinewidth = comptable->width[srcx]) )
+			continue;
+
+		if (slinewidth == 1)
+		{
+			slinex--;
+			if (slinex<viewwidth)
+			{
+				if (wallheight[slinex] >= height)
+					continue;		// obscured by closer wall
+				ScaleLine ();
+			}
+			continue;
+		}
+
+		//
+		// handle multi pixel lines
+		//
+		if (slinex>viewwidth)
+		{
+			slinex -= slinewidth;
+			slinewidth = viewwidth-slinex;
+			if (slinewidth<1)
+				continue;		// still off the right side
+		}
+		else
+		{
+			if (slinewidth>slinex)
+				slinewidth = slinex;
+			slinex -= slinewidth;
+		}
+
+
+		leftvis = (wallheight[slinex] < height);
+		rightvis = (wallheight[slinex+slinewidth-1] < height);
+
+		if (leftvis)
+		{
+			if (rightvis)
+				ScaleLine ();
+			else
+			{
+				while (wallheight[slinex+slinewidth-1] >= height)
+					slinewidth--;
+				ScaleLine ();
+			}
+		}
+		else
+		{
+			if (!rightvis)
+				continue;		// totally obscured
+
+			while (wallheight[slinex] >= height)
+			{
+				slinex++;
+				slinewidth--;
+			}
+			ScaleLine ();
+			break;			// the rest of the shape is gone
+		}
+	}
+
+
+//
+// scale to the right
+//
+	slinex = xcenter;
+	stopx = shape->rightpix;
+	if (shape->leftpix<31)
+	{
+		srcx = 31;
+		cmdptr = &shape->dataofs[32-shape->leftpix];
+	}
+	else
+	{
+		srcx = shape->leftpix-1;
+		cmdptr = &shape->dataofs[0];
+	}
+	slinewidth = 0;
+
+	while ( ++srcx <= stopx && (slinex+=slinewidth)<viewwidth)
+	{
+		(unsigned)linecmds = *cmdptr++;
+		if ( !(slinewidth = comptable->width[srcx]) )
+			continue;
+
+		if (slinewidth == 1)
+		{
+			if (slinex>=0 && wallheight[slinex] < height)
+			{
+				ScaleLine ();
+			}
+			continue;
+		}
+
+		//
+		// handle multi pixel lines
+		//
+		if (slinex<0)
+		{
+			if (slinewidth <= -slinex)
+				continue;		// still off the left edge
+
+			slinewidth += slinex;
+			slinex = 0;
+		}
+		else
+		{
+			if (slinex + slinewidth > viewwidth)
+				slinewidth = viewwidth-slinex;
+		}
+
+
+		leftvis = (wallheight[slinex] < height);
+		rightvis = (wallheight[slinex+slinewidth-1] < height);
+
+		if (leftvis)
+		{
+			if (rightvis)
+			{
+				ScaleLine ();
+			}
+			else
+			{
+				while (wallheight[slinex+slinewidth-1] >= height)
+					slinewidth--;
+				ScaleLine ();
+				break;			// the rest of the shape is gone
+			}
+		}
+		else
+		{
+			if (rightvis)
+			{
+				while (wallheight[slinex] >= height)
+				{
+					slinex++;
+					slinewidth--;
+				}
+				ScaleLine ();
+			}
+			else
+				continue;		// totally obscured
+		}
+	}
+}
+
+
+
+/*
+=======================
+=
+= SimpleScaleShape
+=
+= NO CLIPPING, height in pixels
+=
+= Draws a compiled shape at [scale] pixels high
+=
+= each vertical line of the shape has a pointer to segment data:
+= 	end of segment pixel*2 (0 terminates line) used to patch rtl in scaler
+= 	top of virtual line with segment in proper place
+=	start of segment pixel*2, used to jsl into compiled scaler
+=	<repeat>
+=
+= Setup for call
+= --------------
+= GC_MODE			read mode 1, write mode 2
+= GC_COLORDONTCARE  set to 0, so all reads from video memory return 0xff
+= GC_INDEX			pointing at GC_BITMASK
+=
+=======================
+*/
+
+void SimpleScaleShape (int xcenter, int shapenum, unsigned height)
+{
+	t_compshape	_seg *shape;
+	t_compscale _seg *comptable;
+	unsigned	scale,srcx,stopx,tempx;
+	int			t;
+	unsigned	far *cmdptr;
+	boolean		leftvis,rightvis;
+
+
+	shape = PM_GetSpritePage (shapenum);
+
+	scale = height>>1;
+	comptable = scaledirectory[scale];
+
+	*(((unsigned *)&linescale)+1)=(unsigned)comptable;	// seg of far call
+	*(((unsigned *)&linecmds)+1)=(unsigned)shape;		// seg of shape
+
+//
+// scale to the left (from pixel 31 to shape->leftpix)
+//
+	srcx = 32;
+	slinex = xcenter;
+	stopx = shape->leftpix;
+	cmdptr = &shape->dataofs[31-stopx];
+
+	while ( --srcx >=stopx )
+	{
+		(unsigned)linecmds = *cmdptr--;
+		if ( !(slinewidth = comptable->width[srcx]) )
+			continue;
+
+		slinex -= slinewidth;
+		ScaleLine ();
+	}
+
+
+//
+// scale to the right
+//
+	slinex = xcenter;
+	stopx = shape->rightpix;
+	if (shape->leftpix<31)
+	{
+		srcx = 31;
+		cmdptr = &shape->dataofs[32-shape->leftpix];
+	}
+	else
+	{
+		srcx = shape->leftpix-1;
+		cmdptr = &shape->dataofs[0];
+	}
+	slinewidth = 0;
+
+	while ( ++srcx <= stopx )
+	{
+		(unsigned)linecmds = *cmdptr++;
+		if ( !(slinewidth = comptable->width[srcx]) )
+			continue;
+
+		ScaleLine ();
+		slinex+=slinewidth;
+	}
+}
+
+
+
+
+//
+// bit mask tables for drawing scaled strips up to eight pixels wide
+//
+// down here so the STUPID inline assembler doesn't get confused!
+//
+
+
+byte	mapmasks1[4][8] = {
+{1 ,3 ,7 ,15,15,15,15,15},
+{2 ,6 ,14,14,14,14,14,14},
+{4 ,12,12,12,12,12,12,12},
+{8 ,8 ,8 ,8 ,8 ,8 ,8 ,8} };
+
+byte	mapmasks2[4][8] = {
+{0 ,0 ,0 ,0 ,1 ,3 ,7 ,15},
+{0 ,0 ,0 ,1 ,3 ,7 ,15,15},
+{0 ,0 ,1 ,3 ,7 ,15,15,15},
+{0 ,1 ,3 ,7 ,15,15,15,15} };
+
+byte	mapmasks3[4][8] = {
+{0 ,0 ,0 ,0 ,0 ,0 ,0 ,0},
+{0 ,0 ,0 ,0 ,0 ,0 ,0 ,1},
+{0 ,0 ,0 ,0 ,0 ,0 ,1 ,3},
+{0 ,0 ,0 ,0 ,0 ,1 ,3 ,7} };
+
+
+unsigned	wordmasks[8][8] = {
+{0x0080,0x00c0,0x00e0,0x00f0,0x00f8,0x00fc,0x00fe,0x00ff},
+{0x0040,0x0060,0x0070,0x0078,0x007c,0x007e,0x007f,0x807f},
+{0x0020,0x0030,0x0038,0x003c,0x003e,0x003f,0x803f,0xc03f},
+{0x0010,0x0018,0x001c,0x001e,0x001f,0x801f,0xc01f,0xe01f},
+{0x0008,0x000c,0x000e,0x000f,0x800f,0xc00f,0xe00f,0xf00f},
+{0x0004,0x0006,0x0007,0x8007,0xc007,0xe007,0xf007,0xf807},
+{0x0002,0x0003,0x8003,0xc003,0xe003,0xf003,0xf803,0xfc03},
+{0x0001,0x8001,0xc001,0xe001,0xf001,0xf801,0xfc01,0xfe01} };
+
+int			slinex,slinewidth;
+unsigned	far *linecmds;
+long		linescale;
+unsigned	maskword;
+
diff --git a/src/lib/hb/wl_state.c b/src/lib/hb/wl_state.c
new file mode 100755
index 00000000..ad534ba9
--- /dev/null
+++ b/src/lib/hb/wl_state.c
@@ -0,0 +1,1480 @@
+// WL_STATE.C
+
+#include "WL_DEF.H"
+#pragma hdrstop
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+
+/*
+=============================================================================
+
+						 GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+
+dirtype opposite[9] =
+	{west,southwest,south,southeast,east,northeast,north,northwest,nodir};
+
+dirtype diagonal[9][9] =
+{
+/* east */	{nodir,nodir,northeast,nodir,nodir,nodir,southeast,nodir,nodir},
+			{nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir},
+/* north */ {northeast,nodir,nodir,nodir,northwest,nodir,nodir,nodir,nodir},
+			{nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir},
+/* west */  {nodir,nodir,northwest,nodir,nodir,nodir,southwest,nodir,nodir},
+			{nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir},
+/* south */ {southeast,nodir,nodir,nodir,southwest,nodir,nodir,nodir,nodir},
+			{nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir},
+			{nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir}
+};
+
+
+
+void	SpawnNewObj (unsigned tilex, unsigned tiley, statetype *state);
+void	NewState (objtype *ob, statetype *state);
+
+boolean TryWalk (objtype *ob);
+void	MoveObj (objtype *ob, long move);
+
+void	KillActor (objtype *ob);
+void	DamageActor (objtype *ob, unsigned damage);
+
+boolean CheckLine (objtype *ob);
+void FirstSighting (objtype *ob);
+boolean	CheckSight (objtype *ob);
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+
+
+//===========================================================================
+
+
+/*
+===================
+=
+= SpawnNewObj
+=
+= Spaws a new actor at the given TILE coordinates, with the given state, and
+= the given size in GLOBAL units.
+=
+= new			= a pointer to an initialized new actor
+=
+===================
+*/
+
+void SpawnNewObj (unsigned tilex, unsigned tiley, statetype *state)
+{
+	GetNewActor ();
+	new->state = state;
+	if (state->tictime)
+		new->ticcount = US_RndT () % state->tictime;
+	else
+		new->ticcount = 0;
+
+	new->tilex = tilex;
+	new->tiley = tiley;
+	new->x = ((long)tilex<<TILESHIFT)+TILEGLOBAL/2;
+	new->y = ((long)tiley<<TILESHIFT)+TILEGLOBAL/2;
+	new->dir = nodir;
+
+	actorat[tilex][tiley] = new;
+	new->areanumber =
+		*(mapsegs[0] + farmapylookup[new->tiley]+new->tilex) - AREATILE;
+}
+
+
+
+/*
+===================
+=
+= NewState
+=
+= Changes ob to a new state, setting ticcount to the max for that state
+=
+===================
+*/
+
+void NewState (objtype *ob, statetype *state)
+{
+	ob->state = state;
+	ob->ticcount = state->tictime;
+}
+
+
+
+/*
+=============================================================================
+
+				ENEMY TILE WORLD MOVEMENT CODE
+
+=============================================================================
+*/
+
+
+/*
+==================================
+=
+= TryWalk
+=
+= Attempts to move ob in its current (ob->dir) direction.
+=
+= If blocked by either a wall or an actor returns FALSE
+=
+= If move is either clear or blocked only by a door, returns TRUE and sets
+=
+= ob->tilex			= new destination
+= ob->tiley
+= ob->areanumber    = the floor tile number (0-(NUMAREAS-1)) of destination
+= ob->distance  	= TILEGLOBAl, or -doornumber if a door is blocking the way
+=
+= If a door is in the way, an OpenDoor call is made to start it opening.
+= The actor code should wait until
+= 	doorobjlist[-ob->distance].action = dr_open, meaning the door has been
+=	fully opened
+=
+==================================
+*/
+
+#define CHECKDIAG(x,y)								\
+{                                                   \
+	temp=(unsigned)actorat[x][y];                   \
+	if (temp)                                       \
+	{                                               \
+		if (temp<256)                               \
+			return false;                           \
+		if (((objtype *)temp)->flags&FL_SHOOTABLE)  \
+			return false;                           \
+	}                                               \
+}
+
+#define CHECKSIDE(x,y)								\
+{                                                   \
+	temp=(unsigned)actorat[x][y];                   \
+	if (temp)                                       \
+	{                                               \
+		if (temp<128)                               \
+			return false;                           \
+		if (temp<256)                               \
+			doornum = temp&63;                      \
+		else if (((objtype *)temp)->flags&FL_SHOOTABLE)\
+			return false;                           \
+	}                                               \
+}
+
+
+boolean TryWalk (objtype *ob)
+{
+	int			doornum;
+	unsigned	temp;
+
+	doornum = -1;
+
+	if (ob->obclass == inertobj)
+	{
+		switch (ob->dir)
+		{
+		case north:
+			ob->tiley--;
+			break;
+
+		case northeast:
+			ob->tilex++;
+			ob->tiley--;
+			break;
+
+		case east:
+			ob->tilex++;
+			break;
+
+		case southeast:
+			ob->tilex++;
+			ob->tiley++;
+			break;
+
+		case south:
+			ob->tiley++;
+			break;
+
+		case southwest:
+			ob->tilex--;
+			ob->tiley++;
+			break;
+
+		case west:
+			ob->tilex--;
+			break;
+
+		case northwest:
+			ob->tilex--;
+			ob->tiley--;
+			break;
+		}
+	}
+	else
+		switch (ob->dir)
+		{
+		case north:
+			if (ob->obclass == dogobj || ob->obclass == fakeobj)
+			{
+				CHECKDIAG(ob->tilex,ob->tiley-1);
+			}
+			else
+			{
+				CHECKSIDE(ob->tilex,ob->tiley-1);
+			}
+			ob->tiley--;
+			break;
+
+		case northeast:
+			CHECKDIAG(ob->tilex+1,ob->tiley-1);
+			CHECKDIAG(ob->tilex+1,ob->tiley);
+			CHECKDIAG(ob->tilex,ob->tiley-1);
+			ob->tilex++;
+			ob->tiley--;
+			break;
+
+		case east:
+			if (ob->obclass == dogobj || ob->obclass == fakeobj)
+			{
+				CHECKDIAG(ob->tilex+1,ob->tiley);
+			}
+			else
+			{
+				CHECKSIDE(ob->tilex+1,ob->tiley);
+			}
+			ob->tilex++;
+			break;
+
+		case southeast:
+			CHECKDIAG(ob->tilex+1,ob->tiley+1);
+			CHECKDIAG(ob->tilex+1,ob->tiley);
+			CHECKDIAG(ob->tilex,ob->tiley+1);
+			ob->tilex++;
+			ob->tiley++;
+			break;
+
+		case south:
+			if (ob->obclass == dogobj || ob->obclass == fakeobj)
+			{
+				CHECKDIAG(ob->tilex,ob->tiley+1);
+			}
+			else
+			{
+				CHECKSIDE(ob->tilex,ob->tiley+1);
+			}
+			ob->tiley++;
+			break;
+
+		case southwest:
+			CHECKDIAG(ob->tilex-1,ob->tiley+1);
+			CHECKDIAG(ob->tilex-1,ob->tiley);
+			CHECKDIAG(ob->tilex,ob->tiley+1);
+			ob->tilex--;
+			ob->tiley++;
+			break;
+
+		case west:
+			if (ob->obclass == dogobj || ob->obclass == fakeobj)
+			{
+				CHECKDIAG(ob->tilex-1,ob->tiley);
+			}
+			else
+			{
+				CHECKSIDE(ob->tilex-1,ob->tiley);
+			}
+			ob->tilex--;
+			break;
+
+		case northwest:
+			CHECKDIAG(ob->tilex-1,ob->tiley-1);
+			CHECKDIAG(ob->tilex-1,ob->tiley);
+			CHECKDIAG(ob->tilex,ob->tiley-1);
+			ob->tilex--;
+			ob->tiley--;
+			break;
+
+		case nodir:
+			return false;
+
+		default:
+			Quit ("Walk: Bad dir");
+		}
+
+	if (doornum != -1)
+	{
+		OpenDoor (doornum);
+		ob->distance = -doornum-1;
+		return true;
+	}
+
+
+	ob->areanumber =
+		*(mapsegs[0] + farmapylookup[ob->tiley]+ob->tilex) - AREATILE;
+
+	ob->distance = TILEGLOBAL;
+	return true;
+}
+
+
+
+/*
+==================================
+=
+= SelectDodgeDir
+=
+= Attempts to choose and initiate a movement for ob that sends it towards
+= the player while dodging
+=
+= If there is no possible move (ob is totally surrounded)
+=
+= ob->dir			=	nodir
+=
+= Otherwise
+=
+= ob->dir			= new direction to follow
+= ob->distance		= TILEGLOBAL or -doornumber
+= ob->tilex			= new destination
+= ob->tiley
+= ob->areanumber    = the floor tile number (0-(NUMAREAS-1)) of destination
+=
+==================================
+*/
+
+void SelectDodgeDir (objtype *ob)
+{
+	int 		deltax,deltay,i;
+	unsigned	absdx,absdy;
+	dirtype 	dirtry[5];
+	dirtype 	turnaround,tdir;
+
+	if (ob->flags & FL_FIRSTATTACK)
+	{
+	//
+	// turning around is only ok the very first time after noticing the
+	// player
+	//
+		turnaround = nodir;
+		ob->flags &= ~FL_FIRSTATTACK;
+	}
+	else
+		turnaround=opposite[ob->dir];
+
+	deltax = player->tilex - ob->tilex;
+	deltay = player->tiley - ob->tiley;
+
+//
+// arange 5 direction choices in order of preference
+// the four cardinal directions plus the diagonal straight towards
+// the player
+//
+
+	if (deltax>0)
+	{
+		dirtry[1]= east;
+		dirtry[3]= west;
+	}
+	else
+	{
+		dirtry[1]= west;
+		dirtry[3]= east;
+	}
+
+	if (deltay>0)
+	{
+		dirtry[2]= south;
+		dirtry[4]= north;
+	}
+	else
+	{
+		dirtry[2]= north;
+		dirtry[4]= south;
+	}
+
+//
+// randomize a bit for dodging
+//
+	absdx = abs(deltax);
+	absdy = abs(deltay);
+
+	if (absdx > absdy)
+	{
+		tdir = dirtry[1];
+		dirtry[1] = dirtry[2];
+		dirtry[2] = tdir;
+		tdir = dirtry[3];
+		dirtry[3] = dirtry[4];
+		dirtry[4] = tdir;
+	}
+
+	if (US_RndT() < 128)
+	{
+		tdir = dirtry[1];
+		dirtry[1] = dirtry[2];
+		dirtry[2] = tdir;
+		tdir = dirtry[3];
+		dirtry[3] = dirtry[4];
+		dirtry[4] = tdir;
+	}
+
+	dirtry[0] = diagonal [ dirtry[1] ] [ dirtry[2] ];
+
+//
+// try the directions util one works
+//
+	for (i=0;i<5;i++)
+	{
+		if ( dirtry[i] == nodir || dirtry[i] == turnaround)
+			continue;
+
+		ob->dir = dirtry[i];
+		if (TryWalk(ob))
+			return;
+	}
+
+//
+// turn around only as a last resort
+//
+	if (turnaround != nodir)
+	{
+		ob->dir = turnaround;
+
+		if (TryWalk(ob))
+			return;
+	}
+
+	ob->dir = nodir;
+}
+
+
+/*
+============================
+=
+= SelectChaseDir
+=
+= As SelectDodgeDir, but doesn't try to dodge
+=
+============================
+*/
+
+void SelectChaseDir (objtype *ob)
+{
+	int deltax,deltay,i;
+	dirtype d[3];
+	dirtype tdir, olddir, turnaround;
+
+
+	olddir=ob->dir;
+	turnaround=opposite[olddir];
+
+	deltax=player->tilex - ob->tilex;
+	deltay=player->tiley - ob->tiley;
+
+	d[1]=nodir;
+	d[2]=nodir;
+
+	if (deltax>0)
+		d[1]= east;
+	else if (deltax<0)
+		d[1]= west;
+	if (deltay>0)
+		d[2]=south;
+	else if (deltay<0)
+		d[2]=north;
+
+	if (abs(deltay)>abs(deltax))
+	{
+		tdir=d[1];
+		d[1]=d[2];
+		d[2]=tdir;
+	}
+
+	if (d[1]==turnaround)
+		d[1]=nodir;
+	if (d[2]==turnaround)
+		d[2]=nodir;
+
+
+	if (d[1]!=nodir)
+	{
+		ob->dir=d[1];
+		if (TryWalk(ob))
+			return;     /*either moved forward or attacked*/
+	}
+
+	if (d[2]!=nodir)
+	{
+		ob->dir=d[2];
+		if (TryWalk(ob))
+			return;
+	}
+
+/* there is no direct path to the player, so pick another direction */
+
+	if (olddir!=nodir)
+	{
+		ob->dir=olddir;
+		if (TryWalk(ob))
+			return;
+	}
+
+	if (US_RndT()>128) 	/*randomly determine direction of search*/
+	{
+		for (tdir=north;tdir<=west;tdir++)
+		{
+			if (tdir!=turnaround)
+			{
+				ob->dir=tdir;
+				if ( TryWalk(ob) )
+					return;
+			}
+		}
+	}
+	else
+	{
+		for (tdir=west;tdir>=north;tdir--)
+		{
+			if (tdir!=turnaround)
+			{
+			  ob->dir=tdir;
+			  if ( TryWalk(ob) )
+				return;
+			}
+		}
+	}
+
+	if (turnaround !=  nodir)
+	{
+		ob->dir=turnaround;
+		if (ob->dir != nodir)
+		{
+			if ( TryWalk(ob) )
+				return;
+		}
+	}
+
+	ob->dir = nodir;		// can't move
+}
+
+
+/*
+============================
+=
+= SelectRunDir
+=
+= Run Away from player
+=
+============================
+*/
+
+void SelectRunDir (objtype *ob)
+{
+	int deltax,deltay,i;
+	dirtype d[3];
+	dirtype tdir, olddir, turnaround;
+
+
+	deltax=player->tilex - ob->tilex;
+	deltay=player->tiley - ob->tiley;
+
+	if (deltax<0)
+		d[1]= east;
+	else
+		d[1]= west;
+	if (deltay<0)
+		d[2]=south;
+	else
+		d[2]=north;
+
+	if (abs(deltay)>abs(deltax))
+	{
+		tdir=d[1];
+		d[1]=d[2];
+		d[2]=tdir;
+	}
+
+	ob->dir=d[1];
+	if (TryWalk(ob))
+		return;     /*either moved forward or attacked*/
+
+	ob->dir=d[2];
+	if (TryWalk(ob))
+		return;
+
+/* there is no direct path to the player, so pick another direction */
+
+	if (US_RndT()>128) 	/*randomly determine direction of search*/
+	{
+		for (tdir=north;tdir<=west;tdir++)
+		{
+			ob->dir=tdir;
+			if ( TryWalk(ob) )
+				return;
+		}
+	}
+	else
+	{
+		for (tdir=west;tdir>=north;tdir--)
+		{
+			ob->dir=tdir;
+			if ( TryWalk(ob) )
+			  return;
+		}
+	}
+
+	ob->dir = nodir;		// can't move
+}
+
+
+/*
+=================
+=
+= MoveObj
+=
+= Moves ob be move global units in ob->dir direction
+= Actors are not allowed to move inside the player
+= Does NOT check to see if the move is tile map valid
+=
+= ob->x			= adjusted for new position
+= ob->y
+=
+=================
+*/
+
+void MoveObj (objtype *ob, long move)
+{
+	long	deltax,deltay;
+
+	switch (ob->dir)
+	{
+	case north:
+		ob->y -= move;
+		break;
+	case northeast:
+		ob->x += move;
+		ob->y -= move;
+		break;
+	case east:
+		ob->x += move;
+		break;
+	case southeast:
+		ob->x += move;
+		ob->y += move;
+		break;
+	case south:
+		ob->y += move;
+		break;
+	case southwest:
+		ob->x -= move;
+		ob->y += move;
+		break;
+	case west:
+		ob->x -= move;
+		break;
+	case northwest:
+		ob->x -= move;
+		ob->y -= move;
+		break;
+
+	case nodir:
+		return;
+
+	default:
+		Quit ("MoveObj: bad dir!");
+	}
+
+//
+// check to make sure it's not on top of player
+//
+	if (areabyplayer[ob->areanumber])
+	{
+		deltax = ob->x - player->x;
+		if (deltax < -MINACTORDIST || deltax > MINACTORDIST)
+			goto moveok;
+		deltay = ob->y - player->y;
+		if (deltay < -MINACTORDIST || deltay > MINACTORDIST)
+			goto moveok;
+
+		if (ob->obclass == ghostobj || ob->obclass == spectreobj)
+			TakeDamage (tics*2,ob);
+
+	//
+	// back up
+	//
+		switch (ob->dir)
+		{
+		case north:
+			ob->y += move;
+			break;
+		case northeast:
+			ob->x -= move;
+			ob->y += move;
+			break;
+		case east:
+			ob->x -= move;
+			break;
+		case southeast:
+			ob->x -= move;
+			ob->y -= move;
+			break;
+		case south:
+			ob->y -= move;
+			break;
+		case southwest:
+			ob->x += move;
+			ob->y -= move;
+			break;
+		case west:
+			ob->x += move;
+			break;
+		case northwest:
+			ob->x += move;
+			ob->y += move;
+			break;
+
+		case nodir:
+			return;
+		}
+		return;
+	}
+moveok:
+	ob->distance -=move;
+}
+
+/*
+=============================================================================
+
+							STUFF
+
+=============================================================================
+*/
+
+/*
+===============
+=
+= DropItem
+=
+= Tries to drop a bonus item somewhere in the tiles surrounding the
+= given tilex/tiley
+=
+===============
+*/
+
+void DropItem (stat_t itemtype, int tilex, int tiley)
+{
+	int	x,y,xl,xh,yl,yh;
+
+//
+// find a free spot to put it in
+//
+	if (!actorat[tilex][tiley])
+	{
+		PlaceItemType (itemtype, tilex,tiley);
+		return;
+	}
+
+	xl = tilex-1;
+	xh = tilex+1;
+	yl = tiley-1;
+	yh = tiley+1;
+
+	for (x=xl ; x<= xh ; x++)
+		for (y=yl ; y<= yh ; y++)
+			if (!actorat[x][y])
+			{
+				PlaceItemType (itemtype, x,y);
+				return;
+			}
+}
+
+
+
+/*
+===============
+=
+= KillActor
+=
+===============
+*/
+
+void KillActor (objtype *ob)
+{
+	int	tilex,tiley;
+
+	tilex = ob->tilex = ob->x >> TILESHIFT;		// drop item on center
+	tiley = ob->tiley = ob->y >> TILESHIFT;
+
+	switch (ob->obclass)
+	{
+	case guardobj:
+		GivePoints (100);
+		NewState (ob,&s_grddie1);
+		PlaceItemType (bo_clip2,tilex,tiley);
+		break;
+
+	case officerobj:
+		GivePoints (400);
+		NewState (ob,&s_ofcdie1);
+		PlaceItemType (bo_clip2,tilex,tiley);
+		break;
+
+	case mutantobj:
+		GivePoints (700);
+		NewState (ob,&s_mutdie1);
+		PlaceItemType (bo_clip2,tilex,tiley);
+		break;
+
+	case ssobj:
+		GivePoints (500);
+		NewState (ob,&s_ssdie1);
+		if (gamestate.bestweapon < wp_machinegun)
+			PlaceItemType (bo_machinegun,tilex,tiley);
+		else
+			PlaceItemType (bo_clip2,tilex,tiley);
+		break;
+
+	case dogobj:
+		GivePoints (200);
+		NewState (ob,&s_dogdie1);
+		break;
+
+#ifndef SPEAR
+	case bossobj:
+		GivePoints (5000);
+		NewState (ob,&s_bossdie1);
+		PlaceItemType (bo_key1,tilex,tiley);
+		break;
+
+	case gretelobj:
+		GivePoints (5000);
+		NewState (ob,&s_greteldie1);
+		PlaceItemType (bo_key1,tilex,tiley);
+		break;
+
+	case giftobj:
+		GivePoints (5000);
+		gamestate.killx = player->x;
+		gamestate.killy = player->y;
+		NewState (ob,&s_giftdie1);
+		break;
+
+	case fatobj:
+		GivePoints (5000);
+		gamestate.killx = player->x;
+		gamestate.killy = player->y;
+		NewState (ob,&s_fatdie1);
+		break;
+
+	case schabbobj:
+		GivePoints (5000);
+		gamestate.killx = player->x;
+		gamestate.killy = player->y;
+		NewState (ob,&s_schabbdie1);
+		A_DeathScream(ob);
+		break;
+	case fakeobj:
+		GivePoints (2000);
+		NewState (ob,&s_fakedie1);
+		break;
+
+	case mechahitlerobj:
+		GivePoints (5000);
+		NewState (ob,&s_mechadie1);
+		break;
+	case realhitlerobj:
+		GivePoints (5000);
+		gamestate.killx = player->x;
+		gamestate.killy = player->y;
+		NewState (ob,&s_hitlerdie1);
+		A_DeathScream(ob);
+		break;
+#else
+	case spectreobj:
+		GivePoints (200);
+		NewState (ob,&s_spectredie1);
+		break;
+
+	case angelobj:
+		GivePoints (5000);
+		NewState (ob,&s_angeldie1);
+		break;
+
+	case transobj:
+		GivePoints (5000);
+		NewState (ob,&s_transdie0);
+		PlaceItemType (bo_key1,tilex,tiley);
+		break;
+
+	case uberobj:
+		GivePoints (5000);
+		NewState (ob,&s_uberdie0);
+		PlaceItemType (bo_key1,tilex,tiley);
+		break;
+
+	case willobj:
+		GivePoints (5000);
+		NewState (ob,&s_willdie1);
+		PlaceItemType (bo_key1,tilex,tiley);
+		break;
+
+	case deathobj:
+		GivePoints (5000);
+		NewState (ob,&s_deathdie1);
+		PlaceItemType (bo_key1,tilex,tiley);
+		break;
+#endif
+	}
+
+	gamestate.killcount++;
+	ob->flags &= ~FL_SHOOTABLE;
+	actorat[ob->tilex][ob->tiley] = NULL;
+	ob->flags |= FL_NONMARK;
+}
+
+
+
+/*
+===================
+=
+= DamageActor
+=
+= Called when the player succesfully hits an enemy.
+=
+= Does damage points to enemy ob, either putting it into a stun frame or
+= killing it.
+=
+===================
+*/
+
+void DamageActor (objtype *ob, unsigned damage)
+{
+	madenoise = true;
+
+//
+// do double damage if shooting a non attack mode actor
+//
+	if ( !(ob->flags & FL_ATTACKMODE) )
+		damage <<= 1;
+
+	ob->hitpoints -= damage;
+
+	if (ob->hitpoints<=0)
+		KillActor (ob);
+	else
+	{
+		if (! (ob->flags & FL_ATTACKMODE) )
+			FirstSighting (ob);		// put into combat mode
+
+		switch (ob->obclass)		// dogs only have one hit point
+		{
+		case guardobj:
+			if (ob->hitpoints&1)
+				NewState (ob,&s_grdpain);
+			else
+				NewState (ob,&s_grdpain1);
+			break;
+
+		case officerobj:
+			if (ob->hitpoints&1)
+				NewState (ob,&s_ofcpain);
+			else
+				NewState (ob,&s_ofcpain1);
+			break;
+
+		case mutantobj:
+			if (ob->hitpoints&1)
+				NewState (ob,&s_mutpain);
+			else
+				NewState (ob,&s_mutpain1);
+			break;
+
+		case ssobj:
+			if (ob->hitpoints&1)
+				NewState (ob,&s_sspain);
+			else
+				NewState (ob,&s_sspain1);
+
+			break;
+
+		}
+	}
+}
+
+/*
+=============================================================================
+
+							CHECKSIGHT
+
+=============================================================================
+*/
+
+
+/*
+=====================
+=
+= CheckLine
+=
+= Returns true if a straight line between the player and ob is unobstructed
+=
+=====================
+*/
+
+boolean CheckLine (objtype *ob)
+{
+	int	x1,y1,xt1,yt1,x2,y2,xt2,yt2;
+	int	x,y;
+	int	xdist,ydist,xstep,ystep;
+	int	temp;
+	int	partial,delta;
+	long	ltemp;
+	int	xfrac,yfrac,deltafrac;
+	unsigned	value,intercept;
+
+	x1 = ob->x >> UNSIGNEDSHIFT;		// 1/256 tile precision
+	y1 = ob->y >> UNSIGNEDSHIFT;
+	xt1 = x1 >> 8;
+	yt1 = y1 >> 8;
+
+	x2 = plux;
+	y2 = pluy;
+	xt2 = player->tilex;
+	yt2 = player->tiley;
+
+
+	xdist = abs(xt2-xt1);
+
+	if (xdist > 0)
+	{
+		if (xt2 > xt1)
+		{
+			partial = 256-(x1&0xff);
+			xstep = 1;
+		}
+		else
+		{
+			partial = x1&0xff;
+			xstep = -1;
+		}
+
+		deltafrac = abs(x2-x1);
+		delta = y2-y1;
+		ltemp = ((long)delta<<8)/deltafrac;
+		if (ltemp > 0x7fffl)
+			ystep = 0x7fff;
+		else if (ltemp < -0x7fffl)
+			ystep = -0x7fff;
+		else
+			ystep = ltemp;
+		yfrac = y1 + (((long)ystep*partial) >>8);
+
+		x = xt1+xstep;
+		xt2 += xstep;
+		do
+		{
+			y = yfrac>>8;
+			yfrac += ystep;
+
+			value = (unsigned)tilemap[x][y];
+			x += xstep;
+
+			if (!value)
+				continue;
+
+			if (value<128 || value>256)
+				return false;
+
+			//
+			// see if the door is open enough
+			//
+			value &= ~0x80;
+			intercept = yfrac-ystep/2;
+
+			if (intercept>doorposition[value])
+				return false;
+
+		} while (x != xt2);
+	}
+
+	ydist = abs(yt2-yt1);
+
+	if (ydist > 0)
+	{
+		if (yt2 > yt1)
+		{
+			partial = 256-(y1&0xff);
+			ystep = 1;
+		}
+		else
+		{
+			partial = y1&0xff;
+			ystep = -1;
+		}
+
+		deltafrac = abs(y2-y1);
+		delta = x2-x1;
+		ltemp = ((long)delta<<8)/deltafrac;
+		if (ltemp > 0x7fffl)
+			xstep = 0x7fff;
+		else if (ltemp < -0x7fffl)
+			xstep = -0x7fff;
+		else
+			xstep = ltemp;
+		xfrac = x1 + (((long)xstep*partial) >>8);
+
+		y = yt1 + ystep;
+		yt2 += ystep;
+		do
+		{
+			x = xfrac>>8;
+			xfrac += xstep;
+
+			value = (unsigned)tilemap[x][y];
+			y += ystep;
+
+			if (!value)
+				continue;
+
+			if (value<128 || value>256)
+				return false;
+
+			//
+			// see if the door is open enough
+			//
+			value &= ~0x80;
+			intercept = xfrac-xstep/2;
+
+			if (intercept>doorposition[value])
+				return false;
+		} while (y != yt2);
+	}
+
+	return true;
+}
+
+
+
+/*
+================
+=
+= CheckSight
+=
+= Checks a straight line between player and current object
+=
+= If the sight is ok, check alertness and angle to see if they notice
+=
+= returns true if the player has been spoted
+=
+================
+*/
+
+#define MINSIGHT	0x18000l
+
+boolean CheckSight (objtype *ob)
+{
+	long		deltax,deltay;
+
+//
+// don't bother tracing a line if the area isn't connected to the player's
+//
+	if (!areabyplayer[ob->areanumber])
+		return false;
+
+//
+// if the player is real close, sight is automatic
+//
+	deltax = player->x - ob->x;
+	deltay = player->y - ob->y;
+
+	if (deltax > -MINSIGHT && deltax < MINSIGHT
+	&& deltay > -MINSIGHT && deltay < MINSIGHT)
+		return true;
+
+//
+// see if they are looking in the right direction
+//
+	switch (ob->dir)
+	{
+	case north:
+		if (deltay > 0)
+			return false;
+		break;
+
+	case east:
+		if (deltax < 0)
+			return false;
+		break;
+
+	case south:
+		if (deltay < 0)
+			return false;
+		break;
+
+	case west:
+		if (deltax > 0)
+			return false;
+		break;
+	}
+
+//
+// trace a line to check for blocking tiles (corners)
+//
+	return CheckLine (ob);
+
+}
+
+
+
+/*
+===============
+=
+= FirstSighting
+=
+= Puts an actor into attack mode and possibly reverses the direction
+= if the player is behind it
+=
+===============
+*/
+
+void FirstSighting (objtype *ob)
+{
+//
+// react to the player
+//
+	switch (ob->obclass)
+	{
+	case guardobj:
+		PlaySoundLocActor(HALTSND,ob);
+		NewState (ob,&s_grdchase1);
+		ob->speed *= 3;			// go faster when chasing player
+		break;
+
+	case officerobj:
+		PlaySoundLocActor(SPIONSND,ob);
+		NewState (ob,&s_ofcchase1);
+		ob->speed *= 5;			// go faster when chasing player
+		break;
+
+	case mutantobj:
+		NewState (ob,&s_mutchase1);
+		ob->speed *= 3;			// go faster when chasing player
+		break;
+
+	case ssobj:
+		PlaySoundLocActor(SCHUTZADSND,ob);
+		NewState (ob,&s_sschase1);
+		ob->speed *= 4;			// go faster when chasing player
+		break;
+
+	case dogobj:
+		PlaySoundLocActor(DOGBARKSND,ob);
+		NewState (ob,&s_dogchase1);
+		ob->speed *= 2;			// go faster when chasing player
+		break;
+
+#ifndef SPEAR
+	case bossobj:
+		SD_PlaySound(GUTENTAGSND);
+		NewState (ob,&s_bosschase1);
+		ob->speed = SPDPATROL*3;	// go faster when chasing player
+		break;
+
+	case gretelobj:
+		SD_PlaySound(KEINSND);
+		NewState (ob,&s_gretelchase1);
+		ob->speed *= 3;			// go faster when chasing player
+		break;
+
+	case giftobj:
+		SD_PlaySound(EINESND);
+		NewState (ob,&s_giftchase1);
+		ob->speed *= 3;			// go faster when chasing player
+		break;
+
+	case fatobj:
+		SD_PlaySound(ERLAUBENSND);
+		NewState (ob,&s_fatchase1);
+		ob->speed *= 3;			// go faster when chasing player
+		break;
+
+	case schabbobj:
+		SD_PlaySound(SCHABBSHASND);
+		NewState (ob,&s_schabbchase1);
+		ob->speed *= 3;			// go faster when chasing player
+		break;
+
+	case fakeobj:
+		SD_PlaySound(TOT_HUNDSND);
+		NewState (ob,&s_fakechase1);
+		ob->speed *= 3;			// go faster when chasing player
+		break;
+
+	case mechahitlerobj:
+		SD_PlaySound(DIESND);
+		NewState (ob,&s_mechachase1);
+		ob->speed *= 3;			// go faster when chasing player
+		break;
+
+	case realhitlerobj:
+		SD_PlaySound(DIESND);
+		NewState (ob,&s_hitlerchase1);
+		ob->speed *= 5;			// go faster when chasing player
+		break;
+
+	case ghostobj:
+		NewState (ob,&s_blinkychase1);
+		ob->speed *= 2;			// go faster when chasing player
+		break;
+#else
+
+	case spectreobj:
+		SD_PlaySound(GHOSTSIGHTSND);
+		NewState (ob,&s_spectrechase1);
+		ob->speed = 800;			// go faster when chasing player
+		break;
+
+	case angelobj:
+		SD_PlaySound(ANGELSIGHTSND);
+		NewState (ob,&s_angelchase1);
+		ob->speed = 1536;			// go faster when chasing player
+		break;
+
+	case transobj:
+		SD_PlaySound(TRANSSIGHTSND);
+		NewState (ob,&s_transchase1);
+		ob->speed = 1536;			// go faster when chasing player
+		break;
+
+	case uberobj:
+		NewState (ob,&s_uberchase1);
+		ob->speed = 3000;			// go faster when chasing player
+		break;
+
+	case willobj:
+		SD_PlaySound(WILHELMSIGHTSND);
+		NewState (ob,&s_willchase1);
+		ob->speed = 2048;			// go faster when chasing player
+		break;
+
+	case deathobj:
+		SD_PlaySound(KNIGHTSIGHTSND);
+		NewState (ob,&s_deathchase1);
+		ob->speed = 2048;			// go faster when chasing player
+		break;
+
+#endif
+	}
+
+	if (ob->distance < 0)
+		ob->distance = 0;	// ignore the door opening command
+
+	ob->flags |= FL_ATTACKMODE|FL_FIRSTATTACK;
+}
+
+
+
+/*
+===============
+=
+= SightPlayer
+=
+= Called by actors that ARE NOT chasing the player.  If the player
+= is detected (by sight, noise, or proximity), the actor is put into
+= it's combat frame and true is returned.
+=
+= Incorporates a random reaction delay
+=
+===============
+*/
+
+boolean SightPlayer (objtype *ob)
+{
+	if (ob->flags & FL_ATTACKMODE)
+		Quit ("An actor in ATTACKMODE called SightPlayer!");
+
+	if (ob->temp2)
+	{
+	//
+	// count down reaction time
+	//
+		ob->temp2 -= tics;
+		if (ob->temp2 > 0)
+			return false;
+		ob->temp2 = 0;					// time to react
+	}
+	else
+	{
+		if (!areabyplayer[ob->areanumber])
+			return false;
+
+		if (ob->flags & FL_AMBUSH)
+		{
+			if (!CheckSight (ob))
+				return false;
+			ob->flags &= ~FL_AMBUSH;
+		}
+		else
+		{
+			if (!madenoise && !CheckSight (ob))
+				return false;
+		}
+
+
+		switch (ob->obclass)
+		{
+		case guardobj:
+			ob->temp2 = 1+US_RndT()/4;
+			break;
+		case officerobj:
+			ob->temp2 = 2;
+			break;
+		case mutantobj:
+			ob->temp2 = 1+US_RndT()/6;
+			break;
+		case ssobj:
+			ob->temp2 = 1+US_RndT()/6;
+			break;
+		case dogobj:
+			ob->temp2 = 1+US_RndT()/8;
+			break;
+
+		case bossobj:
+		case schabbobj:
+		case fakeobj:
+		case mechahitlerobj:
+		case realhitlerobj:
+		case gretelobj:
+		case giftobj:
+		case fatobj:
+		case spectreobj:
+		case angelobj:
+		case transobj:
+		case uberobj:
+		case willobj:
+		case deathobj:
+			ob->temp2 = 1;
+			break;
+		}
+		return false;
+	}
+
+	FirstSighting (ob);
+
+	return true;
+}
+
+
diff --git a/src/lib/hb/wl_text.c b/src/lib/hb/wl_text.c
new file mode 100755
index 00000000..1df86b83
--- /dev/null
+++ b/src/lib/hb/wl_text.c
@@ -0,0 +1,859 @@
+// WL_TEXT.C
+
+#include "WL_DEF.H"
+#pragma	hdrstop
+
+/*
+=============================================================================
+
+TEXT FORMATTING COMMANDS
+------------------------
+^C<hex digit>  			Change text color
+^E[enter]				End of layout (all pages)
+^G<y>,<x>,<pic>[enter]	Draw a graphic and push margins
+^P[enter]				start new page, must be the first chars in a layout
+^L<x>,<y>[ENTER]		Locate to a specific spot, x in pixels, y in lines
+
+=============================================================================
+*/
+
+/*
+=============================================================================
+
+						 LOCAL CONSTANTS
+
+=============================================================================
+*/
+
+#define BACKCOLOR		0x11
+
+
+#define WORDLIMIT		80
+#define FONTHEIGHT		10
+#define	TOPMARGIN		16
+#define BOTTOMMARGIN	32
+#define LEFTMARGIN		16
+#define RIGHTMARGIN		16
+#define PICMARGIN		8
+#define TEXTROWS		((200-TOPMARGIN-BOTTOMMARGIN)/FONTHEIGHT)
+#define	SPACEWIDTH		7
+#define SCREENPIXWIDTH	320
+#define SCREENMID		(SCREENPIXWIDTH/2)
+
+/*
+=============================================================================
+
+						 LOCAL VARIABLES
+
+=============================================================================
+*/
+
+int			pagenum,numpages;
+
+unsigned	leftmargin[TEXTROWS],rightmargin[TEXTROWS];
+char		far *text;
+unsigned	rowon;
+
+int			picx,picy,picnum,picdelay;
+boolean		layoutdone;
+
+//===========================================================================
+
+#ifndef JAPAN
+/*
+=====================
+=
+= RipToEOL
+=
+=====================
+*/
+
+void RipToEOL (void)
+{
+	while (*text++ != '\n')		// scan to end of line
+	;
+}
+
+
+/*
+=====================
+=
+= ParseNumber
+=
+=====================
+*/
+
+int	ParseNumber (void)
+{
+	char	ch;
+	char	num[80],*numptr;
+
+//
+// scan until a number is found
+//
+	ch = *text;
+	while (ch < '0' || ch >'9')
+		ch = *++text;
+
+//
+// copy the number out
+//
+	numptr = num;
+	do
+	{
+		*numptr++ = ch;
+		ch = *++text;
+	} while (ch >= '0' && ch <= '9');
+	*numptr = 0;
+
+	return atoi (num);
+}
+
+
+
+/*
+=====================
+=
+= ParsePicCommand
+=
+= Call with text pointing just after a ^P
+= Upon exit text points to the start of next line
+=
+=====================
+*/
+
+void	ParsePicCommand (void)
+{
+	picy=ParseNumber();
+	picx=ParseNumber();
+	picnum=ParseNumber();
+	RipToEOL ();
+}
+
+
+void	ParseTimedCommand (void)
+{
+	picy=ParseNumber();
+	picx=ParseNumber();
+	picnum=ParseNumber();
+	picdelay=ParseNumber();
+	RipToEOL ();
+}
+
+
+/*
+=====================
+=
+= TimedPicCommand
+=
+= Call with text pointing just after a ^P
+= Upon exit text points to the start of next line
+=
+=====================
+*/
+
+void	TimedPicCommand (void)
+{
+	ParseTimedCommand ();
+
+//
+// update the screen, and wait for time delay
+//
+	VW_UpdateScreen ();
+
+//
+// wait for time
+//
+	TimeCount = 0;
+	while (TimeCount < picdelay)
+	;
+
+//
+// draw pic
+//
+	VWB_DrawPic (picx&~7,picy,picnum);
+}
+
+
+/*
+=====================
+=
+= HandleCommand
+=
+=====================
+*/
+
+void HandleCommand (void)
+{
+	int	i,margin,top,bottom;
+	int	picwidth,picheight,picmid;
+
+	switch (toupper(*++text))
+	{
+	case 'B':
+		picy=ParseNumber();
+		picx=ParseNumber();
+		picwidth=ParseNumber();
+		picheight=ParseNumber();
+		VWB_Bar(picx,picy,picwidth,picheight,BACKCOLOR);
+		RipToEOL();
+		break;
+	case ';':		// comment
+		RipToEOL();
+		break;
+	case 'P':		// ^P is start of next page, ^E is end of file
+	case 'E':
+		layoutdone = true;
+		text--;    	// back up to the '^'
+		break;
+
+	case 'C':		// ^c<hex digit> changes text color
+		i = toupper(*++text);
+		if (i>='0' && i<='9')
+			fontcolor = i-'0';
+		else if (i>='A' && i<='F')
+			fontcolor = i-'A'+10;
+
+		fontcolor *= 16;
+		i = toupper(*++text);
+		if (i>='0' && i<='9')
+			fontcolor += i-'0';
+		else if (i>='A' && i<='F')
+			fontcolor += i-'A'+10;
+		text++;
+		break;
+
+	case '>':
+		px = 160;
+		text++;
+		break;
+
+	case 'L':
+		py=ParseNumber();
+		rowon = (py-TOPMARGIN)/FONTHEIGHT;
+		py = TOPMARGIN+rowon*FONTHEIGHT;
+		px=ParseNumber();
+		while (*text++ != '\n')		// scan to end of line
+		;
+		break;
+
+	case 'T':		// ^Tyyy,xxx,ppp,ttt waits ttt tics, then draws pic
+		TimedPicCommand ();
+		break;
+
+	case 'G':		// ^Gyyy,xxx,ppp draws graphic
+		ParsePicCommand ();
+		VWB_DrawPic (picx&~7,picy,picnum);
+		picwidth = pictable[picnum-STARTPICS].width;
+		picheight = pictable[picnum-STARTPICS].height;
+		//
+		// adjust margins
+		//
+		picmid = picx + picwidth/2;
+		if (picmid > SCREENMID)
+			margin = picx-PICMARGIN;			// new right margin
+		else
+			margin = picx+picwidth+PICMARGIN;	// new left margin
+
+		top = (picy-TOPMARGIN)/FONTHEIGHT;
+		if (top<0)
+			top = 0;
+		bottom = (picy+picheight-TOPMARGIN)/FONTHEIGHT;
+		if (bottom>=TEXTROWS)
+			bottom = TEXTROWS-1;
+
+		for (i=top;i<=bottom;i++)
+			if (picmid > SCREENMID)
+				rightmargin[i] = margin;
+			else
+				leftmargin[i] = margin;
+
+		//
+		// adjust this line if needed
+		//
+		if (px < leftmargin[rowon])
+			px = leftmargin[rowon];
+		break;
+	}
+}
+
+
+/*
+=====================
+=
+= NewLine
+=
+=====================
+*/
+
+void NewLine (void)
+{
+	char	ch;
+
+	if (++rowon == TEXTROWS)
+	{
+	//
+	// overflowed the page, so skip until next page break
+	//
+		layoutdone = true;
+		do
+		{
+			if (*text == '^')
+			{
+				ch = toupper(*(text+1));
+				if (ch == 'E' || ch == 'P')
+				{
+					layoutdone = true;
+					return;
+				}
+			}
+			text++;
+
+		} while (1);
+
+	}
+	px = leftmargin[rowon];
+	py+= FONTHEIGHT;
+}
+
+
+
+/*
+=====================
+=
+= HandleCtrls
+=
+=====================
+*/
+
+void HandleCtrls (void)
+{
+	char	ch;
+
+	ch = *text++;			// get the character and advance
+
+	if (ch == '\n')
+	{
+		NewLine ();
+		return;
+	}
+
+}
+
+
+/*
+=====================
+=
+= HandleWord
+=
+=====================
+*/
+
+void HandleWord (void)
+{
+	char		word[WORDLIMIT];
+	int			i,wordindex;
+	unsigned	wwidth,wheight,newpos;
+
+
+	//
+	// copy the next word into [word]
+	//
+	word[0] = *text++;
+	wordindex = 1;
+	while (*text>32)
+	{
+		word[wordindex] = *text++;
+		if (++wordindex == WORDLIMIT)
+			Quit ("PageLayout: Word limit exceeded");
+	}
+	word[wordindex] = 0;		// stick a null at end for C
+
+	//
+	// see if it fits on this line
+	//
+	VW_MeasurePropString (word,&wwidth,&wheight);
+
+	while (px+wwidth > rightmargin[rowon])
+	{
+		NewLine ();
+		if (layoutdone)
+			return;		// overflowed page
+	}
+
+	//
+	// print it
+	//
+	newpos = px+wwidth;
+	VWB_DrawPropString (word);
+	px = newpos;
+
+	//
+	// suck up any extra spaces
+	//
+	while (*text == ' ')
+	{
+		px += SPACEWIDTH;
+		text++;
+	}
+}
+
+/*
+=====================
+=
+= PageLayout
+=
+= Clears the screen, draws the pics on the page, and word wraps the text.
+= Returns a pointer to the terminating command
+=
+=====================
+*/
+
+void PageLayout (boolean shownumber)
+{
+	int		i,oldfontcolor;
+	char	ch;
+
+	oldfontcolor = fontcolor;
+
+	fontcolor = 0;
+
+//
+// clear the screen
+//
+	VWB_Bar (0,0,320,200,BACKCOLOR);
+	VWB_DrawPic (0,0,H_TOPWINDOWPIC);
+	VWB_DrawPic (0,8,H_LEFTWINDOWPIC);
+	VWB_DrawPic (312,8,H_RIGHTWINDOWPIC);
+	VWB_DrawPic (8,176,H_BOTTOMINFOPIC);
+
+
+	for (i=0;i<TEXTROWS;i++)
+	{
+		leftmargin[i] = LEFTMARGIN;
+		rightmargin[i] = SCREENPIXWIDTH-RIGHTMARGIN;
+	}
+
+	px = LEFTMARGIN;
+	py = TOPMARGIN;
+	rowon = 0;
+	layoutdone = false;
+
+//
+// make sure we are starting layout text (^P first command)
+//
+	while (*text <= 32)
+		text++;
+
+	if (*text != '^' || toupper(*++text) != 'P')
+		Quit ("PageLayout: Text not headed with ^P");
+
+	while (*text++ != '\n')
+	;
+
+
+//
+// process text stream
+//
+	do
+	{
+		ch = *text;
+
+		if (ch == '^')
+			HandleCommand ();
+		else
+		if (ch == 9)
+		{
+		 px = (px+8)&0xf8;
+		 text++;
+		}
+		else if (ch <= 32)
+			HandleCtrls ();
+		else
+			HandleWord ();
+
+	} while (!layoutdone);
+
+	pagenum++;
+
+	if (shownumber)
+	{
+		#ifdef SPANISH
+		strcpy (str,"Hoja ");
+		itoa (pagenum,str2,10);
+		strcat (str,str2);
+		strcat (str," de ");
+		py = 183;
+		px = 208;
+		#else
+		strcpy (str,"pg ");
+		itoa (pagenum,str2,10);
+		strcat (str,str2);
+		strcat (str," of ");
+		py = 183;
+		px = 213;
+		#endif
+		itoa (numpages,str2,10);
+		strcat (str,str2);
+		fontcolor = 0x4f; 			   //12^BACKCOLOR;
+
+		VWB_DrawPropString (str);
+	}
+
+	fontcolor = oldfontcolor;
+}
+
+//===========================================================================
+
+/*
+=====================
+=
+= BackPage
+=
+= Scans for a previous ^P
+=
+=====================
+*/
+
+void BackPage (void)
+{
+	pagenum--;
+	do
+	{
+		text--;
+		if (*text == '^' && toupper(*(text+1)) == 'P')
+			return;
+	} while (1);
+}
+
+
+//===========================================================================
+
+
+/*
+=====================
+=
+= CacheLayoutGraphics
+=
+= Scans an entire layout file (until a ^E) marking all graphics used, and
+= counting pages, then caches the graphics in
+=
+=====================
+*/
+void CacheLayoutGraphics (void)
+{
+	char	far *bombpoint, far *textstart;
+	char	ch;
+
+	textstart = text;
+	bombpoint = text+30000;
+	numpages = pagenum = 0;
+
+	do
+	{
+		if (*text == '^')
+		{
+			ch = toupper(*++text);
+			if (ch == 'P')		// start of a page
+				numpages++;
+			if (ch == 'E')		// end of file, so load graphics and return
+			{
+				CA_MarkGrChunk(H_TOPWINDOWPIC);
+				CA_MarkGrChunk(H_LEFTWINDOWPIC);
+				CA_MarkGrChunk(H_RIGHTWINDOWPIC);
+				CA_MarkGrChunk(H_BOTTOMINFOPIC);
+				CA_CacheMarks ();
+				text = textstart;
+				return;
+			}
+			if (ch == 'G')		// draw graphic command, so mark graphics
+			{
+				ParsePicCommand ();
+				CA_MarkGrChunk (picnum);
+			}
+			if (ch == 'T')		// timed draw graphic command, so mark graphics
+			{
+				ParseTimedCommand ();
+				CA_MarkGrChunk (picnum);
+			}
+		}
+		else
+			text++;
+
+	} while (text<bombpoint);
+
+	Quit ("CacheLayoutGraphics: No ^E to terminate file!");
+}
+#endif
+
+
+/*
+=====================
+=
+= ShowArticle
+=
+=====================
+*/
+
+#ifdef JAPAN
+void ShowArticle (int which)
+#else
+void ShowArticle (char far *article)
+#endif
+{
+	#ifdef JAPAN
+	int		snames[10] = {	H_HELP1PIC,
+							H_HELP2PIC,
+							H_HELP3PIC,
+							H_HELP4PIC,
+							H_HELP5PIC,
+							H_HELP6PIC,
+							H_HELP7PIC,
+							H_HELP8PIC,
+							H_HELP9PIC,
+							H_HELP10PIC};
+	int		enames[14] = {
+							0,0,
+							#ifndef JAPDEMO
+							C_ENDGAME1APIC,
+							C_ENDGAME1BPIC,
+							C_ENDGAME2APIC,
+							C_ENDGAME2BPIC,
+							C_ENDGAME3APIC,
+							C_ENDGAME3BPIC,
+							C_ENDGAME4APIC,
+							C_ENDGAME4BPIC,
+							C_ENDGAME5APIC,
+							C_ENDGAME5BPIC,
+							C_ENDGAME6APIC,
+							C_ENDGAME6BPIC
+							#endif
+							};
+	#endif
+	unsigned	oldfontnumber;
+	unsigned	temp;
+	boolean 	newpage,firstpage;
+
+	#ifdef JAPAN
+	pagenum = 1;
+	if (!which)
+		numpages = 10;
+	else
+		numpages = 2;
+
+	#else
+
+	text = article;
+	oldfontnumber = fontnumber;
+	fontnumber = 0;
+	CA_MarkGrChunk(STARTFONT);
+	VWB_Bar (0,0,320,200,BACKCOLOR);
+	CacheLayoutGraphics ();
+	#endif
+
+	newpage = true;
+	firstpage = true;
+
+	do
+	{
+		if (newpage)
+		{
+			newpage = false;
+			#ifdef JAPAN
+			if (!which)
+				CA_CacheScreen(snames[pagenum - 1]);
+			else
+				CA_CacheScreen(enames[which*2 + pagenum - 1]);
+			#else
+			PageLayout (true);
+			#endif
+			VW_UpdateScreen ();
+			if (firstpage)
+			{
+				VL_FadeIn(0,255,&gamepal,10);
+				// VW_FadeIn ()
+				firstpage = false;
+			}
+		}
+
+		LastScan = 0;
+		while (!LastScan)
+		;
+
+		switch (LastScan)
+		{
+		case sc_UpArrow:
+		case sc_PgUp:
+		case sc_LeftArrow:
+			if (pagenum>1)
+			{
+				#ifndef JAPAN
+				BackPage ();
+				BackPage ();
+				#else
+				pagenum--;
+				#endif
+				newpage = true;
+			}
+			break;
+
+		case sc_Enter:
+		case sc_DownArrow:
+		case sc_PgDn:
+		case sc_RightArrow:		// the text allready points at next page
+			if (pagenum<numpages)
+			{
+				newpage = true;
+				#ifdef JAPAN
+				pagenum++;
+				#endif
+			}
+			break;
+		}
+
+		#ifndef SPEAR
+		if (Keyboard[sc_Tab] && Keyboard[sc_P] && MS_CheckParm("goobers"))
+			PicturePause();
+		#endif
+
+	} while (LastScan != sc_Escape);
+
+	IN_ClearKeysDown ();
+	fontnumber = oldfontnumber;
+}
+
+
+//===========================================================================
+
+#ifndef JAPAN
+#ifdef ARTSEXTERN
+int 	endextern = T_ENDART1;
+#ifndef SPEAR
+int		helpextern = T_HELPART;
+#endif
+#endif
+char helpfilename[13] = "HELPART.",
+	 endfilename[13] = "ENDART1.";
+#endif
+
+/*
+=================
+=
+= HelpScreens
+=
+=================
+*/
+#ifndef SPEAR
+void HelpScreens (void)
+{
+	int			artnum;
+	char far 	*text;
+	memptr		layout;
+
+
+	CA_UpLevel ();
+	MM_SortMem ();
+#ifdef JAPAN
+	ShowArticle (0);
+	VW_FadeOut();
+	FreeMusic ();
+	CA_DownLevel ();
+	MM_SortMem ();
+#else
+
+
+
+
+#ifdef ARTSEXTERN
+	artnum = helpextern;
+	CA_CacheGrChunk (artnum);
+	text = (char _seg *)grsegs[artnum];
+	MM_SetLock (&grsegs[artnum], true);
+#else
+	CA_LoadFile (helpfilename,&layout);
+	text = (char _seg *)layout;
+	MM_SetLock (&layout, true);
+#endif
+
+	ShowArticle (text);
+
+#ifdef ARTSEXTERN
+	MM_FreePtr (&grsegs[artnum]);
+#else
+	MM_FreePtr (&layout);
+#endif
+
+
+
+	VW_FadeOut();
+
+	FreeMusic ();
+	CA_DownLevel ();
+	MM_SortMem ();
+#endif
+}
+#endif
+
+//
+// END ARTICLES
+//
+void EndText (void)
+{
+	int			artnum;
+	char far 	*text;
+	memptr		layout;
+
+
+	ClearMemory ();
+
+	CA_UpLevel ();
+	MM_SortMem ();
+#ifdef JAPAN
+	ShowArticle(gamestate.episode + 1);
+
+	VW_FadeOut();
+
+	SETFONTCOLOR(0,15);
+	IN_ClearKeysDown();
+	if (MousePresent)
+		Mouse(MDelta);	// Clear accumulated mouse movement
+
+	FreeMusic ();
+	CA_DownLevel ();
+	MM_SortMem ();
+#else
+
+
+
+#ifdef ARTSEXTERN
+	artnum = endextern+gamestate.episode;
+	CA_CacheGrChunk (artnum);
+	text = (char _seg *)grsegs[artnum];
+	MM_SetLock (&grsegs[artnum], true);
+#else
+	endfilename[6] = '1'+gamestate.episode;
+	CA_LoadFile (endfilename,&layout);
+	text = (char _seg *)layout;
+	MM_SetLock (&layout, true);
+#endif
+
+	ShowArticle (text);
+
+#ifdef ARTSEXTERN
+	MM_FreePtr (&grsegs[artnum]);
+#else
+	MM_FreePtr (&layout);
+#endif
+
+
+	VW_FadeOut();
+	SETFONTCOLOR(0,15);
+	IN_ClearKeysDown();
+	if (MousePresent)
+		Mouse(MDelta);	// Clear accumulated mouse movement
+
+	FreeMusic ();
+	CA_DownLevel ();
+	MM_SortMem ();
+#endif
+}