1a7b22450SDavid du Colombier #include <u.h>
2a7b22450SDavid du Colombier #include <libc.h>
3a7b22450SDavid du Colombier #include <draw.h>
4a7b22450SDavid du Colombier #include <event.h>
5a7b22450SDavid du Colombier
6a7b22450SDavid du Colombier #include "sokoban.h"
7a7b22450SDavid du Colombier
8*0a5ecf54SDavid du Colombier #define SOKOTREE "/sys/games/lib/sokoban/"
9*0a5ecf54SDavid du Colombier
10*0a5ecf54SDavid du Colombier char *LEasy = SOKOTREE "levels/easy.slc";
11*0a5ecf54SDavid du Colombier char *LHard = SOKOTREE "levels/hard.slc";
12a7b22450SDavid du Colombier char *levelfile;
13a7b22450SDavid du Colombier
14*0a5ecf54SDavid du Colombier #define SOKOIMG SOKOTREE "images/"
15*0a5ecf54SDavid du Colombier
16*0a5ecf54SDavid du Colombier char *GRImage = SOKOIMG "right.bit";
17*0a5ecf54SDavid du Colombier char *GLImage = SOKOIMG "left.bit";
18*0a5ecf54SDavid du Colombier char *WallImage = SOKOIMG "wall.bit";
19*0a5ecf54SDavid du Colombier char *EmptyImage = SOKOIMG "empty.bit";
20*0a5ecf54SDavid du Colombier char *CargoImage = SOKOIMG "cargo.bit";
21*0a5ecf54SDavid du Colombier char *GoalCargoImage= SOKOIMG "goalcargo.bit";
22*0a5ecf54SDavid du Colombier char *GoalImage = SOKOIMG "goal.bit";
23*0a5ecf54SDavid du Colombier char *WinImage = SOKOIMG "win.bit";
24a7b22450SDavid du Colombier
25a7b22450SDavid du Colombier char *buttons[] =
26a7b22450SDavid du Colombier {
27a7b22450SDavid du Colombier "restart",
28a7b22450SDavid du Colombier "easy",
29a7b22450SDavid du Colombier "hard",
3097787252SDavid du Colombier "noanimate", /* this menu string initialized in main */
31a7b22450SDavid du Colombier "exit",
32a7b22450SDavid du Colombier 0
33a7b22450SDavid du Colombier };
34a7b22450SDavid du Colombier
3501e2ed2dSDavid du Colombier char **levelnames;
3601e2ed2dSDavid du Colombier
37a7b22450SDavid du Colombier Menu menu =
38a7b22450SDavid du Colombier {
3901e2ed2dSDavid du Colombier buttons,
40a7b22450SDavid du Colombier };
41a7b22450SDavid du Colombier
42a7b22450SDavid du Colombier Menu lmenu =
43a7b22450SDavid du Colombier {
4401e2ed2dSDavid du Colombier levelnames,
45a7b22450SDavid du Colombier };
46a7b22450SDavid du Colombier
4701e2ed2dSDavid du Colombier void
buildmenu(void)4801e2ed2dSDavid du Colombier buildmenu(void)
4901e2ed2dSDavid du Colombier {
5001e2ed2dSDavid du Colombier int i;
5101e2ed2dSDavid du Colombier
5201e2ed2dSDavid du Colombier if (levelnames != nil) {
5301e2ed2dSDavid du Colombier for(i=0; levelnames[i] != 0; i++)
5401e2ed2dSDavid du Colombier free(levelnames[i]);
5501e2ed2dSDavid du Colombier }
5601e2ed2dSDavid du Colombier levelnames = realloc(levelnames, sizeof(char*)*(numlevels+1));
5701e2ed2dSDavid du Colombier if (levelnames == nil)
5801e2ed2dSDavid du Colombier sysfatal("cannot allocate levelnames");
5901e2ed2dSDavid du Colombier for(i=0; i < numlevels; i++)
6001e2ed2dSDavid du Colombier levelnames[i] = genlevels(i);
6101e2ed2dSDavid du Colombier levelnames[numlevels] = 0;
6201e2ed2dSDavid du Colombier lmenu.item = levelnames;
6301e2ed2dSDavid du Colombier }
6401e2ed2dSDavid du Colombier
65a7b22450SDavid du Colombier Image *
eallocimage(Rectangle r,int repl,uint color)66a7b22450SDavid du Colombier eallocimage(Rectangle r, int repl, uint color)
67a7b22450SDavid du Colombier {
68a7b22450SDavid du Colombier Image *tmp;
69a7b22450SDavid du Colombier
70a7b22450SDavid du Colombier tmp = allocimage(display, r, screen->chan, repl, color);
71a7b22450SDavid du Colombier if(tmp == nil)
72a7b22450SDavid du Colombier sysfatal("cannot allocate buffer image: %r");
73a7b22450SDavid du Colombier
74a7b22450SDavid du Colombier return tmp;
75a7b22450SDavid du Colombier }
76a7b22450SDavid du Colombier
77a7b22450SDavid du Colombier Image *
eloadfile(char * path)78a7b22450SDavid du Colombier eloadfile(char *path)
79a7b22450SDavid du Colombier {
80a7b22450SDavid du Colombier Image *img;
81a7b22450SDavid du Colombier int fd;
82a7b22450SDavid du Colombier
83a7b22450SDavid du Colombier fd = open(path, OREAD);
84a7b22450SDavid du Colombier if(fd < 0) {
85a7b22450SDavid du Colombier fprint(2, "cannot open image file %s: %r\n", path);
86a7b22450SDavid du Colombier exits("image");
87a7b22450SDavid du Colombier }
88a7b22450SDavid du Colombier img = readimage(display, fd, 0);
89a7b22450SDavid du Colombier if(img == nil)
90a7b22450SDavid du Colombier sysfatal("cannot load image: %r");
91a7b22450SDavid du Colombier close(fd);
92a7b22450SDavid du Colombier
93a7b22450SDavid du Colombier return img;
94a7b22450SDavid du Colombier }
95a7b22450SDavid du Colombier
96a7b22450SDavid du Colombier
97a7b22450SDavid du Colombier void
allocimages(void)98a7b22450SDavid du Colombier allocimages(void)
99a7b22450SDavid du Colombier {
100a7b22450SDavid du Colombier Rectangle one = Rect(0, 0, 1, 1);
101a7b22450SDavid du Colombier
102a7b22450SDavid du Colombier bg = eallocimage(one, 1, DDarkyellow);
103a7b22450SDavid du Colombier text = eallocimage(one, 1, DBluegreen);
104a7b22450SDavid du Colombier
105a7b22450SDavid du Colombier gright = eloadfile(GRImage);
106a7b22450SDavid du Colombier gleft = eloadfile(GLImage);
107a7b22450SDavid du Colombier wall = eloadfile(WallImage);
108a7b22450SDavid du Colombier empty = eloadfile(EmptyImage);
109a7b22450SDavid du Colombier empty->repl = 1;
110a7b22450SDavid du Colombier goalcargo = eloadfile(GoalCargoImage);
111a7b22450SDavid du Colombier cargo = eloadfile(CargoImage);
112a7b22450SDavid du Colombier goal = eloadfile(GoalImage);
113a7b22450SDavid du Colombier win = eloadfile(WinImage);
114a7b22450SDavid du Colombier }
115a7b22450SDavid du Colombier
116a7b22450SDavid du Colombier int
key2move(int key)117a7b22450SDavid du Colombier key2move(int key)
118a7b22450SDavid du Colombier {
119a7b22450SDavid du Colombier int k = 0;
120a7b22450SDavid du Colombier
121a7b22450SDavid du Colombier switch(key) {
122a7b22450SDavid du Colombier case 61454:
123a7b22450SDavid du Colombier k = Up;
124a7b22450SDavid du Colombier break;
125a7b22450SDavid du Colombier case 63488:
126a7b22450SDavid du Colombier k = Down;
127a7b22450SDavid du Colombier break;
128a7b22450SDavid du Colombier case 61457:
129a7b22450SDavid du Colombier k = Left;
130a7b22450SDavid du Colombier break;
131a7b22450SDavid du Colombier case 61458:
132a7b22450SDavid du Colombier k = Right;
133a7b22450SDavid du Colombier break;
134a7b22450SDavid du Colombier }
135a7b22450SDavid du Colombier
136a7b22450SDavid du Colombier return k;
137a7b22450SDavid du Colombier }
13897787252SDavid du Colombier
13997787252SDavid du Colombier static Route*
mouse2route(Mouse m)14097787252SDavid du Colombier mouse2route(Mouse m)
141a7b22450SDavid du Colombier {
14297787252SDavid du Colombier Point p, q;
14301e2ed2dSDavid du Colombier Route *r;
144a7b22450SDavid du Colombier
145a7b22450SDavid du Colombier p = subpt(m.xy, screen->r.min);
146a7b22450SDavid du Colombier p.x /= BoardX;
147a7b22450SDavid du Colombier p.y /= BoardY;
148a7b22450SDavid du Colombier
14997787252SDavid du Colombier q = subpt(p, level.glenda);
15097787252SDavid du Colombier // fprint(2, "x=%d y=%d\n", q.x, q.y);
15197787252SDavid du Colombier
15297787252SDavid du Colombier if (q.x == 0 && q.y == 0)
15301e2ed2dSDavid du Colombier return nil;
15497787252SDavid du Colombier
15501e2ed2dSDavid du Colombier if (q.x == 0 || q.y == 0) {
15697787252SDavid du Colombier if (q.x < 0)
15701e2ed2dSDavid du Colombier r = extend(nil, Left, -q.x, Pt(level.glenda.x, p.y));
15801e2ed2dSDavid du Colombier else if (q.x > 0)
15901e2ed2dSDavid du Colombier r = extend(nil, Right, q.x, Pt(level.glenda.x, p.y));
16001e2ed2dSDavid du Colombier else if (q.y < 0)
16101e2ed2dSDavid du Colombier r = extend(nil, Up, -q.y, level.glenda);
16201e2ed2dSDavid du Colombier else if (q.y > 0)
16301e2ed2dSDavid du Colombier r = extend(nil, Down, q.y, level.glenda);
16401e2ed2dSDavid du Colombier else
16501e2ed2dSDavid du Colombier r = nil;
16697787252SDavid du Colombier
16701e2ed2dSDavid du Colombier if (r != nil && isvalid(level.glenda, r, validpush))
16897787252SDavid du Colombier return r;
16997787252SDavid du Colombier freeroute(r);
17001e2ed2dSDavid du Colombier }
17197787252SDavid du Colombier
17201e2ed2dSDavid du Colombier return findroute(level.glenda, p);
173a7b22450SDavid du Colombier }
174a7b22450SDavid du Colombier
175a7b22450SDavid du Colombier char *
genlevels(int i)176a7b22450SDavid du Colombier genlevels(int i)
177a7b22450SDavid du Colombier {
178a7b22450SDavid du Colombier
179a7b22450SDavid du Colombier if(i >= numlevels)
180a7b22450SDavid du Colombier return 0;
181a7b22450SDavid du Colombier
182a7b22450SDavid du Colombier return smprint("level %d", i+1);
183a7b22450SDavid du Colombier }
184a7b22450SDavid du Colombier
185a7b22450SDavid du Colombier
186a7b22450SDavid du Colombier int
finished(void)187a7b22450SDavid du Colombier finished(void)
188a7b22450SDavid du Colombier {
189a7b22450SDavid du Colombier int x, y;
190a7b22450SDavid du Colombier for(x = 0; x < MazeX; x++)
191a7b22450SDavid du Colombier for(y = 0; y < MazeY; y++)
192a7b22450SDavid du Colombier if(level.board[x][y] == Goal)
193a7b22450SDavid du Colombier return 0;
194a7b22450SDavid du Colombier
195a7b22450SDavid du Colombier return 1;
196a7b22450SDavid du Colombier }
197a7b22450SDavid du Colombier
198a7b22450SDavid du Colombier void
eresized(int new)199a7b22450SDavid du Colombier eresized(int new)
200a7b22450SDavid du Colombier {
201a7b22450SDavid du Colombier Point p;
202a7b22450SDavid du Colombier
203a7b22450SDavid du Colombier if(new && getwindow(display, Refnone) < 0)
204a7b22450SDavid du Colombier sysfatal("can't reattach to window");
205a7b22450SDavid du Colombier
206a7b22450SDavid du Colombier p = Pt(Dx(screen->r), Dy(screen->r));
207a7b22450SDavid du Colombier
208a7b22450SDavid du Colombier if(!new || !eqpt(p, boardsize(level.max))) {
209a7b22450SDavid du Colombier drawlevel();
210a7b22450SDavid du Colombier }
211a7b22450SDavid du Colombier drawscreen();
212a7b22450SDavid du Colombier }
213a7b22450SDavid du Colombier
214a7b22450SDavid du Colombier void
main(int argc,char ** argv)215a7b22450SDavid du Colombier main(int argc, char **argv)
216a7b22450SDavid du Colombier {
217a7b22450SDavid du Colombier Mouse m;
21801e2ed2dSDavid du Colombier Event ev;
21901e2ed2dSDavid du Colombier int e;
22097787252SDavid du Colombier Route *r;
22101e2ed2dSDavid du Colombier int timer;
22201e2ed2dSDavid du Colombier Animation a;
22301e2ed2dSDavid du Colombier int animate;
22401e2ed2dSDavid du Colombier
225a7b22450SDavid du Colombier
226a7b22450SDavid du Colombier if(argc == 2)
227a7b22450SDavid du Colombier levelfile = argv[1];
228a7b22450SDavid du Colombier else
229a7b22450SDavid du Colombier levelfile = LEasy;
230a7b22450SDavid du Colombier
231a7b22450SDavid du Colombier if(! loadlevels(levelfile)) {
232a7b22450SDavid du Colombier fprint(2, "usage: %s [levelfile]\n", argv[0]);
233a7b22450SDavid du Colombier exits("usage");
234a7b22450SDavid du Colombier }
23501e2ed2dSDavid du Colombier buildmenu();
236a7b22450SDavid du Colombier
23797787252SDavid du Colombier animate = 0;
23897787252SDavid du Colombier buttons[3] = animate ? "noanimate" : "animate";
23997787252SDavid du Colombier
240a7b22450SDavid du Colombier if(initdraw(nil, nil, "sokoban") < 0)
241a7b22450SDavid du Colombier sysfatal("initdraw failed: %r");
242a7b22450SDavid du Colombier einit(Emouse|Ekeyboard);
243a7b22450SDavid du Colombier
24401e2ed2dSDavid du Colombier timer = etimer(0, 200);
24501e2ed2dSDavid du Colombier initanimation(&a);
24601e2ed2dSDavid du Colombier
247a7b22450SDavid du Colombier allocimages();
248a7b22450SDavid du Colombier glenda = gright;
249a7b22450SDavid du Colombier eresized(0);
250a7b22450SDavid du Colombier
251a7b22450SDavid du Colombier for(;;) {
25201e2ed2dSDavid du Colombier e = event(&ev);
25301e2ed2dSDavid du Colombier switch(e) {
254a7b22450SDavid du Colombier case Emouse:
25501e2ed2dSDavid du Colombier m = ev.mouse;
256a7b22450SDavid du Colombier if(m.buttons&1) {
25701e2ed2dSDavid du Colombier stopanimation(&a);
25897787252SDavid du Colombier r = mouse2route(m);
25901e2ed2dSDavid du Colombier if (r)
26001e2ed2dSDavid du Colombier setupanimation(&a, r);
26101e2ed2dSDavid du Colombier if (! animate) {
26201e2ed2dSDavid du Colombier while(onestep(&a))
26301e2ed2dSDavid du Colombier ;
264a7b22450SDavid du Colombier drawscreen();
265a7b22450SDavid du Colombier }
26601e2ed2dSDavid du Colombier }
267a7b22450SDavid du Colombier if(m.buttons&2) {
268a7b22450SDavid du Colombier int l;
269a7b22450SDavid du Colombier /* levels start from 1 */
270a7b22450SDavid du Colombier lmenu.lasthit = level.index;
271a7b22450SDavid du Colombier l=emenuhit(2, &m, &lmenu);
272a7b22450SDavid du Colombier if(l>=0){
27301e2ed2dSDavid du Colombier stopanimation(&a);
274a7b22450SDavid du Colombier level = levels[l];
275a7b22450SDavid du Colombier drawlevel();
276a7b22450SDavid du Colombier drawscreen();
277a7b22450SDavid du Colombier }
278a7b22450SDavid du Colombier }
279a7b22450SDavid du Colombier if(m.buttons&4)
280a7b22450SDavid du Colombier switch(emenuhit(3, &m, &menu)) {
281a7b22450SDavid du Colombier case 0:
28201e2ed2dSDavid du Colombier stopanimation(&a);
283a7b22450SDavid du Colombier level = levels[level.index];
284a7b22450SDavid du Colombier drawlevel();
285a7b22450SDavid du Colombier drawscreen();
286a7b22450SDavid du Colombier break;
287a7b22450SDavid du Colombier case 1:
28801e2ed2dSDavid du Colombier stopanimation(&a);
289a7b22450SDavid du Colombier loadlevels(LEasy);
29001e2ed2dSDavid du Colombier buildmenu();
291a7b22450SDavid du Colombier drawlevel();
292a7b22450SDavid du Colombier drawscreen();
293a7b22450SDavid du Colombier break;
294a7b22450SDavid du Colombier case 2:
29501e2ed2dSDavid du Colombier stopanimation(&a);
296a7b22450SDavid du Colombier loadlevels(LHard);
29701e2ed2dSDavid du Colombier buildmenu();
298a7b22450SDavid du Colombier drawlevel();
299a7b22450SDavid du Colombier drawscreen();
300a7b22450SDavid du Colombier break;
301a7b22450SDavid du Colombier case 3:
30297787252SDavid du Colombier animate = !animate;
30397787252SDavid du Colombier buttons[3] = animate ? "noanimate" : "animate";
30497787252SDavid du Colombier break;
30597787252SDavid du Colombier case 4:
306a7b22450SDavid du Colombier exits(nil);
307a7b22450SDavid du Colombier }
308a7b22450SDavid du Colombier break;
309a7b22450SDavid du Colombier
310a7b22450SDavid du Colombier case Ekeyboard:
311a7b22450SDavid du Colombier if(level.done)
312a7b22450SDavid du Colombier break;
313a7b22450SDavid du Colombier
31401e2ed2dSDavid du Colombier stopanimation(&a);
31501e2ed2dSDavid du Colombier
31601e2ed2dSDavid du Colombier switch(ev.kbdc) {
317a7b22450SDavid du Colombier case 127:
318a7b22450SDavid du Colombier case 'q':
319a7b22450SDavid du Colombier case 'Q':
320a7b22450SDavid du Colombier exits(nil);
321a7b22450SDavid du Colombier case 'n':
322a7b22450SDavid du Colombier case 'N':
323a7b22450SDavid du Colombier if(level.index < numlevels - 1) {
324a7b22450SDavid du Colombier level = levels[++level.index];
325a7b22450SDavid du Colombier drawlevel();
326a7b22450SDavid du Colombier drawscreen();
327a7b22450SDavid du Colombier }
328a7b22450SDavid du Colombier break;
329a7b22450SDavid du Colombier case 'p':
330a7b22450SDavid du Colombier case 'P':
331a7b22450SDavid du Colombier if(level.index > 0) {
332a7b22450SDavid du Colombier level = levels[--level.index];
333a7b22450SDavid du Colombier drawlevel();
334a7b22450SDavid du Colombier drawscreen();
335a7b22450SDavid du Colombier }
336a7b22450SDavid du Colombier break;
337a7b22450SDavid du Colombier case 'r':
338a7b22450SDavid du Colombier case 'R':
339a7b22450SDavid du Colombier level = levels[level.index];
340a7b22450SDavid du Colombier drawlevel();
341a7b22450SDavid du Colombier drawscreen();
342a7b22450SDavid du Colombier break;
343a7b22450SDavid du Colombier case 61454:
344a7b22450SDavid du Colombier case 63488:
345a7b22450SDavid du Colombier case 61457:
346a7b22450SDavid du Colombier case 61458:
347a7b22450SDavid du Colombier case ' ':
34801e2ed2dSDavid du Colombier move(key2move(ev.kbdc));
349a7b22450SDavid du Colombier drawscreen();
350a7b22450SDavid du Colombier break;
35197787252SDavid du Colombier default:
35297787252SDavid du Colombier // fprint(2, "key: %d]\n", e.kbdc);
35397787252SDavid du Colombier break;
354a7b22450SDavid du Colombier }
355a7b22450SDavid du Colombier break;
35601e2ed2dSDavid du Colombier
35701e2ed2dSDavid du Colombier default:
35801e2ed2dSDavid du Colombier if (e == timer) {
35901e2ed2dSDavid du Colombier if (animate)
36001e2ed2dSDavid du Colombier onestep(&a);
36101e2ed2dSDavid du Colombier else
36201e2ed2dSDavid du Colombier while(onestep(&a))
36301e2ed2dSDavid du Colombier ;
36401e2ed2dSDavid du Colombier drawscreen();
36501e2ed2dSDavid du Colombier }
36601e2ed2dSDavid du Colombier break;
367a7b22450SDavid du Colombier }
368a7b22450SDavid du Colombier
369a7b22450SDavid du Colombier if(finished()) {
370a7b22450SDavid du Colombier level.done = 1;
371a7b22450SDavid du Colombier drawwin();
372a7b22450SDavid du Colombier drawscreen();
373a7b22450SDavid du Colombier sleep(3000);
374a7b22450SDavid du Colombier if(level.index < numlevels - 1) {
375a7b22450SDavid du Colombier level = levels[++level.index];
376a7b22450SDavid du Colombier drawlevel();
377a7b22450SDavid du Colombier drawscreen();
378a7b22450SDavid du Colombier }
379a7b22450SDavid du Colombier }
380a7b22450SDavid du Colombier }
381a7b22450SDavid du Colombier }
382