ref: 3f5e3ef31d6c423f7eb86fcd9059c5a7387a5e72
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; } } } }