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