diff options
author | Antoine Cellerier <dionoea@videolan.org> | 2007-06-28 20:45:00 +0000 |
---|---|---|
committer | Antoine Cellerier <dionoea@videolan.org> | 2007-06-28 20:45:00 +0000 |
commit | f91d06de7bf724e8e0aa580c18efa3eb345f88f9 (patch) | |
tree | 29c6446a4556cd074dbee3c3d97cdaae207ff228 /apps/plugins/sokoban.c | |
parent | 7a1108227b67fb62f3d3447d795447b5b631ed32 (diff) | |
download | rockbox-f91d06de7bf724e8e0aa580c18efa3eb345f88f9.tar.gz rockbox-f91d06de7bf724e8e0aa580c18efa3eb345f88f9.zip |
Apply FS #6702: More Sokoban Improvements.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13731 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/sokoban.c')
-rw-r--r-- | apps/plugins/sokoban.c | 1355 |
1 files changed, 880 insertions, 475 deletions
diff --git a/apps/plugins/sokoban.c b/apps/plugins/sokoban.c index 40a2f549ad..976f552dbd 100644 --- a/apps/plugins/sokoban.c +++ b/apps/plugins/sokoban.c | |||
@@ -10,6 +10,7 @@ | |||
10 | * Copyright (C) 2002 Eric Linenberg | 10 | * Copyright (C) 2002 Eric Linenberg |
11 | * February 2003: Robert Hak performs a cleanup/rewrite/feature addition. | 11 | * February 2003: Robert Hak performs a cleanup/rewrite/feature addition. |
12 | * Eric smiles. Bjorn cries. Linus say 'huh?'. | 12 | * Eric smiles. Bjorn cries. Linus say 'huh?'. |
13 | * March 2007: Sean Morrisey performs a major rewrite/feature addition. | ||
13 | * | 14 | * |
14 | * All files in this archive are subject to the GNU General Public License. | 15 | * All files in this archive are subject to the GNU General Public License. |
15 | * See the file COPYING in the source tree root for full license agreement. | 16 | * See the file COPYING in the source tree root for full license agreement. |
@@ -19,95 +20,126 @@ | |||
19 | * | 20 | * |
20 | ****************************************************************************/ | 21 | ****************************************************************************/ |
21 | #include "plugin.h" | 22 | #include "plugin.h" |
23 | #include "lib/playback_control.h" | ||
22 | 24 | ||
23 | #ifdef HAVE_LCD_BITMAP | 25 | #ifdef HAVE_LCD_BITMAP |
24 | 26 | ||
25 | PLUGIN_HEADER | 27 | PLUGIN_HEADER |
26 | 28 | ||
27 | #if LCD_DEPTH >= 2 | 29 | #if LCD_DEPTH >= 2 && ((LCD_HEIGHT >= 96 && LCD_WIDTH >= 152) || \ |
30 | (LCD_HEIGHT >= 121 && LCD_WIDTH >= 120)) | ||
28 | extern const fb_data sokoban_tiles[]; | 31 | extern const fb_data sokoban_tiles[]; |
29 | #endif | 32 | #endif |
30 | 33 | ||
31 | #define SOKOBAN_TITLE "Sokoban" | 34 | #define SOKOBAN_TITLE "Sokoban" |
32 | 35 | ||
33 | #define LEVELS_FILE PLUGIN_DIR "/sokoban.levels" | 36 | #define SOKOBAN_LEVELS_FILE PLUGIN_DIR "/sokobanlevels.sok" |
37 | #define SOKOBAN_SAVE_FILE PLUGIN_DIR "/sokobansave.sok" | ||
34 | 38 | ||
35 | #define ROWS 16 | 39 | /* Magnify is the number of pixels for each block. |
36 | #define COLS 20 | 40 | * Set dynamically so all targets can support levels |
37 | #define SOKOBAN_LEVEL_SIZE (ROWS*COLS) | 41 | * that fill their entire screen, less the stat box. |
38 | #define MAX_BUFFERED_BOARDS 500 | 42 | * 16 rows & 20 cols minimum */ |
39 | /* Use either all but 12k of the plugin buffer for board data | 43 | #if (LCD_HEIGHT >= 224) && (LCD_WIDTH >= 320) |
40 | or just enough for MAX_BUFFERED_BOARDS, which ever is less */ | 44 | #define MAGNIFY 14 |
41 | #if (PLUGIN_BUFFER_SIZE - 0x3000)/SOKOBAN_LEVEL_SIZE < MAX_BUFFERED_BOARDS | 45 | #define ROWS (LCD_HEIGHT/MAGNIFY) |
42 | #define NUM_BUFFERED_BOARDS (PLUGIN_BUFFER_SIZE - 0x3000)/SOKOBAN_LEVEL_SIZE | 46 | #define COLS ((LCD_WIDTH-40)/MAGNIFY) |
47 | #elif (LCD_HEIGHT >= 249) && (LCD_WIDTH >= 280) | ||
48 | #define MAGNIFY 14 | ||
49 | #define ROWS ((LCD_HEIGHT-25)/MAGNIFY) | ||
50 | #define COLS (LCD_WIDTH/MAGNIFY) | ||
51 | #elif (LCD_HEIGHT >= 144) && (LCD_WIDTH >= 220) | ||
52 | #define MAGNIFY 9 | ||
53 | #define ROWS (LCD_HEIGHT/MAGNIFY) | ||
54 | #define COLS ((LCD_WIDTH-40)/MAGNIFY) | ||
55 | #elif (LCD_HEIGHT >= 169) && (LCD_WIDTH+4 >= 180) /* plus 4 for sansa */ | ||
56 | #define MAGNIFY 9 | ||
57 | #define ROWS ((LCD_HEIGHT-25)/MAGNIFY) | ||
58 | #define COLS ((LCD_WIDTH+4)/MAGNIFY) | ||
59 | #elif (LCD_HEIGHT >= 96) && (LCD_WIDTH >= 160) | ||
60 | #define MAGNIFY 6 | ||
61 | #define ROWS (LCD_HEIGHT/MAGNIFY) | ||
62 | #define COLS ((LCD_WIDTH-40)/MAGNIFY) | ||
63 | #elif (LCD_HEIGHT >= 121) && (LCD_WIDTH >= 120) | ||
64 | #define MAGNIFY 6 | ||
65 | #define ROWS ((LCD_HEIGHT-25)/MAGNIFY) | ||
66 | #define COLS (LCD_WIDTH/MAGNIFY) | ||
43 | #else | 67 | #else |
44 | #define NUM_BUFFERED_BOARDS MAX_BUFFERED_BOARDS | 68 | #define MAGNIFY 4 |
69 | #define ROWS 16 | ||
70 | #define COLS 20 | ||
45 | #endif | 71 | #endif |
46 | /* Use 4k plus remaining plugin buffer (-8k for prog) for undo, up to 32k */ | 72 | |
47 | #if PLUGIN_BUFFER_SIZE - NUM_BUFFERED_BOARDS*SOKOBAN_LEVEL_SIZE - 0x2000 > \ | 73 | /* Use either all but 16k of the plugin buffer for level data |
48 | 0x7FFF | 74 | * or 128k, which ever is less */ |
49 | #define MAX_UNDOS 0x7FFF | 75 | #if PLUGIN_BUFFER_SIZE - 0x4000 < 0x20000 |
76 | #define MAX_LEVEL_DATA (PLUGIN_BUFFER_SIZE - 0x4000) | ||
77 | #else | ||
78 | #define MAX_LEVEL_DATA 0x20000 | ||
79 | #endif | ||
80 | |||
81 | /* Number of levels for which to allocate buffer indexes */ | ||
82 | #define MAX_LEVELS MAX_LEVEL_DATA/70 | ||
83 | |||
84 | /* Use 4k plus remaining plugin buffer (-12k for prog) for undo, up to 64k */ | ||
85 | #if PLUGIN_BUFFER_SIZE - MAX_LEVEL_DATA - 0x3000 > 0x10000 | ||
86 | #define MAX_UNDOS 0x10000 | ||
50 | #else | 87 | #else |
51 | #define MAX_UNDOS PLUGIN_BUFFER_SIZE - \ | 88 | #define MAX_UNDOS (PLUGIN_BUFFER_SIZE - MAX_LEVEL_DATA - 0x3000) |
52 | NUM_BUFFERED_BOARDS*SOKOBAN_LEVEL_SIZE - 0x2000 | ||
53 | #endif | 89 | #endif |
54 | 90 | ||
55 | /* Move/push definitions for undo */ | 91 | /* Move/push definitions for undo */ |
56 | enum { | 92 | #define SOKOBAN_PUSH_LEFT 'L' |
57 | SOKOBAN_PUSH_LEFT, | 93 | #define SOKOBAN_PUSH_RIGHT 'R' |
58 | SOKOBAN_PUSH_RIGHT, | 94 | #define SOKOBAN_PUSH_UP 'U' |
59 | SOKOBAN_PUSH_UP, | 95 | #define SOKOBAN_PUSH_DOWN 'D' |
60 | SOKOBAN_PUSH_DOWN, | 96 | #define SOKOBAN_MOVE_LEFT 'l' |
61 | SOKOBAN_MOVE_LEFT, | 97 | #define SOKOBAN_MOVE_RIGHT 'r' |
62 | SOKOBAN_MOVE_RIGHT, | 98 | #define SOKOBAN_MOVE_UP 'u' |
63 | SOKOBAN_MOVE_UP, | 99 | #define SOKOBAN_MOVE_DOWN 'd' |
64 | SOKOBAN_MOVE_DOWN | 100 | |
65 | }; | 101 | #define SOKOBAN_MOVE_DIFF (SOKOBAN_MOVE_LEFT-SOKOBAN_PUSH_LEFT) |
66 | #define SOKOBAN_MOVE_DIFF (SOKOBAN_MOVE_LEFT-SOKOBAN_PUSH_LEFT) | 102 | #define SOKOBAN_MOVE_MIN SOKOBAN_MOVE_DOWN |
67 | #define SOKOBAN_MOVE_MIN SOKOBAN_MOVE_LEFT | ||
68 | 103 | ||
69 | /* variable button definitions */ | 104 | /* variable button definitions */ |
70 | #if CONFIG_KEYPAD == RECORDER_PAD | 105 | #if (CONFIG_KEYPAD == RECORDER_PAD) || \ |
106 | (CONFIG_KEYPAD == ARCHOS_AV300_PAD) | ||
71 | #define SOKOBAN_UP BUTTON_UP | 107 | #define SOKOBAN_UP BUTTON_UP |
72 | #define SOKOBAN_DOWN BUTTON_DOWN | 108 | #define SOKOBAN_DOWN BUTTON_DOWN |
73 | #define SOKOBAN_QUIT BUTTON_OFF | 109 | #define SOKOBAN_MENU BUTTON_OFF |
74 | #define SOKOBAN_UNDO BUTTON_ON | 110 | #define SOKOBAN_UNDO BUTTON_ON |
75 | #define SOKOBAN_REDO BUTTON_PLAY | 111 | #define SOKOBAN_REDO BUTTON_PLAY |
76 | #define SOKOBAN_LEVEL_UP BUTTON_F3 | ||
77 | #define SOKOBAN_LEVEL_DOWN BUTTON_F1 | 112 | #define SOKOBAN_LEVEL_DOWN BUTTON_F1 |
78 | #define SOKOBAN_LEVEL_REPEAT BUTTON_F2 | 113 | #define SOKOBAN_LEVEL_REPEAT BUTTON_F2 |
79 | |||
80 | #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD | ||
81 | #define SOKOBAN_UP BUTTON_UP | ||
82 | #define SOKOBAN_DOWN BUTTON_DOWN | ||
83 | #define SOKOBAN_QUIT BUTTON_OFF | ||
84 | #define SOKOBAN_UNDO BUTTON_ON | ||
85 | #define SOKOBAN_REDO BUTTON_PLAY | ||
86 | #define SOKOBAN_LEVEL_UP BUTTON_F3 | 114 | #define SOKOBAN_LEVEL_UP BUTTON_F3 |
87 | #define SOKOBAN_LEVEL_DOWN BUTTON_F1 | 115 | #define BUTTON_SAVE BUTTON_ON |
88 | #define SOKOBAN_LEVEL_REPEAT BUTTON_F2 | 116 | #define BUTTON_SAVE_NAME "ON" |
89 | 117 | ||
90 | #elif CONFIG_KEYPAD == ONDIO_PAD | 118 | #elif CONFIG_KEYPAD == ONDIO_PAD |
91 | #define SOKOBAN_UP BUTTON_UP | 119 | #define SOKOBAN_UP BUTTON_UP |
92 | #define SOKOBAN_DOWN BUTTON_DOWN | 120 | #define SOKOBAN_DOWN BUTTON_DOWN |
93 | #define SOKOBAN_QUIT BUTTON_OFF | 121 | #define SOKOBAN_MENU BUTTON_OFF |
94 | #define SOKOBAN_UNDO_PRE BUTTON_MENU | 122 | #define SOKOBAN_UNDO_PRE BUTTON_MENU |
95 | #define SOKOBAN_UNDO (BUTTON_MENU | BUTTON_REL) | 123 | #define SOKOBAN_UNDO (BUTTON_MENU | BUTTON_REL) |
96 | #define SOKOBAN_REDO (BUTTON_MENU | BUTTON_DOWN) | 124 | #define SOKOBAN_REDO (BUTTON_MENU | BUTTON_DOWN) |
97 | #define SOKOBAN_LEVEL_UP (BUTTON_MENU | BUTTON_RIGHT) | ||
98 | #define SOKOBAN_LEVEL_DOWN (BUTTON_MENU | BUTTON_LEFT) | 125 | #define SOKOBAN_LEVEL_DOWN (BUTTON_MENU | BUTTON_LEFT) |
99 | #define SOKOBAN_LEVEL_REPEAT (BUTTON_MENU | BUTTON_UP) | 126 | #define SOKOBAN_LEVEL_REPEAT (BUTTON_MENU | BUTTON_UP) |
127 | #define SOKOBAN_LEVEL_UP (BUTTON_MENU | BUTTON_RIGHT) | ||
128 | #define BUTTON_SAVE BUTTON_MENU | ||
129 | #define BUTTON_SAVE_NAME "MENU" | ||
100 | 130 | ||
101 | #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \ | 131 | #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \ |
102 | (CONFIG_KEYPAD == IRIVER_H300_PAD) | 132 | (CONFIG_KEYPAD == IRIVER_H300_PAD) |
103 | #define SOKOBAN_UP BUTTON_UP | 133 | #define SOKOBAN_UP BUTTON_UP |
104 | #define SOKOBAN_DOWN BUTTON_DOWN | 134 | #define SOKOBAN_DOWN BUTTON_DOWN |
105 | #define SOKOBAN_QUIT BUTTON_OFF | 135 | #define SOKOBAN_MENU BUTTON_OFF |
106 | #define SOKOBAN_UNDO BUTTON_REC | 136 | #define SOKOBAN_UNDO BUTTON_REC |
107 | #define SOKOBAN_REDO BUTTON_MODE | 137 | #define SOKOBAN_REDO BUTTON_MODE |
108 | #define SOKOBAN_LEVEL_UP (BUTTON_ON | BUTTON_UP) | ||
109 | #define SOKOBAN_LEVEL_DOWN (BUTTON_ON | BUTTON_DOWN) | 138 | #define SOKOBAN_LEVEL_DOWN (BUTTON_ON | BUTTON_DOWN) |
110 | #define SOKOBAN_LEVEL_REPEAT BUTTON_ON | 139 | #define SOKOBAN_LEVEL_REPEAT BUTTON_ON |
140 | #define SOKOBAN_LEVEL_UP (BUTTON_ON | BUTTON_UP) | ||
141 | #define BUTTON_SAVE BUTTON_MODE | ||
142 | #define BUTTON_SAVE_NAME "MODE" | ||
111 | 143 | ||
112 | #define SOKOBAN_RC_QUIT BUTTON_RC_STOP | 144 | #define SOKOBAN_RC_QUIT BUTTON_RC_STOP |
113 | 145 | ||
@@ -115,65 +147,70 @@ enum { | |||
115 | (CONFIG_KEYPAD == IPOD_3G_PAD) | 147 | (CONFIG_KEYPAD == IPOD_3G_PAD) |
116 | #define SOKOBAN_UP BUTTON_MENU | 148 | #define SOKOBAN_UP BUTTON_MENU |
117 | #define SOKOBAN_DOWN BUTTON_PLAY | 149 | #define SOKOBAN_DOWN BUTTON_PLAY |
118 | #define SOKOBAN_QUIT (BUTTON_SELECT | BUTTON_MENU) | 150 | #define SOKOBAN_MENU (BUTTON_SELECT | BUTTON_MENU) |
119 | #define SOKOBAN_UNDO_PRE BUTTON_SELECT | 151 | #define SOKOBAN_UNDO_PRE BUTTON_SELECT |
120 | #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL) | 152 | #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL) |
121 | #define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_PLAY) | 153 | #define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_PLAY) |
122 | #define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_RIGHT) | ||
123 | #define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_LEFT) | 154 | #define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_LEFT) |
155 | #define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_RIGHT) | ||
156 | #define BUTTON_SAVE BUTTON_SELECT | ||
157 | #define BUTTON_SAVE_NAME "SELECT" | ||
124 | 158 | ||
125 | /* fixme: if/when simultaneous button presses work for X5, | 159 | /* FIXME: if/when simultaneous button presses work for X5, |
126 | add redo & level repeat */ | 160 | * add redo & level repeat */ |
127 | #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD) | 161 | #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD |
128 | #define SOKOBAN_UP BUTTON_UP | 162 | #define SOKOBAN_UP BUTTON_UP |
129 | #define SOKOBAN_DOWN BUTTON_DOWN | 163 | #define SOKOBAN_DOWN BUTTON_DOWN |
130 | #define SOKOBAN_QUIT BUTTON_POWER | 164 | #define SOKOBAN_MENU BUTTON_POWER |
131 | #define SOKOBAN_UNDO_PRE BUTTON_SELECT | 165 | #define SOKOBAN_UNDO_PRE BUTTON_SELECT |
132 | #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL) | 166 | #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL) |
133 | #define SOKOBAN_LEVEL_UP BUTTON_PLAY | ||
134 | #define SOKOBAN_LEVEL_DOWN BUTTON_REC | 167 | #define SOKOBAN_LEVEL_DOWN BUTTON_REC |
168 | #define SOKOBAN_LEVEL_UP BUTTON_PLAY | ||
169 | #define BUTTON_SAVE BUTTON_SELECT | ||
170 | #define BUTTON_SAVE_NAME "SELECT" | ||
135 | 171 | ||
136 | #elif (CONFIG_KEYPAD == GIGABEAT_PAD) | 172 | #elif CONFIG_KEYPAD == IRIVER_H10_PAD |
173 | #define SOKOBAN_UP BUTTON_SCROLL_UP | ||
174 | #define SOKOBAN_DOWN BUTTON_SCROLL_DOWN | ||
175 | #define SOKOBAN_MENU BUTTON_POWER | ||
176 | #define SOKOBAN_UNDO_PRE BUTTON_REW | ||
177 | #define SOKOBAN_UNDO (BUTTON_REW | BUTTON_REL) | ||
178 | #define SOKOBAN_REDO BUTTON_FF | ||
179 | #define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_SCROLL_DOWN) | ||
180 | #define SOKOBAN_LEVEL_REPEAT (BUTTON_PLAY | BUTTON_RIGHT) | ||
181 | #define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_SCROLL_UP) | ||
182 | #define BUTTON_SAVE BUTTON_PLAY | ||
183 | #define BUTTON_SAVE_NAME "PLAY" | ||
184 | |||
185 | #elif CONFIG_KEYPAD == GIGABEAT_PAD | ||
137 | #define SOKOBAN_UP BUTTON_UP | 186 | #define SOKOBAN_UP BUTTON_UP |
138 | #define SOKOBAN_DOWN BUTTON_DOWN | 187 | #define SOKOBAN_DOWN BUTTON_DOWN |
139 | #define SOKOBAN_QUIT BUTTON_POWER | 188 | #define SOKOBAN_MENU BUTTON_POWER |
140 | #define SOKOBAN_UNDO BUTTON_SELECT | 189 | #define SOKOBAN_UNDO BUTTON_SELECT |
141 | #define SOKOBAN_REDO BUTTON_A | 190 | #define SOKOBAN_REDO BUTTON_A |
142 | #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP | ||
143 | #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN | 191 | #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN |
144 | #define SOKOBAN_LEVEL_REPEAT BUTTON_MENU | 192 | #define SOKOBAN_LEVEL_REPEAT BUTTON_MENU |
193 | #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP | ||
194 | #define BUTTON_SAVE BUTTON_SELECT | ||
195 | #define BUTTON_SAVE_NAME "SELECT" | ||
145 | 196 | ||
146 | #elif (CONFIG_KEYPAD == SANSA_E200_PAD) | 197 | #elif CONFIG_KEYPAD == SANSA_E200_PAD |
147 | #define SOKOBAN_UP BUTTON_UP | 198 | #define SOKOBAN_UP BUTTON_UP |
148 | #define SOKOBAN_DOWN BUTTON_DOWN | 199 | #define SOKOBAN_DOWN BUTTON_DOWN |
149 | #define SOKOBAN_QUIT BUTTON_POWER | 200 | #define SOKOBAN_MENU BUTTON_POWER |
150 | #define SOKOBAN_UNDO_PRE BUTTON_SELECT | 201 | #define SOKOBAN_UNDO_PRE BUTTON_SELECT |
151 | #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL) | 202 | #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL) |
152 | #define SOKOBAN_REDO BUTTON_REC | 203 | #define SOKOBAN_REDO BUTTON_REC |
153 | #define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_UP) | ||
154 | #define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_DOWN) | 204 | #define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_DOWN) |
155 | #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT) | 205 | #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT) |
156 | 206 | #define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_UP) | |
157 | #elif (CONFIG_KEYPAD == IRIVER_H10_PAD) | 207 | #define BUTTON_SAVE BUTTON_SELECT |
158 | #define SOKOBAN_UP BUTTON_SCROLL_UP | 208 | #define BUTTON_SAVE_NAME "SELECT" |
159 | #define SOKOBAN_DOWN BUTTON_SCROLL_DOWN | ||
160 | #define SOKOBAN_QUIT BUTTON_POWER | ||
161 | #define SOKOBAN_UNDO_PRE BUTTON_REW | ||
162 | #define SOKOBAN_UNDO (BUTTON_REW | BUTTON_REL) | ||
163 | #define SOKOBAN_REDO BUTTON_FF | ||
164 | #define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_SCROLL_UP) | ||
165 | #define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_SCROLL_DOWN) | ||
166 | #define SOKOBAN_LEVEL_REPEAT (BUTTON_PLAY | BUTTON_RIGHT) | ||
167 | 209 | ||
168 | #endif | 210 | #endif |
169 | 211 | ||
170 | #ifdef HAVE_LCD_COLOR | 212 | #define SOKOBAN_FONT FONT_SYSFIXED |
171 | /* Background color. Default Rockbox light blue. */ | ||
172 | #define BG_COLOR LCD_RGBPACK(181, 199, 231) | ||
173 | 213 | ||
174 | #elif LCD_DEPTH >= 2 | ||
175 | #define MEDIUM_GRAY LCD_BRIGHTNESS(127) | ||
176 | #endif | ||
177 | 214 | ||
178 | /* The Location, Undo and LevelInfo structs are OO-flavored. | 215 | /* The Location, Undo and LevelInfo structs are OO-flavored. |
179 | * (oooh!-flavored as Schnueff puts it.) It makes more you have to know, | 216 | * (oooh!-flavored as Schnueff puts it.) It makes more you have to know, |
@@ -181,10 +218,12 @@ enum { | |||
181 | 218 | ||
182 | /* Level data & stats */ | 219 | /* Level data & stats */ |
183 | struct LevelInfo { | 220 | struct LevelInfo { |
184 | short level; | 221 | short index; /* Level index (level number - 1) */ |
185 | short moves; | 222 | int moves; /* Moves & pushes for the stats */ |
186 | short pushes; | 223 | int pushes; |
187 | short boxes_to_go; | 224 | short boxes_to_go; /* Number of unplaced boxes remaining in level */ |
225 | short height; /* Height & width for centering level display */ | ||
226 | short width; | ||
188 | }; | 227 | }; |
189 | 228 | ||
190 | struct Location { | 229 | struct Location { |
@@ -192,39 +231,42 @@ struct Location { | |||
192 | short col; | 231 | short col; |
193 | }; | 232 | }; |
194 | 233 | ||
195 | struct Board { | ||
196 | char spaces[ROWS][COLS]; | ||
197 | }; | ||
198 | |||
199 | /* Our full undo history */ | 234 | /* Our full undo history */ |
200 | static struct UndoInfo { | 235 | static struct UndoInfo { |
201 | short count; /* How many undos are left */ | 236 | int count; /* How many undos have been done */ |
202 | short current; /* Which history is the current undo */ | 237 | int current; /* Which history is the current undo */ |
203 | short max; /* Which history is the max redoable */ | 238 | int max; /* Which history is the max redoable */ |
204 | char history[MAX_UNDOS]; | 239 | char history[MAX_UNDOS]; |
205 | } undo_info; | 240 | } undo_info; |
206 | 241 | ||
207 | /* Our playing board */ | 242 | /* Our playing board */ |
208 | static struct BoardInfo { | 243 | static struct BoardInfo { |
209 | char board[ROWS][COLS]; | 244 | char board[ROWS][COLS]; /* The current board data */ |
210 | struct LevelInfo level; | 245 | struct LevelInfo level; /* Level data & stats */ |
211 | struct Location player; | 246 | struct Location player; /* Where the player is */ |
212 | int max_level; /* How many levels do we have? */ | 247 | int max_level; /* The number of levels we have */ |
213 | int loaded_level; /* Which level is in memory */ | ||
214 | } current_info; | 248 | } current_info; |
215 | 249 | ||
216 | static struct BufferedBoards { | 250 | static struct BufferedBoards { |
217 | struct Board levels[NUM_BUFFERED_BOARDS]; | 251 | char filename[MAX_PATH]; /* Filename of the levelset we're using */ |
218 | int low; | 252 | char data[MAX_LEVEL_DATA]; /* Buffered level data */ |
253 | int index[MAX_LEVELS + 1]; /* Where each buffered board begins & ends */ | ||
254 | int start; /* Index of first buffered board */ | ||
255 | int end; /* Index of last buffered board */ | ||
256 | short prebuffered_boards; /* Number of boards before current to store */ | ||
219 | } buffered_boards; | 257 | } buffered_boards; |
220 | 258 | ||
259 | |||
221 | static struct plugin_api* rb; | 260 | static struct plugin_api* rb; |
222 | 261 | ||
262 | static char buf[ROWS*(COLS + 1)]; /* Enough for a whole board or a filename */ | ||
263 | |||
264 | |||
223 | static void init_undo(void) | 265 | static void init_undo(void) |
224 | { | 266 | { |
225 | undo_info.count = 0; | 267 | undo_info.count = 0; |
226 | undo_info.current = -1; | 268 | undo_info.current = 0; |
227 | undo_info.max = -1; | 269 | undo_info.max = 0; |
228 | } | 270 | } |
229 | 271 | ||
230 | static void get_delta(char direction, short *d_r, short *d_c) | 272 | static void get_delta(char direction, short *d_r, short *d_c) |
@@ -261,9 +303,17 @@ static void undo(void) | |||
261 | bool undo_push = false; | 303 | bool undo_push = false; |
262 | 304 | ||
263 | /* If no more undos or we've wrapped all the way around, quit */ | 305 | /* If no more undos or we've wrapped all the way around, quit */ |
264 | if (undo_info.count == 0 || undo_info.current-1 == undo_info.max) | 306 | if (undo_info.count == 0 || undo_info.current - 1 == undo_info.max) |
265 | return; | 307 | return; |
266 | 308 | ||
309 | /* Move to previous undo in the list */ | ||
310 | if (undo_info.current == 0 && undo_info.count > 1) | ||
311 | undo_info.current = MAX_UNDOS - 1; | ||
312 | else | ||
313 | undo_info.current--; | ||
314 | |||
315 | undo_info.count--; | ||
316 | |||
267 | undo = undo_info.history[undo_info.current]; | 317 | undo = undo_info.history[undo_info.current]; |
268 | 318 | ||
269 | if (undo < SOKOBAN_MOVE_MIN) | 319 | if (undo < SOKOBAN_MOVE_MIN) |
@@ -281,17 +331,17 @@ static void undo(void) | |||
281 | 331 | ||
282 | /* Update board info */ | 332 | /* Update board info */ |
283 | if (undo_push) { | 333 | if (undo_push) { |
284 | /* Moving box from goal to blank */ | 334 | /* Moving box from goal to floor */ |
285 | if (*space_next == '%' && *space_cur == '@') | 335 | if (*space_next == '*' && *space_cur == '@') |
286 | current_info.level.boxes_to_go++; | 336 | current_info.level.boxes_to_go++; |
287 | /* Moving box from blank to goal */ | 337 | /* Moving box from floor to goal */ |
288 | else if (*space_next == '$' && *space_cur == '+') | 338 | else if (*space_next == '$' && *space_cur == '+') |
289 | current_info.level.boxes_to_go--; | 339 | current_info.level.boxes_to_go--; |
290 | 340 | ||
291 | /* Move box off of next space... */ | 341 | /* Move box off of next space... */ |
292 | *space_next = (*space_next == '%' ? '.' : ' '); | 342 | *space_next = (*space_next == '*' ? '.' : ' '); |
293 | /* ...and on to current space */ | 343 | /* ...and on to current space */ |
294 | *space_cur = (*space_cur == '+' ? '%' : '$'); | 344 | *space_cur = (*space_cur == '+' ? '*' : '$'); |
295 | 345 | ||
296 | current_info.level.pushes--; | 346 | current_info.level.pushes--; |
297 | } else | 347 | } else |
@@ -306,27 +356,19 @@ static void undo(void) | |||
306 | 356 | ||
307 | current_info.level.moves--; | 357 | current_info.level.moves--; |
308 | 358 | ||
309 | /* Move to previous undo in the list */ | ||
310 | if (undo_info.current == 0 && undo_info.count > 1) | ||
311 | undo_info.current = MAX_UNDOS - 1; | ||
312 | else | ||
313 | undo_info.current--; | ||
314 | |||
315 | undo_info.count--; | ||
316 | |||
317 | return; | 359 | return; |
318 | } | 360 | } |
319 | 361 | ||
320 | static void add_undo(char undo) | 362 | static void add_undo(char undo) |
321 | { | 363 | { |
364 | undo_info.history[undo_info.current] = undo; | ||
365 | |||
322 | /* Wrap around if MAX_UNDOS exceeded */ | 366 | /* Wrap around if MAX_UNDOS exceeded */ |
323 | if (undo_info.current < (MAX_UNDOS - 1)) | 367 | if (undo_info.current < (MAX_UNDOS - 1)) |
324 | undo_info.current++; | 368 | undo_info.current++; |
325 | else | 369 | else |
326 | undo_info.current = 0; | 370 | undo_info.current = 0; |
327 | 371 | ||
328 | undo_info.history[undo_info.current] = undo; | ||
329 | |||
330 | if (undo_info.count < MAX_UNDOS) | 372 | if (undo_info.count < MAX_UNDOS) |
331 | undo_info.count++; | 373 | undo_info.count++; |
332 | } | 374 | } |
@@ -353,34 +395,37 @@ static bool move(char direction, bool redo) | |||
353 | space_next = ¤t_info.board[r + d_r][c + d_c]; | 395 | space_next = ¤t_info.board[r + d_r][c + d_c]; |
354 | space_beyond = ¤t_info.board[r + 2*d_r][c + 2*d_c]; | 396 | space_beyond = ¤t_info.board[r + 2*d_r][c + 2*d_c]; |
355 | 397 | ||
356 | if (*space_next == '$' || *space_next == '%') { | 398 | if (*space_next == '$' || *space_next == '*') { |
357 | /* Change direction from move to push for undo */ | 399 | /* Change direction from move to push for undo */ |
358 | if (direction >= SOKOBAN_MOVE_MIN) | 400 | if (direction >= SOKOBAN_MOVE_MIN) |
359 | direction -= SOKOBAN_MOVE_DIFF; | 401 | direction -= SOKOBAN_MOVE_DIFF; |
360 | push = true; | 402 | push = true; |
361 | } | 403 | } |
404 | else if (direction < SOKOBAN_MOVE_MIN) | ||
405 | /* Change back to move if redo/solution playback push is invalid */ | ||
406 | direction += SOKOBAN_MOVE_DIFF; | ||
362 | 407 | ||
363 | /* Update board info */ | 408 | /* Update board info */ |
364 | if (push) { | 409 | if (push) { |
365 | /* Moving box from goal to blank */ | 410 | /* Moving box from goal to floor */ |
366 | if (*space_next == '%' && *space_beyond == ' ') | 411 | if (*space_next == '*' && *space_beyond == ' ') |
367 | current_info.level.boxes_to_go++; | 412 | current_info.level.boxes_to_go++; |
368 | /* Moving box from blank to goal */ | 413 | /* Moving box from floor to goal */ |
369 | else if (*space_next == '$' && *space_beyond == '.') | 414 | else if (*space_next == '$' && *space_beyond == '.') |
370 | current_info.level.boxes_to_go--; | 415 | current_info.level.boxes_to_go--; |
371 | /* Check for illegal move */ | 416 | /* Check for invalid move */ |
372 | else if (*space_beyond != '.' && *space_beyond != ' ') | 417 | else if (*space_beyond != '.' && *space_beyond != ' ') |
373 | return false; | 418 | return false; |
374 | 419 | ||
375 | /* Move player onto next space */ | 420 | /* Move player onto next space */ |
376 | *space_next = (*space_next == '%' ? '+' : '@'); | 421 | *space_next = (*space_next == '*' ? '+' : '@'); |
377 | /* Move box onto space beyond next */ | 422 | /* Move box onto space beyond next */ |
378 | *space_beyond = (*space_beyond == '.' ? '%' : '$'); | 423 | *space_beyond = (*space_beyond == '.' ? '*' : '$'); |
379 | 424 | ||
380 | current_info.level.pushes++; | 425 | current_info.level.pushes++; |
381 | } else { | 426 | } else { |
382 | /* Check for illegal move */ | 427 | /* Check for invalid move */ |
383 | if (*space_next == '#' || *space_next == 'X') | 428 | if (*space_next != '.' && *space_next != ' ') |
384 | return false; | 429 | return false; |
385 | 430 | ||
386 | /* Move player onto next space */ | 431 | /* Move player onto next space */ |
@@ -396,19 +441,19 @@ static bool move(char direction, bool redo) | |||
396 | current_info.level.moves++; | 441 | current_info.level.moves++; |
397 | 442 | ||
398 | /* Update undo_info.max to current on every normal move, | 443 | /* Update undo_info.max to current on every normal move, |
399 | except if it's the same as a redo. */ | 444 | * except if it's the same as a redo. */ |
400 | /* normal move */ | 445 | /* normal move and either */ |
401 | if (!redo && | 446 | if (!redo && |
402 | /* moves have been undone */ | 447 | /* moves have been undone... */ |
403 | ((undo_info.max != undo_info.current && | 448 | ((undo_info.max != undo_info.current && |
404 | /* and the current move is NOT the same as the one in history */ | 449 | /* ...and the current move is NOT the same as the one in history */ |
405 | undo_info.history[undo_info.current+1] != direction) || | 450 | undo_info.history[undo_info.current] != direction) || |
406 | /* or moves have not been undone */ | 451 | /* or moves have not been undone */ |
407 | undo_info.max == undo_info.current)) { | 452 | undo_info.max == undo_info.current)) { |
408 | add_undo(direction); | 453 | add_undo(direction); |
409 | undo_info.max = undo_info.current; | 454 | undo_info.max = undo_info.current; |
410 | } else /* redo move or move was same as redo */ | 455 | } else /* redo move or move was same as redo */ |
411 | add_undo(direction); /* (just to update current) */ | 456 | add_undo(direction); /* add_undo to update current */ |
412 | 457 | ||
413 | return true; | 458 | return true; |
414 | } | 459 | } |
@@ -420,290 +465,370 @@ static bool redo(void) | |||
420 | if (undo_info.current == undo_info.max) | 465 | if (undo_info.current == undo_info.max) |
421 | return false; | 466 | return false; |
422 | 467 | ||
423 | return move(undo_info.history[(undo_info.current+1 < MAX_UNDOS ? | 468 | return move(undo_info.history[(undo_info.current < MAX_UNDOS ? |
424 | undo_info.current+1 : 0)], true); | 469 | undo_info.current : 0)], true); |
425 | } | 470 | } |
426 | #endif | 471 | #endif |
427 | 472 | ||
428 | static void init_boards(void) | 473 | static void init_boards(void) |
429 | { | 474 | { |
430 | current_info.level.level = 0; | 475 | rb->strncpy(buffered_boards.filename, SOKOBAN_LEVELS_FILE, MAX_PATH); |
431 | current_info.level.moves = 0; | 476 | |
432 | current_info.level.pushes = 0; | 477 | current_info.level.index = 0; |
433 | current_info.level.boxes_to_go = 0; | ||
434 | current_info.player.row = 0; | 478 | current_info.player.row = 0; |
435 | current_info.player.col = 0; | 479 | current_info.player.col = 0; |
436 | current_info.max_level = 0; | 480 | current_info.max_level = 0; |
437 | current_info.loaded_level = 0; | ||
438 | 481 | ||
439 | buffered_boards.low = 0; | 482 | buffered_boards.start = 0; |
483 | buffered_boards.end = 0; | ||
484 | buffered_boards.prebuffered_boards = 0; | ||
440 | 485 | ||
441 | init_undo(); | 486 | init_undo(); |
442 | } | 487 | } |
443 | 488 | ||
444 | static int read_levels(int initialize_count) | 489 | static bool read_levels(bool initialize) |
445 | { | 490 | { |
446 | int fd = 0; | 491 | int fd = 0; |
447 | int len; | 492 | short len; |
448 | int lastlen = 0; | 493 | short lastlen = 0; |
449 | int row = 0; | 494 | short row = 0; |
450 | int level_count = 0; | 495 | int level_count = 0; |
451 | char buffer[COLS + 3]; /* COLS plus CR/LF and \0 */ | ||
452 | int endpoint = current_info.level.level-1; | ||
453 | |||
454 | if (endpoint < buffered_boards.low) | ||
455 | endpoint = current_info.level.level - NUM_BUFFERED_BOARDS; | ||
456 | 496 | ||
457 | if (endpoint < 0) endpoint = 0; | 497 | int i = 0; |
498 | int level_len = 0; | ||
499 | bool index_set = false; | ||
458 | 500 | ||
459 | buffered_boards.low = endpoint; | 501 | /* Get the index of the first level to buffer */ |
460 | endpoint += NUM_BUFFERED_BOARDS; | 502 | if (current_info.level.index > buffered_boards.prebuffered_boards && |
503 | !initialize) | ||
504 | buffered_boards.start = current_info.level.index - | ||
505 | buffered_boards.prebuffered_boards; | ||
506 | else | ||
507 | buffered_boards.start = 0; | ||
461 | 508 | ||
462 | if ((fd = rb->open(LEVELS_FILE, O_RDONLY)) < 0) { | 509 | if ((fd = rb->open(buffered_boards.filename, O_RDONLY)) < 0) { |
463 | rb->splash(HZ*2, "Unable to open %s", LEVELS_FILE); | 510 | rb->splash(HZ*2, "Unable to open %s", buffered_boards.filename); |
464 | return -1; | 511 | return false; |
465 | } | 512 | } |
466 | 513 | ||
467 | do { | 514 | do { |
468 | len = rb->read_line(fd, buffer, sizeof(buffer)); | 515 | len = rb->read_line(fd, buf, sizeof(buf)); |
469 | if (len >= 3) { | 516 | |
470 | /* This finds lines that are more than 1 or 2 characters | 517 | /* Correct len when trailing \r's or \n's are counted */ |
471 | * shorter than they should be. Due to the possibility of | 518 | if (len > 2 && buf[len - 2] == '\0') |
472 | * a mixed unix and dos CR/LF file format, I'm not going to | 519 | len -= 2; |
473 | * do a precise check */ | 520 | else if (len > 1 && buf[len - 1] == '\0') |
474 | if (len < COLS) { | 521 | len--; |
475 | rb->splash(HZ*2, "Error in levels file: short line"); | 522 | |
476 | return -1; | 523 | /* Skip short lines & lines with non-level data */ |
477 | } | 524 | if (len >= 3 && ((buf[0] >= '1' && buf[0] <= '9') || buf[0] == '#' || |
478 | if (level_count >= buffered_boards.low && level_count < endpoint) { | 525 | buf[0] == ' ' || buf[0] == '-' || buf[0] == '_')) { |
479 | int index = level_count - buffered_boards.low; | 526 | if (level_count >= buffered_boards.start) { |
480 | rb->memcpy( | 527 | /* Set the index of this level */ |
481 | buffered_boards.levels[index].spaces[row], buffer, COLS); | 528 | if (!index_set && |
529 | level_count - buffered_boards.start < MAX_LEVELS) { | ||
530 | buffered_boards.index[level_count - buffered_boards.start] | ||
531 | = i; | ||
532 | index_set = true; | ||
533 | } | ||
534 | /* Copy buffer to board data */ | ||
535 | if (i + level_len + len < MAX_LEVEL_DATA) { | ||
536 | rb->memcpy(&buffered_boards.data[i + level_len], buf, len); | ||
537 | buffered_boards.data[i + level_len + len] = '\n'; | ||
538 | } | ||
482 | } | 539 | } |
540 | level_len += len + 1; | ||
483 | row++; | 541 | row++; |
484 | } else if (len) { | 542 | |
485 | if (lastlen < 3) { | 543 | /* If newline & level is tall enough or is RLE */ |
486 | /* Two short lines in a row means new level */ | 544 | } else if (buf[0] == '\0' && (row > 2 || lastlen > 22)) { |
487 | level_count++; | 545 | level_count++; |
488 | if (level_count >= endpoint && !initialize_count) break; | 546 | if (level_count >= buffered_boards.start) { |
489 | if (level_count && row != ROWS) { | 547 | i += level_len; |
490 | rb->splash(HZ*2, "Error in levels file: short board"); | 548 | if (i < MAX_LEVEL_DATA) |
491 | return -1; | 549 | buffered_boards.end = level_count; |
492 | } | 550 | else if (!initialize) |
493 | row = 0; | 551 | break; |
494 | } | 552 | } |
495 | } | 553 | row = 0; |
496 | } while ((lastlen=len)); | 554 | level_len = 0; |
555 | index_set = false; | ||
497 | 556 | ||
498 | rb->close(fd); | 557 | } else if (len > 22) |
499 | if (initialize_count) { | 558 | len = 1; |
500 | /* Plus one because there aren't trailing short lines in the file */ | 559 | |
501 | current_info.max_level = level_count + 1; | 560 | } while ((lastlen = len)); |
561 | |||
562 | /* Set the index of the end of the last level */ | ||
563 | if (level_count - buffered_boards.start < MAX_LEVELS) | ||
564 | buffered_boards.index[level_count - buffered_boards.start] = i; | ||
565 | |||
566 | if (initialize) { | ||
567 | current_info.max_level = level_count; | ||
568 | buffered_boards.prebuffered_boards = buffered_boards.end/2; | ||
502 | } | 569 | } |
503 | return 0; | 570 | |
571 | rb->close(fd); | ||
572 | |||
573 | return true; | ||
504 | } | 574 | } |
505 | 575 | ||
506 | /* return non-zero on error */ | ||
507 | static void load_level(void) | 576 | static void load_level(void) |
508 | { | 577 | { |
509 | int c = 0; | 578 | int c, r; |
510 | int r = 0; | 579 | int i, n; |
511 | int index = current_info.level.level - buffered_boards.low - 1; | 580 | int level_size; |
512 | struct Board *level; | 581 | int index = current_info.level.index - buffered_boards.start; |
513 | 582 | char *level; | |
514 | if (index < 0 || index >= NUM_BUFFERED_BOARDS) { | 583 | |
584 | /* Get the buffered board index of the current level */ | ||
585 | if (current_info.level.index < buffered_boards.start || | ||
586 | current_info.level.index >= buffered_boards.end) { | ||
515 | read_levels(false); | 587 | read_levels(false); |
516 | index = index < 0 ? NUM_BUFFERED_BOARDS-1 : 0; | 588 | if (current_info.level.index > buffered_boards.prebuffered_boards) |
589 | index = buffered_boards.prebuffered_boards; | ||
590 | else | ||
591 | index = current_info.level.index; | ||
517 | } | 592 | } |
518 | level = &buffered_boards.levels[index]; | 593 | level = &buffered_boards.data[buffered_boards.index[index]]; |
519 | 594 | ||
520 | current_info.level.boxes_to_go = 0; | 595 | /* Reset level info */ |
521 | current_info.level.moves = 0; | 596 | current_info.level.moves = 0; |
522 | current_info.level.pushes = 0; | 597 | current_info.level.pushes = 0; |
523 | current_info.loaded_level = current_info.level.level; | 598 | current_info.level.boxes_to_go = 0; |
599 | current_info.level.width = 0; | ||
600 | |||
601 | /* Clear board */ | ||
602 | for (r = 0; r < ROWS; r++) | ||
603 | for (c = 0; c < COLS; c++) | ||
604 | current_info.board[r][c] = 'X'; | ||
605 | |||
606 | level_size = buffered_boards.index[index + 1] - | ||
607 | buffered_boards.index[index]; | ||
608 | |||
609 | for (r = 0, c = 0, n = 1, i = 0; i < level_size; i++) { | ||
610 | if (level[i] == '\n' || level[i] == '|') { | ||
611 | if (c > 3) { | ||
612 | /* Update max width of level & go to next row */ | ||
613 | if (c > current_info.level.width) | ||
614 | current_info.level.width = c; | ||
615 | c = 0; | ||
616 | r++; | ||
617 | if (r >= ROWS) | ||
618 | break; | ||
619 | } | ||
620 | } else if (c < COLS) { | ||
621 | /* Read RLE character's length into n */ | ||
622 | if (level[i] >= '0' && level[i] <= '9') { | ||
623 | n = level[i++] - '0'; | ||
624 | if (level[i] >= '0' && level[i] <= '9') | ||
625 | n = n*10 + level[i++] - '0'; | ||
626 | } | ||
627 | |||
628 | /* Cleanup & replace */ | ||
629 | if (level[i] == '%') | ||
630 | level[i] = '*'; | ||
631 | else if (level[i] == '-' || level[i] == '_') | ||
632 | level[i] = ' '; | ||
633 | |||
634 | if (n > 1) { | ||
635 | if (c + n >= COLS) | ||
636 | n = COLS - c; | ||
524 | 637 | ||
525 | for (r = 0; r < ROWS; r++) { | 638 | if (level[i] == '.') |
526 | for (c = 0; c < COLS; c++) { | 639 | current_info.level.boxes_to_go += n; |
527 | current_info.board[r][c] = level->spaces[r][c]; | ||
528 | 640 | ||
529 | if (current_info.board[r][c] == '.' || | 641 | /* Put RLE character n times */ |
530 | current_info.board[r][c] == '+') | 642 | while (n--) |
531 | current_info.level.boxes_to_go++; | 643 | current_info.board[r][c++] = level[i]; |
644 | n = 1; | ||
532 | 645 | ||
533 | if (current_info.board[r][c] == '@' || | 646 | } else { |
534 | current_info.board[r][c] == '+') { | 647 | if (level[i] == '.' || level[i] == '+') |
535 | current_info.player.row = r; | 648 | current_info.level.boxes_to_go++; |
536 | current_info.player.col = c; | 649 | |
650 | if (level[i] == '@' ||level[i] == '+') { | ||
651 | current_info.player.row = r; | ||
652 | current_info.player.col = c; | ||
653 | } | ||
654 | |||
655 | current_info.board[r][c++] = level[i]; | ||
537 | } | 656 | } |
538 | } | 657 | } |
539 | } | 658 | } |
659 | |||
660 | current_info.level.height = r; | ||
661 | |||
662 | #if LCD_DEPTH > 2 | ||
663 | /* Fill in blank space outside level on color targets */ | ||
664 | for (r = 0; r < ROWS; r++) | ||
665 | for (c = 0; current_info.board[r][c] == ' ' && c < COLS; c++) | ||
666 | current_info.board[r][c] = 'X'; | ||
667 | |||
668 | for (c = 0; c < COLS; c++) { | ||
669 | for (r = 0; (current_info.board[r][c] == ' ' || | ||
670 | current_info.board[r][c] == 'X') && r < ROWS; r++) | ||
671 | current_info.board[r][c] = 'X'; | ||
672 | for (r = ROWS - 1; (current_info.board[r][c] == ' ' || | ||
673 | current_info.board[r][c] == 'X') && r >= 0; r--) | ||
674 | current_info.board[r][c] = 'X'; | ||
675 | } | ||
676 | #endif | ||
540 | } | 677 | } |
541 | 678 | ||
542 | static void update_screen(void) | 679 | static void update_screen(void) |
543 | { | 680 | { |
544 | int b = 0, c = 0; | 681 | int c, r; |
545 | int rows = 0, cols = 0; | 682 | int rows, cols; |
546 | char s[25]; | ||
547 | |||
548 | /* magnify is the number of pixels for each block */ | ||
549 | #if (LCD_HEIGHT >= 224) && (LCD_WIDTH >= 312) || \ | ||
550 | (LCD_HEIGHT >= 249) && (LCD_WIDTH >= 280) /* ipod 5g */ | ||
551 | #define MAGNIFY 14 | ||
552 | #elif (LCD_HEIGHT >= 144) && (LCD_WIDTH >= 212) || \ | ||
553 | (LCD_HEIGHT >= 169) && (LCD_WIDTH >= 180-4) /* h3x0, ipod color/photo */ | ||
554 | #define MAGNIFY 9 | ||
555 | #elif (LCD_HEIGHT >= 96) && (LCD_WIDTH >= 152) || \ | ||
556 | (LCD_HEIGHT >= 121) && (LCD_WIDTH >= 120) /* h1x0, ipod nano/mini */ | ||
557 | #define MAGNIFY 6 | ||
558 | #else /* other */ | ||
559 | #define MAGNIFY 4 | ||
560 | #endif | ||
561 | 683 | ||
562 | #if LCD_DEPTH < 2 | 684 | #if LCD_DEPTH < 2 || ((LCD_HEIGHT < 96 || LCD_WIDTH < 152) && \ |
685 | (LCD_HEIGHT < 121 || LCD_WIDTH < 120)) | ||
563 | int i, j; | 686 | int i, j; |
564 | int max = MAGNIFY - 1; | 687 | int max = MAGNIFY - 1; |
565 | int middle = max / 2; | 688 | int middle = max/2; |
566 | int ldelta = (middle + 1) / 2; | 689 | int ldelta = (middle + 1)/2; |
567 | #endif | 690 | #endif |
568 | 691 | ||
569 | /* load the board to the screen */ | 692 | #if LCD_WIDTH - (COLS*MAGNIFY) < 32 |
570 | for (rows=0; rows < ROWS; rows++) { | 693 | #define STAT_HEIGHT 25 |
571 | for (cols = 0; cols < COLS; cols++) { | 694 | #define STAT_X (LCD_WIDTH - 120)/2 |
572 | c = cols * MAGNIFY; | 695 | #define STAT_Y (LCD_HEIGHT - STAT_HEIGHT) |
573 | b = rows * MAGNIFY; | 696 | #define BOARD_WIDTH LCD_WIDTH |
574 | 697 | #define BOARD_HEIGHT (LCD_HEIGHT - STAT_HEIGHT) | |
575 | switch(current_info.board[rows][cols]) { | 698 | rb->lcd_putsxy(STAT_X + 4, STAT_Y + 4, "Level"); |
576 | case 'X': /* black space */ | 699 | rb->snprintf(buf, sizeof(buf), "%d", current_info.level.index + 1); |
577 | break; | 700 | rb->lcd_putsxy(STAT_X + 7, STAT_Y + 14, buf); |
578 | 701 | rb->lcd_putsxy(STAT_X + 41, STAT_Y + 4, "Moves"); | |
579 | case '#': /* this is a wall */ | 702 | rb->snprintf(buf, sizeof(buf), "%d", current_info.level.moves); |
580 | #if LCD_DEPTH >= 2 | 703 | rb->lcd_putsxy(STAT_X + 44, STAT_Y + 14, buf); |
581 | rb->lcd_bitmap_part(sokoban_tiles, 0, 1*MAGNIFY, MAGNIFY, | 704 | rb->lcd_putsxy(STAT_X + 79, STAT_Y + 4, "Pushes"); |
582 | c, b, MAGNIFY, MAGNIFY); | 705 | rb->snprintf(buf, sizeof(buf), "%d", current_info.level.pushes); |
706 | rb->lcd_putsxy(STAT_X + 82, STAT_Y + 14, buf); | ||
707 | |||
708 | rb->lcd_drawrect(STAT_X, STAT_Y, 38, STAT_HEIGHT); | ||
709 | rb->lcd_drawrect(STAT_X + 37, STAT_Y, 39, STAT_HEIGHT); | ||
710 | rb->lcd_drawrect(STAT_X + 75, STAT_Y, 45, STAT_HEIGHT); | ||
583 | #else | 711 | #else |
584 | for (i = c; i < c + MAGNIFY; i++) | 712 | #if LCD_WIDTH - (COLS*MAGNIFY) > 40 |
585 | for (j = b; j < b + MAGNIFY; j++) | 713 | #define STAT_X (LCD_WIDTH - 40) |
586 | if ((i ^ j) & 1) | 714 | #else |
587 | rb->lcd_drawpixel(i, j); | 715 | #define STAT_X COLS*MAGNIFY |
588 | #endif | 716 | #endif |
589 | break; | 717 | #if LCD_HEIGHT >= 70 |
590 | 718 | #define STAT_Y (LCD_HEIGHT - 70)/2 | |
591 | case '$': /* this is a box */ | ||
592 | #if LCD_DEPTH >= 2 | ||
593 | rb->lcd_bitmap_part(sokoban_tiles, 0, 2*MAGNIFY, MAGNIFY, | ||
594 | c, b, MAGNIFY, MAGNIFY); | ||
595 | #else | 719 | #else |
596 | /* Free boxes are not filled in */ | 720 | #define STAT_Y (LCD_HEIGHT - 47)/2 |
597 | rb->lcd_drawrect(c, b, MAGNIFY, MAGNIFY); | ||
598 | #endif | 721 | #endif |
599 | break; | 722 | #define STAT_WIDTH (LCD_WIDTH - STAT_X) |
723 | #define BOARD_WIDTH (LCD_WIDTH - STAT_WIDTH) | ||
724 | #define BOARD_HEIGHT LCD_HEIGHT | ||
725 | rb->lcd_putsxy(STAT_X + 1, STAT_Y + 3, "Level"); | ||
726 | rb->snprintf(buf, sizeof(buf), "%d", current_info.level.index + 1); | ||
727 | rb->lcd_putsxy(STAT_X + 4, STAT_Y + 13, buf); | ||
728 | rb->lcd_putsxy(STAT_X + 1, STAT_Y + 26, "Moves"); | ||
729 | rb->snprintf(buf, sizeof(buf), "%d", current_info.level.moves); | ||
730 | rb->lcd_putsxy(STAT_X + 4, STAT_Y + 36, buf); | ||
731 | |||
732 | rb->lcd_drawrect(STAT_X, STAT_Y + 0, STAT_WIDTH, 24); | ||
733 | rb->lcd_drawrect(STAT_X, STAT_Y + 23, STAT_WIDTH, 24); | ||
600 | 734 | ||
601 | case '*': | 735 | #if LCD_HEIGHT >= 70 |
602 | case '%': /* this is a box on a goal */ | 736 | rb->lcd_putsxy(STAT_X + 1, STAT_Y + 49, "Pushes"); |
737 | rb->snprintf(buf, sizeof(buf), "%d", current_info.level.pushes); | ||
738 | rb->lcd_putsxy(STAT_X + 4, STAT_Y + 59, buf); | ||
603 | 739 | ||
604 | #if LCD_DEPTH >= 2 | 740 | rb->lcd_drawrect(STAT_X, STAT_Y + 46, STAT_WIDTH, 24); |
605 | rb->lcd_bitmap_part(sokoban_tiles, 0, 3*MAGNIFY, MAGNIFY, | ||
606 | c, b, MAGNIFY, MAGNIFY ); | ||
607 | #else | ||
608 | rb->lcd_drawrect(c, b, MAGNIFY, MAGNIFY); | ||
609 | rb->lcd_drawrect(c+(MAGNIFY/2)-1, b+(MAGNIFY/2)-1, MAGNIFY/2, | ||
610 | MAGNIFY/2); | ||
611 | #endif | 741 | #endif |
612 | break; | ||
613 | 742 | ||
614 | case '.': /* this is a goal */ | ||
615 | #if LCD_DEPTH >= 2 | ||
616 | rb->lcd_bitmap_part(sokoban_tiles, 0, 4*MAGNIFY, MAGNIFY, | ||
617 | c, b, MAGNIFY, MAGNIFY); | ||
618 | #else | ||
619 | rb->lcd_drawrect(c+(MAGNIFY/2)-1, b+(MAGNIFY/2)-1, MAGNIFY/2, | ||
620 | MAGNIFY/2); | ||
621 | #endif | 743 | #endif |
622 | break; | ||
623 | 744 | ||
624 | case '@': /* this is you */ | 745 | /* load the board to the screen */ |
625 | #if LCD_DEPTH >= 2 | 746 | for (rows = 0; rows < ROWS; rows++) { |
626 | rb->lcd_bitmap_part(sokoban_tiles, 0, 5*MAGNIFY, MAGNIFY, | 747 | for (cols = 0; cols < COLS; cols++) { |
627 | c, b, MAGNIFY, MAGNIFY); | 748 | c = cols*MAGNIFY + |
628 | #else | 749 | (BOARD_WIDTH - current_info.level.width*MAGNIFY)/2; |
629 | rb->lcd_drawline(c, b+middle, c+max, b+middle); | 750 | r = rows*MAGNIFY + |
630 | rb->lcd_drawline(c+middle, b, c+middle, b+max-ldelta); | 751 | (BOARD_HEIGHT - current_info.level.height*MAGNIFY)/2; |
631 | rb->lcd_drawline(c+max-middle, b, | ||
632 | c+max-middle, b+max-ldelta); | ||
633 | rb->lcd_drawline(c+middle, b+max-ldelta, | ||
634 | c+middle-ldelta, b+max); | ||
635 | rb->lcd_drawline(c+max-middle, b+max-ldelta, | ||
636 | c+max-middle+ldelta, b+max); | ||
637 | #endif | ||
638 | break; | ||
639 | 752 | ||
640 | case '+': /* this is you on drugs, erm, on a goal */ | 753 | switch(current_info.board[rows][cols]) { |
641 | #if LCD_DEPTH >= 2 | 754 | case 'X': /* blank space outside of level */ |
642 | rb->lcd_bitmap_part(sokoban_tiles, 0, 6*MAGNIFY, MAGNIFY, | 755 | break; |
643 | c, b, MAGNIFY, MAGNIFY ); | ||
644 | #else | ||
645 | rb->lcd_drawline(c, b+middle, c+max, b+middle); | ||
646 | rb->lcd_drawline(c+middle, b, c+middle, b+max-ldelta); | ||
647 | rb->lcd_drawline(c+max-middle, b, c+max-middle, b+max-ldelta); | ||
648 | rb->lcd_drawline(c+middle, b+max-ldelta, c+middle-ldelta, | ||
649 | b+max); | ||
650 | rb->lcd_drawline(c+max-middle, b+max-ldelta, | ||
651 | c+max-middle+ldelta, b+max); | ||
652 | rb->lcd_drawline(c+middle-1, b+middle+1, c+max-middle+1, | ||
653 | b+middle+1); | ||
654 | #endif | ||
655 | break; | ||
656 | 756 | ||
657 | #if LCD_DEPTH >= 2 | 757 | #if LCD_DEPTH >= 2 && ((LCD_HEIGHT >= 96 && LCD_WIDTH >= 152) || \ |
658 | default: | 758 | (LCD_HEIGHT >= 121 && LCD_WIDTH >= 120)) |
659 | rb->lcd_bitmap_part(sokoban_tiles, 0, 0*MAGNIFY, MAGNIFY, | 759 | case ' ': /* floor */ |
660 | c, b, MAGNIFY, MAGNIFY ); | 760 | rb->lcd_bitmap_part(sokoban_tiles, 0, 0*MAGNIFY, MAGNIFY, |
661 | #endif | 761 | c, r, MAGNIFY, MAGNIFY); |
662 | } | 762 | break; |
663 | } | ||
664 | } | ||
665 | 763 | ||
666 | #if LCD_WIDTH-(COLS*MAGNIFY) < 32 | 764 | case '#': /* wall */ |
667 | #define STAT_SIZE 25 | 765 | rb->lcd_bitmap_part(sokoban_tiles, 0, 1*MAGNIFY, MAGNIFY, |
668 | #define STAT_POS LCD_HEIGHT-STAT_SIZE | 766 | c, r, MAGNIFY, MAGNIFY); |
669 | #define STAT_CENTER (LCD_WIDTH-120)/2 | 767 | break; |
670 | 768 | ||
671 | rb->lcd_putsxy(4+STAT_CENTER, STAT_POS+4, "Level"); | 769 | case '$': /* box */ |
672 | rb->snprintf(s, sizeof(s), "%d", current_info.level.level); | 770 | rb->lcd_bitmap_part(sokoban_tiles, 0, 2*MAGNIFY, MAGNIFY, |
673 | rb->lcd_putsxy(7+STAT_CENTER, STAT_POS+14, s); | 771 | c, r, MAGNIFY, MAGNIFY); |
674 | rb->lcd_putsxy(41+STAT_CENTER, STAT_POS+4, "Moves"); | 772 | break; |
675 | rb->snprintf(s, sizeof(s), "%d", current_info.level.moves); | ||
676 | rb->lcd_putsxy(44+STAT_CENTER, STAT_POS+14, s); | ||
677 | rb->lcd_putsxy(79+STAT_CENTER, STAT_POS+4, "Pushes"); | ||
678 | rb->snprintf(s, sizeof(s), "%d", current_info.level.pushes); | ||
679 | rb->lcd_putsxy(82+STAT_CENTER, STAT_POS+14, s); | ||
680 | |||
681 | rb->lcd_drawrect(STAT_CENTER, STAT_POS, 38, STAT_SIZE); | ||
682 | rb->lcd_drawrect(37+STAT_CENTER, STAT_POS, 39, STAT_SIZE); | ||
683 | rb->lcd_drawrect(75+STAT_CENTER, STAT_POS, 45, STAT_SIZE); | ||
684 | 773 | ||
774 | case '*': /* box on goal */ | ||
775 | rb->lcd_bitmap_part(sokoban_tiles, 0, 3*MAGNIFY, MAGNIFY, | ||
776 | c, r, MAGNIFY, MAGNIFY); | ||
777 | break; | ||
778 | |||
779 | case '.': /* goal */ | ||
780 | rb->lcd_bitmap_part(sokoban_tiles, 0, 4*MAGNIFY, MAGNIFY, | ||
781 | c, r, MAGNIFY, MAGNIFY); | ||
782 | break; | ||
783 | |||
784 | case '@': /* player */ | ||
785 | rb->lcd_bitmap_part(sokoban_tiles, 0, 5*MAGNIFY, MAGNIFY, | ||
786 | c, r, MAGNIFY, MAGNIFY); | ||
787 | break; | ||
788 | |||
789 | case '+': /* player on goal */ | ||
790 | rb->lcd_bitmap_part(sokoban_tiles, 0, 6*MAGNIFY, MAGNIFY, | ||
791 | c, r, MAGNIFY, MAGNIFY); | ||
792 | break; | ||
685 | #else | 793 | #else |
686 | #define STAT_POS COLS*MAGNIFY | 794 | case '#': /* wall */ |
687 | #define STAT_SIZE LCD_WIDTH-STAT_POS | 795 | for (i = c; i < c + MAGNIFY; i++) |
796 | for (j = r; j < r + MAGNIFY; j++) | ||
797 | if ((i ^ j) & 1) | ||
798 | rb->lcd_drawpixel(i, j); | ||
799 | break; | ||
688 | 800 | ||
689 | rb->lcd_putsxy(STAT_POS+1, 3, "Level"); | 801 | case '$': /* box */ |
690 | rb->snprintf(s, sizeof(s), "%d", current_info.level.level); | 802 | rb->lcd_drawrect(c, r, MAGNIFY, MAGNIFY); |
691 | rb->lcd_putsxy(STAT_POS+4, 13, s); | 803 | break; |
692 | rb->lcd_putsxy(STAT_POS+1, 26, "Moves"); | ||
693 | rb->snprintf(s, sizeof(s), "%d", current_info.level.moves); | ||
694 | rb->lcd_putsxy(STAT_POS+4, 36, s); | ||
695 | 804 | ||
696 | rb->lcd_drawrect(STAT_POS, 0, STAT_SIZE, 24); | 805 | case '*': /* box on goal */ |
697 | rb->lcd_drawrect(STAT_POS, 23, STAT_SIZE, 24); | 806 | rb->lcd_drawrect(c, r, MAGNIFY, MAGNIFY); |
807 | rb->lcd_drawrect(c + MAGNIFY/2 - 1, r + MAGNIFY/2 - 1, | ||
808 | MAGNIFY/2, MAGNIFY/2); | ||
809 | break; | ||
698 | 810 | ||
699 | #if LCD_HEIGHT >= 70 | 811 | case '.': /* goal */ |
700 | rb->lcd_putsxy(STAT_POS+1, 49, "Pushes"); | 812 | rb->lcd_drawrect(c + MAGNIFY/2 - 1, r + MAGNIFY/2 - 1, |
701 | rb->snprintf(s, sizeof(s), "%d", current_info.level.pushes); | 813 | MAGNIFY/2, MAGNIFY/2); |
702 | rb->lcd_putsxy(STAT_POS+4, 59, s); | 814 | break; |
703 | 815 | ||
704 | rb->lcd_drawrect(STAT_POS, 46, STAT_SIZE, 24); | 816 | case '@': /* player */ |
705 | #endif | 817 | case '+': /* player on goal */ |
818 | rb->lcd_drawline(c, r + middle, c + max, r + middle); | ||
819 | rb->lcd_drawline(c + middle, r, c + middle, | ||
820 | r + max - ldelta); | ||
821 | rb->lcd_drawline(c + max - middle, r, c + max - middle, | ||
822 | r + max - ldelta); | ||
823 | rb->lcd_drawline(c + middle, r + max - ldelta, | ||
824 | c + middle - ldelta, r + max); | ||
825 | rb->lcd_drawline(c + max - middle, r + max - ldelta, | ||
826 | c + max - middle + ldelta, r + max); | ||
827 | break; | ||
706 | #endif | 828 | #endif |
829 | } | ||
830 | } | ||
831 | } | ||
707 | 832 | ||
708 | /* print out the screen */ | 833 | /* print out the screen */ |
709 | rb->lcd_update(); | 834 | rb->lcd_update(); |
@@ -716,21 +841,320 @@ static void draw_level(void) | |||
716 | update_screen(); | 841 | update_screen(); |
717 | } | 842 | } |
718 | 843 | ||
844 | static bool save(char *filename, bool solution) | ||
845 | { | ||
846 | int fd; | ||
847 | |||
848 | rb->splash(0, "Saving..."); | ||
849 | |||
850 | if (filename[0] == '\0' || | ||
851 | (fd = rb->open(filename, O_WRONLY|O_CREAT|O_TRUNC)) < 0) { | ||
852 | rb->splash(HZ*2, "Unable to open %s", filename); | ||
853 | return false; | ||
854 | } | ||
855 | |||
856 | /* Sokoban: S/P for solution/progress : level number : current undo */ | ||
857 | rb->snprintf(buf, sizeof(buf), "Sokoban:%c:%d:%d\n", (solution ? 'S' : 'P'), | ||
858 | current_info.level.index + 1, undo_info.current); | ||
859 | rb->write(fd, buf, rb->strlen(buf)); | ||
860 | |||
861 | /* Filename of levelset */ | ||
862 | rb->write(fd, buffered_boards.filename, | ||
863 | rb->strlen(buffered_boards.filename)); | ||
864 | rb->write(fd, "\n", 1); | ||
865 | |||
866 | /* Full undo history */ | ||
867 | rb->write(fd, undo_info.history, undo_info.max); | ||
868 | |||
869 | rb->close(fd); | ||
870 | |||
871 | return true; | ||
872 | } | ||
873 | |||
874 | static bool load(char *filename, bool silent) | ||
875 | { | ||
876 | int fd; | ||
877 | int i = 0, n; | ||
878 | int len; | ||
879 | bool play_solution; | ||
880 | int button; | ||
881 | int step_delay = HZ/4; | ||
882 | |||
883 | if (filename[0] == '\0' || (fd = rb->open(filename, O_RDONLY)) < 0) { | ||
884 | if (!silent) | ||
885 | rb->splash(HZ*2, "Unable to open %s", filename); | ||
886 | return false; | ||
887 | } | ||
888 | |||
889 | /* Read header, level number, & current undo */ | ||
890 | rb->read_line(fd, buf, sizeof(buf)); | ||
891 | |||
892 | /* If we're opening a level file, not a solution/progress file */ | ||
893 | if (rb->strncmp(buf, "Sokoban", 7) != 0) { | ||
894 | rb->close(fd); | ||
895 | |||
896 | rb->strncpy(buffered_boards.filename, filename, MAX_PATH); | ||
897 | if (!read_levels(true)) | ||
898 | return false; | ||
899 | |||
900 | current_info.level.index = 0; | ||
901 | load_level(); | ||
902 | |||
903 | /* If there aren't any boxes to go or the player position wasn't set, | ||
904 | * the file probably wasn't a Sokoban level file */ | ||
905 | if (current_info.level.boxes_to_go == 0 || | ||
906 | current_info.player.row == 0 || current_info.player.col == 0) { | ||
907 | if (!silent) | ||
908 | rb->splash(HZ*2, "File is not a Sokoban level file"); | ||
909 | return false; | ||
910 | } | ||
911 | |||
912 | } else { | ||
913 | |||
914 | /* Read filename of levelset */ | ||
915 | rb->read_line(fd, buffered_boards.filename, | ||
916 | sizeof(buffered_boards.filename)); | ||
917 | |||
918 | /* Read full undo history */ | ||
919 | len = rb->read_line(fd, undo_info.history, MAX_UNDOS); | ||
920 | |||
921 | /* Correct len when trailing \r's or \n's are counted */ | ||
922 | if (len > 2 && undo_info.history[len - 2] == '\0') | ||
923 | len -= 2; | ||
924 | else if (len > 1 && undo_info.history[len - 1] == '\0') | ||
925 | len--; | ||
926 | |||
927 | rb->close(fd); | ||
928 | |||
929 | /* Check to see if we're going to play a solution or resume progress */ | ||
930 | play_solution = (buf[8] == 'S'); | ||
931 | |||
932 | /* Get level number */ | ||
933 | for (n = 0, i = 10; buf[i] >= '0' && buf[i] <= '9' && i < 15; i++) | ||
934 | n = n*10 + buf[i] - '0'; | ||
935 | current_info.level.index = n - 1; | ||
936 | |||
937 | /* Get current undo index */ | ||
938 | for (n = 0, i++; buf[i] >= '0' && buf[i] <= '9' && i < 21; i++) | ||
939 | n = n*10 + buf[i] - '0'; | ||
940 | if (n > len) | ||
941 | n = len; | ||
942 | |||
943 | if (current_info.level.index < 0) { | ||
944 | if (!silent) | ||
945 | rb->splash(HZ*2, "Error loading level"); | ||
946 | return false; | ||
947 | } | ||
948 | if (!read_levels(true)) | ||
949 | return false; | ||
950 | if (current_info.level.index >= current_info.max_level) { | ||
951 | if (!silent) | ||
952 | rb->splash(HZ*2, "Error loading level"); | ||
953 | return false; | ||
954 | } | ||
955 | |||
956 | load_level(); | ||
957 | |||
958 | if (play_solution) { | ||
959 | rb->lcd_clear_display(); | ||
960 | update_screen(); | ||
961 | rb->sleep(2*step_delay); | ||
962 | |||
963 | /* Replay solution until the end or quit button is pressed */ | ||
964 | for (i = 0; i < len; i++) { | ||
965 | if (!move(undo_info.history[i], true)) { | ||
966 | n = i; | ||
967 | break; | ||
968 | } | ||
969 | |||
970 | rb->lcd_clear_display(); | ||
971 | update_screen(); | ||
972 | rb->sleep(step_delay); | ||
973 | |||
974 | /* Ignore keypresses except for quit & changing speed */ | ||
975 | while ((button = rb->button_get(false)) != BUTTON_NONE) { | ||
976 | switch (button) { | ||
977 | case SOKOBAN_MENU: | ||
978 | /* Pretend the level is complete so we'll quit */ | ||
979 | current_info.level.boxes_to_go = 0; | ||
980 | return true; | ||
981 | |||
982 | case SOKOBAN_UP: | ||
983 | if (step_delay > HZ/12) | ||
984 | step_delay = 5*step_delay/6; | ||
985 | break; | ||
986 | |||
987 | case SOKOBAN_DOWN: | ||
988 | if (step_delay < 3*HZ/4) | ||
989 | step_delay = 6*step_delay/5; | ||
990 | } | ||
991 | } | ||
992 | } | ||
993 | |||
994 | /* If level complete, wait for keypress before quitting */ | ||
995 | if (current_info.level.boxes_to_go == 0) | ||
996 | rb->button_get(true); | ||
997 | |||
998 | } else { | ||
999 | /* Advance to current undo */ | ||
1000 | for (i = 0; i < n; i++) { | ||
1001 | if (!move(undo_info.history[i], true)) { | ||
1002 | n = i; | ||
1003 | break; | ||
1004 | } | ||
1005 | } | ||
1006 | |||
1007 | rb->button_clear_queue(); | ||
1008 | rb->lcd_clear_display(); | ||
1009 | } | ||
1010 | |||
1011 | undo_info.max = len; | ||
1012 | undo_info.current = n; | ||
1013 | } | ||
1014 | |||
1015 | return true; | ||
1016 | } | ||
1017 | |||
1018 | static int sokoban_menu(void) | ||
1019 | { | ||
1020 | int button; | ||
1021 | int selection = 0; | ||
1022 | int i; | ||
1023 | bool menu_quit; | ||
1024 | int start_selected = 0; | ||
1025 | |||
1026 | MENUITEM_STRINGLIST(menu, "Sokoban Menu", NULL, | ||
1027 | "Resume", "Audio Playback", "Keys", | ||
1028 | "Load Default Level Set", "Quit Without Saving", | ||
1029 | "Save Progress & Quit"); | ||
1030 | |||
1031 | do { | ||
1032 | menu_quit = true; | ||
1033 | selection = rb->do_menu(&menu, &start_selected); | ||
1034 | |||
1035 | switch (selection) { | ||
1036 | case 0: /* Resume */ | ||
1037 | break; | ||
1038 | |||
1039 | case 1: /* Audio playback control */ | ||
1040 | playback_control(rb); | ||
1041 | menu_quit = false; | ||
1042 | break; | ||
1043 | |||
1044 | case 2: /* Keys */ | ||
1045 | FOR_NB_SCREENS(i) | ||
1046 | rb->screens[i]->clear_display(); | ||
1047 | rb->lcd_setfont(SOKOBAN_FONT); | ||
1048 | |||
1049 | #if (CONFIG_KEYPAD == RECORDER_PAD) || \ | ||
1050 | (CONFIG_KEYPAD == ARCHOS_AV300_PAD) | ||
1051 | rb->lcd_putsxy(3, 6, "[OFF] Menu"); | ||
1052 | rb->lcd_putsxy(3, 16, "[ON] Undo"); | ||
1053 | rb->lcd_putsxy(3, 26, "[PLAY] Redo"); | ||
1054 | rb->lcd_putsxy(3, 36, "[F1] Down a Level"); | ||
1055 | rb->lcd_putsxy(3, 46, "[F2] Restart Level"); | ||
1056 | rb->lcd_putsxy(3, 56, "[F3] Up a Level"); | ||
1057 | #elif CONFIG_KEYPAD == ONDIO_PAD | ||
1058 | rb->lcd_putsxy(3, 6, "[OFF] Menu"); | ||
1059 | rb->lcd_putsxy(3, 16, "[MODE] Undo"); | ||
1060 | rb->lcd_putsxy(3, 26, "[MODE+DOWN] Redo"); | ||
1061 | rb->lcd_putsxy(3, 36, "[MODE+LEFT] Previous Level"); | ||
1062 | rb->lcd_putsxy(3, 46, "[MODE+UP] Restart Level"); | ||
1063 | rb->lcd_putsxy(3, 56, "[MODE+RIGHT] Up Level"); | ||
1064 | #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \ | ||
1065 | (CONFIG_KEYPAD == IRIVER_H300_PAD) | ||
1066 | rb->lcd_putsxy(3, 6, "[STOP] Menu"); | ||
1067 | rb->lcd_putsxy(3, 16, "[REC] Undo"); | ||
1068 | rb->lcd_putsxy(3, 26, "[MODE] Redo"); | ||
1069 | rb->lcd_putsxy(3, 36, "[PLAY+DOWN] Previous Level"); | ||
1070 | rb->lcd_putsxy(3, 46, "[PLAY] Restart Level"); | ||
1071 | rb->lcd_putsxy(3, 56, "[PLAY+UP] Next Level"); | ||
1072 | #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \ | ||
1073 | (CONFIG_KEYPAD == IPOD_3G_PAD) | ||
1074 | rb->lcd_putsxy(3, 6, "[SELECT+MENU] Menu"); | ||
1075 | rb->lcd_putsxy(3, 16, "[SELECT] Undo"); | ||
1076 | rb->lcd_putsxy(3, 26, "[SELECT+PLAY] Redo"); | ||
1077 | rb->lcd_putsxy(3, 36, "[SELECT+LEFT] Previous Level"); | ||
1078 | rb->lcd_putsxy(3, 46, "[SELECT+RIGHT] Next Level"); | ||
1079 | #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD | ||
1080 | rb->lcd_putsxy(3, 6, "[POWER] Menu"); | ||
1081 | rb->lcd_putsxy(3, 16, "[SELECT] Undo"); | ||
1082 | rb->lcd_putsxy(3, 26, "[REC] Previous Level"); | ||
1083 | rb->lcd_putsxy(3, 36, "[PLAY] Next Level"); | ||
1084 | #elif CONFIG_KEYPAD == IRIVER_H10_PAD | ||
1085 | rb->lcd_putsxy(3, 6, "[POWER] Menu"); | ||
1086 | rb->lcd_putsxy(3, 16, "[REW] Undo"); | ||
1087 | rb->lcd_putsxy(3, 26, "[FF] Redo"); | ||
1088 | rb->lcd_putsxy(3, 36, "[PLAY+DOWN] Previous Level"); | ||
1089 | rb->lcd_putsxy(3, 46, "[PLAY+RIGHT] Restart Level"); | ||
1090 | rb->lcd_putsxy(3, 56, "[PLAY+UP] Next Level"); | ||
1091 | #elif CONFIG_KEYPAD == GIGABEAT_PAD | ||
1092 | rb->lcd_putsxy(3, 6, "[POWER] Menu"); | ||
1093 | rb->lcd_putsxy(3, 16, "[SELECT] Undo"); | ||
1094 | rb->lcd_putsxy(3, 26, "[A] Redo"); | ||
1095 | rb->lcd_putsxy(3, 36, "[VOL-] Previous Level"); | ||
1096 | rb->lcd_putsxy(3, 46, "[MENU] Restart Level"); | ||
1097 | rb->lcd_putsxy(3, 56, "[VOL+] Next Level"); | ||
1098 | #elif CONFIG_KEYPAD == SANSA_E200_PAD | ||
1099 | rb->lcd_putsxy(3, 6, "[POWER] Menu"); | ||
1100 | rb->lcd_putsxy(3, 16, "[SELECT] Undo"); | ||
1101 | rb->lcd_putsxy(3, 26, "[REC] Redo"); | ||
1102 | rb->lcd_putsxy(3, 36, "[SELECT+DOWN] Previous Level"); | ||
1103 | rb->lcd_putsxy(3, 46, "[SELECT+RIGHT] Restart Level"); | ||
1104 | rb->lcd_putsxy(3, 56, "[SELECT+UP] Next Level"); | ||
1105 | #endif | ||
1106 | |||
1107 | FOR_NB_SCREENS(i) | ||
1108 | rb->screens[i]->update(); | ||
1109 | |||
1110 | /* Display until keypress */ | ||
1111 | do { | ||
1112 | rb->sleep(HZ/20); | ||
1113 | button = rb->button_get(false); | ||
1114 | } while (!button || button & BUTTON_REL || | ||
1115 | button & BUTTON_REPEAT); | ||
1116 | |||
1117 | menu_quit = false; | ||
1118 | break; | ||
1119 | |||
1120 | case 3: /* Load default levelset */ | ||
1121 | init_boards(); | ||
1122 | if (!read_levels(true)) | ||
1123 | return 4; | ||
1124 | load_level(); | ||
1125 | break; | ||
1126 | |||
1127 | case 4: /* Quit */ | ||
1128 | break; | ||
1129 | |||
1130 | case 5: /* Save & quit */ | ||
1131 | save(SOKOBAN_SAVE_FILE, false); | ||
1132 | rb->reload_directory(); | ||
1133 | } | ||
1134 | |||
1135 | } while (!menu_quit); | ||
1136 | |||
1137 | /* Restore font */ | ||
1138 | rb->lcd_setfont(SOKOBAN_FONT); | ||
1139 | |||
1140 | FOR_NB_SCREENS(i) { | ||
1141 | rb->screens[i]->clear_display(); | ||
1142 | rb->screens[i]->update(); | ||
1143 | } | ||
1144 | |||
1145 | return selection; | ||
1146 | } | ||
1147 | |||
719 | static bool sokoban_loop(void) | 1148 | static bool sokoban_loop(void) |
720 | { | 1149 | { |
721 | bool moved = true; | 1150 | bool moved; |
722 | int i = 0, button = 0, lastbutton = 0; | 1151 | int i = 0, button = 0, lastbutton = 0; |
723 | short r = 0, c = 0; | 1152 | short r = 0, c = 0; |
724 | int w, h; | 1153 | int w, h; |
725 | char s[25]; | 1154 | char *loc; |
726 | 1155 | ||
727 | current_info.level.level = 1; | 1156 | while (true) { |
728 | 1157 | moved = false; | |
729 | load_level(); | ||
730 | update_screen(); | ||
731 | |||
732 | while (1) { | ||
733 | moved = true; | ||
734 | 1158 | ||
735 | r = current_info.player.row; | 1159 | r = current_info.player.row; |
736 | c = current_info.player.col; | 1160 | c = current_info.player.col; |
@@ -742,24 +1166,25 @@ static bool sokoban_loop(void) | |||
742 | #ifdef SOKOBAN_RC_QUIT | 1166 | #ifdef SOKOBAN_RC_QUIT |
743 | case SOKOBAN_RC_QUIT: | 1167 | case SOKOBAN_RC_QUIT: |
744 | #endif | 1168 | #endif |
745 | case SOKOBAN_QUIT: | 1169 | case SOKOBAN_MENU: |
746 | /* get out of here */ | 1170 | switch (sokoban_menu()) { |
747 | #ifdef HAVE_LCD_COLOR /* reset background color */ | 1171 | case 4: /* Quit */ |
748 | rb->lcd_set_background(rb->global_settings->bg_color); | 1172 | case 5: /* Save & quit */ |
749 | #endif | 1173 | return PLUGIN_OK; |
750 | return PLUGIN_OK; | 1174 | } |
1175 | update_screen(); | ||
1176 | break; | ||
751 | 1177 | ||
752 | case SOKOBAN_UNDO: | 1178 | case SOKOBAN_UNDO: |
753 | #ifdef SOKOBAN_UNDO_PRE | 1179 | #ifdef SOKOBAN_UNDO_PRE |
754 | if (lastbutton != SOKOBAN_UNDO_PRE) | 1180 | if (lastbutton != SOKOBAN_UNDO_PRE) |
755 | break; | 1181 | break; |
756 | #else /* repeat can't work here for Ondio et al */ | 1182 | #else /* repeat can't work here for Ondio, iPod, et al */ |
757 | case SOKOBAN_UNDO | BUTTON_REPEAT: | 1183 | case SOKOBAN_UNDO | BUTTON_REPEAT: |
758 | #endif | 1184 | #endif |
759 | undo(); | 1185 | undo(); |
760 | rb->lcd_clear_display(); | 1186 | rb->lcd_clear_display(); |
761 | update_screen(); | 1187 | update_screen(); |
762 | moved = false; | ||
763 | break; | 1188 | break; |
764 | 1189 | ||
765 | #ifdef SOKOBAN_REDO | 1190 | #ifdef SOKOBAN_REDO |
@@ -775,22 +1200,20 @@ static bool sokoban_loop(void) | |||
775 | case SOKOBAN_LEVEL_UP | BUTTON_REPEAT: | 1200 | case SOKOBAN_LEVEL_UP | BUTTON_REPEAT: |
776 | /* next level */ | 1201 | /* next level */ |
777 | init_undo(); | 1202 | init_undo(); |
778 | if (current_info.level.level < current_info.max_level) | 1203 | if (current_info.level.index + 1 < current_info.max_level) |
779 | current_info.level.level++; | 1204 | current_info.level.index++; |
780 | 1205 | ||
781 | draw_level(); | 1206 | draw_level(); |
782 | moved = false; | ||
783 | break; | 1207 | break; |
784 | 1208 | ||
785 | case SOKOBAN_LEVEL_DOWN: | 1209 | case SOKOBAN_LEVEL_DOWN: |
786 | case SOKOBAN_LEVEL_DOWN | BUTTON_REPEAT: | 1210 | case SOKOBAN_LEVEL_DOWN | BUTTON_REPEAT: |
787 | /* previous level */ | 1211 | /* previous level */ |
788 | init_undo(); | 1212 | init_undo(); |
789 | if (current_info.level.level > 1) | 1213 | if (current_info.level.index > 0) |
790 | current_info.level.level--; | 1214 | current_info.level.index--; |
791 | 1215 | ||
792 | draw_level(); | 1216 | draw_level(); |
793 | moved = false; | ||
794 | break; | 1217 | break; |
795 | 1218 | ||
796 | #ifdef SOKOBAN_LEVEL_REPEAT | 1219 | #ifdef SOKOBAN_LEVEL_REPEAT |
@@ -799,7 +1222,6 @@ static bool sokoban_loop(void) | |||
799 | /* same level */ | 1222 | /* same level */ |
800 | init_undo(); | 1223 | init_undo(); |
801 | draw_level(); | 1224 | draw_level(); |
802 | moved = false; | ||
803 | break; | 1225 | break; |
804 | #endif | 1226 | #endif |
805 | 1227 | ||
@@ -826,13 +1248,10 @@ static bool sokoban_loop(void) | |||
826 | default: | 1248 | default: |
827 | if (rb->default_event_handler(button) == SYS_USB_CONNECTED) | 1249 | if (rb->default_event_handler(button) == SYS_USB_CONNECTED) |
828 | return PLUGIN_USB_CONNECTED; | 1250 | return PLUGIN_USB_CONNECTED; |
829 | |||
830 | moved = false; | ||
831 | break; | 1251 | break; |
832 | } | 1252 | } |
833 | 1253 | ||
834 | if (button != BUTTON_NONE) | 1254 | lastbutton = button; |
835 | lastbutton = button; | ||
836 | 1255 | ||
837 | if (moved) { | 1256 | if (moved) { |
838 | rb->lcd_clear_display(); | 1257 | rb->lcd_clear_display(); |
@@ -844,58 +1263,102 @@ static bool sokoban_loop(void) | |||
844 | 1263 | ||
845 | if (moved) { | 1264 | if (moved) { |
846 | rb->lcd_clear_display(); | 1265 | rb->lcd_clear_display(); |
847 | /* Center level completed message */ | 1266 | |
848 | rb->snprintf(s, sizeof(s), "Level %d Complete!", | 1267 | /* Show level complete message & stats */ |
849 | current_info.level.level); | 1268 | rb->snprintf(buf, sizeof(buf), "Level %d Complete!", |
850 | rb->lcd_getstringsize(s, &w, &h); | 1269 | current_info.level.index + 1); |
851 | rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - 16 , s); | 1270 | rb->lcd_getstringsize(buf, &w, &h); |
852 | rb->snprintf(s, sizeof(s), "%4d Moves ", | 1271 | rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h*3, buf); |
1272 | |||
1273 | rb->snprintf(buf, sizeof(buf), "%4d Moves ", | ||
853 | current_info.level.moves); | 1274 | current_info.level.moves); |
854 | rb->lcd_getstringsize(s, &w, &h); | 1275 | rb->lcd_getstringsize(buf, &w, &h); |
855 | rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + 0 , s); | 1276 | rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h, buf); |
856 | rb->snprintf(s, sizeof(s), "%4d Pushes", | 1277 | |
1278 | rb->snprintf(buf, sizeof(buf), "%4d Pushes", | ||
857 | current_info.level.pushes); | 1279 | current_info.level.pushes); |
858 | rb->lcd_getstringsize(s, &w, &h); | 1280 | rb->lcd_getstringsize(buf, &w, &h); |
859 | rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + 8 , s); | 1281 | rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2, buf); |
1282 | |||
1283 | if (undo_info.count < MAX_UNDOS) { | ||
1284 | rb->snprintf(buf, sizeof(buf), "%s: Save solution", | ||
1285 | BUTTON_SAVE_NAME); | ||
1286 | rb->lcd_getstringsize(buf, &w, &h); | ||
1287 | rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + h*2, buf); | ||
1288 | } | ||
1289 | |||
860 | rb->lcd_update(); | 1290 | rb->lcd_update(); |
861 | rb->button_get(false); | 1291 | rb->sleep(HZ/4); |
1292 | rb->button_clear_queue(); | ||
862 | 1293 | ||
863 | rb->sleep(HZ/2); | 1294 | /* Display for 4 seconds or until new keypress */ |
864 | for (i = 0; i < 30; i++) { | 1295 | for (i = 0; i < 75; i++) { |
865 | rb->sleep(HZ/20); | 1296 | rb->sleep(HZ/20); |
866 | button = rb->button_get(false); | 1297 | button = rb->button_get(false); |
867 | if (button && ((button & BUTTON_REL) != BUTTON_REL)) | 1298 | if (button && !(button & BUTTON_REL) && |
1299 | !(button & BUTTON_REPEAT)) | ||
868 | break; | 1300 | break; |
869 | } | 1301 | } |
1302 | |||
1303 | if (button == BUTTON_SAVE) { | ||
1304 | if (undo_info.count < MAX_UNDOS) { | ||
1305 | /* Default filename to current levelset plus | ||
1306 | * level number and .sok extension */ | ||
1307 | loc = rb->strrchr(buffered_boards.filename, '.'); | ||
1308 | if (loc != NULL) | ||
1309 | *loc = '\0'; | ||
1310 | rb->snprintf(buf, sizeof(buf), "%s.%d.sok", | ||
1311 | buffered_boards.filename, | ||
1312 | current_info.level.index + 1); | ||
1313 | if (loc != NULL) | ||
1314 | *loc = '.'; | ||
1315 | |||
1316 | if (!rb->kbd_input(buf, MAX_PATH)) | ||
1317 | save(buf, true); | ||
1318 | } else | ||
1319 | rb->splash(HZ*2, "Solution too long to save"); | ||
1320 | |||
1321 | rb->lcd_setfont(SOKOBAN_FONT); /* Restore font */ | ||
1322 | } | ||
870 | } | 1323 | } |
871 | 1324 | ||
872 | current_info.level.level++; | 1325 | FOR_NB_SCREENS(i) { |
1326 | rb->screens[i]->clear_display(); | ||
1327 | rb->screens[i]->update(); | ||
1328 | } | ||
1329 | |||
1330 | current_info.level.index++; | ||
873 | 1331 | ||
874 | /* clear undo stats */ | 1332 | /* clear undo stats */ |
875 | init_undo(); | 1333 | init_undo(); |
876 | 1334 | ||
877 | rb->lcd_clear_display(); | 1335 | if (current_info.level.index >= current_info.max_level) { |
878 | 1336 | /* Show levelset complete message */ | |
879 | if (current_info.level.level > current_info.max_level) { | 1337 | rb->snprintf(buf, sizeof(buf), "You WIN!!"); |
880 | /* Center "You WIN!!" on all screen sizes */ | 1338 | rb->lcd_getstringsize(buf, &w, &h); |
881 | rb->snprintf(s, sizeof(s), "You WIN!!"); | 1339 | rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h/2, buf); |
882 | rb->lcd_getstringsize(s, &w, &h); | ||
883 | rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h/2, s); | ||
884 | 1340 | ||
885 | rb->lcd_set_drawmode(DRMODE_COMPLEMENT); | 1341 | rb->lcd_set_drawmode(DRMODE_COMPLEMENT); |
886 | /* Display for 10 seconds or until keypress */ | 1342 | /* Display for 4 seconds or until keypress */ |
887 | for (i = 0; i < 200; i++) { | 1343 | for (i = 0; i < 80; i++) { |
888 | rb->lcd_fillrect(0, 0, LCD_WIDTH, LCD_HEIGHT); | 1344 | rb->lcd_fillrect(0, 0, LCD_WIDTH, LCD_HEIGHT); |
889 | rb->lcd_update(); | 1345 | rb->lcd_update(); |
890 | rb->sleep(HZ/20); | 1346 | rb->sleep(HZ/10); |
891 | 1347 | ||
892 | button = rb->button_get(false); | 1348 | button = rb->button_get(false); |
893 | if (button && ((button & BUTTON_REL) != BUTTON_REL)) | 1349 | if (button && !(button & BUTTON_REL)) |
894 | break; | 1350 | break; |
895 | } | 1351 | } |
896 | rb->lcd_set_drawmode(DRMODE_SOLID); | 1352 | rb->lcd_set_drawmode(DRMODE_SOLID); |
897 | 1353 | ||
898 | return PLUGIN_OK; | 1354 | /* Reset to first level & show quit menu */ |
1355 | current_info.level.index = 0; | ||
1356 | |||
1357 | switch (sokoban_menu()) { | ||
1358 | case 4: /* Quit */ | ||
1359 | case 5: /* Save & quit */ | ||
1360 | return PLUGIN_OK; | ||
1361 | } | ||
899 | } | 1362 | } |
900 | 1363 | ||
901 | load_level(); | 1364 | load_level(); |
@@ -911,99 +1374,41 @@ static bool sokoban_loop(void) | |||
911 | enum plugin_status plugin_start(struct plugin_api* api, void* parameter) | 1374 | enum plugin_status plugin_start(struct plugin_api* api, void* parameter) |
912 | { | 1375 | { |
913 | int w, h; | 1376 | int w, h; |
914 | int i; | ||
915 | int button = 0; | ||
916 | 1377 | ||
917 | (void)(parameter); | 1378 | (void)(parameter); |
918 | rb = api; | 1379 | rb = api; |
919 | 1380 | ||
920 | rb->lcd_setfont(FONT_SYSFIXED); | 1381 | rb->lcd_setfont(SOKOBAN_FONT); |
921 | |||
922 | #ifdef HAVE_LCD_COLOR | ||
923 | rb->lcd_set_background(BG_COLOR); | ||
924 | #endif | ||
925 | 1382 | ||
926 | rb->lcd_clear_display(); | 1383 | rb->lcd_clear_display(); |
927 | rb->lcd_getstringsize(SOKOBAN_TITLE, &w, &h); | 1384 | rb->lcd_getstringsize(SOKOBAN_TITLE, &w, &h); |
928 | rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h/2, SOKOBAN_TITLE); | 1385 | rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h/2, SOKOBAN_TITLE); |
929 | rb->lcd_update(); | 1386 | rb->lcd_update(); |
930 | rb->sleep(HZ); | 1387 | rb->sleep(HZ); /* Show title for 1 second */ |
931 | 1388 | ||
932 | rb->lcd_clear_display(); | 1389 | init_boards(); |
933 | 1390 | ||
934 | #if (CONFIG_KEYPAD == RECORDER_PAD) || \ | 1391 | if (parameter == NULL) { |
935 | (CONFIG_KEYPAD == ARCHOS_AV300_PAD) | 1392 | /* Attempt to resume saved progress, otherwise start at beginning */ |
936 | rb->lcd_putsxy(3, 6, "[OFF] Quit"); | 1393 | if (!load(SOKOBAN_SAVE_FILE, true)) { |
937 | rb->lcd_putsxy(3, 16, "[ON] Undo"); | 1394 | init_boards(); |
938 | rb->lcd_putsxy(3, 26, "[PLAY] Redo"); | 1395 | if (!read_levels(true)) |
939 | rb->lcd_putsxy(3, 36, "[F1] Down a Level"); | 1396 | return PLUGIN_OK; |
940 | rb->lcd_putsxy(3, 46, "[F2] Restart Level"); | 1397 | load_level(); |
941 | rb->lcd_putsxy(3, 56, "[F3] Up a Level"); | 1398 | } |
942 | #elif CONFIG_KEYPAD == ONDIO_PAD | ||
943 | rb->lcd_putsxy(3, 6, "[OFF] Quit"); | ||
944 | rb->lcd_putsxy(3, 16, "[MODE] Undo"); | ||
945 | rb->lcd_putsxy(3, 26, "[MODE+DOWN] Redo"); | ||
946 | rb->lcd_putsxy(3, 36, "[MODE+LEFT] Down a Level"); | ||
947 | rb->lcd_putsxy(3, 46, "[MODE+UP] Restart Level"); | ||
948 | rb->lcd_putsxy(3, 56, "[MODE+RIGHT] Up Level"); | ||
949 | #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \ | ||
950 | (CONFIG_KEYPAD == IRIVER_H300_PAD) | ||
951 | rb->lcd_putsxy(3, 6, "[STOP] Quit"); | ||
952 | rb->lcd_putsxy(3, 16, "[REC] Undo"); | ||
953 | rb->lcd_putsxy(3, 26, "[MODE] Redo"); | ||
954 | rb->lcd_putsxy(3, 36, "[PLAY+DOWN] Down a Level"); | ||
955 | rb->lcd_putsxy(3, 46, "[PLAY] Restart Level"); | ||
956 | rb->lcd_putsxy(3, 56, "[PLAY+UP] Up a Level"); | ||
957 | #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \ | ||
958 | (CONFIG_KEYPAD == IPOD_3G_PAD) | ||
959 | rb->lcd_putsxy(3, 6, "[SELECT+MENU] Quit"); | ||
960 | rb->lcd_putsxy(3, 16, "[SELECT] Undo"); | ||
961 | rb->lcd_putsxy(3, 26, "[SELECT+PLAY] Redo"); | ||
962 | rb->lcd_putsxy(3, 36, "[SELECT+LEFT] Down a Level"); | ||
963 | rb->lcd_putsxy(3, 46, "[SELECT+RIGHT] Up a Level"); | ||
964 | #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD | ||
965 | rb->lcd_putsxy(3, 6, "[POWER] Quit"); | ||
966 | rb->lcd_putsxy(3, 16, "[SELECT] Undo"); | ||
967 | rb->lcd_putsxy(3, 26, "[REC] Down a Level"); | ||
968 | rb->lcd_putsxy(3, 36, "[PLAY] Up Level"); | ||
969 | #elif CONFIG_KEYPAD == GIGABEAT_PAD | ||
970 | rb->lcd_putsxy(3, 6, "[POWER] Quit"); | ||
971 | rb->lcd_putsxy(3, 16, "[SELECT] Undo"); | ||
972 | rb->lcd_putsxy(3, 26, "[A] Redo"); | ||
973 | rb->lcd_putsxy(3, 36, "[VOL-] Down a Level"); | ||
974 | rb->lcd_putsxy(3, 46, "[MENU] Restart Level"); | ||
975 | rb->lcd_putsxy(3, 56, "[VOL+] Up Level"); | ||
976 | #elif CONFIG_KEYPAD == SANSA_E200_PAD | ||
977 | rb->lcd_putsxy(3, 6, "[POWER] Quit"); | ||
978 | rb->lcd_putsxy(3, 16, "[SELECT] Undo"); | ||
979 | rb->lcd_putsxy(3, 26, "[REC] Redo"); | ||
980 | rb->lcd_putsxy(3, 36, "[SELECT+DOWN] Down a Level"); | ||
981 | rb->lcd_putsxy(3, 46, "[SELECT+RIGHT] Restart Level"); | ||
982 | rb->lcd_putsxy(3, 56, "[SELECT+UP] Up Level"); | ||
983 | #elif CONFIG_KEYPAD == IRIVER_H10_PAD | ||
984 | rb->lcd_putsxy(3, 6, "[POWER] Quit"); | ||
985 | rb->lcd_putsxy(3, 16, "[REW] Undo"); | ||
986 | rb->lcd_putsxy(3, 26, "[FF] Redo"); | ||
987 | rb->lcd_putsxy(3, 36, "[PLAY+DOWN] Down a Level"); | ||
988 | rb->lcd_putsxy(3, 46, "[PLAY+RIGHT] Restart Level"); | ||
989 | rb->lcd_putsxy(3, 56, "[PLAY+UP] Up Level"); | ||
990 | #endif | ||
991 | 1399 | ||
992 | rb->lcd_update(); | 1400 | } else { |
993 | rb->button_get(false); | 1401 | /* The plugin is being used to open a file */ |
994 | /* Display for 3 seconds or until keypress */ | 1402 | if (load((char*) parameter, false)) { |
995 | for (i = 0; i < 60; i++) { | 1403 | /* If we loaded & played a solution, quit */ |
996 | rb->sleep(HZ/20); | 1404 | if (current_info.level.boxes_to_go == 0) |
997 | button = rb->button_get(false); | 1405 | return PLUGIN_OK; |
998 | if (button && ((button & BUTTON_REL) != BUTTON_REL)) | 1406 | } else |
999 | break; | 1407 | return PLUGIN_OK; |
1000 | } | 1408 | } |
1001 | rb->lcd_clear_display(); | ||
1002 | |||
1003 | init_boards(); | ||
1004 | 1409 | ||
1005 | if (read_levels(1) != 0) | 1410 | rb->lcd_clear_display(); |
1006 | return PLUGIN_OK; | 1411 | update_screen(); |
1007 | 1412 | ||
1008 | return sokoban_loop(); | 1413 | return sokoban_loop(); |
1009 | } | 1414 | } |