wm: glendy

ref: be15c241daa91595963dd986f7cbfcbc904557ba
dir: /gui9.c/

View raw version
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include "engine.h"
#include "netclient.h"

Game game;

Font *font;

Image	*gl;	/* glenda */
Image 	*glm;	/* glenda's mask */
Image	*cc; /* clicked */
Image	*ec; /* empty; not clicked */
Image 	*bg;
Image 	*lost;
Image	*won;

int debug;
char *server = nil;

enum
{
	New = 0,
	Undo,
	Restart,
	Exit,

	Border = 3,
};

char *mbuttons[] = 
{
	"Easy",
	"Medium",
	"Hard",
	"Impossible",
	0
};

char *rbuttons[] = 
{
	"New",
	"Undo",
	"Reset",
	"Exit",
	0
};

Menu mmenu = 
{
	mbuttons,
};

Menu rmenu =
{
	rbuttons,
};

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;
}

Image *
eloadfile(char *path)
{
	Image *img;
	int fd;

	fd = open(path, OREAD);
	if(fd < 0) {
		fprint(2, "cannot open image file %s: %r\n", path);
		exits("image");
	}
	img = readimage(display, fd, 0);
	if(img == nil)
		sysfatal("cannot load image: %r");
	close(fd);
	
	return img;
}

void
allocimages(void)
{
	Rectangle one = Rect(0, 0, 1, 1);
	
	cc = eallocimage(one, 1, DGreyblue);
	ec = eallocimage(one, 1, DPalebluegreen);
	bg = eallocimage(one, 1, DPaleyellow);
	lost = eallocimage(one, 1, DRed);
	won = eallocimage(one, 1, DPalegreen);
	gl = eloadfile("/lib/face/48x48x4/g/glenda.1");

	glm = allocimage(display, Rect(0, 0, 48, 48), gl->chan, 1, DCyan);
	if(glm == nil)
        		sysfatal("cannot allocate mask: %r");

    	draw(glm, glm->r, display->white, nil, ZP);
    	gendraw(glm, glm->r, display->black, ZP, gl, gl->r.min);
    	freeimage(gl);
    	gl = display->black;


}

Point
pix2board(int x, int y)
{
	float d, rx, ry, yh;
	int ny, nx;

	/* XXX: float→int causes small rounding errors */

	d = (float)(Dx(screen->r) > Dy(screen->r)) ? Dy(screen->r) -20: Dx(screen->r)-20;
	rx = d/(float)SzX;
	rx = rx/2.0;
	ry =d/(float)SzY;
	ry = ry/2.0;

	yh = ry/3.73205082;

	/* reverse board2pix() */
	ny = (int)(((float)y - ry)/(2*ry - ((y>2*ry)?yh:0.0)) + 0.5); /* ny = (y - ry)/(2ry-yh) */
	nx = (int)(((float)x - rx - (ny%2?rx:0.0))/(rx*2.0) + 0.5); /* nx = (x - rx - rx)/2rx */
	
	if (nx >= SzX)
		nx = SzX-1;
	if (ny >=SzY)
		ny = SzY-1;

	return Pt(nx, ny);
}

/* unnecessary calculations here, but it's fine */
Point
board2pix(int x, int y)
{
	float d, rx, ry, yh;
	int nx, ny;

	d = (float)(Dx(screen->r) > Dy(screen->r)) ? Dy(screen->r) -20 : Dx(screen->r) -20;
	rx = d/(float)SzX;
	rx = rx/2.0;
	ry = d/(float)SzY;
	ry = ry/2.0;

	yh = ry/3.73205082;

	nx = (int)((float)x*rx*2.0+rx +(y%2?rx:0.0)); /* nx = x*(2rx) + rx + rx (conditional) */
	ny = (int)((float)y*(ry*2.0-(y>0?yh:0.0)) + ry); /* ny = y*(2ry-yh) +ry */
	return Pt(nx, ny);
}

void
drawlevel(void)
{
	Point p;
	int  x, y, rx, ry, d;
	char *s = nil;

	if(game.state == Won)
		draw(screen, screen->r, won, nil, ZP);
	else if(game.state == Lost)
		draw(screen, screen->r, lost, nil, ZP);
	else
		draw(screen, screen->r, bg, nil, ZP);

	d = (Dx(screen->r) > Dy(screen->r)) ? Dy(screen->r) -20: Dx(screen->r) -20;
	rx = (int)ceil((float)(d-2*Border)/(float)SzX)/2;
	ry = (int)ceil((float)(d-2*Border)/(float)SzY)/2;

	for(x = 0; x < SzX; x++) {
		for(y = 0; y < SzY; y++)
		{
			p = board2pix(x, y);
			switch(game.grid[x][y])
			{
				case Wall: 
					fillellipse(screen, addpt(screen->r.min, p), rx, ry, cc, ZP);
					break;
				case Glenda:
					p = addpt(screen->r.min, p);
					fillellipse(screen, p, rx, ry, ec, ZP);
					p = subpt(p, Pt(24, 24));
					draw(screen, Rpt(p, addpt(p, Pt(48, 48))), gl, glm, ZP);
					break;
				default:
					fillellipse(screen, addpt(screen->r.min, p), rx, ry, ec, ZP);
					USED(s);
					if(debug)
					{
						s = smprint("%d", game.grid[x][y]);
						string(screen, addpt(screen->r.min, p), display->black, ZP, font, s);
						free(s);
					}
					break;
			}
		}
	}
	flushimage(display, 1);
}

void
move(Point m)
{
	int dir;
	Point g, p, nm;

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

	/* figure out where the click falls */
	p = pix2board(nm.x, nm.y);
	g = findglenda(&game);
	
	dir = pointdir(g, p);
	
	if(game.grid[p.x][p.y] >= 999 || dir == Err)
		return;

	/* find the direction to p from our currently pos, then move */
	domove(&game, dir);
}

void
put(Point m)
{
	Point p, nm;

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

	/* figure out where the click falls */
	p = pix2board(nm.x, nm.y);
	
	if(game.grid[p.x][p.y] >= 999)
		return;

	doput(&game, p);
}

void
resize(void)
{
	int fd, size = (Dx(screen->r) > Dy(screen->r)) ? Dy(screen->r) + 20 : Dx(screen->r)+20; 

	fd = open("/dev/wctl", OWRITE);
	if(fd >= 0)
	{
		fprint(fd, "resize -dx %d -dy %d", size, size);
		close(fd);
	}
}

void
eresized(int new)
{
	if(new && getwindow(display, Refnone) < 0)
		sysfatal("can't reattach to window");
	
	drawlevel();
}

void
usage(void)
{
	fprint(2, "usage: %s [-dg] [-s sidenum] [-n server]\n"
	"side:\n"
	"\t0: trapper\n"
	"\t1: glenda\n"
	"\t2: random\n"
	, argv0);
	exits("usage");
}

void 
main(int argc, char **argv)
{
	char *side;
	Mouse m;
	Event ev;
	Netmsg *msg;
	int e, mousedown = 0;

	pnick = getenv("user");
	/* todo, add flags for human playing */
	ARGBEGIN{
		case 'D':
			debug++;
			break;
		case 'd':
			//ptype[0] = Computer /* todo */
			sysfatal("No computer player for defenders yet");
			break;
		case 'g':
			game.ptype[1] = Computer;
			break;
		case 's':
			side = EARGF(usage());
			pside = atoi(side);
			break;
		case 'n':
			game.ptype[0] = Net;
			game.ptype[1] = Net;
			game.networked = 1;
			server = EARGF(usage());
			break;
		case 'N':
			pnick = EARGF(usage());
			break;
		default:
			usage();
	}ARGEND
	if(initdraw(nil, nil, "glendy") < 0)
		sysfatal("initdraw failed: %r");
	einit(Emouse);
	
	resize();

	srand(time(0));

	allocimages();

	if(server != nil && game.networked)
	{
		srvfd = dial(server, nil, nil, nil);
		if(srvfd < 0)
			sysfatal("unable to connect: %r");

		netinit(&game);
		drawlevel();
	}
	else
		initlevel(&game);	/* must happen before "eresized" */

	eresized(0);
	for(;;)
	{
		if(game.networked && game.waitbit && isplaying(&game))
		{
			msg = netmain(&game);
			if(msg->tokens[0] != nil && strcmp(msg->tokens[0], "SYNC"))
				drawlevel();
		}
		else
		{
			e = event(&ev);
			switch(e)
			{
				case Emouse:
					m = ev.mouse;
					if(m.buttons == 0)
					{
						if(mousedown && isplaying(&game))
						{
							mousedown = 0;
							if(game.turn % 2 == 0)
								put(m.xy);
							else
								move(m.xy);
							if(!game.networked)
								drawlevel();
						}
					}
					if(m.buttons&1)
					{
						mousedown = 1;
					}
					if(m.buttons&2)
					{
						switch(emenuhit(2, &m, &mmenu))
						{
						case 0:
							game.difficulty = DEasy;
							initlevel(&game);
							break;
						case 1:				
							game.difficulty = DMed;
							initlevel(&game);
							break;
						case 2:
							game.difficulty = DHard;
							initlevel(&game);
							break;
						case 3:
							game.difficulty = DImp;
							initlevel(&game);
							break;
						}
						drawlevel();
					}
					if(m.buttons&4)
					{
						switch(emenuhit(3, &m, &rmenu))
						{
							case New:
								initlevel(&game);
								break;
							case Undo:
								undo(&game);
								break;
							case Restart:
								restart(&game);
								break;
							case Exit:
								exits(nil);
						}
						drawlevel();
					}
					break;
			}
		}
	}
}