shithub: wl3d

Download patch

ref: 0c46469f2135819a33663e80657a7c4c521dd66e
parent: ddcaa37baf64e0fc44f8ebf051005a0b0a1fd29b
author: Konstantinn Bonnet <qu7uux@gmail.com>
date: Sat Jan 28 00:13:19 EST 2017

playable game, most relevant seqs, usable menus, fixes

- fs: fix setting sfxs .pcm for missing pcms, again
- drw: fix wrong fizzout offsets
- gm: disallow picking up items after death
- gm: fix reverted mouse buttons 2 and 3
- hub: rework implementation to allow all the crazy permutations
- hub: (refbug) actual fizzlein when starting regular game
- map: fix disabled guy spawns with GDbaby
- map: fix opool size not taking in account the two dummy nodes
- rend: fix tiles .vis overflow
- rend: scaltab: just generate a scaler for every height, save doesn't do
  anything here
- snd: handle disabled sound
- snd: move one-time opl2 settings to initsnd
- snd: allow playing pcm when al sfx are disabled
- wl3d: remove -x and make difc param to -w mandatory
- wl3d: ignore first mouse event after grab to prevent crazy deltas
- sod/sdm: already in game message done in ctl rather than in difc; this is
  arguably better (simpler, no flicker)
- notes: map load screen is purely cosmetic

--- a/dat.h
+++ b/dat.h
@@ -15,6 +15,7 @@
 typedef struct Static Static;
 typedef struct Tile Tile;
 typedef struct Game Game;
+typedef struct Score Score;
 
 enum{
 	WL6,
@@ -43,8 +44,9 @@
 	Ke
 };
 extern int msense;
-extern int kbon, mson;
-extern int kb, mΔx, mΔy, mΔb;
+extern int vwsize;
+extern int kbon;
+extern int kb, mΔx, mΔy, mb;
 extern int sfxon, muson, pcmon;
 extern int sfxlck;
 extern Rune keys[];
@@ -51,7 +53,8 @@
 extern void (*step)(void);
 extern int Δtc;
 extern int nosleep;
-extern int mtc;
+extern int autorun;
+extern int qtc;
 
 enum{
 	Vw = 320,
@@ -64,6 +67,8 @@
 };
 extern uchar *px, pxb[], fzb[];
 extern int npx, scale;
+extern void (*mclear)(void);
+extern void (*stripe)(int);
 
 enum{
 	C0,
@@ -226,12 +231,16 @@
 	Sangeltired = Sotto,
 	Ssend = Sfett,
 
+	Msdwon = 6,
+	Mend = 7,
 	Mintro = 7,
 	Mmenu = 14,
+	Minter = 16,
 	Mnazjazz = 18,
 	Maward = 20,
 	Mroster = 23,
 	Mtower = 23,
+	Mwon = 24,
 
 	Pbackdrop = 0,
 	Pmouselback,
@@ -597,7 +606,6 @@
 extern s32int sint[], *cost;
 
 struct View{
-	int size;
 	int dx;
 	int dy;
 	int ofs;
@@ -1224,15 +1232,21 @@
 	WPmg,
 	WPgatling,
 
+	GMup = 0,
+	GMsetec,
+	GMret,
+
 	EDfizz = 1,
+	EDcam,
+	EDcam2,
 	EDdem,
 	EDkey,
 	EDdie,
-	EDcam,
-	EDcam2,
 	EDup,
 	EDsetec,
-	EDwon
+	EDwon,
+	EDspear,
+	EDmsg
 };
 struct Game{
 	int hp;
@@ -1243,24 +1257,39 @@
 	int lives;
 	int keys;
 	int pt;
+	int oldpt;
 	int to1up;
 	int wfrm;
 	int facefrm;
 	int map;
 	int difc;
-	int lvltc;
-	int kills;
-	int nkills;
-	int secret;
-	int nsecret;
-	int treasure;
-	int ntreasure;
+	int tc;
+	int eptm;
+	int kp;
+	int ktot;
+	int epk;
+	int sp;
+	int stot;
+	int eps;
+	int tp;
+	int ttot;
+	int ept;
 	int won;
 	int mut;
 	int end;
+	int com;
 	int demo;
 	int record;
 	int load;
 };
 extern Game gm;
-extern int god, noclip, onestep;
+extern int allrecv, god, noclip, slomo;
+
+struct Score{
+	char name[58];
+	int n;
+	int lvl;
+	int ep;
+};
+extern Score scs[];
+extern char savs[][32];
--- a/debug.c
+++ b/debug.c
@@ -10,187 +10,19 @@
 	int esc;
 	s16int level,i;
 
-	if (Keyboard[sc_B])		// B = border color
+	if (Keyboard[sc_O])
 	{
-		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 (Keyboard[sc_E])		// E = quit level
-	{
-		if (tedlevel)
-			Quit (NULL);
-		gm.φ = ex_completed;
-//		gamestate.mapon++;
-	}
-
-	if (Keyboard[sc_F])		// F = facing spot
-	{
-		CenterWindow (14,4);
-		US_Print ("X:");
-		US_PrintUnsigned (player->x);
-		US_Print ("\nY:");
-		US_PrintUnsigned (player->y);
-		US_Print ("\nA:");
-		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
-	{
-		IN_ClearKeysDown ();
-		hurt (16,NULL);
-	}
-	else if (Keyboard[sc_I])			// I = item cheat
-	{
-		CenterWindow (12,3);
-		US_PrintCentered ("Free items!");
-		VW_UpdateScreen();
-		givep (100000);
-		HealSelf (99);
-		if (gm.bestw<WPgatling)
-			givew (gm.bestw+1);
-		gamestate.ammo += 50;
-		if (gamestate.ammo > 99)
-			gamestate.ammo = 99;
-		huda ();
-		IN_Ack ();
-		return 1;
-	}
-#ifdef SPEAR
-	else if (Keyboard[sc_N])			// N = no clip
-	{
-		noclip^=1;
-		CenterWindow (18,3);
-		if (noclip)
-			US_PrintCentered ("No clipping ON");
-		else
-			US_PrintCentered ("No clipping OFF");
-		VW_UpdateScreen();
-		IN_Ack ();
-		return 1;
-	}
-#endif
-#if 0
-	else if (Keyboard[sc_O])			// O = overhead
-	{
 		ViewMap();
 		return 1;
 	}
-#endif
-	else if (Keyboard[sc_Q])			// Q = fast quit
-		Quit (NULL);
-	else if (Keyboard[sc_S])			// S = slow motion
-	{
-		onestep^=1;
-		CenterWindow (18,3);
-		if (onestep)
-			US_PrintCentered ("Slow motion ON");
-		else
-			US_PrintCentered ("Slow motion OFF");
-		VW_UpdateScreen();
-		IN_Ack ();
-		return 1;
-	}
-	else if (Keyboard[sc_T])			// 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;
-#ifndef SPEAR
-		US_Print("  Warp to which level(1-10):");
-#else
-		US_Print("  Warp to which level(1-21):");
-#endif
-		VW_UpdateScreen();
-		esc = !US_LineInput (px,py,str,NULL,true,2,0);
-		if (!esc)
-		{
-			level = atoi (str);
-#ifndef SPEAR
-			if (level>0 && level<11)
-#else
-			if (level>0 && level<22)
-#endif
-			{
-				gamestate.mapon = level-1;
-				gm.φ = ex_warped;
-			}
-		}
-		return 1;
-	}
-	else if (Keyboard[sc_X])			// X = item cheat
-	{
-		CenterWindow (12,3);
-		US_PrintCentered ("Extra stuff!");
-		VW_UpdateScreen();
-		// DEBUG: put stuff here
-		IN_Ack ();
-		return 1;
-	}
-
 	return 0;
 }
 
-
-#if 0
-/*
-===================
-=
-= OverheadRefresh
-=
-===================
-*/
-
 void OverheadRefresh (void)
 {
 	u16int	x,y,endx,endy,sx,sy;
 	u16int	tile;
 
-
 	endx = maporgx+VIEWTILEX;
 	endy = maporgy+VIEWTILEY;
 
@@ -232,17 +64,7 @@
 		}
 
 }
-#endif
 
-#if 0
-/*
-===================
-=
-= ViewMap
-=
-===================
-*/
-
 void ViewMap (void)
 {
 	int		button0held;
@@ -276,7 +98,6 @@
 			maporgy--;
 		if (controly > 0 && maporgy<mapheight-VIEWTILEY)
 			maporgy++;
-
 #if 0
 		if (c.button0 && !button0held)
 		{
@@ -295,5 +116,3 @@
 
 	IN_ClearKeysDown ();
 }
-#endif
-
--- a/drw.c
+++ b/drw.c
@@ -9,13 +9,14 @@
 Spr *sprs;
 
 int scale, npx;
-uchar *px, pxb[Va], fzb[Vw*Vhud];
+uchar *px, pxb[Va], *fzd, fzb[Vw*Vhud];
 View vw;
+void (*mclear)(void);
+void (*stripe)(int);
 
 static Col *fcol;
 static u32int *fref;
 static int fi, fo, fdt;
-static uchar *fzd;
 static int fzc, fzdx, fzdy, fzdn, fzn, fzout;
 
 static uchar wl6ceil[] = {
@@ -53,6 +54,76 @@
 }
 
 void
+out(void)
+{
+	int n;
+	u32int c;
+	uchar *s, *d, *w;
+
+	d = px;
+	s = pxb;
+	n = scale * 3;
+	while(s < pxb + sizeof pxb){
+		c = pal[*s++];
+		w = d + n;
+		while(d < w){
+			*d++ = c;
+			*d++ = c>>8;
+			*d++ = c>>16;
+		}
+	}
+	flush();
+}
+
+void
+put(int x, int y, int dx, int dy, int c)
+{
+	uchar *d;
+
+	d = pxb + x + y*Vw;
+	while(dy-- > 0){
+		memset(d, c, dx);
+		d += Vw;
+	}	
+}
+
+void
+pput(int x, int y, int dx, int dy, uchar *s)
+{
+	uchar *d;
+
+	d = pxb + x + y*Vw;
+	while(dy-- > 0){
+		memcpy(d, s, dx);
+		s += dx;
+		d += Vw;
+	}	
+}
+
+void
+fill(int c)
+{
+	memset(pxb, c, sizeof pxb);
+}
+
+void
+clear(void)
+{
+	int n;
+	uchar c, *p;
+
+	c = ver < SDM ? wl6ceil[gm.map] : sodceil[gm.map];
+	p = pxb + vw.ofs;
+	n = 0;
+	while(n++ < vw.dy){
+		memset(p, c, vw.dx);
+		p += Vw;
+		if(n == vw.dy/2)
+			c = 0x19;
+	}
+}
+
+void
 fizz(void)
 {
 	int i, x, y, ofs;
@@ -78,16 +149,18 @@
 {
 	if(save)
 		memcpy(fzb, pxb + vw.ofs, (vw.dy-1) * Vw + vw.dx-1);
+	fzdx = vw.dx;
+	fzdy = vw.dy;
+	fzd = pxb + vw.ofs;
 	if(c < 0){
-		fzd = pxb + vw.ofs;
-		fzdx = vw.dx;
-		fzdy = vw.dy;
 		fzdn = Va / 20;
 		fzout = 0;
 	}else{
-		fzd = pxb;
-		fzdx = Vw;
-		fzdy = Vhud;
+		if(gm.won){
+			fzd = pxb;
+			fzdx = Vw;
+			fzdy = Vhud;
+		}
 		fzdn = Va / 70;
 		fzout = 1;
 		fzc = c;
@@ -157,64 +230,30 @@
 }
 
 void
-palpic(uchar *s)
+palfill(Col *c)
 {
-	u32int *p;
+	u32int v, *p;
 
-	p = pal = pals[Csod];
-	while(p < pals[Csod] + nelem(pals[0])){
-		*p++ = s[0]*255/63<<16 | s[1]*255/63<<8 | s[2]*255/63;
-		s += 3;
-	}
+	p = pals[Csod];
+	v = c->r << 16 | c->g << 8 | c->b;
+	while(p < pals[Cend])
+		*p++ =  v;
+	pal = pals[Csod];
 }
 
 void
-out(void)
+palpic(uchar *s)
 {
-	int n;
-	u32int c;
-	uchar *s, *d, *w;
+	u32int *p;
 
-	d = px;
-	s = pxb;
-	n = scale * 3;
-	while(s < pxb + sizeof pxb){
-		c = pal[*s++];
-		w = d + n;
-		while(d < w){
-			*d++ = c;
-			*d++ = c>>8;
-			*d++ = c>>16;
-		}
+	p = pals[Csod];
+	while(p < pals[Csod] + nelem(pals[0])){
+		*p++ = s[0]*255/63 << 16 | s[1]*255/63 << 8 | s[2]*255/63;
+		s += 3;
 	}
-	flush();
+	pal = pals[Csod];
 }
 
-void
-pput(int x, int y, int dx, int dy, uchar *s)
-{
-	uchar *d;
-
-	d = pxb + x + y*Vw;
-	while(dy-- > 0){
-		memcpy(d, s, dx);
-		s += dx;
-		d += Vw;
-	}	
-}
-
-void
-put(int x, int y, int dx, int dy, int c)
-{
-	uchar *d;
-
-	d = pxb + x + y*Vw;
-	while(dy-- > 0){
-		memset(d, c, dx);
-		d += Vw;
-	}	
-}
-
 int
 txt(int x, int y, char *t, int col)
 {
@@ -299,9 +338,12 @@
 }
 
 void
-fill(int c)
+txtcen(int y, char *t, int c)
 {
-	memset(pxb, c, sizeof pxb);
+	int n;
+
+	n = txtw(t);
+	txt((Vw - n) / 2, y, t, c);
 }
 
 void
@@ -340,12 +382,65 @@
 }
 
 void
+wlmclear(void)
+{
+	put(0, 0, Vw, Vh, 0x29);
+}
+void
+sdmclear(void)
+{
+	pic(0, 0, pict[Pbackdrop]);
+}
+
+void
+wlstripe(int y)
+{
+	put(0, y, Vw, 24, 0);
+	put(0, y+22, 320, 1, 0x2c);
+}
+void
+sdstripe(int y)
+{
+	put(0, y, Vw, 22, 0);
+	put(0, y+23, 320, 1, 0);
+}
+
+void
+outbox(int x, int y, int dx, int dy, int c1, int c2)
+{
+	put(x, y, dx, 1, c2);
+	put(x, y+1, 1, dy-1, c2);
+	put(x, y+dy, dx+1, 1, c1);
+	put(x+dx, y, 1, dy, c1);
+}
+
+void
+box(int x, int y, int dx, int dy, int col, int out, int out2)
+{
+	put(x+1, y+1, dx-1, dy-1, col);
+	outbox(x, y, dx, dy, out, out2);
+}
+
+void
+viewbox(void)
+{
+	int x, y;
+
+	x = Vhud - vw.dx / 2 - 1;
+	y = (Vhud - vw.dy) / 2 - 1;
+	put(0, 0, 320, Vhud, 0x7f);
+	box(x, y, vw.dx+1, vw.dy+1, 0, 0x7d, 0);
+	put(x, y+vw.dy+1, 1, 1, 0x7c);
+}
+
+void
 hudf(void)
 {
 	int p;
 
 	if(gm.hp > 0){
-		p = god ? pict[Pgod] : pict[Pface1] + 3 * (100 - gm.hp >> 4);
+		p = ver >= SDM && god ? pict[Pgod]
+			: pict[Pface1] + 3 * (100 - gm.hp >> 4);
 		p += gm.facefrm;
 	}else
 		p = gm.mut ? pict[Pmut] : pict[Pface8];
@@ -367,7 +462,10 @@
 void
 hudm(void)
 {
-	hudnp(16, 176, 2, ver == SOD && gm.map == 20 ? 18 : gm.map+1);
+	int n;
+
+	n = ver == SOD ? (gm.map == 20 ? 18 : gm.map + 1) : gm.map % 10 + 1;
+	hudnp(16, 176, 2, n);
 }
 
 void
@@ -396,18 +494,16 @@
 }
 
 void
-clear(void)
+view(void)
 {
-	int n;
-	uchar c, *p;
-
-	c = ver < SDM ? wl6ceil[gm.map] : sodceil[gm.map];
-	p = pxb + vw.ofs;
-	n = 0;
-	while(n++ < vw.dy){
-		memset(p, c, vw.dx);
-		p += Vw;
-		if(n == vw.dy/2)
-			c = 0x19;
-	}
+	viewbox();
+	pic(0, Vhud, pict[Pstat]);
+	hudf();
+	hudh();
+	hudl();
+	hudm();
+	huda();
+	hudk();
+	hudw();
+	hudp();
 }
--- a/fns.h
+++ b/fns.h
@@ -1,5 +1,4 @@
 void*	emalloc(ulong);
-void*	erealloc(void*, ulong);
 void	grab(int);
 void	toss(void);
 void	flush(void);
@@ -6,22 +5,32 @@
 char*	demof(char*);
 u16int*	readmap(int);
 void	dat(char*);
+void	out(void);
+void	put(int, int, int, int, int);
+void	pput(int, int, int, int, uchar*);
+void	fill(int);
+void	clear(void);
 void	fizz(void);
 void	fizzop(int, int);
 void	fadeout(void);
 void	fadein(void);
 void	fadeop(Col*, int);
+void	palfill(Col*);
 void	palpic(uchar*);
-void	out(void);
-void	pput(int, int, int, int, uchar*);
-void	put(int, int, int, int, int);
 int	txt(int, int, char*, int);
 int	txtnl(int, int, char*, int);
 int	txth(char*);
 int	txtw(char*);
-void	fill(int);
+void	txtcen(int, char*, int);
 void	pic(int, int, int);
 void	pictxt(int, int, char*);
+void	wlmclear(void);
+void	sdmclear(void);
+void	wlstripe(int);
+void	sdstripe(int);
+void	outbox(int, int, int, int, int, int);
+void	box(int, int, int, int, int, int, int);
+void	viewbox(void);
 void	hudf(void);
 void	hudh(void);
 void	hudl(void);
@@ -30,16 +39,17 @@
 void	hudk(void);
 void	hudw(void);
 void	hudp(void);
-void	clear(void);
+void	view(void);
 void	scalspr(int, int, int);
 s32int	ffs(s32int, s32int);
 void	render(void);
-void	initscal(void);
-void	setvw(int);
+void	setvw(void);
 void	tab(void);
+int	quickkey(Rune);
 void	gend(void);
-void	mstep(void);
-void	init(char*);
+void	eatcs(void);
+void	qstep(void);
+void	init(char*, int, int);
 void	drop(Tile*, int);
 void	dropen(Door*);
 void	druse(Door*);
@@ -51,12 +61,21 @@
 void	mapmus(void);
 void	initmap(void);
 void	sodmap(void);
+void	dieturn(void);
+void	die(void);
 void	camwarp(void);
+void	givew(int);
+void	givep(int);
 void	bonus(Static*);
+void	crm114(int);
+void	eatcs(void);
 int	rnd(void);
 void	gstep(void);
-void	demo(void);
-void	initg(int, uchar*);
+void	nextmap(void);
+void	game(void);
+void	spshunt(void);
+void	greset(void);
+void	ginit(uchar*, int, int);
 uchar*	opl2out(uchar*, int);
 void	opl2wr(int, int);
 void	opl2init(int);
--- a/fs.c
+++ b/fs.c
@@ -805,7 +805,7 @@
 	p = pcmt[ver<SDM ? 0 : 1];
 	e = p + npcm;
 	for(pcm=pcms; p<e; p++, pcm++)
-		if(*p != Send)
+		if(*p != Send && pcm->p != nil)
 			sfxs[*p].pcm = pcm;
 	sfxs[Sscream3].pcm = sfxs[ver<SDM ? Sscream2 : Sscream4].pcm;	/* why */
 }
@@ -999,12 +999,6 @@
 }
 
 static void
-cfg(void)
-{
-	msense = 5;
-}
-
-static void
 loadscr(void)
 {
 	Biobuf *bf;
@@ -1089,8 +1083,7 @@
 
 	rfork(RFNAMEG);
 	if(bind(".", dir, MBEFORE|MCREATE) < 0 || chdir(dir) < 0)
-		fprint(2, "dat: %r\n");
-
+		sysfatal("dat: %r\n");
 	e = ext;
 	loadscr();
 	ext = e;
@@ -1106,5 +1099,4 @@
 		sodmap();
 	}
 	ext = e;
-	cfg();
 }
--- a/game.c
+++ b/game.c
@@ -1,72 +1,6 @@
 int		ingame,fizzlein;
 u16int	latchpics[NUMLATCHPICS];
 
-s32int		spearx,speary;
-u16int	spearangle;
-int		spearflag;
-
-//
-// ELEVATOR BACK MAPS - REMEMBER (-1)!!
-//
-s16int ElevatorBackTo[]={1,1,7,3,5,3};
-
-void DrawPlayBorderSides (void)
-{
-	s16int	xl,yl;
-
-	xl = 160-vw.dx/2;
-	yl = (200-STATUSLINES-vw.dy)/2;
-
-	VWB_Bar (0,0,xl-1,200-STATUSLINES,127);
-	VWB_Bar (xl+vw.dx+1,0,xl-2,200-STATUSLINES,127);
-
-	VWB_Vlin (yl-1,yl+vw.dy,xl-1,0);
-	VWB_Vlin (yl-1,yl+vw.dy,xl+vw.dx,125);
-}
-
-void DrawAllPlayBorderSides (void)
-{
-	u16int	i,temp;
-
-	temp = bufferofs;
-	for (i=0;i<3;i++)
-	{
-		bufferofs = screenloc[i];
-		DrawPlayBorderSides ();
-	}
-	bufferofs = temp;
-}
-
-void DrawAllPlayBorder (void)
-{
-	u16int	i,temp;
-
-	temp = bufferofs;
-	for (i=0;i<3;i++)
-	{
-		bufferofs = screenloc[i];
-		DrawPlayBorder ();
-	}
-	bufferofs = temp;
-}
-
-void DrawPlayBorder (void)
-{
-	s16int	xl,yl;
-
-	VWB_Bar (0,0,320,200-STATUSLINES,127);
-
-	xl = 160-vw.dx/2;
-	yl = (200-STATUSLINES-vw.dy)/2;
-	VWB_Bar (xl,yl,vw.dx,vw.dy,0);
-
-	VWB_Hlin (xl-1,xl+vw.dx,yl-1,0);
-	VWB_Hlin (xl-1,xl+vw.dx,yl+vw.dy,125);
-	VWB_Vlin (yl-1,yl+vw.dy,xl-1,0);
-	VWB_Vlin (yl-1,yl+vw.dy,xl+vw.dx,125);
-	VWB_Plot (xl-1,yl+vw.dy,124);
-}
-
 #define MAXDEMOSIZE	8192
 
 void StartDemoRecord (s16int levelnumber)
@@ -108,8 +42,6 @@
 			CA_WriteFile (demoname,(void far *)demobuffer,length);
 		}
 	}
-
-
 	MM_FreePtr (&demobuffer);
 }
 
@@ -167,119 +99,6 @@
 
 #define DEATHROTATE 2
 
-void Died (void)
-{
-	float	fangle;
-	s32int	dx,dy;
-	s16int		iangle,curangle,clockwise,counter,change;
-
-	gamestate.weapon = -1;			// take away weapon
-	sfx (Sdie);
-//
-// swing around to face attacker
-//
-	dx = killer->x - player->x;
-	dy = player->y - killer->y;
-
-	fangle = atan2(dy,dx);			// returns -pi to pi
-	if (fangle<0)
-		fangle = Fpi*2+fangle;
-
-	iangle = fangle/(Fpi*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;
-
-			render ();
-			ttic ();
-		} 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;
-
-			render ();
-			ttic ();
-		} while (curangle != iangle);
-	}
-
-//
-// fade to red
-//
-	pal = pals[C0];
-
-	bufferofs += screenofs;
-	VW_Bar (0,0,vw.dx,vw.dy,4);
-	IN_ClearKeysDown ();
-	FizzleFade(bufferofs,displayofs+screenofs,vw.dx,vw.dy,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)
-	{
-		gm.hp = 100;
-		gm.w = gm.bestw = gm.lastw = WPpistol;
-		gm.ammo = STARTAMMO;
-		gm.keys = 0;
-		atkfrm = 0;
-		atktc = 0;
-		gm.wfrm = 0;
-		hudk();
-		hudw();
-		huda();
-		hudh();
-		hudf();
-		hudl();
-	}
-
-}
-
 void GameLoop (void)
 {
 	s16int i,xl,yl,xh,yh;
@@ -303,14 +122,6 @@
 		else
 			initmap ();
 
-#ifdef SPEAR
-		if (gamestate.mapon == 20)	// give them the key allways
-		{
-			gamestate.keys |= 1;
-			hudk ();
-		}
-#endif
-
 		ingame = true;
 		mapmus ();
 		PM_CheckMainMem ();
@@ -325,38 +136,6 @@
 startplayloop:
 		PlayLoop ();
 
-#ifdef SPEAR
-		if (spearflag)
-		{
-			SD_StopSound();
-			sfx(Sspear);
-			if (DigiMode != sds_Off)
-			{
-				s32int lasttimecount = TimeCount;
-
-				while(TimeCount < lasttimecount+150)
-				//while(DigiPlaying!=false)
-					SD_Poll();
-			}
-			else
-				SD_WaitSoundDone();
-
-			gamestate.oldscore = gm.pt;
-			gamestate.mapon = 20;
-			initmap ();
-			mapmus ();
-			PM_CheckMainMem ();
-			oplr->x = spearx;
-			oplr->y = speary;
-			oplr->tx = spearx >> Dtlshift;
-			oplr->ty = speary >> Dtlshift;
-			oplr->θ2 = spearangle;
-			oplr->areaid = oplr->tl->p0 - MTfloor;
-			spearflag = false;
-			goto startplayloop;
-		}
-#endif
-
 		stopmus ();
 		ingame = false;
 
@@ -370,112 +149,10 @@
 		{
 		case ex_completed:
 		case ex_secretlevel:
-			gamestate.keys = 0;
-			hudk ();
-			VW_FadeOut ();
-
-
-			LevelCompleted ();		// do the intermission
-#ifdef SPEARDEMO
-			if (gamestate.mapon == 1)
-			{
-				died = true;			// don't "get psyched!"
-
-				VW_FadeOut ();
-
-				CheckHighScore (gm.pt,gamestate.mapon+1);
-
-				#pragma warn -sus
-				_fstrcpy(MainMenu[viewscores].string,"View Scores");
-				MainMenu[viewscores].routine = CP_ViewScores;
-				#pragma warn +sus
-
-				return;
-			}
-#endif
-
-			gamestate.oldscore = gm.pt;
-
-#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 (gm.φ == ex_secretlevel)
-				gamestate.mapon = 9;
-#else
-
-#define FROMSECRET1		3
-#define FROMSECRET2		11
-
-			//
-			// GOING TO SECRET LEVEL
-			//
-			if (gm.φ == 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 ();
-
-			CheckHighScore (gm.pt,gamestate.mapon+1);
-
-			#pragma warn -sus
-			_fstrcpy(MainMenu[viewscores].string,"View Scores");
-			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
-			Victory ();
-
-			CheckHighScore (gm.pt,gamestate.mapon+1);
-
-			#pragma warn -sus
-			_fstrcpy(MainMenu[viewscores].string,"View Scores");
-			MainMenu[viewscores].routine = CP_ViewScores;
-			#pragma warn +sus
-
 			return;
 		}
 	} while (1);
--- a/gm.c
+++ b/gm.c
@@ -11,33 +11,33 @@
 extern QLock inlck;
 
 Rune keys[Ke] = {
-	[K↑] Kup,
-	[K↓] Kdown,
-	[K←] Kleft,
-	[K→] Kright,
-	[Krun] Kshift,
 	[Kfire] Kctl,
-	[Kopen] ' ',
 	[Kstrafe] Kalt,
+	[Krun] Kshift,
+	[Kopen] ' ',
 	[Kknife] '1',
 	[Kpistol] '2',
 	[Kmg] '3',
 	[Kgatling] '4',
+	[K↑] Kup,
+	[K↓] Kdown,
+	[K←] Kleft,
+	[K→] Kright,
 	[Kmenu] Kesc
 };
 Game gm;
 int msense;
-int god, noclip, onestep;
+int allrecv, god, noclip, slomo;
 
+typedef struct Crm Crm;
 enum{
 	Ncrm = 7
 };
-
-typedef struct Crm Crm;
 struct Crm{
 	char s[Ncrm];
 	void (*f)(void);
 };
+static char crs[Ncrm];
 
 static int rndi, rndt[] = {
   0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66, 74, 21,
@@ -60,14 +60,13 @@
 static int demfrm;
 static int kon, kold, kΔx, kΔy;
 static s16int kΔθ;
-static int allrecv, firing, noise;
+static int firing, noise, dirty;
 static char *dem, *deme;
-static Crm crms[];
-static char crs[Ncrm];
 static int dmgtc, bonustc, facetc, funtc;
 static Obj *camobj;
-static int bosskillx, bosskilly;
-static int gotspear, spearx, speary, spearθ;
+static int killx, killy;
+static int dieΔθ, diedir;
+static int spearx, speary, spearθ;
 static int atk[][4] = {
 	{0, 2, 0, -1},
 	{0, 1, 0, -1},
@@ -91,15 +90,6 @@
 }
 
 static void
-givew(int n)
-{
-	givea(6);
-	if(gm.bestw < n)
-		gm.w = gm.lastw = gm.bestw = n;
-	hudw();
-}
-
-static void
 givek(int n)
 {
 	gm.keys |= 1 << n;
@@ -116,17 +106,6 @@
 }
 
 static void
-givep(int n)
-{
-	gm.pt += n;
-	while(gm.pt >= gm.to1up){
-		gm.to1up += 40000;
-		givel();
-	}
-	hudp();
-}
-
-static void
 giveh(int n)
 {
 	gm.hp += n;
@@ -287,10 +266,11 @@
 static void
 yelp(Obj *o)
 {
-	int n, s[] = {
+	static int s[] = {
 		Sscream1, Sscream2, Sscream3, Sscream4,
 		Sscream5, Sscream7, Sscream8, Sscream9
 	};
+	int n;
 
 	n = gm.map;
 	if((ver == WL6 && n % 10 == 9 || ver == SOD && (n == 18 || n == 19)) && !rnd())
@@ -303,7 +283,6 @@
 			sfxatt(Sscream6, 1, o->x, o->y);
 			return;
 		}
-
 	switch(o->type){
 	case Omut: sfxatt(Smutdie, 1, o->x, o->y); break;
 	case Ogd: sfxatt(s[rnd()%(ver==WL1?2:8)], 1, o->x, o->y); break;
@@ -368,8 +347,8 @@
 		break;
 	case Oschb:
 		givep(5000);
-		bosskillx = oplr->x;
-		bosskilly = oplr->y;
+		killx = oplr->x;
+		killy = oplr->y;
 		ostate(o, stt+GSschbdie1);
 		yelp(o);
 		break;
@@ -380,14 +359,14 @@
 		break;
 	case Ootto:
 		givep(5000);
-		bosskillx = oplr->x;
-		bosskilly = oplr->y;
+		killx = oplr->x;
+		killy = oplr->y;
 		ostate(o, stt+GSottodie1);
 		break;
 	case Ofett:
 		givep(5000);
-		bosskillx = oplr->x;
-		bosskilly = oplr->y;
+		killx = oplr->x;
+		killy = oplr->y;
 		ostate(o, stt+GSfettdie1);
 		break;
 	case Ofake:
@@ -400,8 +379,8 @@
 		break;
 	case Ohitler:
 		givep(5000);
-		bosskillx = oplr->x;
-		bosskilly = oplr->y;
+		killx = oplr->x;
+		killy = oplr->y;
 		ostate(o, stt+GShitlerdie1);
 		yelp(o);
 		break;
@@ -434,7 +413,7 @@
 		drop(tl, Rkey1);
 		break;
 	}
-	gm.kills++;
+	gm.kp++;
 	o->f &= ~OFshootable;
 	o->f |= OFnomark;
 	tl->o = nil;
@@ -472,8 +451,9 @@
 		gm.hp -= n;
 	if(gm.hp <= 0){
 		gm.hp = 0;
-		gm.end = EDdie;
-		camobj = from;
+		gm.end = gm.demo || gm.record ? EDdem : EDdie;
+		killx = from->x;
+		killy = from->y;
 		if(ver >= SDM && from->type == Oneedle)
 			gm.mut++;
 	}
@@ -527,7 +507,6 @@
 	py = oplr->y >> 8;
 	ptx = oplr->tx;
 	pty = oplr->ty;
-
 	if(ptx != otx){
 		if(ptx > otx){
 			δ = 256 - (ox & 0xff);
@@ -541,7 +520,6 @@
 			dy = 0x7fff;
 		else if(dy < -0x7fff)
 			dy = -0x7fff;
-
 		y = oy + (dy * δ >> 8);
 		x = otx + dx;
 		ptx += dx;
@@ -568,7 +546,6 @@
 			dx = 0x7fff;
 		else if(dx < -0x7fff)
 			dx = -0x7fff;
-
 		x = ox + (dx * δ >> 8);
 		y = oty + dy;
 		pty += dy;
@@ -1311,9 +1288,8 @@
 		gm.end = EDcam2;
 		return;
 	}
-	gm.won++;
 	gm.end = EDcam;
-	mtc = 0;
+	qtc = 0;
 	dofizz++;
 	camobj = o;
 }
@@ -1374,7 +1350,6 @@
 static void
 kmove(void)
 {
-	int θ;
 	s16int u;
 
 	oplr->v = 0;
@@ -1387,12 +1362,7 @@
 		kΔθ += kΔx;
 		u = kΔθ / 20;
 		kΔθ -= u * 20;
-		θ = oplr->θ - u;
-		if(θ >= 360)
-			θ -= 360;
-		if(θ < 0)
-			θ += 360;
-		oplr->θ = θ;
+		oplr->θ = (oplr->θ - u + 360) % 360;
 	}
 	if(kΔy < 0)
 		thrust(oplr->θ, -kΔy * 150);
@@ -1420,7 +1390,7 @@
 	}
 	c->to = tl->tl;
 	c->tl = tl->tl;
-	gm.secret++;
+	gm.sp++;
 	pusher.tl = tl;
 	pusher.isvert = θ;
 	pusher.φ = 1;
@@ -1463,6 +1433,7 @@
 			kold |= 1<<Kopen;
 			tl->tl++;	/* flip switch */
 			gm.end = oplr->tl->p0 == MTsetec ? EDsetec : EDup;
+			stopmus();
 			sfx(Slvlend);
 			sfxlck++;
 			return;
@@ -1690,163 +1661,6 @@
 }
 
 static void
-crmwapr(void)
-{
-	allrecv++;
-}
-static void
-crmamo(void)
-{
-	gm.ammo = 99;
-	huda();
-}
-static void
-crmkey(void)
-{
-	gm.keys = 15;
-	hudk();
-}
-static void
-crmwep(void)
-{
-	if(gm.bestw < WPgatling)
-		givew(gm.bestw + 1);
-}
-static void
-crmmli(void)
-{
-	gm.hp = 100;
-	gm.ammo = 99;
-	gm.keys = 15;
-	givew(WPgatling);
-	gm.pt = 0;
-	gm.lvltc += 42000;
-	hudw();
-	hudh();
-	hudk();
-	huda();
-	hudp();
-}
-static void
-crmmap(void)
-{
-}
-static void
-crmgod(void)
-{
-	if(ver < SDM)
-		return;
-	god ^= 1;
-	if(god)
-		sfx(Sendb2);
-	else
-		sfx(Snobonus);
-}
-static void
-crmclp(void)
-{
-	if(ver < SDM)
-		return;
-	noclip ^= 1;
-}
-static void
-crmslo(void)
-{
-	onestep ^= 1;
-}
-static void
-crmskp(void)
-{
-}
-static void
-crmwrp(void)
-{
-}
-
-static Crm crms[] = {
-	{"fgd135", crmwapr},
-	{"opepak", crmamo},
-	{"opeopn", crmkey},
-	{"opephz", crmwep},
-	{"opemli", crmmli},
-	{"opepda", crmmap},
-	{"opedqd", crmgod},
-	{"opeclp", crmclp},
-	{"opeslo", crmslo},
-	{"opeskp", crmskp},
-	{"opewrp", crmwrp}
-};
-
-static char *
-crm114(void)
-{
-	int n;
-	Crm *p, *e;
-
-	n = strlen(crs);
-	p = crms;
-	e = crms + 1;
-	if(allrecv){
-		p++;
-		e = crms + nelem(crms);
-	}
-	while(p < e){
-		if(strncmp(crs, p->s, n) != 0){
-			memset(crs, 0, sizeof crs);
-			return crs;
-		}else if(n = Ncrm-1){
-			p->f();
-			memset(crs, 0, sizeof crs);
-			return crs;
-		}
-		p++;
-	}
-	return crs+n;
-}
-static int
-quickkey(Rune r)
-{
-	switch(r){
-	case Kesc:
-	case KF|1:
-	case KF|2:
-	case KF|3:
-	case KF|4:
-	case KF|5:
-	case KF|6:
-	case KF|7:
-	case KF|8:
-	case KF|9:
-		;
-	}
-	return 0;
-}
-static void
-eatcs(void)
-{
-	int i, d;
-	char *p, rc[UTFmax];
-	Rune r;
-
-	if(gm.demo && nbrecv(csc, nil) > 0){
-		gm.end = EDkey;
-		return;
-	}
-	if(gm.record)
-		return;
-	i = 0;
-	p = crs + strlen(crs);
-	while(nbrecv(csc, &r) > 0 && i++ < 6){
-		if(quickkey(r))
-			continue;
-		d = runetochar(rc, &r);
-		if(p + d < crs + sizeof crs){
-			memcpy(p, rc, d);
-			p = crm114();
-		}
-	}
-}
-static void
 gamein(void)
 {
 	int mx, my, scale;
@@ -1854,10 +1668,16 @@
 	qlock(&inlck);
 	mx = mΔx;
 	my = mΔy;
-	kon = kb | mΔb & 5 | (mΔb & 2) << 2;
 	mΔx = mΔy = 0;
+	kon = kb | mb & 1 | (mb & 2) << 2 | (mb & 4) >> 1;
 	qunlock(&inlck);
 
+	if(kon & 1<<Kmenu){
+		gm.end = EDkey;
+		return;
+	}
+	if(autorun)
+		kon |= 1<<Krun;
 	kΔx = kΔy = 0;
 	scale = Δtc * (kon & 1<<Krun ? 70 : 35);
 	if(kon & 1<<K↑)
@@ -1884,6 +1704,8 @@
 static void
 demoin(void)
 {
+	if(dem >= deme)
+		sysfatal("demoin: read past end of lump");
 	kon = *dem++;
 	kΔx = *dem++ * Δtc;
 	kΔy = *dem++ * Δtc;
@@ -1894,7 +1716,7 @@
 input(void)
 {
 	kold = kon;
-	if(gm.demo && dem < deme)
+	if(gm.demo)
 		demoin();
 	else
 		gamein();
@@ -1909,7 +1731,7 @@
 		if(kon & 1<<Kopen & ~kold)
 			kon &= ~(1<<Kopen);
 		if(kon & 1<<Kfire & ~kold)
-			kon &= ~(1<<Kfire);	
+			kon &= ~(1<<Kfire);
 	}
 }
 
@@ -2381,17 +2203,167 @@
 		pal = pals[C0];
 }
 
+static void
+crmchop(void)
+{
+	gm.tc = 99 * 60 * Tb;
+	gm.pt = 0;
+	hudp();
+	bonustc = 6 * (Cfad-Cwht);
+	dirty = 1;
+}
+static void
+crmrcv(void)
+{
+	allrecv++;
+	sfx(Snobonus);
+}
+static void
+crmmed(void)
+{
+	giveh(100);
+	sfx(Shealth2);
+	crmchop();
+}
+static void
+crmamo(void)
+{
+	givea(99);
+	sfx(Sgetammo);
+	crmchop();
+}
+static void
+crmkey(void)
+{
+	givek(0);
+	givek(1);
+	sfx(Sgetkey);
+	crmchop();
+}
+static void
+crmwep(void)
+{
+	givew(WPgatling);
+	sfx(Sgetgatling);
+	crmchop();
+}
+static void
+crmmli(void)
+{
+	giveh(100);
+	givea(99);
+	givek(0);
+	givek(1);
+	givew(WPgatling);
+	sfx(Sgetgatling);
+	crmchop();
+}
+static void
+crmmap(void)
+{
+}
+static void
+crmgod(void)
+{
+	god ^= 1;
+	if(god){
+		sfx(Sendb2);
+		crmchop();
+	}else
+		sfx(Snobonus);
+}
+static void
+crmclp(void)
+{
+	noclip ^= 1;
+	if(noclip){
+		sfx(Sbonus4);
+		crmchop();
+	}else
+		sfx(Snobonus);
+}
+static void
+crmslo(void)
+{
+	slomo ^= 1;
+	if(slomo){
+		sfx(Sbonus4);
+		crmchop();
+	}else
+		sfx(Snobonus);
+}
+static void
+crmskp(void)
+{
+	gm.end = EDup;
+	if(ver < SDM && gm.map % 10 == 8 || ver >= SDM && gm.map == 20)
+		gm.end = EDwon;
+	sfx(Slvlend);
+	crmchop();
+}
+static void
+crmmus(void)
+{
+	mus(nrand(ver < SDM ? 27 : 24));
+}
+
 void
+dieturn(void)
+{
+	int Δθ;
+
+	Δθ = Δtc * 2;
+	if(dieΔθ - Δθ < 0)
+		Δθ = dieΔθ;
+	dieΔθ -= Δθ;
+	oplr->θ = (oplr->θ + Δθ * diedir + 360) % 360;
+	render();
+	out();
+	if(dieΔθ != 0)
+		qtc = 0;
+	else
+		pal = pals[C0];
+}
+
+void
+die(void)
+{
+	int θ, lrot, rrot;
+	double fθ;
+
+	gm.w = -1;
+	gm.lives--;
+	stopmus();
+	sfx(Sdie);
+	fizzop(0x4, 0);
+	fθ = atan2(oplr->y - killy, killx - oplr->x);
+	if(fθ < 0)
+		fθ += Fpi * 2;
+	θ = fθ / (Fpi * 2) * 360;
+	lrot = θ - oplr->θ;
+	rrot = oplr->θ - θ;
+	if(oplr->θ > θ)
+		lrot += 360;
+	else
+		rrot += 360;
+	if(lrot < rrot){
+		diedir = 1;
+		dieΔθ = abs(lrot % 360);
+	}else{
+		diedir = -1;
+		dieΔθ = abs(rrot % 360);
+	}
+}
+
+void
 camwarp(void)
 {
-	int Δx, Δy, Δr;
+	int Δr;
 	double θ;
 
-	oplr->x = bosskillx;
-	oplr->y = bosskilly;
-	Δx = camobj->x - oplr->x;
-	Δy = oplr->y - camobj->y;
-	θ = atan2(Δy, Δx);
+	oplr->x = killx;
+	oplr->y = killy;
+	θ = atan2(oplr->y - camobj->y, camobj->x - oplr->x);
 	if(θ < 0)
 		θ = Fpi * 2 + θ;
 	oplr->θ = θ / (Fpi * 2) * 360;
@@ -2414,8 +2386,36 @@
 }
 
 void
+givew(int n)
+{
+	givea(6);
+	if(gm.bestw < n)
+		gm.w = gm.lastw = gm.bestw = n;
+	hudw();
+	if(n == WPgatling){
+		pic(136, 164, pict[Pgat]);
+		facetc = 0;
+	}
+}
+
+void
+givep(int n)
+{
+	if(dirty)
+		return;
+	gm.pt += n;
+	while(gm.pt >= gm.to1up){
+		gm.to1up += 40000;
+		givel();
+	}
+	hudp();
+}
+
+void
 bonus(Static *s)
 {
+	if(gm.hp == 0)
+		s->item = Rnil;
 	switch(s->item){
 	case Rstim:
 		if(gm.hp == 100)
@@ -2433,22 +2433,22 @@
 	case Rcross:
 		sfx(Sbonus1);
 		givep(100);
-		gm.treasure++;
+		gm.tp++;
 		break;
 	case Rchalice:
 		sfx(Sbonus2);
 		givep(500);
-		gm.treasure++;
+		gm.tp++;
 		break;
 	case Rbible:
 		sfx(Sbonus3);
 		givep(1000);
-		gm.treasure++;
+		gm.tp++;
 		break;
 	case Rcrown:
 		sfx(Sbonus4);
 		givep(5000);
-		gm.treasure++;
+		gm.tp++;
 		break;
 	case Rclip1:
 		if(gm.ammo == 99)
@@ -2475,8 +2475,6 @@
 	case Rchaingun:
 		sfx(Sgetgatling);
 		givew(WPgatling);
-		pic(136, 164, pict[Pgat]);
-		facetc = 0;
 		break;
 	case R1up:
 		sfx(S1up);
@@ -2483,7 +2481,7 @@
 		giveh(99);
 		givea(25);
 		givel();
-		gm.treasure++;
+		gm.tp++;
 		break;
 	case Rfood:
 		if(gm.hp == 100)
@@ -2504,16 +2502,81 @@
 		giveh(1);
 		break;
 	case Rspear:
-		gotspear++;
 		spearx = oplr->x;
 		speary = oplr->y;
 		spearθ = oplr->θ;
-		gm.end = EDup;
+		gm.end = gm.demo || gm.record ? EDdem : EDspear;
 	}
 	bonustc = 6 * (Cfad-Cwht);
 	s->tl = nil;
 }
 
+void
+crm114(int n)
+{
+	static Crm crms[] = {
+		{"fgd135", crmrcv},
+		{"opeaid", crmmed},
+		{"opepak", crmamo},
+		{"opeopn", crmkey},
+		{"opephz", crmwep},
+		{"opemli", crmmli},
+		{"opepda", crmmap},
+		{"opedqd", crmgod},
+		{"opeclp", crmclp},
+		{"opeslo", crmslo},
+		{"opeskp", crmskp},
+		{"opemus", crmmus}
+	};
+	Crm *p, *e;
+
+	p = crms;
+	e = crms + 1;
+	if(allrecv){
+		p++;
+		e = crms + nelem(crms);
+	}
+	while(p < e){
+		if(strncmp(crs, p->s, n) == 0){
+			if(n == Ncrm-1){
+				p->f();
+				memset(crs, 0, sizeof crs);
+			}
+			return;
+		}
+		p++;
+	}
+	memset(crs, 0, sizeof crs);
+}
+
+void
+eatcs(void)
+{
+	int i, d;
+	char *p, rc[UTFmax];
+	Rune r;
+
+	if(gm.demo){
+		if(nbrecv(csc, nil) > 0)
+			gm.end = EDkey;
+		return;
+	}
+	i = 0;
+	p = crs + strlen(crs);
+	while(nbrecv(csc, &r) > 0 && i++ < Ncrm-1){
+		if(quickkey(r))
+			return;
+		if(gm.record)
+			continue;
+		d = runetochar(rc, &r);
+		if(p + d < crs + sizeof crs){
+			memcpy(p, rc, d);
+			p += d;
+			crm114(p - crs);
+		}
+	}
+}
+
 int
 rnd(void)
 {
@@ -2524,11 +2587,11 @@
 void
 gstep(void)
 {
-	if(gm.demo || gm.record){
+	if(gm.demo || gm.record || slomo){
 		if(demfrm-- != 0)
 			return;
 		demfrm = 3;
-		Δtc = 4;
+		Δtc = slomo ? 1 : 4;
 	}
 	input();
 	noise = 0;
@@ -2535,8 +2598,8 @@
 	uworld();
 	upal();
 	render();
-	mtc += Δtc;
-	gm.lvltc += Δtc;
+	qtc += Δtc;
+	gm.tc += Δtc;
 	if(dofizz){
 		dofizz = 0;
 		if(!gm.end)
@@ -2553,9 +2616,34 @@
 }
 
 void
-demo(void)
+nextmap(void)
 {
+	static int setece[] = {1, 11, 27, 33, 45, 53};
+	int n, e, m;
+
+	m = gm.map;
+	n = m + 1;
+	if(ver < SDM){
+		e = m / 10;
+		if(gm.com == GMret)
+			n = setece[e];
+		else if(gm.com == GMsetec)
+			n = e + 9;
+	}else{
+		if(gm.com == GMret)
+			n = m == 18 ? 4 : m == 19 ? 12 : m + 1;
+		else if(gm.com == GMsetec)
+			n = m == 3 ? 18 : m == 11 ? 19 : m + 1;
+	}
+	gm.map = n;
+}
+
+void
+game(void)
+{
 	initmap();
+	killx = oplr->x;
+	killy = oplr->y;
 	mapmus();
 	pal = pals[C0];
 	dofizz++;
@@ -2563,8 +2651,56 @@
 }
 
 void
-initg(int r, uchar *p)
+spshunt(void)
 {
+	gm.oldpt = gm.pt;
+	gm.map = 20;
+	dmgtc = bonustc = 0;
+	initmap();
+	sfx(Sspear);
+	oplr->x = spearx;
+	oplr->y = speary;
+	oplr->tx = spearx >> Dtlshift;
+	oplr->ty = speary >> Dtlshift;
+	oplr->θ = spearθ;
+	oplr->areaid = oplr->tl->p0 - MTfloor;
+}
+
+void
+greset(void)
+{
+	if(gm.w == -1){
+		gm.hp = 100;
+		gm.ammo = 8;
+		gm.w = gm.lastw = gm.bestw = WPpistol;
+	}
+	gm.pt = gm.oldpt;
+	gm.tc = 0;
+	gm.kp = gm.sp = gm.tp = 0;
+	gm.ktot = gm.stot = gm.ttot = 0;
+	gm.wfrm = gm.facefrm = 0;
+	gm.keys = 0;
+	gm.won = gm.mut = 0;
+	dmgtc = bonustc = facetc = funtc = 0;
+	firing = 0;
+	kb = 0;
+	mb = 0;
+	kon = kold = 0;
+	mΔx = mΔy = 0;
+	kΔθ = 0;
+	allrecv = 0;
+	dirty = 0;
+	slomo = noclip = god = 0;
+	if(ver == SOD && gm.map == 20)
+		givek(0);
+}
+
+void
+ginit(uchar *p, int m, int d)
+{
+	int r;
+
+	r = 1;
 	memset(&gm, 0, sizeof gm);
 	if(p != nil){
 		gm.demo++;
@@ -2575,20 +2711,16 @@
 		if((deme-dem) % 3 != 0)
 			sysfatal("initd: invalid demo lump\n");
 		demfrm = 0;
+		r = 0;
+		greset();
+	}else if(m != -1){
+		gm.map = m;
+		gm.difc = d;
 	}
+	rndi = r ? time(nil) & 0xff : 0;
 	gm.hp = 100;
 	gm.ammo = 8;
 	gm.lives = 3;
 	gm.w = gm.lastw = gm.bestw = WPpistol;
 	gm.to1up = GPextra;
-	rndi = r ? time(nil) & 0xff : 0;
-	dmgtc = bonustc = facetc = funtc = 0;
-	firing = 0;
-	kon = kold = 0;
-	kΔθ = 0;
-	allrecv = 0;
-	sfxlck = 0;
-	gotspear = 0;
-	if(ver == SOD && gm.map == 20)
-		givek(0);
 }
--- a/hub.c
+++ b/hub.c
@@ -7,20 +7,8 @@
 
 extern Channel *csc;
 
-int mtc;
-
-typedef struct Score Score;
-typedef struct Seq Seq;
-typedef struct Item Item;
-typedef struct Menu Menu;
-
-struct Score{
-	char name[58];
-	int n;
-	int lvl;
-	int ep;
-};
-static Score sc[] = {
+int qtc;
+Score scs[] = {
 	{"id software-'92", 10000, 1},
 	{"Adrian Carmack", 10000, 1},
 	{"John Carmack", 10000, 1},
@@ -29,56 +17,236 @@
 	{"John Romero", 10000, 1},
 	{"Jay Wilbur", 10000, 1},
 };
+char savs[10][32];
 
+typedef struct Fade Fade;
+typedef struct Sp Sp;
+typedef struct Seq Seq;
+typedef struct Item Item;
+typedef struct Menu Menu;
+
 enum{
-	Lload,
+	LMctl,
+	LMnew,
+	LMdifc,
+	LMsnd,
+	LMin,
+	LMsav,
+	LMmsg,
+
+	Lload = 0,
 	Lintro,
+	Lftitle,
 	Ltitle,
 	Lcreds,
 	Lscore,
+	Lfpants,
+	Lpants,
 	Ldemo,
+	Lpsych,
+	Lgame,
+	Lretry,
+	Lmsg,
+	Lcont1,
+	Lcont2,
+	Lgcont,
+	Lfdie,
+	Ldie,
+	Ldie2,
+	Lhigh,
 	Lcam,
+	Lspear,
 	Linter,
-	Lwin,
+	Linteri,
+	Linterm,
+	Linterw,
+	Lintere,
+	Lsinter,
+	Lsdmend,
+	Lcolp,
+	Lcolp2,
+	Lcolp3,
+	Lwon,
+	Lwon2,
+	Lsdepi,
+	Lpres,
+	Lroll1,
+	Lroll2,
+	Lroll3,
+	Lroll4,
+	Lroll5,
+	Lroll6,
+	Lroll7,
 	Ldecay,
-	Linctl,
 	Lctl,
+	Ltoctl,
+	Lftoctl,
+	Lmtoctl,
 	Lcur,
-	Lesc,
-	Lback,
-	Lwait,
-	Lsfxwait,
-	Lack,
+	Lftonew,
+	Lmtonew,
+	Lnewgame,
+	Lnctl,
+	Ldenied,
+	Ldifc1,
+	Ldifc2,
+	Ldifc3,
+	Ldifc4,
+	Ldifc5,
+	Lfsnd,
+	Lmsnd,
+	Lsctl,
+	Lsndtog,
+	Lfin,
+	Lmin,
+	Lictl,
+	Lintog,
+	Lfsens,
+	Lmsens,
+	Lsectl,
+	Lfsav,
+	Lmsav,
+	Lsvctl,
+	Lflod,
+	Lmlod,
+	Lldctl,
+	Lfvw,
+	Lmvw,
+	Lvwctl,
+	Lfmscore,
 	Lmscore,
-	Lpants,
+	Lend,
+	Lcurgame,
 	Lquit,
 	Lmexit,
 	Lexit
 };
-struct Seq{
+struct Sp{
 	int dt;
 	void (*f)(void);
 };
+struct Fade{
+	Col c;
+	int dt;
+};
+struct Seq{
+	void (*init)(void);
+	Sp *s;
+	Sp *e;
+	Seq *q;
+	Fade *f;
+};
+static Seq *qsp, *qesc, ql[];
+static Sp *qp;
+
+enum{
+	DMbg,
+	DMoff,
+	DMbrd,
+	DMbrd2,
+	DMend,
+
+	DIreg = 0x17,
+	DIsee = 0x4a,
+	DIeps = 0x6b,
+	DIrhi = 0x13,
+	DIshi = 0x47,
+	DIepi = 0xd0,
+};
+static int mcol[DMend] = {[DMbg] 0x2d, 0};
+
+#define SEL(c)		((c) - 4 + (((c) >> 1 ^ (c)) >> 5 & 1))
+#define UNSEL(c)	((c) + 4 - (((c) >> 1 ^ (c)) >> 5 & 1))
+
 struct Item{
 	char *s;
 	int c;
-	Menu *m;
+	Seq *q;
+	int a;
 };
+static Item
+ictl[] = {
+	{"New Game", SEL(DIreg), ql+Lftonew},
+	{"Sound", DIreg, ql+Lfsnd},
+	{"Control", DIreg, ql+Lfin},
+	{"Load Game", DIreg, ql+Lflod},
+	{"Save Game", DIreg, ql+Lfsav},
+	{"Change View", DIreg, ql+Lfvw},
+	{"View Scores", DIreg, ql+Lfmscore},
+	{"Back to Demo", DIreg, ql+Lftitle},
+	{"Quit", DIreg, ql+Lquit}
+},
+inew[] = {
+	{"Episode 1\nEscape from Wolfenstein", SEL(DIreg), ql+Ldifc1},
+	{nil},
+	{"Episode 2\nOperation: Eisenfaust", DIreg, ql+Ldifc1},
+	{nil},
+	{"Episode 3\nDie, Fuhrer, Die!", DIreg, ql+Ldifc1},
+	{nil},
+	{"Episode 4\nA Dark Secret", DIreg, ql+Ldifc1},
+	{nil},
+	{"Episode 5\nTrail of the Madman", DIreg, ql+Ldifc1},
+	{nil},
+	{"Episode 6\nConfrontation", DIreg, ql+Ldifc1},
+	{nil}
+},
+idifc[] = {
+	{"Can I play, Daddy?", DIreg, ql+Ldifc4},
+	{"Don't hurt me.", DIreg, ql+Ldifc4},
+	{"Bring 'em on!", SEL(DIreg), ql+Ldifc4},
+	{"I am Death incarnate!", DIreg, ql+Ldifc4}
+},
+isnd[] = {
+	{"None", SEL(DIreg), ql+Lsndtog},
+	{"PC Speaker"},
+	{"AdLib/Sound Blaster", DIreg, nil, 1},
+	{nil},
+	{nil},
+	{"None", DIreg, ql+Lsndtog},
+	{"Disney Sound Source"},
+	{"Sound Blaster", DIreg, nil, 1},
+	{nil},
+	{nil},
+	{"None", DIreg, ql+Lsndtog},
+	{"AdLib/Sound Blaster", DIreg, nil, 1}
+},
+iin[] = {
+	{"Mouse Enabled", SEL(DIreg), ql+Lintog, 1},
+	{"Autorun Enabled", DIreg, ql+Lintog},
+	{"Mouse Sensitivity", DIreg, ql+Lfsens}
+},
+isv[] = {
+	{"", SEL(DIreg), nil},
+	{"", DIreg, nil},
+	{"", DIreg, nil},
+	{"", DIreg, nil},
+	{"", DIreg, nil},
+	{"", DIreg, nil},
+	{"", DIreg, nil},
+	{"", DIreg, nil},
+	{"", DIreg, nil},
+	{"", DIreg, nil}
+},
+imsg[] = {
+	{nil}
+};
 struct Menu{
-	void (*init)(void);
-	Seq *qs;
-	Seq *qe;
-	Menu *m;
-	Col *c;
-	Item *is;
-	Item *ie;
-	Item *ip;
-	int cx;
-	int cy;
-	int cur;
+	Item *p;
+	Item *s;
+	Item *e;
+	int x;
+	int y;
+	int n;
 };
-static Menu *mp, ml[];
-static Seq *mqp;
+static Menu *mp, ml[] = {
+	[LMctl] {ictl, ictl, ictl+nelem(ictl), 72},
+	[LMnew] {inew, inew, inew+nelem(inew), 8},
+	[LMdifc] {idifc+2, idifc, idifc+nelem(idifc), 48},
+	[LMsnd] {isnd, isnd, isnd+nelem(isnd), 48},
+	[LMin] {iin, iin, iin+nelem(iin), 24},
+	[LMsav] {isv, isv, isv+nelem(isv), 80},
+	[LMmsg] {imsg}
+};
 
 static char *ends[] = {
 	"Dost thou wish to\nleave with such hasty\nabandon?",
@@ -103,198 +271,436 @@
 };
 static char **quits;
 
-enum{
-	Dbg,
-	Doff,
-	Dbrd,
-	Dbrd2,
-	Dend
-};
-static int mcol[Dend] = {[Dbg] 0x2d, 0};
+static Fade
+	fblk = {{0, 0, 0}, 30},
+	ftra = {{0, 0, 0}, 10},
+	fctl = {{0xae, 0, 0}, 10},
+	focl = {{0, 0x44, 0x44}, 300},
+	ficl = {{0, 0x44, 0x44}, 30},
+	fecl = {{0, 0x44, 0x44}, 5};
 
+static int irr[4], *irp;
+static int iri, iry, irb;
+
 static uchar **demd;
 static char *demf;
 
-static void (*mclear)(void);
-static void (*stripe)(int);
+static void
+reset(Seq *p)
+{
+	qp = p->s;
+	qtc = 0;
+	if(p == qsp)
+		return;
+	toss();
+	if(qp->f != fadeout)
+		pal = pals[C0];
+	qsp = p;
+	if(p->init != nil)
+		p->init();
+	if(p->f != nil)
+		fadeop(&p->f->c, p->f->dt);
+}
 
 static void
-wlmclear(void)
+pblink(void)
 {
-	put(0, 0, Vw, Vh, 0x29);
+	if(mp->n == 0)
+		txt(mp->x, mp->y, "_", 0);
+	else
+		put(mp->x, mp->y, fnt->w['_'], fnt->h, DIreg);
+	out();
+	mp->n ^= 1;
 }
+
 static void
-sdmclear(void)
+blink(void)
 {
-	pic(0, 0, pict[Pbackdrop]);
+	put(mp->x, mp->y, 24, 16, mcol[DMbg]);
+	pic(mp->x, mp->y, pict[Pcur1] + mp->n);
+	out();
+	mp->n ^= 1;
 }
 
 static void
-wlstripe(int y)
+iswp(Item *on, Item *off)
 {
-	put(0, y, Vw, 24, 0);
-	put(0, y+22, 320, 1, 0x2c);
+	on->a = 1;
+	on->q = nil;
+	off->a = 0;
+	off->q = ql+Lsndtog;
 }
+
 static void
-sdstripe(int y)
+toggle(void)
 {
-	put(0, y, Vw, 22, 0);
-	put(0, y+23, 320, 1, 0);
+	Item *i;
+
+	i = mp->s;
+	switch(mp - ml){
+	case LMsnd:
+		if(mp->p->a)
+			return;
+		switch(mp->p - i){
+		case 0: iswp(i, i+2); stopsfx(); sfxon = 0; break;
+		case 2: iswp(i+2, i); sfxon++; sfx(Sshoot); break;
+		case 5: iswp(i+5, i+7); pcmon = 0; break;
+		case 7: iswp(i+7, i+5); pcmon++; break;
+		case 10: iswp(i+10, i+11); stopmus(); muson = 0; sfx(Sshoot); break;
+		case 11: iswp(i+11, i+10); muson++; mus(Mmenu); break;
+		}
+		break;
+	case LMin:
+		switch(mp->p - i){
+		tog: mp->p->a ^= 1; break;
+		case 0: grabon ^= 1; goto tog;
+		case 1: autorun ^= 1; goto tog;
+		}
+		break;
+	}
 }
 
 static void
-outbox(int x, int y, int dx, int dy, int c1, int c2)
+inctl(void)
 {
-	put(x, y, dx, 1, c2);
-	put(x, y+1, 1, dy-1, c2);
-	put(x, y+dy, dx+1, 1, c1);
-	put(x+dx, y, 1, dy, c1);
+	Item *i, *e;
+	Menu *m;
+
+	m = ml+LMctl;
+	i = m->s;
+	i[4].c = mcol[DMoff];
+	i[6].s = "View Scores";
+	i[6].q = ql+Lfmscore;
+	i[6].c = DIreg;
+	i[7].s = "Back to Demo";
+	i[7].q = ql+Lftitle;
+	i[7].c = DIreg;
+	m->p = m->s;
+	m->p->c = SEL(DIreg);
+	if(ver >= SDM)
+		i[0].q = ql+Ldifc1;
+	else
+		for(i=ml[LMnew].s, e=ml[LMnew].e; i<e && i->q!=ql+Ldenied; i++)
+			i->q = ql+Ldifc1;
 }
 
 static void
-box(int x, int y, int dx, int dy, int col, int out, int out2)
+ingctl(void)
 {
-	put(x+1, y+1, dx-1, dy-1, col);
-	outbox(x, y, dx, dy, out, out2);
+	Item *i, *e;
+
+	i = ml[LMctl].s;
+	i[4].c = DIreg;
+	i[6].s = "End Game";
+	i[6].q = ql+Lend;
+	i[7].s = "Back to Game";
+	i[7].q = ql+Lcont1;
+	i[7].c = DIsee;
+	if(i[0].q == ql+Ldifc1)
+		i[0].q = ql+Lcurgame;
+	else
+		for(i=ml[LMnew].s, e=ml[LMnew].e; i<e && i->q!=ql+Ldenied; i++)
+			i->q = ql+Lcurgame;
 }
 
 static void
-viewbox(void)
+mbox(int x, int y, int dx, int dy)
 {
-	int x, y;
+	box(x, y, dx, dy, mcol[DMbg], mcol[DMbrd2], mcol[DMoff]);
+}
 
-	x = Vhud - vw.dx / 2 - 1;
-	y = (Vhud - vw.dy) / 2 - 1;
-	put(0, 0, 320, Vhud, 0x7f);
-	box(x, y, vw.dx+1, vw.dy+1, 0, 0x7d, 0);
-	put(x, y+vw.dy+1, 1, 1, 0x7c);
+static void
+msg(char *s, int o)
+{
+	int x, y, w, h, curw;
+	char *nl;
+
+	fnt = fnts+1;
+	h = txth(s);
+	w = txtw(s);
+	nl = strrchr(s, '\n');
+	curw = txtw(nl != nil ? nl + 1 : s);
+	w = w > curw + 10 ? w : curw + 10;
+	x = Vw / 2 - w / 2;
+	y = (step == gstep ? Vhud : Vh) / 2 - h / 2;
+	box(x - 5, y - 5, w + 10, h + 10, DIreg, 0, DIrhi);
+	txtnl(x, y, s, 0);
+	if(o)
+		out();
+	mp = ml+LMmsg;
+	mp->n = 0;
+	mp->x = x + curw;
+	mp->y = y + h - fnt->h;
 }
 
 static void
-view(void)
+quit(void)
 {
-	viewbox();
-	pic(0, Vhud, pict[Pstat]);
-	hudf();
-	hudh();
-	hudl();
-	hudm();
-	huda();
-	hudk();
-	hudw();
-	hudp();
+	msg(quits[nrand(nelem(ends)/2)], 0);
+	mp->p->q = ql+Lmexit;
+	qesc = ql+Lctl;
 }
 
 static void
-fixedw(char *s)
+curgame(void)
 {
-	char c;
+	msg("You are currently in\na game. Continuing will\nerase old game. Ok?", 0);
+	mp->p->q = ql+Ldifc1;
+	qesc = ver < SDM ? ql+Lftoctl : ql+Lctl;
+}
 
-	while(c = *s, c != 0)
-		*s++ = c - '0' + 129;
+static void
+mend2(void)
+{
+	view();
+	render();
+	gm.lives = 0;
+	ql[Ldie].q = ql+Ldie2;
 }
 
 static void
-reset(Menu *m)
+mend(void)
 {
-	Seq *q;
+	msg("Are you sure you want\nto end the game you\nare playing? (Y or N):", 0);
+	mp->p->q = ql+Lfdie;
+	qesc = ql+Lctl;
+}
 
-	q = m->qs;
-	mqp = q;
-	mtc = 0;
-	if(m != mp){
-		toss();
-		if(q->f != fadeout && mp != ml+Lpants)
-			pal = pals[C0];
-		mp = m;
-		if(m->init != nil)
-			m->init();
-		if(m->c != nil)
-			fadeop(m->c, q->dt);
-	}
+static void
+denied(void)
+{
+	msg("Please select \"Read This!\"\nfrom the Options menu to\n"
+		"find out how to order this\nepisode from Apogee.", 1);
+	stopsfx();
+	sfx(Snoway);
 }
 
 static void
-blink(void)
+sdmend(void)
 {
+	sfx(S1up);
+	msg("This concludes your demo\nof Spear of Destiny! Now,\n"
+		"go to your local software\nstore and buy it!", 1);
+}
+
+static void
+setdifc(void)
+{
+	int m, d;
+
+	m = ver < SDM ? (ml[LMnew].p - ml[LMnew].s) * 5 : 0;
+	d = ml[LMdifc].p - ml[LMdifc].s;
+	ginit(nil, m, d);
+	ingctl();
+	palfill(&fctl.c);
+}
+
+static void
+difc(void)
+{
 	Menu *m;
+	Item *i, *e;
 
-	m = mp;
-	if(m == ml+Lctl){
-		put(m->cx, m->cy, 24, 16, mcol[Dbg]);
-		pic(m->cx, m->cy, pict[Pcur1]+m->cur);
-	}else if(m == ml+Lquit){
-		if(m->cur == 0)
-			txt(m->cx, m->cy, "_", 0);
-		else
-			put(m->cx, m->cy, fnt->w['_'], fnt->h, 0x17);
+	mclear();
+	pic(112, 184, pict[Pmouselback]);
+	if(ver < SDM){
+		txt(70, 68, "How tough are you?", DIshi);
+		qesc = ql+Lftonew;
+	}else{
+		pic(70, 68, pict[Pdiffc]);
+		qesc = ql+Lftoctl;
 	}
-	out();
-	m->cur ^= 1;
+	mbox(45, 90, 225, 67);
+	m = ml+LMdifc;
+	mp = m;
+	i = m->s;
+	e = m->e;
+	do{
+		txt(74, 100 + 13 * (i - m->s), i->s, i->c);
+	}while(++i < e);
+	pic(235, 107, pict[Pbaby] + m->p - m->s);
+	m->n = 0;
+	m->y = 98 + 13 * (m->p - m->s);
 }
 
 static void
-ask(void)
+newgame(void)
 {
-	Rune r;
+	int n;
+	Item *i, *e;
+	Menu *m;
 
-	if(nbrecv(csc, &r) <= 0)
-		return;
-	if(r == 'y'){
-		sfx(Sshoot);
-		reset(ml+Lmexit);
-	}
-	else if(r == 'n' || r == Kesc){
-		sfx(Sesc);
-		reset(ml+Lctl);
-	}
+	mclear();
+	pic(112, 184, pict[Pmouselback]);
+	mbox(6, 19, 308, 162);
+	txtcen(2, "Which episode to play?", DIshi);
+	for(n=0; n<6; n++)
+		pic(40, 23 + n * 26, pict[Pep1] + n);
+	m = ml+LMnew;
+	mp = m;
+	i = m->s;
+	e = m->e;
+	do{
+		txtnl(98, 23 + 13 * (i - m->s), i->s, i->c);
+		i += 2;
+	}while(i < e);
+	qesc = ql+Lftoctl;
+	m->n = 0;
+	m->y = 21 + 13 * (m->p - m->s);
 }
 
 static void
-quit(void)
+snd(void)
 {
-	int x, y, w, h, curw;
-	char *s, *nl;
+	Item *i, *e;
+	Menu *m;
 
-	s = quits[nrand(nelem(ends)/2)];
-	h = txth(s);
-	w = txtw(s);
-	nl = strrchr(s, '\n');
-	curw = txtw(nl != nil ? nl+1 : s);
-	w = w > curw+10 ? w : curw+10;
-	y = 200/2 - h/2;
-	x = Vhud - w/2;
+	mclear();
+	pic(112, 184, pict[Pmouselback]);
+	mbox(40, 17, 250, 45);
+	mbox(40, 82, 250, 45);
+	mbox(40, 147, 250, 32);
+	pic(96, 0, pict[Psfx]);
+	pic(96, 65, pict[Ppcm]);
+	pic(96, 130, pict[Pmus]);
+	m = ml+LMsnd;
+	mp = m;
+	i = m->s;
+	e = m->e;
+	do{
+		if(i->s == nil)
+			continue;
+		txt(100, 20 + 13 * (i - m->s), i->s, i->c);
+		pic(72, 22 + 13 * (i - m->s), pict[i->a ? Psel : Punsel]);
+	}while(++i < e);
+	qesc = ql+Lftoctl;
+	m->n = 0;
+	m->y = 18 + 13 * (m->p - m->s);
+}
 
-	box(x-5, y-5, w+10, h+10, 0x17, 0, 0x13);
-	txtnl(x, y, s, 0);
-	mp->cx = x+curw;
-	mp->cy = y + h - fnt->h;
+static void
+sens(void)
+{
+	int n;
+
+	mclear();
+	pic(112, 184, pict[Pmouselback]);
+	mbox(10, 80, 300, 30);
+	txtcen(82, "Adjust Mouse Sensitivity", DIsee);
+	txt(14, 95, "Slow", DIreg);
+	txt(269, 95, "Fast", DIreg);
+	put(60, 97, 200, 10, DIreg);
+	outbox(60, 97, 200, 10, 0x0, DIrhi);
+	n = 60 + 20 * iri;
+	put(n + 1, 98, 19, 9, DIshi);
+	outbox(n, 97, 20, 10, 0x0, DIsee);
+	qesc = ql+Lfin;
 }
 
 static void
+in(void)
+{
+	Menu *m;
+	Item *i, *e;
+
+	mclear();
+	stripe(10);
+	pic(80, 0, pict[Pctl]);
+	pic(112, 184, pict[Pmouselback]);
+	mbox(16, 65, 284, 45);
+	m = ml+LMin;
+	mp = m;
+	i = m->s;
+	e = m->e;
+	do{
+		txt(80, 70 + 13 * (i - m->s), i->s, i->c);
+		if(i < e - 1)
+			pic(56, 73 + 13 * (i - m->s), pict[i->a ? Psel : Punsel]);
+	}while(++i < e);
+	qesc = ql+Lftoctl;
+	m->n = 0;
+	m->y = 68 + 13 * (m->p - m->s);
+	iri = msense;
+}
+
+static void
+savitem(int i, int c)
+{
+	outbox(109, 55 + i * 13, 136, 11, c, c);
+	fnt = fnts;
+	txt(111, 56 + i * 13, savs[i][0] == 0 ? "      - empty -" : savs[i], c);
+	fnt = fnts+1;
+}
+
+static void
+sav(void)
+{
+	Menu *m;
+	Item *i, *e;
+
+	mclear();
+	pic(112, 184, pict[Pmouselback]);
+	mbox(75, 50, 175, 140);
+	stripe(10);
+	pic(56, 0, pict[qsp->q == ql+Lsvctl ? Psave : Pload]);
+	m = ml+LMsav;
+	mp = m;
+	i = m->s;
+	e = m->e;
+	do
+		savitem(i - m->s, i->c);
+	while(++i < e);
+	qesc = ql+Lftoctl;
+	m->n = 0;
+	m->y = 53 + 13 * (m->p - m->s);
+}
+
+static void
+mvw(void)
+{
+	int dx, dy;
+
+	dx = vw.dx;
+	dy = vw.dy;
+	vw.dx = iri * 16;
+	vw.dy = iri * 8;
+	viewbox();
+	vw.dx = dx;
+	vw.dy = dy;
+	put(0, Vhud, Vw, 40, 0x7f);
+	txtcen(161, "Use arrows to size", DIrhi);
+	txtcen(161 + fnt->h, "ENTER to accept", DIrhi);
+	txtcen(161 + 2 * fnt->h, "ESC to cancel", DIrhi);
+	qesc = ql+Lftoctl;
+}
+
+static void
 ctl(void)
 {
 	Menu *m;
-	Item *i, *s, *e;
+	Item *i, *e;
 
 	mclear();
 	pic(112, 184, pict[Pmouselback]);
 	stripe(10);
 	pic(80, 0, pict[Popt]);
-	box(68, 52, 178, 6+13*9, mcol[Dbg], mcol[Dbrd2], mcol[Doff]);
-
+	mbox(68, 52, 178, 123);
 	fnt = fnts+1;
-	m = ml+Lctl;
-	s = i = m->is;
-	e = m->ie;
+	m = ml+LMctl;
+	mp = m;
+	i = m->s;
+	e = m->e;
 	do
-		txt(100, 55+13*(i-s), i->s, i->c);
+		txt(100, 55 + 13 * (i - m->s), i->s, i->c);
 	while(++i < e);
-	m->cur = 0;
-	m->cx = 72;
-	m->cy = 53+13*(m->ip-s);
-	if(mp == m)
-		mus(Mmenu);
+	qesc = ql+Lquit;
+	m->n = 0;
+	m->y = 53 + 13 * (m->p - m->s);
+	if(qsp == ql+Ltoctl)
+		stopsfx();
+	mus(Mmenu);
+	grab(0);
+	iri = vwsize;
 }
 
 static void
@@ -304,116 +710,160 @@
 }
 
 static void
-movcur(Menu *m, Item *p, int dir)
+slcur(int dir)
 {
+	iri += dir;
+	switch(qsp - ql){
+	case Lsectl:
+		if(iri < 0)
+			iri = 0;
+		else if(iri > 9)
+			iri = 9;
+		break;
+	case Lvwctl:
+		if(iri < 4)
+			iri = 4;
+		else if(iri > 19)
+			iri = 19;
+		break;
+	}
+	qsp->init();
+	out();
+}
+
+static void
+slider(void)
+{
+	Rune r;
+
+	if(nbrecv(csc, &r) <= 0)
+		return;
+	switch(r){
+	case Kleft: case Kdown: slcur(-1); break;
+	case Kright: case Kup: slcur(1); break;
+	case Kesc: sfx(Sesc); reset(qesc); break;
+	case '\n':
+	case ' ':
+		switch(qsp - ql){
+		case Lsectl: msense = iri; break;
+		case Lvwctl: vwsize = iri; msg("Thinking...", 1); setvw(); break;
+		}
+		sfx(Sshoot);
+		reset(qesc);
+		break;
+	}
+}
+
+static void
+movcur(Item *p, int dir)
+{
 	Item *i;
 
-	p->c = 0x17;
+	p->c = UNSEL(p->c);
 	i = p;
 	do{
 		i += dir;
-		if(i < m->is)
-			i = m->ie-1;
-		else if(i == m->ie)
-			i = m->is;
-	}while(i->c != 0x17);
-	i->c = 0x13;
-	m->ip = i;
-	m->cur = 0;
-	if(i != p+dir){
+		if(i < mp->s)
+			i = mp->e - 1;
+		else if(i == mp->e)
+			i = mp->s;
+	}while(i->c == mcol[DMoff] || i->c == 0);
+	i->c = SEL(i->c);
+	mp->p = i;
+	mp->n = 0;
+	if(i != p + dir){
 		cursfx();
-		reset(ml+Lctl);
-		ctl();
+		reset(qsp);
+		if(qsp->init != nil)
+			qsp->init();
 		return;
 	}
-	put(m->cx, m->cy, 24, 16, mcol[Dbg]);
-	m->cy += dir * 6;
+	put(mp->x, mp->y, 24, 16, mcol[DMbg]);
+	mp->y += dir * 6;
 	blink();
 	sfx(Sdrawgun1);
-	reset(ml+Lcur);
+	ql[Lcur].q = qsp;
+	reset(ql+Lcur);
 }
 
 static void
 cwalk(void)
 {
+	static char s[UTFmax*2], *sp = s;
 	Rune r;
-	Menu *m;
+	Seq *q;
 	Item *i;
-	static int p;
 
 	if(nbrecv(csc, &r) <= 0)
 		return;
-	m = mp;
-	i = m->ip;
+	i = mp->p;
 	switch(r){
-	case Kup: movcur(m, i, -1); break;
-	case Kdown: movcur(m, i, 1); break;
-	case Kesc: sfx(Sesc); reset(ml+Lquit); break;
-	case 'i': p++; break;
-	case 'd':
-		if(ver == SOD && p == 1)
-			reset(ml+Lpants);
-		break;
+	case Kup: movcur(i, -1); break;
+	case Kdown: movcur(i, 1); break;
+	case Kesc: sfx(Sesc); reset(qesc); break;
 	case '\n':
-		m = i->m;
-		if(m == nil)
+	case ' ':
+		q = i->q;
+		if(q == nil)
 			break;
-		if(m != ml+Lquit && m != ml+Lesc)
+		if(q != ql+Lquit && q != ql+Lftitle)
 			sfx(Sshoot);
-		reset(m);
+		reset(q);
+		break;
+	default:
+		if(ver < SOD)
+			break;
+		sp += runetochar(sp, &r);
+		if(strncmp(s, "id", sp-s) == 0){
+			if(sp - s == 2){
+				reset(ql+Lfpants);
+				sp = s;
+			}
+		}else
+			sp = s;
 	}
-	if(r != 'i')
-		p = 0;
 }
 
 static void
-inctl(void)
+ask(void)
 {
-	Item *i;
-	Menu *m;
+	Rune r;
 
-	m = ml+Lctl;
-	if(m->ip != nil)
-		m->ip->c = 0x17;
-	i = m->is;
-	m->ip = i;
-	i[0].c = 0x13;
-	i[4].c = mcol[Doff];
-	grab(0);
-	ctl();
-	stopsfx();
+	if(nbrecv(csc, &r) <= 0)
+		return;
+	if(r == 'y'){
+		sfx(Sshoot);
+		reset(mp->p->q);
+	}else if(r == 'n' || r == Kesc){
+		sfx(Sesc);
+		reset(qesc);
+	}
 }
 
 static void
-skipstep(void)
+skip(void)
 {
-	if(nbrecv(csc, nil) > 0){
-		mqp++;
-		mtc = 0;
-	}
+	if(nbrecv(csc, nil) > 0)
+		reset(ql+Ldecay);
 }
-
 static void
-skiploop(void)
+swait(void)
 {
-	if(nbrecv(csc, nil) > 0)
-		reset(ml+Ldecay);
+	if(lastsfx() >= 0)
+		qtc = 0;
+	else
+		sfxlck = 0;
 }
-
 static void
-ack(void)
+nbwait(void)
 {
 	if(nbrecv(csc, nil) > 0)
-		reset(ml+Lback);
+		qtc = qp->dt;
 }
-
 static void
-swait(void)
+bwait(void)
 {
-	if(lastsfx() < 0){
-		reset(ml+Linter);
-		step = mstep;
-	}
+	qtc = nbrecv(csc, nil) > 0 ? qp->dt : 0;
 }
 
 static void
@@ -422,33 +872,350 @@
 	pic(0, 0, pict[Pid1]);
 	pic(0, 80, pict[Pid2]);
 	palpic(exts[Eid]);
-	fadeop(mp->c, mp->qs->dt);
 	mus(Mnazjazz);
 }
 
 static void
-iwin(void)
+roll(void)
 {
+	pic(0, 0, pict[Pend1]+iri);
+	palpic(exts[Eend1+iri]);
+	iri++;
+	if(iri == 9)
+		iri = 1;
 }
 static void
-win(void)
+pres3(void)
 {
+	put(0, 180, Vw, 20, 0);
+	txtcen(180, "With the spear gone, the Allies will finally", DIepi);
+	/* bug: typo "by able to" */
+	txtcen(180 + fnt->h, "be able to destroy Hitler...", DIepi);
+	out();
+	iri = 3;
 }
+static void
+pres2(void)
+{
+	txtcen(180, "We owe you a great debt, Mr. Blazkowicz.", DIepi);
+	txtcen(180 + fnt->h, "You have served your country well.", DIepi);
+	out();
+}
+static void
+pres1(void)
+{
+	pic(0, 0, pict[Pend1]+2);
+	palpic(exts[Eend1+2]);
+	fnt = fnts;
+}
+static void
+sdepi(void)
+{
+	pic(0, 0, pict[Pend1]);
+	palpic(exts[Eend1]);
+}
 
 static void
-iscore(void)
+won(void)
 {
+	int n;
+	char a[10];
+
+	put(0, 0, Vw, Vhud, 0x7f);
+	pic(8, 4, pict[Pwin]);
+	pictxt(144, 16, "YOU WIN!");
+	pictxt(112, 48, "TOTAL TIME");
+	if(gm.eptm > 99 * 60)
+		gm.eptm = 99 * 60 + 99;
+	sprint(a, "%02d:%02d", gm.eptm / 60, gm.eptm % 60);
+	pictxt(113, 64, a);
+	pictxt(96, 96, "AVERAGES");
+	n = ver < SDM ? 8 : 14;
+	sprint(a, "KILL %3d%%", (gm.epk / n));
+	pictxt(112, 112, a);
+	sprint(a, "SECRET %3d%%", (gm.eps / n));
+	pictxt(80, 128, a);
+	sprint(a, "TREASURE %3d%%", (gm.ept / n));
+	pictxt(48, 144, a);
+	if(ver == WL6 && gm.difc >= GDmed){
+		pic(240, 64, pict[Ptc]);
+		fnt = fnts;
+		n = gm.eptm / 60;
+		a[0] = 'A' + (n / 10 ^ n % 10 ^ 0xa);
+		n = gm.eptm % 60;
+		a[1] = 'A' + (n / 10 ^ n % 10 ^ 0xa);
+		a[2] = 'A' + (a[0] ^ a[1]);
+		a[3] = 0;
+		txt(241, 72, a, DIshi);
+	}
+	fnt = fnts+1;
+	mus(ver < SDM ? Mwon : Msdwon);
+	grab(0);
 }
+
 static void
+colp(void)
+{
+	pic(124, 44, pict[Pcollapse] + iri++);
+	out();
+}
+static void
+incolp(void)
+{
+	iri = 1;
+	fill(0x7f);
+	pic(124, 44, pict[Pcollapse]);
+	mus(Mend);
+}
+
+static void
+breathe(void)
+{
+	static int n, t;
+
+	if(++t > 35){
+		t = 0;
+		n ^= 1;
+		pic(0, 16, pict[n ? Pguy2 : Pguy]);
+		out();
+	}
+}
+
+static void
+sinterw(void)
+{
+	breathe();
+	if(nbrecv(csc, nil) <= 0)
+		qtc = 0;
+}
+
+static void
+siscore(void)
+{
+	givep(15000);
+	gm.oldpt = gm.pt;
+}
+static void
+sinter(void)
+{
+	put(0, 0, Vw, Vhud, 0x7f);
+	pic(0, 16, pict[Pguy]);
+	if(ver < SDM)
+		pictxt(112, 32, "SECRET FLOOR\n COMPLETED!");
+	else{
+		switch(gm.map){
+		case 4: pictxt(112, 32, " TRANS\n GROSSE\nDEFEATED!"); break;
+		case 9: pictxt(112, 32, "BARNACLE\nWILHELM\nDEFEATED!"); break;
+		case 15: pictxt(112, 32, "UBERMUTANT\nDEFEATED!"); break;
+		case 17: pictxt(112, 32, " DEATH\n KNIGHT\nDEFEATED!"); break;
+		case 18: pictxt(104, 32, "SECRET TUNNEL\n    AREA\n  COMPLETED!"); break;
+		case 19: pictxt(104, 32, "SECRET CASTLE\n    AREA\n  COMPLETED!"); break;
+		}
+	}
+	if(gm.map == 17){	/* solely for crmskp */
+		gm.com = GMup;
+		gm.map = 19;
+	}
+	pictxt(80, 128, "15000 BONUS!");
+	mus(Minter);
+}
+
+static void
+interw(void)
+{
+	breathe();
+	if(nbrecv(csc, nil) > 0)
+		reset(ver == SDM && gm.map == 1 ? ql+Lsdmend : ql+Lintere);
+}
+
+static void
+intere(void)
+{
+	givep(irb);
+	gm.oldpt = gm.pt;
+	qsp->q = ql+Linterw;
+}
+
+static void
+interskip(void)
+{
+	int n;
+	char a[10];
+
+	irb = irr[0] * 500;
+	irb += 10000 * ((irr[1] == 100) + (irr[2] == 100) + (irr[3] == 100));
+	n = sprint(a, "%d", irb);
+	pictxt((36 - n * 2) * 8, 56, a);
+	n = sprint(a, "%d", irr[1]);
+	pictxt((37 - n * 2) * 8, 112, a);
+	n = sprint(a, "%d", irr[2]);
+	pictxt((37 - n * 2) * 8, 128, a);
+	n = sprint(a, "%d", irr[3]);
+	pictxt((37 - n * 2) * 8, 144, a);
+	intere();
+}
+
+static void
+interb(void)
+{
+	if(nbrecv(csc, nil) > 0)
+		interskip();
+	else if(lastsfx() >= 0){
+		breathe();
+		qtc = 0;
+	}
+}
+
+static void
+interm(void)
+{
+	int n;
+	char a[10];
+
+	if(nbrecv(csc, nil) > 0){
+		interskip();
+		return;
+	}
+	if(*irp == 100){
+		stopsfx();
+		irb += 10000;
+		n = sprint(a, "%d", irb);
+		pictxt((36 - n * 2) * 8, 56, a);
+		out();
+		sfx(S100);
+	}else if(*irp == 0)
+		sfx(Snobonus);
+	else
+		sfx(Sendb2);
+	iri = 0;
+	qsp->q = ql+Linteri;
+	qsp->q->q = ql+Linteri;
+	switch(++irp - irr){
+	case 1: iry = 112; break;
+	case 2: iry = 128; break;
+	case 3: iry = 144; break;
+	case 4: intere(); break;
+	}
+}
+
+static void
+interi(void)
+{
+	int n;
+	char a[10];
+
+	if(nbrecv(csc, nil) > 0){
+		interskip();
+		return;
+	}
+	n = sprint(a, "%d", iri);
+	pictxt((37 - n * 2) * 8, iry, a);
+	sfx(Sendb1);
+	out();
+	iri += 10;
+	if(iri == *irp + 10){
+		ql[Linterm].s[0].dt = *irp == 100 || *irp == 0 ? 30 : 0;
+		qsp->q = ql+Linterm;
+	}else if(iri >= *irp)
+		iri = *irp;
+}
+
+static void
+interp(void)
+{
+	int m;
+	char a[10];
+
+	m = sprint(a, "%d", iri * 500);
+	pictxt((18 - m) * 16, 56, a);
+	sfx(Sendb1);
+	out();
+	iri += 50;
+	if(iri == *irp + 50){
+		sfx(Sendb2);
+		iri = 0;
+		irb += iri * 500;
+		iry = 112;
+		irp++;
+		qsp->s[0].f = interi;
+	}else if(iri >= *irp)
+		iri = *irp;
+}
+
+static void
 inter(void)
 {
+	static int wlpar[] = {
+		90, 120, 120, 210, 180, 180, 150, 150, 0, 0,
+		90, 210, 180, 120, 240, 360, 60, 180, 0, 0,
+		90, 90, 150, 150, 210, 150, 120, 360, 0, 0,
+		120, 120, 90, 60, 270, 210, 120, 270, 0, 0,
+		150, 90, 150, 150, 240, 180, 270, 210, 0, 0,
+		390, 240, 270, 360, 300, 330, 330, 510, 0, 0
+	},
+	sdpar[] = {
+		 90, 210, 165, 210, 0, 270, 195, 165, 285, 0,
+		 390, 270, 165, 270, 360, 0, 360, 0, 0, 0
+	};
+	int s, p;
+	char a[10];
+
+	put(0, 0, Vw, Vhud, 0x7f);
+	pic(0, 16, pict[Pguy]);
+	pictxt(112, 16, "FLOOR\nCOMPLETED");
+	sprint(a, "%d", ver<SDM ? gm.map % 10 + 1 : gm.map + 1);
+	pictxt(208, 16, a);
+	pictxt(112, 56, "BONUS     0");
+	pictxt(128, 80, "TIME");
+	pictxt(128, 96, " PAR");
+	p = ver < SDM ? wlpar[gm.map] : sdpar[gm.map];
+	sprint(a, "%02d:%2d", p / 60, p % 60);
+	pictxt(208, 96, a);
+	pictxt(72, 112, "KILL RATIO    %");
+	pictxt(40, 128, "SECRET RATIO    %");
+	pictxt(8, 144, "TREASURE RATIO    %");
+	s = gm.tc / Tb;
+	if(s > 99 * 60)
+		s = 99 * 60;
+	sprint(a, "%02d:%02d", s / 60, s % 60);
+	pictxt(208, 80, a);
+	gm.eptm += s;
+	memset(irr, 0, sizeof irr);
+	s = p - s;
+	irr[0] = s > 0 ? s : 0;
+	if(gm.ktot)
+		irr[1] = gm.kp * 100 / gm.ktot;
+	if(gm.stot)
+		irr[2] = gm.sp * 100 / gm.stot;
+	if(gm.ttot)
+		irr[3] = gm.tp * 100 / gm.ttot;
+	gm.epk += irr[1];
+	gm.eps += irr[2];
+	gm.ept += irr[3];
+	irp = irr;
+	iri = 0;
+	irb = 0;
+	if(irr[0] == 0){
+		ql[Linteri].s[0].f = interi;
+		iry = 112;
+		irp++;
+	}else
+		ql[Linteri].s[0].f = interp;
+	ql[Linteri].q = ql+Linteri;
+	mus(Minter);
 }
 
 static void
 gcont(void)
 {
+	kb = 0;
+	mΔx = mΔy = 0;
+	mb = 0;
+	if(!gm.demo){
+		kbon++;
+		grab(1);
+	}
 	step = gstep;
-	gm.end = 0;
 }
 
 static void
@@ -455,7 +1222,7 @@
 camtxt2(void)
 {
 	camwarp();
-	mtc = 32;
+	qtc = 32;
 	render();
 	fizzop(-1, 1);
 	put(0, 0, Vw, Vhud, 0x7f);
@@ -469,9 +1236,49 @@
 }
 
 static void
+cont(void)
+{
+	view();
+	render();
+	mapmus();
+}
+
+static void
+ingam(void)
+{
+	greset();
+	view();
+}
+
+static void
+psych2(void)
+{
+	int n;
+
+	n = (qtc - 1) * 16 + 6;
+	if(n > 214 || nbrecv(csc, nil) > 0){
+		n = 214;
+		qp = qsp->e - 1;
+		qtc = 0;
+	}
+	put(53, 101, n, 2, 0x37);
+	put(53, 101, n-1, 1, 0x32);
+	out();
+}
+
+static void
+psych(void)
+{
+	ingam();
+	mapmus();
+	put(0, 0, Vw, Vhud, 0x7f);
+	pic(48, 56, pict[Ppsyched]);
+}
+
+static void
 indem(void)
 {
-	initg(0, *demd++);
+	ginit(*demd++, -1, 0);
 	if(demd >= epis)
 		demd = dems;
 	view();
@@ -478,6 +1285,15 @@
 }
 
 static void
+fixedw(char *s)
+{
+	char c;
+
+	while(c = *s, c != 0)
+		*s++ = c - '0' + 129;
+}
+
+static void
 score(void)
 {
 	int x, y;
@@ -490,25 +1306,22 @@
 	pic(32, 68, pict[Pname]);
 	pic(160, 68, pict[Plvl]);
 	pic(224, 68, pict[Phigh]);
-
 	fnt = fnts;
-	for(s=sc, y=76; s<sc+nelem(sc); s++, y+=16){
+	for(s=scs, y=76; s<scs+nelem(scs); s++, y+=16){
 		txt(32, y, s->name, 0xf);
-
 		sprint(a, "%d", s->lvl);
 		fixedw(a);
 		x = 176 - txtw(a);
 		if(ver == WL6){
-			sprint(b, "E%d/L", s->ep+1);
-			x += txt(x-6, y, b, 0xf) - 6;
+			sprint(b, "E%d/L", s->ep + 1);
+			x += txt(x - 6, y, b, 0xf) - 6;
 		}
 		txt(x, y, a, 0xf);
-
 		sprint(a, "%d", s->n);
 		fixedw(a);
 		txt(264 - txtw(a), y, a, 0xf);
 	}
-	if(mp == ml+Lmscore)
+	if(qsp != ql+Lscore)
 		mus(Mroster);
 }
 static void
@@ -520,11 +1333,9 @@
 
 	mclear();
 	pic(0, 0, pict[Pscores]);
-
 	fnt = fnts+1;
-	for(s=sc, y=76; s<sc+nelem(sc); s++, y+=16){
-		txt(16, y, s->name, 0x13);
-
+	for(s=scs, y=76; s<scs+nelem(scs); s++, y+=16){
+		txt(16, y, s->name, DIrhi);
 		if(s->lvl == 21)
 			pic(176, y-1, pict[Pspear]);
 		else{
@@ -531,15 +1342,25 @@
 			sprint(a, "%d", s->lvl);
 			txt(194 - txtw(a), y, a, 0xf);
 		}
-
 		sprint(a, "%d", s->n);
 		txt(292 - txtw(a), y, a, 0xf);
 	}
-	if(mp == ml+Lmscore)
+	if(qsp != ql+Lscore)
 		mus(Maward);
 }
 
 static void
+high(void)
+{
+	if(ver < SDM)
+		score();
+	else
+		sdscore();
+	inctl();
+	grab(0);
+}
+
+static void
 creds(void)
 {
 	pic(0, 0, pict[Pcreds]);
@@ -573,179 +1394,354 @@
 	threadexitsall(nil);
 }
 
-static Item ictl[] = {
-	{"New Game", 0x17},
-	{"Sound", 0x17},
-	{"Control", 0x17},
-	{"Load Game", 0x17},
-	{"Save Game", 0x17},
-	{"Change View", 0x17},
-	{"View Scores", 0x17, ml+Lmscore},
-	{"Back to Demo", 0x17, ml+Lesc},
-	{"Quit", 0x17, ml+Lquit}
-};
-
-static Col fblk, fmenu = { 0xae, 0, 0 };
-static Seq *mqp,
-	introq[] = {{30, fadein}, {7*Tb, skiploop}, {30, fadeout}},
-	titleq[] = {{30, fadein}, {15*Tb, skiploop}, {30, fadeout}},
-	loopq[] = {{30, fadein}, {10*Tb, skiploop}, {30, fadeout}},
-	scoreq[] = {{30, fadein}, {10*Tb, skiploop}, {30, fadeout}},
-	demoq[] = {{30, fadein}, {1, demo}, {41, fizz}, {1, gcont}, {30, fadeout}},
-	camq[] = {{100, nil}, {144, fizz}, {0, camtxt}, {300, skipstep}, {0, camtxt2}, {41, fizz}, {1, gcont}, {100, nil}, {30, fadeout}},
-	interq[] = {{30, fadein}, {0, iscore}, {30, fadeout}},
-	winq[] = {{30, fadein}, {0, iwin}, {30, fadeout}},
-	decq[] = {{30, fadeout}},
-	inctlq[] = {{10, fadein}},
+static Sp
+	loadq[] = {{30, fadeout}},
+	introq[] = {{30, fadein}, {7*Tb, skip}, {30, fadeout}},
+	titleq[] = {{30, fadein}, {15*Tb, skip}, {30, fadeout}},
+	loopq[] = {{30, fadein}, {10*Tb, skip}, {30, fadeout}},
+	scoreq[] = {{30, fadein}, {10*Tb, skip}, {30, fadeout}},
+	pantsq[] = {{30, fadein}, {1, bwait}, {30, fadeout}},
+	psychq[] = {{30, fadein}, {14, psych2}, {70, nbwait}, {30, fadeout}},
+	gamq[] = {{30, fadein}, {1, game}, {41, fizz}, {1, gcont}, {30, fadeout}},
+	gamsq[] = {{1, game}, {41, fizz}, {1, gcont}, {30, fadeout}},
+	contq[] = {{30, fadein}, {1, gcont}, {30, fadeout}},
+	msgq[] = {{1, bwait}, {1, gcont}, {30, fadeout}},
+	gcontq[] = {{1, gcont}, {30, fadeout}},
+	dieq[] = {{1, dieturn}, {144, fizz}, {100, nbwait}, {1, swait}},
+	fdieq[] = {{0, ctl}, {1, blink}, {30, fadeout}, {1, mend2}, {30, fadein}},
+	camq[] = {{100, nil}, {144, fizz}, {0, camtxt}, {300, nbwait}, {0, camtxt2}, {41, fizz}, {1, gcont}, {100, nil}, {0, stopmus}, {30, fadeout}},
+	spq[] = {{1, swait}, {0, mapmus}, {1, gcont}, {30, fadeout}},
+	interq[] = {{1, swait}, {30, fadein}},
+	interiq[] = {{0, nil}, {1, interb}},
+	intermq[] = {{0, nil}, {1, interm}, {1, interb}},
+	interwq[] = {{1, interw}},
+	intereq[] = {{0, nextmap}, {30, fadeout}},
+	sinterq[] = {{1, swait}, {30, fadein}, {0, siscore}, {1, sinterw}, {0, nextmap}, {30, fadeout}},
+	sdmq[] = {{1, bwait}, {30, fadeout}},
+	colpq[] = {{300, fadeout}},
+	colp2q[] = {{30, fadein}, {2*Tb, nil}, {0, colp}, {105, nil}, {0, colp}, {105, nil}, {0, colp}, {3*Tb, nil}},
+	colp3q[] = {{5, fadeout}},
+	wonq[] = {{1, swait}, {30, fadein}, {1, bwait}},
+	won2q[] = {{0, nil}, {30, fadeout}},
+	sdeq[] = {{30, fadein}, {1, bwait}, {30, fadeout}},
+	presq[] = {{30, fadein}, {0, pres2}, {10*Tb, nbwait}, {0, pres3}, {10*Tb, nbwait}, {30, fadeout}},
+	highq[] = {{30, fadein}, {1, bwait}, {30, fadeout}},
+	escq[] = {{10, fadeout}},
+	toctlq[] = {{10, fadein}},
 	ctlq[] = {{0, blink}, {70, cwalk}, {0, blink}, {8, cwalk}},
+	slq[] = {{1, slider}},
 	curq[] = {{8, nil}, {0, cursfx}},
-	escq[] = {{10, fadeout}},
-	backq[] = {{10, fadeout}, {0, ctl}, {10, fadein}},
-	waitq[] = {{1, skiploop}},
-	swaitq[] = {{1, swait}},
-	ackq[] = {{1, ack}},
-	mscoreq[] = {{10, fadeout}, {0, score}, {10, fadein}},
-	pantsq[] = {{30, fadeout}, {0, pants}, {30, fadein}},
-	quitq[] = {{0, blink}, {10, ask}},
-	mexitq[] = {{10, fadeout}},
+	togq[] = {{1, toggle}},
+	mscoreq[] = {{10, fadein}, {1, bwait}, {10, fadeout}},
+	quitq[] = {{0, pblink}, {10, ask}},
+	ackq[] = {{1, bwait}},
 	exitq[] = {{1, exit}};
-
-static Menu *mp, ml[] = {
-	[Lload] {nil, decq, decq+nelem(decq), ml+Lintro, &fblk},
-	[Lintro] {intro, introq, introq+nelem(introq), ml+Ltitle, &fblk},
-	[Ltitle] {title, titleq, titleq+nelem(titleq), ml+Lcreds, &fblk},
-	[Lcreds] {creds, loopq, loopq+nelem(loopq), ml+Lscore, &fblk},
-	[Lscore] {score, loopq, loopq+nelem(loopq), ml+Ldemo, &fblk},
-	[Ldemo] {indem, demoq, demoq+nelem(demoq), nil, &fblk},
-	[Lcam] {nil, camq, camq+nelem(camq), nil},
-	[Linter] {inter, interq, interq+nelem(interq), nil, &fblk},
-	[Lwin] {win, winq, winq+nelem(winq), nil, &fblk},
-	[Ldecay] {nil, decq, decq+nelem(decq), ml+Linctl},
-	[Linctl] {inctl, inctlq, inctlq+nelem(inctlq), ml+Lctl, &fblk},
-	[Lctl] {ctl, ctlq, ctlq+nelem(ctlq), ml+Lctl, nil, ictl, ictl+nelem(ictl)},
-	[Lcur] {nil, curq, curq+nelem(curq), ml+Lctl},
-	[Lesc] {nil, escq, escq+nelem(escq), ml+Ltitle, &fblk},
-	[Lback] {nil, backq, backq+nelem(backq), ml+Lctl, &fmenu},
-	[Lwait] {nil, waitq, waitq+nelem(waitq), ml+Lwait},
-	[Lsfxwait] {nil, swaitq, swaitq+nelem(swaitq), ml+Lsfxwait},
-	[Lack] {nil, ackq, ackq+nelem(ackq), ml+Lack},
-	[Lmscore] {nil, mscoreq, mscoreq+nelem(mscoreq), ml+Lack, &fmenu},
-	[Lpants] {nil, pantsq, pantsq+nelem(pantsq), ml+Lwait, &fblk},
-	[Lquit] {quit, quitq, quitq+nelem(quitq), ml+Lquit},
-	[Lmexit] {nil, mexitq, mexitq+nelem(mexitq), ml+Lexit, &fmenu},
+static Seq ql[] = {
+	[Lload] {nil, loadq, loadq+nelem(loadq), ql+Lintro, &fblk},
+	[Lintro] {intro, introq, introq+nelem(introq), ql+Ltitle, &fblk},
+	[Lftitle] {nil, escq, escq+nelem(escq), ql+Ltitle, &ftra},
+	[Ltitle] {title, titleq, titleq+nelem(titleq), ql+Lcreds, &fblk},
+	[Lcreds] {creds, loopq, loopq+nelem(loopq), ql+Lscore, &fblk},
+	[Lscore] {score, loopq, loopq+nelem(loopq), ql+Ldemo, &fblk},
+	[Lfpants] {nil, loadq, loadq+nelem(loadq), ql+Lpants, &fblk},
+	[Lpants] {pants, pantsq, pantsq+nelem(pantsq), ql+Ltoctl, &fblk},
+	[Ldemo] {indem, gamq, gamq+nelem(gamq), nil, &fblk},
+	[Lpsych] {psych, psychq, psychq+nelem(psychq), ql+Lgame, &fblk},
+	[Lgame] {view, gamq, gamq+nelem(gamq), nil, &fblk},
+	[Lretry] {ingam, gamsq, gamsq+nelem(gamsq), nil},
+	[Lmsg] {nil, msgq, msgq+nelem(msgq), nil, &fblk},
+	[Lcont1] {nil, escq, escq+nelem(escq), ql+Lcont2, &ftra},
+	[Lcont2] {cont, contq, contq+nelem(contq), nil, &fblk},
+	[Lgcont] {cont, gcontq, gcontq+nelem(gcontq), nil, &fblk},
+	[Lfdie] {nil, fdieq, fdieq+nelem(fdieq), ql+Ldie, &fblk},
+	[Ldie] {die, dieq, dieq+nelem(dieq), nil},
+	[Ldie2] {nil, loadq, loadq+nelem(loadq), ql+Lhigh, &fblk},
+	[Lhigh] {high, highq, highq+nelem(highq), ql+Ltitle, &fblk},
+	[Lcam] {nil, camq, camq+nelem(camq), nil, &fblk},
+	[Lspear] {spshunt, spq, spq+nelem(spq), nil, &fblk},
+	[Linter] {inter, interq, interq+nelem(interq), ql+Linteri, &fblk},
+	[Linteri] {nil, interiq, interiq+nelem(interiq), nil},
+	[Linterm] {nil, intermq, intermq+nelem(intermq), ql+Linteri},
+	[Linterw] {nil, interwq, interwq+nelem(interwq), ql+Linterw},
+	[Lintere] {nil, intereq, intereq+nelem(intereq), ql+Lpsych, &fblk},
+	[Lsinter] {sinter, sinterq, sinterq+nelem(sinterq), ql+Lpsych, &fblk},
+	[Lsdmend] {sdmend, sdmq, sdmq+nelem(sdmq), ql+Lhigh, &fblk},
+	[Lcolp] {nil, colpq, colpq+nelem(colpq), ql+Lcolp2, &focl},
+	[Lcolp2] {incolp, colp2q, colp2q+nelem(colp2q), ql+Lcolp3, &ficl},
+	[Lcolp3] {nil, colp3q, colp3q+nelem(colp3q), ql+Lwon, &fecl},
+	[Lwon] {won, wonq, wonq+nelem(wonq), ql+Lwon2, &fblk},
+	[Lwon2] {nil, won2q, won2q+nelem(won2q), ql+Lhigh, &fblk},
+	[Lsdepi] {sdepi, sdeq, sdeq+nelem(sdeq), ql+Lpres, &fblk},
+	[Lpres] {pres1, presq, presq+nelem(presq), ql+Lroll1, &fblk},
+	[Lroll1] {roll, sdeq, sdeq+nelem(sdeq), ql+Lroll2, &fblk},
+	[Lroll2] {roll, sdeq, sdeq+nelem(sdeq), ql+Lroll3, &fblk},
+	[Lroll3] {roll, sdeq, sdeq+nelem(sdeq), ql+Lroll4, &fblk},
+	[Lroll4] {roll, sdeq, sdeq+nelem(sdeq), ql+Lroll5, &fblk},
+	[Lroll5] {roll, sdeq, sdeq+nelem(sdeq), ql+Lroll6, &fblk},
+	[Lroll6] {roll, sdeq, sdeq+nelem(sdeq), ql+Lroll7, &fblk},
+	[Lroll7] {roll, sdeq, sdeq+nelem(sdeq), ql+Lhigh, &fblk},
+	[Ldecay] {nil, loadq, loadq+nelem(loadq), ql+Ltoctl},
+	[Ltoctl] {ctl, toctlq, toctlq+nelem(toctlq), ql+Lctl, &ftra},
+	[Lftoctl] {nil, escq, escq+nelem(escq), ql+Lmtoctl, &fctl},
+	[Lmtoctl] {ctl, toctlq, toctlq+nelem(toctlq), ql+Lctl, &fctl},
+	[Lctl] {ctl, ctlq, ctlq+nelem(ctlq), ql+Lctl},
+	[Lcur] {nil, curq, curq+nelem(curq), ql+Lctl},
+	[Lftonew] {nil, escq, escq+nelem(escq), ql+Lmtonew, &fctl},
+	[Lmtonew] {newgame, toctlq, toctlq+nelem(toctlq), ql+Lnctl, &fctl},
+	[Lnewgame] {newgame, toctlq, toctlq+nelem(toctlq), ql+Lnctl, &fctl},
+	[Lnctl] {newgame, ctlq, ctlq+nelem(ctlq), ql+Lnctl},
+	[Ldenied] {denied, ackq, ackq+nelem(ackq), ql+Lnctl},
+	[Ldifc1] {nil, escq, escq+nelem(escq), ql+Ldifc2, &fctl},
+	[Ldifc2] {difc, toctlq, toctlq+nelem(toctlq), ql+Ldifc3, &fctl},
+	[Ldifc3] {difc, ctlq, ctlq+nelem(ctlq), ql+Ldifc3},
+	[Ldifc4] {nil, escq, escq+nelem(escq), ql+Ldifc5, &fctl},
+	[Ldifc5] {setdifc, loadq, loadq+nelem(loadq), ql+Lpsych, &fblk},
+	[Lfsnd] {nil, escq, escq+nelem(escq), ql+Lmsnd, &fctl},
+	[Lmsnd] {snd, toctlq, toctlq+nelem(toctlq), ql+Lsctl, &fctl},
+	[Lsctl] {snd, ctlq, ctlq+nelem(ctlq), ql+Lsctl},
+	[Lsndtog] {nil, togq, togq+nelem(togq), ql+Lsctl},
+	[Lfin] {nil, escq, escq+nelem(escq), ql+Lmin, &fctl},
+	[Lmin] {in, toctlq, toctlq+nelem(toctlq), ql+Lictl, &fctl},
+	[Lictl] {in, ctlq, ctlq+nelem(ctlq), ql+Lictl},
+	[Lintog] {nil, togq, togq+nelem(togq), ql+Lictl},
+	[Lfsav] {nil, escq, escq+nelem(escq), ql+Lmsav, &fctl},
+	[Lmsav] {sav, toctlq, toctlq+nelem(toctlq), ql+Lsvctl, &fctl},
+	[Lsvctl] {sav, ctlq, ctlq+nelem(ctlq), ql+Lsvctl},
+	[Lflod] {nil, escq, escq+nelem(escq), ql+Lmlod, &fctl},
+	[Lmlod] {sav, toctlq, toctlq+nelem(toctlq), ql+Lldctl, &fctl},
+	[Lldctl] {sav, ctlq, ctlq+nelem(ctlq), ql+Lldctl},
+	[Lfsens] {nil, escq, escq+nelem(escq), ql+Lmsens, &fctl},
+	[Lmsens] {sens, toctlq, toctlq+nelem(toctlq), ql+Lsectl, &fctl},
+	[Lsectl] {sens, slq, slq+nelem(slq), ql+Lsectl},
+	[Lfvw] {nil, escq, escq+nelem(escq), ql+Lmvw, &fctl},
+	[Lmvw] {mvw, toctlq, toctlq+nelem(toctlq), ql+Lvwctl, &fctl},
+	[Lvwctl] {mvw, slq, slq+nelem(slq), ql+Lvwctl},
+	[Lfmscore] {nil, escq, escq+nelem(escq), ql+Lmscore, &fctl},
+	[Lmscore] {score, mscoreq, mscoreq+nelem(mscoreq), ql+Lmtoctl, &fctl},
+	[Lend] {mend, quitq, quitq+nelem(quitq), ql+Lend},
+	[Lcurgame] {curgame, quitq, quitq+nelem(quitq), ql+Lcurgame},
+	[Lquit] {quit, quitq, quitq+nelem(quitq), ql+Lquit},
+	[Lmexit] {nil, escq, escq+nelem(escq), ql+Lexit, &fctl},
 	[Lexit] {nil, exitq, exitq+nelem(exitq)}
 };
 
 static void
-dend(void)
+initseqs(void)
 {
-	gm.demo = gm.record = 0;
-	pal = pals[Cfad];
+	Item *i;
+
+	mclear = wlmclear;
+	stripe = wlstripe;
+	quits = ends;
+	if(ver == WL1){
+		for(i=ml[LMnew].s+2; i<ml[LMnew].e; i+=2){
+			i->c = DIeps;
+			i->q = ql+Ldenied;
+		}
+	}
+	if(ver >= SDM){
+		mclear = sdmclear;
+		stripe = sdstripe;
+		ql[Ltitle].init = sdtitle;
+		ql[Lscore].init = sdscore;
+		ql[Lmscore].init = sdscore;
+		ql[Lwon].f = &ficl;
+		ql[Lwon2].q = ql+Lsdepi;
+		fctl.c = (Col){0, 0, 0xce};
+		mcol[DMbg] = 0x9d;
+		quits += nelem(ends)/2;
+	}
+	mcol[DMoff] = mcol[DMbg] ^ 6;
+	mcol[DMbrd] = mcol[DMbg] ^ 4;
+	mcol[DMbrd2] = mcol[DMbg] ^ 14;
+	ml[LMsnd].s[1].c = mcol[DMoff];
+	ml[LMsnd].s[6].c = mcol[DMoff];
+}
+
+static void
+cfg(void)
+{
+	muson = sfxon = pcmon = 1;
+	grabon++;
+	autorun = 0;
+	msense = 5;
+	vwsize = 15;
+	/* fs.c: load config file and read values */
+	if(msense < 0)
+		msense = 0;
+	else if(msense > 9)
+		msense = 9;
+	if(vwsize < 4)
+		vwsize = 4;
+	else if(vwsize > 19)
+		vwsize = 19;
+	setvw();
+}
+
+static void
+sqend(void)
+{
+	if(!gm.demo && !gm.record)
+		return;
+	qsp->q = gm.end == EDkey ? ql+Ltoctl : ql+Ltitle;
 	if(demf != nil){
 		free(demf);
 		demf = nil;
 		demd = dems;
 		if(demexit)
-			mp->m = ml+Lexit;
+			qsp->q = ql+Lexit;
 	}
+	gm.demo = gm.record = 0;
 }
+static void
+edfizz(void)
+{
+	fizzop(-1, 1);
+	put((Vw - vw.dx) / 2, (Vhud - vw.dy) / 2, vw.dx, vw.dy,
+		qsp == ql+Lretry ? 4 : 0);
+	out();
+}
+static void
+edcam(void)
+{
+	gm.won++;
+	out();
+	fizzop(0x7f, 0);
+	reset(ql+Lcam);
+	qsp->q = ql+Lwon;
+}
+static void
+eddie(void)
+{
+	u32int *p;
+
+	p = pal;
+	reset(ql+Ldie);
+	pal = p;
+	if(gm.lives >= 0)
+		qsp->q = ql+Lretry;
+	else
+		qsp->q = ql+Ldie2;
+}
+static void
+edup(void)
+{
+	gm.keys = 0;
+	hudk();
+	if(ver < SDM && gm.map % 10 == 9 || ver >= SDM
+	&& (gm.map == 4 || gm.map == 9 || gm.map == 15 || gm.map >= 17)){
+		gm.com = GMret;
+		qsp->q = ql+Lsinter;
+	}else if(gm.end == EDsetec){
+		gm.com = GMsetec;
+		qsp->q = ql+Linter;
+	}else{
+		gm.com = GMup;
+		qsp->q = ql+Linter;
+	}
+}
+static void
+edspear(void)
+{
+	reset(ql+Lspear);
+	qp->dt = pcmon ? 150 : 1;
+	qp->f = pcmon ? nil : swait;
+}
+static void
+edwon(void)
+{
+	stopmus();
+	qsp->q = ql+Lwon;
+	if(ver == SOD)
+		reset(ql+Lcolp);
+}
 void
 gend(void)
 {
+	kbon = 0;
 	switch(gm.end){
-	case EDfizz:
-		fizzop(-1, 1);
-		put((Vw - vw.dx) / 2, (Vhud - vw.dy) / 2, vw.dx, vw.dy, 0);
-		out();
-		break;
-	enddem:
-	case EDdem:
-		mp->m = ml+Ltitle;
-		dend();
-		break;
-	case EDcam:
-		if(gm.record || gm.demo)
-			scalspr(SPdemo, vw.dx/2, vw.dy+1);
-		out();
-		fizzop(0x7f, 0);
-		reset(ml+Lcam);
-		mp->m = gm.demo || gm.record ? ml+Ltitle : ml+Lwin;
-		break;
-	case EDcam2:
-		if(gm.demo || gm.record)
-			dend();
-		else
-			pal = pals[Cfad];
-		scalspr(SPcam, vw.dx/2, vw.dy+1);
-		break;
-	case EDkey:
-		mp->m = ml+Linctl;
-		dend();
-		break;
-	case EDdie:
-		if(gm.demo || gm.record)
-			goto enddem;
-		break;
-	case EDup:
-	case EDsetec:
-	case EDwon:
-		if(gm.demo || gm.record)
-			goto enddem;
-		mp->m = ml+Lsfxwait;
-		break;
+	case EDfizz: edfizz(); break;
+	case EDcam: edcam(); break;
+	case EDcam2: scalspr(SPcam, vw.dx / 2, vw.dy + 1); sqend(); break;
+	case EDdem: sqend(); break;
+	case EDkey: qsp->q = ql+Ltoctl; sqend(); break;
+	case EDdie: eddie(); break;
+	case EDup: /* wet floor */
+	case EDsetec: edup(); sqend(); break;
+	case EDspear: edspear(); break;
+	case EDwon: edwon(); sqend(); break;
+	case EDmsg:;
 	}
-	mtc = 0;
-	step = mstep;
+	gm.end = 0;
+	pal = pals[Cfad];
+	qtc = 0;
+	step = qstep;
 }
 
+int
+quickkey(Rune r)
+{
+	switch(r){
+	case KF|1:
+	case KF|2:
+	case KF|3:
+	case KF|4:
+	case KF|5:
+	case KF|6:
+	case KF|7:
+	case KF|8:
+	case KF|9:
+	default: return 0;
+	case KF|10: reset(ql+Lquit); qesc = ql+Lgcont; break;
+	}
+	gm.end = EDmsg;
+	grab(0);
+	return 1;
+}
+
 void
-mstep(void)
+qstep(void)
 {
-	Menu *m;
-	Seq *q;
+	Sp *p;
 
 rep:
-	m = mp;
-	q = mqp;
-	mtc += Δtc;
-	if(q->f != nil)
-		q->f();
-	if(mtc >= q->dt){
-		if(++mqp == m->qe)
-			reset(m->m);
-		mtc = 0;
+	p = qp;
+	qtc += Δtc;
+	if(p->f != nil)
+		p->f();
+	if(p != qp)
+		return;
+	if(qtc >= p->dt){
+		if(++qp == qsp->e){
+			reset(qsp->q);
+			return;
+		}
+		qtc = 0;
 	}
-	if(q->dt == 0)
+	if(p->dt == 0)
 		goto rep;
 }
 
 void
-init(char *f)
+init(char *f, int m, int d)
 {
-	tab();
-	mclear = wlmclear;
-	stripe = wlstripe;
-	quits = ends;
-	if(ver >= SDM){
-		mclear = sdmclear;
-		stripe = sdstripe;
-		ml[Ltitle].init = sdtitle;
-		ml[Lscore].init = sdscore;
-		mscoreq[1].f = sdscore;
-		fmenu = (Col){0, 0, 0xce};
-		mcol[Dbg] = 0x9d;
-		quits += nelem(ends)/2;
-	}
-	mcol[Doff] = mcol[Dbg] ^ 6;
-	mcol[Dbrd] = mcol[Dbg] ^ 4;
-	mcol[Dbrd2] = mcol[Dbg] ^ 14;
+	cfg();
+	initseqs();
+	inctl();
 	demd = dems;
-	reset(ml+Lload);
-	setvw(15);
+	reset(ql+Lload);
+	if(m != -1){
+		if(d > GDhard)
+			d = GDhard;
+		qsp->q = ql+Lpsych;
+		ginit(nil, m, d);
+		ingctl();
+		return;
+	}
 	if(f != nil){
 		demf = demof(f);
 		demd = (uchar **)&demf;
-		mp->m = ml+Ldemo;
+		qsp->q = ql+Ldemo;
 	}
 	mus(ver<SDM ? Mintro : Mtower);
 }
--- a/in.c
+++ /dev/null
@@ -1,182 +1,0 @@
-#define	MaxPlayers	4
-#define	NumCodes	128
-
-// 	Stuff for the mouse
-#define	MReset		0
-#define	MButtons	3
-#define	MDelta		11
-
-#define	MouseInt	0x33
-#define	Mouse(x)	_AX = x,geninterrupt(MouseInt)
-
-typedef	enum		{
-						demo_Off,demo_Record,demo_Playback,demo_PlayDone
-					} Demo;
-typedef	enum		{
-						ctrl_Keyboard,
-							ctrl_Keyboard1 = ctrl_Keyboard,ctrl_Keyboard2,
-						ctrl_Mouse
-					} ControlType;
-typedef	enum		{
-						motion_Left = -1,motion_Up = -1,
-						motion_None = 0,
-						motion_Right = 1,motion_Down = 1
-					} Motion;
-typedef	enum		{
-						dir_North,dir_NorthEast,
-						dir_East,dir_SouthEast,
-						dir_South,dir_SouthWest,
-						dir_West,dir_NorthWest,
-						dir_None
-					} Direction;
-typedef	struct		{
-						int		button0,button1,button2,button3;
-						s16int			x,y;
-						Motion		xaxis,yaxis;
-						Direction	dir;
-					} CursorInfo;
-typedef	CursorInfo	ControlInfo;
-typedef	struct		{
-						u8int	button0,button1,
-									upleft,		up,		upright,
-									left,				right,
-									downleft,	down,	downright;
-					} KeyboardDef;
-
-// Function prototypes
-#define	IN_KeyDown(code)	(Keyboard[(code)])
-#define	IN_ClearKey(code)	{Keyboard[code] = false;\
-							if (code == LastScan) LastScan = sc_None;}
-								
-int		Keyboard[NumCodes];
-KeyboardDef	KbdDefs = {0x1d,0x38,0x47,0x48,0x49,0x4b,0x4d,0x4f,0x50,0x51};
-ControlType	Controls[MaxPlayers];
-Demo		DemoMode = demo_Off;
-u8int _seg	*DemoBuffer;
-u16int		DemoOffset,DemoSize;
-
-static	Direction	DirTable[] =		// Quick lookup for total direction
-{
-						dir_NorthWest,	dir_North,	dir_NorthEast,
-						dir_West,		dir_None,	dir_East,
-						dir_SouthWest,	dir_South,	dir_SouthEast
-					};
-
-void
-IN_ReadControl(s16int player,ControlInfo *info)
-{
-			int		realdelta;
-			u8int		dbyte;
-			u16int		buttons;
-			s16int			dx,dy;
-			Motion		mx,my;
-			ControlType	type;
-register	KeyboardDef	*def;
-
-	dx = dy = 0;
-	mx = my = motion_None;
-	buttons = 0;
-
-	if (DemoMode == demo_Playback)
-	{
-		dbyte = DemoBuffer[DemoOffset + 1];
-		my = (dbyte & 3) - 1;
-		mx = ((dbyte >> 2) & 3) - 1;
-		buttons = (dbyte >> 4) & 3;
-
-		if (!(--DemoBuffer[DemoOffset]))
-		{
-			DemoOffset += 2;
-			if (DemoOffset >= DemoSize)
-				DemoMode = demo_PlayDone;
-		}
-
-		realdelta = false;
-	}
-	else if (DemoMode == demo_PlayDone)
-		Quit("Demo playback exceeded");
-	else
-	{
-		switch (type = Controls[player])
-		{
-		case ctrl_Keyboard:
-			def = &KbdDefs;
-
-			if (Keyboard[def->upleft])
-				mx = motion_Left,my = motion_Up;
-			else if (Keyboard[def->upright])
-				mx = motion_Right,my = motion_Up;
-			else if (Keyboard[def->downleft])
-				mx = motion_Left,my = motion_Down;
-			else if (Keyboard[def->downright])
-				mx = motion_Right,my = motion_Down;
-
-			if (Keyboard[def->up])
-				my = motion_Up;
-			else if (Keyboard[def->down])
-				my = motion_Down;
-
-			if (Keyboard[def->left])
-				mx = motion_Left;
-			else if (Keyboard[def->right])
-				mx = motion_Right;
-
-			if (Keyboard[def->button0])
-				buttons += 1 << 0;
-			if (Keyboard[def->button1])
-				buttons += 1 << 1;
-			realdelta = false;
-			break;
-		case ctrl_Mouse:
-			INL_GetMouseDelta(&dx,&dy);
-			buttons = INL_GetMouseButtons();
-			realdelta = true;
-			break;
-		}
-	}
-
-	if (realdelta)
-	{
-		mx = (dx < 0)? motion_Left : ((dx > 0)? motion_Right : motion_None);
-		my = (dy < 0)? motion_Up : ((dy > 0)? motion_Down : motion_None);
-	}
-	else
-	{
-		dx = mx * 127;
-		dy = my * 127;
-	}
-
-	info->x = dx;
-	info->xaxis = mx;
-	info->y = dy;
-	info->yaxis = my;
-	info->button0 = buttons & (1 << 0);
-	info->button1 = buttons & (1 << 1);
-	info->button2 = buttons & (1 << 2);
-	info->button3 = buttons & (1 << 3);
-	info->dir = DirTable[((my + 1) * 3) + (mx + 1)];
-
-	if (DemoMode == demo_Record)
-	{
-		// Pack the control info into a byte
-		dbyte = (buttons << 4) | ((mx + 1) << 2) | (my + 1);
-
-		if
-		(
-			(DemoBuffer[DemoOffset + 1] == dbyte)
-		&&	(DemoBuffer[DemoOffset] < 255)
-		)
-			(DemoBuffer[DemoOffset])++;
-		else
-		{
-			if (DemoOffset || DemoBuffer[DemoOffset])
-				DemoOffset += 2;
-
-			if (DemoOffset >= DemoSize)
-				Quit("Demo buffer overflow");
-
-			DemoBuffer[DemoOffset] = 1;
-			DemoBuffer[DemoOffset + 1] = dbyte;
-		}
-	}
-}
--- a/inter.c
+++ b/inter.c
@@ -1,807 +1,15 @@
-// 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 (s16int palette, s16int screen)
-{
-	CA_CacheScreen (screen);
-	VW_UpdateScreen ();
-	VL_FadeIn(0,255,grsegs[palette],30);
-	IN_ClearKeysDown ();
-	IN_Ack ();
-	VW_FadeOut ();
-}
-
-
-void EndSpear(void)
-{
-	EndScreen (Eend1, Pend1);
-
-	CA_CacheScreen (Pend1+2);
-	VW_UpdateScreen ();
-	VL_FadeIn(0,255,Eend1+2,30);
-	fontnumber = 0;
-	fontcolor = 0xd0;
-	WindowX = 0;
-	WindowW = 320;
-	PrintX = 0;
-	PrintY = 180;
-	US_CPrint ("We owe you a great debt, Mr. Blazkowicz.\n");
-	US_CPrint ("You have served your country well.");
-	VW_UpdateScreen ();
-	IN_StartAck ();
-	TimeCount = 0;
-	while (!IN_CheckAck () && TimeCount < 700);
-
-	PrintX = 0;
-	PrintY = 180;
-	VWB_Bar(0,180,320,20,0);
-	US_CPrint ("With the spear gone, the Allies will finally\n");
-	US_CPrint ("be able to destroy Hitler...");
-	VW_UpdateScreen ();
-	IN_StartAck ();
-	TimeCount = 0;
-	while (!IN_CheckAck () && TimeCount < 700);
-
-	VW_FadeOut ();
-
-	EndScreen (Eend1+3, Pend1+3);
-	EndScreen (Eend1+4, Pend1+4);
-	EndScreen (Eend1+5, Pend1+5);
-	EndScreen (Eend1+6, Pend1+6);
-	EndScreen (Eend1+7, Pend1+7);
-	EndScreen (Eend1+8, Pend1+8);
-	EndScreen (Eend1+1, Pend1+1);
-
-	MainMenu[savegame].active = 0;
-}
-#endif
-#endif
-
-//==========================================================================
-
-/*
-==================
-=
-= Victory
-=
-==================
-*/
-
 void Victory (void)
 {
-#ifndef SPEARDEMO
-	s32int	sec;
-	s16int i,min,kr,sr,tr,x;
-	char tempstr[8];
-
-#define RATIOX	6
-#define RATIOY	14
-#define TIMEX	14
-#define TIMEY	8
-
-
-#ifdef SPEAR
-	StartCPMusic (7);
-
-	VWB_Bar(0,0,320,200,VIEWCOLOR);
-	VWB_DrawPic (124,44,Pcollapse);
-	VW_UpdateScreen ();
-	VW_FadeIn ();
-	VW_WaitVBL(2*70);
-	VWB_DrawPic (124,44,Pcollapse+1);
-	VW_UpdateScreen ();
-	VW_WaitVBL(105);
-	VWB_DrawPic (124,44,Pcollapse+2);
-	VW_UpdateScreen ();
-	VW_WaitVBL(105);
-	VWB_DrawPic (124,44,Pcollapse+3);
-	VW_UpdateScreen ();
-	VW_WaitVBL(3*70);
-
-	VL_FadeOut (0,255,0,17,17,5);
-#endif
-
-#ifndef SPEAR
-	StartCPMusic (24);
-#else
-	StartCPMusic (6);
-#endif
-	ClearSplitVWB ();
-
-	VWB_Bar (0,0,320,200-STATUSLINES,127);
-	pictxt(18*8, 2*8, "YOU WIN!");
-
-	pictxt(TIMEX*8, TIMEY-2*8, "TOTAL TIME");
-
-	pictxt(12*8, RATIOY-2*8, "AVERAGES");
-
-	pictxt(RATIOX+8*8, RATIOY*8, "KILL    %");
-	pictxt(RATIOX+4*8, RATIOY+2*8, "SECRET    %");
-	pictxt(RATIOX*8, RATIOY+4*8, "TREASURE    %");
-
-	VWB_DrawPic (8,4,Pwin);
-
-#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,P0+(min/10));
-	i += 2*8;
-	VWB_DrawPic(i,TIMEY*8,P0+(min%10));
-	i += 2*8;
-	pictxt(i/8*8, TIMEY*8, ":");
-	i += 1*8;
-	VWB_DrawPic(i,TIMEY*8,P0+(sec/10));	/* huh? */
-	i += 2*8;
-	VWB_DrawPic(i,TIMEY*8,P0+(sec%10));
-	VW_UpdateScreen ();
-
-	itoa(kr,tempstr,10);
-	x=RATIOX+24-strlen(tempstr)*2;
-	pictxt(x*8, RATIOY*8, tempstr);
-
-	itoa(sr,tempstr,10);
-	x=RATIOX+24-strlen(tempstr)*2;
-	pictxt(x*8, RATIOY+2*8, tempstr);
-
-	itoa(tr,tempstr,10);
-	x=RATIOX+24-strlen(tempstr)*2;
-	pictxt(x*8, RATIOY+4*8, tempstr);
-
-
-#ifndef UPLOAD
-#ifndef SPEAR
-	//
-	// TOTAL TIME VERIFICATION CODE
-	//
-	if (gamestate.difficulty>=GDmed)
-	{
-		VWB_DrawPic (30*8,TIMEY*8,Ptc);
-		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
-
-
-	fontnumber = 1;
-
-	VW_UpdateScreen ();
-	VW_FadeIn ();
-
+	won();
 	IN_Ack();
-
 	VW_FadeOut ();
-
 #ifndef SPEAR
 	EndText();
 #else
 	EndSpear();
 #endif
-
-#endif // SPEARDEMO
 }
 
-//
-// Breathe Mr. BJ!!!
-//
-void BJ_Breathe(void)
-{
-	static s16int which=0,max=10;
-	s16int pics[2]={Pguy,Pguy2};
-
-
-	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;
-
-	s16int	x,i,min,sec,ratio,kr,sr,tr;
-	u16int	temp;
-	char tempstr[10];
-	s32int 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
-	};
-
-	ClearSplitVWB ();			// set up for double buffering in split screen
-	VWB_Bar (0,0,320,200-STATUSLINES,127);
-	StartCPMusic(16);
-
-//
-// do the intermission
-//
-	IN_ClearKeysDown();
-	IN_StartAck();
-
-	VWB_DrawPic(0,16,Pguy);
-
-#ifndef SPEAR
-	if (mapon<8)
-#else
-	if (mapon != 4 &&
-		mapon != 9 &&
-		mapon != 15 &&
-		mapon < 17)
-#endif
-	{
-	 pictxt(14*8, 2*8, "FLOOR\nCOMPLETED");
-
-	 pictxt(14*8, 7*8, "BONUS     0");
-	 pictxt(16*8, 10*8, "TIME");
-	 pictxt(16*8, 12*8, " PAR");
-
-	 pictxt(9*8, 14*8, "KILL RATIO    %");
-	 pictxt(5*8, 16*8, "SECRET RATIO    %");
-	 pictxt(1*8, 18*8, "TREASURE RATIO    %");
-
-	 pictxt(26*8, 2*8, itoa(gamestate.mapon+1,tempstr,10));
-
-	 pictxt(26*8, 12*8, parTimes[gamestate.episode*10+mapon].timestr);
-
-	 //
-	 // PRINT TIME
-	 //
-	 sec=gm.lvltc/70;
-
-	 if (sec > 99*60)		// 99 minutes max
-	   sec = 99*60;
-
-	 if (gm.lvltc<parTimes[gamestate.episode*10+mapon].time*4200)
-		timeleft=(parTimes[gamestate.episode*10+mapon].time*4200)/70-sec;
-
-	 min=sec/60;
-	 sec%=60;
-
-	 i=26*8;
-	 VWB_DrawPic(i,10*8,P0+(min/10));
-	 i+=2*8;
-	 VWB_DrawPic(i,10*8,P0+(min%10));
-	 i+=2*8;
-	 pictxt(i/8*8, 10*8, ":");
-	 i+=1*8;
-	 VWB_DrawPic(i,10*8,P0+(sec/10));
-	 i+=2*8;
-	 VWB_DrawPic(i,10*8,P0+(sec%10));
-
-	 VW_UpdateScreen ();
-	 VW_FadeIn ();
-
-
-	 //
-	 // FIGURE RATIOS OUT BEFOREHAND
-	 //
-	 kr = sr = tr = 0;
-	 if (gm.nkills)
-		kr=(gm.kills*100)/gm.nkills;
-	 if (gm.nsecret)
-		sr=(gm.secret*100)/gm.nsecret;
-	 if (gm.ntreasure)
-		tr=(gm.treasure*100)/gm.ntreasure;
-
-
-	 //
-	 // PRINT TIME BONUS
-	 //
-	 bonus=timeleft*PAR_AMOUNT;
-	 if (bonus)
-	 {
-	  for (i=0;i<=timeleft;i++)
-	  {
-	   ltoa((s32int)i*PAR_AMOUNT,tempstr,10);
-	   x=36-strlen(tempstr)*2;
-	   pictxt(x*8, 7*8, tempstr);
-	   if (!(i%(PAR_AMOUNT/10)))
-		 sfx(Sendb1);
-	   VW_UpdateScreen();
-	   while(SD_SoundPlaying())
-		 BJ_Breathe();
-	   if (IN_CheckAck())
-		 goto done;
-	  }
-
-	  VW_UpdateScreen();
-	  sfx(Sendb2);
-	  while(SD_SoundPlaying())
-		BJ_Breathe();
-	 }
-
-
-	 #define RATIOXX		37
-	 //
-	 // KILL RATIO
-	 //
-	 ratio=kr;
-	 for (i=0;i<=ratio;i++)
-	 {
-	  itoa(i,tempstr,10);
-	  x=RATIOXX-strlen(tempstr)*2;
-	  pictxt(x*8, 14*8, tempstr);
-	  if (!(i%10))
-		sfx(Sendb1);
-	  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;
-	   pictxt(x*8, 7*8, tempstr);
-	   VW_UpdateScreen();
-	   sfx(S100);
-	 }
-	 else
-	 if (!ratio)
-	 {
-	   VW_WaitVBL(VBLWAIT);
-	   SD_StopSound();
-	   sfx(Snobonus);
-	 }
-	 else
-	 sfx(Sendb2);
-
-	 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;
-	  pictxt(x*8, 16*8, tempstr);
-	  if (!(i%10))
-		sfx(Sendb1);
-	  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;
-	   pictxt(x*8, 7*8, tempstr);
-	   VW_UpdateScreen();
-	   sfx(S100);
-	 }
-	 else
-	 if (!ratio)
-	 {
-	   VW_WaitVBL(VBLWAIT);
-	   SD_StopSound();
-	   sfx(Snobonus);
-	 }
-	 else
-	   sfx(Sendb2);
-	 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;
-	  pictxt(x*8, 18*8, tempstr);
-	  if (!(i%10))
-		sfx(Sendb1);
-	  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;
-	   pictxt(x*8, 7*8, tempstr);
-	   VW_UpdateScreen();
-	   sfx(S100);
-	 }
-	 else
-	 if (!ratio)
-	 {
-	   VW_WaitVBL(VBLWAIT);
-	   SD_StopSound();
-	   sfx(Snobonus);
-	 }
-	 else
-	 sfx(Sendb2);
-	 VW_UpdateScreen();
-	 while(SD_SoundPlaying())
-	   BJ_Breathe();
-
-
-	 //
-	 // JUMP STRAIGHT HERE IF KEY PRESSED
-	 //
-	 done:
-
-	 itoa(kr,tempstr,10);
-	 x=RATIOXX-strlen(tempstr)*2;
-	 pictxt(x*8, 14*8, tempstr);
-
-	 itoa(sr,tempstr,10);
-	 x=RATIOXX-strlen(tempstr)*2;
-	 pictxt(x*8, 16*8, tempstr);
-
-	 itoa(tr,tempstr,10);
-	 x=RATIOXX-strlen(tempstr)*2;
-	 pictxt(x*8, 18*8, tempstr);
-
-	 bonus=(s32int)timeleft*PAR_AMOUNT+
-		   (PERCENT100AMT*(kr==100))+
-		   (PERCENT100AMT*(sr==100))+
-		   (PERCENT100AMT*(tr==100));
-
-	 givep(bonus);
-	 ltoa(bonus,tempstr,10);
-	 x=36-strlen(tempstr)*2;
-	 pictxt(x*8, 7*8, 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: pictxt(14*8, 4*8, " TRANS\n GROSSE\nDEFEATED!"); break;
-	   case 9: pictxt(14*8, 4*8, "BARNACLE\nWILHELM\nDEFEATED!"); break;
-	   case 15: pictxt(14*8, 4*8, "UBERMUTANT\nDEFEATED!"); break;
-	   case 17: pictxt(14*8, 4*8, " DEATH\n KNIGHT\nDEFEATED!"); break;
-	   case 18: pictxt(13*8, 4*8, "SECRET TUNNEL\n    AREA\n  COMPLETED!"); break;
-	   case 19: pictxt(13*8, 4*8, "SECRET CASTLE\n    AREA\n  COMPLETED!"); break;
-	  }
-#endif
-#else
-	  pictxt(14*8, 4*8, "SECRET FLOOR\n COMPLETED!");
-#endif
-
-	  pictxt(10*8, 16*8, "15000 BONUS!");
-
-	  VW_UpdateScreen();
-	  VW_FadeIn();
-
-	  givep(15000);
-	}
-
-
-	hudp();
-	VW_UpdateScreen();
-
-	TimeCount=0;
-	IN_StartAck();
-	while(!IN_CheckAck())
-	  BJ_Breathe();
-
-//
-// done
-//
-#ifdef SPEARDEMO
-	if (gamestate.mapon == 1)
-	{
-		sfx (S1up);
-		Message ("This concludes your demo\n"
-				 "of Spear of Destiny! Now,\n"
-				 "go to your local software\n"
-				 "store and buy it!");
-
-		IN_ClearKeysDown();
-		IN_Ack();
-	}
-#endif
-
-	VW_FadeOut ();
-	temp = bufferofs;
-	for (i=0;i<3;i++)
-	{
-		bufferofs = screenloc[i];
-		DrawPlayBorder ();
-	}
-	bufferofs = temp;
-}
-
-
-
-//==========================================================================
-
-
-/*
-=================
-=
-= PreloadGraphics
-=
-= Fill the cache up
-=
-=================
-*/
-
-int PreloadUpdate(u16int current, u16int total)
-{
-	u16int w = WindowW - 10;
-
-
-	VWB_Bar(WindowX + 5,WindowY + WindowH - 3,w,2,BLACK);
-	w = ((s32int)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();
-	return false;
-}
-
-void PreloadGraphics(void)
-{
-	hudm ();
-	ClearSplitVWB ();			// set up for double buffering in split screen
-
-	VWB_Bar (0,0,320,200-STATUSLINES,127);
-
-	LatchDrawPic (20-14,80-3*8,Ppsyched);
-
-	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 ();
-}
-
 void	CheckHighScore (s32int score,u16int other)
 {
 	u16int		i,j;
@@ -867,5 +75,4 @@
 		IN_ClearKeysDown ();
 		IN_UserInput(500);
 	}
-
 }
--- a/main.c
+++ b/main.c
@@ -1,17 +1,8 @@
-char            str[80],str2[20];
-s16int				tedlevelnum;
-int         tedlevel;
-s16int  dirangle[9] = {0,ANGLES/8,2*ANGLES/8,3*ANGLES/8,4*ANGLES/8,
-	5*ANGLES/8,6*ANGLES/8,7*ANGLES/8,ANGLES};
-
-u16int        screenofs;
-
 int         startgame;
 s16int             mouseadjustment;
 
 char	configname[13]="CONFIG.";
 
-
 void ReadConfig(void)
 {
 	s16int                     file;
@@ -61,7 +52,6 @@
 			mouseenabled = false;
 
 		MainMenu[6].active=1;
-		MainItems.curpos=0;
 	}
 	else
 	{
@@ -132,11 +122,6 @@
 	}
 }
 
-void NewGame (s16int difficulty,s16int episode)
-{
-	→ initg, w/o difficulty, map
-}
-
 void DiskFlopAnim(s16int x,s16int y)
 {
  static char which=0;
@@ -368,123 +353,12 @@
 	return true;
 }
 
-void SetupWalls (void)	/* map tile values to scaled pics */
-{
-	s16int     i;
-
-	for (i=1;i<MAXWALLTILES;i++)
-	{
-		horizwall[i]=(i-1)*2;
-		vertwall[i]=(i-1)*2+1;
-	}
-}
-
-#define	PORTTILESHIGH		13		// non displayed port of this size
-
-void InitGame (void)
-{
-	s16int                     i,x,y;
-	u16int        *blockstart;
-
-	mapon = -1;
-
-	for (i=0;i<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 ();
-
-	IntroScreen ();
-
-	LoadLatchMem ();
-	SetupWalls ();
-
-	NewViewSize (vw.size);
-
-	InitRedShifts ();
-
-	displayofs = PAGE1START;
-	bufferofs = PAGE2START;
-}
-
-int SetViewSize (u16int width, u16int height)
-{
-	→ setvw()
-}
-
-void ShowViewSize (s16int width)
-{
-	s16int     oldwidth,oldheight;
-
-	oldwidth = vw.dx;
-	oldheight = vw.dy;
-
-	vw.dx = width*16;
-	vw.dy = width*16*HEIGHTRATIO;
-	DrawPlayBorder ();
-
-	vw.dy = oldheight;
-	vw.dx = oldwidth;
-}
-
-void NewViewSize (s16int width)
-{
-	vw.size = width;
-	SetViewSize (width*16,width*16*HEIGHTRATIO);
-}
-
 void    DemoLoop (void)
 {
-	static s16int LastDemo;
-	s16int     i,level;
-	s32int nsize;
-	uchar *nullblock;
-
-//
-// check for launch from ted
-//
-	/* → if warping to map [tedlevel] */
-	if (tedlevel)
-	{
-		NoWait = true;
-		NewGame(1,0);
-
-		/* → set difficulty level 1-4 if parameter passed as
-		 * gamestate.difficulty */
-
-#ifndef SPEAR
-		gamestate.episode = tedlevelnum/10;
-		gamestate.mapon = tedlevelnum%10;
-#else
-		gamestate.episode = 0;
-		gamestate.mapon = tedlevelnum;
-#endif
-		GameLoop();
-		Quit (NULL);
-	}
-
-	StartCPMusic(INTROSONG);
-	// pg13
-
 	while (1)
 	{
-		p = dems;
 		while (!NoWait)
 		{
-			/* title loop */
 			PlayDemo(p++);
 			if(p >= epis)
 				p = dems;
@@ -492,13 +366,11 @@
 				break;
 			StartCPMusic(INTROSONG);
 		}
-
 		VW_FadeOut ();
 		if (Keyboard[sc_Tab] && debug)
 			RecordDemo ();
 		else
 			US_ControlPanel (0);
-
 		if (startgame || gm.load)
 		{
 			GameLoop ();
@@ -507,25 +379,3 @@
 		}
 	}
 }
-
-void main (void)
-{
-	if (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;
-	}
-
-	InitGame ();
-
-	DemoLoop();
-}
-
--- a/man/1/wl3d
+++ b/man/1/wl3d
@@ -13,10 +13,7 @@
 .I datadir
 ] [
 .B -w
-.I map
-] [
-.B -x
-.I 1-4
+.I map 0-3
 ]
 .SH DESCRIPTION
 .I Wl3d
@@ -34,7 +31,7 @@
 .RE
 .PP
 The command line options are:
-.TP \w'\fLf\ \ \ \fIdemo'u
+.TP \w'\fLw\ \ \fI"map\ 0-3"'u
 .B -2
 Set game version to Spear of Destiny Mission 2: Return to Danger.
 .TP
@@ -62,11 +59,8 @@
 .B -s
 Set game version to Spear of Destiny 1.0 retail.
 .TP
-.BI -w\  map
-Warp to the given map number on startup.
-.TP
-.BI -x\  1-4
-Set default game difficulty.
+.BI -w\  "map 0-3"
+Warp to the given map number with a given difficulty on startup.
 .PD
 .PP
 .I Wl3d
@@ -103,10 +97,10 @@
 .B -w
 parameter is used, the game starts immediately at map number
 .IR map ,
-and
-.B -x
-optionally sets the game difficulty to 1-4, from easiest to hardest,
-the default being 2.
+with difficulty set to
+.BR 0-3 ,
+.B 0
+being the easiest.
 The
 .B -p
 parameter runs the program at the fastest speed possible for testing purposes.
@@ -138,9 +132,9 @@
 .L sd2
 and
 .L sd3
-versions are the same as
-.L sod
-with the exception of substituting some of the data files.
+versions are variants of
+.LR sod ,
+only substituting some of the data files.
 .PD
 .SS Demo lumps
 .I Wl3d
@@ -172,21 +166,30 @@
 Most of
 .I wl3d
 has been rewritten from scratch, and some parts have been implemented differently from the reference.
-Most importantly, individual data lumps are no longer read and cached as needed, but rather all loaded into memory, uncompressed, and in some cases converted, at startup.
-This bumps the required amount of free memory up to around at least 5 megabytes, depending on the game version, architecture and window size.
-In addition, a single executable handles all supported game versions.
+Most importantly, individual data lumps are no longer read and cached as needed,
+but rather all loaded into memory, uncompressed, and in some cases converted,
+at startup.
+This bumps the required amount of free memory up to around at least 5 megabytes,
+depending on the game version, architecture and window size.
 .PP
-Intro screens are now additional data files to be loaded on start up, rather than being compiled in, and must therefore be installed in the
+A single executable handles all supported game versions.
+.PP
+Intro screens are now additional data files to be loaded on startup,
+rather than being compiled in, and must therefore be installed in the
 .IR datadir .
-Also unlike the reference implementation, these are displayed during data file loading, and are immediately faded out of afterwards.
+Also unlike the reference implementation,
+these are displayed during data file loading,
+and are immediately faded out of afterwards.
 .PP
 Copy protection code and the Spear of Destiny Jukebox have been excised.
-.PP
 Menus are implemented differently, and some have been altered in functionality.
+Debug mode has been removed, and cheats work differently.
 .PP
-Game keys are no longer set in the options menu, but rather in the config file.
-A single global configuration file is used, rather than a version dependent one.
-Also, while savegames are in a compatible format, config files are not.
+Game keys are no longer set in the options menu, but rather in the configuration file.
+A single configuration file is used for all game versions.
+Savegames and config files are incompatible in format.
+.PP
+The texture and sprite scaling implementation does not attempt to save memory and avoids quantization at close range.
 .SH FILES
 .TF /sys/games/lib/wl3d/intro.wl6
 .TP
@@ -213,13 +216,14 @@
 .SM OPL2
 emulation, Adlib sound effects crack too much during playback.
 .PP
+The upsampling implementation for digital sound effects is overkill given the number of constraints.
+It is also buggy and playback cracks too much.
+.PP
 With sound effects enabled, the
 .SM OPL2
 emulation runs on every frame and
 .L /dev/audio
 is written to, even when no sound is playing.
-.PP
-The upsampling implementation for digital sound effects is overkill given the number of constraints.
 .PP
 Little is done in case the program is unable to run at a framerate of 70 Hz.
 .PD
--- a/man/6/wl3d
+++ b/man/6/wl3d
@@ -9,36 +9,39 @@
 .TP
 .B audiohed
 PC Speaker, Adlib, digital effects and music offsets in
-.B audiot
+.BR audiot .
 .TP
 .B audiot
-uncompressed PC Speaker, Adlib, digital effects and music lumps
+Uncompressed PC Speaker, Adlib, digital effects and music lumps.
 .TP
 .B config
-saved game settings and highscores
+Saved game settings and highscores.
 .TP
 .B gamemaps
-maps lump
+Map lumps.
 .TP
 .B maphead
-map offsets in
-.B gamemaps
+Map offsets in
+.BR gamemaps .
 .TP
+.B savegam?
+Saved games.
+.TP
 .B vgadict
-huffman dictionary for lumps contained in
-.B vgagraph
+Huffman dictionary for lumps contained in
+.BR vgagraph .
 .TP
 .B vgagraph
-fonts, pictures, tiles and screens, encoded for
+Fonts, pictures, tiles and screens, encoded for
 .SM VGA
-graphics cards
+graphics cards.
 .TP
 .B vgahead
-graphics offsets in
-.B vgagraph
+Graphics offsets in
+.BR vgagraph .
 .TP
 .B vswap
-wall textures, sprites and raw pcm audio
+Wall textures, sprites and raw pcm audio.
 .PD
 .PP
 Integers are stored in little-endian byte order.
@@ -64,7 +67,7 @@
 for Spear of Destiny 1.0 demo and
 .B sod
 for Spear of Destiny 1.0 retail (including mission packs).
-Other versions are outside of the scope of this document.
+Other versions are outside the scope of this document.
 .SH SOUND EFFECTS AND MUSIC
 .SS Audiohed
 .RS
@@ -128,8 +131,8 @@
 format.
 Each sound effect has a PC Speaker, Adlib and digital version.
 .PP
-Each types of lump is stored in a specific format detailed below.
-No digital sounds are ever stored in
+Each type of lump is stored in a specific format detailed below.
+No digital sound is ever stored in
 .BR audiot ,
 having raw pcm lumps in
 .B vswap
@@ -193,13 +196,13 @@
 .I priority
 is lower than the new one, it is interrupted, then substituted.
 .PP
-At the end of playback, the engine resets the values of the instrument registers and writes a zero to registers
-.L 0xb0
-and
-.LR 0xc0 .
+At the end of playback, the engine resets the values of the instrument registers and writes a zero to register
+.LR 0xb0 .
 .PP
 .I Ignored
-contains 6 fields only used by Muse.
+contains 1 byte intended to be written to register
+.LR 0xc0 ,
+but never is, then 5 fields only used by Muse.
 .I Tag
 is a variable-length field suffixed by Muse and is also ignored.
 .SS Digital sound effect
@@ -301,10 +304,14 @@
 Maps are decomposed into three planes.
 .B Gamemaps
 holds an array of map headers, followed by doubly-compressed plane data.
-.IR Pls and pll
+.I Pls
+and
+.I pll
 are respectively arrays of offsets and lengths for each plane.
 Only the first two planes are ever used.
-.IR dx and dy
+.I dx
+and
+.I dy
 are the planes' dimensions, and must both be 64.
 .I Name
 is an unused unterminated
@@ -312,11 +319,31 @@
 string.
 .I Planes
 stores contiguously each plane's data.
-.SS Compression
+.PP
+The first plane is an array of [words].
+.PP
+The second plane is an array of [other words].
+.PP
+There are static limits for objects on the map:
+.TF "static objects"
+.TP
+.B actors
+150 (including the player)
+.TP
+.B doors
+64
+.TP
+.B static objects
+399
+.SS RLEW Compression
 Each map plane is first compressed using
 .SM RLEW,
 then further using what is eponymously refered to as
 .SM Carmack compression.
+.PP
+[words]
+.SS Carmack Compression
+[words]
 .SH GRAPHICS
 Graphics are either static data loaded in the executable, or huffman-compressed lumps contained in
 .BR vgagraph .
@@ -420,13 +447,14 @@
 .SM VGA
 color planes rather than contiguously.
 .SS Tiles
-Tiles used in Wolfenstein 3-D are exclusively of size 8x8, and thus stored as arrays of 64 bytes.
+These tiles are exclusively of size 8x8, and thus stored as arrays of 64 bytes.
 All tiles are stored contiguously in a single lump following the last pic lump.
 Their number is version dependent, but only the first 8 tiles are ever used.
 .PP
-The tile lumps in Wolfenstein 3-D and Spear of Destiny versions all decode incorrectly and result in reads past the allocated buffer (or past the end of the lump).
+This lump decodes incorrectly in all game versions
+and results in reads past the allocated buffer (or past the end of the lump).
 .IR Wl3d (1)
-skips this lump entirely.
+skips it entirely.
 .SS Screens and demos
 .RS
 .IR misc [ ns ][]
@@ -535,6 +563,7 @@
 for file offsets, and
 .IR sz ,
 for lengths in bytes.
+.PP
 A chunk of size 0 is skipped.
 This is permitted when the specific lump is never referenced,
 which can occur in the
@@ -550,12 +579,15 @@
 Because they are drawn vertically, they are stored as an array of coloumns.
 In other words, if drawn as is using a given palette,
 the tile would appear rotated by -90° then flipped along its vertical axis.
-The last 8 wall textures are door textures.
+The last 8 wall textures are used for doors.
 .SS Sprites
 .RS
 .IR lx [2]
 .IR rx [2]
 .IR cofs [ rx-lx+1 ][2]
+.IR pad []
+.IR cmd []
+.IR pad []
 .IR data []
 .br
 .BR cmd :
@@ -574,15 +606,19 @@
 and must respect the following restrictions:
 .PP
 .RS
-.I lx  ∈ {0,1,...,63}
+.BR lx \ ∈\ {0,1,...,63}
 .br
-.I rx ∈ {32,33,...,63}
+.BR rx \ ∈\ {32,33,...,63}
 .br
-.I lx ≤ rx
+.BR lx \ ≤\ rx
 .RE
 .PP
 Sprites are drawn centered on the 32nd coloumn.
 A left bound equal to or greater than 32 will offset the sprite to the right.
+Because of a bug in the original
+.SM DOS
+binaries, such a left bound results in reads past the lump
+and excess pixels may show up as garbage on the screen.
 .PP
 The following variable-length array,
 .IR cofs ,
@@ -595,7 +631,7 @@
 .I se / 2
 are, respectively, an upper and lower bound,
 defining a contiguous vertical strip of pixels to draw within a coloumn, with
-.I po + ss
+.I po + ss / 2
 an offset into the sprite lump pointing to the strip's palette indices.
 The
 .I cmd
@@ -654,6 +690,8 @@
 pcm lump,
 as indicated in the pcm table.
 .SH "CONFIGURATION FILE AND HIGHSCORES"
+[words]
+.SH "SAVED GAMES"
 [words]
 .SH "SEE ALSO"
 .IR opl2 (1) ,
--- a/map.c
+++ b/map.c
@@ -24,7 +24,7 @@
 	Rblock, Rblock, Rgibs, Rblock, Rblock, Rnil, Rnil, Rnil, Rnil,
 	Rblock, Rblock, Rnil, Rclip2, Rammobox, Rblock, Rspear, Rclip2
 };
-static Obj opool[Nobj];
+static Obj opool[Nobj+2];
 
 static void
 spawnstc(Tile *tl, int n)
@@ -50,7 +50,7 @@
 	case Rcrown:
 	case R1up:
 		if(!gm.load)
-			gm.ntreasure++;
+			gm.ttot++;
 		/* wet floor */
 	default:
 		stce->f = OFbonus;
@@ -427,7 +427,7 @@
 	o->θ = θE;
 	o->f |= OFambush;
 	if(!gm.load)
-		gm.nkills++;
+		gm.ktot++;
 }
 
 static void
@@ -485,6 +485,7 @@
 		θ = θS;
 		goto wlonly;
 	case Ofake:
+		/* bug? */
 		stt[GShitlerdie2].dt = pcmon ? 140 : 5;
 		s = stt+GSfake;
 		hp = 200 + 100 * gm.difc;
@@ -538,7 +539,7 @@
 	o->θ = θ;
 	o->f |= OFshootable | OFambush;
 	if(!gm.load)
-		gm.nkills++;
+		gm.ktot++;
 }
 
 static void
@@ -605,7 +606,7 @@
 	o->θ = dir * 90;
 
 	if(!gm.load)
-		gm.nkills++;
+		gm.ktot++;
 	if(patrol){
 		o->Δr = Dtlglobal;
 		o->on++;
@@ -622,7 +623,7 @@
 	int n, difc;
 
 	n = tl->p1;
-	difc = GDeasy;
+	difc = GDbaby;
 	switch(n){
 	case 19: case 20: case 21: case 22:
 		spawnplr(tl, n-19);
@@ -638,64 +639,64 @@
 		break;
 	case 98:
 		if(!gm.load)
-			gm.nsecret++;
+			gm.stot++;
 		break;
 	case 180: case 181: case 182: case 183: difc++;	n-=36; /* wet floor */
-	case 144: case 145: case 146: case 147: difc++;	n-=36; /* wet floor */
+	case 144: case 145: case 146: case 147: difc+=2; n-=36; /* wet floor */
 	case 108: case 109: case 110: case 111:
 		if(difc <= gm.difc)
 			spawnguy(tl, Ogd, n-108, 0);
 		break;
 	case 184: case 185: case 186: case 187: difc++;	n-=36; /* wet floor */
-	case 148: case 149: case 150: case 151: difc++;	n-=36; /* wet floor */
+	case 148: case 149: case 150: case 151: difc+=2; n-=36; /* wet floor */
 	case 112: case 113: case 114: case 115:
 		if(difc <= gm.difc)
 			spawnguy(tl, Ogd, n-112, 1);
 		break;
 	case 188: case 189: case 190: case 191: difc++;	n-=36; /* wet floor */
-	case 152: case 153: case 154: case 155: difc++;	n-=36; /* wet floor */
+	case 152: case 153: case 154: case 155: difc+=2; n-=36; /* wet floor */
 	case 116: case 117: case 118: case 119:
 		if(difc <= gm.difc)
 			spawnguy(tl, Oofc, n-116, 0);
 		break;
 	case 192: case 193: case 194: case 195: difc++;	n-=36; /* wet floor */
-	case 156: case 157: case 158: case 159: difc++;	n-=36; /* wet floor */
+	case 156: case 157: case 158: case 159: difc+=2; n-=36; /* wet floor */
 	case 120: case 121: case 122: case 123:
 		if(difc <= gm.difc)
 			spawnguy(tl, Oofc, n-120, 1);
 		break;
 	case 198: case 199: case 200: case 201: difc++;	n-=36; /* wet floor */
-	case 162: case 163: case 164: case 165: difc++;	n-=36; /* wet floor */
+	case 162: case 163: case 164: case 165: difc+=2; n-=36; /* wet floor */
 	case 126: case 127: case 128: case 129:
 		if(difc <= gm.difc)
 			spawnguy(tl, Oss, n-126, 0);
 		break;
 	case 202: case 203: case 204: case 205: difc++;	n-=36; /* wet floor */
-	case 166: case 167: case 168: case 169: difc++;	n-=36; /* wet floor */
+	case 166: case 167: case 168: case 169: difc+=2; n-=36; /* wet floor */
 	case 130: case 131: case 132: case 133:
 		if(difc <= gm.difc)
 			spawnguy(tl, Oss, n-130, 1);
 		break;
 	case 206: case 207: case 208: case 209: difc++;	n-=36; /* wet floor */
-	case 170: case 171: case 172: case 173: difc++;	n-=36; /* wet floor */
+	case 170: case 171: case 172: case 173: difc+=2; n-=36; /* wet floor */
 	case 134: case 135: case 136: case 137:
 		if(difc <= gm.difc)
 			spawnguy(tl, Odog, n-134, 0);
 		break;
 	case 210: case 211: case 212: case 213: difc++;	n-=36; /* wet floor */
-	case 174: case 175: case 176: case 177: difc++;	n-=36; /* wet floor */
+	case 174: case 175: case 176: case 177: difc+=2; n-=36; /* wet floor */
 	case 138: case 139: case 140: case 141:
 		if(difc <= gm.difc)
 			spawnguy(tl, Odog, n-138, 1);
 		break;
 	case 252: case 253: case 254: case 255: difc++;	n-=18; /* wet floor */
-	case 234: case 235: case 236: case 237: difc++;	n-=18; /* wet floor */
+	case 234: case 235: case 236: case 237: difc+=2; n-=18; /* wet floor */
 	case 216: case 217: case 218: case 219:
 		if(difc <= gm.difc)
 			spawnguy(tl, Omut, n-216, 0);
 		break;
 	case 256: case 257: case 258: case 259: difc++;	n-=18; /* wet floor */
-	case 238: case 239: case 240: case 241: difc++;	n-=18; /* wet floor */
+	case 238: case 239: case 240: case 241: difc+=2; n-=18; /* wet floor */
 	case 220: case 221: case 222: case 223:
 		if(difc <= gm.difc)
 			spawnguy(tl, Omut, n-220, 1);
@@ -812,7 +813,14 @@
 	o->ty = (tl-tiles) / Mapdxy;
 	osetglobal(o);
 	o->areaid = tl->p0 - MTfloor;
-	o->tc = s->dt != 0 ? rnd() % s->dt : 0;
+	if(s->dt != 0){
+		o->tc = rnd() % s->dt;
+		/* bug: if .tc is 0, uobj won't update its state on its own,
+		 * and moving objects randomly won't change their sprite */
+		if(!gm.record && !gm.demo && o->tc == 0)
+			o->tc = 1;
+	}else
+		o->tc = 0;
 	return o;
 }
 
--- a/menu.c
+++ b/menu.c
@@ -1,227 +1,11 @@
-void CP_ReadThis(void);
-
-#define STARTITEM	newgame
-
-#define ENDGAMESTR	"Are you sure you want\nto end the game you\nare playing? (Y or N):"
-#define GAMESVD	"There's already a game\nsaved at this position.\n      Overwrite?"
-#define CURGAME	"You are currently in\na game. Continuing will\nerase old game. Ok?"
-
-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
-	"Heroes don't quit, but\ngo ahead and press Y\nif you aren't one.",
-	"Press Y to quit,\nor press N to enjoy\nmore violent diversion.",
-	"Depressing the Y key means\nyou must return to the\nhumdrum workday world.",
-	"Hey, quit or play,\nY or N:\nit's your choice.",
-	"Sure you don't want to\nwaste a few more\nproductive hours?",
-	"I think you had better\nplay some more. Please\npress N...please?",
-	"If you are tough, press N.\nIf not, press Y daintily.",
-	"I'm thinkin' that\nyou might wanna press N\nto play more. You do it.",
-	"Sure. Fine. Quit.\nSee if we care.\nGet it over with.\nPress Y."
-
-#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[]=
-{
-	{1,"New Game",CP_NewGame},
-	{1,"Sound",CP_Sound},
-	{1,"Control",CP_Control},
-	{1,"Load Game",CP_LoadGame},
-	{0,"Save Game",CP_SaveGame},
-	{1,"Change View",CP_ChangeView},
-#ifndef SPEAR
-	{2,"Read This!",CP_ReadThis},
-#endif
-	{1,"View Scores",CP_ViewScores},
-	{1,"Back to Demo",0},
-	{1,"Quit",0}
-},
-
-far SndMenu[]=
-{
-	{1,"None",0},
-	{1,"PC Speaker",0},
-	{1,"AdLib/Sound Blaster",0},
-	{0,"",0},
-	{0,"",0},
-	{1,"None",0},
-	{1,"Disney Sound Source",0},
-	{1,"Sound Blaster",0},
-	{0,"",0},
-	{0,"",0},
-	{1,"None",0},
-	{1,"AdLib/Sound Blaster",0}
-},
-
-far CtlMenu[]=
-{
-	{0,"Mouse Enabled",0},
-	{0,"Joystick Enabled",0},
-	{0,"Use joystick port 2",0},
-	{0,"Gravis GamePad Enabled",0},
-	{0,"Mouse Sensitivity",MouseSensitivity},
-	{1,"Customize controls",CustomControls}
-},
-
-#pragma warn +sus
-
-#ifndef SPEAR
-far NewEmenu[]=
-{
-	{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
-
-
-far NewMenu[]=
-{
-	{1,"Can I play, Daddy?",0},
-	{1,"Don't hurt me.",0},
-	{1,"Bring 'em on!",0},
-	{1,"I am Death incarnate!",0}
-},
-
-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}
-}
-;
-
-
-s16int color_hlite[]={
-   DEACTIVE,
-   HIGHLIGHT,
-   READHCOLOR,
-   0x67
-   },
-
-   color_norml[]={
-   DEACTIVE,
-   TEXTCOLOR,
-   READCOLOR,
-   0x6b
-   };
-
-s16int EpisodeSelect[6]={1};
-
-
 s16int SaveGamesAvail[10],StartGame,SoundStatus=1,pickquick;
 char SaveGameNames[10][32],SaveName[13]="SAVEGAM?.";
 
-
-////////////////////////////////////////////////////////////////////
-//
-// INPUT MANAGER SCANCODE TABLES
-//
-////////////////////////////////////////////////////////////////////
-static u8int
-					*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(u8int scancode)
 {
-	s16int which,i,start;
-
-
 	if (ingame)
 		if (CP_CheckQuick(scancode))
 			return;
-
-	StartCPMusic(MENUSONG);
-	SetupControlPanel();
-
-	//
-	// F-KEYS FROM WITHIN GAME
-	//
 	switch(scancode)
 	{
 		case sc_F1:
@@ -251,145 +35,10 @@
 			goto finishup;
 
 		finishup:
-			CleanupControlPanel();
 			return;
 	}
-
-	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 (18);
-			MM_SortMem ();
-			ClearMemory ();
-
-			VWB_DrawPic(0,0,Pid1);
-			VWB_DrawPic(0,80,Pid2);
-
-			VW_UpdateScreen();
-
-			VL_FadeIn(0,255,Eid,30);	/* sod only */
-
-			while (Keyboard[sc_I] || Keyboard[sc_D]);
-			IN_ClearKeysDown();
-			IN_Ack();
-
-			VW_FadeOut();
-
-			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:
-				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 || gm.load)
-	{
-		#pragma warn -sus
-		MainMenu[viewscores].routine = NULL;
-		_fstrcpy(MainMenu[viewscores].string,"End Game");
-		#pragma warn +sus
-	}
 }
 
-
-////////////////////////
-//
-// DRAW MAIN MENU SCREEN
-//
-void DrawMainMenu(void)
-{
-	ClearMScreen();
-
-	VWB_DrawPic(112,184,Pmouselback);
-	DrawStripes(10);
-	VWB_DrawPic(84,0,Popt);
-
-	DrawWindow(MENU_X-8,MENU_Y-3,MENU_W,MENU_H,BKGDCOLOR);
-
-	//
-	// CHANGE "GAME" AND "DEMO"
-	//
-	if (ingame)
-	{
-		_fstrcpy(&MainMenu[backtodemo].string[8],"Game");
-		MainMenu[backtodemo].active=2;
-	}
-	else
-	{
-		_fstrcpy(&MainMenu[backtodemo].string[8],"Demo");
-		MainMenu[backtodemo].active=1;
-	}
-
-	DrawMenu(&MainItems,&MainMenu[0]);
-	VW_UpdateScreen();
-}
-
-////////////////////////////////////////////////////////////////////
-//
-// READ THIS!
-//
-////////////////////////////////////////////////////////////////////
 void CP_ReadThis(void)
 {
 	StartCPMusic(0);
@@ -397,11 +46,6 @@
 	StartCPMusic(MENUSONG);
 }
 
-////////////////////////////////////////////////////////////////////
-//
-// CHECK QUICK-KEYS & QUIT (WHILE IN A GAME)
-//
-////////////////////////////////////////////////////////////////////
 s16int CP_CheckQuick(u16int scancode)
 {
 	switch(scancode)
@@ -513,99 +157,11 @@
 				PM_CheckMainMem ();
 			}
 			return 1;
-
-		//
-		// QUIT
-		//
-		case sc_F10:
-			CA_CacheGrChunk(STARTFONT+1);
-
-			WindowX=WindowY=0;
-			WindowW=320;
-			WindowH=160;
-			if (Confirm(endStrings[rnd()&0x7+(rnd()&1)]))
-			{
-				s16int 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
-//
-////////////////////////////////////////////////////////////////////
-s16int CP_EndGame(void)
-{
-	if (!Confirm(ENDGAMESTR))
-		return 0;
-
-	pickquick = gamestate.lives = 0;
-	gm.φ = ex_died;
-
-	#pragma warn -sus
-	MainMenu[savegame].active = 0;
-	MainMenu[viewscores].routine=CP_ViewScores;
-	_fstrcpy(MainMenu[viewscores].string,"View Scores");
-	#pragma warn +sus
-
-	return 1;
-}
-
-
-////////////////////////////////////////////////////////////////////
-//
-// VIEW THE HIGH SCORES
-//
-////////////////////////////////////////////////////////////////////
-void CP_ViewScores(void)
-{
-	fontnumber=0;
-
-#ifdef SPEAR
-	StartCPMusic (20);
-#else
-	StartCPMusic (23);
-#endif
-
-	DrawHighScores ();
-	VW_UpdateScreen ();
-	MenuFadeIn();
-	fontnumber=1;
-
-	IN_Ack();
-
-	StartCPMusic(MENUSONG);
-	MenuFadeOut();
-}
-
-
-////////////////////////////////////////////////////////////////////
-//
-// START A NEW GAME
-//
-////////////////////////////////////////////////////////////////////
 void CP_NewGame(void)
 {
 	s16int which,episode;
@@ -703,267 +259,6 @@
 	pickquick = 0;
 }
 
-
-#ifndef SPEAR
-/////////////////////
-//
-// DRAW NEW EPISODE MENU
-//
-void DrawNewEpisode(void)
-{
-	s16int i;
-
-	ClearMScreen();
-	VWB_DrawPic(112,184,Pmouselback);
-
-	DrawWindow(NE_X-4,NE_Y-4,NE_W+8,NE_H+8,BKGDCOLOR);
-	SETFONTCOLOR(READHCOLOR,BKGDCOLOR);
-	PrintY=2;
-	WindowX=0;
-	US_CPrint("Which episode to play?");
-
-	SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR);
-	DrawMenu(&NewEitems,&NewEmenu[0]);
-
-	for (i=0;i<6;i++)
-		VWB_DrawPic(NE_X+32,NE_Y+i*26,Pep1+i);
-
-	VW_UpdateScreen();
-	MenuFadeIn();
-	WaitKeyUp();
-}
-#endif
-
-/////////////////////
-//
-// DRAW NEW GAME MENU
-//
-void DrawNewGame(void)
-{
-	ClearMScreen();
-	VWB_DrawPic(112,184,Pmouselback);
-
-	SETFONTCOLOR(READHCOLOR,BKGDCOLOR);
-	PrintX=NM_X+20;
-	PrintY=NM_Y-32;
-
-#ifndef SPEAR
-	US_Print("How tough are you?");
-#else
-	VWB_DrawPic (PrintX,PrintY,Pdiffc);
-#endif
-
-	DrawWindow(NM_X-5,NM_Y-10,NM_W,NM_H,BKGDCOLOR);
-
-	DrawMenu(&NewItems,&NewMenu[0]);
-	DrawNewGameDiff(NewItems.curpos);
-	VW_UpdateScreen();
-	MenuFadeIn();
-	WaitKeyUp();
-}
-
-
-////////////////////////
-//
-// DRAW NEW GAME GRAPHIC
-//
-void DrawNewGameDiff(s16int w)
-{
-	VWB_DrawPic(NM_X+185,NM_Y+7,w+Pbaby);
-}
-
-
-////////////////////////////////////////////////////////////////////
-//
-// HANDLE SOUND MENU
-//
-////////////////////////////////////////////////////////////////////
-void CP_Sound(void)
-{
-	s16int which,i;
-
-	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();
-}
-
-
-//////////////////////
-//
-// DRAW THE SOUND MENU
-//
-void DrawSoundMenu(void)
-{
-	s16int i,on;
-
-	//
-	// DRAW SOUND MENU
-	//
-	ClearMScreen();
-	VWB_DrawPic(112,184,Pmouselback);
-
-	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);
-
-	//
-	// 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]);
-	VWB_DrawPic(100,SM_Y1-20,Psfx);
-	VWB_DrawPic(100,SM_Y2-20,Ppcm);
-	VWB_DrawPic(100,SM_Y3-20,Pmus);
-
-	for (i=0;i<SndItems.amount;i++)
-		if (SndMenu[i].string[0])
-		{
-			//
-			// 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,Psel);
-			else
-				VWB_DrawPic(SM_X+24,SM_Y1+i*13+2,Punsel);
-		}
-
-	DrawMenuGun(&SndItems);
-	VW_UpdateScreen();
-}
-
-
-//
-// DRAW LOAD/SAVE IN PROGRESS
-//
 void DrawLSAction(s16int which)
 {
 	#define LSA_X	96
@@ -988,12 +283,6 @@
 	VW_UpdateScreen();
 }
 
-
-////////////////////////////////////////////////////////////////////
-//
-// LOAD SAVED GAMES
-//
-////////////////////////////////////////////////////////////////////
 s16int CP_LoadGame(s16int quick)
 {
 	s16int handle,which,exit=0;
@@ -1070,11 +359,6 @@
 	return exit;
 }
 
-
-///////////////////////////////////
-//
-// HIGHLIGHT CURRENT SELECTED ENTRY
-//
 void TrackWhichGame(s16int w)
 {
 	static s16int lastgameon=0;
@@ -1085,19 +369,10 @@
 	lastgameon=w;
 }
 
-
-////////////////////////////
-//
-// DRAW THE LOAD/SAVE SCREEN
-//
 void DrawLoadSaveScreen(s16int loadsave)
 {
-	#define DISKX	100
-	#define DISKY	0
-
 	s16int i;
 
-
 	ClearMScreen();
 	fontnumber=1;
 	VWB_DrawPic(112,184,Pmouselback);
@@ -1118,11 +393,6 @@
 	WaitKeyUp();
 }
 
-
-///////////////////////////////////////////
-//
-// PRINT LOAD/SAVE GAME ENTRY W/BOX OUTLINE
-//
 void PrintLSEntry(s16int w,s16int color)
 {
 	SETFONTCOLOR(color,BKGDCOLOR);
@@ -1139,12 +409,6 @@
 	fontnumber=1;
 }
 
-
-////////////////////////////////////////////////////////////////////
-//
-// SAVE CURRENT GAME
-//
-////////////////////////////////////////////////////////////////////
 s16int CP_SaveGame(s16int quick)
 {
 	s16int handle,which,exit=0;
@@ -1189,7 +453,7 @@
 			// OVERWRITE EXISTING SAVEGAME?
 			//
 			if (SaveGamesAvail[which])
-				if (!Confirm(GAMESVD))
+				if (!Confirm("There's already a game\nsaved at this position.\n      Overwrite?"))
 				{
 					DrawLoadSaveScreen(1);
 					continue;
@@ -1249,994 +513,9 @@
 	return exit;
 }
 
-////////////////////////////////////////////////////////////////////
-//
-// DEFINE CONTROLS
-//
-////////////////////////////////////////////////////////////////////
-void CP_Control(void)
-{
-	#define CTL_SPC	70
-	enum {MOUSEENABLE,JOYENABLE,USEPORT2,PADENABLE,MOUSESENS,CUSTOMIZE};
-	s16int i,which;
-
-	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;
-				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();
-}
-
-
-////////////////////////////////
-//
-// DRAW MOUSE SENSITIVITY SCREEN
-//
-void DrawMouseSens(void)
-{
-	ClearMScreen();
-	VWB_DrawPic(112,184,Pmouselback);
-	DrawWindow(10,80,300,30,BKGDCOLOR);
-
-	WindowX=0;
-	WindowW=320;
-	PrintY=82;
-	SETFONTCOLOR(READCOLOR,BKGDCOLOR);
-	US_CPrint("Adjust Mouse Sensitivity");
-
-	SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR);
-	PrintX=14;
-	PrintY=95;
-	US_Print("Slow");
-	PrintX=269;
-	US_Print("Fast");
-
-	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;
-	s16int 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();
-					sfx(Sdrawgun1);
-					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();
-					sfx(Sdrawgun1);
-					while(Keyboard[sc_RightArrow]);
-					WaitKeyUp();
-				}
-				break;
-		}
-
-		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;
-		sfx(Sesc);
-	}
-	else
-		sfx(Sshoot);
-
-	WaitKeyUp();
-	MenuFadeOut();
-}
-
-
-///////////////////////////
-//
-// DRAW CONTROL MENU SCREEN
-//
-void DrawCtlScreen(void)
-{
- s16int i,x,y;
-
- ClearMScreen();
- DrawStripes(10);
- VWB_DrawPic(80,0,Pctl);
- VWB_DrawPic(112,184,Pmouselback);
- DrawWindow(CTL_X-8,CTL_Y-5,CTL_W,CTL_H,BKGDCOLOR);
- 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,Psel);
- else
-   VWB_DrawPic(x,y,Punsel);
-
- y=CTL_Y+16;
- if (joystickenabled)
-   VWB_DrawPic(x,y,Psel);
- else
-   VWB_DrawPic(x,y,Punsel);
-
- y=CTL_Y+29;
- if (joystickport)
-   VWB_DrawPic(x,y,Psel);
- else
-   VWB_DrawPic(x,y,Punsel);
-
- y=CTL_Y+42;
- if (joypadenabled)
-   VWB_DrawPic(x,y,Psel);
- else
-   VWB_DrawPic(x,y,Punsel);
-
- //
- // 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)
-{
- s16int which;
-
-
- DrawCustomScreen();
- do
- {
-  which=HandleMenu(&CusItems,&CusMenu[0],FixupCustom);
-  switch(which)
-  {
-   case 0:
-	 DefineMouseBtns();
-	 DrawCustMouse(1);
-	 break;
-   case 3:
-	 // joystick
-	 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 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};
-s16int moveorder[4]={LEFT,RIGHT,FWRD,BKWD};
-
-void EnterCtrlData(s16int index,CustomCtrls *cust,void (*DrawRtn)(s16int),void (*PrintRtn)(s16int),s16int type)
-{
- s16int 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
-   {
-	s16int 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("?");
-	sfx(Shitwall);
-	 }
-	 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)
-	   {
-	s16int z;
-
-
-	for (z=0;z<4;z++)
-	  if (order[which]==buttonmouse[z])
-	  {
-	   buttonmouse[z]=bt_nobutton;
-	   break;
-	  }
-
-	buttonmouse[result-1]=order[which];
-	picked=1;
-	sfx(Shitdoor);
-	   }
-	   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)
-	   {
-	s16int z;
-
-
-	for (z=0;z<4;z++)
-	  if (order[which]==buttonjoy[z])
-	  {
-	   buttonjoy[z]=bt_nobutton;
-	   break;
-	  }
-
-	buttonjoy[result-1]=order[which];
-	picked=1;
-	sfx(Shitdoor);
-	   }
-	   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;
-	 sfx(Sdrawgun1);
-	 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;
-	 sfx(Sdrawgun1);
-	 while(ReadAnyControl(&ci),ci.dir!=dir_None);
-	 IN_ClearKeysDown();
-	 break;
-   case dir_North:
-   case dir_South:
-	 exit=1;
-  }
- } while(!exit);
-
- sfx(Sesc);
- WaitKeyUp();
- DrawWindow(5,PrintY-1,310,13,BKGDCOLOR);
-}
-
-
-////////////////////////
-//
-// FIXUP GUN CURSOR OVERDRAW SHIT
-//
-void FixupCustom(s16int w)
-{
-	static s16int lastwhich=-1;
-	s16int 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: /* joystick */ 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: /* joystick */ break;
-				case 6: DrawCustKeybd(0); break;
-				case 8: DrawCustKeys(0);
-			}
-	}
-
-	lastwhich=w;
-}
-
-
-////////////////////////
-//
-// DRAW CUSTOMIZE SCREEN
-//
-void DrawCustomScreen(void)
-{
-	s16int i;
-
-	ClearMScreen();
-	WindowX=0;
-	WindowW=320;
-	VWB_DrawPic(112,184,Pmouselback);
-	DrawStripes(10);
-	VWB_DrawPic(80,0,Pcustom);
-
-	//
-	// 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,Pmouse);
-#endif
-
-	SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR);
-	PrintX=CST_START;
-	US_Print("Run");
-	PrintX=CST_START+CST_SPC*1;
-	US_Print("Open");
-	PrintX=CST_START+CST_SPC*2;
-	US_Print("Fire");
-	PrintX=CST_START+CST_SPC*3;
-	US_Print("Strafe\n");
-
-	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,Pjs);
-#endif
-
-#ifdef SPEAR
-	VWB_DrawPic (112,120,Pkb);
-#endif
-
-	SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR);
-	PrintX=CST_START;
-	US_Print("Run");
-	PrintX=CST_START+CST_SPC*1;
-	US_Print("Open");
-	PrintX=CST_START+CST_SPC*2;
-	US_Print("Fire");
-	PrintX=CST_START+CST_SPC*3;
-	US_Print("Strafe\n");
-	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);
-	PrintX=CST_START;
-	US_Print("Run");
-	PrintX=CST_START+CST_SPC*1;
-	US_Print("Open");
-	PrintX=CST_START+CST_SPC*2;
-	US_Print("Fire");
-	PrintX=CST_START+CST_SPC*3;
-	US_Print("Strafe\n");
-	DrawWindow(5,PrintY-1,310,13,BKGDCOLOR);
-	DrawCustKeybd(0);
-	US_Print("\n");
-
-
-	//
-	// KEYBOARD MOVE KEYS
-	//
-	SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR);
-	PrintX=CST_START;
-	US_Print("Left");
-	PrintX=CST_START+CST_SPC*1;
-	US_Print("Right");
-	PrintX=CST_START+CST_SPC*2;
-	US_Print("Frwd");
-	PrintX=CST_START+CST_SPC*3;
-	US_Print("Bkwrd\n");
-	DrawWindow(5,PrintY-1,310,13,BKGDCOLOR);
-	DrawCustKeys(0);
-	//
-	// 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(s16int i)
-{
-	s16int 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(s16int hilight)
-{
-	s16int 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 PrintCustKeybd(s16int i)
-{
-	PrintX=CST_START+CST_SPC*i;
-	US_Print(IN_GetScanName(buttonscan[order[i]]));
-}
-
-void DrawCustKeybd(s16int hilight)
-{
-	s16int 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(s16int i)
-{
-	PrintX=CST_START+CST_SPC*i;
-	US_Print(IN_GetScanName(dirscan[moveorder[i]]));
-}
-
-void DrawCustKeys(s16int hilight)
-{
-	s16int 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)
-{
-	s16int exit=0,oldview,newview;
-	ControlInfo ci;
-
-
-	WindowX=WindowY=0;
-	WindowW=320;
-	WindowH=200;
-	newview=oldview=vw.dx/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();
-			sfx(Shitwall);
-			TicDelay(10);
-			break;
-
-		case dir_North:
-		case dir_East:
-			newview++;
-			if (newview>19)
-				newview=19;
-			ShowViewSize(newview);
-			VW_UpdateScreen();
-			sfx(Shitwall);
-			TicDelay(10);
-			break;
-		}
-
-		if (ci.button0 || Keyboard[sc_Enter])
-			exit=1;
-		else
-		if (ci.button1 || Keyboard[sc_Escape])
-		{
-			vw.dx=oldview*16;
-			sfx(Sesc);
-			MenuFadeOut();
-			return;
-		}
-
-	} while(!exit);
-
-
-	if (oldview!=newview)
-	{
-		sfx (Sshoot);
-		Message("Thinking...");
-		NewViewSize(newview);
-	}
-
-	ShootSnd();
-	MenuFadeOut();
-}
-
-
-/////////////////////////////
-//
-// DRAW THE CHANGEVIEW SCREEN
-//
-void DrawChangeView(s16int view)
-{
-	VWB_Bar(0,160,320,40,VIEWCOLOR);
-	ShowViewSize(view);
-
-	PrintY=161;
-	WindowX=0;
-	WindowY=320;
-	SETFONTCOLOR(HIGHLIGHT,BKGDCOLOR);
-
-	US_CPrint("Use arrows to size\n");
-	US_CPrint("ENTER to accept\n");
-	US_CPrint("ESC to cancel");
-	VW_UpdateScreen();
-
-	MenuFadeIn();
-}
-
-
-////////////////////////////////////////////////////////////////////
-//
-// QUIT THIS INFERNAL GAME!
-//
-////////////////////////////////////////////////////////////////////
-void CP_Quit(void)
-{
-	s16int i;
-
-	if (Confirm(endStrings[rnd()&0x7+(rnd()&1)]))
-	{
-		VW_UpdateScreen();
-		SD_MusicOff();
-		SD_StopSound();
-		MenuFadeOut();
-		//
-		// SHUT-UP THE ADLIB
-		//
-		for (i=1;i<=0xf5;i++)
-			alOut(i,0);
-		Quit(NULL);
-	}
-
-	DrawMainMenu();
-}
-
-////////////////////////////////////////////////////////////////////
-//
-// Draw a window for a menu
-//
-////////////////////////////////////////////////////////////////////
-void DrawWindow(s16int x,s16int y,s16int w,s16int h,s16int wcolor)
-{
-	VWB_Bar(x,y,w,h,wcolor);
-	DrawOutline(x,y,w,h,BORD2COLOR,DEACTIVE);
-}
-
-
-void DrawOutline(s16int x,s16int y,s16int w,s16int h,s16int color1,s16int 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];
-	s16int which,i;
-
-
 	//
-	// CACHE GRAPHICS & SOUNDS
-	//
-
-	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);
@@ -2256,551 +535,8 @@
 				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)
-{
-	fontnumber = 0;
-}
-
-
-////////////////////////////////////////////////////////////////////
-//
-// Handle moving gun around a menu
-//
-////////////////////////////////////////////////////////////////////
-s16int HandleMenu(CP_iteminfo *item_i,CP_itemtype far *items,void (*routine)(s16int w))
-{
-	char key;
-	static s16int redrawitem=1,lastitem=-1;
-	s16int 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,Pcur1);
-	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=Pcur1;
-	timer=8;
-	exit=0;
-	TimeCount=0;
-	IN_ClearKeysDown();
-
-
-	do
-	{
-		//
-		// CHANGE GUN SHAPE
-		//
-		if (TimeCount>timer)
-		{
-			TimeCount=0;
-			if (shape==Pcur1)
-			{
-				shape=Pcur2;
-				timer=8;
-			}
-			else
-			{
-				shape=Pcur1;
-				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)
-		{
-			s16int ok=0;
-
-			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:
-			sfx(Sesc);
-			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,s16int x,s16int y,s16int 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(s16int x,s16int y)
-{
-	VWB_DrawPic(x,y,Pcur1);
-	VW_UpdateScreen();
-	sfx(Sdrawgun1);
-	TimeCount=0;
-	while(TimeCount<8);
-}
-
-
-//
-// DRAW GUN AT NEW POSITION
-//
-void DrawGun(CP_iteminfo *item_i,CP_itemtype far *items,s16int x,s16int *y,s16int which,s16int basey,void (*routine)(s16int w))
-{
-	VWB_Bar(x-1,*y,25,16,BKGDCOLOR);
-	*y=basey+which*13;
-	VWB_DrawPic(x,*y,Pcur1);
-	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();
-	sfx(Sdrawgun2);
-}
-
-////////////////////////////////////////////////////////////////////
-//
-// DELAY FOR AN AMOUNT OF TICS OR UNTIL CONTROLS ARE INACTIVE
-//
-////////////////////////////////////////////////////////////////////
-void TicDelay(s16int 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)
-{
-	s16int 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");
-	}
-}
-
-void SetTextColor(CP_itemtype far *items,s16int 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)
-{
-	s16int mouseactive=0;
-
-
-	IN_ReadControl(0,ci);
-
-	if (mouseenabled)
-	{
-		s16int 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;
-		}
-	}
-}
-
-
-////////////////////////////////////////////////////////////////////
-//
-// DRAW DIALOG AND CONFIRM YES OR NO TO QUESTION
-//
-////////////////////////////////////////////////////////////////////
-s16int Confirm(char far *string)
-{
-	s16int xit=0,i,x,y,tick=0,time,whichsnd[2]={Sesc,Sshoot};
-
-
-	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;
-		}
-	} while(!Keyboard[sc_Y] && !Keyboard[sc_N] && !Keyboard[sc_Escape]);
-
-	if (Keyboard[sc_Y])
-	{
-		xit=1;
-		ShootSnd();
-	}
-
-	while(Keyboard[sc_Y] || Keyboard[sc_N] || Keyboard[sc_Escape]);
-
-	IN_ClearKeysDown();
-	sfx(whichsnd[xit]);
-	return xit;
-}
-
-////////////////////////////////////////////////////////////////////
-//
-// PRINT A MESSAGE IN A WINDOW
-//
-////////////////////////////////////////////////////////////////////
-void Message(char far *string)
-{
-	s16int h=0,w=0,mw=0,i,x,y,time;
-	fontstruct _seg *font;
-
-	fontnumber=1;
-	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();
-}
-
 void StartCPMusic(s16int song)
 {
 	SD_MusicOff();
@@ -2807,31 +543,6 @@
 	SD_mapmus((MusicGroup far *)audiosegs[STARTMUSIC + song]);
 }
 
-///////////////////////////////////////////////////////////////////////////
-//
-//	IN_GetScanName() - Returns a string containing the name of the
-//		specified scan code
-//
-///////////////////////////////////////////////////////////////////////////
-u8int *
-IN_GetScanName(u8int scan)
-{
-	u8int		**p;
-	u8int	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)
@@ -2846,33 +557,5 @@
 		VW_WaitVBL(3);
 		IN_ClearKeysDown();
 		Paused=false;
- }
-}
-
-
-///////////////////////////////////////////////////////////////////////////
-//
-// DRAW GUN CURSOR AT CORRECT POSITION IN MENU
-//
-///////////////////////////////////////////////////////////////////////////
-void DrawMenuGun(CP_iteminfo *iteminfo)
-{
-	s16int x,y;
-
-
-	x=iteminfo->x;
-	y=iteminfo->y+iteminfo->curpos*13-2;
-	VWB_DrawPic(x,y,Pcur1);
-}
-
-
-///////////////////////////////////////////////////////////////////////////
-//
-// DRAW SCREEN TITLE STRIPES
-//
-///////////////////////////////////////////////////////////////////////////
-
-void ShootSnd(void)
-{
-	sfx(Sshoot);
+	}
 }
--- a/menu.h
+++ /dev/null
@@ -1,216 +1,0 @@
-//
-// WL_MENU.H
-//
-#ifdef SPEAR
-
-#define BORDCOLOR	0x99
-#define BORD2COLOR	0x93
-#define DEACTIVE	0x9b
-#define BKGDCOLOR	0x9d
-
-#define MenuFadeOut()	VL_FadeOut(0,255,0,0,51,10)
-
-#else
-
-#define BORDCOLOR	0x29
-#define BORD2COLOR	0x23
-#define DEACTIVE	0x2b
-#define BKGDCOLOR	0x2d
-
-#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)
-
-
-#ifndef SPEAR
-#define INTROSONG	7
-#else
-#define INTROSONG	23
-#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 {
-		s16int x,y,amount,curpos,indent;
-		} CP_iteminfo;
-
-typedef struct {
-		s16int active;
-		char string[36];
-		void (* routine)(s16int temp1);
-		} CP_itemtype;
-
-typedef struct {
-		s16int 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);
-s16int  HandleMenu(CP_iteminfo *item_i,
-		CP_itemtype far *items,
-		void (*routine)(s16int w));
-void ClearMScreen(void);
-void DrawWindow(s16int x,s16int y,s16int w,s16int h,s16int wcolor);
-void DrawOutline(s16int x,s16int y,s16int w,s16int h,s16int color1,s16int color2);
-void WaitKeyUp(void);
-void ReadAnyControl(ControlInfo *ci);
-void TicDelay(s16int count);
-void StartCPMusic(s16int song);
-s16int  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,s16int x,s16int *y,s16int which,s16int basey,void (*routine)(s16int w));
-void DrawHalfStep(s16int x,s16int y);
-void EraseGun(CP_iteminfo *item_i,CP_itemtype far *items,s16int x,s16int y,s16int which);
-void SetTextColor(CP_itemtype far *items,s16int hlight);
-void DrawMenuGun(CP_iteminfo *iteminfo);
-void DrawStripes(s16int y);
-
-void DefineMouseBtns(void);
-void DefineKeyBtns(void);
-void DefineKeyMove(void);
-void EnterCtrlData(s16int index,CustomCtrls *cust,void (*DrawRtn)(s16int),void (*PrintRtn)(s16int),s16int type);
-
-void DrawMainMenu(void);
-void DrawSoundMenu(void);
-void DrawLoadSaveScreen(s16int loadsave);
-void DrawNewEpisode(void);
-void DrawNewGame(void);
-void DrawChangeView(s16int view);
-void DrawMouseSens(void);
-void DrawCtlScreen(void);
-void DrawCustomScreen(void);
-void DrawLSAction(s16int which);
-void DrawCustMouse(s16int hilight);
-void DrawCustKeybd(s16int hilight);
-void DrawCustKeys(s16int hilight);
-void PrintCustMouse(s16int i);
-void PrintCustKeybd(s16int i);
-void PrintCustKeys(s16int i);
-
-void PrintLSEntry(s16int w,s16int color);
-void TrackWhichGame(s16int w);
-void DrawNewGameDiff(s16int w);
-void FixupCustom(s16int w);
-
-void CP_NewGame(void);
-void CP_Sound(void);
-s16int  CP_LoadGame(s16int quick);
-s16int  CP_SaveGame(s16int quick);
-void CP_Control(void);
-void CP_ChangeView(void);
-void CP_ExitOptions(void);
-void CP_Quit(void);
-void CP_ViewScores(void);
-s16int  CP_EndGame(void);
-s16int  CP_CheckQuick(u16int scancode);
-void CustomControls(void);
-void MouseSensitivity(void);
-
-//
-// VARIABLES
-//
-extern s16int SaveGamesAvail[10],StartGame,SoundStatus;
-extern char SaveGameNames[10][32],SaveName[13];
-
-enum {MOUSE,JOYSTICK,KEYBOARDBTNS,KEYBOARDMOVE};	// FOR INPUT TYPES
-
-enum
-{
-	newgame,
-	soundmenu,
-	control,
-	loadgame,
-	savegame,
-	changeview,
-
-#ifndef GOODTIMES
-#ifndef SPEAR
-	readthis,
-#endif
-#endif
-
-	viewscores,
-	backtodemo,
-	quit
-} menuitems;
-
-//
-// WL_INTER
-//
-typedef struct {
-		s16int kill,secret,treasure;
-		s32int time;
-		} LRstruct;
-
-extern LRstruct LevelRatios[];
-
-void Write (s16int x,s16int y,char *string);
--- a/play.c
+++ b/play.c
@@ -1,49 +1,6 @@
-exit_t		gm.φ;
-s16int			DebugOk;
-objtype 	objlist[Nobj],*new,*obj,*player,*lastobj,
-			*objfreelist,*killer;
-u16int	farmapylookup[MAPSIZE];
-int		onestep,godmode,noclip;
-s16int			extravbls;
-u8int		tilemap[MAPSIZE][MAPSIZE];	// wall values only
-u8int		spotvis[MAPSIZE][MAPSIZE];
-objtype		*actorat[MAPSIZE][MAPSIZE];
-
-//
-// replacing refresh manager
-//
-u16int	mapwidth,mapheight,tics;
-int		compatability;
-u8int		*updateptr;
-u16int	mapwidthtable[64];
-u16int	uwidthtable[UPDATEHIGH];
-u16int	blockstarts[UPDATEWIDE*UPDATEHIGH];
-u8int		update[UPDATESIZE];
-
-//
-// control info
-//
-int		mouseenabled,joystickenabled,joypadenabled,joystickprogressive;
-s16int			joystickport;
-s16int			dirscan[4] = {sc_UpArrow,sc_RightArrow,sc_DownArrow,sc_LeftArrow};
-s16int			buttonscan[NUMBUTTONS] =
-			{sc_Control,sc_Alt,sc_RShift,sc_Space,sc_1,sc_2,sc_3,sc_4};
-s16int			buttonmouse[4]={bt_attack,bt_strafe,bt_use,bt_nobutton};
-s16int			buttonjoy[4]={bt_attack,bt_strafe,bt_use,bt_run};
-
-int		buttonheld[NUMBUTTONS];
-
 char		far *demoptr, far *lastdemoptr;
 uchar *demobuffer;
 
-int		buttonstate[NUMBUTTONS];
-
-objtype dummyobj;
-
-#define BASETURN		35
-#define RUNTURN			70
-#define JOYSCALE		2
-
 void CheckKeys (void)
 {
 	s16int		i;
@@ -56,116 +13,6 @@
 
 	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");
-			sfx (Snobonus);
-		}
-		else
-		{
-			Message ("God mode ON");
-			sfx (Sendb2);
-		}
-
-		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;
-		gm.pt = 0;
-		gm.lvltc += 42000L;
-		givew (WPgatling);
-
-		hudw();
-		hudh();
-		hudk();
-		huda();
-		hudp();
-
-		ClearMemory ();
-		ClearSplitVWB ();
-		VW_ScreenToScreen (displayofs,bufferofs,80,160);
-
-		Message("You now have 100% Health,\n"
-			"99 Ammo and both Keys!\n\n"
-			"Note that you have basically\n"
-			"eliminated your chances of\n"
-			"getting a high score!");
-		PM_CheckMainMem ();
-		IN_ClearKeysDown();
-		IN_Ack();
-
-		DrawAllPlayBorder ();
-	}
-
-	//
-	// OPEN UP DEBUG KEYS
-	//
-	if (Keyboard[sc_BackSpace] &&
-		Keyboard[sc_LShift] &&
-		Keyboard[sc_Alt] &&
-		debug)
-	{
-	 ClearMemory ();
-	 ClearSplitVWB ();
-	 VW_ScreenToScreen (displayofs,bufferofs,80,160);
-
-	 Message("Debugging keys are\nnow available!");
-	 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 ();
-	 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?!");
-
-	 PM_CheckMainMem ();
-	 IN_ClearKeysDown();
-	 IN_Ack();
-
-	 DrawAllPlayBorder ();
-	}
-
 //
 // pause key weirdness can't be checked as a scan code
 //
@@ -233,20 +80,4 @@
 		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;
-	}
-
 }
--- a/rend.c
+++ b/rend.c
@@ -4,6 +4,7 @@
 #include "fns.h"
 
 s32int sint[360+90], *cost;
+int vwsize;
 
 typedef struct Vis Vis;
 typedef struct Scaler Scaler;
@@ -177,7 +178,7 @@
 topspr(void)
 {
 	if(ver < SDM && gm.won){
-		if(oplr->s == stt+GSplrcam && mtc & 32)
+		if(oplr->s == stt+GSplrcam && qtc & 32)
 			scalspr(SPcam, vw.dx/2, vw.dy+1);
 		return;
 	}
@@ -644,7 +645,7 @@
 	n = tiles[x].tl;
 	if(n == 0){
 vpass:
-		tiles[x].vis++;
+		tiles[x].vis = 1;
 		tx += dtx;
 		rs = (yinh << 16 | yin & 0xffff) + dy;
 		yinh = rs >> 16;
@@ -688,7 +689,7 @@
 	n = tiles[y].tl;
 	if(n == 0){
 hpass:
-		tiles[y].vis++;
+		tiles[y].vis = 1;
 		ty += dty;
 		rs = (xinh << 16 | xin & 0xffff) + dx;
 		xinh = rs >> 16;
@@ -753,22 +754,14 @@
 static void
 scaltab(int maxdy)
 {
-	int save, dy;
+	int dy;
 	Scaler (*s)[nelem(scals[0])];
 
 	dy = 1;
 	memset(scals, 0, sizeof scals);
 	s = scals + 1;
-	save = vw.dy / 2;
-	while(dy <= maxdy){
-		calcscal(*s++, dy * 2);
-		if(dy >= save){
-			memcpy(s[0], s[-1], sizeof scals[0]);
-			memcpy(s[1], s[-1], sizeof scals[0]);
-			s += 2, dy += 2;
-		}
-		dy++;
-	}
+	while(dy <= maxdy)
+		calcscal(*s++, dy++ * 2);
 	memcpy(scals, scals+1, sizeof *scals);
 	sce = scals[dy-1];
 }
@@ -843,41 +836,32 @@
 }
 
 void
-initscal(void)
+setvw(void)
 {
 	int i, an, dx, *p, *q, *e;
 	double dface;
 
+	vw.dx = vwsize * 16 & ~15;
+	vw.dy = vwsize * 16 / 2 & ~1;
+	vw.mid = vw.dx / 2 - 1;
+	vw.Δhit = vw.dx / 10;
+	vw.ofs = Vw * (160 - vw.dy) / 2 + (Vw - vw.dx) / 2;
+	scaltab((vw.dx * 1.5) / 2);
+
 	dx = vw.dx / 2;
 	dface = Dfoclen + Dmin;
-	prjw = dx * dface / (Dglob/2);
-	prjh = prjw * Dtlglobal >> 6;
-
 	i = 0;
 	p = Δvwθ + dx;
 	e = p + dx;
 	q = p - 1;
-	dx = vw.dx;
 	while(p < e){
 		/* start 0.5px over so vw.θ bisects two middle pixels */
-		an = (float)atan(i++ * Dglob / dx / dface) * Rad;
+		an = (float)atan(i++ * Dglob / vw.dx / dface) * Rad;
 		*p++ = -an;
 		*q-- = an;
 	}
-
-	scaltab((vw.dx * 1.5) / 2);
-}
-
-void
-setvw(int n)
-{
-	vw.size = n;
-	vw.dx = n * 16 & ~15;
-	vw.dy = n * 16 / 2 & ~1;
-	vw.mid = vw.dx / 2 - 1;
-	vw.Δhit = vw.dx / 10;
-	vw.ofs = Vw * (160 - vw.dy) / 2 + (Vw - vw.dx) / 2;
-	initscal();
+	prjw = dx * dface / (Dglob/2);
+	prjh = prjw * Dtlglobal >> 6;
 }
 
 void
--- a/snd.c
+++ b/snd.c
@@ -219,7 +219,6 @@
 	if(sfxd == nil)
 		return;
 	opl2wr(Roct, 0);
-	opl2wr(Rfed, 0);
 	sfxp = sfxe = nil;
 	sfxd = nil;
 }
@@ -265,11 +264,13 @@
 /* this spins constantly even when there's no music being played continuously,
  * in essence only because some sound effects need an echo to play correctly.
  * it sucks. */
-static void
+static int
 opl2step(void)
 {
 	uchar *p;
 
+	if(!muson && !sfxon)
+		return -1;
 	p = sbuf;
 	while(p < sbuf + sizeof sbuf){
 		if(stc == sdt && sfxd != nil)
@@ -279,6 +280,7 @@
 		p = opl2out(p, Nsamp);
 		stc++;
 	}
+	return 0;
 }
 
 /* stolen property cargocult upsampling */
@@ -421,9 +423,13 @@
 void
 sndstep(void)
 {
-	if(!muson && !sfxon)
+	if(sfd < 0)
 		return;
-	opl2step();
+	if(opl2step() < 0){
+		if(!pcmon)
+			return;
+		memset(sbuf, 0, sizeof sbuf);
+	}
 	pcmstep();
 	if(!nosleep)
 		write(sfd, sbuf, sizeof sbuf);
@@ -432,6 +438,8 @@
 void
 stopsfx(void)
 {
+	if(sfd < 0)
+		return;
 	stopal();
 	pcm = pcme = nil;
 	pcmd = nil;
@@ -453,7 +461,7 @@
 	Sfx *s;
 	uchar *r, *i;
 
-	if(sfd < 0 || !sfxon || sfxlck)
+	if(sfd < 0 || sfxlck)
 		return;
 	s = sfxs+n;
 	if(pcmon && s->pcm != nil){
@@ -470,7 +478,7 @@
 			atty = y;
 		}else
 			atton = 0;
-	}else{
+	}else if(sfxon){
 		if(sfxd != nil && s->pri < sfxd->pri)
 			return;
 		stopal();
@@ -496,10 +504,11 @@
 {
 	int i;
 
+	if(sfd < 0)
+		return;
 	stopsfx();
 	if(!muson && !sfxon)
 		return;
-	opl2wr(Ropm, 0);
 	for(i=Roct+1; i<Roct+9; i++)
 		opl2wr(i, 0);
 	imf = nil;
@@ -532,8 +541,9 @@
 	}
 	opl2init(Rate);
 	opl2wr(Rwse, 0x20);
+	opl2wr(Ropm, 0);
+	opl2wr(Rfed, 0);
 	for(n=0; n<nelem(hΔ)-1; n++)
 		hΔ[n] = h[n+1] - h[n];
 	sfd = fd;
-	muson = sfxon = pcmon = 1;
 }
--- a/us.h
+++ b/us.h
@@ -1,8 +1,3 @@
-#define	MaxX	320
-#define	MaxY	200
-
-#define	MaxHelpLines	500
-
 #define	MaxHighName	57
 #define	MaxScores	7
 typedef	struct
@@ -12,91 +7,6 @@
 			u16int	completed,episode;
 		} HighScore;
 
-#define	MaxGameName		32
-#define	MaxSaveGames	6
-typedef	struct
-		{
-			char	signature[4];
-			u16int	*oldtest;
-			int	present;
-			char	name[MaxGameName + 1];
-		} SaveGame;
-
 #define	MaxString	128	// Maximum input string size
 
-typedef	struct
-		{
-			s16int	x,y,
-				w,h,
-				px,py;
-		} WindowRec;	// Record used to save & restore screen windows
-
-typedef	enum
-		{
-			gd_Continue,
-			gd_Easy,
-			gd_Normal,
-			gd_Hard
-		} GameDiff;
-
-//	Hack import for TED launch support
-extern	int		tedlevel;
-extern	s16int			tedlevelnum;
-extern	void		TEDDeath(void);
-
-extern	int		ingame,		// Set by game code if a game is in progress
-					abortgame,	// Set if a game load failed
-					gm.load,	// Set if the current game was loaded
-					NoWait,
-					HighScoresDirty;
-extern	char		*abortprogram;	// Set to error msg if program is dying
-extern	GameDiff	restartgame;	// Normally gd_Continue, else starts game
-extern	u16int		PrintX,PrintY;	// Current printing location in the window
-extern	u16int		WindowX,WindowY,// Current location of window
-					WindowW,WindowH;// Current size of window
-
-extern	int		Button0,Button1,
-					CursorBad;
-extern	s16int			CursorX,CursorY;
-
-extern	void		(*USL_MeasureString)(char far *,u16int *,u16int *),
-					(*USL_DrawString)(char far *);
-
-extern	int		(*USL_SaveGame)(s16int),(*USL_LoadGame)(s16int);
-extern	void		(*USL_ResetGame)(void);
-extern	SaveGame	Games[MaxSaveGames];
 extern	HighScore	Scores[];
-
-#define	US_HomeWindow()	{PrintX = WindowX; PrintY = WindowY;}
-
-extern	void	US_Startup(void),
-				US_Setup(void),
-				US_Shutdown(void),
-				US_SetLoadSaveHooks(int (*load)(s16int),
-									int (*save)(s16int),
-									void (*reset)(void)),
-				US_TextScreen(void),
-				US_UpdateTextScreen(void),
-				US_FinishTextScreen(void),
-				US_DrawWindow(u16int x,u16int y,u16int w,u16int h),
-				US_CenterWindow(u16int,u16int),
-				US_SaveWindow(WindowRec *win),
-				US_RestoreWindow(WindowRec *win),
-				US_ClearWindow(void),
-				US_SetPrintRoutines(void (*measure)(char far *,u16int *,u16int *),
-									void (*print)(char far *)),
-				US_PrintCentered(char far *s),
-				US_CPrint(char far *s),
-				US_CPrintLine(char far *s),
-				US_Print(char far *s),
-				US_PrintUnsigned(u32int n),
-				US_PrintSigned(s32int n),
-				US_StartCursor(void),
-				US_ShutCursor(void),
-				US_CheckHighScore(s32int score,u16int other),
-extern	int	US_UpdateCursor(void),
-				US_LineInput(s16int x,s16int y,char *buf,char *def,int escok,
-								s16int maxchars,s16int maxwidth);
-
-		void	USL_PrintInCenter(char far *s,Rct r);
-		char 	*USL_GiveSaveName(u16int game);
--- a/us1.c
+++ b/us1.c
@@ -1,212 +1,3 @@
-		char		*abortprogram;
-		int		NoWait;
-		u16int		PrintX,PrintY;
-		u16int		WindowX,WindowY,WindowW,WindowH;
-
-		int		Button0,Button1,
-					CursorBad;
-		s16int			CursorX,CursorY;
-
-		void		(*USL_MeasureString)(char far *,u16int *,u16int *) = VW_MeasurePropString,
-					(*USL_DrawString)(char far *) = VWB_DrawPropString;
-
-		SaveGame	Games[MaxSaveGames];
-
-//	Window/Printing routines
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	US_Print() - Prints a string in the current window. Newlines are
-//		supported.
-//
-///////////////////////////////////////////////////////////////////////////
-void
-US_Print(char far *s)
-{
-	char	c,far *se;
-	u16int	w,h;
-
-	while (*s)
-	{
-		se = s;
-		while ((c = *se) && (c != '\n'))
-			se++;
-		*se = '\0';
-
-		USL_MeasureString(s,&w,&h);
-		px = PrintX;
-		py = PrintY;
-		USL_DrawString(s);
-
-		s = se;
-		if (c)
-		{
-			*se = c;
-			s++;
-
-			PrintX = WindowX;
-			PrintY += h;
-		}
-		else
-			PrintX += w;
-	}
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	US_PrintUnsigned() - Prints an u32int
-//
-///////////////////////////////////////////////////////////////////////////
-void
-US_PrintUnsigned(u32int n)
-{
-	char	buffer[32];
-
-	US_Print(ultoa(n,buffer,10));
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	US_PrintSigned() - Prints a s32int
-//
-///////////////////////////////////////////////////////////////////////////
-void
-US_PrintSigned(s32int n)
-{
-	char	buffer[32];
-
-	US_Print(ltoa(n,buffer,10));
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	USL_PrintInCenter() - Prints a string in the center of the given rect
-//
-///////////////////////////////////////////////////////////////////////////
-void
-USL_PrintInCenter(char far *s,Rectangle r)
-{
-	u16int	w,h;
-
-	USL_MeasureString(s,&w,&h);
-
-	px = r.min.x + ((Dx(r) - w) / 2);
-	py = r.min.y + ((Dy(r) - h) / 2);
-	USL_DrawString(s);
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	US_PrintCentered() - Prints a string centered in the current window.
-//
-///////////////////////////////////////////////////////////////////////////
-void
-US_PrintCentered(char far *s)
-{
-	USL_PrintInCenter(s,Rect(WindowX,WindowY,WindowW+WindowX,WindowH+WindowY);
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	US_CPrintLine() - Prints a string centered on the current line and
-//		advances to the next line. Newlines are not supported.
-//
-///////////////////////////////////////////////////////////////////////////
-void
-US_CPrintLine(char far *s)
-{
-	u16int	w,h;
-
-	USL_MeasureString(s,&w,&h);
-
-	if (w > WindowW)
-		Quit("US_CPrintLine() - String exceeds width");
-	px = WindowX + ((WindowW - w) / 2);
-	py = PrintY;
-	USL_DrawString(s);
-	PrintY += h;
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	US_CPrint() - Prints a string in the current window. Newlines are
-//		supported.
-//
-///////////////////////////////////////////////////////////////////////////
-void
-US_CPrint(char far *s)
-{
-	char	c,far *se;
-
-	while (*s)
-	{
-		se = s;
-		while ((c = *se) && (c != '\n'))
-			se++;
-		*se = '\0';
-
-		US_CPrintLine(s);
-
-		s = se;
-		if (c)
-		{
-			*se = c;
-			s++;
-		}
-	}
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	US_ClearWindow() - Clears the current window to white and homes the
-//		cursor
-//
-///////////////////////////////////////////////////////////////////////////
-void
-US_ClearWindow(void)
-{
-	VWB_Bar(WindowX,WindowY,WindowW,WindowH,WHITE);
-	PrintX = WindowX;
-	PrintY = WindowY;
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	US_DrawWindow() - Draws a frame and sets the current window parms
-//
-///////////////////////////////////////////////////////////////////////////
-void
-US_DrawWindow(u16int x,u16int y,u16int w,u16int h)
-{
-	u16int	i,
-			sx,sy,sw,sh;
-
-	WindowX = x * 8;
-	WindowY = y * 8;
-	WindowW = w * 8;
-	WindowH = h * 8;
-
-	PrintX = WindowX;
-	PrintY = WindowY;
-
-	sx = (x - 1) * 8;
-	sy = (y - 1) * 8;
-	sw = (w + 1) * 8;
-	sh = (h + 1) * 8;
-
-	US_ClearWindow();
-
-	VWB_DrawTile8(sx,sy,0),VWB_DrawTile8(sx,sy + sh,5);
-	for (i = sx + 8;i <= sx + sw - 8;i += 8)
-		VWB_DrawTile8(i,sy,1),VWB_DrawTile8(i,sy + sh,6);
-	VWB_DrawTile8(i,sy,2),VWB_DrawTile8(i,sy + sh,7);
-
-	for (i = sy + 8;i <= sy + sh - 8;i += 8)
-		VWB_DrawTile8(sx,i,3),VWB_DrawTile8(sx + sw,i,4);
-}
-
-//	Input routines
-
 ///////////////////////////////////////////////////////////////////////////
 //
 //	USL_XORICursor() - XORs the I-bar text cursor. Used by US_LineInput()
--- a/vh.asm
+++ /dev/null
@@ -1,110 +1,0 @@
-; ID_VL.ASM
-
-	IDEAL
-	MODEL	MEDIUM,C
-
-	INCLUDE	'ID_VL.EQU'
-
-SCREENSEG	=	0a000h
-
-UPDATEWIDE	=	20
-UPDATEHIGH	=	13
-
-	DATASEG
-
-
-EXTRN	bufferofs			:WORD
-EXTRN	displayofs			:WORD
-EXTRN	ylookup				:WORD
-EXTRN	linewidth			:WORD
-EXTRN	blockstarts			:WORD	;offsets from drawofs for each update block
-
-EXTRN	update				:BYTE
-
-	CODESEG
-
-
-;=================
-;
-; VH_UpdateScreen
-;
-;=================
-
-PROC	VH_UpdateScreen
-PUBLIC	VH_UpdateScreen
-USES	si,di
-
-	mov	dx,SC_INDEX
-	mov	ax,SC_MAPMASK+15*256
-	out	dx,ax
-
-	mov dx,GC_INDEX
-	mov al,GC_MODE
-	out dx,al
-
-	inc dx
-	in	al,dx
-	and al,252
-	or	al,1
-	out dx,al
-
-	mov	bx,UPDATEWIDE*UPDATEHIGH-1		; bx is the tile number
-	mov	dx,[linewidth]
-
-;
-; see if the tile needs to be copied
-;
-@@checktile:
-	test	[update+bx],1
-	jnz	@@copytile
-@@next:
-	dec	bx
-	jns	@@checktile
-
-;
-; done
-;
-	mov dx,GC_INDEX+1
-	in	al,dx
-	and al,NOT 3
-	or	al,0
-	out dx,al
-	ret
-
-;
-; copy a tile
-;
-@@copytile:
-	mov	[update+bx],0
-	shl	bx,1
-	mov	si,[blockstarts+bx]
-	shr	bx,1
-	mov	di,si
-	add	si,[bufferofs]
-	add	di,[displayofs]
-
-	mov	ax,SCREENSEG
-	mov	ds,ax
-
-REPT	16
-	mov	al,[si]
-	mov	[di],al
-	mov	al,[si+1]
-	mov	[di+1],al
-	mov	al,[si+2]
-	mov	[di+2],al
-	mov	al,[si+3]
-	mov	[di+3],al
-	add	si,dx
-	add	di,dx
-ENDM
-
-	mov	ax,ss
-	mov	ds,ax
-	jmp	@@next
-
-ENDP
-
-
-	END
-
--- a/vh.c
+++ /dev/null
@@ -1,230 +1,0 @@
-#define WHITE			15			// graphics mode independant colors
-#define BLACK			0
-#define FIRSTCOLOR		1
-#define SECONDCOLOR		12
-#define F_WHITE			15
-#define F_BLACK			0
-#define F_FIRSTCOLOR	1
-#define F_SECONDCOLOR	12
-
-#define MAXSHIFTS	1
-
-typedef struct
-{
-	s16int width,height;
-} pictabletype;
-
-typedef struct
-{
-	s16int height;
-	s16int location[256];
-	char width[256];
-} fontstruct;
-
-extern	pictabletype	_seg *pictable;
-extern	pictabletype	_seg *picmtable;
-extern	spritetabletype _seg *spritetable;
-
-extern	u8int	fontcolor;
-extern	s16int	fontnumber;
-extern	s16int	px,py;
-
-#define VW_SetCRTC		VL_SetCRTC
-#define VW_SetScreen	VL_SetScreen
-#define VW_Bar			VL_Bar
-#define VW_Plot			VL_Plot
-#define VW_Hlin(x,z,y,c)	VL_Hlin(x,y,(z)-(x)+1,c)
-#define VW_Vlin(y,z,x,c)	VL_Vlin(x,y,(z)-(y)+1,c)
-#define VW_DrawPic		VH_DrawPic
-#define VW_ColorBorder	VL_ColorBorder
-#define VW_WaitVBL		VL_WaitVBL
-#define VW_FadeIn()		VL_FadeIn(0,255,&gamepal,30);
-#define VW_FadeOut()	VL_FadeOut(0,255,0,0,0,30);
-#define VW_ScreenToScreen	VL_ScreenToScreen
-#define VW_SetDefaultColors	VH_SetDefaultColors
-#define EGAMAPMASK(x)	VGAMAPMASK(x)
-#define EGAWRITEMODE(x)	VGAWRITEMODE(x)
-#define LatchDrawChar(x,y,p) VL_LatchToScreen(latchpics[0]+(p)*16,2,8,x,y)
-#define LatchDrawTile(x,y,p) VL_LatchToScreen(latchpics[1]+(p)*64,4,16,x,y)
-#define NUMLATCHPICS	100
-
-#define PIXTOBLOCK		4		// 16 pixels to an update block
-
-u8int	update[UPDATEHIGH][UPDATEWIDE];
-
-//==========================================================================
-
-pictabletype	_seg *pictable;
-
-
-s16int	px,py;
-u8int	fontcolor,backcolor;
-s16int	fontnumber;
-s16int bufferwidth,bufferheight;
-
-
-/*
-=============================================================================
-
-				Double buffer management routines
-
-=============================================================================
-*/
-
-
-/*
-=======================
-=
-= VW_MarkUpdateBlock
-=
-= Takes a pixel bounded block and marks the tiles in bufferblocks
-= Returns 0 if the entire block is off the buffer screen
-=
-=======================
-*/
-
-s16int VW_MarkUpdateBlock (s16int x1, s16int y1, s16int x2, s16int y2)
-{
-	s16int	x,y,xt1,yt1,xt2,yt2,nextline;
-	u8int *mark;
-
-	xt1 = x1>>PIXTOBLOCK;
-	yt1 = y1>>PIXTOBLOCK;
-
-	xt2 = x2>>PIXTOBLOCK;
-	yt2 = y2>>PIXTOBLOCK;
-
-	if (xt1<0)
-		xt1=0;
-	else if (xt1>=UPDATEWIDE)
-		return 0;
-
-	if (yt1<0)
-		yt1=0;
-	else if (yt1>UPDATEHIGH)
-		return 0;
-
-	if (xt2<0)
-		return 0;
-	else if (xt2>=UPDATEWIDE)
-		xt2 = UPDATEWIDE-1;
-
-	if (yt2<0)
-		return 0;
-	else if (yt2>=UPDATEHIGH)
-		yt2 = UPDATEHIGH-1;
-
-	mark = updateptr + uwidthtable[yt1] + xt1;
-	nextline = UPDATEWIDE - (xt2-xt1) - 1;
-
-	for (y=yt1;y<=yt2;y++)
-	{
-		for (x=xt1;x<=xt2;x++)
-			*mark++ = 1;			// this tile will need to be updated
-
-		mark += nextline;
-	}
-
-	return 1;
-}
-
-void VWB_DrawTile8 (s16int x, s16int y, s16int tile)
-{
-	if (VW_MarkUpdateBlock (x,y,x+7,y+7))
-		LatchDrawChar(x,y,tile);
-}
-
-void VWB_Plot (s16int x, s16int y, s16int color)
-{
-	if (VW_MarkUpdateBlock (x,y,x,y))
-		VW_Plot(x,y,color);
-}
-
-void VWB_Vlin (s16int y1, s16int y2, s16int x, s16int color)
-{
-	if (VW_MarkUpdateBlock (x,y1,x,y2))
-		VW_Vlin(y1,y2,x,color);
-}
-
-void VW_UpdateScreen (void)
-{
-	VH_UpdateScreen ();
-}
-
-
-/*
-=============================================================================
-
-						WOLFENSTEIN STUFF
-
-=============================================================================
-*/
-
-/*
-=====================
-=
-= LatchDrawPic
-=
-=====================
-*/
-
-void LatchDrawPic (u16int x, u16int y, u16int picnum)
-{
-	u16int wide, height, source;
-
-	wide = pictable[picnum-STARTPICS].width;
-	height = pictable[picnum-STARTPICS].height;
-	source = latchpics[2+picnum-LATCHPICS_LUMP_START];
-
-	VL_LatchToScreen (source,wide/4,height,x*8,y);
-}
-
-
-//==========================================================================
-
-/*
-===================
-=
-= LoadLatchMem
-=
-===================
-*/
-
-void LoadLatchMem (void)
-{
-	s16int	i,j,p,m,width,height,start,end;
-	u8int	far *src;
-	u16int	destoff;
-
-//
-// tile 8s
-//
-	→ fuck this
-	latchpics[0] = freelatch;
-	src = (u8int _seg *)grsegs[STARTTILE8];
-	destoff = freelatch;
-
-	for (i=0;i<NUMTILE8;i++)
-	{
-		VL_MemToLatch (src,8,8,destoff);
-		src += 64;
-		destoff +=16;
-	}
-
-//
-// pics
-//
-	start = LATCHPICS_LUMP_START;
-	end = LATCHPICS_LUMP_END;
-
-	for (i=start;i<=end;i++)
-	{
-		latchpics[2+i-start] = destoff;
-		width = pictable[i-STARTPICS].width;
-		height = pictable[i-STARTPICS].height;
-		VL_MemToLatch (grsegs[i],width,height,destoff);
-		destoff += width/4 *height;
-	}
-
-	EGAMAPMASK(15);
-}
--- a/vl.asm
+++ /dev/null
@@ -1,124 +1,0 @@
-SCREENSEG	=	0a000h
-
-	DATASEG
-
-	EXTRN	TimeCount:WORD ; incremented every 70th of a second
-	EXTRN	linewidth:WORD
-
-starttime	dw	?
-
-	CODESEG
-
-;==============
-;
-; VL_SetCRTC
-;
-;==============
-
-PROC	VL_SetCRTC  crtc:WORD
-PUBLIC	VL_SetCRTC
-
-;
-; wait for a display signal to make sure the raster isn't in the middle
-; of a sync
-;
-	cli
-
-	mov	dx,STATUS_REGISTER_1
-
-@@waitdisplay:
-	in	al,dx
-	test	al,1	;1 = display is disabled (HBL / VBL)
-	jnz	@@waitdisplay
-
-
-;
-; set CRTC start
-;
-; for some reason, my XT's EGA card doesn't like word outs to the CRTC
-; index...
-;
-	mov	cx,[crtc]
-	mov	dx,CRTC_INDEX
-	mov	al,0ch		;start address high register
-	out	dx,al
-	inc	dx
-	mov	al,ch
-	out	dx,al
-	dec	dx
-	mov	al,0dh		;start address low register
-	out	dx,al
-	mov	al,cl
-	inc	dx
-	out	dx,al
-
-
-	sti
-
-	ret
-
-ENDP
-
-;============================================================================
-;
-; VL_ScreenToScreen
-;
-; Basic block copy routine.  Copies one block of screen memory to another,
-; using write mode 1 (sets it and returns with write mode 0).  bufferofs is
-; NOT accounted for.
-;
-;============================================================================
-
-PROC	VL_ScreenToScreen	source:WORD, dest:WORD, wide:WORD, height:WORD
-PUBLIC	VL_ScreenToScreen
-USES	SI,DI
-
-	pushf
-	cli
-
-	mov	dx,SC_INDEX
-	mov	ax,SC_MAPMASK+15*256
-	out	dx,ax
-	mov	dx,GC_INDEX
-	mov	al,GC_MODE
-	out	dx,al
-	inc	dx
-	in	al,dx
-	and	al,NOT 3
-	or	al,1
-	out	dx,al
-
-	popf
-
-	mov	bx,[linewidth]
-	sub	bx,[wide]
-
-	mov	ax,SCREENSEG
-	mov	es,ax
-	mov	ds,ax
-
-	mov	si,[source]
-	mov	di,[dest]				;start at same place in all planes
-	mov	dx,[height]				;scan lines to draw
-	mov	ax,[wide]
-
-@@lineloop:
-	mov	cx,ax
-	rep	movsb
-	add	si,bx
-	add	di,bx
-
-	dec	dx
-	jnz	@@lineloop
-
-	mov	dx,GC_INDEX+1
-	in	al,dx
-	and	al,NOT 3
-	out	dx,al
-
-	mov	ax,ss
-	mov	ds,ax					;restore turbo's data segment
-
-	ret
-
-ENDP
--- a/vl.c
+++ /dev/null
@@ -1,243 +1,0 @@
-u16int	bufferofs;
-u16int	displayofs,pelpan;
-
-#define SC_INDEX			0x3C4
-#define SC_RESET			0
-#define SC_CLOCK			1
-#define SC_MAPMASK			2
-#define SC_CHARMAP			3
-#define SC_MEMMODE			4
-
-#define CRTC_INDEX			0x3D4
-#define CRTC_H_TOTAL		0
-#define CRTC_H_DISPEND		1
-#define CRTC_H_BLANK		2
-#define CRTC_H_ENDBLANK		3
-#define CRTC_H_RETRACE		4
-#define CRTC_H_ENDRETRACE 	5
-#define CRTC_V_TOTAL		6
-#define CRTC_OVERFLOW		7
-#define CRTC_ROWSCAN		8
-#define CRTC_MAXSCANLINE 	9
-#define CRTC_CURSORSTART 	10
-#define CRTC_CURSOREND		11
-#define CRTC_STARTHIGH		12
-#define CRTC_STARTLOW		13
-#define CRTC_CURSORHIGH		14
-#define CRTC_CURSORLOW		15
-#define CRTC_V_RETRACE		16
-#define CRTC_V_ENDRETRACE 	17
-#define CRTC_V_DISPEND		18
-#define CRTC_OFFSET			19
-#define CRTC_UNDERLINE		20
-#define CRTC_V_BLANK		21
-#define CRTC_V_ENDBLANK		22
-#define CRTC_MODE			23
-#define CRTC_LINECOMPARE 	24
-
-
-#define GC_INDEX			0x3CE
-#define GC_SETRESET			0
-#define GC_ENABLESETRESET 	1
-#define GC_COLORCOMPARE		2
-#define GC_DATAROTATE		3
-#define GC_READMAP			4
-#define GC_MODE				5
-#define GC_MISCELLANEOUS 	6
-#define GC_COLORDONTCARE 	7
-#define GC_BITMASK			8
-
-#define ATR_INDEX			0x3c0
-#define ATR_MODE			16
-#define ATR_OVERSCAN		17
-#define ATR_COLORPLANEENABLE 18
-#define ATR_PELPAN			19
-#define ATR_COLORSELECT		20
-
-#define	STATUS_REGISTER_1    0x3da
-
-#define PEL_WRITE_ADR		0x3c8
-#define PEL_READ_ADR		0x3c7
-#define PEL_DATA			0x3c9
-
-#define SCREENSEG		0xa000
-
-#define SCREENWIDTH		80			// default screen width in bytes
-#define MAXSCANLINES	200			// size of ylookup table
-
-#define TILEWIDTH		4
-
-extern	u16int	bufferofs;			// all drawing is reletive to this
-extern	u16int	displayofs,pelpan;	// last setscreen coordinates
-
-extern	u16int	screenseg;			// set to 0xa000 for asm convenience
-
-extern	u16int	linewidth;
-extern	u16int	ylookup[MAXSCANLINES];
-
-extern	int		screenfaded;
-extern	u16int	bordercolor;
-
-#define VGAWRITEMODE(x) asm{\
-cli;\
-mov dx,GC_INDEX;\
-mov al,GC_MODE;\
-out dx,al;\
-inc dx;\
-in al,dx;\
-and al,252;\
-or al,x;\
-out dx,al;\
-sti;}
-
-#define VGAMAPMASK(x) asm{cli;mov dx,SC_INDEX;mov al,SC_MAPMASK;mov ah,x;out dx,ax;sti;}
-#define VGAREADMAP(x) asm{cli;mov dx,GC_INDEX;mov al,GC_READMAP;mov ah,x;out dx,ax;sti;}
-
-u16int	screenseg=SCREENSEG;		// set to 0xa000 for asm convenience
-
-u16int	linewidth;
-u16int	ylookup[MAXSCANLINES];
-
-int		screenfaded;
-u16int	bordercolor;
-
-int		fastpalette;				// if true, use outsb to set
-
-u8int	pixmasks[4] = {1,2,4,8};
-u8int	leftmasks[4] = {15,14,12,8};
-u8int	rightmasks[4] = {1,3,7,15};
-
-void VL_Plot (s16int x, s16int y, s16int color)
-{
-	u8int mask;
-
-	mask = pixmasks[x&3];
-	VGAMAPMASK(mask);
-	*(u8int far *)MK_FP(SCREENSEG,bufferofs+(ylookup[y]+(x>>2))) = color;
-	VGAMAPMASK(15);
-}
-
-void VL_Vlin (s16int x, s16int y, s16int height, s16int color)
-{
-	u8int	far *dest,mask;
-
-	mask = pixmasks[x&3];
-	VGAMAPMASK(mask);
-
-	dest = MK_FP(SCREENSEG,bufferofs+ylookup[y]+(x>>2));
-
-	while (height--)
-	{
-		*dest = color;
-		dest += linewidth;
-	}
-
-	VGAMAPMASK(15);
-}
-
-void VL_MemToLatch (u8int far *source, s16int width, s16int height, u16int dest)
-{
-	u16int	count;
-	u8int	plane,mask;
-
-	count = ((width+3)/4)*height;
-	mask = 1;
-	for (plane = 0; plane<4 ; plane++)
-	{
-		VGAMAPMASK(mask);
-		mask <<= 1;
-
-asm	mov	cx,count
-asm mov ax,SCREENSEG
-asm mov es,ax
-asm	mov	di,[dest]
-asm	lds	si,[source]
-asm	rep movsb
-asm mov	ax,ss
-asm	mov	ds,ax
-
-		source+= count;
-	}
-}
-
-void VL_MemToScreen (u8int far *source, s16int width, s16int height, s16int x, s16int y)
-{
-	u8int    far *screen,far *dest,mask;
-	s16int		plane;
-
-	width>>=2;
-	dest = MK_FP(SCREENSEG,bufferofs+ylookup[y]+(x>>2) );
-	mask = 1 << (x&3);
-
-	for (plane = 0; plane<4; plane++)
-	{
-		VGAMAPMASK(mask);
-		mask <<= 1;
-		if (mask == 16)
-			mask = 1;
-
-		screen = dest;
-		for (y=0;y<height;y++,screen+=linewidth,source+=width)
-			_fmemcpy (screen,source,width);
-	}
-}
-
-void VL_MaskedToScreen (u8int far *source, s16int width, s16int height, s16int x, s16int y)
-{
-	u8int    far *screen,far *dest,mask;
-	u8int	far *maskptr;
-	s16int		plane;
-
-	width>>=2;
-	dest = MK_FP(SCREENSEG,bufferofs+ylookup[y]+(x>>2) );
-//	mask = 1 << (x&3);
-
-//	maskptr = source;
-
-	for (plane = 0; plane<4; plane++)
-	{
-		VGAMAPMASK(mask);
-		mask <<= 1;
-		if (mask == 16)
-			mask = 1;
-
-		screen = dest;
-		for (y=0;y<height;y++,screen+=linewidth,source+=width)
-			_fmemcpy (screen,source,width);
-	}
-}
-
-void VL_LatchToScreen (u16int source, s16int width, s16int height, s16int x, s16int y)
-{
-	VGAWRITEMODE(1);
-	VGAMAPMASK(15);
-
-asm	mov	di,[y]				// dest = bufferofs+ylookup[y]+(x>>2)
-asm	shl	di,1
-asm	mov	di,[WORD PTR ylookup+di]
-asm	add	di,[bufferofs]
-asm	mov	ax,[x]
-asm	shr	ax,2
-asm	add	di,ax
-
-asm	mov	si,[source]
-asm	mov	ax,[width]
-asm	mov	bx,[linewidth]
-asm	sub	bx,ax
-asm	mov	dx,[height]
-asm	mov	cx,SCREENSEG
-asm	mov	ds,cx
-asm	mov	es,cx
-
-drawline:
-asm	mov	cx,ax
-asm	rep movsb
-asm	add	di,bx
-asm	dec	dx
-asm	jnz	drawline
-
-asm	mov	ax,ss
-asm	mov	ds,ax
-
-	VGAWRITEMODE(0);
-}
--- a/wl3d.c
+++ b/wl3d.c
@@ -11,12 +11,13 @@
 char *ext = "wl6";
 int ver;
 int grabon;
-int kbon, mson;
-int kb, mΔx, mΔy, mΔb;
+int kbon;
+int kb, mΔx, mΔy, mb;
 int demexit;
 void (*step)(void);
 int Δtc;
 int nosleep;
+int autorun;
 Channel *csc;
 QLock inlck;
 
@@ -29,12 +30,12 @@
 static Rectangle fbr, grabr;
 static Image *fb;
 static Channel *reszc;
-static int cson;
+static int cson, mson, fixms;
 
 static void
 mproc(void *)
 {
-	int n, fd, nerr;
+	int n, b, fd, nerr;
 	char buf[1+5*12], *px, *py, *pb;
 	Point o, p;
 
@@ -62,14 +63,20 @@
 		case 'm':
 			if(!mson)
 				break;
+			if(fixms){
+				fixms = 0;
+				goto res;
+			}
 			p.x = strtol(px, nil, 10);
 			p.y = strtol(py, nil, 10);
+			b = strtol(pb, nil, 10);
 			qlock(&inlck);
 			mΔx += p.x - o.x;
-			mΔy += o.y - p.y;
-			mΔb = *pb;
+			mΔy += p.y - o.y;
+			mb = b;
 			qunlock(&inlck);
 			if(!ptinrect(p, grabr)){
+		res:
 				fprint(fd, "m%d %d", p0.x, p0.y);
 				p = p0;
 			}
@@ -106,7 +113,7 @@
 			chartorune(&r, buf+1);
 			nbsend(csc, &r);
 		}
-		if(c != 'k' || c != 'K' || !kbon)
+		if(c != 'k' && c != 'K' || !kbon)
 			continue;
 		s = buf+1;
 		k = 0;
@@ -157,7 +164,7 @@
 croak(void *, char *s)
 {
 	if(strncmp(s, "sys:", 4) == 0)
-		mson = 0;
+		grab(0);
 	noted(NDFLT);
 }
 
@@ -164,7 +171,7 @@
 static void
 usage(void)
 {
-	fprint(2, "usage: %s [-23dopqs] [-f demo] [-m dir] [-w map] [-x difficulty]\n", argv0);
+	fprint(2, "usage: %s [-23dopqs] [-f demo] [-m dir] [-w map difc]\n", argv0);
 	threadexits("usage");
 }
 
@@ -176,18 +183,10 @@
 	p = mallocz(n, 1);
 	if(p == nil)
 		sysfatal("emalloc: %r");
+	setmalloctag(p, getcallerpc(&n));
 	return p;
 }
 
-void *
-erealloc(void *p, ulong n)
-{
-	p = realloc(p, n);
-	if(p == nil)
-		sysfatal("erealloc: %r");
-	return p;
-}
-
 void
 grab(int on)
 {
@@ -203,6 +202,7 @@
 			return;
 		}
 		write(fd, nocurs, sizeof nocurs);
+		fixms++;
 	}else if(fd >= 0){
 		close(fd);
 		fd = -1;
@@ -242,13 +242,14 @@
 void
 threadmain(int argc, char **argv)
 {
-	int tc;
+	int tc, m, d;
 	vlong t0, t, dt;
 	char *datdir, *df;
 
 	datdir = "/sys/games/lib/wl3d/";
 	df = nil;
-	step = mstep;
+	m = -1;
+	d = 0;
 	ARGBEGIN{
 	case '2': ext = "sd2"; ver = SOD; break;
 	case '3': ext = "sd3"; ver = SOD; break;
@@ -259,8 +260,7 @@
 	case 'p': nosleep++; break;
 	case 'q': demexit++; break;
 	case 's': ext = "sod"; ver = SOD; break;
-	case 'w': /* TODO: warp to ep, level */ break;
-	case 'x': /* TODO: set difficulty for warp */ break;
+	case 'w': m = strtoul(EARGF(usage()), nil, 0); d = strtoul(EARGF(usage()), nil, 0); break;
 	default:
 		usage();
 	}ARGEND;
@@ -270,7 +270,6 @@
 	pal = pals[C0];
 	resetfb();
 	dat(datdir);
-	initsnd();
 	csc = chancreate(sizeof(Rune), 20);
 	reszc = chancreate(sizeof(int), 2);
 	if(csc == nil | reszc == nil)
@@ -277,11 +276,13 @@
 		sysfatal("chancreate: %r");
 	if(proccreate(kproc, nil, 8192) < 0 || proccreate(mproc, nil, 8192) < 0)
 		sysfatal("proccreate: %r");
-
-	init(df);
+	tab();
+	initsnd();
+	init(df, m, d);
 	cson++;
 	t0 = nsec();
 	Δtc = 1;
+	step = qstep;
 	for(;;){
 		if(nbrecv(reszc, nil) != 0){
 			if(getwindow(display, Refnone) < 0)
@@ -298,8 +299,6 @@
 			tc = 10;
 		Δtc = tc;
 		t0 += tc * Td;
-		if(onestep)
-			t0 += Td;
 		dt = (t0 - t) / Te6;
 		if(dt > 0 && !nosleep)
 			sleep(dt);