shithub: wl3d

Download patch

ref: b109f9a857d3a69745e707913e5b321c0740bdb3
parent: 3537473ee848597fc6fe8ecd1ef8bcde939ae187
author: Konstantinn Bonnet <qu7uux@gmail.com>
date: Wed Apr 13 00:05:37 EDT 2016

implement most of main menu proper

- arg: remove -i: obsoleted by a single keystroke skipping to menu rather than 3
- implement fade into and out of color
- kproc: update for recent kbdfs(8) changes
- release mouse if sys: note is received

--- a/dat.h
+++ b/dat.h
@@ -24,7 +24,7 @@
 };
 extern int cson, kbon, mson;
 extern Rune keys[];
-extern int (*step)(void);
+extern void (*step)(void);
 
 enum{
 	Vw = 320,
@@ -37,6 +37,7 @@
 extern uchar *px;
 extern int npx, scale;
 
+typedef struct Col Col;
 enum{
 	C0,
 	Cred,
@@ -46,6 +47,11 @@
 	Cend
 };
 extern u32int *pal, pals[][256];
+struct Col{
+	int r;
+	int g;
+	int b;
+};
 
 typedef struct Dat Dat;
 typedef struct Pic Pic;
@@ -185,12 +191,13 @@
 	Mintro = 7,
 	Mmenu = 14,
 	Mnazjazz = 18,
+	Maward = 20,
+	Mroster = 23,
 	Mtower = 23,
 
 	Pbackdrop = 0,
 	Pmouselback,
 	Pcur1,
-	Pcur2,
 	Punsel,
 	Psel,
 	Pcustom,
--- a/fns.h
+++ b/fns.h
@@ -5,21 +5,23 @@
 void	flush(void);
 void	dat(char *);
 void	out(void);
-void	fade(void);
-void	fadeop(int, u32int, int);
+void	fadeout(void);
+void	fadein(void);
+void	fadeop(Col *, int);
 void	palpic(uchar *);
 void	put(int, int, int, int, uchar *, int);
 int	txt(int, int, char *, int);
 int	txtnl(int, int, char *, int);
-int	txtw(char *s);
+int	txth(char *);
+int	txtw(char *);
 void	fill(int);
 void	pic(int, int, int);
-int	estep(void);
-int	mstep(void);
-void	init(int);
+void	mstep(void);
+void	init(void);
 int	rnd(void);
-int	gstep(void);
-int	dstep(void);
+void	gstep(void);
+void	dstep(void);
 void	initg(int);
+void	sfx(int);
 void	stopmus(void);
-void	playmus(int);
+void	mus(int);
--- a/fs.c
+++ b/fs.c
@@ -390,20 +390,20 @@
 /* what bullshit. */
 uchar *pict;
 static uchar picts[4][Pend]={ {
-	3, 15, 8, 9, 10, 11, 24, 27, 34, 35, 23, 7, 12, 13, 14, 36, 16, 21, 25,
+	3, 15, 8, 10, 11, 24, 27, 34, 35, 23, 7, 12, 13, 14, 36, 16, 21, 25,
 	26, 87, 37, 129, 40, 41, 42, 52, 53,79, 80, 81, 82, 84, 0, 0, 83, 85, 86,
 	0, 0, 88, 92, 93, 94, 95, 96, 106, 127, 128, 0, 0, 0, 130, 131
 	},{
-	14, 27, 20, 21, 22, 23, 36, 39, 46, 47, 35, 19, 24, 25, 26, 48, 28, 33,
+	14, 27, 20, 22, 23, 36, 39, 46, 47, 35, 19, 24, 25, 26, 48, 28, 33,
 	37, 38, 99, 49, 141, 52, 53, 54, 64, 65, 91, 92, 93, 94, 96, 0, 0, 95,
 	97, 98, 0, 0, 100, 104, 105, 106, 107, 108, 118, 139, 140, 0, 0, 0, 142,
 	143
 	},{
-	0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 22, 24, 25,
+	0, 1, 2, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 22, 24, 25,
 	26, 27, 0, 28, 29, 30, 40, 41, 67, 68, 69, 70, 71, 72, 0, 73, 74, 75, 0,
 	0, 76, 80, 81, 82, 83, 84, 94, 115, 116, 117, 120, 122, 123, 124
 	},{
- 	0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 22, 24, 25,
+ 	0, 1, 2, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 22, 24, 25,
 	26, 27, 28, 33, 34, 35, 45, 46, 72, 73, 74, 75, 76, 77, 78, 87, 88, 89,
 	90, 91, 98, 102, 103, 104, 105, 106, 116, 137, 138, 139, 142, 144, 145,
 	146
@@ -515,12 +515,14 @@
 static int
 unrlew(u16int *d, u16int *s)
 {
-	u16int n, v, *e;
+	u16int n, t, v, *e;
+
 	s++;
 	e = d + Planesz/Nplane;
+	t = rlewtag;
 	while(d < e){
 		v = *s++;
-		if(v == rlewtag){
+		if(v == t){
 			n = *s++;
 			v = *s++;
 			while(n-- > 0)
--- a/gm.c
+++ b/gm.c
@@ -47,17 +47,15 @@
 	return rndt[rndi];
 }
 
-int
+void
 gstep(void)
 {
-	return 0;
 }
 
-int
+void
 dstep(void)
 {
 	step = mstep;
-	return 0;
 }
 
 void
--- a/menu.c
+++ b/menu.c
@@ -2615,12 +2615,6 @@
 	}
 }
 
-
-////////////////////////////////////////////////////////////////////
-//
-// SET TEXT COLOR (HIGHLIGHT OR NO)
-//
-////////////////////////////////////////////////////////////////////
 void SetTextColor(CP_itemtype far *items,s16int hlight)
 {
 	if (hlight)
@@ -2781,10 +2775,7 @@
 	s16int h=0,w=0,mw=0,i,x,y,time;
 	fontstruct _seg *font;
 
-
-	CA_CacheGrChunk (STARTFONT+1);
 	fontnumber=1;
-	font=grsegs[STARTFONT+fontnumber];
 	h=font->height;
 	for (i=0;i<_fstrlen(string);i++)
 		if (string[i]=='\n')
--- a/menu.h
+++ b/menu.h
@@ -29,8 +29,6 @@
 #define MenuFadeIn()	VL_FadeIn(0,255,&gamepal,10)
 
 
-#define MENUSONG	14
-
 #ifndef SPEAR
 #define INTROSONG	7
 #else
--- a/mn.c
+++ b/mn.c
@@ -5,11 +5,14 @@
 #include "dat.h"
 #include "fns.h"
 
+/* FIXME: non-trivial */
+
 extern Channel *csc;
 
 typedef struct Score Score;
-typedef struct Menu Menu;
 typedef struct Seq Seq;
+typedef struct Item Item;
+typedef struct Menu Menu;
 
 struct Score{
 	char name[58];
@@ -17,7 +20,7 @@
 	int lvl;
 	int ep;
 };
-static Score score[] = {
+static Score sc[] = {
 	{"id software-'92", 10000, 1},
 	{"Adrian Carmack", 10000, 1},
 	{"John Carmack", 10000, 1},
@@ -27,61 +30,87 @@
 	{"Jay Wilbur", 10000, 1},
 };
 
-static void (*clear)(void);
-static void (*stripe)(int);
-
+enum{
+	Lintro,
+	Ltitle,
+	Lcreds,
+	Lscore,
+	Ldemo,
+	Ldecay,
+	Linctl,
+	Lctl,
+	Lcur,
+	Lesc,
+	Lback,
+	Lwait,
+	Lack,
+	Lmscore,
+	Lpants,
+	Lquit,
+	Ldie
+};
 struct Seq{
 	int dt;
 	void (*f)(void);
 };
+struct Item{
+	char *s;
+	int c;
+	Menu *m;
+};
 struct Menu{
-	void (*draw)(void);
-	int nq;
-	Seq *sq;
+	void (*init)(void);
+	Seq *qs;
+	Seq *qe;
 	Menu *m;
+	Col *c;
+	Item *is;
+	Item *ie;
+	Item *ip;
+	int cx;
+	int cy;
+	int cur;
 };
-static Menu *mp;
-static Seq *msq;
-static int tc;
+static Menu *mp, ml[];
+static Seq *mqp;
 
-static void waitkb(void);
-static void menuk(void);
-static void menu(void);
-static void pants(void);
-static void demo(void);
-static void scores(void);
-static void creds(void);
-static void title(void);
-static void intro(void);
+static char *ends[] = {
+	"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.",
 
-static Seq menuq[] = {{1, menuk}};
-static Seq mfadeq[] = {{30, fade}};
-static Seq pantsq[] = {{30, fade}, {600*Tb, waitkb}, {30, fade}};
-static Seq demoq[] = {{1, demo}};
-static Seq loopq[] = {{30, fade}, {10*Tb, waitkb}, {30, fade}};
-static Seq titleq[] = {{30, fade}, {15*Tb, waitkb}, {30, fade}};
-static Seq introq[] = {{30, fade}, {7*Tb, waitkb}, {30, fade}};
+	"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."
+};
+static char **quits;
+
 enum{
-	Lmenu,
-	Lmfade,
-	Lpants,
-	Ldemo,
-	Lscores,
-	Lcreds,
-	Ltitle,
-	Lintro
+	Dbg,
+	Doff,
+	Dbrd,
+	Dbrd2,
+	Dend
 };
-static Menu ml[] = {
-	[Lmenu] {menu, nelem(menuq), menuq, ml+Lmenu},
-	[Lmfade] {nil, nelem(mfadeq), mfadeq, ml+Lmenu},
-	[Lpants] {pants, nelem(pantsq), pantsq, ml+Lmenu},
-	[Ldemo] {demo, nelem(demoq), demoq, ml+Ltitle},
-	[Lscores] {scores, nelem(loopq), loopq, ml+Ldemo},
-	[Lcreds] {creds, nelem(loopq), loopq, ml+Lscores},
-	[Ltitle] {title, nelem(titleq), titleq, ml+Lcreds},
-	[Lintro] {intro, nelem(introq), introq, ml+Ltitle},
-};
+static int mcol[Dend] = {[Dbg] 0x2d, 0};
 
+static int tc;
+
+static void (*clear)(void);
+static void (*stripe)(int);
+
 static void
 wlclear(void)
 {
@@ -107,6 +136,22 @@
 }
 
 static void
+outbox(int x, int y, int dx, int dy, int c1, int c2)
+{
+	put(x, y, dx, 1, nil, c2);
+	put(x, y, 1, dy, nil, c2);
+	put(x, y+dy, dx+1, 1, nil, c1);
+	put(x+dx, y, 1, dy, nil, c1);
+}
+
+static 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, nil, col);
+	outbox(x, y, dx, dy, out, out2);
+}
+
+static void
 fixedw(char *s)
 {
 	char c;
@@ -116,53 +161,219 @@
 }
 
 static void
-reset(Menu *m, int pal0)
+reset(Menu *m)
 {
-	mp = m;
+	Seq *q;
+
+	q = m->qs;
+	mqp = q;
 	tc = 0;
-	if(pal0)
-		pal = pals[C0];
-	msq = m->sq;
-	if(mp->draw != nil){
-		mp->draw();
-		fadeop(msq->dt, 0, 0);
-	}else
-		fadeop(msq->dt, 0, 1);
+	if(m != mp){
+		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
-waitkb(void)
+blink(void)
 {
-	if(nbrecv(csc, nil) > 0)
-		reset(ml+Lmfade, 0);
+	Menu *m;
+
+	m = mp;
+	if(m == ml+Lctl){
+		put(m->cx, m->cy, 24, 16, nil, 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, nil, 0x17);
+	}
+	out();
+	m->cur ^= 1;
 }
 
 static void
-menuk(void)
+ask(void)
 {
 	Rune r;
 
-	while(nbrecv(csc, &r) != 0);
+	if(nbrecv(csc, &r) <= 0)
+		return;
+	if(r == 'y'){
+		sfx(Sshoot);
+		reset(ml+Ldie);
+	}
+	else if(r == 'n' || r == Kesc){
+		sfx(Sesc);
+		reset(ml+Lctl);
+	}
 }
 
 static void
-pants(void)
+quit(void)
 {
-	pic(0, 0, pict[Pid1]);
-	pic(0, 80, pict[Pid2]);
-	palpic(exts[Eid].p);
-	playmus(Mnazjazz);
+	int x, y, w, h, curw;
+	char *s, *nl;
+	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 = 160 - w/2;
+	m = mp;
+
+	box(x-5, y-5, w+10, h+10, 0x17, 0, 0x13);
+	txtnl(x, y, s, 0);
+	m->cx = x+curw;
+	m->cy = y + h - fnt->h;
 }
 
 static void
-menu(void)
+ctl(void)
 {
-	playmus(Mmenu);
+	Menu *m;
+	Item *i, *s, *e;
+
+	clear();
+	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]);
+
+	fnt = fnts+1;
+	m = ml+Lctl;
+	s = i = m->is;
+	e = m->ie;
+	do
+		txt(100, 55+13*(i-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);
+}
+
+static void
+cursfx(void)
+{
+	sfx(Sdrawgun2);
+}
+
+static void
+movcur(Menu *m, Item *p, int dir)
+{
+	Item *i;
+
+	p->c = 0x17;
+	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){
+		cursfx();
+		reset(ml+Lctl);
+		ctl();
+		return;
+	}
+	put(m->cx, m->cy, 24, 16, nil, mcol[Dbg]);
+	m->cy += dir * 6;
+	blink();
+	sfx(Sdrawgun1);
+	reset(ml+Lcur);
+}
+
+static void
+cwalk(void)
+{
+	Rune r;
+	Menu *m;
+	Item *i;
+	static int p;
+
+	if(nbrecv(csc, &r) <= 0)
+		return;
+	m = mp;
+	i = m->ip;
+	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 '\n':
+		m = i->m;
+		if(m == nil)
+			break;
+		sfx(Sshoot);
+		reset(m);
+	}
+	if(r != 'i')
+		p = 0;
+}
+
+static void
+inctl(void)
+{
+	Item *i;
+	Menu *m;
+
+	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);
-	step = estep;
+	ctl();
 }
 
 static void
+skip(void)
+{
+	if(nbrecv(csc, nil) > 0)
+		reset(ml+Ldecay);
+}
+
+static void
+ack(void)
+{
+	if(nbrecv(csc, nil) > 0)
+		reset(ml+Lback);
+}
+
+static void
+pants(void)
+{
+	pic(0, 0, pict[Pid1]);
+	pic(0, 80, pict[Pid2]);
+	palpic(exts[Eid].p);
+	fadeop(mp->c, mp->qs->dt);
+	mus(Mnazjazz);
+}
+
+static void
 demo(void)
 {
 	step = dstep;
@@ -169,7 +380,7 @@
 }
 
 static void
-scores(void)
+score(void)
 {
 	int x, y;
 	char a[16], b[16];
@@ -183,7 +394,7 @@
 	pic(224, 68, pict[Phigh]);
 
 	fnt = fnts;
-	for(s=score, y=76; s<score+nelem(score); s++, y+=16){
+	for(s=sc, y=76; s<sc+nelem(sc); s++, y+=16){
 		txt(32, y, s->name, 0xf);
 
 		sprint(a, "%d", s->lvl);
@@ -199,9 +410,11 @@
 		fixedw(a);
 		txt(264 - txtw(a), y, a, 0xf);
 	}
+	if(mp == ml+Lmscore)
+		mus(Mroster);
 }
 static void
-sdscores(void)
+sdscore(void)
 {
 	int y;
 	char a[16];
@@ -211,7 +424,7 @@
 	pic(0, 0, pict[Pscores]);
 
 	fnt = fnts+1;
-	for(s=score, y=76; s<score+nelem(score); s++, y+=16){
+	for(s=sc, y=76; s<sc+nelem(sc); s++, y+=16){
 		txt(16, y, s->name, 0x13);
 
 		if(s->lvl == 21)
@@ -224,6 +437,8 @@
 		sprint(a, "%d", s->n);
 		txt(292 - txtw(a), y, a, 0xf);
 	}
+	if(mp == ml+Lmscore)
+		mus(Maward);
 }
 
 static void
@@ -236,7 +451,7 @@
 title(void)
 {
 	pic(0, 0, pict[Ptitle1]);
-	playmus(Mintro);
+	mus(Mintro);
 }
 static void
 sdtitle(void)
@@ -244,7 +459,7 @@
 	pic(0, 0, pict[Ptitle1]);
 	pic(0, 80, pict[Ptitle2]);
 	palpic(exts[Etitpal].p);
-	playmus(Mtower);
+	mus(Mtower);
 }
 
 static void
@@ -252,47 +467,107 @@
 {
 	fill(0x82);
 	pic(216, 110, pict[Ppg13]);
-	playmus(Mintro);
 }
+
 static void
-sdintro(void)
+die(void)
 {
-	fill(0x82);
-	pic(216, 110, pict[Ppg13]);
-	playmus(Mtower);
+	threadexitsall(nil);
 }
 
-int
-estep(void)
-{
-	return -1;
-}
+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}
+};
 
-int
+static Col fblk, fmenu = { 0xae, 0, 0 };
+static Seq *mqp,
+	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}},
+	demoq[] = {{1, nil}},
+	decq[] = {{30, fadeout}},	/* uses previous fadeop */
+	inctlq[] = {{10, fadein}},
+	ctlq[] = {{0, blink}, {70, cwalk}, {0, blink}, {8, cwalk}},
+	curq[] = {{8, nil}, {0, cursfx}},
+	escq[] = {{10, fadeout}},
+	backq[] = {{10, fadeout}, {0, ctl}, {10, fadein}},
+	waitq[] = {{1, skip}},
+	ackq[] = {{1, ack}},
+	mscoreq[] = {{10, fadeout}, {0, score}, {10, fadein}},
+	pantsq[] = {{30, fadeout}, {0, pants}, {30, fadein}},
+	quitq[] = {{0, blink}, {10, ask}},
+	dieq[] = {{10, fadeout}, {1, die}};
+
+static Menu *mp, ml[] = {
+	[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] {demo, demoq, demoq+nelem(demoq), ml+Ltitle, &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},
+	[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},
+	[Ldie] {nil, dieq, dieq+nelem(dieq), nil, &fmenu}
+};
+
+void
 mstep(void)
 {
+	Menu *m;
+	Seq *q;
+
+rep:
+	m = mp;
+	q = mqp;
 	tc++;
-	msq->f();
-	if(tc == msq->dt){
-		if(++msq == mp->sq + mp->nq)
-			reset(mp->m, 1);
+	if(q->f != nil)
+		q->f();
+	if(tc >= q->dt){
+		if(++mqp == m->qe)
+			reset(m->m);
 		tc = 0;
 	}
-	return 0;
+	if(q->dt == 0)
+		goto rep;
 }
 
 void
-init(int nointro)
+init(void)
 {
 	clear = wlclear;
 	stripe = wlstripe;
+	quits = ends;
 	if(ver >= SDM){
 		clear = sdclear;
 		stripe = sdstripe;
-		ml[Lintro].draw = sdintro;
-		ml[Ltitle].draw = sdtitle;
-		ml[Lscores].draw = sdscores;
+		ml[Ltitle].init = sdtitle;
+		ml[Lscore].init = sdscore;
+		mscoreq[1].f = sdscore;
+		fmenu = (Col){0, 0, 0xce};
+		mcol[Dbg] = 0x9d;
+		quits += nelem(ends)/2;
 	}
-	reset(nointro ? ml+Lmenu : ml+Lintro, 0);
+	mcol[Doff] = mcol[Dbg] ^ 6;
+	mcol[Dbrd] = mcol[Dbg] ^ 4;
+	mcol[Dbrd2] = mcol[Dbg] ^ 14;
+	reset(ml+Lintro);
 	cson++;
+	mus(ver<SDM ? Mintro : Mtower);
 }
--- a/rend.c
+++ b/rend.c
@@ -12,16 +12,20 @@
 int scale, npx;
 uchar *px;
 static uchar pxb[Va];
-static void (*ffp)(void);
-static int fi, fdt, fr, fg, fb;
+static Col *fcol;
 static u32int *fref;
+static int fi, fo, fdt;
 
-static void
+void
 fadeout(void)
 {
-	int u, v, w;
+	int i, t, u, v, w;
 	u32int p, *s, *d, *e;
+	Col *c;
 
+	i = fo++;
+	t = fdt;
+	c = fcol;
 	s = fref;
 	d = pal;
 	e = d+nelem(pals[0]);
@@ -30,54 +34,49 @@
 		u = p & 0xff;
 		v = p>>8 & 0xff;
 		w = p>>16 & 0xff;
-		u = u + (fb-u) * fi/fdt;
-		v = v + (fg-v) * fi/fdt;
-		w = w + (fr-w) * fi/fdt;
+		u = u + (c->b-u) * i/t;
+		v = v + (c->g-v) * i/t;
+		w = w + (c->r-w) * i/t;
 		*d++ = w<<16 | v<<8 | u;
 	}
+	out();
 }
 
-static void
+void
 fadein(void)
 {
-	int u, v, w;
+	int i, t, u, v, w;
 	u32int p, *s, *d, *e;
+	Col *c;
 
+	i = fi++;
+	t = fdt;
+	c = fcol;
 	s = fref;
 	d = pal;
 	e = d+nelem(pals[0]);
 	while(d < e){
 		p = *s++;
-		u = (p & 0xff) * fi/fdt;
-		v = (p>>8 & 0xff) * fi/fdt;
-		w = (p>>16 & 0xff) * fi/fdt;
+		u = p & 0xff;
+		v = p>>8 & 0xff;
+		w = p>>16 & 0xff;
+		u = c->b + (u-c->b) * i/t;
+		v = c->g + (v-c->g) * i/t;
+		w = c->r + (w-c->r) * i/t;
 		*d++ = w<<16 | v<<8 | u;
 	}
-}
-
-void
-fade(void)
-{
-	ffp();
 	out();
-	if(fi == fdt && ffp == fadein){
-		ffp = fadeout;
-		fi = 0;
-	}
-	fi++;
 }
 
 void
-fadeop(int dt, u32int c, int noin)
+fadeop(Col *c, int dt)
 {
+	fi = 0;
+	fo = 1;
 	fdt = dt;
-	fb = (c & 0xff) * 255 / 63;
-	fg = (c>>8 & 0xff) * 255 / 63;
-	fr = (c>>16 & 0xff) * 255 / 63;
+	fcol = c;
 	fref = pal;
 	pal = pals[Cfad];
-	fi = 1;
-	ffp = noin ? fadeout : fadein;
 }
 
 void
@@ -171,27 +170,51 @@
 txtnl(int x, int y, char *t, int col)
 {
 	int n;
-	char *s;
+	char *s, *m;
 
 	n = 0;
-	s = strtok(t, "\n");
+	m = strdup(t);
+	if(m == nil)
+		sysfatal("txtnl: %r");
+	s = strtok(m, "\n");
 	while(s != nil){
 		n += txt(x, y, s, col);
 		s = strtok(nil, "\n");
 		y += fnt->h;
 	}
+	free(m);
 	return n;
 }
 
 int
-txtw(char *t)
+txth(char *t)
 {
-	int n;
+	int h, n;
 
-	n = 0;
+	h = fnt->h;
+	n = h;
 	while(*t != 0)
-		n += fnt->w[(uchar)*t++];
+		if(*t++ == '\n')
+			n += h;
 	return n;
+}
+
+int
+txtw(char *t)
+{
+	int n, m;
+
+	n = m = 0;
+	while(*t != 0){
+		if(*t == '\n'){
+			if(n > m)
+				m = n;
+			n = 0;
+		}else
+			n += fnt->w[(uchar)*t];
+		t++;
+	}
+	return n > m ? n : m;
 }
 
 void
--- a/snd.c
+++ b/snd.c
@@ -7,14 +7,26 @@
 Dat *imfs, *imfe;
 Sfx *sfxs, *sfxe;
 
+static int mi;
+
 void
+sfx(int n)
+{
+	Sfx *s;
+
+	s = sfxs+n;
+	USED(s);
+}
+
+void
 stopmus(void)
 {
 }
 
 void
-playmus(int n)
+mus(int n)
 {
+	if(n == mi)
+		return;
 	stopmus();
-	USED(n);
 }
--- a/vl.c
+++ b/vl.c
@@ -11,8 +11,6 @@
 
 int		fastpalette;				// if true, use outsb to set
 
-u8int		far	palette1[256][3],far palette2[256][3];
-
 //===========================================================================
 
 // asm
@@ -28,28 +26,6 @@
 
 =============================================================================
 */
-
-
-/*
-=================
-=
-= VL_FillPalette
-=
-=================
-*/
-
-void VL_FillPalette (s16int red, s16int green, s16int blue)
-{
-	s16int	i;
-
-	outportb (PEL_WRITE_ADR,0);
-	for (i=0;i<256;i++)
-	{
-		outportb (PEL_DATA,red);
-		outportb (PEL_DATA,green);
-		outportb (PEL_DATA,blue);
-	}
-}
 
 /*
 ==================
--- a/wl3d.c
+++ b/wl3d.c
@@ -12,7 +12,7 @@
 int ver;
 int grabon;
 int cson, kbon, mson;
-int (*step)(void);
+void (*step)(void);
 Channel *csc, *kbc, *msc;
 
 enum{
@@ -83,9 +83,17 @@
 		sysfatal("kproc: %r");
 	memset(buf, 0, sizeof buf);
 	for(;;){
-		n = read(fd, buf, sizeof(buf)-1);
-		if(n <= 0)
-			break;
+		if(buf[0] != 0){
+			n = strlen(buf)+1;
+			memmove(buf, buf+n, sizeof(buf)-n);
+		}
+		if(buf[0] == 0){
+			n = read(fd, buf, sizeof(buf)-1);
+			if(n <= 0)
+				break;
+			buf[n-1] = 0;
+			buf[n] = 0;
+		}
 		c = *buf;
 		if(c == 'c' && cson){
 			chartorune(&r, buf+1);
@@ -137,9 +145,17 @@
 }
 
 static void
+croak(void *, char *s)
+{
+	if(strncmp(s, "sys:", 4) == 0)
+		mson = 0;
+	noted(NDFLT);
+}
+
+static void
 usage(void)
 {
-	fprint(2, "usage: %s [-23dios] [-m dir] [-w map] [-x difficulty]\n", argv0);
+	fprint(2, "usage: %s [-23dos] [-m dir] [-w map] [-x difficulty]\n", argv0);
 	threadexits("usage");
 }
 
@@ -218,17 +234,14 @@
 void
 threadmain(int argc, char **argv)
 {
-	int n;
 	vlong t0, t, dt, Δ;
 	char *datdir = "/sys/games/lib/wl3d/";
 
-	n = 0;
 	step = mstep;
 	ARGBEGIN{
 	case '2': ext = "sd2"; ver = SOD; break;
 	case '3': ext = "sd3"; ver = SOD; break;
 	case 'd': ext = "wl1"; ver = WL1; break;
-	case 'i': n++; break;
 	case 'm': datdir = EARGF(usage()); break;
 	case 'o': ext = "sdm"; ver = SDM; break;
 	case 's': ext = "sod"; ver = SOD; break;
@@ -239,6 +252,7 @@
 	}ARGEND;
 	dat(datdir);
 
+	notify(croak);
 	if(initdraw(nil, nil, "wl3d") < 0)
 		sysfatal("initdraw: %r");
 	resetfb();
@@ -251,7 +265,7 @@
 	if(proccreate(kproc, nil, 8192) < 0 || proccreate(mproc, nil, 8192) < 0)
 		sysfatal("proccreate: %r");
 
-	init(n);
+	init();
 	t0 = Δ = 0;
 	for(;;){
 		if(nbrecv(reszc, nil) != 0){
@@ -259,8 +273,7 @@
 				sysfatal("resize failed: %r");
 			resetfb();
 		}
-		if(step() < 0)
-			break;
+		step();
 		t = nsec();
 		dt = 0;
 		if(t0 != 0){
@@ -274,5 +287,4 @@
 			Δ += (dt - Δ) / 100;
 		}
 	}
-	threadexitsall(nil);
 }