diff options
author | Björn Stenberg <bjorn@haxx.se> | 2003-06-29 16:33:04 +0000 |
---|---|---|
committer | Björn Stenberg <bjorn@haxx.se> | 2003-06-29 16:33:04 +0000 |
commit | ba371fb595affd68c823926b85718d1d613dc7d3 (patch) | |
tree | cfda303d0603d623cdb12f3928905d3ae02f1d87 /apps/plugins/sokoban.c | |
parent | 9bcbe3fd723d23a709873a0855f27b86bc5c96f1 (diff) | |
download | rockbox-ba371fb595affd68c823926b85718d1d613dc7d3.tar.gz rockbox-ba371fb595affd68c823926b85718d1d613dc7d3.zip |
Added plugin loader. Moved games, demos and the text viewer to loadable plugins. Copy your *.rock files to /.rockbox/rocks/
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@3769 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/sokoban.c')
-rw-r--r-- | apps/plugins/sokoban.c | 868 |
1 files changed, 868 insertions, 0 deletions
diff --git a/apps/plugins/sokoban.c b/apps/plugins/sokoban.c new file mode 100644 index 0000000000..2387fa9517 --- /dev/null +++ b/apps/plugins/sokoban.c | |||
@@ -0,0 +1,868 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 Eric Linenberg | ||
11 | * February 2003: Robert Hak performs a cleanup/rewrite/feature addition. | ||
12 | * Eric smiles. Bjorn cries. Linus say 'huh?'. | ||
13 | * | ||
14 | * All files in this archive are subject to the GNU General Public License. | ||
15 | * See the file COPYING in the source tree root for full license agreement. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "plugin.h" | ||
22 | |||
23 | #ifdef HAVE_LCD_BITMAP | ||
24 | |||
25 | #define SOKOBAN_TITLE "Sokoban" | ||
26 | #define SOKOBAN_TITLE_FONT 2 | ||
27 | |||
28 | #define LEVELS_FILE "/.rockbox/sokoban/levels.txt" | ||
29 | |||
30 | #define ROWS 16 | ||
31 | #define COLS 20 | ||
32 | #define MAX_UNDOS 5 | ||
33 | |||
34 | #define SOKOBAN_LEVEL_SIZE (ROWS*COLS) | ||
35 | |||
36 | static void init_undo(void); | ||
37 | static void undo(void); | ||
38 | static void add_undo(int button); | ||
39 | |||
40 | static int get_level(char *level, int level_size); | ||
41 | static int get_level_count(void); | ||
42 | static int load_level(void); | ||
43 | static void draw_level(void); | ||
44 | |||
45 | static void init_boards(void); | ||
46 | static void update_screen(void); | ||
47 | static bool sokoban_loop(void); | ||
48 | |||
49 | /* The Location, Undo and LevelInfo structs are OO-flavored. | ||
50 | * (oooh!-flavored as Schnueff puts it.) It makes more you have to know, | ||
51 | * but the overall data layout becomes more manageable. */ | ||
52 | |||
53 | /* We use the same three values in 2 structs. Makeing them a struct | ||
54 | * hopefully ensures that if you change things in one, the other changes | ||
55 | * as well. */ | ||
56 | struct LevelInfo { | ||
57 | short level; | ||
58 | short moves; | ||
59 | short boxes_to_go; | ||
60 | }; | ||
61 | |||
62 | /* What a given location on the board looks like at a given time */ | ||
63 | struct Location { | ||
64 | char spot; | ||
65 | short row; | ||
66 | short col; | ||
67 | }; | ||
68 | |||
69 | /* A single level of undo. Each undo move can affect upto, | ||
70 | * but not more then, 3 spots on the board */ | ||
71 | struct Undo { | ||
72 | struct LevelInfo level; | ||
73 | struct Location location[3]; | ||
74 | }; | ||
75 | |||
76 | /* Our full undo history */ | ||
77 | static struct UndoInfo { | ||
78 | short count; /* How many undos are there in history */ | ||
79 | short current; /* Which history is the current undo */ | ||
80 | struct Undo history[MAX_UNDOS]; | ||
81 | } undo_info; | ||
82 | |||
83 | /* Our playing board */ | ||
84 | static struct BoardInfo { | ||
85 | char board[ROWS][COLS]; | ||
86 | struct LevelInfo level; | ||
87 | struct Location player; | ||
88 | int max_level; /* How many levels do we have? */ | ||
89 | int level_offset; /* Where in the level file is this level */ | ||
90 | int loaded_level; /* Which level is in memory */ | ||
91 | } current_info; | ||
92 | |||
93 | static struct plugin_api* rb; | ||
94 | |||
95 | static void init_undo(void) | ||
96 | { | ||
97 | undo_info.count = 0; | ||
98 | undo_info.current = 0; | ||
99 | } | ||
100 | |||
101 | static void undo(void) | ||
102 | { | ||
103 | struct Undo *undo; | ||
104 | int i = 0; | ||
105 | short row, col; | ||
106 | |||
107 | if (undo_info.count == 0) | ||
108 | return; | ||
109 | |||
110 | /* Update board info */ | ||
111 | undo = &undo_info.history[undo_info.current]; | ||
112 | |||
113 | rb->memcpy(¤t_info.level, &undo->level, sizeof(undo->level)); | ||
114 | rb->memcpy(¤t_info.player, &undo->location[0], sizeof(undo->location[0])); | ||
115 | |||
116 | row = undo->location[0].row; | ||
117 | col = undo->location[0].col; | ||
118 | current_info.board[row][col] = '@'; | ||
119 | |||
120 | /* Update the two other possible spots */ | ||
121 | for (i = 1; i < 3; i++) { | ||
122 | if (undo->location[i].spot != '\0') { | ||
123 | row = undo->location[i].row; | ||
124 | col = undo->location[i].col; | ||
125 | current_info.board[row][col] = undo->location[i].spot; | ||
126 | undo->location[i].spot = '\0'; | ||
127 | } | ||
128 | } | ||
129 | |||
130 | /* Remove this undo from the list */ | ||
131 | if (undo_info.current == 0) { | ||
132 | if (undo_info.count > 1) | ||
133 | undo_info.current = MAX_UNDOS - 1; | ||
134 | } else { | ||
135 | undo_info.current--; | ||
136 | } | ||
137 | |||
138 | undo_info.count--; | ||
139 | |||
140 | return; | ||
141 | } | ||
142 | |||
143 | static void add_undo(int button) | ||
144 | { | ||
145 | struct Undo *undo; | ||
146 | int row, col, i; | ||
147 | bool storable; | ||
148 | |||
149 | if ((button != BUTTON_LEFT) && (button != BUTTON_RIGHT) && | ||
150 | (button != BUTTON_UP) && (button != BUTTON_DOWN)) | ||
151 | return; | ||
152 | |||
153 | if (undo_info.count != 0) { | ||
154 | if (undo_info.current < (MAX_UNDOS - 1)) | ||
155 | undo_info.current++; | ||
156 | else | ||
157 | undo_info.current = 0; | ||
158 | } | ||
159 | |||
160 | /* Make what follows more readable */ | ||
161 | undo = &undo_info.history[undo_info.current]; | ||
162 | |||
163 | /* Store our level info */ | ||
164 | rb->memcpy(&undo->level, ¤t_info.level, sizeof(undo->level)); | ||
165 | |||
166 | /* Store our player info */ | ||
167 | rb->memcpy(&undo->location[0], ¤t_info.player, sizeof(undo->location[0])); | ||
168 | |||
169 | /* Now we need to store upto 2 blocks that may be affected. | ||
170 | * If player.spot is NULL, then there is no info stored | ||
171 | * for that block */ | ||
172 | |||
173 | row = current_info.player.row; | ||
174 | col = current_info.player.col; | ||
175 | |||
176 | /* This must stay as _1_ because the first block (0) is the player */ | ||
177 | for (i = 1; i < 3; i++) { | ||
178 | storable = true; | ||
179 | |||
180 | switch (button) { | ||
181 | case BUTTON_LEFT: | ||
182 | col--; | ||
183 | if (col < 0) | ||
184 | storable = false; | ||
185 | break; | ||
186 | |||
187 | case BUTTON_RIGHT: | ||
188 | col++; | ||
189 | if (col >= COLS) | ||
190 | storable = false; | ||
191 | break; | ||
192 | |||
193 | case BUTTON_UP: | ||
194 | row--; | ||
195 | if (row < 0) | ||
196 | storable = false; | ||
197 | break; | ||
198 | |||
199 | case BUTTON_DOWN: | ||
200 | row++; | ||
201 | if (row >= ROWS) | ||
202 | storable = false; | ||
203 | break; | ||
204 | |||
205 | default: | ||
206 | return; | ||
207 | } | ||
208 | |||
209 | if (storable) { | ||
210 | undo->location[i].col = col; | ||
211 | undo->location[i].row = row; | ||
212 | undo->location[i].spot = current_info.board[row][col]; | ||
213 | } else { | ||
214 | undo->location[i].spot = '\0'; | ||
215 | } | ||
216 | } | ||
217 | |||
218 | if (undo_info.count < MAX_UNDOS) | ||
219 | undo_info.count++; | ||
220 | } | ||
221 | |||
222 | static void init_boards(void) | ||
223 | { | ||
224 | current_info.level.level = 0; | ||
225 | current_info.level.moves = 0; | ||
226 | current_info.level.boxes_to_go = 0; | ||
227 | current_info.player.row = 0; | ||
228 | current_info.player.col = 0; | ||
229 | current_info.player.spot = ' '; | ||
230 | current_info.max_level = 0; | ||
231 | current_info.level_offset = 0; | ||
232 | current_info.loaded_level = 0; | ||
233 | |||
234 | init_undo(); | ||
235 | } | ||
236 | |||
237 | static int get_level_count(void) | ||
238 | { | ||
239 | int fd = 0; | ||
240 | int lastlen = 0; | ||
241 | char buffer[COLS + 3]; /* COLS plus CR/LF and \0 */ | ||
242 | |||
243 | if ((fd = rb->open(LEVELS_FILE, O_RDONLY)) < 0) { | ||
244 | rb->splash(0, 0, true, "Unable to open %s", LEVELS_FILE); | ||
245 | return -1; | ||
246 | } | ||
247 | |||
248 | while(1) { | ||
249 | int len = rb->read_line(fd, buffer, sizeof(buffer)); | ||
250 | if(len <= 0) | ||
251 | break; | ||
252 | |||
253 | /* Two short lines in a row means new level */ | ||
254 | if(len < 3 && lastlen < 3) | ||
255 | current_info.max_level++; | ||
256 | |||
257 | lastlen = len; | ||
258 | } | ||
259 | |||
260 | rb->close(fd); | ||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | static int get_level(char *level, int level_size) | ||
265 | { | ||
266 | int fd = 0, i = 0; | ||
267 | int nread = 0; | ||
268 | int count = 0; | ||
269 | int lastlen = 0; | ||
270 | int level_ct = 1; | ||
271 | unsigned char buffer[SOKOBAN_LEVEL_SIZE * 2]; | ||
272 | bool level_found = false; | ||
273 | |||
274 | /* open file */ | ||
275 | if ((fd = rb->open(LEVELS_FILE, O_RDONLY)) < 0) | ||
276 | return -1; | ||
277 | |||
278 | /* Lets not reparse the full file if we can avoid it */ | ||
279 | if (current_info.loaded_level < current_info.level.level) { | ||
280 | rb->lseek(fd, current_info.level_offset, SEEK_SET); | ||
281 | level_ct = current_info.loaded_level; | ||
282 | } | ||
283 | |||
284 | if(current_info.level.level > 1) { | ||
285 | while(!level_found) { | ||
286 | int len = rb->read_line(fd, buffer, SOKOBAN_LEVEL_SIZE); | ||
287 | if(len <= 0) { | ||
288 | rb->close(fd); | ||
289 | return -1; | ||
290 | } | ||
291 | |||
292 | /* Two short lines in a row means new level */ | ||
293 | if(len < 3 && lastlen < 3) { | ||
294 | level_ct++; | ||
295 | if(level_ct == current_info.level.level) | ||
296 | level_found = true; | ||
297 | } | ||
298 | lastlen = len; | ||
299 | } | ||
300 | } | ||
301 | |||
302 | /* Remember the current offset */ | ||
303 | current_info.level_offset = rb->lseek(fd, 0, SEEK_CUR); | ||
304 | |||
305 | /* read a full buffer chunk from here */ | ||
306 | nread = rb->read(fd, buffer, sizeof(buffer)-1); | ||
307 | if (nread < 0) | ||
308 | return -1; | ||
309 | buffer[nread] = 0; | ||
310 | |||
311 | rb->close(fd); | ||
312 | |||
313 | /* If we read less then a level, error */ | ||
314 | if (nread < level_size) | ||
315 | return -1; | ||
316 | |||
317 | /* Load our new level */ | ||
318 | for(i=0, count=0; (count < nread) && (i<level_size);) { | ||
319 | if (buffer[count] != '\n' && buffer[count] != '\r') | ||
320 | level[i++] = buffer[count]; | ||
321 | count++; | ||
322 | } | ||
323 | level[i] = 0; | ||
324 | |||
325 | current_info.loaded_level = current_info.level.level; | ||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | /* return non-zero on error */ | ||
330 | static int load_level(void) | ||
331 | { | ||
332 | short c = 0; | ||
333 | short r = 0; | ||
334 | short i = 0; | ||
335 | char level[ROWS*COLS+1]; | ||
336 | int x = 0; | ||
337 | |||
338 | current_info.player.spot=' '; | ||
339 | current_info.level.boxes_to_go = 0; | ||
340 | current_info.level.moves = 0; | ||
341 | |||
342 | if (get_level(level, sizeof(level)) != 0) | ||
343 | return -1; | ||
344 | |||
345 | i = 0; | ||
346 | for (r = 0; r < ROWS; r++) { | ||
347 | x++; | ||
348 | for (c = 0; c < COLS; c++, i++) { | ||
349 | current_info.board[r][c] = level[i]; | ||
350 | |||
351 | if (current_info.board[r][c] == '.') | ||
352 | current_info.level.boxes_to_go++; | ||
353 | |||
354 | else if (current_info.board[r][c] == '@') { | ||
355 | current_info.player.row = r; | ||
356 | current_info.player.col = c; | ||
357 | } | ||
358 | } | ||
359 | } | ||
360 | |||
361 | return 0; | ||
362 | } | ||
363 | |||
364 | static void update_screen(void) | ||
365 | { | ||
366 | short b = 0, c = 0; | ||
367 | short rows = 0, cols = 0; | ||
368 | char s[25]; | ||
369 | |||
370 | short magnify = 4; | ||
371 | |||
372 | /* load the board to the screen */ | ||
373 | for (rows=0 ; rows < ROWS ; rows++) { | ||
374 | for (cols = 0 ; cols < COLS ; cols++) { | ||
375 | c = cols * magnify; | ||
376 | b = rows * magnify; | ||
377 | |||
378 | switch(current_info.board[rows][cols]) { | ||
379 | case 'X': /* black space */ | ||
380 | rb->lcd_drawrect(c, b, magnify, magnify); | ||
381 | rb->lcd_drawrect(c+1, b+1, 2, 2); | ||
382 | break; | ||
383 | |||
384 | case '#': /* this is a wall */ | ||
385 | rb->lcd_drawpixel(c, b); | ||
386 | rb->lcd_drawpixel(c+2, b); | ||
387 | rb->lcd_drawpixel(c+1, b+1); | ||
388 | rb->lcd_drawpixel(c+3, b+1); | ||
389 | rb->lcd_drawpixel(c, b+2); | ||
390 | rb->lcd_drawpixel(c+2, b+2); | ||
391 | rb->lcd_drawpixel(c+1, b+3); | ||
392 | rb->lcd_drawpixel(c+3, b+3); | ||
393 | break; | ||
394 | |||
395 | case '.': /* this is a home location */ | ||
396 | rb->lcd_drawrect(c+1, b+1, 2, 2); | ||
397 | break; | ||
398 | |||
399 | case '$': /* this is a box */ | ||
400 | rb->lcd_drawrect(c, b, magnify, magnify); | ||
401 | break; | ||
402 | |||
403 | case '@': /* this is you */ | ||
404 | rb->lcd_drawline(c+1, b, c+2, b); | ||
405 | rb->lcd_drawline(c, b+1, c+3, b+1); | ||
406 | rb->lcd_drawline(c+1, b+2, c+2, b+2); | ||
407 | |||
408 | rb->lcd_drawpixel(c, b+3); | ||
409 | rb->lcd_drawpixel(c+3, b+3); | ||
410 | break; | ||
411 | |||
412 | case '%': /* this is a box on a home spot */ | ||
413 | rb->lcd_drawrect(c, b, magnify, magnify); | ||
414 | rb->lcd_drawrect(c+1, b+1, 2, 2); | ||
415 | break; | ||
416 | } | ||
417 | } | ||
418 | } | ||
419 | |||
420 | |||
421 | rb->snprintf(s, sizeof(s), "%d", current_info.level.level); | ||
422 | rb->lcd_putsxy(86, 22, s); | ||
423 | rb->snprintf(s, sizeof(s), "%d", current_info.level.moves); | ||
424 | rb->lcd_putsxy(86, 54, s); | ||
425 | |||
426 | rb->lcd_drawrect(80,0,32,32); | ||
427 | rb->lcd_drawrect(80,32,32,64); | ||
428 | rb->lcd_putsxy(81, 10, "Level"); | ||
429 | rb->lcd_putsxy(81, 42, "Moves"); | ||
430 | |||
431 | /* print out the screen */ | ||
432 | rb->lcd_update(); | ||
433 | } | ||
434 | |||
435 | static void draw_level(void) | ||
436 | { | ||
437 | load_level(); | ||
438 | rb->lcd_clear_display(); | ||
439 | update_screen(); | ||
440 | } | ||
441 | |||
442 | static bool sokoban_loop(void) | ||
443 | { | ||
444 | char new_spot; | ||
445 | bool moved = true; | ||
446 | int i = 0, button = 0; | ||
447 | short r = 0, c = 0; | ||
448 | |||
449 | current_info.level.level = 1; | ||
450 | |||
451 | load_level(); | ||
452 | update_screen(); | ||
453 | |||
454 | while (1) { | ||
455 | moved = true; | ||
456 | |||
457 | r = current_info.player.row; | ||
458 | c = current_info.player.col; | ||
459 | |||
460 | button = rb->button_get(true); | ||
461 | |||
462 | add_undo(button); | ||
463 | |||
464 | switch(button) | ||
465 | { | ||
466 | case BUTTON_OFF: | ||
467 | /* get out of here */ | ||
468 | return PLUGIN_OK; | ||
469 | |||
470 | case BUTTON_ON: | ||
471 | case BUTTON_ON | BUTTON_REPEAT: | ||
472 | /* this is UNDO */ | ||
473 | undo(); | ||
474 | rb->lcd_clear_display(); | ||
475 | update_screen(); | ||
476 | moved = false; | ||
477 | break; | ||
478 | |||
479 | case BUTTON_F3: | ||
480 | case BUTTON_F3 | BUTTON_REPEAT: | ||
481 | /* increase level */ | ||
482 | init_undo(); | ||
483 | current_info.level.boxes_to_go=0; | ||
484 | moved = true; | ||
485 | break; | ||
486 | |||
487 | case BUTTON_F1: | ||
488 | case BUTTON_F1 | BUTTON_REPEAT: | ||
489 | /* previous level */ | ||
490 | init_undo(); | ||
491 | if (current_info.level.level > 1) | ||
492 | current_info.level.level--; | ||
493 | |||
494 | draw_level(); | ||
495 | moved = false; | ||
496 | break; | ||
497 | |||
498 | case BUTTON_F2: | ||
499 | case BUTTON_F2 | BUTTON_REPEAT: | ||
500 | /* same level */ | ||
501 | init_undo(); | ||
502 | draw_level(); | ||
503 | moved = false; | ||
504 | break; | ||
505 | |||
506 | case BUTTON_LEFT: | ||
507 | switch(current_info.board[r][c-1]) | ||
508 | { | ||
509 | case ' ': /* if it is a blank spot */ | ||
510 | case '.': /* if it is a home spot */ | ||
511 | new_spot = current_info.board[r][c-1]; | ||
512 | current_info.board[r][c-1] = '@'; | ||
513 | current_info.board[r][c] = current_info.player.spot; | ||
514 | current_info.player.spot = new_spot; | ||
515 | break; | ||
516 | |||
517 | case '$': | ||
518 | switch(current_info.board[r][c-2]) | ||
519 | { | ||
520 | case ' ': /* going from blank to blank */ | ||
521 | current_info.board[r][c-2] = current_info.board[r][c-1]; | ||
522 | current_info.board[r][c-1] = current_info.board[r][c]; | ||
523 | current_info.board[r][c] = current_info.player.spot; | ||
524 | current_info.player.spot = ' '; | ||
525 | break; | ||
526 | |||
527 | case '.': /* going from a blank to home */ | ||
528 | current_info.board[r][c-2] = '%'; | ||
529 | current_info.board[r][c-1] = current_info.board[r][c]; | ||
530 | current_info.board[r][c] = current_info.player.spot; | ||
531 | current_info.player.spot = ' '; | ||
532 | current_info.level.boxes_to_go--; | ||
533 | break; | ||
534 | |||
535 | default: | ||
536 | moved = false; | ||
537 | break; | ||
538 | } | ||
539 | break; | ||
540 | |||
541 | case '%': | ||
542 | switch(current_info.board[r][c-2]) { | ||
543 | case ' ': /* we are going from a home to a blank */ | ||
544 | current_info.board[r][c-2] = '$'; | ||
545 | current_info.board[r][c-1] = current_info.board[r][c]; | ||
546 | current_info.board[r][c] = current_info.player.spot; | ||
547 | current_info.player.spot = '.'; | ||
548 | current_info.level.boxes_to_go++; | ||
549 | break; | ||
550 | |||
551 | case '.': /* if we are going from a home to home */ | ||
552 | current_info.board[r][c-2] = '%'; | ||
553 | current_info.board[r][c-1] = current_info.board[r][c]; | ||
554 | current_info.board[r][c] = current_info.player.spot; | ||
555 | current_info.player.spot = '.'; | ||
556 | break; | ||
557 | |||
558 | default: | ||
559 | moved = false; | ||
560 | break; | ||
561 | } | ||
562 | break; | ||
563 | |||
564 | default: | ||
565 | moved = false; | ||
566 | break; | ||
567 | } | ||
568 | |||
569 | if (moved) | ||
570 | current_info.player.col--; | ||
571 | break; | ||
572 | |||
573 | case BUTTON_RIGHT: /* if it is a blank spot */ | ||
574 | switch(current_info.board[r][c+1]) { | ||
575 | case ' ': | ||
576 | case '.': /* if it is a home spot */ | ||
577 | new_spot = current_info.board[r][c+1]; | ||
578 | current_info.board[r][c+1] = '@'; | ||
579 | current_info.board[r][c] = current_info.player.spot; | ||
580 | current_info.player.spot = new_spot; | ||
581 | break; | ||
582 | |||
583 | case '$': | ||
584 | switch(current_info.board[r][c+2]) { | ||
585 | case ' ': /* going from blank to blank */ | ||
586 | current_info.board[r][c+2] = current_info.board[r][c+1]; | ||
587 | current_info.board[r][c+1] = current_info.board[r][c]; | ||
588 | current_info.board[r][c] = current_info.player.spot; | ||
589 | current_info.player.spot = ' '; | ||
590 | break; | ||
591 | |||
592 | case '.': /* going from a blank to home */ | ||
593 | current_info.board[r][c+2] = '%'; | ||
594 | current_info.board[r][c+1] = current_info.board[r][c]; | ||
595 | current_info.board[r][c] = current_info.player.spot; | ||
596 | current_info.player.spot = ' '; | ||
597 | current_info.level.boxes_to_go--; | ||
598 | break; | ||
599 | |||
600 | default: | ||
601 | moved = false; | ||
602 | break; | ||
603 | } | ||
604 | break; | ||
605 | |||
606 | case '%': | ||
607 | switch(current_info.board[r][c+2]) { | ||
608 | case ' ': /* going from a home to a blank */ | ||
609 | current_info.board[r][c+2] = '$'; | ||
610 | current_info.board[r][c+1] = current_info.board[r][c]; | ||
611 | current_info.board[r][c] = current_info.player.spot; | ||
612 | current_info.player.spot = '.'; | ||
613 | current_info.level.boxes_to_go++; | ||
614 | break; | ||
615 | |||
616 | case '.': | ||
617 | current_info.board[r][c+2] = '%'; | ||
618 | current_info.board[r][c+1] = current_info.board[r][c]; | ||
619 | current_info.board[r][c] = current_info.player.spot; | ||
620 | current_info.player.spot = '.'; | ||
621 | break; | ||
622 | |||
623 | default: | ||
624 | moved = false; | ||
625 | break; | ||
626 | } | ||
627 | break; | ||
628 | |||
629 | default: | ||
630 | moved = false; | ||
631 | break; | ||
632 | } | ||
633 | |||
634 | if (moved) | ||
635 | current_info.player.col++; | ||
636 | break; | ||
637 | |||
638 | case BUTTON_UP: | ||
639 | switch(current_info.board[r-1][c]) { | ||
640 | case ' ': /* if it is a blank spot */ | ||
641 | case '.': /* if it is a home spot */ | ||
642 | new_spot = current_info.board[r-1][c]; | ||
643 | current_info.board[r-1][c] = '@'; | ||
644 | current_info.board[r][c] = current_info.player.spot; | ||
645 | current_info.player.spot = new_spot; | ||
646 | break; | ||
647 | |||
648 | case '$': | ||
649 | switch(current_info.board[r-2][c]) { | ||
650 | case ' ': /* going from blank to blank */ | ||
651 | current_info.board[r-2][c] = current_info.board[r-1][c]; | ||
652 | current_info.board[r-1][c] = current_info.board[r][c]; | ||
653 | current_info.board[r][c] = current_info.player.spot; | ||
654 | current_info.player.spot = ' '; | ||
655 | break; | ||
656 | |||
657 | case '.': /* going from a blank to home */ | ||
658 | current_info.board[r-2][c] = '%'; | ||
659 | current_info.board[r-1][c] = current_info.board[r][c]; | ||
660 | current_info.board[r][c] = current_info.player.spot; | ||
661 | current_info.player.spot = ' '; | ||
662 | current_info.level.boxes_to_go--; | ||
663 | break; | ||
664 | |||
665 | default: | ||
666 | moved = false; | ||
667 | break; | ||
668 | } | ||
669 | break; | ||
670 | |||
671 | case '%': | ||
672 | switch(current_info.board[r-2][c]) { | ||
673 | case ' ': /* we are going from a home to a blank */ | ||
674 | current_info.board[r-2][c] = '$'; | ||
675 | current_info.board[r-1][c] = current_info.board[r][c]; | ||
676 | current_info.board[r][c] = current_info.player.spot; | ||
677 | current_info.player.spot = '.'; | ||
678 | current_info.level.boxes_to_go++; | ||
679 | break; | ||
680 | |||
681 | case '.': /* if we are going from a home to home */ | ||
682 | current_info.board[r-2][c] = '%'; | ||
683 | current_info.board[r-1][c] = current_info.board[r][c]; | ||
684 | current_info.board[r][c] = current_info.player.spot; | ||
685 | current_info.player.spot = '.'; | ||
686 | break; | ||
687 | |||
688 | default: | ||
689 | moved = false; | ||
690 | break; | ||
691 | } | ||
692 | break; | ||
693 | |||
694 | default: | ||
695 | moved = false; | ||
696 | break; | ||
697 | } | ||
698 | |||
699 | if (moved) | ||
700 | current_info.player.row--; | ||
701 | break; | ||
702 | |||
703 | case BUTTON_DOWN: | ||
704 | switch(current_info.board[r+1][c]) { | ||
705 | case ' ': /* if it is a blank spot */ | ||
706 | case '.': /* if it is a home spot */ | ||
707 | new_spot = current_info.board[r+1][c]; | ||
708 | current_info.board[r+1][c] = '@'; | ||
709 | current_info.board[r][c] = current_info.player.spot; | ||
710 | current_info.player.spot = new_spot; | ||
711 | break; | ||
712 | |||
713 | case '$': | ||
714 | switch(current_info.board[r+2][c]) { | ||
715 | case ' ': /* going from blank to blank */ | ||
716 | current_info.board[r+2][c] = current_info.board[r+1][c]; | ||
717 | current_info.board[r+1][c] = current_info.board[r][c]; | ||
718 | current_info.board[r][c] = current_info.player.spot; | ||
719 | current_info.player.spot = ' '; | ||
720 | break; | ||
721 | |||
722 | case '.': /* going from a blank to home */ | ||
723 | current_info.board[r+2][c] = '%'; | ||
724 | current_info.board[r+1][c] = current_info.board[r][c]; | ||
725 | current_info.board[r][c] = current_info.player.spot; | ||
726 | current_info.player.spot = ' '; | ||
727 | current_info.level.boxes_to_go--; | ||
728 | break; | ||
729 | |||
730 | default: | ||
731 | moved = false; | ||
732 | break; | ||
733 | } | ||
734 | break; | ||
735 | |||
736 | case '%': | ||
737 | switch(current_info.board[r+2][c]) { | ||
738 | case ' ': /* going from a home to a blank */ | ||
739 | current_info.board[r+2][c] = '$'; | ||
740 | current_info.board[r+1][c] = current_info.board[r][c]; | ||
741 | current_info.board[r][c] = current_info.player.spot; | ||
742 | current_info.player.spot = '.'; | ||
743 | current_info.level.boxes_to_go++; | ||
744 | break; | ||
745 | |||
746 | case '.': /* going from a home to home */ | ||
747 | current_info.board[r+2][c] = '%'; | ||
748 | current_info.board[r+1][c] = current_info.board[r][c]; | ||
749 | current_info.board[r][c] = current_info.player.spot; | ||
750 | current_info.player.spot = '.'; | ||
751 | break; | ||
752 | |||
753 | default: | ||
754 | moved = false; | ||
755 | break; | ||
756 | } | ||
757 | break; | ||
758 | |||
759 | default: | ||
760 | moved = false; | ||
761 | break; | ||
762 | } | ||
763 | |||
764 | if (moved) | ||
765 | current_info.player.row++; | ||
766 | break; | ||
767 | |||
768 | case SYS_USB_CONNECTED: | ||
769 | rb->usb_screen(); | ||
770 | return PLUGIN_USB_CONNECTED; | ||
771 | |||
772 | default: | ||
773 | moved = false; | ||
774 | break; | ||
775 | } | ||
776 | |||
777 | if (moved) { | ||
778 | current_info.level.moves++; | ||
779 | rb->lcd_clear_display(); | ||
780 | update_screen(); | ||
781 | } | ||
782 | |||
783 | /* We have completed this level */ | ||
784 | if (current_info.level.boxes_to_go == 0) { | ||
785 | current_info.level.level++; | ||
786 | |||
787 | /* clear undo stats */ | ||
788 | init_undo(); | ||
789 | |||
790 | rb->lcd_clear_display(); | ||
791 | |||
792 | if (current_info.level.level > current_info.max_level) { | ||
793 | rb->lcd_putsxy(10, 20, "You WIN!!"); | ||
794 | |||
795 | for (i = 0; i < 30000 ; i++) { | ||
796 | rb->lcd_invertrect(0, 0, 111, 63); | ||
797 | rb->lcd_update(); | ||
798 | |||
799 | button = rb->button_get(false); | ||
800 | if (button && ((button & BUTTON_REL) != BUTTON_REL)) | ||
801 | break; | ||
802 | } | ||
803 | |||
804 | return PLUGIN_OK; | ||
805 | } | ||
806 | |||
807 | load_level(); | ||
808 | update_screen(); | ||
809 | } | ||
810 | |||
811 | } /* end while */ | ||
812 | |||
813 | return PLUGIN_OK; | ||
814 | } | ||
815 | |||
816 | |||
817 | enum plugin_status plugin_start(struct plugin_api* api, void* parameter) | ||
818 | { | ||
819 | int w, h; | ||
820 | int len; | ||
821 | |||
822 | TEST_PLUGIN_API(api); | ||
823 | (void)(parameter); | ||
824 | rb = api; | ||
825 | |||
826 | rb->lcd_setfont(FONT_SYSFIXED); | ||
827 | rb->lcd_getstringsize(SOKOBAN_TITLE, &w, &h); | ||
828 | |||
829 | /* Get horizontel centering for text */ | ||
830 | len = w; | ||
831 | if (len%2 != 0) | ||
832 | len =((len+1)/2)+(w/2); | ||
833 | else | ||
834 | len /= 2; | ||
835 | |||
836 | if (h%2 != 0) | ||
837 | h = (h/2)+1; | ||
838 | else | ||
839 | h /= 2; | ||
840 | |||
841 | rb->lcd_clear_display(); | ||
842 | rb->lcd_putsxy(LCD_WIDTH/2-len,(LCD_HEIGHT/2)-h, SOKOBAN_TITLE); | ||
843 | rb->lcd_update(); | ||
844 | rb->sleep(HZ*2); | ||
845 | |||
846 | rb->lcd_clear_display(); | ||
847 | |||
848 | rb->lcd_putsxy(3, 6, "[OFF] To Stop"); | ||
849 | rb->lcd_putsxy(3, 16, "[ON] To Undo"); | ||
850 | rb->lcd_putsxy(3, 26, "[F1] - Level"); | ||
851 | rb->lcd_putsxy(3, 36, "[F2] Same Level"); | ||
852 | rb->lcd_putsxy(3, 46, "[F3] + Level"); | ||
853 | |||
854 | rb->lcd_update(); | ||
855 | rb->sleep(HZ*2); | ||
856 | rb->lcd_clear_display(); | ||
857 | |||
858 | init_boards(); | ||
859 | |||
860 | if (get_level_count() != 0) { | ||
861 | rb->splash(HZ*2,0,true,"Failed loading levels!"); | ||
862 | return PLUGIN_OK; | ||
863 | } | ||
864 | |||
865 | return sokoban_loop(); | ||
866 | } | ||
867 | |||
868 | #endif | ||