From ffbdb25e67230f13590e17330c20cb26cd2c8bb6 Mon Sep 17 00:00:00 2001 From: Zakk Roberts Date: Tue, 13 Feb 2007 06:48:10 +0000 Subject: [PATCH] Large Sokoban update, by Sean Morrisey (FS#6625). Some new features include effectively unlimited (several thousand) undos on any platform, a redo feature, improved level/move info, 'level completed' screen showing moves/pushes made, and cleaned up code (full list of changes at tracker entry). git-svn-id: svn://svn.rockbox.org/rockbox/trunk@12294 a1c6a512-1295-4272-9138-f99709370657 --- apps/plugins/bitmaps/native/SOURCES | 8 +- .../bitmaps/native/sokoban_tiles.14x14.bmp | Bin 3750 -> 4366 bytes .../bitmaps/native/sokoban_tiles.6x6.bmp | Bin 774 -> 894 bytes .../bitmaps/native/sokoban_tiles.6x6x2.bmp | Bin 774 -> 894 bytes .../bitmaps/native/sokoban_tiles.9x9.bmp | Bin 1566 -> 1818 bytes apps/plugins/sokoban.c | 1016 ++++++++--------- docs/CREDITS | 1 + 7 files changed, 502 insertions(+), 523 deletions(-) diff --git a/apps/plugins/bitmaps/native/SOURCES b/apps/plugins/bitmaps/native/SOURCES index 1b6487b292..9781e49b12 100644 --- a/apps/plugins/bitmaps/native/SOURCES +++ b/apps/plugins/bitmaps/native/SOURCES @@ -262,12 +262,14 @@ snake2_bottom.160x128x2.bmp /* Sokoban*/ #ifdef HAVE_LCD_COLOR -#if LCD_HEIGHT >= 240 +#if (LCD_HEIGHT >= 224) && (LCD_WIDTH >= 312) || \ + (LCD_HEIGHT >= 249) && (LCD_WIDTH >= 280) sokoban_tiles.14x14.bmp -#elif LCD_HEIGHT >= 176 +#elif (LCD_HEIGHT >= 144) && (LCD_WIDTH >= 212) || \ + (LCD_HEIGHT >= 169) && (LCD_WIDTH >= 180-4) sokoban_tiles.9x9.bmp #else -/* LCD_HEIGHT >= 128 */ +/* LCD_HEIGHT >= 96~121 && LCD_WIDTH >= 152~120 */ sokoban_tiles.6x6.bmp #endif #elif LCD_DEPTH >= 2 diff --git a/apps/plugins/bitmaps/native/sokoban_tiles.14x14.bmp b/apps/plugins/bitmaps/native/sokoban_tiles.14x14.bmp index 9c6ee20a70cb41292b3048bae1aea46ebb2a2000..47dc5486cfb1800d0fccb22ffc65cf1ef55c380e 100644 GIT binary patch delta 318 zcmYjKy-LGS6h1dsLcAx$Hl?%<39WRnD4l9XmCV`)uuCflZhZlV3XVkx-!teO=`2Wn ziB7(O#jY;b)3itq=ljn2&hKmVd~}*#2URL85k3|Kf?I*2rZCa6LR8}oaN-;ycba%t z)hrz{S!>Y}CyQLgp=hF#TboTAa>NF!b-A&XnCM1WE^l-E=t#c(pTUQ2d$KWJ*zp3H zj_clR^SRJF7MV>Wyr?dIRsNQKI=I{G&TsUCi;?Nzi#-@V!;*bmnh=kskG_fVZT3;| kd$`~>TDv^qy3`LaU>7sKlIRyR33o6tE}l$pe(v8pe}S224FCWD delta 186 zcmeBES|+RGa!NJb6Eh*u=c(iF0)~$1z(o zP2RvHF*%n>Wb#=y&B^nCIG;^t@_a_N&9ZEYY?GDvA}0Is=>T!W=GCwcdWFI~?Fnxm82d8SQ&CB>Qm;m+WFP#7Y diff --git a/apps/plugins/bitmaps/native/sokoban_tiles.6x6.bmp b/apps/plugins/bitmaps/native/sokoban_tiles.6x6.bmp index 3ddfad0ef76cc4b7293a89d857b5fa3363e95bfb..38c045790d3b0edc0d345329d47e3c3d937de95c 100644 GIT binary patch delta 95 zcmZo;`^To@b&O?F{Y;s??|9z+00PWENm2>^;k80G)~ delta 78 zcmeyz*2bpds+s{v#gX}TfGsY&>!TxTKx7V@u7Br2 hH;5m&l?F5XRRKtIb6siylD?<~NSdJfe{JYL+#3hy&ZGbU literal 774 zcmchTu@Qqn31uO5tsG6OHf!~0=Cmbm-`C|_ zk7>eA^6G3Nd|~xuSXjPjfXVEpU3SWP+JntEU;QidP4EM2eDzgLO%I%)=XsBv2&JmR MW(XjEc{cxHAH9Tnn*aa+ diff --git a/apps/plugins/bitmaps/native/sokoban_tiles.9x9.bmp b/apps/plugins/bitmaps/native/sokoban_tiles.9x9.bmp index 87f3bf55dd38f4a649a6f29948e182d6cb7ff382..9675db8b1479cf77b4b5a0a76d3a7537629e37f0 100644 GIT binary patch delta 246 zcmbQoGmB5f$ybV<0SwH5qy`Xk0jMRmrGRV(xH>FylM`7aCfl*FPp)N=-295ggb@JZWKw4U delta 86 zcmbQmH;+ff$ybhz0SwH5qy`Xk0x^iq$RGhE85mx$PE-lrT+FzMc`_@T \ + 0x7FFF +#define MAX_UNDOS 0x7FFF +#else +#define MAX_UNDOS PLUGIN_BUFFER_SIZE - \ + NUM_BUFFERED_BOARDS*SOKOBAN_LEVEL_SIZE - 0x2000 +#endif + +/* Move/push definitions for undo */ +enum { + SOKOBAN_PUSH_LEFT, + SOKOBAN_PUSH_RIGHT, + SOKOBAN_PUSH_UP, + SOKOBAN_PUSH_DOWN, + SOKOBAN_MOVE_LEFT, + SOKOBAN_MOVE_RIGHT, + SOKOBAN_MOVE_UP, + SOKOBAN_MOVE_DOWN +}; +#define SOKOBAN_MOVE_DIFF (SOKOBAN_MOVE_LEFT-SOKOBAN_PUSH_LEFT) +#define SOKOBAN_MOVE_MIN SOKOBAN_MOVE_LEFT /* variable button definitions */ #if CONFIG_KEYPAD == RECORDER_PAD @@ -46,6 +72,7 @@ extern const fb_data sokoban_tiles[]; #define SOKOBAN_DOWN BUTTON_DOWN #define SOKOBAN_QUIT BUTTON_OFF #define SOKOBAN_UNDO BUTTON_ON +#define SOKOBAN_REDO BUTTON_PLAY #define SOKOBAN_LEVEL_UP BUTTON_F3 #define SOKOBAN_LEVEL_DOWN BUTTON_F1 #define SOKOBAN_LEVEL_REPEAT BUTTON_F2 @@ -55,6 +82,7 @@ extern const fb_data sokoban_tiles[]; #define SOKOBAN_DOWN BUTTON_DOWN #define SOKOBAN_QUIT BUTTON_OFF #define SOKOBAN_UNDO BUTTON_ON +#define SOKOBAN_REDO BUTTON_PLAY #define SOKOBAN_LEVEL_UP BUTTON_F3 #define SOKOBAN_LEVEL_DOWN BUTTON_F1 #define SOKOBAN_LEVEL_REPEAT BUTTON_F2 @@ -65,6 +93,7 @@ extern const fb_data sokoban_tiles[]; #define SOKOBAN_QUIT BUTTON_OFF #define SOKOBAN_UNDO_PRE BUTTON_MENU #define SOKOBAN_UNDO (BUTTON_MENU | BUTTON_REL) +#define SOKOBAN_REDO (BUTTON_MENU | BUTTON_DOWN) #define SOKOBAN_LEVEL_UP (BUTTON_MENU | BUTTON_RIGHT) #define SOKOBAN_LEVEL_DOWN (BUTTON_MENU | BUTTON_LEFT) #define SOKOBAN_LEVEL_REPEAT (BUTTON_MENU | BUTTON_UP) @@ -74,10 +103,11 @@ extern const fb_data sokoban_tiles[]; #define SOKOBAN_UP BUTTON_UP #define SOKOBAN_DOWN BUTTON_DOWN #define SOKOBAN_QUIT BUTTON_OFF -#define SOKOBAN_UNDO BUTTON_ON -#define SOKOBAN_LEVEL_UP BUTTON_MODE -#define SOKOBAN_LEVEL_DOWN BUTTON_REC -#define SOKOBAN_LEVEL_REPEAT BUTTON_SELECT +#define SOKOBAN_UNDO BUTTON_REC +#define SOKOBAN_REDO BUTTON_MODE +#define SOKOBAN_LEVEL_UP (BUTTON_ON | BUTTON_UP) +#define SOKOBAN_LEVEL_DOWN (BUTTON_ON | BUTTON_DOWN) +#define SOKOBAN_LEVEL_REPEAT BUTTON_ON #define SOKOBAN_RC_QUIT BUTTON_RC_STOP @@ -88,38 +118,41 @@ extern const fb_data sokoban_tiles[]; #define SOKOBAN_QUIT (BUTTON_SELECT | BUTTON_MENU) #define SOKOBAN_UNDO_PRE BUTTON_SELECT #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL) +#define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_PLAY) #define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_RIGHT) #define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_LEFT) -#define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_PLAY) +/* fixme: if/when simultaneous button presses work for X5, + add redo & level repeat */ #elif (CONFIG_KEYPAD == IAUDIO_X5_PAD) #define SOKOBAN_UP BUTTON_UP #define SOKOBAN_DOWN BUTTON_DOWN #define SOKOBAN_QUIT BUTTON_POWER #define SOKOBAN_UNDO_PRE BUTTON_SELECT #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL) -#define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_UP) -#define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_DOWN) -#define SOKOBAN_LEVEL_REPEAT BUTTON_REC +#define SOKOBAN_LEVEL_UP BUTTON_PLAY +#define SOKOBAN_LEVEL_DOWN BUTTON_REC #elif (CONFIG_KEYPAD == GIGABEAT_PAD) #define SOKOBAN_UP BUTTON_UP #define SOKOBAN_DOWN BUTTON_DOWN #define SOKOBAN_QUIT BUTTON_A -#define SOKOBAN_UNDO BUTTON_MENU -#define SOKOBAN_LEVEL_UP (BUTTON_POWER | BUTTON_UP) -#define SOKOBAN_LEVEL_DOWN (BUTTON_POWER | BUTTON_DOWN) -#define SOKOBAN_LEVEL_REPEAT BUTTON_SELECT +#define SOKOBAN_UNDO BUTTON_SELECT +#define SOKOBAN_REDO BUTTON_POWER +#define SOKOBAN_LEVEL_UP (BUTTON_MENU | BUTTON_UP) +#define SOKOBAN_LEVEL_DOWN (BUTTON_MENU | BUTTON_DOWN) +#define SOKOBAN_LEVEL_REPEAT BUTTON_MENU #elif (CONFIG_KEYPAD == SANSA_E200_PAD) #define SOKOBAN_UP BUTTON_UP #define SOKOBAN_DOWN BUTTON_DOWN #define SOKOBAN_QUIT BUTTON_POWER #define SOKOBAN_UNDO_PRE BUTTON_SELECT -#define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_UP) -#define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_RIGHT) -#define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_LEFT) -#define SOKOBAN_LEVEL_REPEAT BUTTON_REC +#define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL) +#define SOKOBAN_REDO BUTTON_REC +#define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_UP) +#define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_DOWN) +#define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT) #elif (CONFIG_KEYPAD == IRIVER_H10_PAD) #define SOKOBAN_UP BUTTON_SCROLL_UP @@ -127,67 +160,48 @@ extern const fb_data sokoban_tiles[]; #define SOKOBAN_QUIT BUTTON_POWER #define SOKOBAN_UNDO_PRE BUTTON_REW #define SOKOBAN_UNDO (BUTTON_REW | BUTTON_REL) +#define SOKOBAN_REDO BUTTON_FF #define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_SCROLL_UP) #define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_SCROLL_DOWN) -#define SOKOBAN_LEVEL_REPEAT BUTTON_FF +#define SOKOBAN_LEVEL_REPEAT (BUTTON_PLAY | BUTTON_RIGHT) #endif #ifdef HAVE_LCD_COLOR -#define BG_COLOR LCD_RGBPACK(181,199,231) /* Background color. Default Rockbox light blue. */ +/* Background color. Default Rockbox light blue. */ +#define BG_COLOR LCD_RGBPACK(181, 199, 231) #elif LCD_DEPTH >= 2 #define MEDIUM_GRAY LCD_BRIGHTNESS(127) #endif -static void init_undo(void); -static void undo(void); -static void add_undo(int button); - -static int read_levels(int initialize_count); -static void load_level(void); -static void draw_level(void); - -static void init_boards(void); -static void update_screen(void); -static bool sokoban_loop(void); - /* The Location, Undo and LevelInfo structs are OO-flavored. * (oooh!-flavored as Schnueff puts it.) It makes more you have to know, * but the overall data layout becomes more manageable. */ -/* We use the same three values in 2 structs. Makeing them a struct - * hopefully ensures that if you change things in one, the other changes - * as well. */ +/* Level data & stats */ struct LevelInfo { short level; short moves; + short pushes; short boxes_to_go; }; -/* What a given location on the board looks like at a given time */ struct Location { - char spot; short row; short col; }; -/* A single level of undo. Each undo move can affect upto, - * but not more then, 3 spots on the board */ -struct Undo { - struct LevelInfo level; - struct Location location[3]; -}; - struct Board { char spaces[ROWS][COLS]; }; /* Our full undo history */ static struct UndoInfo { - short count; /* How many undos are there in history */ + short count; /* How many undos are left */ short current; /* Which history is the current undo */ - struct Undo history[MAX_UNDOS]; + short max; /* Which history is the max redoable */ + char history[MAX_UNDOS]; } undo_info; /* Our playing board */ @@ -200,7 +214,7 @@ static struct BoardInfo { } current_info; static struct BufferedBoards { - struct Board levels[MAX_BUFFERED_BOARDS]; + struct Board levels[NUM_BUFFERED_BOARDS]; int low; } buffered_boards; @@ -209,138 +223,216 @@ static struct plugin_api* rb; static void init_undo(void) { undo_info.count = 0; - undo_info.current = 0; + undo_info.current = -1; + undo_info.max = -1; +} + +static void get_delta(char direction, short *d_r, short *d_c) +{ + switch (direction) { + case SOKOBAN_PUSH_LEFT: + case SOKOBAN_MOVE_LEFT: + *d_r = 0; + *d_c = -1; + break; + case SOKOBAN_PUSH_RIGHT: + case SOKOBAN_MOVE_RIGHT: + *d_r = 0; + *d_c = 1; + break; + case SOKOBAN_PUSH_UP: + case SOKOBAN_MOVE_UP: + *d_r = -1; + *d_c = 0; + break; + case SOKOBAN_PUSH_DOWN: + case SOKOBAN_MOVE_DOWN: + *d_r = 1; + *d_c = 0; + } } static void undo(void) { - struct Undo *undo; - int i = 0; - short row, col; + char undo; + short r, c; + short d_r = 0, d_c = 0; /* delta row & delta col */ + char *space_cur, *space_next, *space_prev; + bool undo_push = false; - if (undo_info.count == 0) + /* If no more undos or we've wrapped all the way around, quit */ + if (undo_info.count == 0 || undo_info.current-1 == undo_info.max) return; + undo = undo_info.history[undo_info.current]; + + if (undo < SOKOBAN_MOVE_MIN) + undo_push = true; + + get_delta(undo, &d_r, &d_c); + + r = current_info.player.row; + c = current_info.player.col; + + /* Give the 3 spaces we're going to use better names */ + space_cur = ¤t_info.board[r][c]; + space_next = ¤t_info.board[r + d_r][c + d_c]; + space_prev = ¤t_info.board[r - d_r][c - d_c]; + /* Update board info */ - undo = &undo_info.history[undo_info.current]; + if (undo_push) { + /* Moving box from goal to blank */ + if (*space_next == '%' && *space_cur == '@') + current_info.level.boxes_to_go++; + /* Moving box from blank to goal */ + else if (*space_next == '$' && *space_cur == '+') + current_info.level.boxes_to_go--; - rb->memcpy(¤t_info.level, &undo->level, sizeof(undo->level)); - rb->memcpy(¤t_info.player, &undo->location[0], sizeof(undo->location[0])); + /* Move box off of next space... */ + *space_next = (*space_next == '%' ? '.' : ' '); + /* ...and on to current space */ + *space_cur = (*space_cur == '+' ? '%' : '$'); - row = undo->location[0].row; - col = undo->location[0].col; - current_info.board[row][col] = '@'; + current_info.level.pushes--; + } else + /* Just move player off of current space */ + *space_cur = (*space_cur == '+' ? '.' : ' '); + /* Move player back to previous space */ + *space_prev = (*space_prev == '.' ? '+' : '@'); - /* Update the two other possible spots */ - for (i = 1; i < 3; i++) { - if (undo->location[i].spot != '\0') { - row = undo->location[i].row; - col = undo->location[i].col; - current_info.board[row][col] = undo->location[i].spot; - undo->location[i].spot = '\0'; - } - } + /* Update position */ + current_info.player.row -= d_r; + current_info.player.col -= d_c; - /* Remove this undo from the list */ - if (undo_info.current == 0) { - if (undo_info.count > 1) - undo_info.current = MAX_UNDOS - 1; - } else { + current_info.level.moves--; + + /* Move to previous undo in the list */ + if (undo_info.current == 0 && undo_info.count > 1) + undo_info.current = MAX_UNDOS - 1; + else undo_info.current--; - } undo_info.count--; return; } -static void add_undo(int button) +static void add_undo(char undo) { - struct Undo *undo; - int row, col, i; - bool storable; + /* Wrap around if MAX_UNDOS exceeded */ + if (undo_info.current < (MAX_UNDOS - 1)) + undo_info.current++; + else + undo_info.current = 0; - if ((button != BUTTON_LEFT) && (button != BUTTON_RIGHT) && - (button != SOKOBAN_UP) && (button != SOKOBAN_DOWN)) - return; - - if (undo_info.count != 0) { - if (undo_info.current < (MAX_UNDOS - 1)) - undo_info.current++; - else - undo_info.current = 0; - } - - /* Make what follows more readable */ - undo = &undo_info.history[undo_info.current]; - - /* Store our level info */ - rb->memcpy(&undo->level, ¤t_info.level, sizeof(undo->level)); - - /* Store our player info */ - rb->memcpy(&undo->location[0], ¤t_info.player, sizeof(undo->location[0])); - - /* Now we need to store upto 2 blocks that may be affected. - * If player.spot is NULL, then there is no info stored - * for that block */ - - row = current_info.player.row; - col = current_info.player.col; - - /* This must stay as _1_ because the first block (0) is the player */ - for (i = 1; i < 3; i++) { - storable = true; - - switch (button) { - case BUTTON_LEFT: - col--; - if (col < 0) - storable = false; - break; - - case BUTTON_RIGHT: - col++; - if (col >= COLS) - storable = false; - break; - - case SOKOBAN_UP: - row--; - if (row < 0) - storable = false; - break; - - case SOKOBAN_DOWN: - row++; - if (row >= ROWS) - storable = false; - break; - - default: - return; - } - - if (storable) { - undo->location[i].col = col; - undo->location[i].row = row; - undo->location[i].spot = current_info.board[row][col]; - } else { - undo->location[i].spot = '\0'; - } - } + undo_info.history[undo_info.current] = undo; if (undo_info.count < MAX_UNDOS) undo_info.count++; } +static bool move(char direction, bool redo) +{ + short r, c; + short d_r = 0, d_c = 0; /* delta row & delta col */ + char *space_cur, *space_next, *space_beyond; + bool push = false; + + get_delta(direction, &d_r, &d_c); + + r = current_info.player.row; + c = current_info.player.col; + + /* Check for out-of-bounds */ + if (r + 2*d_r < 0 || r + 2*d_r >= ROWS || + c + 2*d_c < 0 || c + 2*d_c >= COLS) + return false; + + /* Give the 3 spaces we're going to use better names */ + space_cur = ¤t_info.board[r][c]; + space_next = ¤t_info.board[r + d_r][c + d_c]; + space_beyond = ¤t_info.board[r + 2*d_r][c + 2*d_c]; + + if (*space_next == '$' || *space_next == '%') { + /* Change direction from move to push for undo */ + if (direction >= SOKOBAN_MOVE_MIN) + direction -= SOKOBAN_MOVE_DIFF; + push = true; + } + + /* Update board info */ + if (push) { + /* Moving box from goal to blank */ + if (*space_next == '%' && *space_beyond == ' ') + current_info.level.boxes_to_go++; + /* Moving box from blank to goal */ + else if (*space_next == '$' && *space_beyond == '.') + current_info.level.boxes_to_go--; + /* Check for illegal move */ + else if (*space_beyond != '.' && *space_beyond != ' ') + return false; + + /* Move player onto next space */ + *space_next = (*space_next == '%' ? '+' : '@'); + /* Move box onto space beyond next */ + *space_beyond = (*space_beyond == '.' ? '%' : '$'); + + current_info.level.pushes++; + } else { + /* Check for illegal move */ + if (*space_next == '#' || *space_next == 'X') + return false; + + /* Move player onto next space */ + *space_next = (*space_next == '.' ? '+' : '@'); + } + /* Move player off of current space */ + *space_cur = (*space_cur == '+' ? '.' : ' '); + + /* Update position */ + current_info.player.row += d_r; + current_info.player.col += d_c; + + current_info.level.moves++; + + /* Update undo_info.max to current on every normal move, + except if it's the same as a redo. */ + /* normal move */ + if (!redo && + /* moves have been undone */ + ((undo_info.max != undo_info.current && + /* and the current move is NOT the same as the one in history */ + undo_info.history[undo_info.current+1] != direction) || + /* or moves have not been undone */ + undo_info.max == undo_info.current)) { + add_undo(direction); + undo_info.max = undo_info.current; + } else /* redo move or move was same as redo */ + add_undo(direction); /* (just to update current) */ + + return true; +} + +#ifdef SOKOBAN_REDO +static bool redo(void) +{ + /* If no moves have been undone, quit */ + if (undo_info.current == undo_info.max) + return false; + + return move(undo_info.history[(undo_info.current+1 < MAX_UNDOS ? + undo_info.current+1 : 0)], true); +} +#endif + static void init_boards(void) { current_info.level.level = 0; current_info.level.moves = 0; + current_info.level.pushes = 0; current_info.level.boxes_to_go = 0; current_info.player.row = 0; current_info.player.col = 0; - current_info.player.spot = ' '; current_info.max_level = 0; current_info.loaded_level = 0; @@ -360,15 +452,15 @@ static int read_levels(int initialize_count) int endpoint = current_info.level.level-1; if (endpoint < buffered_boards.low) - endpoint = current_info.level.level - MAX_BUFFERED_BOARDS; + endpoint = current_info.level.level - NUM_BUFFERED_BOARDS; if (endpoint < 0) endpoint = 0; buffered_boards.low = endpoint; - endpoint += MAX_BUFFERED_BOARDS; + endpoint += NUM_BUFFERED_BOARDS; if ((fd = rb->open(LEVELS_FILE, O_RDONLY)) < 0) { - rb->splash(0, true, "Unable to open %s", LEVELS_FILE); + rb->splash(HZ*2, true, "Unable to open %s", LEVELS_FILE); return -1; } @@ -380,13 +472,13 @@ static int read_levels(int initialize_count) * a mixed unix and dos CR/LF file format, I'm not going to * do a precise check */ if (len < COLS) { - rb->splash(0, true, "Error in levels file: short line"); + rb->splash(HZ*2, true, "Error in levels file: short line"); return -1; } if (level_count >= buffered_boards.low && level_count < endpoint) { int index = level_count - buffered_boards.low; rb->memcpy( - buffered_boards.levels[index].spaces[row],buffer,COLS); + buffered_boards.levels[index].spaces[row], buffer, COLS); } row++; } else if (len) { @@ -395,7 +487,8 @@ static int read_levels(int initialize_count) level_count++; if (level_count >= endpoint && !initialize_count) break; if (level_count && row != ROWS) { - rb->splash(0, true, "Error in levels file: short board"); + rb->splash(HZ*2, true, + "Error in levels file: short board"); return -1; } row = 0; @@ -419,32 +512,33 @@ static void load_level(void) int index = current_info.level.level - buffered_boards.low - 1; struct Board *level; - if (index < 0 || index >= MAX_BUFFERED_BOARDS) { + if (index < 0 || index >= NUM_BUFFERED_BOARDS) { read_levels(false); - index=index<0?MAX_BUFFERED_BOARDS-1:0; + index = index < 0 ? NUM_BUFFERED_BOARDS-1 : 0; } level = &buffered_boards.levels[index]; - current_info.player.spot=' '; current_info.level.boxes_to_go = 0; current_info.level.moves = 0; + current_info.level.pushes = 0; current_info.loaded_level = current_info.level.level; for (r = 0; r < ROWS; r++) { for (c = 0; c < COLS; c++) { current_info.board[r][c] = level->spaces[r][c]; - if (current_info.board[r][c] == '.') + if (current_info.board[r][c] == '.' || + current_info.board[r][c] == '+') current_info.level.boxes_to_go++; - else if (current_info.board[r][c] == '@') { + if (current_info.board[r][c] == '@' || + current_info.board[r][c] == '+') { current_info.player.row = r; current_info.player.col = c; } } } } -#define STAT_WIDTH (LCD_WIDTH-(COLS * magnify)) static void update_screen(void) { @@ -453,21 +547,31 @@ static void update_screen(void) char s[25]; /* magnify is the number of pixels for each block */ -#if LCD_HEIGHT >= 240 /* ipod 5g */ - int magnify = 14; -#elif LCD_HEIGHT >= 176 /* h3x0, ipod color/photo */ - int magnify = 9; -#elif LCD_HEIGHT >= 110 /* h1x0, ipod nano, ipod mini */ - int magnify = 6; +#if (LCD_HEIGHT >= 224) && (LCD_WIDTH >= 312) || \ + (LCD_HEIGHT >= 249) && (LCD_WIDTH >= 280) /* ipod 5g */ +#define MAGNIFY 14 +#elif (LCD_HEIGHT >= 144) && (LCD_WIDTH >= 212) || \ + (LCD_HEIGHT >= 169) && (LCD_WIDTH >= 180-4) /* h3x0, ipod color/photo */ +#define MAGNIFY 9 +#elif (LCD_HEIGHT >= 96) && (LCD_WIDTH >= 152) || \ + (LCD_HEIGHT >= 121) && (LCD_WIDTH >= 120) /* h1x0, ipod nano/mini */ +#define MAGNIFY 6 #else /* other */ - int magnify = 4; +#define MAGNIFY 4 +#endif + +#if LCD_DEPTH < 2 + int i, j; + int max = MAGNIFY - 1; + int middle = max / 2; + int ldelta = (middle + 1) / 2; #endif /* load the board to the screen */ - for (rows=0 ; rows < ROWS ; rows++) { - for (cols = 0 ; cols < COLS ; cols++) { - c = cols * magnify; - b = rows * magnify; + for (rows=0; rows < ROWS; rows++) { + for (cols = 0; cols < COLS; cols++) { + c = cols * MAGNIFY; + b = rows * MAGNIFY; switch(current_info.board[rows][cols]) { case 'X': /* black space */ @@ -475,89 +579,132 @@ static void update_screen(void) case '#': /* this is a wall */ #if LCD_DEPTH >= 2 - rb->lcd_bitmap_part( sokoban_tiles, 0, 1*magnify, magnify, - c, b, magnify, magnify ); + rb->lcd_bitmap_part(sokoban_tiles, 0, 1*MAGNIFY, MAGNIFY, + c, b, MAGNIFY, MAGNIFY); #else - { - int i, j; - for (i = c; i < c + magnify; i++) - for (j = b; j < b + magnify; j++) - if ((i ^ j) & 1) - rb->lcd_drawpixel(i, j); - } -#endif - break; - - case '.': /* this is a home location */ -#if LCD_DEPTH >= 2 - rb->lcd_bitmap_part( sokoban_tiles, 0, 4*magnify, magnify, - c, b, magnify, magnify ); -#else - rb->lcd_drawrect(c+(magnify/2)-1, b+(magnify/2)-1, magnify/2, - magnify/2); + for (i = c; i < c + MAGNIFY; i++) + for (j = b; j < b + MAGNIFY; j++) + if ((i ^ j) & 1) + rb->lcd_drawpixel(i, j); #endif break; case '$': /* this is a box */ #if LCD_DEPTH >= 2 - rb->lcd_bitmap_part( sokoban_tiles, 0, 2*magnify, magnify, - c, b, magnify, magnify ); + rb->lcd_bitmap_part(sokoban_tiles, 0, 2*MAGNIFY, MAGNIFY, + c, b, MAGNIFY, MAGNIFY); #else - rb->lcd_drawrect(c, b, magnify, magnify); /* Free boxes are not filled in */ + /* Free boxes are not filled in */ + rb->lcd_drawrect(c, b, MAGNIFY, MAGNIFY); +#endif + break; + + case '*': + case '%': /* this is a box on a goal */ + +#if LCD_DEPTH >= 2 + rb->lcd_bitmap_part(sokoban_tiles, 0, 3*MAGNIFY, MAGNIFY, + c, b, MAGNIFY, MAGNIFY ); +#else + rb->lcd_drawrect(c, b, MAGNIFY, MAGNIFY); + rb->lcd_drawrect(c+(MAGNIFY/2)-1, b+(MAGNIFY/2)-1, MAGNIFY/2, + MAGNIFY/2); +#endif + break; + + case '.': /* this is a goal */ +#if LCD_DEPTH >= 2 + rb->lcd_bitmap_part(sokoban_tiles, 0, 4*MAGNIFY, MAGNIFY, + c, b, MAGNIFY, MAGNIFY); +#else + rb->lcd_drawrect(c+(MAGNIFY/2)-1, b+(MAGNIFY/2)-1, MAGNIFY/2, + MAGNIFY/2); #endif break; case '@': /* this is you */ - { #if LCD_DEPTH >= 2 - rb->lcd_bitmap_part( sokoban_tiles, 0, 5*magnify, magnify, - c, b, magnify, magnify ); + rb->lcd_bitmap_part(sokoban_tiles, 0, 5*MAGNIFY, MAGNIFY, + c, b, MAGNIFY, MAGNIFY); #else - int max = magnify - 1; - int middle = max / 2; - int ldelta = (middle + 1) / 2; - rb->lcd_drawline(c, b+middle, c+max, b+middle); - rb->lcd_drawline(c+middle, b, c+middle, b+max-ldelta); - rb->lcd_drawline(c+max-middle, b, - c+max-middle, b+max-ldelta); - rb->lcd_drawline(c+middle, b+max-ldelta, - c+middle-ldelta, b+max); - rb->lcd_drawline(c+max-middle, b+max-ldelta, - c+max-middle+ldelta, b+max); + rb->lcd_drawline(c, b+middle, c+max, b+middle); + rb->lcd_drawline(c+middle, b, c+middle, b+max-ldelta); + rb->lcd_drawline(c+max-middle, b, + c+max-middle, b+max-ldelta); + rb->lcd_drawline(c+middle, b+max-ldelta, + c+middle-ldelta, b+max); + rb->lcd_drawline(c+max-middle, b+max-ldelta, + c+max-middle+ldelta, b+max); #endif - } break; - case '%': /* this is a box on a home spot */ - + case '+': /* this is you on drugs, erm, on a goal */ #if LCD_DEPTH >= 2 - rb->lcd_bitmap_part( sokoban_tiles, 0, 3*magnify, magnify, - c, b, magnify, magnify ); + rb->lcd_bitmap_part(sokoban_tiles, 0, 6*MAGNIFY, MAGNIFY, + c, b, MAGNIFY, MAGNIFY ); #else - rb->lcd_drawrect(c, b, magnify, magnify); - rb->lcd_drawrect(c+(magnify/2)-1, b+(magnify/2)-1, magnify/2, - magnify/2); + rb->lcd_drawline(c, b+middle, c+max, b+middle); + rb->lcd_drawline(c+middle, b, c+middle, b+max-ldelta); + rb->lcd_drawline(c+max-middle, b, c+max-middle, b+max-ldelta); + rb->lcd_drawline(c+middle, b+max-ldelta, c+middle-ldelta, + b+max); + rb->lcd_drawline(c+max-middle, b+max-ldelta, + c+max-middle+ldelta, b+max); + rb->lcd_drawline(c+middle-1, b+middle+1, c+max-middle+1, + b+middle+1); #endif break; #if LCD_DEPTH >= 2 default: - rb->lcd_bitmap_part( sokoban_tiles, 0, 0*magnify, magnify, - c, b, magnify, magnify ); + rb->lcd_bitmap_part(sokoban_tiles, 0, 0*MAGNIFY, MAGNIFY, + c, b, MAGNIFY, MAGNIFY ); #endif } } } - rb->snprintf(s, sizeof(s), "%d", current_info.level.level); - rb->lcd_putsxy(LCD_WIDTH-STAT_WIDTH+4, 22, s); - rb->snprintf(s, sizeof(s), "%d", current_info.level.moves); - rb->lcd_putsxy(LCD_WIDTH-STAT_WIDTH+4, 54, s); +#if LCD_WIDTH-(COLS*MAGNIFY) < 32 +#define STAT_SIZE 25 +#define STAT_POS LCD_HEIGHT-STAT_SIZE +#define STAT_CENTER (LCD_WIDTH-120)/2 - rb->lcd_drawrect(LCD_WIDTH-STAT_WIDTH,0,STAT_WIDTH,32); - rb->lcd_drawrect(LCD_WIDTH-STAT_WIDTH,32,STAT_WIDTH,LCD_HEIGHT-32); - rb->lcd_putsxy(LCD_WIDTH-STAT_WIDTH+1, 10, "Level"); - rb->lcd_putsxy(LCD_WIDTH-STAT_WIDTH+1, 42, "Moves"); + rb->lcd_putsxy(4+STAT_CENTER, STAT_POS+4, "Level"); + rb->snprintf(s, sizeof(s), "%d", current_info.level.level); + rb->lcd_putsxy(7+STAT_CENTER, STAT_POS+14, s); + rb->lcd_putsxy(41+STAT_CENTER, STAT_POS+4, "Moves"); + rb->snprintf(s, sizeof(s), "%d", current_info.level.moves); + rb->lcd_putsxy(44+STAT_CENTER, STAT_POS+14, s); + rb->lcd_putsxy(79+STAT_CENTER, STAT_POS+4, "Pushes"); + rb->snprintf(s, sizeof(s), "%d", current_info.level.pushes); + rb->lcd_putsxy(82+STAT_CENTER, STAT_POS+14, s); + + rb->lcd_drawrect(STAT_CENTER, STAT_POS, 38, STAT_SIZE); + rb->lcd_drawrect(37+STAT_CENTER, STAT_POS, 39, STAT_SIZE); + rb->lcd_drawrect(75+STAT_CENTER, STAT_POS, 45, STAT_SIZE); + +#else +#define STAT_POS COLS*MAGNIFY +#define STAT_SIZE LCD_WIDTH-STAT_POS + + rb->lcd_putsxy(STAT_POS+1, 3, "Level"); + rb->snprintf(s, sizeof(s), "%d", current_info.level.level); + rb->lcd_putsxy(STAT_POS+4, 13, s); + rb->lcd_putsxy(STAT_POS+1, 26, "Moves"); + rb->snprintf(s, sizeof(s), "%d", current_info.level.moves); + rb->lcd_putsxy(STAT_POS+4, 36, s); + + rb->lcd_drawrect(STAT_POS, 0, STAT_SIZE, 24); + rb->lcd_drawrect(STAT_POS, 23, STAT_SIZE, 24); + +#if LCD_HEIGHT >= 70 + rb->lcd_putsxy(STAT_POS+1, 49, "Pushes"); + rb->snprintf(s, sizeof(s), "%d", current_info.level.pushes); + rb->lcd_putsxy(STAT_POS+4, 59, s); + + rb->lcd_drawrect(STAT_POS, 46, STAT_SIZE, 24); +#endif +#endif /* print out the screen */ rb->lcd_update(); @@ -572,10 +719,11 @@ static void draw_level(void) static bool sokoban_loop(void) { - char new_spot; bool moved = true; int i = 0, button = 0, lastbutton = 0; short r = 0, c = 0; + int w, h; + char s[25]; current_info.level.level = 1; @@ -590,8 +738,6 @@ static bool sokoban_loop(void) button = rb->button_get(true); - add_undo(button); - switch(button) { #ifdef SOKOBAN_RC_QUIT @@ -608,22 +754,33 @@ static bool sokoban_loop(void) #ifdef SOKOBAN_UNDO_PRE if (lastbutton != SOKOBAN_UNDO_PRE) break; -#else /* repeat can't work here for Ondio */ +#else /* repeat can't work here for Ondio et al */ case SOKOBAN_UNDO | BUTTON_REPEAT: #endif - /* this is UNDO */ undo(); rb->lcd_clear_display(); update_screen(); moved = false; break; +#ifdef SOKOBAN_REDO + case SOKOBAN_REDO: + case SOKOBAN_REDO | BUTTON_REPEAT: + moved = redo(); + rb->lcd_clear_display(); + update_screen(); + break; +#endif + case SOKOBAN_LEVEL_UP: case SOKOBAN_LEVEL_UP | BUTTON_REPEAT: - /* increase level */ + /* next level */ init_undo(); - current_info.level.boxes_to_go=0; - moved = true; + if (current_info.level.level < current_info.max_level) + current_info.level.level++; + + draw_level(); + moved = false; break; case SOKOBAN_LEVEL_DOWN: @@ -637,6 +794,7 @@ static bool sokoban_loop(void) moved = false; break; +#ifdef SOKOBAN_LEVEL_REPEAT case SOKOBAN_LEVEL_REPEAT: case SOKOBAN_LEVEL_REPEAT | BUTTON_REPEAT: /* same level */ @@ -644,267 +802,26 @@ static bool sokoban_loop(void) draw_level(); moved = false; break; +#endif case BUTTON_LEFT: - switch(current_info.board[r][c-1]) - { - case ' ': /* if it is a blank spot */ - case '.': /* if it is a home spot */ - new_spot = current_info.board[r][c-1]; - current_info.board[r][c-1] = '@'; - current_info.board[r][c] = current_info.player.spot; - current_info.player.spot = new_spot; - break; - - case '$': - switch(current_info.board[r][c-2]) - { - case ' ': /* going from blank to blank */ - current_info.board[r][c-2] = current_info.board[r][c-1]; - current_info.board[r][c-1] = current_info.board[r][c]; - current_info.board[r][c] = current_info.player.spot; - current_info.player.spot = ' '; - break; - - case '.': /* going from a blank to home */ - current_info.board[r][c-2] = '%'; - current_info.board[r][c-1] = current_info.board[r][c]; - current_info.board[r][c] = current_info.player.spot; - current_info.player.spot = ' '; - current_info.level.boxes_to_go--; - break; - - default: - moved = false; - break; - } - break; - - case '%': - switch(current_info.board[r][c-2]) { - case ' ': /* we are going from a home to a blank */ - current_info.board[r][c-2] = '$'; - current_info.board[r][c-1] = current_info.board[r][c]; - current_info.board[r][c] = current_info.player.spot; - current_info.player.spot = '.'; - current_info.level.boxes_to_go++; - break; - - case '.': /* if we are going from a home to home */ - current_info.board[r][c-2] = '%'; - current_info.board[r][c-1] = current_info.board[r][c]; - current_info.board[r][c] = current_info.player.spot; - current_info.player.spot = '.'; - break; - - default: - moved = false; - break; - } - break; - - default: - moved = false; - break; - } - - if (moved) - current_info.player.col--; + case BUTTON_LEFT | BUTTON_REPEAT: + moved = move(SOKOBAN_MOVE_LEFT, false); break; - case BUTTON_RIGHT: /* if it is a blank spot */ - switch(current_info.board[r][c+1]) { - case ' ': - case '.': /* if it is a home spot */ - new_spot = current_info.board[r][c+1]; - current_info.board[r][c+1] = '@'; - current_info.board[r][c] = current_info.player.spot; - current_info.player.spot = new_spot; - break; - - case '$': - switch(current_info.board[r][c+2]) { - case ' ': /* going from blank to blank */ - current_info.board[r][c+2] = current_info.board[r][c+1]; - current_info.board[r][c+1] = current_info.board[r][c]; - current_info.board[r][c] = current_info.player.spot; - current_info.player.spot = ' '; - break; - - case '.': /* going from a blank to home */ - current_info.board[r][c+2] = '%'; - current_info.board[r][c+1] = current_info.board[r][c]; - current_info.board[r][c] = current_info.player.spot; - current_info.player.spot = ' '; - current_info.level.boxes_to_go--; - break; - - default: - moved = false; - break; - } - break; - - case '%': - switch(current_info.board[r][c+2]) { - case ' ': /* going from a home to a blank */ - current_info.board[r][c+2] = '$'; - current_info.board[r][c+1] = current_info.board[r][c]; - current_info.board[r][c] = current_info.player.spot; - current_info.player.spot = '.'; - current_info.level.boxes_to_go++; - break; - - case '.': - current_info.board[r][c+2] = '%'; - current_info.board[r][c+1] = current_info.board[r][c]; - current_info.board[r][c] = current_info.player.spot; - current_info.player.spot = '.'; - break; - - default: - moved = false; - break; - } - break; - - default: - moved = false; - break; - } - - if (moved) - current_info.player.col++; + case BUTTON_RIGHT: + case BUTTON_RIGHT | BUTTON_REPEAT: + moved = move(SOKOBAN_MOVE_RIGHT, false); break; case SOKOBAN_UP: - switch(current_info.board[r-1][c]) { - case ' ': /* if it is a blank spot */ - case '.': /* if it is a home spot */ - new_spot = current_info.board[r-1][c]; - current_info.board[r-1][c] = '@'; - current_info.board[r][c] = current_info.player.spot; - current_info.player.spot = new_spot; - break; - - case '$': - switch(current_info.board[r-2][c]) { - case ' ': /* going from blank to blank */ - current_info.board[r-2][c] = current_info.board[r-1][c]; - current_info.board[r-1][c] = current_info.board[r][c]; - current_info.board[r][c] = current_info.player.spot; - current_info.player.spot = ' '; - break; - - case '.': /* going from a blank to home */ - current_info.board[r-2][c] = '%'; - current_info.board[r-1][c] = current_info.board[r][c]; - current_info.board[r][c] = current_info.player.spot; - current_info.player.spot = ' '; - current_info.level.boxes_to_go--; - break; - - default: - moved = false; - break; - } - break; - - case '%': - switch(current_info.board[r-2][c]) { - case ' ': /* we are going from a home to a blank */ - current_info.board[r-2][c] = '$'; - current_info.board[r-1][c] = current_info.board[r][c]; - current_info.board[r][c] = current_info.player.spot; - current_info.player.spot = '.'; - current_info.level.boxes_to_go++; - break; - - case '.': /* if we are going from a home to home */ - current_info.board[r-2][c] = '%'; - current_info.board[r-1][c] = current_info.board[r][c]; - current_info.board[r][c] = current_info.player.spot; - current_info.player.spot = '.'; - break; - - default: - moved = false; - break; - } - break; - - default: - moved = false; - break; - } - - if (moved) - current_info.player.row--; + case SOKOBAN_UP | BUTTON_REPEAT: + moved = move(SOKOBAN_MOVE_UP, false); break; case SOKOBAN_DOWN: - switch(current_info.board[r+1][c]) { - case ' ': /* if it is a blank spot */ - case '.': /* if it is a home spot */ - new_spot = current_info.board[r+1][c]; - current_info.board[r+1][c] = '@'; - current_info.board[r][c] = current_info.player.spot; - current_info.player.spot = new_spot; - break; - - case '$': - switch(current_info.board[r+2][c]) { - case ' ': /* going from blank to blank */ - current_info.board[r+2][c] = current_info.board[r+1][c]; - current_info.board[r+1][c] = current_info.board[r][c]; - current_info.board[r][c] = current_info.player.spot; - current_info.player.spot = ' '; - break; - - case '.': /* going from a blank to home */ - current_info.board[r+2][c] = '%'; - current_info.board[r+1][c] = current_info.board[r][c]; - current_info.board[r][c] = current_info.player.spot; - current_info.player.spot = ' '; - current_info.level.boxes_to_go--; - break; - - default: - moved = false; - break; - } - break; - - case '%': - switch(current_info.board[r+2][c]) { - case ' ': /* going from a home to a blank */ - current_info.board[r+2][c] = '$'; - current_info.board[r+1][c] = current_info.board[r][c]; - current_info.board[r][c] = current_info.player.spot; - current_info.player.spot = '.'; - current_info.level.boxes_to_go++; - break; - - case '.': /* going from a home to home */ - current_info.board[r+2][c] = '%'; - current_info.board[r+1][c] = current_info.board[r][c]; - current_info.board[r][c] = current_info.player.spot; - current_info.player.spot = '.'; - break; - - default: - moved = false; - break; - } - break; - - default: - moved = false; - break; - } - - if (moved) - current_info.player.row++; + case SOKOBAN_DOWN | BUTTON_REPEAT: + moved = move(SOKOBAN_MOVE_DOWN, false); break; default: @@ -919,13 +836,40 @@ static bool sokoban_loop(void) lastbutton = button; if (moved) { - current_info.level.moves++; rb->lcd_clear_display(); update_screen(); } /* We have completed this level */ if (current_info.level.boxes_to_go == 0) { + + if (moved) { + rb->lcd_clear_display(); + /* Center level completed message */ + rb->snprintf(s, sizeof(s), "Level %d Complete!", + current_info.level.level); + rb->lcd_getstringsize(s, &w, &h); + rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - 16 , s); + rb->snprintf(s, sizeof(s), "%4d Moves ", + current_info.level.moves); + rb->lcd_getstringsize(s, &w, &h); + rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + 0 , s); + rb->snprintf(s, sizeof(s), "%4d Pushes", + current_info.level.pushes); + rb->lcd_getstringsize(s, &w, &h); + rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + 8 , s); + rb->lcd_update(); + rb->button_get(false); + + rb->sleep(HZ/2); + for (i = 0; i < 30; i++) { + rb->sleep(HZ/20); + button = rb->button_get(false); + if (button && ((button & BUTTON_REL) != BUTTON_REL)) + break; + } + } + current_info.level.level++; /* clear undo stats */ @@ -935,12 +879,13 @@ static bool sokoban_loop(void) if (current_info.level.level > current_info.max_level) { /* Center "You WIN!!" on all screen sizes */ - rb->lcd_putsxy(LCD_WIDTH/2 - 27,(LCD_HEIGHT/2) - 4 , - "You WIN!!"); + rb->snprintf(s, sizeof(s), "You WIN!!"); + rb->lcd_getstringsize(s, &w, &h); + rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h/2, s); rb->lcd_set_drawmode(DRMODE_COMPLEMENT); - /* Pattern Fills whole screen now on all screen sizes */ - for (i = 0; i < 30000 ; i++) { + /* Display for 10 seconds or until keypress */ + for (i = 0; i < 200; i++) { rb->lcd_fillrect(0, 0, LCD_WIDTH, LCD_HEIGHT); rb->lcd_update(); rb->sleep(HZ/20); @@ -967,68 +912,99 @@ static bool sokoban_loop(void) enum plugin_status plugin_start(struct plugin_api* api, void* parameter) { int w, h; - int len; + int i; + int button = 0; (void)(parameter); rb = api; rb->lcd_setfont(FONT_SYSFIXED); - rb->lcd_getstringsize(SOKOBAN_TITLE, &w, &h); #ifdef HAVE_LCD_COLOR rb->lcd_set_background(BG_COLOR); #endif - /* Get horizontel centering for text */ - len = w; - if (len%2 != 0) - len =((len+1)/2)+(w/2); - else - len /= 2; - - if (h%2 != 0) - h = (h/2)+1; - else - h /= 2; - rb->lcd_clear_display(); - rb->lcd_putsxy(LCD_WIDTH/2-len,(LCD_HEIGHT/2)-h, SOKOBAN_TITLE); + rb->lcd_getstringsize(SOKOBAN_TITLE, &w, &h); + rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h/2, SOKOBAN_TITLE); rb->lcd_update(); - rb->sleep(HZ*2); + rb->sleep(HZ); rb->lcd_clear_display(); -#if CONFIG_KEYPAD == RECORDER_PAD +#if (CONFIG_KEYPAD == RECORDER_PAD) || \ + (CONFIG_KEYPAD == ARCHOS_AV300_PAD) rb->lcd_putsxy(3, 6, "[OFF] Quit"); rb->lcd_putsxy(3, 16, "[ON] Undo"); - rb->lcd_putsxy(3, 26, "[F1] Down a Level"); - rb->lcd_putsxy(3, 36, "[F2] Restart Level"); - rb->lcd_putsxy(3, 46, "[F3] Up a Level"); + rb->lcd_putsxy(3, 26, "[PLAY] Redo"); + rb->lcd_putsxy(3, 36, "[F1] Down a Level"); + rb->lcd_putsxy(3, 46, "[F2] Restart Level"); + rb->lcd_putsxy(3, 56, "[F3] Up a Level"); #elif CONFIG_KEYPAD == ONDIO_PAD rb->lcd_putsxy(3, 6, "[OFF] Quit"); rb->lcd_putsxy(3, 16, "[MODE] Undo"); - rb->lcd_putsxy(3, 26, "[M-LEFT] Down a Level"); - rb->lcd_putsxy(3, 36, "[M-UP] Restart Level"); - rb->lcd_putsxy(3, 46, "[M-RIGHT] Up Level"); + rb->lcd_putsxy(3, 26, "[MODE+DOWN] Redo"); + rb->lcd_putsxy(3, 36, "[MODE+LEFT] Down a Level"); + rb->lcd_putsxy(3, 46, "[MODE+UP] Restart Level"); + rb->lcd_putsxy(3, 56, "[MODE+RIGHT] Up Level"); #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \ (CONFIG_KEYPAD == IRIVER_H300_PAD) - rb->lcd_putsxy(3, 6, "[STOP] Stop"); - rb->lcd_putsxy(3, 16, "[PLAY] Undo"); + rb->lcd_putsxy(3, 6, "[STOP] Quit"); + rb->lcd_putsxy(3, 16, "[REC] Undo"); + rb->lcd_putsxy(3, 26, "[MODE] Redo"); + rb->lcd_putsxy(3, 36, "[PLAY+DOWN] Down a Level"); + rb->lcd_putsxy(3, 46, "[PLAY] Restart Level"); + rb->lcd_putsxy(3, 56, "[PLAY+UP] Up a Level"); +#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \ + (CONFIG_KEYPAD == IPOD_3G_PAD) + rb->lcd_putsxy(3, 6, "[SELECT+MENU] Quit"); + rb->lcd_putsxy(3, 16, "[SELECT] Undo"); + rb->lcd_putsxy(3, 26, "[SELECT+PLAY] Redo"); + rb->lcd_putsxy(3, 36, "[SELECT+LEFT] Down a Level"); + rb->lcd_putsxy(3, 46, "[SELECT+RIGHT] Up a Level"); +#elif CONFIG_KEYPAD == IAUDIO_X5_PAD + rb->lcd_putsxy(3, 6, "[POWER] Quit"); + rb->lcd_putsxy(3, 16, "[SELECT] Undo"); rb->lcd_putsxy(3, 26, "[REC] Down a Level"); - rb->lcd_putsxy(3, 36, "[SELECT] Restart Level"); - rb->lcd_putsxy(3, 46, "[MODE] + Up a Level"); + rb->lcd_putsxy(3, 36, "[PLAY] Up Level"); +#elif CONFIG_KEYPAD == GIGABEAT_PAD + rb->lcd_putsxy(3, 6, "[A] Quit"); + rb->lcd_putsxy(3, 16, "[SELECT] Undo"); + rb->lcd_putsxy(3, 26, "[POWER] Redo"); + rb->lcd_putsxy(3, 36, "[MENU+DOWN] Down a Level"); + rb->lcd_putsxy(3, 46, "[MENU] Restart Level"); + rb->lcd_putsxy(3, 56, "[MENU+UP] Up Level"); +#elif CONFIG_KEYPAD == SANSA_E200_PAD + rb->lcd_putsxy(3, 6, "[POWER] Quit"); + rb->lcd_putsxy(3, 16, "[SELECT] Undo"); + rb->lcd_putsxy(3, 26, "[REC] Redo"); + rb->lcd_putsxy(3, 36, "[SELECT+DOWN] Down a Level"); + rb->lcd_putsxy(3, 46, "[SELECT+RIGHT] Restart Level"); + rb->lcd_putsxy(3, 56, "[SELECT+UP] Up Level"); +#elif CONFIG_KEYPAD == IRIVER_H10_PAD + rb->lcd_putsxy(3, 6, "[POWER] Quit"); + rb->lcd_putsxy(3, 16, "[REW] Undo"); + rb->lcd_putsxy(3, 26, "[FF] Redo"); + rb->lcd_putsxy(3, 36, "[PLAY+DOWN] Down a Level"); + rb->lcd_putsxy(3, 46, "[PLAY+RIGHT] Restart Level"); + rb->lcd_putsxy(3, 56, "[PLAY+UP] Up Level"); #endif rb->lcd_update(); - rb->sleep(HZ*2); + rb->button_get(false); + /* Display for 3 seconds or until keypress */ + for (i = 0; i < 60; i++) { + rb->sleep(HZ/20); + button = rb->button_get(false); + if (button && ((button & BUTTON_REL) != BUTTON_REL)) + break; + } rb->lcd_clear_display(); init_boards(); - if (read_levels(1) != 0) { - rb->splash(HZ*2, true, "Failed loading levels!"); + if (read_levels(1) != 0) return PLUGIN_OK; - } return sokoban_loop(); } diff --git a/docs/CREDITS b/docs/CREDITS index 43d02a5e8f..6840e3ef95 100644 --- a/docs/CREDITS +++ b/docs/CREDITS @@ -273,3 +273,4 @@ Takashi Obara Rene Peinthor Roan Horning Ben Keroack +Sean Morrisey