1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <event.h>
5
6 #include "sokoban.h"
7
8 #define SOKOTREE "/sys/games/lib/sokoban/"
9
10 char *LEasy = SOKOTREE "levels/easy.slc";
11 char *LHard = SOKOTREE "levels/hard.slc";
12 char *levelfile;
13
14 #define SOKOIMG SOKOTREE "images/"
15
16 char *GRImage = SOKOIMG "right.bit";
17 char *GLImage = SOKOIMG "left.bit";
18 char *WallImage = SOKOIMG "wall.bit";
19 char *EmptyImage = SOKOIMG "empty.bit";
20 char *CargoImage = SOKOIMG "cargo.bit";
21 char *GoalCargoImage= SOKOIMG "goalcargo.bit";
22 char *GoalImage = SOKOIMG "goal.bit";
23 char *WinImage = SOKOIMG "win.bit";
24
25 char *buttons[] =
26 {
27 "restart",
28 "easy",
29 "hard",
30 "noanimate", /* this menu string initialized in main */
31 "exit",
32 0
33 };
34
35 char **levelnames;
36
37 Menu menu =
38 {
39 buttons,
40 };
41
42 Menu lmenu;
43
44 void
buildmenu(void)45 buildmenu(void)
46 {
47 int i;
48
49 if (levelnames != nil) {
50 for(i=0; levelnames[i] != 0; i++)
51 free(levelnames[i]);
52 }
53 levelnames = realloc(levelnames, sizeof(char*)*(numlevels+1));
54 if (levelnames == nil)
55 sysfatal("cannot allocate levelnames");
56 for(i=0; i < numlevels; i++)
57 levelnames[i] = genlevels(i);
58 levelnames[numlevels] = 0;
59 lmenu.item = levelnames;
60 }
61
62 Image *
eallocimage(Rectangle r,int repl,uint color)63 eallocimage(Rectangle r, int repl, uint color)
64 {
65 Image *tmp;
66
67 tmp = allocimage(display, r, screen->chan, repl, color);
68 if(tmp == nil)
69 sysfatal("cannot allocate buffer image: %r");
70
71 return tmp;
72 }
73
74 Image *
eloadfile(char * path)75 eloadfile(char *path)
76 {
77 Image *img;
78 int fd;
79
80 fd = open(path, OREAD);
81 if(fd < 0) {
82 fprint(2, "cannot open image file %s: %r\n", path);
83 exits("image");
84 }
85 img = readimage(display, fd, 0);
86 if(img == nil)
87 sysfatal("cannot load image: %r");
88 close(fd);
89
90 return img;
91 }
92
93
94 void
allocimages(void)95 allocimages(void)
96 {
97 Rectangle one = Rect(0, 0, 1, 1);
98
99 bg = eallocimage(one, 1, DDarkyellow);
100 text = eallocimage(one, 1, DBluegreen);
101
102 gright = eloadfile(GRImage);
103 gleft = eloadfile(GLImage);
104 wall = eloadfile(WallImage);
105 empty = eloadfile(EmptyImage);
106 empty->repl = 1;
107 goalcargo = eloadfile(GoalCargoImage);
108 cargo = eloadfile(CargoImage);
109 goal = eloadfile(GoalImage);
110 win = eloadfile(WinImage);
111 }
112
113 int
key2move(int key)114 key2move(int key)
115 {
116 int k = 0;
117
118 switch(key) {
119 case 61454:
120 k = Up;
121 break;
122 case 63488:
123 k = Down;
124 break;
125 case 61457:
126 k = Left;
127 break;
128 case 61458:
129 k = Right;
130 break;
131 }
132
133 return k;
134 }
135
136 static Route*
mouse2route(Mouse m)137 mouse2route(Mouse m)
138 {
139 Point p, q;
140 Route *r;
141
142 p = subpt(m.xy, screen->r.min);
143 p.x /= BoardX;
144 p.y /= BoardY;
145
146 q = subpt(p, level.glenda);
147 // fprint(2, "x=%d y=%d\n", q.x, q.y);
148
149 if (q.x == 0 && q.y == 0)
150 return nil;
151
152 if (q.x == 0 || q.y == 0) {
153 if (q.x < 0)
154 r = extend(nil, Left, -q.x, Pt(level.glenda.x, p.y));
155 else if (q.x > 0)
156 r = extend(nil, Right, q.x, Pt(level.glenda.x, p.y));
157 else if (q.y < 0)
158 r = extend(nil, Up, -q.y, level.glenda);
159 else if (q.y > 0)
160 r = extend(nil, Down, q.y, level.glenda);
161 else
162 r = nil;
163
164 if (r != nil && isvalid(level.glenda, r, validpush))
165 return r;
166 freeroute(r);
167 }
168
169 return findroute(level.glenda, p);
170 }
171
172 char *
genlevels(int i)173 genlevels(int i)
174 {
175
176 if(i >= numlevels)
177 return 0;
178
179 return smprint("level %d", i+1);
180 }
181
182
183 int
finished(void)184 finished(void)
185 {
186 int x, y;
187 for(x = 0; x < MazeX; x++)
188 for(y = 0; y < MazeY; y++)
189 if(level.board[x][y] == Goal)
190 return 0;
191
192 return 1;
193 }
194
195 void
eresized(int new)196 eresized(int new)
197 {
198 Point p;
199
200 if(new && getwindow(display, Refnone) < 0)
201 sysfatal("can't reattach to window");
202
203 p = Pt(Dx(screen->r), Dy(screen->r));
204
205 if(!new || !eqpt(p, boardsize(level.max))) {
206 drawlevel();
207 }
208 drawscreen();
209 }
210
211 void
main(int argc,char ** argv)212 main(int argc, char **argv)
213 {
214 Mouse m;
215 Event ev;
216 int e;
217 Route *r;
218 int timer;
219 Animation a;
220 int animate;
221
222
223 if(argc == 2)
224 levelfile = argv[1];
225 else
226 levelfile = LEasy;
227
228 if(! loadlevels(levelfile)) {
229 fprint(2, "usage: %s [levelfile]\n", argv[0]);
230 exits("usage");
231 }
232 buildmenu();
233
234 animate = 0;
235 buttons[3] = animate ? "noanimate" : "animate";
236
237 if(initdraw(nil, nil, "sokoban") < 0)
238 sysfatal("initdraw failed: %r");
239 einit(Emouse|Ekeyboard);
240
241 timer = etimer(0, 200);
242 initanimation(&a);
243
244 allocimages();
245 glenda = gright;
246 eresized(0);
247
248 for(;;) {
249 e = event(&ev);
250 switch(e) {
251 case Emouse:
252 m = ev.mouse;
253 if(m.buttons&1) {
254 stopanimation(&a);
255 r = mouse2route(m);
256 if (r)
257 setupanimation(&a, r);
258 if (! animate) {
259 while(onestep(&a))
260 ;
261 drawscreen();
262 }
263 }
264 if(m.buttons&2) {
265 int l;
266 /* levels start from 1 */
267 lmenu.lasthit = level.index;
268 l=emenuhit(2, &m, &lmenu);
269 if(l>=0){
270 stopanimation(&a);
271 level = levels[l];
272 drawlevel();
273 drawscreen();
274 }
275 }
276 if(m.buttons&4)
277 switch(emenuhit(3, &m, &menu)) {
278 case 0:
279 stopanimation(&a);
280 level = levels[level.index];
281 drawlevel();
282 drawscreen();
283 break;
284 case 1:
285 stopanimation(&a);
286 loadlevels(LEasy);
287 buildmenu();
288 drawlevel();
289 drawscreen();
290 break;
291 case 2:
292 stopanimation(&a);
293 loadlevels(LHard);
294 buildmenu();
295 drawlevel();
296 drawscreen();
297 break;
298 case 3:
299 animate = !animate;
300 buttons[3] = animate ? "noanimate" : "animate";
301 break;
302 case 4:
303 exits(nil);
304 }
305 break;
306
307 case Ekeyboard:
308 if(level.done)
309 break;
310
311 stopanimation(&a);
312
313 switch(ev.kbdc) {
314 case 127:
315 case 'q':
316 case 'Q':
317 exits(nil);
318 case 'n':
319 case 'N':
320 if(level.index < numlevels - 1) {
321 level = levels[++level.index];
322 drawlevel();
323 drawscreen();
324 }
325 break;
326 case 'p':
327 case 'P':
328 if(level.index > 0) {
329 level = levels[--level.index];
330 drawlevel();
331 drawscreen();
332 }
333 break;
334 case 'r':
335 case 'R':
336 level = levels[level.index];
337 drawlevel();
338 drawscreen();
339 break;
340 case 61454:
341 case 63488:
342 case 61457:
343 case 61458:
344 case ' ':
345 move(key2move(ev.kbdc));
346 drawscreen();
347 break;
348 default:
349 // fprint(2, "key: %d]\n", e.kbdc);
350 break;
351 }
352 break;
353
354 default:
355 if (e == timer) {
356 if (animate)
357 onestep(&a);
358 else
359 while(onestep(&a))
360 ;
361 drawscreen();
362 }
363 break;
364 }
365
366 if(finished()) {
367 level.done = 1;
368 drawwin();
369 drawscreen();
370 sleep(3000);
371 if(level.index < numlevels - 1) {
372 level = levels[++level.index];
373 drawlevel();
374 drawscreen();
375 }
376 }
377 }
378 }
379