HD300 - plugins keymaps
[kugel-rb.git] / apps / plugins / sokoban.c
blob6fe2b267aa04ceae52281a39bb8561ba67e4a7f7
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 Eric Linenberg
11 * February 2003: Robert Hak performs a cleanup/rewrite/feature addition.
12 * Eric smiles. Bjorn cries. Linus say 'huh?'.
13 * March 2007: Sean Morrisey performs a major rewrite/feature addition.
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
23 ****************************************************************************/
24 #include "plugin.h"
25 #include "lib/playback_control.h"
29 #define SOKOBAN_TITLE "Sokoban"
31 #define SOKOBAN_LEVELS_FILE PLUGIN_GAMES_DIR "/sokoban.levels"
32 #define SOKOBAN_SAVE_FILE PLUGIN_GAMES_DIR "/sokoban.save"
33 #define SOKOBAN_SAVE_FOLDER "/games"
35 #include "pluginbitmaps/sokoban_tiles.h"
36 #define SOKOBAN_TILESIZE BMPWIDTH_sokoban_tiles
38 /* If tilesize is 0 (which it is during dependency generation) gcc will abort
39 (div by 0) and this plugin won't get any dependencies
41 #if SOKOBAN_TILESIZE < 1
42 #define SOKOBAN_TILESIZE 10
43 #endif
45 /* SOKOBAN_TILESIZE is the number of pixels for each block.
46 * Set dynamically so all targets can support levels
47 * that fill their entire screen, less the stat box.
48 * 16 rows & 20 cols minimum */
49 #if LCD_WIDTH > LCD_HEIGHT /* horizontal layout*/
50 #define ROWS (LCD_HEIGHT/SOKOBAN_TILESIZE)
51 #if (LCD_WIDTH+4) >= (20*SOKOBAN_TILESIZE+40) /* wide or narrow stats box */
52 #define COLS ((LCD_WIDTH-40)/SOKOBAN_TILESIZE)
53 #else
54 #define COLS ((LCD_WIDTH-32)/SOKOBAN_TILESIZE)
55 #endif
56 #else /* vertical layout*/
57 #define ROWS ((LCD_HEIGHT-25)/SOKOBAN_TILESIZE)
58 #define COLS (LCD_WIDTH/SOKOBAN_TILESIZE)
59 #endif
61 /* size of code+bss */
62 #if CONFIG_CPU == SH7034
63 #define CODE_SIZE 0x3000 /* 12k */
64 #else
65 #define CODE_SIZE 0x5000 /* 20k */
66 #endif
68 #define CODE_AND_UNDO_SIZE (CODE_SIZE+0x1000) /* + 4k */
70 /* Use either all but code & undo of the plugin buffer for level data
71 * or 128k, which ever is less */
72 #if PLUGIN_BUFFER_SIZE - CODE_AND_UNDO_SIZE < 0x20000
73 #define MAX_LEVEL_DATA (PLUGIN_BUFFER_SIZE - CODE_AND_UNDO_SIZE)
74 #else
75 #define MAX_LEVEL_DATA 0x20000
76 #endif
78 /* Number of levels for which to allocate buffer indexes */
79 #define MAX_LEVELS MAX_LEVEL_DATA/70
81 /* Use remaining plugin buffer (- code prog) for undo, up to 64k */
82 #if PLUGIN_BUFFER_SIZE - MAX_LEVEL_DATA - CODE_SIZE > 0x10000
83 #define MAX_UNDOS 0x10000
84 #else
85 #define MAX_UNDOS (PLUGIN_BUFFER_SIZE - MAX_LEVEL_DATA - CODE_SIZE)
86 #endif
88 /* Move/push definitions for undo */
89 #define SOKOBAN_PUSH_LEFT 'L'
90 #define SOKOBAN_PUSH_RIGHT 'R'
91 #define SOKOBAN_PUSH_UP 'U'
92 #define SOKOBAN_PUSH_DOWN 'D'
93 #define SOKOBAN_MOVE_LEFT 'l'
94 #define SOKOBAN_MOVE_RIGHT 'r'
95 #define SOKOBAN_MOVE_UP 'u'
96 #define SOKOBAN_MOVE_DOWN 'd'
98 #define SOKOBAN_MOVE_DIFF (SOKOBAN_MOVE_LEFT-SOKOBAN_PUSH_LEFT)
99 #define SOKOBAN_MOVE_MIN SOKOBAN_MOVE_DOWN
101 /* variable button definitions */
102 #if (CONFIG_KEYPAD == RECORDER_PAD) || \
103 (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
104 #define SOKOBAN_LEFT BUTTON_LEFT
105 #define SOKOBAN_RIGHT BUTTON_RIGHT
106 #define SOKOBAN_UP BUTTON_UP
107 #define SOKOBAN_DOWN BUTTON_DOWN
108 #define SOKOBAN_MENU BUTTON_OFF
109 #define SOKOBAN_UNDO BUTTON_ON
110 #define SOKOBAN_REDO BUTTON_PLAY
111 #define SOKOBAN_LEVEL_DOWN BUTTON_F1
112 #define SOKOBAN_LEVEL_REPEAT BUTTON_F2
113 #define SOKOBAN_LEVEL_UP BUTTON_F3
114 #define SOKOBAN_PAUSE BUTTON_PLAY
115 #define BUTTON_SAVE BUTTON_ON
116 #define BUTTON_SAVE_NAME "ON"
118 #elif CONFIG_KEYPAD == ONDIO_PAD
119 #define SOKOBAN_LEFT BUTTON_LEFT
120 #define SOKOBAN_RIGHT BUTTON_RIGHT
121 #define SOKOBAN_UP BUTTON_UP
122 #define SOKOBAN_DOWN BUTTON_DOWN
123 #define SOKOBAN_MENU BUTTON_OFF
124 #define SOKOBAN_UNDO_PRE BUTTON_MENU
125 #define SOKOBAN_UNDO (BUTTON_MENU | BUTTON_REL)
126 #define SOKOBAN_REDO (BUTTON_MENU | BUTTON_DOWN)
127 #define SOKOBAN_LEVEL_DOWN (BUTTON_MENU | BUTTON_LEFT)
128 #define SOKOBAN_LEVEL_REPEAT (BUTTON_MENU | BUTTON_UP)
129 #define SOKOBAN_LEVEL_UP (BUTTON_MENU | BUTTON_RIGHT)
130 #define SOKOBAN_PAUSE BUTTON_MENU
131 #define BUTTON_SAVE BUTTON_MENU
132 #define BUTTON_SAVE_NAME "MENU"
134 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
135 (CONFIG_KEYPAD == IRIVER_H300_PAD)
136 #define SOKOBAN_LEFT BUTTON_LEFT
137 #define SOKOBAN_RIGHT BUTTON_RIGHT
138 #define SOKOBAN_UP BUTTON_UP
139 #define SOKOBAN_DOWN BUTTON_DOWN
140 #define SOKOBAN_MENU BUTTON_OFF
141 #define SOKOBAN_UNDO BUTTON_REC
142 #define SOKOBAN_REDO BUTTON_MODE
143 #define SOKOBAN_LEVEL_DOWN (BUTTON_ON | BUTTON_DOWN)
144 #define SOKOBAN_LEVEL_REPEAT BUTTON_ON
145 #define SOKOBAN_LEVEL_UP (BUTTON_ON | BUTTON_UP)
146 #define SOKOBAN_PAUSE BUTTON_ON
147 #define BUTTON_SAVE BUTTON_MODE
148 #define BUTTON_SAVE_NAME "MODE"
150 #define SOKOBAN_RC_MENU BUTTON_RC_STOP
152 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
153 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
154 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
155 #define SOKOBAN_LEFT BUTTON_LEFT
156 #define SOKOBAN_RIGHT BUTTON_RIGHT
157 #define SOKOBAN_UP BUTTON_MENU
158 #define SOKOBAN_DOWN BUTTON_PLAY
159 #define SOKOBAN_MENU (BUTTON_SELECT | BUTTON_MENU)
160 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
161 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
162 #define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_PLAY)
163 #define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_LEFT)
164 #define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_RIGHT)
165 #define SOKOBAN_PAUSE BUTTON_SELECT
166 #define BUTTON_SAVE BUTTON_SELECT
167 #define BUTTON_SAVE_NAME "SELECT"
169 /* FIXME: if/when simultaneous button presses work for X5/M5,
170 * add level up/down */
171 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
172 #define SOKOBAN_LEFT BUTTON_LEFT
173 #define SOKOBAN_RIGHT BUTTON_RIGHT
174 #define SOKOBAN_UP BUTTON_UP
175 #define SOKOBAN_DOWN BUTTON_DOWN
176 #define SOKOBAN_MENU BUTTON_POWER
177 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
178 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
179 #define SOKOBAN_LEVEL_REPEAT BUTTON_REC
180 #define SOKOBAN_REDO BUTTON_PLAY
181 #define SOKOBAN_PAUSE BUTTON_PLAY
182 #define BUTTON_SAVE BUTTON_SELECT
183 #define BUTTON_SAVE_NAME "SELECT"
185 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
186 #define SOKOBAN_LEFT BUTTON_LEFT
187 #define SOKOBAN_RIGHT BUTTON_RIGHT
188 #define SOKOBAN_UP BUTTON_SCROLL_UP
189 #define SOKOBAN_DOWN BUTTON_SCROLL_DOWN
190 #define SOKOBAN_MENU BUTTON_POWER
191 #define SOKOBAN_UNDO_PRE BUTTON_REW
192 #define SOKOBAN_UNDO (BUTTON_REW | BUTTON_REL)
193 #define SOKOBAN_REDO BUTTON_FF
194 #define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_SCROLL_DOWN)
195 #define SOKOBAN_LEVEL_REPEAT (BUTTON_PLAY | BUTTON_RIGHT)
196 #define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_SCROLL_UP)
197 #define SOKOBAN_PAUSE BUTTON_PLAY
198 #define BUTTON_SAVE BUTTON_PLAY
199 #define BUTTON_SAVE_NAME "PLAY"
201 #elif CONFIG_KEYPAD == GIGABEAT_PAD
202 #define SOKOBAN_LEFT BUTTON_LEFT
203 #define SOKOBAN_RIGHT BUTTON_RIGHT
204 #define SOKOBAN_UP BUTTON_UP
205 #define SOKOBAN_DOWN BUTTON_DOWN
206 #define SOKOBAN_MENU BUTTON_POWER
207 #define SOKOBAN_UNDO BUTTON_SELECT
208 #define SOKOBAN_REDO BUTTON_A
209 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
210 #define SOKOBAN_LEVEL_REPEAT BUTTON_MENU
211 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
212 #define SOKOBAN_PAUSE BUTTON_SELECT
213 #define BUTTON_SAVE BUTTON_SELECT
214 #define BUTTON_SAVE_NAME "SELECT"
216 #elif CONFIG_KEYPAD == SANSA_E200_PAD
217 #define SOKOBAN_LEFT BUTTON_LEFT
218 #define SOKOBAN_RIGHT BUTTON_RIGHT
219 #define SOKOBAN_UP BUTTON_UP
220 #define SOKOBAN_DOWN BUTTON_DOWN
221 #define SOKOBAN_MENU BUTTON_POWER
222 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
223 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
224 #define SOKOBAN_REDO BUTTON_REC
225 #define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_DOWN)
226 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
227 #define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_UP)
228 #define SOKOBAN_PAUSE BUTTON_SELECT
229 #define BUTTON_SAVE BUTTON_SELECT
230 #define BUTTON_SAVE_NAME "SELECT"
232 #elif CONFIG_KEYPAD == SANSA_FUZE_PAD
233 #define SOKOBAN_LEFT BUTTON_LEFT
234 #define SOKOBAN_RIGHT BUTTON_RIGHT
235 #define SOKOBAN_UP BUTTON_UP
236 #define SOKOBAN_DOWN BUTTON_DOWN
237 #define SOKOBAN_MENU (BUTTON_HOME|BUTTON_REPEAT)
238 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
239 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
240 #define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_LEFT)
241 #define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_DOWN)
242 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
243 #define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_UP)
244 #define SOKOBAN_PAUSE BUTTON_SELECT
245 #define BUTTON_SAVE BUTTON_SELECT
246 #define BUTTON_SAVE_NAME "SELECT"
248 #elif CONFIG_KEYPAD == SANSA_C200_PAD
249 #define SOKOBAN_LEFT BUTTON_LEFT
250 #define SOKOBAN_RIGHT BUTTON_RIGHT
251 #define SOKOBAN_UP BUTTON_UP
252 #define SOKOBAN_DOWN BUTTON_DOWN
253 #define SOKOBAN_MENU BUTTON_POWER
254 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
255 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
256 #define SOKOBAN_REDO BUTTON_REC
257 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
258 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
259 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
260 #define SOKOBAN_PAUSE BUTTON_SELECT
261 #define BUTTON_SAVE BUTTON_SELECT
262 #define BUTTON_SAVE_NAME "SELECT"
264 #elif CONFIG_KEYPAD == SANSA_CLIP_PAD
265 #define SOKOBAN_LEFT BUTTON_LEFT
266 #define SOKOBAN_RIGHT BUTTON_RIGHT
267 #define SOKOBAN_UP BUTTON_UP
268 #define SOKOBAN_DOWN BUTTON_DOWN
269 #define SOKOBAN_MENU BUTTON_POWER
270 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
271 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
272 #define SOKOBAN_REDO BUTTON_HOME
273 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
274 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
275 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
276 #define SOKOBAN_PAUSE BUTTON_SELECT
277 #define BUTTON_SAVE BUTTON_SELECT
278 #define BUTTON_SAVE_NAME "SELECT"
280 #elif CONFIG_KEYPAD == SANSA_M200_PAD
281 #define SOKOBAN_LEFT BUTTON_LEFT
282 #define SOKOBAN_RIGHT BUTTON_RIGHT
283 #define SOKOBAN_UP BUTTON_UP
284 #define SOKOBAN_DOWN BUTTON_DOWN
285 #define SOKOBAN_MENU BUTTON_POWER
286 #define SOKOBAN_UNDO_PRE BUTTON_SELECT
287 #define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
288 #define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_UP)
289 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
290 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
291 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
292 #define SOKOBAN_PAUSE BUTTON_SELECT
293 #define BUTTON_SAVE BUTTON_SELECT
294 #define BUTTON_SAVE_NAME "SELECT"
296 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
297 #define SOKOBAN_LEFT BUTTON_LEFT
298 #define SOKOBAN_RIGHT BUTTON_RIGHT
299 #define SOKOBAN_UP BUTTON_UP
300 #define SOKOBAN_DOWN BUTTON_DOWN
301 #define SOKOBAN_MENU BUTTON_MENU
302 #define SOKOBAN_UNDO BUTTON_VOL_UP
303 #define SOKOBAN_REDO BUTTON_VOL_DOWN
304 #define SOKOBAN_LEVEL_DOWN BUTTON_PREV
305 #define SOKOBAN_LEVEL_REPEAT BUTTON_PLAY
306 #define SOKOBAN_LEVEL_UP BUTTON_NEXT
307 #define SOKOBAN_PAUSE BUTTON_SELECT
308 #define BUTTON_SAVE BUTTON_SELECT
309 #define BUTTON_SAVE_NAME "SELECT"
311 #elif CONFIG_KEYPAD == MROBE100_PAD
312 #define SOKOBAN_LEFT BUTTON_LEFT
313 #define SOKOBAN_RIGHT BUTTON_RIGHT
314 #define SOKOBAN_UP BUTTON_UP
315 #define SOKOBAN_DOWN BUTTON_DOWN
316 #define SOKOBAN_MENU BUTTON_POWER
317 #define SOKOBAN_UNDO BUTTON_SELECT
318 #define SOKOBAN_REDO BUTTON_MENU
319 #define SOKOBAN_LEVEL_DOWN (BUTTON_DISPLAY | BUTTON_DOWN)
320 #define SOKOBAN_LEVEL_REPEAT (BUTTON_DISPLAY | BUTTON_RIGHT)
321 #define SOKOBAN_LEVEL_UP (BUTTON_DISPLAY | BUTTON_UP)
322 #define SOKOBAN_PAUSE BUTTON_SELECT
323 #define BUTTON_SAVE BUTTON_SELECT
324 #define BUTTON_SAVE_NAME "SELECT"
326 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
327 #define SOKOBAN_LEFT BUTTON_RC_REW
328 #define SOKOBAN_RIGHT BUTTON_RC_FF
329 #define SOKOBAN_UP BUTTON_RC_VOL_UP
330 #define SOKOBAN_DOWN BUTTON_RC_VOL_DOWN
331 #define SOKOBAN_MENU BUTTON_RC_REC
332 #define SOKOBAN_UNDO BUTTON_RC_MODE
333 #define SOKOBAN_REDO BUTTON_RC_MENU
334 #define SOKOBAN_PAUSE BUTTON_RC_PLAY
335 #define BUTTON_SAVE BUTTON_RC_PLAY
336 #define BUTTON_SAVE_NAME "PLAY"
338 #define SOKOBAN_RC_MENU BUTTON_REC
340 #elif CONFIG_KEYPAD == COWON_D2_PAD
341 #define SOKOBAN_MENU BUTTON_MENU
342 #define SOKOBAN_LEVEL_DOWN BUTTON_MINUS
343 #define SOKOBAN_LEVEL_UP BUTTON_PLUS
344 #define SOKOBAN_MENU_NAME "[MENU]"
346 #elif CONFIG_KEYPAD == IAUDIO67_PAD
347 #define SOKOBAN_LEFT BUTTON_LEFT
348 #define SOKOBAN_RIGHT BUTTON_RIGHT
349 #define SOKOBAN_UP BUTTON_STOP
350 #define SOKOBAN_DOWN BUTTON_PLAY
351 #define SOKOBAN_MENU BUTTON_MENU
352 #define SOKOBAN_UNDO BUTTON_VOLDOWN
353 #define SOKOBAN_REDO BUTTON_VOLUP
354 #define SOKOBAN_PAUSE (BUTTON_MENU|BUTTON_LEFT)
355 #define BUTTON_SAVE (BUTTON_MENU|BUTTON_PLAY)
356 #define BUTTON_SAVE_NAME "MENU+PLAY"
358 #define SOKOBAN_RC_MENU (BUTTON_MENU|BUTTON_STOP)
360 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
361 #define SOKOBAN_LEFT BUTTON_LEFT
362 #define SOKOBAN_RIGHT BUTTON_RIGHT
363 #define SOKOBAN_UP BUTTON_UP
364 #define SOKOBAN_DOWN BUTTON_DOWN
365 #define SOKOBAN_MENU BUTTON_MENU
366 #define SOKOBAN_UNDO BUTTON_BACK
367 #define SOKOBAN_REDO (BUTTON_BACK | BUTTON_PLAY)
368 #define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_DOWN)
369 #define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
370 #define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_UP)
371 #define SOKOBAN_PAUSE BUTTON_PLAY
372 #define BUTTON_SAVE BUTTON_CUSTOM
373 #define BUTTON_SAVE_NAME "CUSTOM"
375 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
376 #define SOKOBAN_LEFT BUTTON_LEFT
377 #define SOKOBAN_RIGHT BUTTON_RIGHT
378 #define SOKOBAN_UP BUTTON_UP
379 #define SOKOBAN_DOWN BUTTON_DOWN
380 #define SOKOBAN_MENU BUTTON_MENU
381 #define SOKOBAN_UNDO BUTTON_VIEW
382 #define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_VIEW)
383 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
384 #define SOKOBAN_LEVEL_REPEAT BUTTON_POWER
385 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
386 #define SOKOBAN_PAUSE BUTTON_SELECT
387 #define BUTTON_SAVE BUTTON_PLAYLIST
388 #define BUTTON_SAVE_NAME "PLAYLIST"
390 #elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
391 #define SOKOBAN_LEFT BUTTON_LEFT
392 #define SOKOBAN_RIGHT BUTTON_RIGHT
393 #define SOKOBAN_UP BUTTON_UP
394 #define SOKOBAN_DOWN BUTTON_DOWN
395 #define SOKOBAN_MENU BUTTON_MENU
396 #define SOKOBAN_UNDO BUTTON_PREV
397 #define SOKOBAN_REDO (BUTTON_PLAY | BUTTON_PREV)
398 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
399 #define SOKOBAN_LEVEL_REPEAT BUTTON_POWER
400 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
401 #define SOKOBAN_PAUSE BUTTON_PLAY
402 #define BUTTON_SAVE BUTTON_NEXT
403 #define BUTTON_SAVE_NAME "NEXT"
405 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
406 #define SOKOBAN_LEFT BUTTON_PREV
407 #define SOKOBAN_RIGHT BUTTON_NEXT
408 #define SOKOBAN_UP BUTTON_UP
409 #define SOKOBAN_DOWN BUTTON_DOWN
410 #define SOKOBAN_MENU BUTTON_MENU
411 #define SOKOBAN_UNDO BUTTON_LEFT
412 #define SOKOBAN_REDO (BUTTON_LEFT | BUTTON_PLAY)
413 #define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
414 #define SOKOBAN_LEVEL_REPEAT BUTTON_POWER
415 #define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
416 #define SOKOBAN_PAUSE BUTTON_PLAY
417 #define BUTTON_SAVE BUTTON_RIGHT
418 #define BUTTON_SAVE_NAME "RIGHT"
420 #elif CONFIG_KEYPAD == ONDAVX747_PAD
421 #define SOKOBAN_MENU BUTTON_MENU
422 #define SOKOBAN_MENU_NAME "[MENU]"
424 #elif CONFIG_KEYPAD == ONDAVX777_PAD
425 #define SOKOBAN_MENU BUTTON_POWER
426 #define SOKOBAN_MENU_NAME "[POWER]"
428 #elif CONFIG_KEYPAD == MROBE500_PAD
430 #define SOKOBAN_MENU BUTTON_POWER
431 #define SOKOBAN_MENU_NAME "[POWER]"
433 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
434 #define SOKOBAN_LEFT BUTTON_LEFT
435 #define SOKOBAN_RIGHT BUTTON_RIGHT
436 #define SOKOBAN_UP BUTTON_UP
437 #define SOKOBAN_DOWN BUTTON_DOWN
438 #define SOKOBAN_MENU BUTTON_REC
439 #define SOKOBAN_UNDO_PRE BUTTON_REW
440 #define SOKOBAN_UNDO (BUTTON_REW | BUTTON_LEFT)
441 #define SOKOBAN_REDO BUTTON_FFWD
442 #define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_DOWN)
443 #define SOKOBAN_LEVEL_REPEAT (BUTTON_PLAY | BUTTON_RIGHT)
444 #define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_UP)
445 #define SOKOBAN_PAUSE BUTTON_PLAY
446 #define BUTTON_SAVE BUTTON_PLAY
447 #define BUTTON_SAVE_NAME "PLAY"
449 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
450 #define SOKOBAN_LEFT BUTTON_PREV
451 #define SOKOBAN_RIGHT BUTTON_NEXT
452 #define SOKOBAN_UP BUTTON_UP
453 #define SOKOBAN_DOWN BUTTON_DOWN
454 #define SOKOBAN_MENU BUTTON_REC
455 #define SOKOBAN_UNDO BUTTON_CANCEL
456 #define SOKOBAN_REDO BUTTON_OK
457 #define SOKOBAN_LEVEL_DOWN (BUTTON_OK | BUTTON_PREV)
458 #define SOKOBAN_LEVEL_REPEAT (BUTTON_OK | BUTTON_CANCEL)
459 #define SOKOBAN_LEVEL_UP (BUTTON_OK | BUTTON_NEXT)
460 #define SOKOBAN_PAUSE BUTTON_PLAY
461 #define BUTTON_SAVE BUTTON_MENU
462 #define BUTTON_SAVE_NAME "MENU"
464 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
465 #define SOKOBAN_LEFT BUTTON_VOL_DOWN
466 #define SOKOBAN_RIGHT BUTTON_VOL_UP
467 #define SOKOBAN_UP BUTTON_REW
468 #define SOKOBAN_DOWN BUTTON_FF
469 #define SOKOBAN_MENU BUTTON_FUNC
470 #define SOKOBAN_UNDO (BUTTON_PLAY | BUTTON_REW)
471 #define SOKOBAN_REDO (BUTTON_PLAY | BUTTON_FF)
472 #define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_VOL_DOWN)
473 #define SOKOBAN_LEVEL_REPEAT BUTTON_REC
474 #define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_VOL_UP)
475 #define SOKOBAN_PAUSE BUTTON_PLAY
476 #define BUTTON_SAVE (BUTTON_PLAY|BUTTON_FUNC)
477 #define BUTTON_SAVE_NAME "PLAY+FUNC"
479 #elif CONFIG_KEYPAD == MPIO_HD300_PAD
480 #define SOKOBAN_LEFT BUTTON_REW
481 #define SOKOBAN_RIGHT BUTTON_FF
482 #define SOKOBAN_UP BUTTON_UP
483 #define SOKOBAN_DOWN BUTTON_DOWN
484 #define SOKOBAN_MENU BUTTON_MENU
485 #define SOKOBAN_UNDO (BUTTON_PLAY | BUTTON_REW)
486 #define SOKOBAN_REDO (BUTTON_PLAY | BUTTON_FF)
487 #define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_DOWN)
488 #define SOKOBAN_LEVEL_REPEAT BUTTON_REC
489 #define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_UP)
490 #define SOKOBAN_PAUSE BUTTON_PLAY
491 #define BUTTON_SAVE (BUTTON_PLAY|BUTTON_ENTER)
492 #define BUTTON_SAVE_NAME "PLAY+ENTER"
494 #else
495 #error No keymap defined!
496 #endif
498 #ifdef HAVE_TOUCHSCREEN
499 #ifndef SOKOBAN_LEFT
500 #define SOKOBAN_LEFT BUTTON_MIDLEFT
501 #endif
502 #ifndef SOKOBAN_RIGHT
503 #define SOKOBAN_RIGHT BUTTON_MIDRIGHT
504 #endif
505 #ifndef SOKOBAN_UP
506 #define SOKOBAN_UP BUTTON_TOPMIDDLE
507 #endif
508 #ifndef SOKOBAN_DOWN
509 #define SOKOBAN_DOWN BUTTON_BOTTOMMIDDLE
510 #endif
511 #ifndef SOKOBAN_MENU
512 #define SOKOBAN_MENU BUTTON_TOPLEFT
513 #define SOKOBAN_MENU_NAME "[TOPLEFT]"
514 #endif
515 #ifndef SOKOBAN_UNDO
516 #define SOKOBAN_UNDO BUTTON_BOTTOMRIGHT
517 #define SOKOBAN_UNDO_NAME "[BOTTOMRIGHT]"
518 #endif
519 #ifndef SOKOBAN_REDO
520 #define SOKOBAN_REDO BUTTON_BOTTOMLEFT
521 #define SOKOBAN_REDO_NAME "[BOTTOMLEFT]"
522 #endif
523 #ifndef SOKOBAN_PAUSE
524 #define SOKOBAN_PAUSE BUTTON_CENTER
525 #define SOKOBAN_PAUSE_NAME "[CENTER]"
526 #endif
527 #ifndef SOKOBAN_LEVEL_REPEAT
528 #define SOKOBAN_LEVEL_REPEAT BUTTON_TOPRIGHT
529 #define SOKOBAN_LEVEL_REPEAT_NAME "[TOPRIGHT]"
530 #endif
531 #ifndef BUTTON_SAVE
532 #define BUTTON_SAVE BUTTON_CENTER
533 #define BUTTON_SAVE_NAME "CENTER"
534 #endif
535 #endif
537 #define SOKOBAN_FONT FONT_SYSFIXED
540 /* The Location, Undo and LevelInfo structs are OO-flavored.
541 * (oooh!-flavored as Schnueff puts it.) It makes more you have to know,
542 * but the overall data layout becomes more manageable. */
544 /* Level data & stats */
545 struct LevelInfo {
546 int index; /* Level index (level number - 1) */
547 int moves; /* Moves & pushes for the stats */
548 int pushes;
549 short boxes_to_go; /* Number of unplaced boxes remaining in level */
550 short height; /* Height & width for centering level display */
551 short width;
554 struct Location {
555 short row;
556 short col;
559 /* Our full undo history */
560 static struct UndoInfo {
561 int count; /* How many undos have been done */
562 int current; /* Which history is the current undo */
563 int max; /* Which history is the max redoable */
564 char history[MAX_UNDOS];
565 } undo_info;
567 /* Our playing board */
568 static struct BoardInfo {
569 char board[ROWS][COLS]; /* The current board data */
570 struct LevelInfo level; /* Level data & stats */
571 struct Location player; /* Where the player is */
572 int max_level; /* The number of levels we have */
573 } current_info;
575 static struct BufferedBoards {
576 char filename[MAX_PATH]; /* Filename of the levelset we're using */
577 char data[MAX_LEVEL_DATA]; /* Buffered level data */
578 int index[MAX_LEVELS + 1]; /* Where each buffered board begins & ends */
579 int start; /* Index of first buffered board */
580 int end; /* Index of last buffered board */
581 short prebuffered_boards; /* Number of boards before current to store */
582 } buffered_boards;
585 static char buf[ROWS*(COLS + 1)]; /* Enough for a whole board or a filename */
588 static void init_undo(void)
590 undo_info.count = 0;
591 undo_info.current = 0;
592 undo_info.max = 0;
595 static void get_delta(char direction, short *d_r, short *d_c)
597 switch (direction) {
598 case SOKOBAN_PUSH_LEFT:
599 case SOKOBAN_MOVE_LEFT:
600 *d_r = 0;
601 *d_c = -1;
602 break;
603 case SOKOBAN_PUSH_RIGHT:
604 case SOKOBAN_MOVE_RIGHT:
605 *d_r = 0;
606 *d_c = 1;
607 break;
608 case SOKOBAN_PUSH_UP:
609 case SOKOBAN_MOVE_UP:
610 *d_r = -1;
611 *d_c = 0;
612 break;
613 case SOKOBAN_PUSH_DOWN:
614 case SOKOBAN_MOVE_DOWN:
615 *d_r = 1;
616 *d_c = 0;
620 static bool undo(void)
622 char undo;
623 short r, c;
624 short d_r = 0, d_c = 0; /* delta row & delta col */
625 char *space_cur, *space_next, *space_prev;
626 bool undo_push = false;
628 /* If no more undos or we've wrapped all the way around, quit */
629 if (undo_info.count == 0 || undo_info.current - 1 == undo_info.max)
630 return false;
632 /* Move to previous undo in the list */
633 if (undo_info.current == 0 && undo_info.count > 1)
634 undo_info.current = MAX_UNDOS - 1;
635 else
636 undo_info.current--;
638 undo_info.count--;
640 undo = undo_info.history[undo_info.current];
642 if (undo < SOKOBAN_MOVE_MIN)
643 undo_push = true;
645 get_delta(undo, &d_r, &d_c);
647 r = current_info.player.row;
648 c = current_info.player.col;
650 /* Give the 3 spaces we're going to use better names */
651 space_cur = &current_info.board[r][c];
652 space_next = &current_info.board[r + d_r][c + d_c];
653 space_prev = &current_info.board[r - d_r][c - d_c];
655 /* Update board info */
656 if (undo_push) {
657 /* Moving box from goal to floor */
658 if (*space_next == '*' && *space_cur == '@')
659 current_info.level.boxes_to_go++;
660 /* Moving box from floor to goal */
661 else if (*space_next == '$' && *space_cur == '+')
662 current_info.level.boxes_to_go--;
664 /* Move box off of next space... */
665 *space_next = (*space_next == '*' ? '.' : ' ');
666 /* ...and on to current space */
667 *space_cur = (*space_cur == '+' ? '*' : '$');
669 current_info.level.pushes--;
670 } else
671 /* Just move player off of current space */
672 *space_cur = (*space_cur == '+' ? '.' : ' ');
673 /* Move player back to previous space */
674 *space_prev = (*space_prev == '.' ? '+' : '@');
676 /* Update position */
677 current_info.player.row -= d_r;
678 current_info.player.col -= d_c;
680 current_info.level.moves--;
682 return true;
685 static void add_undo(char undo)
687 undo_info.history[undo_info.current] = undo;
689 /* Wrap around if MAX_UNDOS exceeded */
690 if (undo_info.current < (MAX_UNDOS - 1))
691 undo_info.current++;
692 else
693 undo_info.current = 0;
695 if (undo_info.count < MAX_UNDOS)
696 undo_info.count++;
699 static bool move(char direction, bool redo)
701 short r, c;
702 short d_r = 0, d_c = 0; /* delta row & delta col */
703 char *space_cur, *space_next, *space_beyond;
704 bool push = false;
706 get_delta(direction, &d_r, &d_c);
708 r = current_info.player.row;
709 c = current_info.player.col;
711 /* Check for out-of-bounds */
712 if (r + 2*d_r < 0 || r + 2*d_r >= ROWS ||
713 c + 2*d_c < 0 || c + 2*d_c >= COLS)
714 return false;
716 /* Give the 3 spaces we're going to use better names */
717 space_cur = &current_info.board[r][c];
718 space_next = &current_info.board[r + d_r][c + d_c];
719 space_beyond = &current_info.board[r + 2*d_r][c + 2*d_c];
721 if (*space_next == '$' || *space_next == '*') {
722 /* Change direction from move to push for undo */
723 if (direction >= SOKOBAN_MOVE_MIN)
724 direction -= SOKOBAN_MOVE_DIFF;
725 push = true;
727 else if (direction < SOKOBAN_MOVE_MIN)
728 /* Change back to move if redo/solution playback push is invalid */
729 direction += SOKOBAN_MOVE_DIFF;
731 /* Update board info */
732 if (push) {
733 /* Moving box from goal to floor */
734 if (*space_next == '*' && *space_beyond == ' ')
735 current_info.level.boxes_to_go++;
736 /* Moving box from floor to goal */
737 else if (*space_next == '$' && *space_beyond == '.')
738 current_info.level.boxes_to_go--;
739 /* Check for invalid move */
740 else if (*space_beyond != '.' && *space_beyond != ' ')
741 return false;
743 /* Move player onto next space */
744 *space_next = (*space_next == '*' ? '+' : '@');
745 /* Move box onto space beyond next */
746 *space_beyond = (*space_beyond == '.' ? '*' : '$');
748 current_info.level.pushes++;
749 } else {
750 /* Check for invalid move */
751 if (*space_next != '.' && *space_next != ' ')
752 return false;
754 /* Move player onto next space */
755 *space_next = (*space_next == '.' ? '+' : '@');
757 /* Move player off of current space */
758 *space_cur = (*space_cur == '+' ? '.' : ' ');
760 /* Update position */
761 current_info.player.row += d_r;
762 current_info.player.col += d_c;
764 current_info.level.moves++;
766 /* Update undo_info.max to current on every normal move,
767 * except if it's the same as a redo. */
768 /* normal move and either */
769 if (!redo &&
770 /* moves have been undone... */
771 ((undo_info.max != undo_info.current &&
772 /* ...and the current move is NOT the same as the one in history */
773 undo_info.history[undo_info.current] != direction) ||
774 /* or moves have not been undone */
775 undo_info.max == undo_info.current)) {
776 add_undo(direction);
777 undo_info.max = undo_info.current;
778 } else /* redo move or move was same as redo */
779 add_undo(direction); /* add_undo to update current */
781 return true;
784 #ifdef SOKOBAN_REDO
785 static bool redo(void)
787 /* If no moves have been undone, quit */
788 if (undo_info.current == undo_info.max)
789 return false;
791 return move(undo_info.history[(undo_info.current < MAX_UNDOS ?
792 undo_info.current : 0)], true);
794 #endif
796 static void init_boards(void)
798 rb->strlcpy(buffered_boards.filename, SOKOBAN_LEVELS_FILE,
799 sizeof(buffered_boards.filename));
801 current_info.level.index = 0;
802 current_info.player.row = 0;
803 current_info.player.col = 0;
804 current_info.max_level = 0;
806 buffered_boards.start = 0;
807 buffered_boards.end = 0;
808 buffered_boards.prebuffered_boards = 0;
810 init_undo();
813 static bool read_levels(bool initialize)
815 int fd = 0;
816 short len;
817 short lastlen = 0;
818 short row = 0;
819 int level_count = 0;
821 int i = 0;
822 int level_len = 0;
823 bool index_set = false;
825 /* Get the index of the first level to buffer */
826 if (current_info.level.index > buffered_boards.prebuffered_boards &&
827 !initialize)
828 buffered_boards.start = current_info.level.index -
829 buffered_boards.prebuffered_boards;
830 else
831 buffered_boards.start = 0;
833 if ((fd = rb->open(buffered_boards.filename, O_RDONLY)) < 0) {
834 rb->splashf(HZ*2, "Unable to open %s", buffered_boards.filename);
835 return false;
838 do {
839 len = rb->read_line(fd, buf, sizeof(buf));
841 /* Correct len when trailing \r's or \n's are counted */
842 if (len > 2 && buf[len - 2] == '\0')
843 len -= 2;
844 else if (len > 1 && buf[len - 1] == '\0')
845 len--;
847 /* Skip short lines & lines with non-level data */
848 if (len >= 3 && ((buf[0] >= '1' && buf[0] <= '9') || buf[0] == '#' ||
849 buf[0] == ' ' || buf[0] == '-' || buf[0] == '_')) {
850 if (level_count >= buffered_boards.start) {
851 /* Set the index of this level */
852 if (!index_set &&
853 level_count - buffered_boards.start < MAX_LEVELS) {
854 buffered_boards.index[level_count - buffered_boards.start]
855 = i;
856 index_set = true;
858 /* Copy buffer to board data */
859 if (i + level_len + len < MAX_LEVEL_DATA) {
860 rb->memcpy(&buffered_boards.data[i + level_len], buf, len);
861 buffered_boards.data[i + level_len + len] = '\n';
864 level_len += len + 1;
865 row++;
867 /* If newline & level is tall enough or is RLE */
868 } else if (buf[0] == '\0' && (row > 2 || lastlen > 22)) {
869 level_count++;
870 if (level_count >= buffered_boards.start) {
871 i += level_len;
872 if (i < MAX_LEVEL_DATA)
873 buffered_boards.end = level_count;
874 else if (!initialize)
875 break;
877 row = 0;
878 level_len = 0;
879 index_set = false;
881 } else if (len > 22)
882 len = 1;
884 } while ((lastlen = len));
886 /* Set the index of the end of the last level */
887 if (level_count - buffered_boards.start < MAX_LEVELS)
888 buffered_boards.index[level_count - buffered_boards.start] = i;
890 if (initialize) {
891 current_info.max_level = level_count;
892 buffered_boards.prebuffered_boards = buffered_boards.end/2;
895 rb->close(fd);
897 return true;
900 static void load_level(void)
902 int c, r;
903 int i, n;
904 int level_size;
905 int index = current_info.level.index - buffered_boards.start;
906 char *level;
908 /* Get the buffered board index of the current level */
909 if (current_info.level.index < buffered_boards.start ||
910 current_info.level.index >= buffered_boards.end) {
911 read_levels(false);
912 if (current_info.level.index > buffered_boards.prebuffered_boards)
913 index = buffered_boards.prebuffered_boards;
914 else
915 index = current_info.level.index;
917 level = &buffered_boards.data[buffered_boards.index[index]];
919 /* Reset level info */
920 current_info.level.moves = 0;
921 current_info.level.pushes = 0;
922 current_info.level.boxes_to_go = 0;
923 current_info.level.width = 0;
925 /* Clear board */
926 for (r = 0; r < ROWS; r++)
927 for (c = 0; c < COLS; c++)
928 current_info.board[r][c] = 'X';
930 level_size = buffered_boards.index[index + 1] -
931 buffered_boards.index[index];
933 for (r = 0, c = 0, n = 1, i = 0; i < level_size; i++) {
934 if (level[i] == '\n' || level[i] == '|') {
935 if (c > 3) {
936 /* Update max width of level & go to next row */
937 if (c > current_info.level.width)
938 current_info.level.width = c;
939 c = 0;
940 r++;
941 if (r >= ROWS)
942 break;
944 } else if (c < COLS) {
945 /* Read RLE character's length into n */
946 if (level[i] >= '0' && level[i] <= '9') {
947 n = level[i++] - '0';
948 if (level[i] >= '0' && level[i] <= '9')
949 n = n*10 + level[i++] - '0';
952 /* Cleanup & replace */
953 if (level[i] == '%')
954 level[i] = '*';
955 else if (level[i] == '-' || level[i] == '_')
956 level[i] = ' ';
958 if (n > 1) {
959 if (c + n >= COLS)
960 n = COLS - c;
962 if (level[i] == '.')
963 current_info.level.boxes_to_go += n;
965 /* Put RLE character n times */
966 while (n--)
967 current_info.board[r][c++] = level[i];
968 n = 1;
970 } else {
971 if (level[i] == '.' || level[i] == '+')
972 current_info.level.boxes_to_go++;
974 if (level[i] == '@' ||level[i] == '+') {
975 current_info.player.row = r;
976 current_info.player.col = c;
979 current_info.board[r][c++] = level[i];
984 current_info.level.height = r;
986 #if LCD_DEPTH > 2
987 /* Fill in blank space outside level on color targets */
988 for (r = 0; r < ROWS; r++)
989 for (c = 0; current_info.board[r][c] == ' ' && c < COLS; c++)
990 current_info.board[r][c] = 'X';
992 for (c = 0; c < COLS; c++) {
993 for (r = 0; (current_info.board[r][c] == ' ' ||
994 current_info.board[r][c] == 'X') && r < ROWS; r++)
995 current_info.board[r][c] = 'X';
996 for (r = ROWS - 1; (current_info.board[r][c] == ' ' ||
997 current_info.board[r][c] == 'X') && r >= 0; r--)
998 current_info.board[r][c] = 'X';
1000 #endif
1003 static void update_screen(void)
1005 int c, r;
1006 int rows, cols;
1008 #if LCD_WIDTH - (COLS*SOKOBAN_TILESIZE) < 32
1009 #define STAT_HEIGHT 25
1010 #define STAT_X (LCD_WIDTH - 120)/2
1011 #define STAT_Y (LCD_HEIGHT - STAT_HEIGHT)
1012 #define BOARD_WIDTH LCD_WIDTH
1013 #define BOARD_HEIGHT (LCD_HEIGHT - STAT_HEIGHT)
1014 rb->lcd_putsxy(STAT_X + 4, STAT_Y + 4, "Level");
1015 rb->lcd_putsxyf(STAT_X + 7, STAT_Y + 14, "%d", current_info.level.index + 1);
1016 rb->lcd_putsxy(STAT_X + 41, STAT_Y + 4, "Moves");
1017 rb->lcd_putsxyf(STAT_X + 44, STAT_Y + 14, "%d", current_info.level.moves);
1018 rb->lcd_putsxy(STAT_X + 79, STAT_Y + 4, "Pushes");
1019 rb->lcd_putsxyf(STAT_X + 82, STAT_Y + 14, "%d", current_info.level.pushes);
1021 rb->lcd_drawrect(STAT_X, STAT_Y, 38, STAT_HEIGHT);
1022 rb->lcd_drawrect(STAT_X + 37, STAT_Y, 39, STAT_HEIGHT);
1023 rb->lcd_drawrect(STAT_X + 75, STAT_Y, 45, STAT_HEIGHT);
1024 #else
1025 #if LCD_WIDTH - (COLS*SOKOBAN_TILESIZE) > 40
1026 #define STAT_X (LCD_WIDTH - 40)
1027 #else
1028 #define STAT_X COLS*SOKOBAN_TILESIZE
1029 #endif
1030 #define STAT_Y (LCD_HEIGHT - 64)/2
1031 #define STAT_WIDTH (LCD_WIDTH - STAT_X)
1032 #define BOARD_WIDTH (LCD_WIDTH - STAT_WIDTH)
1033 #define BOARD_HEIGHT LCD_HEIGHT
1034 rb->lcd_putsxy(STAT_X + 1, STAT_Y + 2, "Level");
1035 rb->lcd_putsxyf(STAT_X + 4, STAT_Y + 12, "%d", current_info.level.index + 1);
1036 rb->lcd_putsxy(STAT_X + 1, STAT_Y + 23, "Moves");
1037 rb->lcd_putsxyf(STAT_X + 4, STAT_Y + 33, "%d", current_info.level.moves);
1038 #if STAT_WIDTH < 38
1039 rb->lcd_putsxy(STAT_X + 1, STAT_Y + 44, "Push");
1040 #else
1041 rb->lcd_putsxy(STAT_X + 1, STAT_Y + 44, "Pushes");
1042 #endif
1043 rb->lcd_putsxyf(STAT_X + 4, STAT_Y + 54, "%d", current_info.level.pushes);
1045 rb->lcd_drawrect(STAT_X, STAT_Y + 0, STAT_WIDTH, 64);
1046 rb->lcd_hline(STAT_X, LCD_WIDTH - 1, STAT_Y + 21);
1047 rb->lcd_hline(STAT_X, LCD_WIDTH - 1, STAT_Y + 42);
1049 #endif
1051 /* load the board to the screen */
1052 for (rows = 0; rows < ROWS; rows++) {
1053 for (cols = 0; cols < COLS; cols++) {
1054 c = cols*SOKOBAN_TILESIZE +
1055 (BOARD_WIDTH - current_info.level.width*SOKOBAN_TILESIZE)/2;
1056 r = rows*SOKOBAN_TILESIZE +
1057 (BOARD_HEIGHT - current_info.level.height*SOKOBAN_TILESIZE)/2;
1059 switch(current_info.board[rows][cols]) {
1060 case 'X': /* blank space outside of level */
1061 break;
1063 case ' ': /* floor */
1064 rb->lcd_bitmap_part(sokoban_tiles, 0, 0*SOKOBAN_TILESIZE,
1065 STRIDE( SCREEN_MAIN,
1066 BMPWIDTH_sokoban_tiles,
1067 BMPHEIGHT_sokoban_tiles),
1068 c, r, SOKOBAN_TILESIZE, SOKOBAN_TILESIZE);
1069 break;
1071 case '#': /* wall */
1072 rb->lcd_bitmap_part(sokoban_tiles, 0, 1*SOKOBAN_TILESIZE,
1073 STRIDE( SCREEN_MAIN,
1074 BMPWIDTH_sokoban_tiles,
1075 BMPHEIGHT_sokoban_tiles),
1076 c, r, SOKOBAN_TILESIZE, SOKOBAN_TILESIZE);
1077 break;
1079 case '$': /* box */
1080 rb->lcd_bitmap_part(sokoban_tiles, 0, 2*SOKOBAN_TILESIZE,
1081 STRIDE( SCREEN_MAIN,
1082 BMPWIDTH_sokoban_tiles,
1083 BMPHEIGHT_sokoban_tiles),
1084 c, r, SOKOBAN_TILESIZE,SOKOBAN_TILESIZE);
1085 break;
1087 case '*': /* box on goal */
1088 rb->lcd_bitmap_part(sokoban_tiles, 0, 3*SOKOBAN_TILESIZE,
1089 STRIDE( SCREEN_MAIN,
1090 BMPWIDTH_sokoban_tiles,
1091 BMPHEIGHT_sokoban_tiles),
1092 c, r, SOKOBAN_TILESIZE, SOKOBAN_TILESIZE);
1093 break;
1095 case '.': /* goal */
1096 rb->lcd_bitmap_part(sokoban_tiles, 0, 4*SOKOBAN_TILESIZE,
1097 STRIDE( SCREEN_MAIN,
1098 BMPWIDTH_sokoban_tiles,
1099 BMPHEIGHT_sokoban_tiles),
1100 c, r, SOKOBAN_TILESIZE, SOKOBAN_TILESIZE);
1101 break;
1103 case '@': /* player */
1104 rb->lcd_bitmap_part(sokoban_tiles, 0, 5*SOKOBAN_TILESIZE,
1105 STRIDE( SCREEN_MAIN,
1106 BMPWIDTH_sokoban_tiles,
1107 BMPHEIGHT_sokoban_tiles),
1108 c, r, SOKOBAN_TILESIZE, SOKOBAN_TILESIZE);
1109 break;
1111 case '+': /* player on goal */
1112 rb->lcd_bitmap_part(sokoban_tiles, 0, 6*SOKOBAN_TILESIZE,
1113 STRIDE( SCREEN_MAIN,
1114 BMPWIDTH_sokoban_tiles,
1115 BMPHEIGHT_sokoban_tiles),
1116 c, r, SOKOBAN_TILESIZE, SOKOBAN_TILESIZE);
1117 break;
1122 /* print out the screen */
1123 rb->lcd_update();
1126 static void draw_level(void)
1128 load_level();
1129 rb->lcd_clear_display();
1130 update_screen();
1133 static bool save(char *filename, bool solution)
1135 int fd;
1136 char *loc;
1137 DIR *dir;
1138 char dirname[MAX_PATH];
1140 rb->splash(0, "Saving...");
1142 /* Create dir if it doesn't exist */
1143 if ((loc = rb->strrchr(filename, '/')) != NULL) {
1144 rb->strlcpy(dirname, filename, loc - filename + 1);
1146 if(!(dir = rb->opendir(dirname)))
1147 rb->mkdir(dirname);
1148 else
1149 rb->closedir(dir);
1152 if (filename[0] == '\0' ||
1153 (fd = rb->open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) {
1154 rb->splashf(HZ*2, "Unable to open %s", filename);
1155 return false;
1158 /* Sokoban: S/P for solution/progress : level number : current undo */
1159 rb->snprintf(buf, sizeof(buf), "Sokoban:%c:%d:%d\n", (solution ? 'S' : 'P'),
1160 current_info.level.index + 1, undo_info.current);
1161 rb->write(fd, buf, rb->strlen(buf));
1163 /* Filename of levelset */
1164 rb->write(fd, buffered_boards.filename,
1165 rb->strlen(buffered_boards.filename));
1166 rb->write(fd, "\n", 1);
1168 /* Full undo history */
1169 rb->write(fd, undo_info.history, undo_info.max);
1171 rb->close(fd);
1173 return true;
1176 static bool load(char *filename, bool silent)
1178 int fd;
1179 int i, n;
1180 int len;
1181 int button;
1182 bool play_solution;
1183 bool paused = false;
1184 unsigned short speed = 2;
1185 int delay[] = {HZ/2, HZ/3, HZ/4, HZ/6, HZ/8, HZ/12, HZ/16, HZ/25};
1187 if (filename[0] == '\0' || (fd = rb->open(filename, O_RDONLY)) < 0) {
1188 if (!silent)
1189 rb->splashf(HZ*2, "Unable to open %s", filename);
1190 return false;
1193 /* Read header, level number, & current undo */
1194 rb->read_line(fd, buf, sizeof(buf));
1196 /* If we're opening a level file, not a solution/progress file */
1197 if (rb->strncmp(buf, "Sokoban", 7) != 0) {
1198 rb->close(fd);
1200 rb->strlcpy(buffered_boards.filename, filename,
1201 sizeof(buffered_boards.filename));
1203 if (!read_levels(true))
1204 return false;
1206 current_info.level.index = 0;
1207 load_level();
1209 /* If there aren't any boxes to go or the player position wasn't set,
1210 * the file probably wasn't a Sokoban level file */
1211 if (current_info.level.boxes_to_go == 0 ||
1212 current_info.player.row == 0 || current_info.player.col == 0) {
1213 if (!silent)
1214 rb->splash(HZ*2, "File is not a Sokoban level file");
1215 return false;
1218 } else {
1220 /* Read filename of levelset */
1221 rb->read_line(fd, buffered_boards.filename,
1222 sizeof(buffered_boards.filename));
1224 /* Read full undo history */
1225 len = rb->read_line(fd, undo_info.history, MAX_UNDOS);
1227 /* Correct len when trailing \r's or \n's are counted */
1228 if (len > 2 && undo_info.history[len - 2] == '\0')
1229 len -= 2;
1230 else if (len > 1 && undo_info.history[len - 1] == '\0')
1231 len--;
1233 rb->close(fd);
1235 /* Check to see if we're going to play a solution or resume progress */
1236 play_solution = (buf[8] == 'S');
1238 /* Get level number */
1239 for (n = 0, i = 10; buf[i] >= '0' && buf[i] <= '9' && i < 15; i++)
1240 n = n*10 + buf[i] - '0';
1241 current_info.level.index = n - 1;
1243 /* Get undo index */
1244 for (n = 0, i++; buf[i] >= '0' && buf[i] <= '9' && i < 21; i++)
1245 n = n*10 + buf[i] - '0';
1246 if (n > len)
1247 n = len;
1248 undo_info.max = len;
1250 if (current_info.level.index < 0) {
1251 if (!silent)
1252 rb->splash(HZ*2, "Error loading level");
1253 return false;
1255 if (!read_levels(true))
1256 return false;
1257 if (current_info.level.index >= current_info.max_level) {
1258 if (!silent)
1259 rb->splash(HZ*2, "Error loading level");
1260 return false;
1263 load_level();
1265 if (play_solution) {
1266 rb->lcd_clear_display();
1267 update_screen();
1268 rb->sleep(2*delay[speed]);
1270 /* Replay solution until menu button is pressed */
1271 i = 0;
1272 while (true) {
1273 if (i < len) {
1274 if (!move(undo_info.history[i], true)) {
1275 n = i;
1276 break;
1278 rb->lcd_clear_display();
1279 update_screen();
1280 i++;
1281 } else
1282 paused = true;
1284 rb->sleep(delay[speed]);
1286 while ((button = rb->button_get(false)) || paused) {
1287 switch (button) {
1288 case SOKOBAN_MENU:
1289 /* Pretend the level is complete so we'll quit */
1290 current_info.level.boxes_to_go = 0;
1291 return true;
1293 case SOKOBAN_PAUSE:
1294 /* Toggle pause state */
1295 paused = !paused;
1296 break;
1298 case SOKOBAN_LEFT:
1299 case SOKOBAN_LEFT | BUTTON_REPEAT:
1300 /* Go back one move */
1301 if (paused) {
1302 if (undo())
1303 i--;
1304 rb->lcd_clear_display();
1305 update_screen();
1307 break;
1309 case SOKOBAN_RIGHT:
1310 case SOKOBAN_RIGHT | BUTTON_REPEAT:
1311 /* Go forward one move */
1312 if (paused) {
1313 if (redo())
1314 i++;
1315 rb->lcd_clear_display();
1316 update_screen();
1318 break;
1320 case SOKOBAN_UP:
1321 case SOKOBAN_UP | BUTTON_REPEAT:
1322 /* Speed up */
1323 if (speed < sizeof(delay)/sizeof(int) - 1)
1324 speed++;
1325 break;
1327 case SOKOBAN_DOWN:
1328 case SOKOBAN_DOWN | BUTTON_REPEAT:
1329 /* Slow down */
1330 if (speed > 0)
1331 speed--;
1334 if (paused)
1335 rb->sleep(HZ/33);
1339 /* If level is complete, wait for keypress before quitting */
1340 if (current_info.level.boxes_to_go == 0)
1341 rb->button_get(true);
1343 } else {
1344 /* Advance to current undo */
1345 for (i = 0; i < n; i++) {
1346 if (!move(undo_info.history[i], true)) {
1347 n = i;
1348 break;
1352 rb->button_clear_queue();
1353 rb->lcd_clear_display();
1356 undo_info.current = n;
1359 return true;
1362 static int sokoban_menu(void)
1364 int button;
1365 int selection = 0;
1366 int i;
1367 bool menu_quit;
1368 int start_selected = 0;
1369 int prev_level = current_info.level.index;
1371 MENUITEM_STRINGLIST(menu, "Sokoban Menu", NULL,
1372 "Resume", "Select Level", "Audio Playback", "Keys",
1373 "Load Default Level Set", "Quit Without Saving",
1374 "Save Progress & Quit");
1376 do {
1377 menu_quit = true;
1378 selection = rb->do_menu(&menu, &start_selected, NULL, false);
1380 switch (selection) {
1381 case 0: /* Resume */
1382 break;
1384 case 1: /* Select level */
1385 current_info.level.index++;
1386 rb->set_int("Select Level", "", UNIT_INT,
1387 &current_info.level.index, NULL, 1, 1,
1388 current_info.max_level, NULL);
1389 current_info.level.index--;
1390 if (prev_level != current_info.level.index) {
1391 init_undo();
1392 draw_level();
1393 } else
1394 menu_quit = false;
1395 break;
1397 case 2: /* Audio playback control */
1398 playback_control(NULL);
1399 menu_quit = false;
1400 break;
1402 case 3: /* Keys */
1403 FOR_NB_SCREENS(i)
1404 rb->screens[i]->clear_display();
1405 rb->lcd_setfont(SOKOBAN_FONT);
1407 #if (CONFIG_KEYPAD == RECORDER_PAD) || \
1408 (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
1409 rb->lcd_putsxy(3, 6, "[OFF] Menu");
1410 rb->lcd_putsxy(3, 16, "[ON] Undo");
1411 rb->lcd_putsxy(3, 26, "[PLAY] Redo");
1412 rb->lcd_putsxy(3, 36, "[F1] Down a Level");
1413 rb->lcd_putsxy(3, 46, "[F2] Restart Level");
1414 rb->lcd_putsxy(3, 56, "[F3] Up a Level");
1415 #elif CONFIG_KEYPAD == ONDIO_PAD
1416 rb->lcd_putsxy(3, 6, "[OFF] Menu");
1417 rb->lcd_putsxy(3, 16, "[MODE] Undo");
1418 rb->lcd_putsxy(3, 26, "[MODE+DOWN] Redo");
1419 rb->lcd_putsxy(3, 36, "[MODE+LEFT] Previous Level");
1420 rb->lcd_putsxy(3, 46, "[MODE+UP] Restart Level");
1421 rb->lcd_putsxy(3, 56, "[MODE+RIGHT] Up Level");
1422 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
1423 (CONFIG_KEYPAD == IRIVER_H300_PAD)
1424 rb->lcd_putsxy(3, 6, "[STOP] Menu");
1425 rb->lcd_putsxy(3, 16, "[REC] Undo");
1426 rb->lcd_putsxy(3, 26, "[MODE] Redo");
1427 rb->lcd_putsxy(3, 36, "[PLAY+DOWN] Previous Level");
1428 rb->lcd_putsxy(3, 46, "[PLAY] Restart Level");
1429 rb->lcd_putsxy(3, 56, "[PLAY+UP] Next Level");
1430 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
1431 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
1432 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
1433 rb->lcd_putsxy(3, 6, "[SELECT+MENU] Menu");
1434 rb->lcd_putsxy(3, 16, "[SELECT] Undo");
1435 rb->lcd_putsxy(3, 26, "[SELECT+PLAY] Redo");
1436 rb->lcd_putsxy(3, 36, "[SELECT+LEFT] Previous Level");
1437 rb->lcd_putsxy(3, 46, "[SELECT+RIGHT] Next Level");
1438 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
1439 rb->lcd_putsxy(3, 6, "[POWER] Menu");
1440 rb->lcd_putsxy(3, 16, "[SELECT] Undo");
1441 rb->lcd_putsxy(3, 26, "[PLAY] Redo");
1442 rb->lcd_putsxy(3, 36, "[REC] Restart Level");
1443 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
1444 rb->lcd_putsxy(3, 6, "[POWER] Menu");
1445 rb->lcd_putsxy(3, 16, "[REW] Undo");
1446 rb->lcd_putsxy(3, 26, "[FF] Redo");
1447 rb->lcd_putsxy(3, 36, "[PLAY+DOWN] Previous Level");
1448 rb->lcd_putsxy(3, 46, "[PLAY+RIGHT] Restart Level");
1449 rb->lcd_putsxy(3, 56, "[PLAY+UP] Next Level");
1450 #elif CONFIG_KEYPAD == GIGABEAT_PAD
1451 rb->lcd_putsxy(3, 6, "[POWER] Menu");
1452 rb->lcd_putsxy(3, 16, "[SELECT] Undo");
1453 rb->lcd_putsxy(3, 26, "[A] Redo");
1454 rb->lcd_putsxy(3, 36, "[VOL-] Previous Level");
1455 rb->lcd_putsxy(3, 46, "[MENU] Restart Level");
1456 rb->lcd_putsxy(3, 56, "[VOL+] Next Level");
1457 #elif CONFIG_KEYPAD == SANSA_E200_PAD
1458 rb->lcd_putsxy(3, 6, "[POWER] Menu");
1459 rb->lcd_putsxy(3, 16, "[SELECT] Undo");
1460 rb->lcd_putsxy(3, 26, "[REC] Redo");
1461 rb->lcd_putsxy(3, 36, "[SELECT+DOWN] Previous Level");
1462 rb->lcd_putsxy(3, 46, "[SELECT+RIGHT] Restart Level");
1463 rb->lcd_putsxy(3, 56, "[SELECT+UP] Next Level");
1464 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
1465 rb->lcd_putsxy(3, 6, "[MENU] Menu");
1466 rb->lcd_putsxy(3, 16, "[VOL+] Undo");
1467 rb->lcd_putsxy(3, 26, "[VOL-] Redo");
1468 rb->lcd_putsxy(3, 36, "[PREV] Previous Level");
1469 rb->lcd_putsxy(3, 46, "[PLAY] Restart Level");
1470 rb->lcd_putsxy(3, 56, "[NEXT] Next Level");
1471 #endif
1473 #ifdef HAVE_TOUCHSCREEN
1474 rb->lcd_putsxy(3, 6, SOKOBAN_MENU_NAME " Menu");
1475 rb->lcd_putsxy(3, 16, SOKOBAN_UNDO_NAME " Undo");
1476 rb->lcd_putsxy(3, 26, SOKOBAN_REDO_NAME " Redo");
1477 rb->lcd_putsxy(3, 36, SOKOBAN_PAUSE_NAME " Pause");
1478 rb->lcd_putsxy(3, 46, SOKOBAN_LEVEL_REPEAT_NAME " Restart Level");
1479 #endif
1481 FOR_NB_SCREENS(i)
1482 rb->screens[i]->update();
1484 /* Display until keypress */
1485 do {
1486 rb->sleep(HZ/20);
1487 button = rb->button_get(false);
1488 } while (!button || button & BUTTON_REL ||
1489 button & BUTTON_REPEAT);
1491 menu_quit = false;
1492 break;
1494 case 4: /* Load default levelset */
1495 init_boards();
1496 if (!read_levels(true))
1497 return 5; /* Quit */
1498 load_level();
1499 break;
1501 case 5: /* Quit */
1502 break;
1504 case 6: /* Save & quit */
1505 save(SOKOBAN_SAVE_FILE, false);
1506 rb->reload_directory();
1509 } while (!menu_quit);
1511 /* Restore font */
1512 rb->lcd_setfont(SOKOBAN_FONT);
1514 FOR_NB_SCREENS(i) {
1515 rb->screens[i]->clear_display();
1516 rb->screens[i]->update();
1519 return selection;
1522 static bool sokoban_loop(void)
1524 bool moved;
1525 int i = 0, button = 0, lastbutton = 0;
1526 short r = 0, c = 0;
1527 int w, h;
1528 char *loc;
1530 while (true) {
1531 moved = false;
1533 r = current_info.player.row;
1534 c = current_info.player.col;
1536 button = rb->button_get(true);
1538 switch(button)
1540 #ifdef SOKOBAN_RC_MENU
1541 case SOKOBAN_RC_MENU:
1542 #endif
1543 case SOKOBAN_MENU:
1544 switch (sokoban_menu()) {
1545 case 5: /* Quit */
1546 case 6: /* Save & quit */
1547 return PLUGIN_OK;
1549 update_screen();
1550 break;
1552 case SOKOBAN_UNDO:
1553 #ifdef SOKOBAN_UNDO_PRE
1554 if (lastbutton != SOKOBAN_UNDO_PRE)
1555 break;
1556 #else /* repeat can't work here for Ondio, iPod, et al */
1557 case SOKOBAN_UNDO | BUTTON_REPEAT:
1558 #endif
1559 undo();
1560 rb->lcd_clear_display();
1561 update_screen();
1562 break;
1564 #ifdef SOKOBAN_REDO
1565 case SOKOBAN_REDO:
1566 case SOKOBAN_REDO | BUTTON_REPEAT:
1567 moved = redo();
1568 rb->lcd_clear_display();
1569 update_screen();
1570 break;
1571 #endif
1573 #ifdef SOKOBAN_LEVEL_UP
1574 case SOKOBAN_LEVEL_UP:
1575 case SOKOBAN_LEVEL_UP | BUTTON_REPEAT:
1576 /* next level */
1577 init_undo();
1578 if (current_info.level.index + 1 < current_info.max_level)
1579 current_info.level.index++;
1581 draw_level();
1582 break;
1583 #endif
1585 #ifdef SOKOBAN_LEVEL_DOWN
1586 case SOKOBAN_LEVEL_DOWN:
1587 case SOKOBAN_LEVEL_DOWN | BUTTON_REPEAT:
1588 /* previous level */
1589 init_undo();
1590 if (current_info.level.index > 0)
1591 current_info.level.index--;
1593 draw_level();
1594 break;
1595 #endif
1597 #ifdef SOKOBAN_LEVEL_REPEAT
1598 case SOKOBAN_LEVEL_REPEAT:
1599 case SOKOBAN_LEVEL_REPEAT | BUTTON_REPEAT:
1600 /* same level */
1601 init_undo();
1602 draw_level();
1603 break;
1604 #endif
1606 case SOKOBAN_LEFT:
1607 case SOKOBAN_LEFT | BUTTON_REPEAT:
1608 moved = move(SOKOBAN_MOVE_LEFT, false);
1609 break;
1611 case SOKOBAN_RIGHT:
1612 case SOKOBAN_RIGHT | BUTTON_REPEAT:
1613 moved = move(SOKOBAN_MOVE_RIGHT, false);
1614 break;
1616 case SOKOBAN_UP:
1617 case SOKOBAN_UP | BUTTON_REPEAT:
1618 moved = move(SOKOBAN_MOVE_UP, false);
1619 break;
1621 case SOKOBAN_DOWN:
1622 case SOKOBAN_DOWN | BUTTON_REPEAT:
1623 moved = move(SOKOBAN_MOVE_DOWN, false);
1624 break;
1626 default:
1627 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
1628 return PLUGIN_USB_CONNECTED;
1629 break;
1632 lastbutton = button;
1634 if (moved) {
1635 rb->lcd_clear_display();
1636 update_screen();
1639 /* We have completed this level */
1640 if (current_info.level.boxes_to_go == 0) {
1642 if (moved) {
1643 rb->lcd_clear_display();
1645 /* Show level complete message & stats */
1646 rb->snprintf(buf, sizeof(buf), "Level %d Complete!",
1647 current_info.level.index + 1);
1648 rb->lcd_getstringsize(buf, &w, &h);
1649 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h*3, buf);
1651 rb->snprintf(buf, sizeof(buf), "%4d Moves ",
1652 current_info.level.moves);
1653 rb->lcd_getstringsize(buf, &w, &h);
1654 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h, buf);
1656 rb->snprintf(buf, sizeof(buf), "%4d Pushes",
1657 current_info.level.pushes);
1658 rb->lcd_getstringsize(buf, &w, &h);
1659 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2, buf);
1661 if (undo_info.count < MAX_UNDOS) {
1662 rb->snprintf(buf, sizeof(buf), "%s: Save solution",
1663 BUTTON_SAVE_NAME);
1664 rb->lcd_getstringsize(buf, &w, &h);
1665 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + h*2, buf);
1668 rb->lcd_update();
1669 rb->sleep(HZ/4);
1670 rb->button_clear_queue();
1672 /* Display for 4 seconds or until new keypress */
1673 for (i = 0; i < 80; i++) {
1674 rb->sleep(HZ/20);
1675 button = rb->button_get(false);
1676 if (button && !(button & BUTTON_REL) &&
1677 !(button & BUTTON_REPEAT))
1678 break;
1681 if (button == BUTTON_SAVE) {
1682 if (undo_info.count < MAX_UNDOS) {
1683 /* Set filename to current levelset plus level number
1684 * and .sok extension. Use SAVE_FOLDER if using the
1685 * default levelset, since it's in a hidden folder. */
1686 if (rb->strcmp(buffered_boards.filename,
1687 SOKOBAN_LEVELS_FILE) == 0) {
1688 rb->snprintf(buf, sizeof(buf),
1689 "%s/sokoban.%d.sok",
1690 SOKOBAN_SAVE_FOLDER,
1691 current_info.level.index + 1);
1692 } else {
1693 if ((loc = rb->strrchr(buffered_boards.filename,
1694 '.')) != NULL)
1695 *loc = '\0';
1696 rb->snprintf(buf, sizeof(buf), "%s.%d.sok",
1697 buffered_boards.filename,
1698 current_info.level.index + 1);
1699 if (loc != NULL)
1700 *loc = '.';
1703 if (!rb->kbd_input(buf, MAX_PATH))
1704 save(buf, true);
1705 } else
1706 rb->splash(HZ*2, "Solution too long to save");
1708 rb->lcd_setfont(SOKOBAN_FONT); /* Restore font */
1712 FOR_NB_SCREENS(i) {
1713 rb->screens[i]->clear_display();
1714 rb->screens[i]->update();
1717 current_info.level.index++;
1719 /* clear undo stats */
1720 init_undo();
1722 if (current_info.level.index >= current_info.max_level) {
1723 /* Show levelset complete message */
1724 rb->snprintf(buf, sizeof(buf), "You WIN!!");
1725 rb->lcd_getstringsize(buf, &w, &h);
1726 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h/2, buf);
1728 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1729 /* Display for 4 seconds or until keypress */
1730 for (i = 0; i < 80; i++) {
1731 rb->lcd_fillrect(0, 0, LCD_WIDTH, LCD_HEIGHT);
1732 rb->lcd_update();
1733 rb->sleep(HZ/10);
1735 button = rb->button_get(false);
1736 if (button && !(button & BUTTON_REL))
1737 break;
1739 rb->lcd_set_drawmode(DRMODE_SOLID);
1741 /* Reset to first level & show quit menu */
1742 current_info.level.index = 0;
1744 switch (sokoban_menu()) {
1745 case 5: /* Quit */
1746 case 6: /* Save & quit */
1747 return PLUGIN_OK;
1751 load_level();
1752 update_screen();
1755 } /* end while */
1757 return PLUGIN_OK;
1761 enum plugin_status plugin_start(const void* parameter)
1763 int w, h;
1765 (void)(parameter);
1767 rb->lcd_setfont(SOKOBAN_FONT);
1769 rb->lcd_clear_display();
1770 rb->lcd_getstringsize(SOKOBAN_TITLE, &w, &h);
1771 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h/2, SOKOBAN_TITLE);
1772 rb->lcd_update();
1773 rb->sleep(HZ); /* Show title for 1 second */
1775 init_boards();
1777 if (parameter == NULL) {
1778 /* Attempt to resume saved progress, otherwise start at beginning */
1779 if (!load(SOKOBAN_SAVE_FILE, true)) {
1780 init_boards();
1781 if (!read_levels(true))
1782 return PLUGIN_OK;
1783 load_level();
1786 } else {
1787 /* The plugin is being used to open a file */
1788 if (load((char*) parameter, false)) {
1789 /* If we loaded & played a solution, quit */
1790 if (current_info.level.boxes_to_go == 0)
1791 return PLUGIN_OK;
1792 } else
1793 return PLUGIN_OK;
1796 rb->lcd_clear_display();
1797 update_screen();
1799 return sokoban_loop();