ref: ef84ea063aea16fea82f017114334ef84e2034ce
parent: 43640e24595ac2f5dda0fe0a837bafd53c951490
author: mkf <mkf@cloud9p.org>
date: Sat May 11 11:59:38 EDT 2024
gui9net: a basic networked client
--- /dev/null
+++ b/gui9net.c
@@ -1,0 +1,391 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <event.h>
+#include "netclient.h"
+#include "engine.h"
+
+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;
+
+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(state == Won)
+ draw(screen, screen->r, won, nil, ZP);
+ else if(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(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", 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();
+
+ dir = pointdir(g, p);
+
+ if(grid[p.x][p.y] >= 999 || dir == Err)
+ return;
+
+ /* find the direction to p from our currently pos, then move */
+ domove(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(grid[p.x][p.y] >= 999)
+ return;
+
+ doput(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] [-n server]\n", argv0);
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ Mouse m;
+ Event ev;
+ Netmsg *msg;
+ int e, mousedown=0;
+
+ /* 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':
+ ptype[1] = Computer;
+ break;
+ case 'n':
+ ptype[0] = Net;
+ ptype[1] = Net;
+ networked = 1;
+ server = EARGF(usage());
+ break;
+ default:
+ usage();
+ }ARGEND
+ if(initdraw(nil, nil, "glendy") < 0)
+ sysfatal("initdraw failed: %r");
+ einit(Emouse);
+
+ if(server != nil && networked)
+ {
+ srvfd = dial(server, nil, nil, nil);
+ if(srvfd < 0)
+ sysfatal("unable to connect: %r");
+ }
+
+ resize();
+
+ srand(time(0));
+
+ allocimages();
+// initlevel(); /* must happen before "eresized" */
+ eresized(0);
+
+ netmain(); /* CONN */
+ netmain(); /* SYNC */
+ netmain(); /* TURN/WAIT */
+ for(;;)
+ {
+ if(networked && waitbit)
+ {
+ msg = netmain();
+ if(msg->tokens[0] != nil && strcmp(msg->tokens[0], "SYNC"))
+ drawlevel();
+ }
+ e = event(&ev);
+ switch(e)
+ {
+ case Emouse:
+ m = ev.mouse;
+ if(m.buttons == 0)
+ {
+ if(mousedown && (state == Playing || state == Start))
+ {
+ mousedown = 0;
+ if(turn % 2 == 0)
+ put(m.xy);
+ else
+ move(m.xy);
+ if(!networked)
+ drawlevel();
+ }
+ }
+ if(m.buttons&1)
+ {
+ mousedown = 1;
+ }
+ if(m.buttons&2)
+ {
+ switch(emenuhit(2, &m, &mmenu))
+ {
+ case 0:
+ difficulty = DEasy;
+ initlevel();
+ break;
+ case 1:
+ difficulty = DMed;
+ initlevel();
+ break;
+ case 2:
+ difficulty = DHard;
+ initlevel();
+ break;
+ case 3:
+ difficulty = DImp;
+ initlevel();
+ break;
+ }
+ drawlevel();
+ }
+ if(m.buttons&4) {
+ switch(emenuhit(3, &m, &rmenu))
+ {
+ case New:
+ initlevel();
+ break;
+ case Undo:
+ undo();
+ break;
+ case Restart:
+ restart();
+ break;
+ case Exit:
+ exits(nil);
+ }
+ drawlevel();
+ }
+ break;
+ }
+ }
+}