blob: 77813a5104187937157bb7a65d9e625ed68ec481 [file] [log] [blame]
Björn Stenbergba371fb2003-06-29 16:33:04 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
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?'.
Antoine Cellerierf91d06d2007-06-28 20:45:00 +000013 * March 2007: Sean Morrisey performs a major rewrite/feature addition.
Björn Stenbergba371fb2003-06-29 16:33:04 +000014 *
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000015 * 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.
Björn Stenbergba371fb2003-06-29 16:33:04 +000019 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
24#include "plugin.h"
Antoine Cellerierf91d06d2007-06-28 20:45:00 +000025#include "lib/playback_control.h"
Björn Stenbergba371fb2003-06-29 16:33:04 +000026
Jens Arnolda36b1d42006-01-15 18:20:18 +000027PLUGIN_HEADER
28
Antoine Cellerierf91d06d2007-06-28 20:45:00 +000029#define SOKOBAN_TITLE "Sokoban"
Björn Stenbergba371fb2003-06-29 16:33:04 +000030
Jonathan Gordonfda7d722007-08-06 13:42:52 +000031#define SOKOBAN_LEVELS_FILE PLUGIN_GAMES_DIR "/sokoban.levels"
32#define SOKOBAN_SAVE_FILE PLUGIN_GAMES_DIR "/sokoban.save"
Antoine Cellerierec7252c2007-06-29 19:52:13 +000033#define SOKOBAN_SAVE_FOLDER "/games"
Björn Stenbergba371fb2003-06-29 16:33:04 +000034
Marianne Arnold4ee3fed2008-05-18 13:05:45 +000035#include "sokoban_tiles.h"
36#define SOKOBAN_TILESIZE BMPWIDTH_sokoban_tiles
37/* SOKOBAN_TILESIZE is the number of pixels for each block.
Antoine Cellerierf91d06d2007-06-28 20:45:00 +000038 * Set dynamically so all targets can support levels
39 * that fill their entire screen, less the stat box.
40 * 16 rows & 20 cols minimum */
Marianne Arnold4ee3fed2008-05-18 13:05:45 +000041#if LCD_WIDTH > LCD_HEIGHT /* horizontal layout*/
42#define ROWS (LCD_HEIGHT/SOKOBAN_TILESIZE)
43#if (LCD_WIDTH+4) >= (20*SOKOBAN_TILESIZE+40) /* wide or narrow stats box */
44#define COLS ((LCD_WIDTH-40)/SOKOBAN_TILESIZE)
Zakk Robertsffbdb252007-02-13 06:48:10 +000045#else
Marianne Arnold4ee3fed2008-05-18 13:05:45 +000046#define COLS ((LCD_WIDTH-32)/SOKOBAN_TILESIZE)
47#endif
48#else /* vertical layout*/
49#define ROWS ((LCD_HEIGHT-25)/SOKOBAN_TILESIZE)
50#define COLS (LCD_WIDTH/SOKOBAN_TILESIZE)
Zakk Robertsffbdb252007-02-13 06:48:10 +000051#endif
Antoine Cellerierf91d06d2007-06-28 20:45:00 +000052
53/* Use either all but 16k of the plugin buffer for level data
54 * or 128k, which ever is less */
55#if PLUGIN_BUFFER_SIZE - 0x4000 < 0x20000
56#define MAX_LEVEL_DATA (PLUGIN_BUFFER_SIZE - 0x4000)
Zakk Robertsffbdb252007-02-13 06:48:10 +000057#else
Antoine Cellerierf91d06d2007-06-28 20:45:00 +000058#define MAX_LEVEL_DATA 0x20000
59#endif
60
61/* Number of levels for which to allocate buffer indexes */
62#define MAX_LEVELS MAX_LEVEL_DATA/70
63
64/* Use 4k plus remaining plugin buffer (-12k for prog) for undo, up to 64k */
65#if PLUGIN_BUFFER_SIZE - MAX_LEVEL_DATA - 0x3000 > 0x10000
66#define MAX_UNDOS 0x10000
67#else
68#define MAX_UNDOS (PLUGIN_BUFFER_SIZE - MAX_LEVEL_DATA - 0x3000)
Zakk Robertsffbdb252007-02-13 06:48:10 +000069#endif
70
71/* Move/push definitions for undo */
Antoine Cellerierf91d06d2007-06-28 20:45:00 +000072#define SOKOBAN_PUSH_LEFT 'L'
73#define SOKOBAN_PUSH_RIGHT 'R'
74#define SOKOBAN_PUSH_UP 'U'
75#define SOKOBAN_PUSH_DOWN 'D'
76#define SOKOBAN_MOVE_LEFT 'l'
77#define SOKOBAN_MOVE_RIGHT 'r'
78#define SOKOBAN_MOVE_UP 'u'
79#define SOKOBAN_MOVE_DOWN 'd'
80
81#define SOKOBAN_MOVE_DIFF (SOKOBAN_MOVE_LEFT-SOKOBAN_PUSH_LEFT)
82#define SOKOBAN_MOVE_MIN SOKOBAN_MOVE_DOWN
Björn Stenbergba371fb2003-06-29 16:33:04 +000083
Jens Arnolde35a6582004-10-18 21:45:00 +000084/* variable button definitions */
Antoine Cellerierf91d06d2007-06-28 20:45:00 +000085#if (CONFIG_KEYPAD == RECORDER_PAD) || \
86 (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
Jens Arnold29361ab2008-03-22 10:24:28 +000087#define SOKOBAN_LEFT BUTTON_LEFT
88#define SOKOBAN_RIGHT BUTTON_RIGHT
Dave Chapman54d44c82005-12-14 01:31:37 +000089#define SOKOBAN_UP BUTTON_UP
90#define SOKOBAN_DOWN BUTTON_DOWN
Antoine Cellerierf91d06d2007-06-28 20:45:00 +000091#define SOKOBAN_MENU BUTTON_OFF
Jens Arnolde35a6582004-10-18 21:45:00 +000092#define SOKOBAN_UNDO BUTTON_ON
Zakk Robertsffbdb252007-02-13 06:48:10 +000093#define SOKOBAN_REDO BUTTON_PLAY
Jens Arnolde35a6582004-10-18 21:45:00 +000094#define SOKOBAN_LEVEL_DOWN BUTTON_F1
95#define SOKOBAN_LEVEL_REPEAT BUTTON_F2
Dave Chapmand64e6262007-01-14 13:48:09 +000096#define SOKOBAN_LEVEL_UP BUTTON_F3
Antoine Cellerierec7252c2007-06-29 19:52:13 +000097#define SOKOBAN_PAUSE BUTTON_PLAY
Antoine Cellerierf91d06d2007-06-28 20:45:00 +000098#define BUTTON_SAVE BUTTON_ON
99#define BUTTON_SAVE_NAME "ON"
Dave Chapmand64e6262007-01-14 13:48:09 +0000100
Jens Arnolde35a6582004-10-18 21:45:00 +0000101#elif CONFIG_KEYPAD == ONDIO_PAD
Jens Arnold29361ab2008-03-22 10:24:28 +0000102#define SOKOBAN_LEFT BUTTON_LEFT
103#define SOKOBAN_RIGHT BUTTON_RIGHT
Dave Chapman54d44c82005-12-14 01:31:37 +0000104#define SOKOBAN_UP BUTTON_UP
105#define SOKOBAN_DOWN BUTTON_DOWN
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000106#define SOKOBAN_MENU BUTTON_OFF
Jens Arnolde35a6582004-10-18 21:45:00 +0000107#define SOKOBAN_UNDO_PRE BUTTON_MENU
108#define SOKOBAN_UNDO (BUTTON_MENU | BUTTON_REL)
Zakk Robertsffbdb252007-02-13 06:48:10 +0000109#define SOKOBAN_REDO (BUTTON_MENU | BUTTON_DOWN)
Jens Arnolde35a6582004-10-18 21:45:00 +0000110#define SOKOBAN_LEVEL_DOWN (BUTTON_MENU | BUTTON_LEFT)
111#define SOKOBAN_LEVEL_REPEAT (BUTTON_MENU | BUTTON_UP)
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000112#define SOKOBAN_LEVEL_UP (BUTTON_MENU | BUTTON_RIGHT)
Antoine Cellerierec7252c2007-06-29 19:52:13 +0000113#define SOKOBAN_PAUSE BUTTON_MENU
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000114#define BUTTON_SAVE BUTTON_MENU
115#define BUTTON_SAVE_NAME "MENU"
Jens Arnolde35a6582004-10-18 21:45:00 +0000116
Daniel Stenberg01377e22005-06-29 12:47:24 +0000117#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
118 (CONFIG_KEYPAD == IRIVER_H300_PAD)
Jens Arnold29361ab2008-03-22 10:24:28 +0000119#define SOKOBAN_LEFT BUTTON_LEFT
120#define SOKOBAN_RIGHT BUTTON_RIGHT
Dave Chapman54d44c82005-12-14 01:31:37 +0000121#define SOKOBAN_UP BUTTON_UP
122#define SOKOBAN_DOWN BUTTON_DOWN
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000123#define SOKOBAN_MENU BUTTON_OFF
Zakk Robertsffbdb252007-02-13 06:48:10 +0000124#define SOKOBAN_UNDO BUTTON_REC
125#define SOKOBAN_REDO BUTTON_MODE
Zakk Robertsffbdb252007-02-13 06:48:10 +0000126#define SOKOBAN_LEVEL_DOWN (BUTTON_ON | BUTTON_DOWN)
127#define SOKOBAN_LEVEL_REPEAT BUTTON_ON
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000128#define SOKOBAN_LEVEL_UP (BUTTON_ON | BUTTON_UP)
Antoine Cellerierec7252c2007-06-29 19:52:13 +0000129#define SOKOBAN_PAUSE BUTTON_ON
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000130#define BUTTON_SAVE BUTTON_MODE
131#define BUTTON_SAVE_NAME "MODE"
Dave Chapman54d44c82005-12-14 01:31:37 +0000132
Antoine Cellerierec7252c2007-06-29 19:52:13 +0000133#define SOKOBAN_RC_MENU BUTTON_RC_STOP
Kevin Ferrare0e027bd2006-06-30 16:43:47 +0000134
Dave Chapmanfb4e3842006-02-24 20:54:09 +0000135#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
Jens Arnoldb7013222007-07-27 09:57:27 +0000136 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
137 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
Jens Arnold29361ab2008-03-22 10:24:28 +0000138#define SOKOBAN_LEFT BUTTON_LEFT
139#define SOKOBAN_RIGHT BUTTON_RIGHT
Dave Chapman54d44c82005-12-14 01:31:37 +0000140#define SOKOBAN_UP BUTTON_MENU
141#define SOKOBAN_DOWN BUTTON_PLAY
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000142#define SOKOBAN_MENU (BUTTON_SELECT | BUTTON_MENU)
Dave Chapman54d44c82005-12-14 01:31:37 +0000143#define SOKOBAN_UNDO_PRE BUTTON_SELECT
144#define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
Zakk Robertsffbdb252007-02-13 06:48:10 +0000145#define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_PLAY)
Dave Chapman54d44c82005-12-14 01:31:37 +0000146#define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_LEFT)
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000147#define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_RIGHT)
Antoine Cellerierec7252c2007-06-29 19:52:13 +0000148#define SOKOBAN_PAUSE BUTTON_SELECT
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000149#define BUTTON_SAVE BUTTON_SELECT
150#define BUTTON_SAVE_NAME "SELECT"
Dave Chapman54d44c82005-12-14 01:31:37 +0000151
Antoine Cellerierec7252c2007-06-29 19:52:13 +0000152/* FIXME: if/when simultaneous button presses work for X5/M5,
153 * add level up/down */
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000154#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
Jens Arnold29361ab2008-03-22 10:24:28 +0000155#define SOKOBAN_LEFT BUTTON_LEFT
156#define SOKOBAN_RIGHT BUTTON_RIGHT
Daniel Stenbergcedba882006-01-18 11:09:06 +0000157#define SOKOBAN_UP BUTTON_UP
158#define SOKOBAN_DOWN BUTTON_DOWN
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000159#define SOKOBAN_MENU BUTTON_POWER
Linus Nielsen Feltzing568ac3e2006-02-24 15:06:25 +0000160#define SOKOBAN_UNDO_PRE BUTTON_SELECT
161#define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
Antoine Cellerierec7252c2007-06-29 19:52:13 +0000162#define SOKOBAN_LEVEL_REPEAT BUTTON_REC
163#define SOKOBAN_REDO BUTTON_PLAY
164#define SOKOBAN_PAUSE BUTTON_PLAY
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000165#define BUTTON_SAVE BUTTON_SELECT
166#define BUTTON_SAVE_NAME "SELECT"
Daniel Stenbergcedba882006-01-18 11:09:06 +0000167
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000168#elif CONFIG_KEYPAD == IRIVER_H10_PAD
Jens Arnold29361ab2008-03-22 10:24:28 +0000169#define SOKOBAN_LEFT BUTTON_LEFT
170#define SOKOBAN_RIGHT BUTTON_RIGHT
Daniel Stenberg1e88be52006-08-03 20:17:25 +0000171#define SOKOBAN_UP BUTTON_SCROLL_UP
172#define SOKOBAN_DOWN BUTTON_SCROLL_DOWN
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000173#define SOKOBAN_MENU BUTTON_POWER
Daniel Stenberg1e88be52006-08-03 20:17:25 +0000174#define SOKOBAN_UNDO_PRE BUTTON_REW
175#define SOKOBAN_UNDO (BUTTON_REW | BUTTON_REL)
Zakk Robertsffbdb252007-02-13 06:48:10 +0000176#define SOKOBAN_REDO BUTTON_FF
Daniel Stenberg1e88be52006-08-03 20:17:25 +0000177#define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY | BUTTON_SCROLL_DOWN)
Zakk Robertsffbdb252007-02-13 06:48:10 +0000178#define SOKOBAN_LEVEL_REPEAT (BUTTON_PLAY | BUTTON_RIGHT)
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000179#define SOKOBAN_LEVEL_UP (BUTTON_PLAY | BUTTON_SCROLL_UP)
Antoine Cellerierec7252c2007-06-29 19:52:13 +0000180#define SOKOBAN_PAUSE BUTTON_PLAY
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000181#define BUTTON_SAVE BUTTON_PLAY
182#define BUTTON_SAVE_NAME "PLAY"
183
184#elif CONFIG_KEYPAD == GIGABEAT_PAD
Jens Arnold29361ab2008-03-22 10:24:28 +0000185#define SOKOBAN_LEFT BUTTON_LEFT
186#define SOKOBAN_RIGHT BUTTON_RIGHT
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000187#define SOKOBAN_UP BUTTON_UP
188#define SOKOBAN_DOWN BUTTON_DOWN
189#define SOKOBAN_MENU BUTTON_POWER
190#define SOKOBAN_UNDO BUTTON_SELECT
191#define SOKOBAN_REDO BUTTON_A
192#define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
193#define SOKOBAN_LEVEL_REPEAT BUTTON_MENU
194#define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
Antoine Cellerierec7252c2007-06-29 19:52:13 +0000195#define SOKOBAN_PAUSE BUTTON_SELECT
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000196#define BUTTON_SAVE BUTTON_SELECT
197#define BUTTON_SAVE_NAME "SELECT"
198
199#elif CONFIG_KEYPAD == SANSA_E200_PAD
Jens Arnold29361ab2008-03-22 10:24:28 +0000200#define SOKOBAN_LEFT BUTTON_LEFT
201#define SOKOBAN_RIGHT BUTTON_RIGHT
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000202#define SOKOBAN_UP BUTTON_UP
203#define SOKOBAN_DOWN BUTTON_DOWN
204#define SOKOBAN_MENU BUTTON_POWER
205#define SOKOBAN_UNDO_PRE BUTTON_SELECT
206#define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
207#define SOKOBAN_REDO BUTTON_REC
208#define SOKOBAN_LEVEL_DOWN (BUTTON_SELECT | BUTTON_DOWN)
209#define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
210#define SOKOBAN_LEVEL_UP (BUTTON_SELECT | BUTTON_UP)
Antoine Cellerierec7252c2007-06-29 19:52:13 +0000211#define SOKOBAN_PAUSE BUTTON_SELECT
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000212#define BUTTON_SAVE BUTTON_SELECT
213#define BUTTON_SAVE_NAME "SELECT"
Daniel Stenberg1e88be52006-08-03 20:17:25 +0000214
Marianne Arnold4ee3fed2008-05-18 13:05:45 +0000215#elif CONFIG_KEYPAD == SANSA_C200_PAD
216#define SOKOBAN_LEFT BUTTON_LEFT
217#define SOKOBAN_RIGHT BUTTON_RIGHT
218#define SOKOBAN_UP BUTTON_UP
219#define SOKOBAN_DOWN BUTTON_DOWN
220#define SOKOBAN_MENU BUTTON_POWER
221#define SOKOBAN_UNDO_PRE BUTTON_SELECT
222#define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
223#define SOKOBAN_REDO BUTTON_REC
224#define SOKOBAN_LEVEL_DOWN BUTTON_VOL_DOWN
225#define SOKOBAN_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_RIGHT)
226#define SOKOBAN_LEVEL_UP BUTTON_VOL_UP
227#define SOKOBAN_PAUSE BUTTON_SELECT
228#define BUTTON_SAVE BUTTON_SELECT
229#define BUTTON_SAVE_NAME "SELECT"
230
Will Robertson8215b342008-02-17 12:23:02 +0000231#elif CONFIG_KEYPAD == GIGABEAT_S_PAD
Jens Arnold29361ab2008-03-22 10:24:28 +0000232#define SOKOBAN_LEFT BUTTON_LEFT
233#define SOKOBAN_RIGHT BUTTON_RIGHT
Will Robertson8215b342008-02-17 12:23:02 +0000234#define SOKOBAN_UP BUTTON_UP
235#define SOKOBAN_DOWN BUTTON_DOWN
236#define SOKOBAN_MENU BUTTON_MENU
237#define SOKOBAN_UNDO BUTTON_VOL_UP
238#define SOKOBAN_REDO BUTTON_VOL_DOWN
239#define SOKOBAN_LEVEL_DOWN BUTTON_PREV
240#define SOKOBAN_LEVEL_REPEAT BUTTON_PLAY
241#define SOKOBAN_LEVEL_UP BUTTON_NEXT
242#define SOKOBAN_PAUSE BUTTON_SELECT
243#define BUTTON_SAVE BUTTON_SELECT
244#define BUTTON_SAVE_NAME "SELECT"
245
Robert Kuklad6c8b572008-03-01 22:55:09 +0000246#elif CONFIG_KEYPAD == MROBE100_PAD
Jens Arnold29361ab2008-03-22 10:24:28 +0000247#define SOKOBAN_LEFT BUTTON_LEFT
248#define SOKOBAN_RIGHT BUTTON_RIGHT
Robert Kuklad6c8b572008-03-01 22:55:09 +0000249#define SOKOBAN_UP BUTTON_UP
250#define SOKOBAN_DOWN BUTTON_DOWN
251#define SOKOBAN_MENU BUTTON_POWER
252#define SOKOBAN_UNDO BUTTON_SELECT
253#define SOKOBAN_REDO BUTTON_MENU
254#define SOKOBAN_LEVEL_DOWN (BUTTON_DISPLAY | BUTTON_DOWN)
255#define SOKOBAN_LEVEL_REPEAT (BUTTON_DISPLAY | BUTTON_RIGHT)
256#define SOKOBAN_LEVEL_UP (BUTTON_DISPLAY | BUTTON_UP)
257#define SOKOBAN_PAUSE BUTTON_SELECT
258#define BUTTON_SAVE BUTTON_SELECT
259#define BUTTON_SAVE_NAME "SELECT"
260
Jens Arnold29361ab2008-03-22 10:24:28 +0000261#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
262#define SOKOBAN_LEFT BUTTON_RC_REW
263#define SOKOBAN_RIGHT BUTTON_RC_FF
264#define SOKOBAN_UP BUTTON_RC_VOL_UP
265#define SOKOBAN_DOWN BUTTON_RC_VOL_DOWN
266#define SOKOBAN_MENU BUTTON_RC_REC
267#define SOKOBAN_UNDO BUTTON_RC_MODE
268#define SOKOBAN_REDO BUTTON_RC_MENU
269#define SOKOBAN_PAUSE BUTTON_RC_PLAY
270#define BUTTON_SAVE BUTTON_RC_PLAY
271#define BUTTON_SAVE_NAME "PLAY"
272
273#define SOKOBAN_RC_MENU BUTTON_REC
274
Rob Purchase554d7ed2008-03-22 22:03:34 +0000275#elif CONFIG_KEYPAD == COWOND2_PAD
Rob Purchase554d7ed2008-03-22 22:03:34 +0000276#define SOKOBAN_MENU BUTTON_MENU
Rob Purchase297e0502008-04-27 15:30:19 +0000277#define SOKOBAN_MENU_NAME "[MENU]"
Rob Purchase554d7ed2008-03-22 22:03:34 +0000278
Robert Kuklad6c8b572008-03-01 22:55:09 +0000279#else
280#error No keymap defined!
Jens Arnolde35a6582004-10-18 21:45:00 +0000281#endif
282
Rob Purchase297e0502008-04-27 15:30:19 +0000283#ifdef HAVE_TOUCHPAD
284#ifndef SOKOBAN_LEFT
285#define SOKOBAN_LEFT BUTTON_MIDLEFT
286#endif
287#ifndef SOKOBAN_RIGHT
288#define SOKOBAN_RIGHT BUTTON_MIDRIGHT
289#endif
290#ifndef SOKOBAN_UP
291#define SOKOBAN_UP BUTTON_TOPMIDDLE
292#endif
293#ifndef SOKOBAN_DOWN
294#define SOKOBAN_DOWN BUTTON_BOTTOMMIDDLE
295#endif
296#ifndef SOKOBAN_MENU
297#define SOKOBAN_MENU BUTTON_TOPLEFT
298#define SOKOBAN_MENU_NAME "[TOPLEFT]"
299#endif
300#ifndef SOKOBAN_UNDO
301#define SOKOBAN_UNDO BUTTON_BOTTOMRIGHT
302#define SOKOBAN_UNDO_NAME "[BOTTOMRIGHT]"
303#endif
304#ifndef SOKOBAN_REDO
305#define SOKOBAN_REDO BUTTON_BOTTOMLEFT
306#define SOKOBAN_REDO_NAME "[BOTTOMLEFT]"
307#endif
308#ifndef SOKOBAN_PAUSE
309#define SOKOBAN_PAUSE BUTTON_CENTER
310#define SOKOBAN_PAUSE_NAME "[CENTER]"
311#endif
312#ifndef SOKOBAN_LEVEL_REPEAT
313#define SOKOBAN_LEVEL_REPEAT BUTTON_TOPRIGHT
314#define SOKOBAN_LEVEL_REPEAT_NAME "[TOPRIGHT]"
315#endif
316#ifndef BUTTON_SAVE
317#define BUTTON_SAVE BUTTON_CENTER
318#define BUTTON_SAVE_NAME "CENTER"
319#endif
320#endif
321
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000322#define SOKOBAN_FONT FONT_SYSFIXED
Brandon Low64fcd4c2006-01-19 06:25:59 +0000323
Jens Arnold79442c82005-07-19 16:48:02 +0000324
Zakk Roberts67faac42006-05-09 05:36:46 +0000325/* The Location, Undo and LevelInfo structs are OO-flavored.
326 * (oooh!-flavored as Schnueff puts it.) It makes more you have to know,
Björn Stenbergba371fb2003-06-29 16:33:04 +0000327 * but the overall data layout becomes more manageable. */
328
Zakk Robertsffbdb252007-02-13 06:48:10 +0000329/* Level data & stats */
Björn Stenbergba371fb2003-06-29 16:33:04 +0000330struct LevelInfo {
Antoine Cellerierec7252c2007-06-29 19:52:13 +0000331 int index; /* Level index (level number - 1) */
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000332 int moves; /* Moves & pushes for the stats */
333 int pushes;
334 short boxes_to_go; /* Number of unplaced boxes remaining in level */
335 short height; /* Height & width for centering level display */
336 short width;
Björn Stenbergba371fb2003-06-29 16:33:04 +0000337};
338
Björn Stenbergba371fb2003-06-29 16:33:04 +0000339struct Location {
Björn Stenbergba371fb2003-06-29 16:33:04 +0000340 short row;
341 short col;
342};
343
Björn Stenbergba371fb2003-06-29 16:33:04 +0000344/* Our full undo history */
345static struct UndoInfo {
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000346 int count; /* How many undos have been done */
347 int current; /* Which history is the current undo */
348 int max; /* Which history is the max redoable */
Zakk Robertsffbdb252007-02-13 06:48:10 +0000349 char history[MAX_UNDOS];
Björn Stenbergba371fb2003-06-29 16:33:04 +0000350} undo_info;
351
352/* Our playing board */
353static struct BoardInfo {
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000354 char board[ROWS][COLS]; /* The current board data */
355 struct LevelInfo level; /* Level data & stats */
356 struct Location player; /* Where the player is */
357 int max_level; /* The number of levels we have */
Björn Stenbergba371fb2003-06-29 16:33:04 +0000358} current_info;
359
Brandon Low64fcd4c2006-01-19 06:25:59 +0000360static struct BufferedBoards {
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000361 char filename[MAX_PATH]; /* Filename of the levelset we're using */
362 char data[MAX_LEVEL_DATA]; /* Buffered level data */
363 int index[MAX_LEVELS + 1]; /* Where each buffered board begins & ends */
364 int start; /* Index of first buffered board */
365 int end; /* Index of last buffered board */
366 short prebuffered_boards; /* Number of boards before current to store */
Brandon Low64fcd4c2006-01-19 06:25:59 +0000367} buffered_boards;
368
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000369
Steve Bavin65265772008-05-13 09:57:56 +0000370static const struct plugin_api* rb;
Antoine Cellerier189a5f82007-06-29 20:23:46 +0000371MEM_FUNCTION_WRAPPERS(rb);
Björn Stenbergba371fb2003-06-29 16:33:04 +0000372
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000373static char buf[ROWS*(COLS + 1)]; /* Enough for a whole board or a filename */
374
375
Björn Stenbergba371fb2003-06-29 16:33:04 +0000376static void init_undo(void)
377{
378 undo_info.count = 0;
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000379 undo_info.current = 0;
380 undo_info.max = 0;
Zakk Robertsffbdb252007-02-13 06:48:10 +0000381}
382
383static void get_delta(char direction, short *d_r, short *d_c)
384{
385 switch (direction) {
386 case SOKOBAN_PUSH_LEFT:
387 case SOKOBAN_MOVE_LEFT:
388 *d_r = 0;
389 *d_c = -1;
390 break;
391 case SOKOBAN_PUSH_RIGHT:
392 case SOKOBAN_MOVE_RIGHT:
393 *d_r = 0;
394 *d_c = 1;
395 break;
396 case SOKOBAN_PUSH_UP:
397 case SOKOBAN_MOVE_UP:
398 *d_r = -1;
399 *d_c = 0;
400 break;
401 case SOKOBAN_PUSH_DOWN:
402 case SOKOBAN_MOVE_DOWN:
403 *d_r = 1;
404 *d_c = 0;
405 }
Björn Stenbergba371fb2003-06-29 16:33:04 +0000406}
407
Antoine Cellerierec7252c2007-06-29 19:52:13 +0000408static bool undo(void)
Björn Stenbergba371fb2003-06-29 16:33:04 +0000409{
Zakk Robertsffbdb252007-02-13 06:48:10 +0000410 char undo;
411 short r, c;
412 short d_r = 0, d_c = 0; /* delta row & delta col */
413 char *space_cur, *space_next, *space_prev;
414 bool undo_push = false;
Björn Stenbergba371fb2003-06-29 16:33:04 +0000415
Zakk Robertsffbdb252007-02-13 06:48:10 +0000416 /* If no more undos or we've wrapped all the way around, quit */
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000417 if (undo_info.count == 0 || undo_info.current - 1 == undo_info.max)
Antoine Cellerierec7252c2007-06-29 19:52:13 +0000418 return false;
Björn Stenbergba371fb2003-06-29 16:33:04 +0000419
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000420 /* Move to previous undo in the list */
421 if (undo_info.current == 0 && undo_info.count > 1)
422 undo_info.current = MAX_UNDOS - 1;
423 else
424 undo_info.current--;
425
426 undo_info.count--;
427
Zakk Robertsffbdb252007-02-13 06:48:10 +0000428 undo = undo_info.history[undo_info.current];
429
430 if (undo < SOKOBAN_MOVE_MIN)
431 undo_push = true;
432
433 get_delta(undo, &d_r, &d_c);
434
435 r = current_info.player.row;
436 c = current_info.player.col;
437
438 /* Give the 3 spaces we're going to use better names */
439 space_cur = &current_info.board[r][c];
440 space_next = &current_info.board[r + d_r][c + d_c];
441 space_prev = &current_info.board[r - d_r][c - d_c];
442
Björn Stenbergba371fb2003-06-29 16:33:04 +0000443 /* Update board info */
Zakk Robertsffbdb252007-02-13 06:48:10 +0000444 if (undo_push) {
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000445 /* Moving box from goal to floor */
446 if (*space_next == '*' && *space_cur == '@')
Zakk Robertsffbdb252007-02-13 06:48:10 +0000447 current_info.level.boxes_to_go++;
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000448 /* Moving box from floor to goal */
Zakk Robertsffbdb252007-02-13 06:48:10 +0000449 else if (*space_next == '$' && *space_cur == '+')
450 current_info.level.boxes_to_go--;
Zakk Roberts67faac42006-05-09 05:36:46 +0000451
Zakk Robertsffbdb252007-02-13 06:48:10 +0000452 /* Move box off of next space... */
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000453 *space_next = (*space_next == '*' ? '.' : ' ');
Zakk Robertsffbdb252007-02-13 06:48:10 +0000454 /* ...and on to current space */
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000455 *space_cur = (*space_cur == '+' ? '*' : '$');
Björn Stenbergba371fb2003-06-29 16:33:04 +0000456
Zakk Robertsffbdb252007-02-13 06:48:10 +0000457 current_info.level.pushes--;
458 } else
459 /* Just move player off of current space */
460 *space_cur = (*space_cur == '+' ? '.' : ' ');
461 /* Move player back to previous space */
462 *space_prev = (*space_prev == '.' ? '+' : '@');
Björn Stenbergba371fb2003-06-29 16:33:04 +0000463
Zakk Robertsffbdb252007-02-13 06:48:10 +0000464 /* Update position */
465 current_info.player.row -= d_r;
466 current_info.player.col -= d_c;
Björn Stenbergba371fb2003-06-29 16:33:04 +0000467
Zakk Robertsffbdb252007-02-13 06:48:10 +0000468 current_info.level.moves--;
469
Antoine Cellerierec7252c2007-06-29 19:52:13 +0000470 return true;
Björn Stenbergba371fb2003-06-29 16:33:04 +0000471}
472
Zakk Robertsffbdb252007-02-13 06:48:10 +0000473static void add_undo(char undo)
Björn Stenbergba371fb2003-06-29 16:33:04 +0000474{
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000475 undo_info.history[undo_info.current] = undo;
476
Zakk Robertsffbdb252007-02-13 06:48:10 +0000477 /* Wrap around if MAX_UNDOS exceeded */
478 if (undo_info.current < (MAX_UNDOS - 1))
479 undo_info.current++;
480 else
481 undo_info.current = 0;
Björn Stenbergba371fb2003-06-29 16:33:04 +0000482
Zakk Roberts67faac42006-05-09 05:36:46 +0000483 if (undo_info.count < MAX_UNDOS)
Björn Stenbergba371fb2003-06-29 16:33:04 +0000484 undo_info.count++;
485}
486
Zakk Robertsffbdb252007-02-13 06:48:10 +0000487static bool move(char direction, bool redo)
488{
489 short r, c;
490 short d_r = 0, d_c = 0; /* delta row & delta col */
491 char *space_cur, *space_next, *space_beyond;
492 bool push = false;
493
494 get_delta(direction, &d_r, &d_c);
495
496 r = current_info.player.row;
497 c = current_info.player.col;
498
499 /* Check for out-of-bounds */
500 if (r + 2*d_r < 0 || r + 2*d_r >= ROWS ||
501 c + 2*d_c < 0 || c + 2*d_c >= COLS)
502 return false;
503
504 /* Give the 3 spaces we're going to use better names */
505 space_cur = &current_info.board[r][c];
506 space_next = &current_info.board[r + d_r][c + d_c];
507 space_beyond = &current_info.board[r + 2*d_r][c + 2*d_c];
508
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000509 if (*space_next == '$' || *space_next == '*') {
Zakk Robertsffbdb252007-02-13 06:48:10 +0000510 /* Change direction from move to push for undo */
511 if (direction >= SOKOBAN_MOVE_MIN)
512 direction -= SOKOBAN_MOVE_DIFF;
513 push = true;
514 }
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000515 else if (direction < SOKOBAN_MOVE_MIN)
516 /* Change back to move if redo/solution playback push is invalid */
517 direction += SOKOBAN_MOVE_DIFF;
Zakk Robertsffbdb252007-02-13 06:48:10 +0000518
519 /* Update board info */
520 if (push) {
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000521 /* Moving box from goal to floor */
522 if (*space_next == '*' && *space_beyond == ' ')
Zakk Robertsffbdb252007-02-13 06:48:10 +0000523 current_info.level.boxes_to_go++;
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000524 /* Moving box from floor to goal */
Zakk Robertsffbdb252007-02-13 06:48:10 +0000525 else if (*space_next == '$' && *space_beyond == '.')
526 current_info.level.boxes_to_go--;
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000527 /* Check for invalid move */
Zakk Robertsffbdb252007-02-13 06:48:10 +0000528 else if (*space_beyond != '.' && *space_beyond != ' ')
529 return false;
530
531 /* Move player onto next space */
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000532 *space_next = (*space_next == '*' ? '+' : '@');
Zakk Robertsffbdb252007-02-13 06:48:10 +0000533 /* Move box onto space beyond next */
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000534 *space_beyond = (*space_beyond == '.' ? '*' : '$');
Zakk Robertsffbdb252007-02-13 06:48:10 +0000535
536 current_info.level.pushes++;
537 } else {
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000538 /* Check for invalid move */
539 if (*space_next != '.' && *space_next != ' ')
Zakk Robertsffbdb252007-02-13 06:48:10 +0000540 return false;
541
542 /* Move player onto next space */
543 *space_next = (*space_next == '.' ? '+' : '@');
544 }
545 /* Move player off of current space */
546 *space_cur = (*space_cur == '+' ? '.' : ' ');
547
548 /* Update position */
549 current_info.player.row += d_r;
550 current_info.player.col += d_c;
551
552 current_info.level.moves++;
553
554 /* Update undo_info.max to current on every normal move,
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000555 * except if it's the same as a redo. */
556 /* normal move and either */
Zakk Robertsffbdb252007-02-13 06:48:10 +0000557 if (!redo &&
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000558 /* moves have been undone... */
Zakk Robertsffbdb252007-02-13 06:48:10 +0000559 ((undo_info.max != undo_info.current &&
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000560 /* ...and the current move is NOT the same as the one in history */
561 undo_info.history[undo_info.current] != direction) ||
Zakk Robertsffbdb252007-02-13 06:48:10 +0000562 /* or moves have not been undone */
563 undo_info.max == undo_info.current)) {
564 add_undo(direction);
565 undo_info.max = undo_info.current;
566 } else /* redo move or move was same as redo */
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000567 add_undo(direction); /* add_undo to update current */
Zakk Robertsffbdb252007-02-13 06:48:10 +0000568
569 return true;
570}
571
572#ifdef SOKOBAN_REDO
573static bool redo(void)
574{
575 /* If no moves have been undone, quit */
576 if (undo_info.current == undo_info.max)
577 return false;
578
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000579 return move(undo_info.history[(undo_info.current < MAX_UNDOS ?
580 undo_info.current : 0)], true);
Zakk Robertsffbdb252007-02-13 06:48:10 +0000581}
582#endif
583
Björn Stenbergba371fb2003-06-29 16:33:04 +0000584static void init_boards(void)
585{
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000586 rb->strncpy(buffered_boards.filename, SOKOBAN_LEVELS_FILE, MAX_PATH);
587
588 current_info.level.index = 0;
Björn Stenbergba371fb2003-06-29 16:33:04 +0000589 current_info.player.row = 0;
590 current_info.player.col = 0;
Björn Stenbergba371fb2003-06-29 16:33:04 +0000591 current_info.max_level = 0;
Zakk Roberts67faac42006-05-09 05:36:46 +0000592
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000593 buffered_boards.start = 0;
594 buffered_boards.end = 0;
595 buffered_boards.prebuffered_boards = 0;
Björn Stenbergba371fb2003-06-29 16:33:04 +0000596
597 init_undo();
598}
599
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000600static bool read_levels(bool initialize)
Björn Stenbergba371fb2003-06-29 16:33:04 +0000601{
602 int fd = 0;
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000603 short len;
604 short lastlen = 0;
605 short row = 0;
Brandon Low64fcd4c2006-01-19 06:25:59 +0000606 int level_count = 0;
Björn Stenbergba371fb2003-06-29 16:33:04 +0000607
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000608 int i = 0;
609 int level_len = 0;
610 bool index_set = false;
Brandon Low64fcd4c2006-01-19 06:25:59 +0000611
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000612 /* Get the index of the first level to buffer */
613 if (current_info.level.index > buffered_boards.prebuffered_boards &&
614 !initialize)
615 buffered_boards.start = current_info.level.index -
616 buffered_boards.prebuffered_boards;
617 else
618 buffered_boards.start = 0;
Zakk Roberts67faac42006-05-09 05:36:46 +0000619
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000620 if ((fd = rb->open(buffered_boards.filename, O_RDONLY)) < 0) {
621 rb->splash(HZ*2, "Unable to open %s", buffered_boards.filename);
622 return false;
Björn Stenbergba371fb2003-06-29 16:33:04 +0000623 }
Zakk Roberts67faac42006-05-09 05:36:46 +0000624
Brandon Low64fcd4c2006-01-19 06:25:59 +0000625 do {
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000626 len = rb->read_line(fd, buf, sizeof(buf));
627
628 /* Correct len when trailing \r's or \n's are counted */
629 if (len > 2 && buf[len - 2] == '\0')
630 len -= 2;
631 else if (len > 1 && buf[len - 1] == '\0')
632 len--;
633
634 /* Skip short lines & lines with non-level data */
635 if (len >= 3 && ((buf[0] >= '1' && buf[0] <= '9') || buf[0] == '#' ||
636 buf[0] == ' ' || buf[0] == '-' || buf[0] == '_')) {
637 if (level_count >= buffered_boards.start) {
638 /* Set the index of this level */
639 if (!index_set &&
640 level_count - buffered_boards.start < MAX_LEVELS) {
641 buffered_boards.index[level_count - buffered_boards.start]
642 = i;
643 index_set = true;
Brandon Low64fcd4c2006-01-19 06:25:59 +0000644 }
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000645 /* Copy buffer to board data */
646 if (i + level_len + len < MAX_LEVEL_DATA) {
647 rb->memcpy(&buffered_boards.data[i + level_len], buf, len);
648 buffered_boards.data[i + level_len + len] = '\n';
649 }
Brandon Low64fcd4c2006-01-19 06:25:59 +0000650 }
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000651 level_len += len + 1;
652 row++;
653
654 /* If newline & level is tall enough or is RLE */
655 } else if (buf[0] == '\0' && (row > 2 || lastlen > 22)) {
656 level_count++;
657 if (level_count >= buffered_boards.start) {
658 i += level_len;
659 if (i < MAX_LEVEL_DATA)
660 buffered_boards.end = level_count;
661 else if (!initialize)
662 break;
663 }
664 row = 0;
665 level_len = 0;
666 index_set = false;
667
668 } else if (len > 22)
669 len = 1;
670
671 } while ((lastlen = len));
672
673 /* Set the index of the end of the last level */
674 if (level_count - buffered_boards.start < MAX_LEVELS)
675 buffered_boards.index[level_count - buffered_boards.start] = i;
676
677 if (initialize) {
678 current_info.max_level = level_count;
679 buffered_boards.prebuffered_boards = buffered_boards.end/2;
680 }
Björn Stenbergba371fb2003-06-29 16:33:04 +0000681
Björn Stenbergba371fb2003-06-29 16:33:04 +0000682 rb->close(fd);
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000683
684 return true;
Björn Stenbergba371fb2003-06-29 16:33:04 +0000685}
Zakk Roberts67faac42006-05-09 05:36:46 +0000686
Brandon Low64fcd4c2006-01-19 06:25:59 +0000687static void load_level(void)
Björn Stenbergba371fb2003-06-29 16:33:04 +0000688{
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000689 int c, r;
690 int i, n;
691 int level_size;
692 int index = current_info.level.index - buffered_boards.start;
693 char *level;
Zakk Roberts67faac42006-05-09 05:36:46 +0000694
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000695 /* Get the buffered board index of the current level */
696 if (current_info.level.index < buffered_boards.start ||
697 current_info.level.index >= buffered_boards.end) {
Brandon Low64fcd4c2006-01-19 06:25:59 +0000698 read_levels(false);
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000699 if (current_info.level.index > buffered_boards.prebuffered_boards)
700 index = buffered_boards.prebuffered_boards;
701 else
702 index = current_info.level.index;
Brandon Low64fcd4c2006-01-19 06:25:59 +0000703 }
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000704 level = &buffered_boards.data[buffered_boards.index[index]];
Björn Stenbergba371fb2003-06-29 16:33:04 +0000705
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000706 /* Reset level info */
Björn Stenbergba371fb2003-06-29 16:33:04 +0000707 current_info.level.moves = 0;
Zakk Robertsffbdb252007-02-13 06:48:10 +0000708 current_info.level.pushes = 0;
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000709 current_info.level.boxes_to_go = 0;
710 current_info.level.width = 0;
Björn Stenbergba371fb2003-06-29 16:33:04 +0000711
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000712 /* Clear board */
713 for (r = 0; r < ROWS; r++)
714 for (c = 0; c < COLS; c++)
715 current_info.board[r][c] = 'X';
Zakk Roberts67faac42006-05-09 05:36:46 +0000716
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000717 level_size = buffered_boards.index[index + 1] -
718 buffered_boards.index[index];
Björn Stenbergba371fb2003-06-29 16:33:04 +0000719
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000720 for (r = 0, c = 0, n = 1, i = 0; i < level_size; i++) {
721 if (level[i] == '\n' || level[i] == '|') {
722 if (c > 3) {
723 /* Update max width of level & go to next row */
724 if (c > current_info.level.width)
725 current_info.level.width = c;
726 c = 0;
727 r++;
728 if (r >= ROWS)
729 break;
730 }
731 } else if (c < COLS) {
732 /* Read RLE character's length into n */
733 if (level[i] >= '0' && level[i] <= '9') {
734 n = level[i++] - '0';
735 if (level[i] >= '0' && level[i] <= '9')
736 n = n*10 + level[i++] - '0';
737 }
738
739 /* Cleanup & replace */
740 if (level[i] == '%')
741 level[i] = '*';
742 else if (level[i] == '-' || level[i] == '_')
743 level[i] = ' ';
744
745 if (n > 1) {
746 if (c + n >= COLS)
747 n = COLS - c;
748
749 if (level[i] == '.')
750 current_info.level.boxes_to_go += n;
751
752 /* Put RLE character n times */
753 while (n--)
754 current_info.board[r][c++] = level[i];
755 n = 1;
756
757 } else {
758 if (level[i] == '.' || level[i] == '+')
759 current_info.level.boxes_to_go++;
760
761 if (level[i] == '@' ||level[i] == '+') {
762 current_info.player.row = r;
763 current_info.player.col = c;
764 }
765
766 current_info.board[r][c++] = level[i];
Björn Stenbergba371fb2003-06-29 16:33:04 +0000767 }
768 }
769 }
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000770
771 current_info.level.height = r;
772
773#if LCD_DEPTH > 2
774 /* Fill in blank space outside level on color targets */
775 for (r = 0; r < ROWS; r++)
776 for (c = 0; current_info.board[r][c] == ' ' && c < COLS; c++)
777 current_info.board[r][c] = 'X';
778
779 for (c = 0; c < COLS; c++) {
780 for (r = 0; (current_info.board[r][c] == ' ' ||
781 current_info.board[r][c] == 'X') && r < ROWS; r++)
782 current_info.board[r][c] = 'X';
783 for (r = ROWS - 1; (current_info.board[r][c] == ' ' ||
784 current_info.board[r][c] == 'X') && r >= 0; r--)
785 current_info.board[r][c] = 'X';
786 }
787#endif
Björn Stenbergba371fb2003-06-29 16:33:04 +0000788}
789
Zakk Roberts67faac42006-05-09 05:36:46 +0000790static void update_screen(void)
Björn Stenbergba371fb2003-06-29 16:33:04 +0000791{
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000792 int c, r;
793 int rows, cols;
Brandon Low64fcd4c2006-01-19 06:25:59 +0000794
Marianne Arnold4ee3fed2008-05-18 13:05:45 +0000795#if LCD_WIDTH - (COLS*SOKOBAN_TILESIZE) < 32
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000796#define STAT_HEIGHT 25
797#define STAT_X (LCD_WIDTH - 120)/2
798#define STAT_Y (LCD_HEIGHT - STAT_HEIGHT)
799#define BOARD_WIDTH LCD_WIDTH
800#define BOARD_HEIGHT (LCD_HEIGHT - STAT_HEIGHT)
801 rb->lcd_putsxy(STAT_X + 4, STAT_Y + 4, "Level");
802 rb->snprintf(buf, sizeof(buf), "%d", current_info.level.index + 1);
803 rb->lcd_putsxy(STAT_X + 7, STAT_Y + 14, buf);
804 rb->lcd_putsxy(STAT_X + 41, STAT_Y + 4, "Moves");
805 rb->snprintf(buf, sizeof(buf), "%d", current_info.level.moves);
806 rb->lcd_putsxy(STAT_X + 44, STAT_Y + 14, buf);
807 rb->lcd_putsxy(STAT_X + 79, STAT_Y + 4, "Pushes");
808 rb->snprintf(buf, sizeof(buf), "%d", current_info.level.pushes);
809 rb->lcd_putsxy(STAT_X + 82, STAT_Y + 14, buf);
810
811 rb->lcd_drawrect(STAT_X, STAT_Y, 38, STAT_HEIGHT);
812 rb->lcd_drawrect(STAT_X + 37, STAT_Y, 39, STAT_HEIGHT);
813 rb->lcd_drawrect(STAT_X + 75, STAT_Y, 45, STAT_HEIGHT);
814#else
Marianne Arnold4ee3fed2008-05-18 13:05:45 +0000815#if LCD_WIDTH - (COLS*SOKOBAN_TILESIZE) > 40
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000816#define STAT_X (LCD_WIDTH - 40)
817#else
Marianne Arnold4ee3fed2008-05-18 13:05:45 +0000818#define STAT_X COLS*SOKOBAN_TILESIZE
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000819#endif
Jens Arnold306acbd2008-05-18 13:58:21 +0000820#define STAT_Y (LCD_HEIGHT - 64)/2
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000821#define STAT_WIDTH (LCD_WIDTH - STAT_X)
822#define BOARD_WIDTH (LCD_WIDTH - STAT_WIDTH)
823#define BOARD_HEIGHT LCD_HEIGHT
Jens Arnold306acbd2008-05-18 13:58:21 +0000824 rb->lcd_putsxy(STAT_X + 1, STAT_Y + 2, "Level");
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000825 rb->snprintf(buf, sizeof(buf), "%d", current_info.level.index + 1);
Jens Arnold306acbd2008-05-18 13:58:21 +0000826 rb->lcd_putsxy(STAT_X + 4, STAT_Y + 12, buf);
827 rb->lcd_putsxy(STAT_X + 1, STAT_Y + 23, "Moves");
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000828 rb->snprintf(buf, sizeof(buf), "%d", current_info.level.moves);
Jens Arnold306acbd2008-05-18 13:58:21 +0000829 rb->lcd_putsxy(STAT_X + 4, STAT_Y + 33, buf);
830#if STAT_WIDTH < 38
831 rb->lcd_putsxy(STAT_X + 1, STAT_Y + 44, "Push");
832#else
833 rb->lcd_putsxy(STAT_X + 1, STAT_Y + 44, "Pushes");
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000834#endif
Jens Arnold306acbd2008-05-18 13:58:21 +0000835 rb->snprintf(buf, sizeof(buf), "%d", current_info.level.pushes);
836 rb->lcd_putsxy(STAT_X + 4, STAT_Y + 54, buf);
837
838 rb->lcd_drawrect(STAT_X, STAT_Y + 0, STAT_WIDTH, 64);
839 rb->lcd_hline(STAT_X, LCD_WIDTH - 1, STAT_Y + 21);
840 rb->lcd_hline(STAT_X, LCD_WIDTH - 1, STAT_Y + 42);
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000841
Linus Nielsen Feltzing47668632005-02-28 07:12:37 +0000842#endif
Zakk Roberts67faac42006-05-09 05:36:46 +0000843
Björn Stenbergba371fb2003-06-29 16:33:04 +0000844 /* load the board to the screen */
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000845 for (rows = 0; rows < ROWS; rows++) {
Zakk Robertsffbdb252007-02-13 06:48:10 +0000846 for (cols = 0; cols < COLS; cols++) {
Marianne Arnold4ee3fed2008-05-18 13:05:45 +0000847 c = cols*SOKOBAN_TILESIZE +
848 (BOARD_WIDTH - current_info.level.width*SOKOBAN_TILESIZE)/2;
849 r = rows*SOKOBAN_TILESIZE +
850 (BOARD_HEIGHT - current_info.level.height*SOKOBAN_TILESIZE)/2;
Björn Stenbergba371fb2003-06-29 16:33:04 +0000851
852 switch(current_info.board[rows][cols]) {
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000853 case 'X': /* blank space outside of level */
854 break;
Jens Arnold8c82d812005-03-02 06:50:06 +0000855
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000856 case ' ': /* floor */
Marianne Arnold4ee3fed2008-05-18 13:05:45 +0000857 rb->lcd_bitmap_part(sokoban_tiles, 0, 0*SOKOBAN_TILESIZE,
858 SOKOBAN_TILESIZE, c, r, SOKOBAN_TILESIZE,
859 SOKOBAN_TILESIZE);
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000860 break;
861
862 case '#': /* wall */
Marianne Arnold4ee3fed2008-05-18 13:05:45 +0000863 rb->lcd_bitmap_part(sokoban_tiles, 0, 1*SOKOBAN_TILESIZE,
864 SOKOBAN_TILESIZE, c, r, SOKOBAN_TILESIZE,
865 SOKOBAN_TILESIZE);
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000866 break;
867
868 case '$': /* box */
Marianne Arnold4ee3fed2008-05-18 13:05:45 +0000869 rb->lcd_bitmap_part(sokoban_tiles, 0, 2*SOKOBAN_TILESIZE,
870 SOKOBAN_TILESIZE, c, r, SOKOBAN_TILESIZE,
871 SOKOBAN_TILESIZE);
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000872 break;
873
874 case '*': /* box on goal */
Marianne Arnold4ee3fed2008-05-18 13:05:45 +0000875 rb->lcd_bitmap_part(sokoban_tiles, 0, 3*SOKOBAN_TILESIZE,
876 SOKOBAN_TILESIZE, c, r, SOKOBAN_TILESIZE,
877 SOKOBAN_TILESIZE);
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000878 break;
879
880 case '.': /* goal */
Marianne Arnold4ee3fed2008-05-18 13:05:45 +0000881 rb->lcd_bitmap_part(sokoban_tiles, 0, 4*SOKOBAN_TILESIZE,
882 SOKOBAN_TILESIZE, c, r, SOKOBAN_TILESIZE,
883 SOKOBAN_TILESIZE);
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000884 break;
885
886 case '@': /* player */
Marianne Arnold4ee3fed2008-05-18 13:05:45 +0000887 rb->lcd_bitmap_part(sokoban_tiles, 0, 5*SOKOBAN_TILESIZE,
888 SOKOBAN_TILESIZE, c, r, SOKOBAN_TILESIZE,
889 SOKOBAN_TILESIZE);
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000890 break;
891
892 case '+': /* player on goal */
Marianne Arnold4ee3fed2008-05-18 13:05:45 +0000893 rb->lcd_bitmap_part(sokoban_tiles, 0, 6*SOKOBAN_TILESIZE,
894 SOKOBAN_TILESIZE, c, r, SOKOBAN_TILESIZE,
895 SOKOBAN_TILESIZE);
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000896 break;
Björn Stenbergba371fb2003-06-29 16:33:04 +0000897 }
898 }
899 }
Björn Stenbergba371fb2003-06-29 16:33:04 +0000900
Björn Stenbergba371fb2003-06-29 16:33:04 +0000901 /* print out the screen */
902 rb->lcd_update();
903}
904
905static void draw_level(void)
906{
907 load_level();
908 rb->lcd_clear_display();
909 update_screen();
910}
911
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000912static bool save(char *filename, bool solution)
913{
914 int fd;
Antoine Cellerierec7252c2007-06-29 19:52:13 +0000915 char *loc;
916 DIR *dir;
917 char dirname[MAX_PATH];
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000918
919 rb->splash(0, "Saving...");
920
Antoine Cellerierec7252c2007-06-29 19:52:13 +0000921 /* Create dir if it doesn't exist */
922 if ((loc = rb->strrchr(filename, '/')) != NULL) {
923 rb->strncpy(dirname, filename, loc - filename);
924 dirname[loc - filename] = '\0';
925 if(!(dir = rb->opendir(dirname)))
926 rb->mkdir(dirname);
927 else
928 rb->closedir(dir);
929 }
930
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000931 if (filename[0] == '\0' ||
932 (fd = rb->open(filename, O_WRONLY|O_CREAT|O_TRUNC)) < 0) {
933 rb->splash(HZ*2, "Unable to open %s", filename);
934 return false;
935 }
936
937 /* Sokoban: S/P for solution/progress : level number : current undo */
938 rb->snprintf(buf, sizeof(buf), "Sokoban:%c:%d:%d\n", (solution ? 'S' : 'P'),
939 current_info.level.index + 1, undo_info.current);
940 rb->write(fd, buf, rb->strlen(buf));
941
942 /* Filename of levelset */
943 rb->write(fd, buffered_boards.filename,
944 rb->strlen(buffered_boards.filename));
945 rb->write(fd, "\n", 1);
946
947 /* Full undo history */
948 rb->write(fd, undo_info.history, undo_info.max);
949
950 rb->close(fd);
951
952 return true;
953}
954
955static bool load(char *filename, bool silent)
956{
957 int fd;
Antoine Cellerierec7252c2007-06-29 19:52:13 +0000958 int i, n;
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000959 int len;
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000960 int button;
Antoine Cellerierec7252c2007-06-29 19:52:13 +0000961 bool play_solution;
962 bool paused = false;
963 unsigned short speed = 2;
964 int delay[] = {HZ/2, HZ/3, HZ/4, HZ/6, HZ/8, HZ/12, HZ/16, HZ/25};
Antoine Cellerierf91d06d2007-06-28 20:45:00 +0000965
966 if (filename[0] == '\0' || (fd = rb->open(filename, O_RDONLY)) < 0) {
967 if (!silent)
968 rb->splash(HZ*2, "Unable to open %s", filename);
969 return false;
970 }
971
972 /* Read header, level number, & current undo */
973 rb->read_line(fd, buf, sizeof(buf));
974
975 /* If we're opening a level file, not a solution/progress file */
976 if (rb->strncmp(buf, "Sokoban", 7) != 0) {
977 rb->close(fd);
978
979 rb->strncpy(buffered_boards.filename, filename, MAX_PATH);
980 if (!read_levels(true))
981 return false;
982
983 current_info.level.index = 0;
984 load_level();
985
986 /* If there aren't any boxes to go or the player position wasn't set,
987 * the file probably wasn't a Sokoban level file */
988 if (current_info.level.boxes_to_go == 0 ||
989 current_info.player.row == 0 || current_info.player.col == 0) {
990 if (!silent)
991 rb->splash(HZ*2, "File is not a Sokoban level file");
992 return false;
993 }
994
995 } else {
996
997 /* Read filename of levelset */
998 rb->read_line(fd, buffered_boards.filename,
999 sizeof(buffered_boards.filename));
1000
1001 /* Read full undo history */
1002 len = rb->read_line(fd, undo_info.history, MAX_UNDOS);
1003
1004 /* Correct len when trailing \r's or \n's are counted */
1005 if (len > 2 && undo_info.history[len - 2] == '\0')
1006 len -= 2;
1007 else if (len > 1 && undo_info.history[len - 1] == '\0')
1008 len--;
1009
1010 rb->close(fd);
1011
1012 /* Check to see if we're going to play a solution or resume progress */
1013 play_solution = (buf[8] == 'S');
1014
1015 /* Get level number */
1016 for (n = 0, i = 10; buf[i] >= '0' && buf[i] <= '9' && i < 15; i++)
1017 n = n*10 + buf[i] - '0';
1018 current_info.level.index = n - 1;
1019
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001020 /* Get undo index */
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001021 for (n = 0, i++; buf[i] >= '0' && buf[i] <= '9' && i < 21; i++)
1022 n = n*10 + buf[i] - '0';
1023 if (n > len)
1024 n = len;
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001025 undo_info.max = len;
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001026
1027 if (current_info.level.index < 0) {
1028 if (!silent)
1029 rb->splash(HZ*2, "Error loading level");
1030 return false;
1031 }
1032 if (!read_levels(true))
1033 return false;
1034 if (current_info.level.index >= current_info.max_level) {
1035 if (!silent)
1036 rb->splash(HZ*2, "Error loading level");
1037 return false;
1038 }
1039
1040 load_level();
1041
1042 if (play_solution) {
1043 rb->lcd_clear_display();
1044 update_screen();
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001045 rb->sleep(2*delay[speed]);
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001046
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001047 /* Replay solution until menu button is pressed */
1048 i = 0;
1049 while (true) {
1050 if (i < len) {
1051 if (!move(undo_info.history[i], true)) {
1052 n = i;
1053 break;
1054 }
1055 rb->lcd_clear_display();
1056 update_screen();
1057 i++;
1058 } else
1059 paused = true;
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001060
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001061 rb->sleep(delay[speed]);
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001062
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001063 while ((button = rb->button_get(false)) || paused) {
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001064 switch (button) {
1065 case SOKOBAN_MENU:
1066 /* Pretend the level is complete so we'll quit */
1067 current_info.level.boxes_to_go = 0;
1068 return true;
1069
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001070 case SOKOBAN_PAUSE:
1071 /* Toggle pause state */
1072 paused = !paused;
1073 break;
1074
Jens Arnold29361ab2008-03-22 10:24:28 +00001075 case SOKOBAN_LEFT:
1076 case SOKOBAN_LEFT | BUTTON_REPEAT:
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001077 /* Go back one move */
1078 if (paused) {
1079 if (undo())
1080 i--;
1081 rb->lcd_clear_display();
1082 update_screen();
1083 }
1084 break;
1085
Jens Arnold29361ab2008-03-22 10:24:28 +00001086 case SOKOBAN_RIGHT:
1087 case SOKOBAN_RIGHT | BUTTON_REPEAT:
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001088 /* Go forward one move */
1089 if (paused) {
1090 if (redo())
1091 i++;
1092 rb->lcd_clear_display();
1093 update_screen();
1094 }
1095 break;
1096
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001097 case SOKOBAN_UP:
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001098 case SOKOBAN_UP | BUTTON_REPEAT:
1099 /* Speed up */
1100 if (speed < sizeof(delay)/sizeof(int) - 1)
1101 speed++;
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001102 break;
1103
1104 case SOKOBAN_DOWN:
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001105 case SOKOBAN_DOWN | BUTTON_REPEAT:
1106 /* Slow down */
1107 if (speed > 0)
1108 speed--;
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001109 }
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001110
1111 if (paused)
1112 rb->sleep(HZ/33);
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001113 }
1114 }
1115
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001116 /* If level is complete, wait for keypress before quitting */
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001117 if (current_info.level.boxes_to_go == 0)
1118 rb->button_get(true);
1119
1120 } else {
1121 /* Advance to current undo */
1122 for (i = 0; i < n; i++) {
1123 if (!move(undo_info.history[i], true)) {
1124 n = i;
1125 break;
1126 }
1127 }
1128
1129 rb->button_clear_queue();
1130 rb->lcd_clear_display();
1131 }
1132
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001133 undo_info.current = n;
1134 }
1135
1136 return true;
1137}
1138
1139static int sokoban_menu(void)
1140{
1141 int button;
1142 int selection = 0;
1143 int i;
1144 bool menu_quit;
1145 int start_selected = 0;
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001146 int prev_level = current_info.level.index;
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001147
1148 MENUITEM_STRINGLIST(menu, "Sokoban Menu", NULL,
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001149 "Resume", "Select Level", "Audio Playback", "Keys",
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001150 "Load Default Level Set", "Quit Without Saving",
1151 "Save Progress & Quit");
1152
1153 do {
1154 menu_quit = true;
Jonathan Gordon5ca15392008-03-26 03:35:24 +00001155 selection = rb->do_menu(&menu, &start_selected, NULL, false);
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001156
1157 switch (selection) {
1158 case 0: /* Resume */
1159 break;
1160
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001161 case 1: /* Select level */
1162 current_info.level.index++;
1163 rb->set_int("Select Level", "", UNIT_INT,
1164 &current_info.level.index, NULL, 1, 1,
1165 current_info.max_level, NULL);
1166 current_info.level.index--;
1167 if (prev_level != current_info.level.index) {
1168 init_undo();
1169 draw_level();
1170 } else
1171 menu_quit = false;
1172 break;
1173
1174 case 2: /* Audio playback control */
Jonathan Gordon33b785e2008-04-23 10:28:34 +00001175 playback_control(rb, NULL);
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001176 menu_quit = false;
1177 break;
1178
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001179 case 3: /* Keys */
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001180 FOR_NB_SCREENS(i)
1181 rb->screens[i]->clear_display();
1182 rb->lcd_setfont(SOKOBAN_FONT);
1183
1184#if (CONFIG_KEYPAD == RECORDER_PAD) || \
1185 (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
1186 rb->lcd_putsxy(3, 6, "[OFF] Menu");
1187 rb->lcd_putsxy(3, 16, "[ON] Undo");
1188 rb->lcd_putsxy(3, 26, "[PLAY] Redo");
1189 rb->lcd_putsxy(3, 36, "[F1] Down a Level");
1190 rb->lcd_putsxy(3, 46, "[F2] Restart Level");
1191 rb->lcd_putsxy(3, 56, "[F3] Up a Level");
1192#elif CONFIG_KEYPAD == ONDIO_PAD
1193 rb->lcd_putsxy(3, 6, "[OFF] Menu");
1194 rb->lcd_putsxy(3, 16, "[MODE] Undo");
1195 rb->lcd_putsxy(3, 26, "[MODE+DOWN] Redo");
1196 rb->lcd_putsxy(3, 36, "[MODE+LEFT] Previous Level");
1197 rb->lcd_putsxy(3, 46, "[MODE+UP] Restart Level");
1198 rb->lcd_putsxy(3, 56, "[MODE+RIGHT] Up Level");
1199#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
1200 (CONFIG_KEYPAD == IRIVER_H300_PAD)
1201 rb->lcd_putsxy(3, 6, "[STOP] Menu");
1202 rb->lcd_putsxy(3, 16, "[REC] Undo");
1203 rb->lcd_putsxy(3, 26, "[MODE] Redo");
1204 rb->lcd_putsxy(3, 36, "[PLAY+DOWN] Previous Level");
1205 rb->lcd_putsxy(3, 46, "[PLAY] Restart Level");
1206 rb->lcd_putsxy(3, 56, "[PLAY+UP] Next Level");
1207#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
Jens Arnoldb7013222007-07-27 09:57:27 +00001208 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
1209 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001210 rb->lcd_putsxy(3, 6, "[SELECT+MENU] Menu");
1211 rb->lcd_putsxy(3, 16, "[SELECT] Undo");
1212 rb->lcd_putsxy(3, 26, "[SELECT+PLAY] Redo");
1213 rb->lcd_putsxy(3, 36, "[SELECT+LEFT] Previous Level");
1214 rb->lcd_putsxy(3, 46, "[SELECT+RIGHT] Next Level");
1215#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
1216 rb->lcd_putsxy(3, 6, "[POWER] Menu");
1217 rb->lcd_putsxy(3, 16, "[SELECT] Undo");
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001218 rb->lcd_putsxy(3, 26, "[PLAY] Redo");
1219 rb->lcd_putsxy(3, 36, "[REC] Restart Level");
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001220#elif CONFIG_KEYPAD == IRIVER_H10_PAD
1221 rb->lcd_putsxy(3, 6, "[POWER] Menu");
1222 rb->lcd_putsxy(3, 16, "[REW] Undo");
1223 rb->lcd_putsxy(3, 26, "[FF] Redo");
1224 rb->lcd_putsxy(3, 36, "[PLAY+DOWN] Previous Level");
1225 rb->lcd_putsxy(3, 46, "[PLAY+RIGHT] Restart Level");
1226 rb->lcd_putsxy(3, 56, "[PLAY+UP] Next Level");
1227#elif CONFIG_KEYPAD == GIGABEAT_PAD
1228 rb->lcd_putsxy(3, 6, "[POWER] Menu");
1229 rb->lcd_putsxy(3, 16, "[SELECT] Undo");
1230 rb->lcd_putsxy(3, 26, "[A] Redo");
1231 rb->lcd_putsxy(3, 36, "[VOL-] Previous Level");
1232 rb->lcd_putsxy(3, 46, "[MENU] Restart Level");
1233 rb->lcd_putsxy(3, 56, "[VOL+] Next Level");
1234#elif CONFIG_KEYPAD == SANSA_E200_PAD
1235 rb->lcd_putsxy(3, 6, "[POWER] Menu");
1236 rb->lcd_putsxy(3, 16, "[SELECT] Undo");
1237 rb->lcd_putsxy(3, 26, "[REC] Redo");
1238 rb->lcd_putsxy(3, 36, "[SELECT+DOWN] Previous Level");
1239 rb->lcd_putsxy(3, 46, "[SELECT+RIGHT] Restart Level");
1240 rb->lcd_putsxy(3, 56, "[SELECT+UP] Next Level");
1241#endif
1242
Rob Purchase297e0502008-04-27 15:30:19 +00001243#ifdef HAVE_TOUCHPAD
1244 rb->lcd_putsxy(3, 6, SOKOBAN_MENU_NAME " Menu");
1245 rb->lcd_putsxy(3, 16, SOKOBAN_UNDO_NAME " Undo");
1246 rb->lcd_putsxy(3, 26, SOKOBAN_REDO_NAME " Redo");
1247 rb->lcd_putsxy(3, 36, SOKOBAN_PAUSE_NAME " Pause");
1248 rb->lcd_putsxy(3, 46, SOKOBAN_LEVEL_REPEAT_NAME " Restart Level");
1249#endif
1250
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001251 FOR_NB_SCREENS(i)
1252 rb->screens[i]->update();
1253
1254 /* Display until keypress */
1255 do {
1256 rb->sleep(HZ/20);
1257 button = rb->button_get(false);
1258 } while (!button || button & BUTTON_REL ||
1259 button & BUTTON_REPEAT);
1260
1261 menu_quit = false;
1262 break;
1263
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001264 case 4: /* Load default levelset */
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001265 init_boards();
1266 if (!read_levels(true))
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001267 return 5; /* Quit */
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001268 load_level();
1269 break;
1270
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001271 case 5: /* Quit */
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001272 break;
1273
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001274 case 6: /* Save & quit */
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001275 save(SOKOBAN_SAVE_FILE, false);
1276 rb->reload_directory();
1277 }
1278
1279 } while (!menu_quit);
1280
1281 /* Restore font */
1282 rb->lcd_setfont(SOKOBAN_FONT);
1283
1284 FOR_NB_SCREENS(i) {
1285 rb->screens[i]->clear_display();
1286 rb->screens[i]->update();
1287 }
1288
1289 return selection;
1290}
1291
Björn Stenbergba371fb2003-06-29 16:33:04 +00001292static bool sokoban_loop(void)
1293{
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001294 bool moved;
Jens Arnolde35a6582004-10-18 21:45:00 +00001295 int i = 0, button = 0, lastbutton = 0;
Björn Stenbergba371fb2003-06-29 16:33:04 +00001296 short r = 0, c = 0;
Zakk Robertsffbdb252007-02-13 06:48:10 +00001297 int w, h;
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001298 char *loc;
Björn Stenbergba371fb2003-06-29 16:33:04 +00001299
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001300 while (true) {
1301 moved = false;
Björn Stenbergba371fb2003-06-29 16:33:04 +00001302
1303 r = current_info.player.row;
1304 c = current_info.player.col;
1305
1306 button = rb->button_get(true);
1307
Zakk Roberts67faac42006-05-09 05:36:46 +00001308 switch(button)
Björn Stenbergba371fb2003-06-29 16:33:04 +00001309 {
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001310#ifdef SOKOBAN_RC_MENU
1311 case SOKOBAN_RC_MENU:
Kevin Ferrare0e027bd2006-06-30 16:43:47 +00001312#endif
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001313 case SOKOBAN_MENU:
1314 switch (sokoban_menu()) {
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001315 case 5: /* Quit */
1316 case 6: /* Save & quit */
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001317 return PLUGIN_OK;
1318 }
1319 update_screen();
1320 break;
Björn Stenbergba371fb2003-06-29 16:33:04 +00001321
Jens Arnolde35a6582004-10-18 21:45:00 +00001322 case SOKOBAN_UNDO:
1323#ifdef SOKOBAN_UNDO_PRE
1324 if (lastbutton != SOKOBAN_UNDO_PRE)
1325 break;
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001326#else /* repeat can't work here for Ondio, iPod, et al */
Jens Arnolde35a6582004-10-18 21:45:00 +00001327 case SOKOBAN_UNDO | BUTTON_REPEAT:
1328#endif
Björn Stenbergba371fb2003-06-29 16:33:04 +00001329 undo();
1330 rb->lcd_clear_display();
Jens Arnolde35a6582004-10-18 21:45:00 +00001331 update_screen();
Björn Stenbergba371fb2003-06-29 16:33:04 +00001332 break;
1333
Zakk Robertsffbdb252007-02-13 06:48:10 +00001334#ifdef SOKOBAN_REDO
1335 case SOKOBAN_REDO:
1336 case SOKOBAN_REDO | BUTTON_REPEAT:
1337 moved = redo();
1338 rb->lcd_clear_display();
1339 update_screen();
1340 break;
1341#endif
1342
Antoine Cellerier0cbb4cd2007-06-29 20:12:04 +00001343#ifdef SOKOBAN_LEVEL_UP
Jens Arnolde35a6582004-10-18 21:45:00 +00001344 case SOKOBAN_LEVEL_UP:
1345 case SOKOBAN_LEVEL_UP | BUTTON_REPEAT:
Zakk Robertsffbdb252007-02-13 06:48:10 +00001346 /* next level */
Björn Stenbergba371fb2003-06-29 16:33:04 +00001347 init_undo();
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001348 if (current_info.level.index + 1 < current_info.max_level)
1349 current_info.level.index++;
Zakk Robertsffbdb252007-02-13 06:48:10 +00001350
1351 draw_level();
Björn Stenbergba371fb2003-06-29 16:33:04 +00001352 break;
Antoine Cellerier0cbb4cd2007-06-29 20:12:04 +00001353#endif
Björn Stenbergba371fb2003-06-29 16:33:04 +00001354
Antoine Cellerier0cbb4cd2007-06-29 20:12:04 +00001355#ifdef SOKOBAN_LEVEL_DOWN
Jens Arnolde35a6582004-10-18 21:45:00 +00001356 case SOKOBAN_LEVEL_DOWN:
1357 case SOKOBAN_LEVEL_DOWN | BUTTON_REPEAT:
Björn Stenbergba371fb2003-06-29 16:33:04 +00001358 /* previous level */
1359 init_undo();
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001360 if (current_info.level.index > 0)
1361 current_info.level.index--;
Björn Stenbergba371fb2003-06-29 16:33:04 +00001362
1363 draw_level();
Björn Stenbergba371fb2003-06-29 16:33:04 +00001364 break;
Antoine Cellerier0cbb4cd2007-06-29 20:12:04 +00001365#endif
Björn Stenbergba371fb2003-06-29 16:33:04 +00001366
Zakk Robertsffbdb252007-02-13 06:48:10 +00001367#ifdef SOKOBAN_LEVEL_REPEAT
Jens Arnolde35a6582004-10-18 21:45:00 +00001368 case SOKOBAN_LEVEL_REPEAT:
1369 case SOKOBAN_LEVEL_REPEAT | BUTTON_REPEAT:
Björn Stenbergba371fb2003-06-29 16:33:04 +00001370 /* same level */
1371 init_undo();
1372 draw_level();
Björn Stenbergba371fb2003-06-29 16:33:04 +00001373 break;
Zakk Robertsffbdb252007-02-13 06:48:10 +00001374#endif
Björn Stenbergba371fb2003-06-29 16:33:04 +00001375
Jens Arnold29361ab2008-03-22 10:24:28 +00001376 case SOKOBAN_LEFT:
1377 case SOKOBAN_LEFT | BUTTON_REPEAT:
Zakk Robertsffbdb252007-02-13 06:48:10 +00001378 moved = move(SOKOBAN_MOVE_LEFT, false);
Björn Stenbergba371fb2003-06-29 16:33:04 +00001379 break;
1380
Jens Arnold29361ab2008-03-22 10:24:28 +00001381 case SOKOBAN_RIGHT:
1382 case SOKOBAN_RIGHT | BUTTON_REPEAT:
Zakk Robertsffbdb252007-02-13 06:48:10 +00001383 moved = move(SOKOBAN_MOVE_RIGHT, false);
Björn Stenbergba371fb2003-06-29 16:33:04 +00001384 break;
1385
Dave Chapman54d44c82005-12-14 01:31:37 +00001386 case SOKOBAN_UP:
Zakk Robertsffbdb252007-02-13 06:48:10 +00001387 case SOKOBAN_UP | BUTTON_REPEAT:
1388 moved = move(SOKOBAN_MOVE_UP, false);
Björn Stenbergba371fb2003-06-29 16:33:04 +00001389 break;
1390
Dave Chapman54d44c82005-12-14 01:31:37 +00001391 case SOKOBAN_DOWN:
Zakk Robertsffbdb252007-02-13 06:48:10 +00001392 case SOKOBAN_DOWN | BUTTON_REPEAT:
1393 moved = move(SOKOBAN_MOVE_DOWN, false);
Björn Stenbergba371fb2003-06-29 16:33:04 +00001394 break;
1395
Björn Stenbergba371fb2003-06-29 16:33:04 +00001396 default:
Jens Arnolde35a6582004-10-18 21:45:00 +00001397 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
1398 return PLUGIN_USB_CONNECTED;
Björn Stenbergba371fb2003-06-29 16:33:04 +00001399 break;
1400 }
Zakk Roberts67faac42006-05-09 05:36:46 +00001401
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001402 lastbutton = button;
Jens Arnolde35a6582004-10-18 21:45:00 +00001403
Björn Stenbergba371fb2003-06-29 16:33:04 +00001404 if (moved) {
Björn Stenbergba371fb2003-06-29 16:33:04 +00001405 rb->lcd_clear_display();
Jens Arnolde35a6582004-10-18 21:45:00 +00001406 update_screen();
Björn Stenbergba371fb2003-06-29 16:33:04 +00001407 }
1408
1409 /* We have completed this level */
1410 if (current_info.level.boxes_to_go == 0) {
Zakk Robertsffbdb252007-02-13 06:48:10 +00001411
1412 if (moved) {
1413 rb->lcd_clear_display();
Zakk Robertsffbdb252007-02-13 06:48:10 +00001414
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001415 /* Show level complete message & stats */
1416 rb->snprintf(buf, sizeof(buf), "Level %d Complete!",
1417 current_info.level.index + 1);
1418 rb->lcd_getstringsize(buf, &w, &h);
1419 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h*3, buf);
1420
1421 rb->snprintf(buf, sizeof(buf), "%4d Moves ",
1422 current_info.level.moves);
1423 rb->lcd_getstringsize(buf, &w, &h);
1424 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h, buf);
1425
1426 rb->snprintf(buf, sizeof(buf), "%4d Pushes",
1427 current_info.level.pushes);
1428 rb->lcd_getstringsize(buf, &w, &h);
1429 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2, buf);
1430
1431 if (undo_info.count < MAX_UNDOS) {
1432 rb->snprintf(buf, sizeof(buf), "%s: Save solution",
1433 BUTTON_SAVE_NAME);
1434 rb->lcd_getstringsize(buf, &w, &h);
1435 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + h*2, buf);
1436 }
1437
1438 rb->lcd_update();
1439 rb->sleep(HZ/4);
1440 rb->button_clear_queue();
1441
1442 /* Display for 4 seconds or until new keypress */
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001443 for (i = 0; i < 80; i++) {
Zakk Robertsffbdb252007-02-13 06:48:10 +00001444 rb->sleep(HZ/20);
1445 button = rb->button_get(false);
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001446 if (button && !(button & BUTTON_REL) &&
1447 !(button & BUTTON_REPEAT))
Zakk Robertsffbdb252007-02-13 06:48:10 +00001448 break;
1449 }
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001450
1451 if (button == BUTTON_SAVE) {
1452 if (undo_info.count < MAX_UNDOS) {
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001453 /* Set filename to current levelset plus level number
1454 * and .sok extension. Use SAVE_FOLDER if using the
1455 * default levelset, since it's in a hidden folder. */
1456 if (rb->strcmp(buffered_boards.filename,
1457 SOKOBAN_LEVELS_FILE) == 0) {
1458 rb->snprintf(buf, sizeof(buf),
1459 "%s/sokoban.%d.sok",
1460 SOKOBAN_SAVE_FOLDER,
1461 current_info.level.index + 1);
1462 } else {
1463 if ((loc = rb->strrchr(buffered_boards.filename,
1464 '.')) != NULL)
1465 *loc = '\0';
1466 rb->snprintf(buf, sizeof(buf), "%s.%d.sok",
1467 buffered_boards.filename,
1468 current_info.level.index + 1);
1469 if (loc != NULL)
1470 *loc = '.';
1471 }
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001472
1473 if (!rb->kbd_input(buf, MAX_PATH))
1474 save(buf, true);
1475 } else
1476 rb->splash(HZ*2, "Solution too long to save");
1477
1478 rb->lcd_setfont(SOKOBAN_FONT); /* Restore font */
1479 }
Zakk Robertsffbdb252007-02-13 06:48:10 +00001480 }
1481
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001482 FOR_NB_SCREENS(i) {
1483 rb->screens[i]->clear_display();
1484 rb->screens[i]->update();
1485 }
1486
1487 current_info.level.index++;
Björn Stenbergba371fb2003-06-29 16:33:04 +00001488
1489 /* clear undo stats */
1490 init_undo();
1491
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001492 if (current_info.level.index >= current_info.max_level) {
1493 /* Show levelset complete message */
1494 rb->snprintf(buf, sizeof(buf), "You WIN!!");
1495 rb->lcd_getstringsize(buf, &w, &h);
1496 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h/2, buf);
Björn Stenbergba371fb2003-06-29 16:33:04 +00001497
Jens Arnold04daef12005-06-24 22:33:21 +00001498 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001499 /* Display for 4 seconds or until keypress */
1500 for (i = 0; i < 80; i++) {
Brandon Low64fcd4c2006-01-19 06:25:59 +00001501 rb->lcd_fillrect(0, 0, LCD_WIDTH, LCD_HEIGHT);
Björn Stenbergba371fb2003-06-29 16:33:04 +00001502 rb->lcd_update();
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001503 rb->sleep(HZ/10);
Björn Stenbergba371fb2003-06-29 16:33:04 +00001504
1505 button = rb->button_get(false);
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001506 if (button && !(button & BUTTON_REL))
Björn Stenbergba371fb2003-06-29 16:33:04 +00001507 break;
1508 }
Jens Arnold04daef12005-06-24 22:33:21 +00001509 rb->lcd_set_drawmode(DRMODE_SOLID);
Björn Stenbergba371fb2003-06-29 16:33:04 +00001510
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001511 /* Reset to first level & show quit menu */
1512 current_info.level.index = 0;
1513
1514 switch (sokoban_menu()) {
Antoine Cellerierec7252c2007-06-29 19:52:13 +00001515 case 5: /* Quit */
1516 case 6: /* Save & quit */
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001517 return PLUGIN_OK;
1518 }
Björn Stenbergba371fb2003-06-29 16:33:04 +00001519 }
1520
1521 load_level();
1522 update_screen();
1523 }
1524
1525 } /* end while */
1526
1527 return PLUGIN_OK;
1528}
1529
1530
Steve Bavin65265772008-05-13 09:57:56 +00001531enum plugin_status plugin_start(const struct plugin_api* api, const void* parameter)
Björn Stenbergba371fb2003-06-29 16:33:04 +00001532{
1533 int w, h;
Björn Stenbergba371fb2003-06-29 16:33:04 +00001534
Björn Stenbergba371fb2003-06-29 16:33:04 +00001535 (void)(parameter);
1536 rb = api;
Zakk Roberts67faac42006-05-09 05:36:46 +00001537
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001538 rb->lcd_setfont(SOKOBAN_FONT);
Zakk Roberts67faac42006-05-09 05:36:46 +00001539
Björn Stenbergba371fb2003-06-29 16:33:04 +00001540 rb->lcd_clear_display();
Zakk Robertsffbdb252007-02-13 06:48:10 +00001541 rb->lcd_getstringsize(SOKOBAN_TITLE, &w, &h);
1542 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h/2, SOKOBAN_TITLE);
Björn Stenbergba371fb2003-06-29 16:33:04 +00001543 rb->lcd_update();
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001544 rb->sleep(HZ); /* Show title for 1 second */
Björn Stenbergba371fb2003-06-29 16:33:04 +00001545
1546 init_boards();
1547
Antoine Cellerierf91d06d2007-06-28 20:45:00 +00001548 if (parameter == NULL) {
1549 /* Attempt to resume saved progress, otherwise start at beginning */
1550 if (!load(SOKOBAN_SAVE_FILE, true)) {
1551 init_boards();
1552 if (!read_levels(true))
1553 return PLUGIN_OK;
1554 load_level();
1555 }
1556
1557 } else {
1558 /* The plugin is being used to open a file */
1559 if (load((char*) parameter, false)) {
1560 /* If we loaded & played a solution, quit */
1561 if (current_info.level.boxes_to_go == 0)
1562 return PLUGIN_OK;
1563 } else
1564 return PLUGIN_OK;
1565 }
1566
1567 rb->lcd_clear_display();
1568 update_screen();
Björn Stenbergba371fb2003-06-29 16:33:04 +00001569
1570 return sokoban_loop();
1571}