summaryrefslogtreecommitdiffstats
path: root/apps/plugins/sokoban.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/sokoban.c')
-rw-r--r--apps/plugins/sokoban.c1006
1 files changed, 491 insertions, 515 deletions
diff --git a/apps/plugins/sokoban.c b/apps/plugins/sokoban.c
index b88df504d3..7941e97585 100644
--- a/apps/plugins/sokoban.c
+++ b/apps/plugins/sokoban.c
@@ -29,16 +29,42 @@ extern const fb_data sokoban_tiles[];
#endif
#define SOKOBAN_TITLE "Sokoban"
-#define SOKOBAN_TITLE_FONT 2
#define LEVELS_FILE PLUGIN_DIR "/sokoban.levels"
#define ROWS 16
#define COLS 20
-/* Use all but 8k of the plugin buffer for board data */
#define SOKOBAN_LEVEL_SIZE (ROWS*COLS)
-#define MAX_BUFFERED_BOARDS (PLUGIN_BUFFER_SIZE - 0x3000)/SOKOBAN_LEVEL_SIZE
-#define MAX_UNDOS 5
+#define MAX_BUFFERED_BOARDS 500
+/* Use either all but 12k of the plugin buffer for board data
+ or just enough for MAX_BUFFERED_BOARDS, which ever is less */
+#if (PLUGIN_BUFFER_SIZE - 0x3000)/SOKOBAN_LEVEL_SIZE < MAX_BUFFERED_BOARDS
+#define NUM_BUFFERED_BOARDS (PLUGIN_BUFFER_SIZE - 0x3000)/SOKOBAN_LEVEL_SIZE
+#else
+#define NUM_BUFFERED_BOARDS MAX_BUFFERED_BOARDS
+#endif
+/* Use 4k plus remaining plugin buffer (-8k for prog) for undo, up to 32k */
+#if PLUGIN_BUFFER_SIZE - NUM_BUFFERED_BOARDS*SOKOBAN_LEVEL_SIZE - 0x2000 > \
+ 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 undo(void)
+static void get_delta(char direction, short *d_r, short *d_c)
{
- struct Undo *undo;
- int i = 0;
- short row, col;
-
- if (undo_info.count == 0)
- return;
-
- /* Update board info */
- undo = &undo_info.history[undo_info.current];
-
- rb->memcpy(&current_info.level, &undo->level, sizeof(undo->level));
- rb->memcpy(&current_info.player, &undo->location[0], sizeof(undo->location[0]));
-
- row = undo->location[0].row;
- col = undo->location[0].col;
- current_info.board[row][col] = '@';
-
- /* 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';
- }
- }
-
- /* Remove this undo from the list */
- if (undo_info.current == 0) {
- if (undo_info.count > 1)
- undo_info.current = MAX_UNDOS - 1;
- } else {
- undo_info.current--;
+ 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;
}
-
- undo_info.count--;
-
- return;
}
-static void add_undo(int button)
+static void undo(void)
{
- struct Undo *undo;
- int row, col, i;
- bool storable;
-
- if ((button != BUTTON_LEFT) && (button != BUTTON_RIGHT) &&
- (button != SOKOBAN_UP) && (button != SOKOBAN_DOWN))
+ 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 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;
- if (undo_info.count != 0) {
- if (undo_info.current < (MAX_UNDOS - 1))
- undo_info.current++;
- else
- undo_info.current = 0;
- }
+ undo = undo_info.history[undo_info.current];
- /* Make what follows more readable */
- undo = &undo_info.history[undo_info.current];
+ if (undo < SOKOBAN_MOVE_MIN)
+ undo_push = true;
- /* Store our level info */
- rb->memcpy(&undo->level, &current_info.level, sizeof(undo->level));
+ get_delta(undo, &d_r, &d_c);
- /* Store our player info */
- rb->memcpy(&undo->location[0], &current_info.player, sizeof(undo->location[0]));
+ r = current_info.player.row;
+ c = current_info.player.col;
- /* 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 */
+ /* Give the 3 spaces we're going to use better names */
+ space_cur = &current_info.board[r][c];
+ space_next = &current_info.board[r + d_r][c + d_c];
+ space_prev = &current_info.board[r - d_r][c - d_c];
- row = current_info.player.row;
- col = current_info.player.col;
+ /* Update board info */
+ 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--;
+
+ /* Move box off of next space... */
+ *space_next = (*space_next == '%' ? '.' : ' ');
+ /* ...and on to current space */
+ *space_cur = (*space_cur == '+' ? '%' : '$');
+
+ 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 position */
+ current_info.player.row -= d_r;
+ current_info.player.col -= d_c;
+
+ 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--;
- /* This must stay as _1_ because the first block (0) is the player */
- for (i = 1; i < 3; i++) {
- storable = true;
+ undo_info.count--;
- switch (button) {
- case BUTTON_LEFT:
- col--;
- if (col < 0)
- storable = false;
- break;
+ return;
+}
- case BUTTON_RIGHT:
- col++;
- if (col >= COLS)
- storable = false;
- break;
+static void add_undo(char undo)
+{
+ /* Wrap around if MAX_UNDOS exceeded */
+ if (undo_info.current < (MAX_UNDOS - 1))
+ undo_info.current++;
+ else
+ undo_info.current = 0;
- case SOKOBAN_UP:
- row--;
- if (row < 0)
- storable = false;
- break;
+ undo_info.history[undo_info.current] = undo;
- case SOKOBAN_DOWN:
- row++;
- if (row >= ROWS)
- storable = false;
- break;
+ if (undo_info.count < MAX_UNDOS)
+ undo_info.count++;
+}
- default:
- return;
- }
+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 = &current_info.board[r][c];
+ space_next = &current_info.board[r + d_r][c + d_c];
+ space_beyond = &current_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;
+ }
- 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';
- }
+ /* 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;
+}
- if (undo_info.count < MAX_UNDOS)
- undo_info.count++;
+#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);
- }
+ 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);
+#else
+ /* Free boxes are not filled in */
+ rb->lcd_drawrect(c, b, MAGNIFY, MAGNIFY);
#endif
break;
- case '.': /* this is a home location */
+ case '*':
+ case '%': /* this is a box on a goal */
+
#if LCD_DEPTH >= 2
- rb->lcd_bitmap_part( sokoban_tiles, 0, 4*magnify, magnify,
- c, b, magnify, magnify );
+ rb->lcd_bitmap_part(sokoban_tiles, 0, 3*MAGNIFY, MAGNIFY,
+ c, b, MAGNIFY, MAGNIFY );
#else
- rb->lcd_drawrect(c+(magnify/2)-1, b+(magnify/2)-1, magnify/2,
- magnify/2);
+ 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 box */
+ case '.': /* this is a goal */
#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, 4*MAGNIFY, MAGNIFY,
+ c, b, MAGNIFY, MAGNIFY);
#else
- rb->lcd_drawrect(c, b, magnify, magnify); /* Free boxes are not filled in */
+ 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
}
}
}
+#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_putsxy(4+STAT_CENTER, STAT_POS+4, "Level");
rb->snprintf(s, sizeof(s), "%d", current_info.level.level);
- rb->lcd_putsxy(LCD_WIDTH-STAT_WIDTH+4, 22, s);
+ 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(LCD_WIDTH-STAT_WIDTH+4, 54, s);
+ 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(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_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();
}