Plan 9 from Bell Labs’s /usr/web/sources/contrib/axel/rushhour/src/rushhour.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <cursor.h>

#include "rushhour.h"

enum {
	LRestart,
	LWeird = 1,
	LBegin,
	LInter,
	LTInter,
	LAdv,
	LTAdv,
	LExpert,
	LTExpert,
	LMaster,
	LArgv,
	Lbeyond,
	LRegen = Lbeyond,
	LView,
	LExit,
};

int ttnrs[] = {
	LTInter,
	LTAdv,
	LTExpert
};

char *lvlfiles[] = {
[LWeird]	"/sys/games/lib/rushhour/levels/weird.slc",
[LBegin]	"/sys/games/lib/rushhour/levels/begin.slc",
[LInter]	"/sys/games/lib/rushhour/levels/inter.slc",
[LAdv]	"/sys/games/lib/rushhour/levels/adv.slc",
[LExpert]	"/sys/games/lib/rushhour/levels/expert.slc",
[LMaster]	"/sys/games/lib/rushhour/levels/master.slc",
[LTInter]	"/mnt/trafficfs/inter",
[LTAdv]	"/mnt/trafficfs/adv",
[LTExpert]	"/mnt/trafficfs/expert",
[LArgv]	"",
};

char *ctlfile = "/mnt/trafficfs/ctl";

char *gencommands[] = {
[LTInter]	"gen inter 20 30 200\n",
[LTAdv]	"gen adv 30 40 200\n",
[LTExpert]	"gen expert 40 55 200\n",
};

char *resetcommands[] = {
[LTInter]	"reset inter\n",
[LTAdv]	"reset adv\n",
[LTExpert]	"reset expert\n",
};


int LFaces;
char *imgdir;

char *buttons[] = 
{
	"restart",
	"weird",
	"beginner",
	"intermediate",
	"tt intermediate",
	"advanced",
	"tt advanced",
	"expert",
	"tt expert",
	"grand master",
	"(no input file)",
	"regenerate tt",
	"faces",
	"exit",
	0
};

char *facesorcars[] = {
	"faces",
	"cars",
};

char *nottbuttons[] = 
{
[LTInter]	"(no tt intermediate)",
[LTAdv]	"(no tt advanced)",
[LTExpert] "(no tt expert)",
};

char **levelnames;

Menu menu = 
{
	buttons,
};

Menu lmenu =
{
	levelnames,
};

Cursor whitearrow = {
	{0, 0},
	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC, 
	 0xFF, 0xF0, 0xFF, 0xF0, 0xFF, 0xF8, 0xFF, 0xFC, 
	 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC, 
	 0xF3, 0xF8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, },
	{0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x06, 0xC0, 0x1C, 
	 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x38, 0xC0, 0x1C, 
	 0xC0, 0x0E, 0xC0, 0x07, 0xCE, 0x0E, 0xDF, 0x1C, 
	 0xD3, 0xB8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, }
};


static int
istt(int n)
{
	switch(n) {
	case LTInter:
	case LTAdv:
	case LTExpert:
		return 1;
	default:
		return 0;
	}
}

void*
erealloc(void *p, ulong sz)
{
	void *v;

	v = realloc(p, sz);
	if(v == nil)
		sysfatal("realloc %lud fails\n", sz);
	return v;
}

char *
estrdup(char *s)
{
	char *r;

	r = strdup(s);
	if(r == nil)
		sysfatal("strdup fails\n");
	return r;
}

Image *
eallocimage(Rectangle r, int repl, uint color)
{
	Image *tmp;

	tmp = allocimage(display, r, screen->chan, repl, color);
	if(tmp == nil)
		sysfatal("cannot allocate buffer image: %r");

	return tmp;
}

enum { 
	Facesize = 48
};

static Image*
readbit(int fd, ulong chan, char *path)
{
	char buf[4096], hx[4], *p;
	uchar data[Facesize*Facesize];	/* more than enough */
	int nhx, i, n, ndata, nbit;
	Image *img;

	n = readn(fd, buf, sizeof buf);
	if(n <= 0)
		return nil;
	if(n >= sizeof buf)
		n = sizeof(buf)-1;
	buf[n] = '\0';

	n = 0;
	nhx = 0;
	nbit = chantodepth(chan);
	ndata = (Facesize*Facesize*nbit)/8;
	p = buf;
	while(n < ndata) {
		p = strpbrk(p+1, "0123456789abcdefABCDEF");
		if(p == nil)
			break;
		if(p[0] == '0' && p[1] == 'x')
			continue;

		hx[nhx] = *p;
		if(++nhx == 2) {
			hx[nhx] = 0;
			i = strtoul(hx, 0, 16);
			data[n++] = ~i;
			nhx = 0;
		}
	}
	if(n < ndata)
		sysfatal("short face %s", path);

	img = allocimage(display, Rect(0,0,Facesize,Facesize), chan, 0, 0);
	if(img == nil)
		return nil;

	loadimage(img, img->r, data, ndata);
	return img;
}

static Image*
resample(Image *src)
{
	int p[2], q[2], kid, pid;
	Image *img;

	if(pipe(p) < 0 || pipe(q) < 0)
		sysfatal("can't make a pipe: %r");
	kid = fork();
	switch(kid){
	case -1:
		sysfatal("can't fork: %r");
	default:
		close(p[0]);
		close(q[1]);
		if (writeimage(p[1], src, 0) < 0)
			fprint(2, "error writeimage\n");
		close(p[1]);
		img = readimage(display, q[0], 0);
		close(q[0]);
		/*
		 * we may have unknown children forked by a previous
		 * program (e.g., rc) that then execed us.
		 */
		while ((pid = waitpid()) != kid && pid != -1)
			continue;
 		return img;
	case 0:
		close(p[1]);
		close(q[0]);
		dup(p[0], 0);
		dup(q[1], 1);
		execl("/bin/resample", "resample", "-x50%", (char *)nil);
		sysfatal("can't exec resample: %r");
	}
	return nil;
}

Image *
openimage(char *dir, char *file)
{
	Image *img;
	int fd;
	char path[1024];

	if (dir != nil)
		sprint(path, "%s/%s", dir, file);
	else
		strcpy(path, file);

	fd = open(path, OREAD);
	if(fd < 0)
		sysfatal("open %s: %r", path);
	img = readimage(display, fd, 0);
	if(img == nil)
		sysfatal("readimage %s: %r", path);
	close(fd);

	return img;
}

Image*
openface(char *path)
{
	char *p;
	int fd, n;
	Image *tmp, *img;

	p = strstr(path, "48x48x");
	if(p == nil)
		img = openimage(nil, path);
	else {
		n = atoi(p+6);
		if(n < 4){
			if((fd = open(path, OREAD)) < 0)
				sysfatal("open %s: %r", path);
			img =  readbit(fd, n==1 ? GREY1 : GREY2, path);
		} else
			img =  openimage(nil, path);
	}
	if (tinyflag) {
		tmp = resample(img);
		if (tmp != nil)
			return tmp;
		return img;
	}
	
	return img;
}

static void
allocimages(void)
{
	int i;
	Rectangle one = Rect(0, 0, 1, 1);
	
	bg		= eallocimage(one, 1, 0xAAAAAAFF);//0xEEEEEEFF
	text 		= eallocimage(one, 1, DBluegreen);
	black 	= eallocimage(one, 1, DBlack);

	wall = openimage(imgdir, "wall.bit");
	empty = openimage(imgdir, "empty.bit");
	empty->repl = 1;
	win = openimage(imgdir, "win.bit");

	col[CarX]		= eallocimage(one, 1, 0xFF0000FF);
	col[CarY]		= eallocimage(one, 1, 0xFF0000FF);
	col[CarZ]		= eallocimage(one, 1, 0xFF0000FF);
	col[CarA]		= eallocimage(one, 1, 0x00FF55FF);
	col[CarB]		= eallocimage(one, 1, 0xCC8800FF);
	col[CarC]		= eallocimage(one, 1, 0x00AAFFFF);
	col[CarD]		= eallocimage(one, 1, 0xFF00FFFF);
	col[CarE]		= eallocimage(one, 1, 0x770077FF);
	col[CarF]		= eallocimage(one, 1, 0x008844FF);
	col[CarG]		= eallocimage(one, 1, 0x333333FF); //0x222222FF
	col[CarH]		= eallocimage(one, 1, 0xBBBB5DFF); //0xCCCC88FF
	col[CarI]		= eallocimage(one, 1, 0xFFFF00FF);
	col[CarJ]		= eallocimage(one, 1, 0x884400FF);
	col[CarK]		= eallocimage(one, 1, 0x555500FF);
	for(i=CarL; i <= CarN; i++)
		col[i]	= col[CarA-CarL+i];
	col[TruckO]	= eallocimage(one, 1, 0xFFAA00FF);
	col[TruckP]	= eallocimage(one, 1, 0xBB5DBBFF);
	col[TruckQ]	= eallocimage(one, 1, 0x0000FFFF);
	col[TruckR]	= eallocimage(one, 1, 0x00BB5DFF);
	for(i=TruckS; i <= TruckV; i++)
		col[i]	= col[TruckO-TruckS+i];

	face[CarX]		= openface("/lib/face/48x48x4/g/glenda.1");
	face[CarY]		= face[CarX];
	face[CarZ]		= face[CarX];
	face[CarA]		= openface("/lib/face/48x48x2/k/ken.1");
	face[CarB]		= openface("/lib/face/48x48x4/b/bobf.1");
	face[CarC]		= openface("/lib/face/48x48x4/p/philw.1");
	face[CarD]		= openface("/lib/face/48x48x4/p/presotto.1");
	face[CarE]		= openface("/lib/face/48x48x4/r/rob.1");
	face[CarF]		= openface("/lib/face/48x48x4/s/sean.1");
	face[CarG]		= openface("/lib/face/48x48x4/b/bwk.1");
	face[CarH]		= openface("/lib/face/48x48x4/c/cyoung.1");
	face[CarI]		= openface("/lib/face/48x48x4/d/dmr.1");
	face[CarJ]		= openface("/lib/face/48x48x4/d/doug.1");
	face[CarK]		= openface("/lib/face/48x48x4/h/howard.1");
	for(i=CarL; i <= CarN; i++)
		face[i]	= face[CarA-CarL+i];
	face[TruckO]	= openface("/lib/face/48x48x4/j/jmk.1");
	face[TruckP]	= openface("/lib/face/48x48x4/s/sape.1");
	face[TruckQ]	= openface("/lib/face/48x48x4/s/seanq.1");
	face[TruckR]	= openface("/lib/face/48x48x4/t/td.1");
	for(i=TruckS; i <= TruckV; i++)
		face[i]	= face[TruckO-TruckS+i];

	face[Wall]		= openface("/lib/face/48x48x2/p/pjw+9ball.2");

	car[CarX][OHoriz]	= openimage(imgdir, "redcarEW.bit");
	car[CarX][OVert]	= openimage(imgdir, "redcarNS.bit");
	car[CarY][OHoriz]	= openimage(imgdir, "limoEW.bit");
	car[CarY][OVert]	= openimage(imgdir, "limoNS.bit");
	car[CarZ][OHoriz]	= car[CarX][OHoriz];
	car[CarZ][OVert]	= car[CarX][OVert];
	car[CarA][OHoriz]	= openimage(imgdir, "AcarEW.bit");
	car[CarA][OVert]	= openimage(imgdir, "AcarNS.bit");
	car[CarB][OHoriz]	= openimage(imgdir, "BcarEW.bit");
	car[CarB][OVert]	= openimage(imgdir, "BcarNS.bit");
	car[CarC][OHoriz]	= openimage(imgdir, "CcarEW.bit");
	car[CarC][OVert]	= openimage(imgdir, "CcarNS.bit");
	car[CarD][OHoriz]	= openimage(imgdir, "DcarEW.bit");
	car[CarD][OVert]	= openimage(imgdir, "DcarNS.bit");
	car[CarE][OHoriz]	= openimage(imgdir, "EcarEW.bit");
	car[CarE][OVert]	= openimage(imgdir, "EcarNS.bit");
	car[CarF][OHoriz]	= openimage(imgdir, "FcarEW.bit");
	car[CarF][OVert]	= openimage(imgdir, "FcarNS.bit");
	car[CarG][OHoriz]	= openimage(imgdir, "GcarEW.bit");
	car[CarG][OVert]	= openimage(imgdir, "GcarNS.bit");
	car[CarH][OHoriz]	= openimage(imgdir, "HcarEW.bit");
	car[CarH][OVert]	= openimage(imgdir, "HcarNS.bit");
	car[CarI][OHoriz]	= openimage(imgdir, "IcarEW.bit");
	car[CarI][OVert]		= openimage(imgdir, "IcarNS.bit");
	car[CarJ][OHoriz]	= openimage(imgdir, "JcarEW.bit");
	car[CarJ][OVert]		= openimage(imgdir, "JcarNS.bit");
	car[CarK][OHoriz]	= openimage(imgdir, "KcarEW.bit");
	car[CarK][OVert]	= openimage(imgdir, "KcarNS.bit");
	for(i=CarL; i <= CarN; i++) {
		car[i][OHoriz]	= car[CarA-CarL+i][OHoriz];
		car[i][OVert]	= car[CarA-CarL+i][OVert];
	}
	car[TruckO][OHoriz]	= openimage(imgdir, "OlorryEW.bit");
	car[TruckO][OVert]	= openimage(imgdir, "OlorryNS.bit");
	car[TruckP][OHoriz]	= openimage(imgdir, "PlorryEW.bit");
	car[TruckP][OVert]	= openimage(imgdir, "PlorryNS.bit");
	car[TruckQ][OHoriz]	= openimage(imgdir, "QlorryEW.bit");
	car[TruckQ][OVert]	= openimage(imgdir, "QlorryNS.bit");
	car[TruckR][OHoriz]	= openimage(imgdir, "RlorryEW.bit");
	car[TruckR][OVert]	= openimage(imgdir, "RlorryNS.bit");
	for(i=TruckS; i <= TruckV; i++) {
		car[i][OHoriz]	= car[TruckO-TruckS+i][OHoriz];
		car[i][OVert]	= car[TruckO-TruckS+i][OVert];
	}

	msk[CarX][OHoriz]	= openimage(imgdir, "redcarEW-msk.bit");
	msk[CarX][OVert]	= openimage(imgdir, "redcarNS-msk.bit");
	msk[CarY][OHoriz]	= openimage(imgdir, "limoEW-msk.bit");
	msk[CarY][OVert]	= openimage(imgdir, "limoNS-msk.bit");
	msk[CarZ][OHoriz]	= msk[CarX][OHoriz];
	msk[CarZ][OVert]	= msk[CarX][OVert];
	msk[CarA][OHoriz]	= openimage(imgdir, "AcarEW-msk.bit");
	msk[CarA][OVert]	= openimage(imgdir, "AcarNS-msk.bit");
	for(i=CarB; i <= CarN; i++) {
		msk[i][OHoriz]	= msk[CarA][OHoriz];
		msk[i][OVert]	= msk[CarA][OVert];
	}
	msk[TruckO][OHoriz]	= openimage(imgdir, "OlorryEW-msk.bit");
	msk[TruckO][OVert]	= openimage(imgdir, "OlorryNS-msk.bit");
	for(i=TruckP; i <= TruckV; i++) {
		msk[i][OHoriz]	= msk[TruckO][OHoriz];
		msk[i][OVert]	= msk[TruckO][OVert];
	}
}

static Point
point2pos(Point p)
{
	p.x /= BoardX;
	p.x += Off;
	p.y /= BoardY;
	p.y += Off;

	return p;
}

static Point
mouse2point(Mouse m)
{
	Point p;

	p = subpt(m.xy, screen->r.min);

	return p;
}

static Point
point2mouse(Point mp)
{
	Point p;

	p = addpt(mp, screen->r.min);

	return p;
}

static Point
project(Point p, Point dir)
{
	if (dir.x == 0)
		p.x = 0;
	if (dir.y == 0)
		p.y = 0;
	return p;
}

static int
writectl(char *s)
{
	int ctlfd;

	ctlfd = open(ctlfile, OWRITE);
	if (ctlfd < 0) {
		fprint(2, "cannot open trafficfs ctl: %r\n");
		return 0;
	} else {
		fprint(ctlfd, "%s", s);
		close(ctlfd);
		return 1;
	}
}

static void
initttlevels(void)
{
	int i, n;

	if (access(ctlfile,0) == 0)
		for (i=0; i < nelem(ttnrs); i++) {
			n = ttnrs[i];
			if (access(lvlfiles[n],0) < 0 && ! writectl(gencommands[n])) {
				lvlfiles[n] = "";
				buttons[n] = nottbuttons[n];
			}
		}
	else
		for (i=0; i < nelem(ttnrs); i++) {
			n = ttnrs[i];
			lvlfiles[n] = "";
			buttons[n] = nottbuttons[n];
		}
}

char *
genlevels(int i)
{
	char *s;

	if(i >= numlevels)
		return 0;

	if (levels[i].name == nil)
		s = smprint("level %d", i+1);
	else
		s = smprint("level %d (%s)", i+1, levels[i].name);

	return s;
}

static void
buildmenu(void)
{
	int i;

	if (levelnames != nil) {
		for(i=0; levelnames[i] != 0; i++)
			free(levelnames[i]);
	}
	levelnames = erealloc(levelnames, sizeof(char*)*(numlevels+1));
	for(i=0; i < numlevels; i++)
		levelnames[i] = genlevels(i);
	levelnames[numlevels] = 0;
	lmenu.item = levelnames;
}

static void
doloadlevels(int n)
{
	int r;

	r = loadlevels(lvlfiles[n]);
	if (! istt(n))
		return;

	if (r < 0 && access(ctlfile,0) < 0) {
		lvlfiles[n] = "";
		buttons[n] = nottbuttons[n];
	} else if (numlevels == 0 && access(ctlfile,0) == 0) {
		writectl(resetcommands[n]);
		writectl(gencommands[n]);
		loadlevels(lvlfiles[n]);
	}
}

static void
activatelevel(Level l, Point *topleft)
{
	*topleft = Pt(-1,-1);
	level = l;
	drawlevel();
	drawscreen();
}

static void
uselevels(int l, int *lvlindex, Point *topleft)
{
	if (strcmp(lvlfiles[l], "") != 0) {
		*lvlindex = l;
		doloadlevels(*lvlindex);
		buildmenu();
		activatelevel(levels[0], topleft);
	}
}

static void
nextlevel(int *lvlindex, Point *topleft)
{
	if(level.index < numlevels - 1)
		activatelevel(levels[++level.index], topleft);
	else if (*lvlindex < Lbeyond -1 &&
	            strcmp(lvlfiles[(*lvlindex)+1], "") != 0) {
		doloadlevels(++(*lvlindex));
		buildmenu();
		activatelevel(levels[0], topleft);
	} else
		*topleft = Pt(-1,-1);
}

static void
prevlevel(int *lvlindex, Point *topleft)
{
	if(level.index > 0)
		activatelevel(levels[--level.index], topleft);
	else if (*lvlindex > 1) {
		doloadlevels(--(*lvlindex));
		buildmenu();
		activatelevel(levels[numlevels-1], topleft);
	} else
		*topleft = Pt(-1,-1);
}

static void
regenlevels(int lvlindex, Point *topleft)
{
	if (istt(lvlindex) && access(ctlfile,0) == 0) {
		writectl(gencommands[lvlindex]);
		doloadlevels(lvlindex);
		buildmenu();
		activatelevel(levels[0], topleft);
	}
}

static void
toggleview(void)
{
	if (usefaces)
		usefaces = 0;
	else
		usefaces = 1;
	buttons[LFaces] = facesorcars[usefaces];
	drawlevel();
	drawscreen();
}

static int
finished(void)
{
	int x, y;

	for(x = 0; x < MazeX; x++)
		for(y = 0; y < MazeY; y++)
			if(level.board[x][y] == level.us)
				return 0;

	return 1;
}

void
eresized(int new)
{
	Point p;

	if(new && getwindow(display, Refnone) < 0)
		sysfatal("can't reattach to window");
	
	p = Pt(Dx(screen->r), Dy(screen->r));

	if(!new || !eqpt(p, boardsize(subpt(level.max,Pt(Off,Off))))) {
		drawlevel();
	}
	drawscreen();
}

static char*
mklabel(char *s)
{
	char *p, *e, c;

	p = strrchr(s, '/');
	if (p != nil)
		p++;
	else
		p = s;
	e = strchr(p, '.');
	if (e != nil) {
		c = *e;
		*e = '\0';
		p = estrdup(p);
		*e = c;
	} else
		p = estrdup(p);
	return p;
}

static Point
key2dir(int kbdc)
{
	switch(kbdc){
	case 61454: /*Up*/
		return Pt(0,-1);
	case 63488: /*Down*/
		return Pt(0,1);
	case 61457: /*Left*/
		return Pt(-1,0);
	case 61458: /*Right*/
		return Pt(1,0);
	default:
		return Pt(0,0);
	}
}

void 
main(int argc, char **argv)
{
	Mouse m, mprev;
	Event ev;
	int e, start, up, lvlindex, l, cursoronly, mouseinitialized;
	Point p, d, off, dir, prevdir, topleft, vacant, dst, winoff, tl, tdir;
	char *fontfile, **bp;
	Rectangle *drp, *srp;

	imgdir = "/sys/games/lib/rushhour/images/normal";
	fontfile = nil;
	BoardX = 48;
	BoardY = 48;
	winoff = Pt(6,6);
	OutlineWidth = 2;

	LFaces = LView;
	lvlindex = LBegin;
	levelnames = nil;
	start = 1;
	up = 0;
	topleft = Pt(-1,-1);
	cursoronly = 0;
	mouseinitialized = 0;

	ARGBEGIN{
	case 'b':
		boutflag = 1;
		break;
	case 'f':
		usefaces = 1;
		buttons[LFaces] = facesorcars[usefaces];
		break;
	case 't':
		tinyflag = 1;
		imgdir = "/sys/games/lib/rushhour/images/small";
		fontfile = "/lib/font/bit/lucidasans/unicode.6.font";
		BoardX = 24;
		BoardY = 24;
		winoff = Pt(3,3);
		OutlineWidth = 1;
		break;
	default:
		fprint(2, "Usage: %s [-f] [-t] [levelfile]\n", argv0);
		exits("usage");
	}ARGEND

	switch(argc) {
	case 1: 
		lvlindex = LArgv;
		lvlfiles[LArgv] = argv[0];
		buttons[LArgv] = mklabel(argv[0]);
		break;
	case 0: 
		buttons[LArgv] = "";
		/* remove empty LArgv buttons menu entry */
		bp = &buttons[LArgv];
		memmove(bp, bp+1, sizeof(char*)*(nelem(buttons)-LArgv-1));
		LFaces--;
		break;
	default:
		fprint(2, "Usage: %s [-f] [-t] [levelfile]\n", argv0);
		exits("usage");
	}

	if(loadlevels(lvlfiles[lvlindex]) < 0) {
		fprint(2, "Usage: %s [-f] [-t] [levelfile]\n", argv0);
		exits("usage");
	}

	initttlevels();
	buildmenu();

	if(initdraw(nil, fontfile, "rushhour") < 0)
		sysfatal("initdraw failed: %r");
	einit(Emouse|Ekeyboard);

	SizeX = MazeX*BoardX+10,	
	SizeY = MazeY*BoardY+10,

	allocimages();
	eresized(0);

	for(;;) {
		e = event(&ev);
		switch(e) {
		case Emouse:
			m = ev.mouse;
			if (start) {
				mprev = m;
				start = 0;
			}
			if(m.buttons&1) {
				 /* setting up here instead of in next else branch
				  * did make a difference in the version with
				  * explicit selection making. should not matter here?
				  */
				up = 1;
				if (! mprev.buttons&1) {
					d = mouse2point(m);
					topleft = gettopleft(point2pos(d));
					prevdir = ZP;
				} else {
					/* up = 1;  moved upwards */
					if (! eqpt(topleft, Pt(-1,-1)) && isvehicle(item(topleft))) {
						p = mouse2point(m);
						off = subpt(p, d);
						dir = getdir(topleft, off);
						if (!eqpt(prevdir, ZP) && !eqpt(prevdir, dir)) {
							dst = destof(topleft, prevdir);
							drawboard(dst, 0, ZP);
						}
						prevdir = dir;
						off = project(off, dir);
						while(canmove(topleft,dir) && !intile(off, dir)) {
							/*
							 * superflous:
							 * dst = destof(tl, dir);
							 * drawboard(dst, 0, ZP);
							 */
							onestep(topleft, dir, &vacant);
							topleft = addpt(topleft, dir);
							drawboard(vacant, 0, ZP);
							off = subtile(off, dir);
							d = addtile(d, dir);
						}
						if (!canmove(topleft, dir))
							off = ZP;
						dst = destof(topleft, dir);
						drawboard(dst, 0, ZP);
						drawboard(topleft, 0, off);
						drawscreen();
					}
				}
			} else if (up) {
				up = 0;
				if (!eqpt(topleft, Pt(-1,-1)) && isvehicle(item(topleft))){
					p = mouse2point(m);
					dir = getdir(topleft, off);
					if (canmove(topleft, dir) && !inhalftile(off, dir)) {
						onestep(topleft, dir, &vacant);
						topleft = addpt(topleft, dir);
						drawboard(vacant, 0, ZP);
					} else {
						dst = destof(topleft, dir);
						drawboard(dst, 0, ZP);
					}
					drawboard(topleft, 0, ZP);
					drawscreen();
				}
				/*
				 * superflous; does clean up if we messed up
				 * which we don't, of course
				 * drawlevel();
				 * drawscreen();
				 */
			} else {
				if(m.buttons&2) {
					lmenu.lasthit = level.index;
					l = emenuhit(2, &m, &lmenu);
					if (l >= 0)
						activatelevel(levels[l], &topleft);
				}
				if(m.buttons&4) {
					menu.lasthit = lvlindex;
					l = emenuhit(3, &m, &menu);
					/* deal with removed empty LArgv menu entry */
					if (l >= LArgv && strcmp(lvlfiles[LArgv], "") == 0)
						l += 1;
					switch(l) {
					case LRestart:
						activatelevel(levels[level.index], &topleft);
						break;
					case LWeird:
					case LBegin:
					case LInter:
					case LTInter:
					case LAdv:
					case LTAdv:
					case LExpert:
					case LTExpert:
					case LMaster:
					case LArgv:
						uselevels(l, &lvlindex, &topleft);
						break;
					case LRegen:
						regenlevels(lvlindex, &topleft);
						break;
					case LView:
						toggleview();
						break;
					case LExit:
						exits(nil);
					}
				}
			}
			mprev = m;
			break;

		case Ekeyboard:
			switch(ev.kbdc) {
			case 127:
			case 'q':
			case 'Q':
				exits(nil);
			case 'n':
			case 'N':
				nextlevel(&lvlindex, &topleft);
				break;
			case 'p':
			case 'P':
				prevlevel(&lvlindex, &topleft);
				break;
			case 'r':
			case 'R':
				activatelevel(levels[level.index], &topleft);
				break;
			case 27: /*Esc*/
			case ' ':
				if (cursoronly) {
					cursoronly = 0;
					esetcursor(nil);
				} else {
					Point m0, p0, p1,p2;
					drp = &display->image->r;
					srp = &screen->r;
					cursoronly = 1;
					esetcursor(&whitearrow);
					if (!ptinrect(m.xy, *srp)) {
						//fprint(2, "m0 %R %P", *srp, m.xy);
						if (eqpt(m.xy, Pt(0,0)) && !mouseinitialized)
							m.xy = addpt(srp->min, Pt(Dx(*srp)/2, Dy(*srp)/2));
						else if (ptinrect(m.xy, Rect(srp->min.x, drp->min.y, srp->max.x, srp->min.y)))
							m.xy.y = srp->min.y + 1; /*above game window*/
						else if (ptinrect(m.xy, Rect(srp->max.x, srp->min.y, drp->max.x, srp->max.y)))
							m.xy.x = srp->max.x - 1; /*to right of game window*/
						else if (ptinrect(m.xy, Rect(srp->min.x, srp->max.y, srp->max.x, drp->max.y)))
							m.xy.y = srp->max.y - 1; /*below game window*/
						else if (ptinrect(m.xy, Rect(drp->min.x,drp->min.y, srp->min.x, srp->max.y)))
							m.xy.x = srp->max.x + 1; /*left of game window*/
						//fprint(2, " m1 %P\n", m.xy);
					}
					mouseinitialized = 1;
					m0 = mouse2point(m);
					p0 = point2pos(m0);
					p1 = subpt(p0, Pt(Off,Off));
					p2 = addpt(Pt(p1.x*BoardX,p1.y*BoardY), divpt(Pt(BoardX,BoardY),2));
					m.xy = point2mouse(p2);
					//fprint(2, "m0%P p0%P p1%P p2%P m1%P\n", m0, p0, p1, p2, m.xy);
					emoveto(m.xy);
				}
				break;
			case 61454: /*Up*/
			case 63488: /*Down*/
			case 61457: /*Left*/
			case 61458: /*Right*/
				if (cursoronly) {
					Point m1;
					tdir = key2dir(ev.kbdc);
					m1 = addpt(m.xy, Pt(tdir.x*BoardX,tdir.y*BoardY));
					if (ptinrect(m1, screen->r)){
						m.xy = m1;
						emoveto(m.xy);
					}
				} else {
					/* avoid updating global variables: topleft, d (dir?)
					 * unless condition in this case holds
					 * (if they are updated, they interfere with the
					 *  handling of mouse-button1-move events
					 * if cursor keys are used while at the same time
					 * mouse button 1 is continuiously pressed,
					 * and the mouse is occasionally moved).
					 * currently there is minor interference if
					 * we B1press and move with mouse tiny bits
					 * back and forth, while one side is blocked.
					 * e.g. right side blocked, we move 1/4 tile left,
					 * then 1/8 back right, back left a bit, etc.,
					 * and then start pressing cursor keys:
					 * nothhing happens, until we mouse-move
					 * the car to the next tile grid border.
					 */
					tl = gettopleft(point2pos(mouse2point(m)));
					tdir = getdir(tl, key2dir(ev.kbdc));
					if (! eqpt(tl, Pt(-1,-1)) &&
					     isvehicle(item(tl)) &&
					     canmove(tl,tdir)) {
					     	topleft = tl;
					     	dir = tdir;
						if (!eqpt(prevdir, ZP) && !eqpt(prevdir, dir)) {
							dst = destof(topleft, prevdir);
							drawboard(dst, 0, ZP);
						}
						prevdir = ZP;
						onestep(topleft, dir, &vacant);
						topleft = addpt(topleft, dir);
						dst = destof(topleft, dir);
						m.xy = addpt(m.xy, Pt(dir.x*BoardX,dir.y*BoardY));
						d = mouse2point(m);
						emoveto(m.xy);
						drawboard(dst, 0, ZP);
						drawboard(vacant, 0, ZP);
						drawboard(topleft, 0, ZP);
						drawscreen();
					}
				}	
				break;
			default:
				// fprint(2, "key: %d\n", ev.kbdc);
				break;
			}
			break;

		default:
			break;
		}

		if(finished() && ! level.done) {
			level.done = 1;
			level.win = vacant; /* set tile of 'you win' msg */
			drawwin(winoff);
			drawscreen();
			sleep(3000);
			nextlevel(&lvlindex, &topleft);
		}
	}
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.