summaryrefslogtreecommitdiff
path: root/apps/plugins/sokoban.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/sokoban.c')
-rw-r--r--apps/plugins/sokoban.c264
1 files changed, 138 insertions, 126 deletions
diff --git a/apps/plugins/sokoban.c b/apps/plugins/sokoban.c
index e823518ddb..32c16c58cd 100644
--- a/apps/plugins/sokoban.c
+++ b/apps/plugins/sokoban.c
@@ -31,10 +31,10 @@ PLUGIN_HEADER
31 31
32#define ROWS 16 32#define ROWS 16
33#define COLS 20 33#define COLS 20
34/* Use all but 8k of the plugin buffer for board data */
35#define MAX_BUFFERED_BOARDS (PLUGIN_BUFFER_SIZE - 0x2000)/(16*20)
34#define MAX_UNDOS 5 36#define MAX_UNDOS 5
35 37
36#define SOKOBAN_LEVEL_SIZE (ROWS*COLS)
37
38/* variable button definitions */ 38/* variable button definitions */
39#if CONFIG_KEYPAD == RECORDER_PAD 39#if CONFIG_KEYPAD == RECORDER_PAD
40#define SOKOBAN_UP BUTTON_UP 40#define SOKOBAN_UP BUTTON_UP
@@ -87,21 +87,24 @@ PLUGIN_HEADER
87 87
88#endif 88#endif
89 89
90#if LCD_DEPTH > 1
91#ifdef HAVE_LCD_COLOR 90#ifdef HAVE_LCD_COLOR
92#define MEDIUM_GRAY LCD_RGBPACK(127, 127, 127) 91#define WALL_COLOR LCD_RGBPACK(16,20,180) /* Color of the walls */
93#else 92#define FREE_TARGET_COLOR LCD_RGBPACK(251,158,25) /* Color of a 'target' without a block on top */
93#define USED_TARGET_COLOR LCD_RGBPACK(255,255,255) /* Color of a 'target' with a block on top */
94#define FREE_BLOCK_COLOR LCD_RGBPACK(22,130,53) /* Color of a block when it's not on a 'target' */
95#define USED_BLOCK_COLOR LCD_RGBPACK(22,130,53) /* Color of a block when it is on a 'target' */
96#define CHAR_COLOR LCD_BLACK /* Color of the 'character' */
97
98#elif LCD_DEPTH > 1
94#define MEDIUM_GRAY LCD_BRIGHTNESS(127) 99#define MEDIUM_GRAY LCD_BRIGHTNESS(127)
95#endif 100#endif
96#endif
97 101
98static void init_undo(void); 102static void init_undo(void);
99static void undo(void); 103static void undo(void);
100static void add_undo(int button); 104static void add_undo(int button);
101 105
102static int get_level(char *level, int level_size); 106static int read_levels(int initialize_count);
103static int get_level_count(void); 107static void load_level(void);
104static int load_level(void);
105static void draw_level(void); 108static void draw_level(void);
106 109
107static void init_boards(void); 110static void init_boards(void);
@@ -135,6 +138,10 @@ struct Undo {
135 struct Location location[3]; 138 struct Location location[3];
136}; 139};
137 140
141struct Board {
142 char spaces[ROWS][COLS];
143};
144
138/* Our full undo history */ 145/* Our full undo history */
139static struct UndoInfo { 146static struct UndoInfo {
140 short count; /* How many undos are there in history */ 147 short count; /* How many undos are there in history */
@@ -148,10 +155,14 @@ static struct BoardInfo {
148 struct LevelInfo level; 155 struct LevelInfo level;
149 struct Location player; 156 struct Location player;
150 int max_level; /* How many levels do we have? */ 157 int max_level; /* How many levels do we have? */
151 int level_offset; /* Where in the level file is this level */
152 int loaded_level; /* Which level is in memory */ 158 int loaded_level; /* Which level is in memory */
153} current_info; 159} current_info;
154 160
161static struct BufferedBoards {
162 struct Board levels[MAX_BUFFERED_BOARDS];
163 int low;
164} buffered_boards;
165
155static struct plugin_api* rb; 166static struct plugin_api* rb;
156 167
157static void init_undo(void) 168static void init_undo(void)
@@ -290,125 +301,97 @@ static void init_boards(void)
290 current_info.player.col = 0; 301 current_info.player.col = 0;
291 current_info.player.spot = ' '; 302 current_info.player.spot = ' ';
292 current_info.max_level = 0; 303 current_info.max_level = 0;
293 current_info.level_offset = 0;
294 current_info.loaded_level = 0; 304 current_info.loaded_level = 0;
305
306 buffered_boards.low = 0;
295 307
296 init_undo(); 308 init_undo();
297} 309}
298 310
299static int get_level_count(void) 311static int read_levels(int initialize_count)
300{ 312{
301 int fd = 0; 313 int fd = 0;
314 int len;
302 int lastlen = 0; 315 int lastlen = 0;
316 int row = 0;
317 int level_count = 0;
303 char buffer[COLS + 3]; /* COLS plus CR/LF and \0 */ 318 char buffer[COLS + 3]; /* COLS plus CR/LF and \0 */
319 int endpoint = current_info.level.level-1;
304 320
321 if (endpoint < buffered_boards.low)
322 endpoint = current_info.level.level - MAX_BUFFERED_BOARDS;
323
324 if (endpoint < 0) endpoint = 0;
325
326 buffered_boards.low = endpoint;
327 endpoint += MAX_BUFFERED_BOARDS;
328
305 if ((fd = rb->open(LEVELS_FILE, O_RDONLY)) < 0) { 329 if ((fd = rb->open(LEVELS_FILE, O_RDONLY)) < 0) {
306 rb->splash(0, true, "Unable to open %s", LEVELS_FILE); 330 rb->splash(0, true, "Unable to open %s", LEVELS_FILE);
307 return -1; 331 return -1;
308 } 332 }
309 333
310 while(1) { 334 do {
311 int len = rb->read_line(fd, buffer, sizeof(buffer)); 335 len = rb->read_line(fd, buffer, sizeof(buffer));
312 if(len <= 0) 336 if (len >= 3) {
313 break; 337 /* This finds lines that are more than 1 or 2 characters
314 338 * shorter than they should be. Due to the possibility of
315 /* Two short lines in a row means new level */ 339 * a mixed unix and dos CR/LF file format, I'm not going to
316 if(len < 3 && lastlen < 3) 340 * do a precise check */
317 current_info.max_level++; 341 if (len < COLS) {
318 342 rb->splash(0, true, "Error in levels file: short line");
319 lastlen = len;
320 }
321
322 rb->close(fd);
323 return 0;
324}
325
326static int get_level(char *level, int level_size)
327{
328 int fd = 0, i = 0;
329 int nread = 0;
330 int count = 0;
331 int lastlen = 0;
332 int level_ct = 1;
333 unsigned char buffer[SOKOBAN_LEVEL_SIZE * 2];
334 bool level_found = false;
335
336 /* open file */
337 if ((fd = rb->open(LEVELS_FILE, O_RDONLY)) < 0)
338 return -1;
339
340 /* Lets not reparse the full file if we can avoid it */
341 if (current_info.loaded_level < current_info.level.level) {
342 rb->lseek(fd, current_info.level_offset, SEEK_SET);
343 level_ct = current_info.loaded_level;
344 }
345
346 if(current_info.level.level > 1) {
347 while(!level_found) {
348 int len = rb->read_line(fd, buffer, SOKOBAN_LEVEL_SIZE);
349 if(len <= 0) {
350 rb->close(fd);
351 return -1; 343 return -1;
352 } 344 }
353 345 if (level_count >= buffered_boards.low && level_count < endpoint) {
354 /* Two short lines in a row means new level */ 346 int index = level_count - buffered_boards.low;
355 if(len < 3 && lastlen < 3) { 347 rb->memcpy(
356 level_ct++; 348 buffered_boards.levels[index].spaces[row],buffer,COLS);
357 if(level_ct == current_info.level.level) 349 }
358 level_found = true; 350 row++;
351 } else if (len) {
352 if (lastlen < 3) {
353 /* Two short lines in a row means new level */
354 level_count++;
355 if (level_count >= endpoint && !initialize_count) break;
356 if (level_count && row != ROWS) {
357 rb->splash(0, true, "Error in levels file: short board");
358 return -1;
359 }
360 row = 0;
359 } 361 }
360 lastlen = len;
361 } 362 }
362 } 363 } while ((lastlen=len));
363 364
364 /* Remember the current offset */
365 current_info.level_offset = rb->lseek(fd, 0, SEEK_CUR);
366
367 /* read a full buffer chunk from here */
368 nread = rb->read(fd, buffer, sizeof(buffer)-1);
369 if (nread < 0)
370 return -1;
371 buffer[nread] = 0;
372
373 rb->close(fd); 365 rb->close(fd);
374 366 if (initialize_count) {
375 /* If we read less then a level, error */ 367 /* Plus one because there aren't trailing short lines in the file */
376 if (nread < level_size) 368 current_info.max_level = level_count + 1;
377 return -1;
378
379 /* Load our new level */
380 for(i=0, count=0; (count < nread) && (i<level_size);) {
381 if (buffer[count] != '\n' && buffer[count] != '\r')
382 level[i++] = buffer[count];
383 count++;
384 } 369 }
385 level[i] = 0;
386
387 current_info.loaded_level = current_info.level.level;
388 return 0; 370 return 0;
389} 371}
390 372
391/* return non-zero on error */ 373/* return non-zero on error */
392static int load_level(void) 374static void load_level(void)
393{ 375{
394 short c = 0; 376 int c = 0;
395 short r = 0; 377 int r = 0;
396 short i = 0; 378 int index = current_info.level.level - buffered_boards.low - 1;
397 char level[ROWS*COLS+1]; 379 struct Board *level;
398 int x = 0; 380
381 if (index < 0 || index >= MAX_BUFFERED_BOARDS) {
382 read_levels(false);
383 index=index<0?MAX_BUFFERED_BOARDS-1:0;
384 }
385 level = &buffered_boards.levels[index];
399 386
400 current_info.player.spot=' '; 387 current_info.player.spot=' ';
401 current_info.level.boxes_to_go = 0; 388 current_info.level.boxes_to_go = 0;
402 current_info.level.moves = 0; 389 current_info.level.moves = 0;
390 current_info.loaded_level = current_info.level.level;
403 391
404 if (get_level(level, sizeof(level)) != 0)
405 return -1;
406
407 i = 0;
408 for (r = 0; r < ROWS; r++) { 392 for (r = 0; r < ROWS; r++) {
409 x++; 393 for (c = 0; c < COLS; c++) {
410 for (c = 0; c < COLS; c++, i++) { 394 current_info.board[r][c] = level->spaces[r][c];
411 current_info.board[r][c] = level[i];
412 395
413 if (current_info.board[r][c] == '.') 396 if (current_info.board[r][c] == '.')
414 current_info.level.boxes_to_go++; 397 current_info.level.boxes_to_go++;
@@ -419,8 +402,6 @@ static int load_level(void)
419 } 402 }
420 } 403 }
421 } 404 }
422
423 return 0;
424} 405}
425#define STAT_WIDTH (LCD_WIDTH-(COLS * magnify)) 406#define STAT_WIDTH (LCD_WIDTH-(COLS * magnify))
426 407
@@ -429,9 +410,11 @@ static void update_screen(void)
429 int b = 0, c = 0; 410 int b = 0, c = 0;
430 int rows = 0, cols = 0; 411 int rows = 0, cols = 0;
431 char s[25]; 412 char s[25];
432 413
433#if LCD_HEIGHT >= 128 414#if LCD_HEIGHT == 128 /* magnify is the number of pixels for each block */
434 int magnify = 6; 415 int magnify = 6; /* 6 on h1x0, 9 on h3x0, and 4 on everything else */
416#elif LCD_HEIGHT >= 176
417 int magnify = 9;
435#else 418#else
436 int magnify = 4; 419 int magnify = 4;
437#endif 420#endif
@@ -447,7 +430,11 @@ static void update_screen(void)
447 break; 430 break;
448 431
449 case '#': /* this is a wall */ 432 case '#': /* this is a wall */
450#if LCD_DEPTH > 1 433#if HAVE_LCD_COLOR
434 rb->lcd_set_foreground(WALL_COLOR);
435 rb->lcd_fillrect(c, b, magnify, magnify);
436 rb->lcd_set_foreground(LCD_BLACK);
437#elif LCD_DEPTH > 1
451 rb->lcd_set_foreground(MEDIUM_GRAY); 438 rb->lcd_set_foreground(MEDIUM_GRAY);
452 rb->lcd_fillrect(c, b, magnify, magnify); 439 rb->lcd_fillrect(c, b, magnify, magnify);
453 rb->lcd_set_foreground(LCD_BLACK); 440 rb->lcd_set_foreground(LCD_BLACK);
@@ -463,12 +450,21 @@ static void update_screen(void)
463 break; 450 break;
464 451
465 case '.': /* this is a home location */ 452 case '.': /* this is a home location */
466 rb->lcd_drawrect(c+(magnify/2)-1, b+(magnify/2)-1, magnify/2, 453#ifdef HAVE_LCD_COLOR
454 rb->lcd_set_foreground(FREE_TARGET_COLOR);
455 rb->lcd_fillrect(c+(magnify/2)-1, b+(magnify/2)-1, magnify/2,
456 magnify/2);
457#else
458 rb->lcd_drawrect(c+(magnify/2)-1, b+(magnify/2)-1, magnify/2,
467 magnify/2); 459 magnify/2);
460#endif
468 break; 461 break;
469 462
470 case '$': /* this is a box */ 463 case '$': /* this is a box */
471 rb->lcd_drawrect(c, b, magnify, magnify); 464#ifdef HAVE_LCD_COLOR
465 rb->lcd_set_foreground(FREE_BLOCK_COLOR);
466#endif
467 rb->lcd_drawrect(c, b, magnify, magnify); /* Free boxes are not filled in */
472 break; 468 break;
473 469
474 case '@': /* this is you */ 470 case '@': /* this is you */
@@ -476,7 +472,9 @@ static void update_screen(void)
476 int max = magnify - 1; 472 int max = magnify - 1;
477 int middle = max / 2; 473 int middle = max / 2;
478 int ldelta = (middle + 1) / 2; 474 int ldelta = (middle + 1) / 2;
479 475#ifdef HAVE_LCD_COLOR
476 rb->lcd_set_foreground(CHAR_COLOR);
477#endif
480 rb->lcd_drawline(c, b+middle, c+max, b+middle); 478 rb->lcd_drawline(c, b+middle, c+max, b+middle);
481 rb->lcd_drawline(c+middle, b, c+middle, b+max-ldelta); 479 rb->lcd_drawline(c+middle, b, c+middle, b+max-ldelta);
482 rb->lcd_drawline(c+max-middle, b, 480 rb->lcd_drawline(c+max-middle, b,
@@ -489,14 +487,25 @@ static void update_screen(void)
489 break; 487 break;
490 488
491 case '%': /* this is a box on a home spot */ 489 case '%': /* this is a box on a home spot */
490
491#ifdef HAVE_LCD_COLOR
492 rb->lcd_set_foreground(USED_BLOCK_COLOR);
493 rb->lcd_fillrect(c, b, magnify, magnify);
494#else
492 rb->lcd_drawrect(c, b, magnify, magnify); 495 rb->lcd_drawrect(c, b, magnify, magnify);
496#endif
497#ifdef HAVE_LCD_COLOR
498 rb->lcd_set_foreground(USED_TARGET_COLOR);
499 rb->lcd_fillrect(c+(magnify/2)-1, b+(magnify/2)-1, magnify/2,
500 magnify/2);
501#else
493 rb->lcd_drawrect(c+(magnify/2)-1, b+(magnify/2)-1, magnify/2, 502 rb->lcd_drawrect(c+(magnify/2)-1, b+(magnify/2)-1, magnify/2,
494 magnify/2); 503 magnify/2);
504#endif
495 break; 505 break;
496 } 506 }
497 } 507 }
498 } 508 }
499
500 509
501 rb->snprintf(s, sizeof(s), "%d", current_info.level.level); 510 rb->snprintf(s, sizeof(s), "%d", current_info.level.level);
502 rb->lcd_putsxy(LCD_WIDTH-STAT_WIDTH+4, 22, s); 511 rb->lcd_putsxy(LCD_WIDTH-STAT_WIDTH+4, 22, s);
@@ -877,11 +886,14 @@ static bool sokoban_loop(void)
877 rb->lcd_clear_display(); 886 rb->lcd_clear_display();
878 887
879 if (current_info.level.level > current_info.max_level) { 888 if (current_info.level.level > current_info.max_level) {
880 rb->lcd_putsxy(10, 20, "You WIN!!"); 889 /* Center "You WIN!!" on all screen sizes */
890 rb->lcd_putsxy(LCD_WIDTH/2 - 27,(LCD_HEIGHT/2) - 4 ,
891 "You WIN!!");
881 892
882 rb->lcd_set_drawmode(DRMODE_COMPLEMENT); 893 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
894 /* Pattern Fills whole screen now on all screen sizes */
883 for (i = 0; i < 30000 ; i++) { 895 for (i = 0; i < 30000 ; i++) {
884 rb->lcd_fillrect(0, 0, 111, 63); 896 rb->lcd_fillrect(0, 0, LCD_WIDTH, LCD_HEIGHT);
885 rb->lcd_update(); 897 rb->lcd_update();
886 898
887 button = rb->button_get(false); 899 button = rb->button_get(false);
@@ -934,24 +946,24 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
934 rb->lcd_clear_display(); 946 rb->lcd_clear_display();
935 947
936#if CONFIG_KEYPAD == RECORDER_PAD 948#if CONFIG_KEYPAD == RECORDER_PAD
937 rb->lcd_putsxy(3, 6, "[OFF] To Stop"); 949 rb->lcd_putsxy(3, 6, "[OFF] Quit");
938 rb->lcd_putsxy(3, 16, "[ON] To Undo"); 950 rb->lcd_putsxy(3, 16, "[ON] Undo");
939 rb->lcd_putsxy(3, 26, "[F1] - Level"); 951 rb->lcd_putsxy(3, 26, "[F1] Down a Level");
940 rb->lcd_putsxy(3, 36, "[F2] Same Level"); 952 rb->lcd_putsxy(3, 36, "[F2] Restart Level");
941 rb->lcd_putsxy(3, 46, "[F3] + Level"); 953 rb->lcd_putsxy(3, 46, "[F3] Up a Level");
942#elif CONFIG_KEYPAD == ONDIO_PAD 954#elif CONFIG_KEYPAD == ONDIO_PAD
943 rb->lcd_putsxy(3, 6, "[OFF] To Stop"); 955 rb->lcd_putsxy(3, 6, "[OFF] Quit");
944 rb->lcd_putsxy(3, 16, "[MODE] To Undo"); 956 rb->lcd_putsxy(3, 16, "[MODE] Undo");
945 rb->lcd_putsxy(3, 26, "[M-LEFT] - Level"); 957 rb->lcd_putsxy(3, 26, "[M-LEFT] Down a Level");
946 rb->lcd_putsxy(3, 36, "[M-UP] Same Level"); 958 rb->lcd_putsxy(3, 36, "[M-UP] Restart Level");
947 rb->lcd_putsxy(3, 46, "[M-RIGHT] + Level"); 959 rb->lcd_putsxy(3, 46, "[M-RIGHT] Up Level");
948#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \ 960#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
949 (CONFIG_KEYPAD == IRIVER_H300_PAD) 961 (CONFIG_KEYPAD == IRIVER_H300_PAD)
950 rb->lcd_putsxy(3, 6, "[STOP] To Stop"); 962 rb->lcd_putsxy(3, 6, "[STOP] Stop");
951 rb->lcd_putsxy(3, 16, "[PLAY] To Undo"); 963 rb->lcd_putsxy(3, 16, "[PLAY] Undo");
952 rb->lcd_putsxy(3, 26, "[REC] - Level"); 964 rb->lcd_putsxy(3, 26, "[REC] Down a Level");
953 rb->lcd_putsxy(3, 36, "[SELECT] Same Level"); 965 rb->lcd_putsxy(3, 36, "[SELECT] Restart Level");
954 rb->lcd_putsxy(3, 46, "[MODE] + Level"); 966 rb->lcd_putsxy(3, 46, "[MODE] + Up a Level");
955#endif 967#endif
956 968
957 rb->lcd_update(); 969 rb->lcd_update();
@@ -960,7 +972,7 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
960 972
961 init_boards(); 973 init_boards();
962 974
963 if (get_level_count() != 0) { 975 if (read_levels(1) != 0) {
964 rb->splash(HZ*2, true, "Failed loading levels!"); 976 rb->splash(HZ*2, true, "Failed loading levels!");
965 return PLUGIN_OK; 977 return PLUGIN_OK;
966 } 978 }