1286 lines · 23.5 KB · C
RawDownload
#include <u.h>#include <libc.h>#include <draw.h>#include <thread.h>#include <keyboard.h>#include <mouse.h> #include "map.h"#include "bt.h" #include "dat.h"#include "alg.h" static int gameover = 0;static Font *smallfont; enum{	CGREEN,	CYELLOW,	CORANGE,	CRED,	CGREY,	CMAGENTA,	CEND,}; typedef struct KeyMenu KeyMenu;struct KeyMenu{	/* label printed with the menu */	char *label;	/* gen is called until it returns Runemax. */	Rune (*gen)(int, char*, int);}; static int menu(Keyboardctl *kc, Mousectl *mc, KeyMenu *km);static int uienter(char *ask, char *buf, int len, Mousectl *mc, Keyboardctl *kc, Screen *scr); typedef struct Msg Msg;struct Msg{	char	msg[256];	ulong	color;	Msg		*next;}; typedef struct UI UI;struct UI{	/* current ui size */	int uisz; 	/* colors */	Image **cols; 	/* mouse, keyboard */	Mousectl *mc;	Keyboardctl *kc; 	/* log chan */	Channel *msgc; 	/* linked list of current msgs */	Msg *msg;	int nmsg; 	/* tileset */	Tileset *tiles; 	/* level view rectangle, in tiles */	Rectangle viewr;	/* ui rectangle, in pixels */	Rectangle uir;	Point camp; 	/* timer tick, see /^tickproc */	Channel *tickcmd;	Channel *tick; 	/* should auto-move? */	int autoidle;	/* show far away messages? */	int farmsg;} ui;  static voidtickproc(void *v){	USED(v); 	ulong time;	time = 0;	enum { ATIME, ATICK, AEND };	Alt a[AEND+1] = {		{ ui.tickcmd,	&time,	CHANRCV },		{ ui.tick,		&time,	CHANEND },		{ nil,			nil,	CHANEND },	};	for(;;){		switch(alt(a)){		case ATIME:			if(time == 0)				a[ATICK].op = CHANEND;			else				a[ATICK].op = CHANSND;			break;		case ATICK:			sleep(time);			break;		}	}} voiduiinit(char *name){	int tx, ty;	char *tileset, *tmp; 	tx = ty = 0; 	if(initdraw(nil, nil, name) < 0)		sysfatal("initdraw: %r"); 	if((smallfont = openfont(display, "/lib/font/bit/fixed/unicode.4x6.font")) == nil)		sysfatal("openfont: %r"); 	if((ui.mc = initmouse(nil, screen)) == nil)		sysfatal("initmouse: %r"); 	if((ui.kc = initkeyboard(nil)) == nil)		sysfatal("initkeyboard: %r"); 	if((ui.tickcmd = chancreate(sizeof(ulong), 0)) == nil)		sysfatal("chancreate: %r"); 	if((ui.tick = chancreate(sizeof(ulong), 0)) == nil)		sysfatal("chancreate: %r"); 	proccreate(tickproc, nil, 4096); 	if((ui.msgc = chancreate(sizeof(Msg*), 1000)) == nil)		sysfatal("chancreate: %r"); 	tileset = getenv("tileset");	if(tileset != nil){		if((tmp = getenv("tilesetx")) == nil || (tx = atoi(tmp)) == 0)			sysfatal("zero tilesetx");		free(tmp); 		if((tmp = getenv("tilesety")) == nil || (ty = atoi(tmp)) == 0)			sysfatal("zero tilesety");		free(tmp);	} else {		if(access("nethack.32x32", AREAD) == 0)			tileset = strdup("nethack.32x32");		else			tileset = strdup("/lib/hack9/nethack.32x32");		tx = 32;		ty = 32;	} 	if((ui.tiles = opentile(tileset, tx, ty)) == nil)		sysfatal("opentile %s @ %dx%d: %r", tileset, tx, ty);	free(tileset); 	if((ui.cols = mallocz(CEND * sizeof(Image*), 1)) == nil)		sysfatal("malloc cols: %r"); 	ui.cols[CGREEN] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DGreen);	ui.cols[CYELLOW] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DYellow);	ui.cols[CORANGE] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xFFA500FF);	ui.cols[CRED] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DRed);	ui.cols[CGREY] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xAAAAAAFF);	ui.cols[CMAGENTA] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMagenta); 	ui.uisz = 6;} static voidtmsg(void *v){	Msg *l;	l = v;	while(nbsendp(ui.msgc, l) == 0)		yield();}  static voiduimsg(uint color, char *fmt, va_list arg){	Msg *l; 	l = mallocz(sizeof(Msg), 1);	assert(l != nil);	l->color = color;	vsnprint(l->msg, sizeof(l->msg)-1, fmt, arg);	if(nbsendp(ui.msgc, l) == 0)		threadcreate(tmsg, l, 4096);} voidmsg(char *fmt, ...){	va_list arg;	va_start(arg, fmt);	uimsg(CGREY, fmt, arg);	va_end(arg);} voidgood(char *fmt, ...){	va_list arg;	va_start(arg, fmt);	uimsg(CGREEN, fmt, arg);	va_end(arg);} voidwarn(char *fmt, ...){	va_list arg;	va_start(arg, fmt);	uimsg(CYELLOW, fmt, arg);	va_end(arg);} voidbad(char *fmt, ...){	va_list arg;	va_start(arg, fmt);	uimsg(CRED, fmt, arg);	va_end(arg);} voiddbg(char *fmt, ...){	va_list arg;	char tfmt[256]; 	if(debug < 1)		return;	snprint(tfmt, sizeof(tfmt), "T:%ld %s", turn, fmt);	va_start(arg, fmt);	uimsg(CMAGENTA, tfmt, arg);	va_end(arg);} static Image*hpcolor(int hp, int maxhp){	Image *c; 	if(hp > (maxhp/4)*3)		c = ui.cols[CGREEN];	else if(hp > maxhp/2)		c = ui.cols[CYELLOW];	else if(hp > maxhp/4)		c = ui.cols[CORANGE];	else		c = ui.cols[CRED]; 	return c;} /* draw the whole level on the screen using tileset ts. * r.min is the upper left visible tile, and r.max is * the bottom right visible tile. * */static voiddrawlevel(Level *l, Tileset *ts, Rectangle r){	int what;	double hp, maxhp;	char buf[16];	Point p, p2, sp, bot;	Image *c;	Tile *t;	Monster *m;		bot = (Point){0, ts->height - smallfont->height}; 	for(p.x = r.min.x; p.x < r.max.x; p.x++){		for(p.y = r.min.y; p.y < r.max.y; p.y++){			/* screen space */			sp = p;			sp = addpt(sp, ui.camp);			sp.x *= ts->width;			sp.y *= ts->height;			sp = addpt(sp, screen->r.min);			t = tileat(l, p);			if(t){				what = t->terrain;				if(t->unit)					what = t->unit;				else if(t->feat)					what = t->feat;				drawtile(ts, screen, sp, what);				if(t->monst != nil){					m = t->monst;					hp = m->hp, maxhp = m->maxhp;					snprint(buf, sizeof(buf), "%d/%d HP", (int)hp, (int)maxhp);					c = hpcolor(hp, maxhp);					p2 = addpt(sp, bot);					string(screen, p2, c, ZP, smallfont, buf);					p2.y -= smallfont->height;					snprint(buf, sizeof(buf), "L%d", m->xpl);					stringbg(screen, p2, display->white, ZP, smallfont, buf, display->black, ZP);					/* debugging the ai state */					if(debug){/*						if(m->ai != nil){							stringbg(screen, sp, display->white, ZP, smallfont, m->ai->name, display->black, ZP);						} else {							stringbg(screen, sp, display->white, ZP, smallfont, m->aglobal->name, display->black, ZP);						}*/					}				} else if(hasflagat(l, p, Fhasitem)){					drawtile(ts, screen, sp, ilnth(&t->items, 0)->id->tile);				}			}		}	}} static voiddrawui(Rectangle r){	int i, logline, sw;	double hp, maxhp;	Point p;	Image *c;	char buf[256];	Msg *m; 	sw = stringwidth(font, " "); 	draw(screen, r, display->black, nil, ZP); 	logline = Dy(r) - (font->height*2); 	p = r.min;	p.y += logline-font->height; 	m = ui.msg;	for(i = logline/font->height; m != nil && i > 0; i--){		snprint(buf, sizeof(buf), "%s", m->msg);		string(screen, p, ui.cols[m->color], ZP, font, buf);		p.y -= font->height;		m = m->next;	} 	p = r.min;	p.y += logline;	line(screen, p, Pt(screen->r.max.x, p.y), Enddisc, Enddisc, 0, display->white, ZP); 	snprint(buf, sizeof(buf), "%s the %s", user, player->md->name);	p = string(screen, p, display->white, ZP, font, buf);	p.x += sw*2; 	snprint(buf, sizeof(buf), "%d kills", player->kills);	p = string(screen, p, display->white, ZP, font, buf); 	p.x = r.min.x;	p.y += font->height;	snprint(buf, sizeof(buf), "T:%ld", turn);	p = string(screen, p, display->white, ZP, font, buf);	p.x += sw; 	snprint(buf, sizeof(buf), "L:%d XP:%d/%d", player->xpl, player->xp, xpcalc(player->xpl));	p = string(screen, p, display->white, ZP, font, buf);	p.x += sw; 	hp = player->hp, maxhp = player->maxhp;	snprint(buf, sizeof(buf), "HP:%d/%d", (int)hp, (int)maxhp);	if(hp > (maxhp/4)*3)		c = ui.cols[CGREEN];	else if(hp > maxhp/2)		c = ui.cols[CYELLOW];	else if(hp > maxhp/4)		c = ui.cols[CORANGE];	else		c = ui.cols[CRED];	p = string(screen, p, c, ZP, font, buf);	p.x += sw; 	snprint(buf, sizeof(buf), "AC:%d", player->ac);	p = string(screen, p, display->white, ZP, font, buf);	p.x += sw; 	if(player->flags & Mdead){		p = stringbg(screen, p, display->black, ZP, font, "dead", ui.cols[CRED], ZP);		p.x += sw;	}} static Rectangleview(Point pos, Level *l, int scrwidth, int scrheight){	Point p;	Rectangle r; 	/* compute vieweable area of the map */	p = Pt((scrwidth/2)+2, (scrheight/2));	r = Rpt(subpt(pos, p), addpt(pos, p));	rectclip(&r, Rect(0, 0, l->width, l->height));	return r;} static voidredraw(UI *ui, int new, int justui){	int width, height; 	if(new){		if(getwindow(display, Refnone) < 0)			sysfatal("getwindow: %r"); 		/* height should be big enough for 5 tiles and the ui size */		if(Dx(screen->r) < 300 || Dy(screen->r) < (ui->tiles->height*5 + font->height*ui->uisz)){			fprint(2, "window %R too small\n", screen->r);			return;		} 	} 	ui->uir = Rpt(Pt(screen->r.min.x, screen->r.max.y-(font->height*ui->uisz)), screen->r.max); 	if(!justui){		width = Dx(screen->r) / ui->tiles->width;		height = (Dy(screen->r) - (font->height*ui->uisz)) / ui->tiles->height;		ui->viewr = view(player->pt, player->l, width, height+2);		ui->camp = subpt(Pt(width/2, height/2), player->pt);		draw(screen, screen->r, display->black, nil, ZP);		drawlevel(player->l, ui->tiles, ui->viewr);	}	drawui(ui->uir);	flushimage(display, 1);} voiduiredraw(int justui){	redraw(&ui, 0, justui);} static Runedbgmenu(int idx, char *s, int sz){	switch(idx){	case 0:		snprint(s, sz, "toggle debug flag: %d", debug);		return 'd';	case 1:		snprint(s, sz, "toggle far messages: %d", farmsg);		return 'f';	case 2:		snprint(s, sz, "revive and gain max hp");		return 'r';	case 3:		snprint(s, sz, "wish for an item");		return 'w';	}	return Runemax;} static Runeinventorymenu(int idx, char *s, int sz){	Item *i; 	if(player->inv.count <= 0 || idx > player->inv.count-1)		return Runemax; 	i = ilnth(&player->inv, idx);	snprint(s, sz, "%i", i); 	return idx+'a';} static Runeequipmenu(int idx, char *s, int sz){	Item *ptr;	Rune key[NEQUIP] = { 'w', 'c', 'h', 's', 'a', 'g', 'b' }; 	if(idx >= NEQUIP)		return Runemax; 	ptr = player->armor[idx]; 	if(ptr != nil){		snprint(s, sz, "%s: %i", itypetoname(ptr->id->type), ptr);

Lines 1–500 of 1286.