ref: d5c2af3bcb04031bd6f2724a480b9c7758eeacbb
dir: /gui9.c/
#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 == STATE_WON)
draw(screen, screen->r, won, nil, ZP);
else if(game.state == 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 PIECE_WALL:
fillellipse(screen, addpt(screen->r.min, p), rx, ry, cc, ZP);
break;
case PIECE_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 = finddir(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;
}
}
}
}