#include #include #include #include #include #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); } } }