ref: af0488a8bcb5aeb7a1c263079cb13ec7419c19db
dir: /sys/src/games/sokoban/sokoban.c/
#include <u.h> #include <libc.h> #include <draw.h> #include <event.h> #include "sokoban.h" #define SOKOTREE "/sys/games/lib/sokoban/" char *LEasy = SOKOTREE "levels/easy.slc"; char *LHard = SOKOTREE "levels/hard.slc"; char *levelfile; #define SOKOIMG SOKOTREE "images/" char *GRImage = SOKOIMG "right.bit"; char *GLImage = SOKOIMG "left.bit"; char *WallImage = SOKOIMG "wall.bit"; char *EmptyImage = SOKOIMG "empty.bit"; char *CargoImage = SOKOIMG "cargo.bit"; char *GoalCargoImage= SOKOIMG "goalcargo.bit"; char *GoalImage = SOKOIMG "goal.bit"; char *WinImage = SOKOIMG "win.bit"; char *buttons[] = { "restart", "easy", "hard", "noanimate", /* this menu string initialized in main */ "exit", 0 }; char **levelnames; Menu menu = { buttons, }; Menu lmenu; void buildmenu(void) { int i; if (levelnames != nil) { for(i=0; levelnames[i] != 0; i++) free(levelnames[i]); } levelnames = realloc(levelnames, sizeof(char*)*(numlevels+1)); if (levelnames == nil) sysfatal("cannot allocate levelnames"); for(i=0; i < numlevels; i++) levelnames[i] = genlevels(i); levelnames[numlevels] = 0; lmenu.item = levelnames; } 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); bg = eallocimage(one, 1, DDarkyellow); text = eallocimage(one, 1, DBluegreen); gright = eloadfile(GRImage); gleft = eloadfile(GLImage); wall = eloadfile(WallImage); empty = eloadfile(EmptyImage); empty->repl = 1; goalcargo = eloadfile(GoalCargoImage); cargo = eloadfile(CargoImage); goal = eloadfile(GoalImage); win = eloadfile(WinImage); } int key2move(int key) { int k = 0; switch(key) { case 61454: k = Up; break; case 63488: k = Down; break; case 61457: k = Left; break; case 61458: k = Right; break; } return k; } static Route* mouse2route(Mouse m) { Point p, q; Route *r; p = subpt(m.xy, screen->r.min); p.x /= BoardX; p.y /= BoardY; q = subpt(p, level.glenda); // fprint(2, "x=%d y=%d\n", q.x, q.y); if (q.x == 0 && q.y == 0) return nil; if (q.x == 0 || q.y == 0) { if (q.x < 0) r = extend(nil, Left, -q.x, Pt(level.glenda.x, p.y)); else if (q.x > 0) r = extend(nil, Right, q.x, Pt(level.glenda.x, p.y)); else if (q.y < 0) r = extend(nil, Up, -q.y, level.glenda); else if (q.y > 0) r = extend(nil, Down, q.y, level.glenda); else r = nil; if (r != nil && isvalid(level.glenda, r, validpush)) return r; freeroute(r); } return findroute(level.glenda, p); } char * genlevels(int i) { if(i >= numlevels) return 0; return smprint("level %d", i+1); } int finished(void) { int x, y; for(x = 0; x < MazeX; x++) for(y = 0; y < MazeY; y++) if(level.board[x][y] == Goal) 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(level.max))) { drawlevel(); } drawscreen(); } void main(int argc, char **argv) { Mouse m; Event ev; int e; Route *r; int timer; Animation a; int animate; if(argc == 2) levelfile = argv[1]; else levelfile = LEasy; if(! loadlevels(levelfile)) { fprint(2, "usage: %s [levelfile]\n", argv[0]); exits("usage"); } buildmenu(); animate = 0; buttons[3] = animate ? "noanimate" : "animate"; if(initdraw(nil, nil, "sokoban") < 0) sysfatal("initdraw failed: %r"); einit(Emouse|Ekeyboard); timer = etimer(0, 200); initanimation(&a); allocimages(); glenda = gright; eresized(0); for(;;) { e = event(&ev); switch(e) { case Emouse: m = ev.mouse; if(m.buttons&1) { stopanimation(&a); r = mouse2route(m); if (r) setupanimation(&a, r); if (! animate) { while(onestep(&a)) ; drawscreen(); } } if(m.buttons&2) { int l; /* levels start from 1 */ lmenu.lasthit = level.index; l=emenuhit(2, &m, &lmenu); if(l>=0){ stopanimation(&a); level = levels[l]; drawlevel(); drawscreen(); } } if(m.buttons&4) switch(emenuhit(3, &m, &menu)) { case 0: stopanimation(&a); level = levels[level.index]; drawlevel(); drawscreen(); break; case 1: stopanimation(&a); loadlevels(LEasy); buildmenu(); drawlevel(); drawscreen(); break; case 2: stopanimation(&a); loadlevels(LHard); buildmenu(); drawlevel(); drawscreen(); break; case 3: animate = !animate; buttons[3] = animate ? "noanimate" : "animate"; break; case 4: exits(nil); } break; case Ekeyboard: if(level.done) break; stopanimation(&a); switch(ev.kbdc) { case 127: case 'q': case 'Q': exits(nil); case 'n': case 'N': if(level.index < numlevels - 1) { level = levels[++level.index]; drawlevel(); drawscreen(); } break; case 'p': case 'P': if(level.index > 0) { level = levels[--level.index]; drawlevel(); drawscreen(); } break; case 'r': case 'R': level = levels[level.index]; drawlevel(); drawscreen(); break; case 61454: case 63488: case 61457: case 61458: case ' ': move(key2move(ev.kbdc)); drawscreen(); break; default: // fprint(2, "key: %d]\n", e.kbdc); break; } break; default: if (e == timer) { if (animate) onestep(&a); else while(onestep(&a)) ; drawscreen(); } break; } if(finished()) { level.done = 1; drawwin(); drawscreen(); sleep(3000); if(level.index < numlevels - 1) { level = levels[++level.index]; drawlevel(); drawscreen(); } } } }