A couple more tweaks
[Rockbox.git] / apps / plugins / mazezam.c
blob41edd3a5e57437313b2e544d253d3841012b4191
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * ### line of auto-generated stuff I don't understand ###
10 * Copyright (C) 2006 Malcolm Tyrrell
12 * MazezaM - a Rockbox version of my ZX Spectrum game from 2002
14 * All files in this archive are subject to the GNU General Public License.
15 * See the file COPYING in the source tree root for full license agreement.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "plugin.h"
22 #include "configfile.h"
23 #include "helper.h"
25 /* Include standard plugin macro */
26 PLUGIN_HEADER
28 static struct plugin_api* rb;
30 MEM_FUNCTION_WRAPPERS(rb);
32 #if CONFIG_KEYPAD == RECORDER_PAD
33 #define MAZEZAM_UP BUTTON_UP
34 #define MAZEZAM_DOWN BUTTON_DOWN
35 #define MAZEZAM_LEFT BUTTON_LEFT
36 #define MAZEZAM_RIGHT BUTTON_RIGHT
37 #define MAZEZAM_SELECT BUTTON_PLAY
39 #define MAZEZAM_RETRY BUTTON_F1
40 #define MAZEZAM_RETRY_KEYNAME "[F1]"
41 #define MAZEZAM_QUIT BUTTON_OFF
42 #define MAZEZAM_QUIT_KEYNAME "[OFF]"
44 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
45 #define MAZEZAM_UP BUTTON_UP
46 #define MAZEZAM_DOWN BUTTON_DOWN
47 #define MAZEZAM_LEFT BUTTON_LEFT
48 #define MAZEZAM_RIGHT BUTTON_RIGHT
49 #define MAZEZAM_SELECT BUTTON_SELECT
51 #define MAZEZAM_RETRY BUTTON_F1
52 #define MAZEZAM_RETRY_KEYNAME "[F1]"
53 #define MAZEZAM_QUIT BUTTON_OFF
54 #define MAZEZAM_QUIT_KEYNAME "[OFF]"
56 #elif CONFIG_KEYPAD == ONDIO_PAD
57 #define MAZEZAM_UP BUTTON_UP
58 #define MAZEZAM_DOWN BUTTON_DOWN
59 #define MAZEZAM_LEFT BUTTON_LEFT
60 #define MAZEZAM_RIGHT BUTTON_RIGHT
61 #define MAZEZAM_SELECT BUTTON_RIGHT
63 #define MAZEZAM_RETRY BUTTON_MENU
64 #define MAZEZAM_RETRY_KEYNAME "[MENU]"
65 #define MAZEZAM_QUIT BUTTON_OFF
66 #define MAZEZAM_QUIT_KEYNAME "[OFF]"
68 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
69 #define MAZEZAM_UP BUTTON_UP
70 #define MAZEZAM_DOWN BUTTON_DOWN
71 #define MAZEZAM_LEFT BUTTON_LEFT
72 #define MAZEZAM_RIGHT BUTTON_RIGHT
73 #define MAZEZAM_SELECT BUTTON_SELECT
75 #define MAZEZAM_RETRY BUTTON_REC
76 #define MAZEZAM_RETRY_KEYNAME "[REC]"
77 #define MAZEZAM_QUIT BUTTON_POWER
78 #define MAZEZAM_QUIT_KEYNAME "[POWER]"
80 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
81 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
82 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
83 #define MAZEZAM_UP BUTTON_MENU
84 #define MAZEZAM_DOWN BUTTON_PLAY
85 #define MAZEZAM_LEFT BUTTON_LEFT
86 #define MAZEZAM_RIGHT BUTTON_RIGHT
87 #define MAZEZAM_SELECT BUTTON_SELECT
89 #define MAZEZAM_RETRY BUTTON_SELECT
90 #define MAZEZAM_RETRY_KEYNAME "[SELECT]"
91 #define MAZEZAM_QUIT (BUTTON_SELECT | BUTTON_REPEAT)
92 #define MAZEZAM_QUIT_KEYNAME "[SELECT] (held)"
94 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
95 (CONFIG_KEYPAD == IRIVER_H300_PAD)
96 #define MAZEZAM_UP BUTTON_UP
97 #define MAZEZAM_DOWN BUTTON_DOWN
98 #define MAZEZAM_LEFT BUTTON_LEFT
99 #define MAZEZAM_RIGHT BUTTON_RIGHT
100 #define MAZEZAM_SELECT BUTTON_SELECT
102 #define MAZEZAM_RETRY BUTTON_ON
103 #define MAZEZAM_RETRY_KEYNAME "[ON]"
104 #define MAZEZAM_QUIT BUTTON_OFF
105 #define MAZEZAM_QUIT_KEYNAME "[OFF]"
107 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
108 #define MAZEZAM_UP BUTTON_UP
109 #define MAZEZAM_DOWN BUTTON_DOWN
110 #define MAZEZAM_LEFT BUTTON_LEFT
111 #define MAZEZAM_RIGHT BUTTON_RIGHT
112 #define MAZEZAM_SELECT BUTTON_SELECT
114 #define MAZEZAM_RETRY BUTTON_A
115 #define MAZEZAM_RETRY_KEYNAME "[A]"
116 #define MAZEZAM_QUIT BUTTON_POWER
117 #define MAZEZAM_QUIT_KEYNAME "[POWER]"
119 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
120 (CONFIG_KEYPAD == SANSA_C200_PAD)
121 #define MAZEZAM_UP BUTTON_UP
122 #define MAZEZAM_DOWN BUTTON_DOWN
123 #define MAZEZAM_LEFT BUTTON_LEFT
124 #define MAZEZAM_RIGHT BUTTON_RIGHT
125 #define MAZEZAM_SELECT BUTTON_SELECT
127 #define MAZEZAM_RETRY BUTTON_REC
128 #define MAZEZAM_RETRY_KEYNAME "[REC]"
129 #define MAZEZAM_QUIT BUTTON_POWER
130 #define MAZEZAM_QUIT_KEYNAME "[POWER]"
132 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
133 #define MAZEZAM_UP BUTTON_SCROLL_UP
134 #define MAZEZAM_DOWN BUTTON_SCROLL_DOWN
135 #define MAZEZAM_LEFT BUTTON_LEFT
136 #define MAZEZAM_RIGHT BUTTON_RIGHT
137 #define MAZEZAM_SELECT BUTTON_PLAY
139 #define MAZEZAM_RETRY BUTTON_PLAY
140 #define MAZEZAM_RETRY_KEYNAME "[PLAY]"
141 #define MAZEZAM_QUIT BUTTON_POWER
142 #define MAZEZAM_QUIT_KEYNAME "[POWER]"
144 #endif
146 /* The gap for the border around the heading in text pages. In fact, 2 is
147 * really the only acceptable value.
149 #define MAZEZAM_MENU_BORDER 2
150 #define MAZEZAM_EXTRA_LIFE 2 /* get an extra life every _ levels */
151 #define MAZEZAM_START_LIVES 3 /* how many lives at game start */
153 #ifdef HAVE_LCD_COLOR
154 #define MAZEZAM_HEADING_COLOR LCD_RGBPACK(255,255, 0) /* Yellow */
155 #define MAZEZAM_BORDER_COLOR LCD_RGBPACK( 0, 0,255) /* Blue */
156 #define MAZEZAM_TEXT_COLOR LCD_RGBPACK(255,255,255) /* White */
157 #define MAZEZAM_BG_COLOR LCD_RGBPACK( 0, 0, 0) /* Black */
158 #define MAZEZAM_WALL_COLOR LCD_RGBPACK(100,100,100) /* Dark gray */
159 #define MAZEZAM_PLAYER_COLOR LCD_RGBPACK(255,255,255) /* White */
160 #define MAZEZAM_GATE_COLOR LCD_RGBPACK(100,100,100) /* Dark gray */
162 /* the rows are coloured sequentially */
163 #define MAZEZAM_NUM_CHUNK_COLORS 8
164 static const unsigned chunk_colors[MAZEZAM_NUM_CHUNK_COLORS] = {
165 LCD_RGBPACK(255,192, 32), /* Orange */
166 LCD_RGBPACK(255, 0, 0), /* Red */
167 LCD_RGBPACK( 0,255, 0), /* Green */
168 LCD_RGBPACK( 0,255,255), /* Cyan */
169 LCD_RGBPACK(255,175,175), /* Pink */
170 LCD_RGBPACK(255,255, 0), /* Yellow */
171 LCD_RGBPACK( 0, 0,255), /* Blue */
172 LCD_RGBPACK(255, 0,255), /* Magenta */
175 #elif LCD_DEPTH > 1
177 #define MAZEZAM_HEADING_GRAY LCD_BLACK
178 #define MAZEZAM_BORDER_GRAY LCD_DARKGRAY
179 #define MAZEZAM_TEXT_GRAY LCD_BLACK
180 #define MAZEZAM_BG_GRAY LCD_WHITE
181 #define MAZEZAM_WALL_GRAY LCD_DARKGRAY
182 #define MAZEZAM_PLAYER_GRAY LCD_BLACK
183 #define MAZEZAM_GATE_GRAY LCD_BLACK
184 #define MAZEZAM_CHUNK_EDGE_GRAY LCD_BLACK
186 #define MAZEZAM_NUM_CHUNK_GRAYS 2
187 static const unsigned chunk_gray[MAZEZAM_NUM_CHUNK_GRAYS] = {
188 LCD_LIGHTGRAY,
189 LCD_DARKGRAY,
191 /* darker version of the above */
192 static const unsigned chunk_gray_shade[MAZEZAM_NUM_CHUNK_GRAYS] = {
193 LCD_DARKGRAY,
194 LCD_BLACK,
196 #endif
198 #define MAZEZAM_GAMEOVER_TEXT "Game Over"
199 #define MAZEZAM_GAMEOVER_DELAY (3 * HZ) / 2
200 #define MAZEZAM_LEVEL_LIVES_TEXT "Level %d, Lives %d"
201 #define MAZEZAM_LEVEL_LIVES_DELAY HZ
202 #define MAZEZAM_WELLDONE_DELAY 4 * HZ
204 /* The maximum number of lines that a text page can display.
205 * This must be 4 or less if the Archos recorder is to be
206 * supported.
208 #define MAZEZAM_TEXT_MAXLINES 4
210 /* A structure for holding text pages */
211 struct textpage {
212 /* Ensure 1 < num_lines <= MAZEZAM_TEXT_MAXLINES */
213 short num_lines;
214 char *line[MAZEZAM_TEXT_MAXLINES]; /* text of lines */
217 /* The text page for the welcome screen */
218 static const struct textpage title_page = {
220 {"MazezaM", "play game", "instructions", "quit"}
223 /* The number of help screens */
224 #define MAZEZAM_NUM_HELP_PAGES 4
226 /* The instruction screens */
227 static const struct textpage help_page[] = {
228 {4,{"Instructions","10 mazezams","bar your way","to freedom"}},
229 {4,{"Instructions","Push the rows","left and right","to escape"}},
230 {4,{"Instructions","Press " MAZEZAM_RETRY_KEYNAME " to","retry a level",
231 "(lose 1 life)"}},
232 {4,{"Instructions","Press " MAZEZAM_QUIT_KEYNAME,"to quit","the game"}}
235 /* the text of the screen that asks for a quit confirmation */
236 static const struct textpage confirm_page = {
238 {"Quit","Are you sure?","yes","no"}
241 /* the text of the screen at the end of the game */
242 static const struct textpage welldone_page = {
244 {"Well Done","You have","escaped",""}
247 /* the text of the screen asking if the user wants to
248 * resume or start a new game.
250 static const struct textpage resume_page = {
252 {"Checkpoint", "continue", "new game"}
255 /* maximum height of a level */
256 #define MAZEZAM_MAX_LINES 11
257 /* maximum number of chunks on a line */
258 #define MAZEZAM_MAX_CHUNKS 5
260 /* A structure for holding levels */
261 struct mazezam_level {
262 short height; /* the number of lines */
263 short width; /* the width */
264 short entrance; /* the line on which the entrance lies */
265 short exit; /* the line on which the exit lies */
266 char *line[MAZEZAM_MAX_LINES]; /* the chunk data in string form */
269 /* The number of levels. Note that the instruction screens reference this
270 * number
272 #define MAZEZAM_NUM_LEVELS 10
274 /* The levels. In theory, they could be stored in a file so this data
275 * structure should not be accessed outside parse_level()
277 * These levels are copyright (C) 2002 Malcolm Tyrrell. They're
278 * probably covered by the GPL as they constitute part of the source
279 * code of this plugin, but you may distibute them seperately with
280 * other Free Software if you want. You can download them from:
281 * http://webpages.dcu.ie/~tyrrelma/MazezaM.
283 static const struct mazezam_level level_data[MAZEZAM_NUM_LEVELS] = {
284 {2,7,0,0,{" $ $"," $ $$",NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
285 NULL}},
286 {3,8,2,1,{" $ $$$"," $ $ $"," $ $ $",NULL,NULL,NULL,NULL,NULL,NULL,
287 NULL,NULL}},
288 {4,14,1,3,{" $$$$$ $$ $$"," $$ $$ $$","$$ $ $$ $$$",
289 " $$$$$$$$ $",NULL,NULL,NULL,NULL,NULL,NULL,NULL}},
290 {6,7,4,2,{" $"," $$$$"," $$$ $$"," $ $ $"," $ $$","$ $$",
291 NULL,NULL,NULL,NULL,NULL}},
292 {6,13,0,0,{" $$$$$","$ $$$$$ $$$"," $ $$$ $$$$",
293 "$ $ $$$$$$$"," $$$ $ $$","$ $ $ $$ $",NULL,NULL,
294 NULL,NULL,NULL}},
295 {11,5,10,0,{" $"," $ $$"," $$","$ $"," $ $"," $$$","$ $",
296 " $ $"," $ $","$ $$"," $"}},
297 {7,16,0,6,{" $$$$$$$"," $$$$ $$$$ $ $","$$ $$ $$$$$$ $ $",
298 "$ $ $"," $$$$$$$$$$$$$$"," $ $$ $ $$$",
299 " $ $$$ $$",NULL,NULL,NULL,NULL}},
300 {4,15,2,0,{" $$$$ $$$$ $$"," $ $$ $$ $ $$"," $ $$ $$$$ $$",
301 " $ $$ $$$$ $",NULL,NULL,NULL,NULL,NULL,NULL,NULL}},
302 {7,9,6,2,{" $ $$$$"," $ $ $$"," $ $$$$ $","$ $$ $"," $ $$$",
303 " $$$$$$"," $",NULL,NULL,NULL,NULL}},
304 {10,14,8,0,{" $"," $$$$$$$$$$ $"," $$$ $$",
305 " $ $$$$$$$$ $"," $$$ $$$ $$$"," $$$ $ $$$",
306 " $ $$$$$$$ $$"," $ $ $ $$$"," $$$$$$$$$$$$",
307 "",NULL}}
310 /* This is the data structure the game uses for managing levels */
311 struct chunk_data {
312 /* the number of chunks on a line */
313 short l_num[MAZEZAM_MAX_LINES];
314 /* the width of a chunk */
315 short c_width[MAZEZAM_MAX_LINES][MAZEZAM_MAX_CHUNKS];
316 /* the inset of a chunk */
317 short c_inset[MAZEZAM_MAX_LINES][MAZEZAM_MAX_CHUNKS];
320 /* The state and exit code of the level loop */
321 enum level_state {
322 LEVEL_STATE_LOOPING,
323 LEVEL_STATE_COMPLETED,
324 LEVEL_STATE_FAILED,
325 LEVEL_STATE_QUIT,
326 LEVEL_STATE_PARSE_ERROR,
327 LEVEL_STATE_USB_CONNECTED,
330 /* The state and exit code of the text screens. I use the
331 * same enum for all of them, even though there are some
332 * differences.
334 enum text_state {
335 TEXT_STATE_LOOPING,
336 TEXT_STATE_QUIT,
337 TEXT_STATE_OKAY,
338 TEXT_STATE_USB_CONNECTED,
339 TEXT_STATE_PARSE_ERROR,
340 TEXT_STATE_BACK,
343 /* The state and exit code of the game loop */
344 enum game_state {
345 GAME_STATE_LOOPING,
346 GAME_STATE_QUIT,
347 GAME_STATE_OKAY,
348 GAME_STATE_USB_CONNECTED,
349 GAME_STATE_OVER,
350 GAME_STATE_COMPLETED,
351 GAME_STATE_PARSE_ERROR,
354 /* The various constants needed for configuration files.
355 * See apps/plugins/lib/configfile.*
357 #define MAZEZAM_CONFIG_FILENAME "mazezam.data"
358 #define MAZEZAM_CONFIG_NUM_ITEMS 1
359 #define MAZEZAM_CONFIG_VERSION 0
360 #define MAZEZAM_CONFIG_MINVERSION 0
361 #define MAZEZAM_CONFIG_LEVELS_NAME "restart_level"
363 /* A structure containing the data that is written to
364 * the configuration file
366 struct resume_data {
367 int level; /* level at which to restart the game */
370 /* Display a screen of text. line[0] is the heading.
371 * line[highlight] will be highlighted, unless highlight == 0
373 static void display_text_page(struct textpage text, int highlight)
375 int w[text.num_lines], h[text.num_lines];
376 int hsum,i,vgap,vnext;
378 rb->lcd_clear_display();
380 /* find out how big the text is so we can determine the positioning */
381 hsum = 0;
382 for(i = 0; i < text.num_lines; i++) {
383 rb->lcd_getstringsize(text.line[i], w+i, h+i);
384 hsum += h[i];
387 vgap = (LCD_HEIGHT-hsum)/(text.num_lines+1);
389 /* The Heading */
391 #ifdef HAVE_LCD_COLOR
392 rb->lcd_set_foreground(MAZEZAM_BORDER_COLOR);
393 #elif LCD_DEPTH > 1
394 rb->lcd_set_foreground(MAZEZAM_BORDER_GRAY);
395 #endif
396 rb->lcd_drawrect((LCD_WIDTH-w[0])/2-MAZEZAM_MENU_BORDER,
397 vgap-MAZEZAM_MENU_BORDER, w[0] + 2*MAZEZAM_MENU_BORDER,
398 h[0] + 2*MAZEZAM_MENU_BORDER);
399 rb->lcd_drawrect((LCD_WIDTH-w[0])/2-MAZEZAM_MENU_BORDER*2,
400 vgap-MAZEZAM_MENU_BORDER*2, w[0] + 4*MAZEZAM_MENU_BORDER,
401 h[0] + 4*MAZEZAM_MENU_BORDER);
402 rb->lcd_drawline(0,vgap + h[0]/2,(LCD_WIDTH-w[0])/2-MAZEZAM_MENU_BORDER*2,
403 vgap + h[0]/2);
404 rb->lcd_drawline((LCD_WIDTH-w[0])/2+w[0]+MAZEZAM_MENU_BORDER*2,
405 vgap + h[0]/2,LCD_WIDTH-1,vgap + h[0]/2);
406 #ifdef HAVE_LCD_COLOR
407 rb->lcd_set_foreground(MAZEZAM_HEADING_COLOR);
408 #elif LCD_DEPTH > 1
409 rb->lcd_set_foreground(MAZEZAM_HEADING_GRAY);
410 #endif
411 rb->lcd_putsxy((LCD_WIDTH-w[0])/2,vgap,text.line[0]);
413 vnext = vgap*2 + h[0];
415 /* The other lines */
417 #ifdef HAVE_LCD_COLOR
418 rb->lcd_set_foreground(MAZEZAM_TEXT_COLOR);
419 #elif LCD_DEPTH > 1
420 rb->lcd_set_foreground(MAZEZAM_TEXT_GRAY);
421 #endif
422 for (i = 1; i<text.num_lines; i++) {
423 rb->lcd_putsxy((LCD_WIDTH-w[i])/2,vnext,text.line[i]);
425 /* add underlining if i is the highlighted line */
426 if (i == highlight) {
427 rb->lcd_drawline((LCD_WIDTH-w[i])/2, vnext + h[i] + 1,
428 (LCD_WIDTH-w[i])/2 + w[i], vnext + h[i] + 1);
431 vnext += vgap + h[i];
434 rb->lcd_update();
438 /* Parse the level data from the level_data structure. This could be
439 * replaced by a file read. Returns true if the level parsed correctly.
441 static bool parse_level(short level, struct chunk_data *cd,
442 short *width, short *height, short *entrance, short *exit)
444 int i,j;
445 char c,clast;
447 *width = level_data[level].width;
448 *height = level_data[level].height;
449 *entrance = level_data[level].entrance;
450 *exit = level_data[level].exit;
452 /* for each line in the level */
453 for (i = 0; i<level_data[level].height; i++) {
454 if (level_data[level].line[i] == NULL)
455 return false;
456 else {
457 j = 0;
458 cd->l_num[i] = 0;
459 clast = ' '; /* the character we last considered */
460 while ((c = level_data[level].line[i][j]) != '\0') {
461 if (c != ' ') {
462 if (clast == ' ') {
463 cd->l_num[i] += 1;
464 if (cd->l_num[i] > MAZEZAM_MAX_CHUNKS)
465 return false;
466 cd->c_inset[i][cd->l_num[i] - 1] = j;
467 cd->c_width[i][cd->l_num[i] - 1] = 1;
469 else
470 cd->c_width[i][cd->l_num[i] - 1] += 1;
472 clast = c;
473 j++;
477 return true;
480 /* Draw the level */
481 static void draw_level(
482 struct chunk_data *cd, /* the data about the chunks */
483 short *shift, /* an array of the horizontal offset of the lines */
484 short width,
485 short height,
486 short entrance,
487 short exit,
488 short x, /* player's x and y coords */
489 short y)
491 /* The number of pixels the side of a square should be */
492 short size = (LCD_WIDTH/(width+2)) < (LCD_HEIGHT/height) ?
493 (LCD_WIDTH/(width+2)) : (LCD_HEIGHT/height);
494 /* The x and y position (in pixels) of the top left corner of the
495 * level
497 short xOff = (LCD_WIDTH - (size*width))/2;
498 short yOff = (LCD_HEIGHT - (size*height))/2;
499 /* For drawing the player, taken from the sokoban plugin */
500 short max = size - 1;
501 short middle = max / 2;
502 short ldelta = (middle + 1) / 2;
503 short i,j;
504 short third = size / 3;
505 short twothirds = (2 * size) / 3;
506 #ifndef HAVE_LCD_COLOR
507 /* We #def these out to supress a compiler warning */
508 short k;
509 #if LCD_DEPTH <= 1
510 short l;
511 #endif
512 #endif
514 rb->lcd_clear_display();
516 #ifdef HAVE_LCD_COLOR
517 rb->lcd_set_foreground(MAZEZAM_WALL_COLOR);
518 #elif LCD_DEPTH > 1
519 rb->lcd_set_foreground(MAZEZAM_WALL_GRAY);
520 #endif
521 /* draw the upper wall */
522 rb->lcd_fillrect(0,0,xOff,yOff+(size*entrance));
523 rb->lcd_fillrect(xOff,0,size*width,yOff);
524 rb->lcd_fillrect(xOff+(size*width),0,LCD_WIDTH-xOff-(size*width),
525 yOff+(size*exit));
527 /* draw the lower wall */
528 rb->lcd_fillrect(0,yOff+(size*entrance)+size,xOff,
529 LCD_HEIGHT-yOff-(size*entrance)-size);
530 rb->lcd_fillrect(xOff,yOff+(size*height),size*width,
531 LCD_HEIGHT-yOff-(size*height));
532 /* Note: the exit is made one pixel thinner than necessary as a visual
533 * clue that chunks cannot be pushed into it
535 rb->lcd_fillrect(xOff+(size*width),yOff+(size*exit)+size-1,
536 LCD_WIDTH-xOff+(size*width),
537 LCD_HEIGHT-yOff-(size*exit)-size+1);
539 /* draw the chunks */
540 for (i = 0; i<height; i++) {
541 #ifdef HAVE_LCD_COLOR
542 /* adding width to i should have a fixed, but randomising effect on
543 * the choice of the colours of the top line of chunks
545 rb->lcd_set_foreground(chunk_colors[(i+width) %
546 MAZEZAM_NUM_CHUNK_COLORS]);
547 #endif
548 for (j = 0; j<cd->l_num[i]; j++) {
549 #ifdef HAVE_LCD_COLOR
550 rb->lcd_fillrect(xOff+size*shift[i]+size*cd->c_inset[i][j],
551 yOff+size*i, cd->c_width[i][j]*size,size);
552 #elif LCD_DEPTH > 1
553 rb->lcd_set_foreground(MAZEZAM_CHUNK_EDGE_GRAY);
554 rb->lcd_drawrect(xOff+size*shift[i]+size*cd->c_inset[i][j],
555 yOff+size*i, cd->c_width[i][j]*size,size);
557 /* draw shade */
558 rb->lcd_set_foreground(chunk_gray_shade[(i+width) %
559 MAZEZAM_NUM_CHUNK_GRAYS]);
560 rb->lcd_drawline(xOff+size*shift[i]+size*cd->c_inset[i][j]+1,
561 yOff+size*i+size-2,
562 xOff+size*shift[i]+size*cd->c_inset[i][j]+
563 cd->c_width[i][j]*size-3,
564 yOff+size*i+size-2);
565 rb->lcd_drawline(xOff+size*shift[i]+size*cd->c_inset[i][j]+
566 cd->c_width[i][j]*size-2,
567 yOff+size*i,
568 xOff+size*shift[i]+size*cd->c_inset[i][j]+
569 cd->c_width[i][j]*size-2,
570 yOff+size*i+size-2);
572 /* draw fill */
573 rb->lcd_set_foreground(chunk_gray[(i+width) %
574 MAZEZAM_NUM_CHUNK_GRAYS]);
575 for (k = yOff+size*i+2; k < yOff+size*i+size-2; k += 2)
576 rb->lcd_drawline(xOff+size*shift[i]+size*cd->c_inset[i][j]+2,k,
577 xOff+size*shift[i]+size*cd->c_inset[i][j]+
578 cd->c_width[i][j]*size-3,k);
579 #else
580 rb->lcd_drawrect(xOff+size*shift[i]+size*cd->c_inset[i][j],
581 yOff+size*i, cd->c_width[i][j]*size,size);
582 for (k = xOff+size*shift[i]+size*cd->c_inset[i][j]+2;
583 k < xOff+size*shift[i]+size*cd->c_inset[i][j]+
584 cd->c_width[i][j]*size;
585 k += 2 + (i & 1))
586 for (l = yOff+size*i+2; l < yOff+size*i+size; l += 2 + (i & 1))
587 rb->lcd_drawpixel(k, l);
588 #endif
592 /* draw the player (mostly copied from the sokoban plugin) */
593 #ifdef HAVE_LCD_COLOR
594 rb->lcd_set_foreground(MAZEZAM_PLAYER_COLOR);
595 #elif LCD_DEPTH > 1
596 rb->lcd_set_foreground(MAZEZAM_PLAYER_GRAY);
597 #endif
598 rb->lcd_drawline(xOff+size*x, yOff+size*y+middle,
599 xOff+size*x+max, yOff+size*y+middle);
600 rb->lcd_drawline(xOff+size*x+middle, yOff+size*y,
601 xOff+size*x+middle, yOff+size*y+max-ldelta);
602 rb->lcd_drawline(xOff+size*x+middle, yOff+size*y+max-ldelta,
603 xOff+size*x+middle-ldelta, yOff+size*y+max);
604 rb->lcd_drawline(xOff+size*x+middle, yOff+size*y+max-ldelta,
605 xOff+size*x+middle+ldelta, yOff+size*y+max);
607 /* draw the gate, if the player has moved into the level */
608 if (x >= 0) {
609 #ifdef HAVE_LCD_COLOR
610 rb->lcd_set_foreground(MAZEZAM_GATE_COLOR);
611 #elif LCD_DEPTH > 1
612 rb->lcd_set_foreground(MAZEZAM_GATE_GRAY);
613 #endif
614 rb->lcd_drawline(xOff-size,yOff+entrance*size+third,
615 xOff-1,yOff+entrance*size+third);
616 rb->lcd_drawline(xOff-size,yOff+entrance*size+twothirds,
617 xOff-1,yOff+entrance*size+twothirds);
618 rb->lcd_drawline(xOff-size+third,yOff+entrance*size,
619 xOff-size+third,yOff+entrance*size+size-1);
620 rb->lcd_drawline(xOff-size+twothirds,yOff+entrance*size,
621 xOff-size+twothirds,yOff+entrance*size+size-1);
625 /* Manage the congratulations screen */
626 static enum text_state welldone_screen(void)
628 int button = BUTTON_NONE;
629 enum text_state state = TEXT_STATE_LOOPING;
631 display_text_page(welldone_page, 0);
633 while (state == TEXT_STATE_LOOPING) {
634 button = rb->button_get(true);
636 switch (button) {
637 case MAZEZAM_QUIT:
638 state = TEXT_STATE_QUIT;
639 break;
641 case MAZEZAM_SELECT:
642 #if CONFIG_KEYPAD != ONDIO_PAD
643 case MAZEZAM_RIGHT:
644 #endif
645 state = TEXT_STATE_OKAY;
646 break;
648 default:
649 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
650 state = TEXT_STATE_USB_CONNECTED;
651 break;
655 return state;
658 /* Manage the quit confimation screen */
659 static enum text_state quitconfirm_loop(void)
661 int button = BUTTON_NONE;
662 enum text_state state = TEXT_STATE_LOOPING;
663 short select = 2;
665 display_text_page(confirm_page, select + 1);
667 /* Wait for a button release. This is useful when a repeated button
668 * press is used for quit.
670 while ((rb->button_get(true) & BUTTON_REL) != BUTTON_REL);
672 while (state == TEXT_STATE_LOOPING) {
673 display_text_page(confirm_page, select + 1);
675 button = rb->button_get(true);
677 switch (button) {
678 case MAZEZAM_QUIT:
679 state = TEXT_STATE_QUIT;
680 break;
682 case MAZEZAM_UP:
683 case MAZEZAM_DOWN:
684 select = (2 - select) + 1;
685 break;
687 case MAZEZAM_SELECT:
688 #if CONFIG_KEYPAD != ONDIO_PAD
689 case MAZEZAM_RIGHT:
690 #endif
691 if (select == 1)
692 state = TEXT_STATE_QUIT;
693 else
694 state = TEXT_STATE_OKAY;
695 break;
697 default:
698 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
699 state = TEXT_STATE_USB_CONNECTED;
700 break;
704 return state;
707 /* Manage the playing of a level */
708 static enum level_state level_loop(short level, short lives)
710 struct chunk_data cd;
711 short shift[MAZEZAM_MAX_LINES]; /* amount each line has been shifted */
712 short width;
713 short height;
714 short entrance;
715 short exit;
716 short i;
717 short x,y;
718 int button;
719 enum level_state state = LEVEL_STATE_LOOPING;
720 bool blocked; /* is there a chunk in the way of the player? */
722 if (!(parse_level(level,&cd,&width,&height,&entrance,&exit)))
723 return LEVEL_STATE_PARSE_ERROR;
725 for (i = 0; i < height; i++)
726 shift[i] = 0;
728 x = -1;
729 y = entrance;
731 draw_level(&cd, shift, width, height, entrance, exit, x, y);
733 #ifdef HAVE_REMOTE_LCD
734 /* Splash text seems to use the remote display by
735 * default. I suppose I better keep it tidy!
737 rb->lcd_remote_clear_display();
738 #endif
739 rb->splash(MAZEZAM_LEVEL_LIVES_DELAY, MAZEZAM_LEVEL_LIVES_TEXT,
740 level+1, lives);
742 /* ensure keys pressed during the splash screen are ignored */
743 rb->button_clear_queue();
745 while (state == LEVEL_STATE_LOOPING) {
746 draw_level(&cd, shift, width, height, entrance, exit, x, y);
747 rb->lcd_update();
748 button = rb->button_get(true);
749 blocked = false;
751 switch (button) {
752 case MAZEZAM_UP:
753 case MAZEZAM_UP | BUTTON_REPEAT:
754 if ((y > 0) && (x >= 0) && (x < width)) {
755 for (i = 0; i < cd.l_num[y-1]; i++)
756 blocked = blocked ||
757 ((x>=shift[y-1]+cd.c_inset[y-1][i]) &&
758 (x<shift[y-1]+cd.c_inset[y-1][i]+
759 cd.c_width[y-1][i]));
760 if (!blocked) y -= 1;
762 break;
764 case MAZEZAM_DOWN:
765 case MAZEZAM_DOWN | BUTTON_REPEAT:
766 if ((y < height-1) && (x >= 0) && (x < width)) {
767 for (i = 0; i < cd.l_num[y+1]; i++)
768 blocked = blocked ||
769 ((x>=shift[y+1]+cd.c_inset[y+1][i]) &&
770 (x<shift[y+1]+cd.c_inset[y+1][i]+
771 cd.c_width[y+1][i]));
772 if (!blocked) y += 1;
774 break;
776 case MAZEZAM_LEFT:
777 case MAZEZAM_LEFT | BUTTON_REPEAT:
778 if (x > 0) {
779 for (i = 0; i < cd.l_num[y]; i++)
780 blocked = blocked ||
781 (x == shift[y]+cd.c_inset[y][i]+
782 cd.c_width[y][i]);
783 if (!blocked) x -= 1;
784 else if (shift[y] + cd.c_inset[y][0] > 0) {
785 x -= 1;
786 shift[y] -= 1;
789 break;
791 case MAZEZAM_RIGHT:
792 case MAZEZAM_RIGHT | BUTTON_REPEAT:
793 if (x < width-1) {
794 for (i = 0; i < cd.l_num[y]; i++)
795 blocked = blocked || (x+1 == shift[y]+cd.c_inset[y][i]);
796 if (!blocked) x += 1;
797 else if (shift[y] + cd.c_inset[y][cd.l_num[y]-1] +
798 cd.c_width[y][cd.l_num[y]-1] < width) {
799 x += 1;
800 shift[y] += 1;
803 else if (x == width) state = LEVEL_STATE_COMPLETED;
804 else if (y == exit) x += 1;
805 break;
807 case MAZEZAM_RETRY:
808 state = LEVEL_STATE_FAILED;
809 break;
811 case MAZEZAM_QUIT:
812 switch (quitconfirm_loop()) {
813 case TEXT_STATE_QUIT:
814 state = LEVEL_STATE_QUIT;
815 break;
817 case TEXT_STATE_USB_CONNECTED:
818 state = LEVEL_STATE_USB_CONNECTED;
819 break;
821 default:
822 break;
824 break;
826 default:
827 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
828 state = LEVEL_STATE_USB_CONNECTED;
829 break;
833 return state;
836 /* The loop which manages a full game of MazezaM */
837 static enum game_state game_loop(struct resume_data *r)
839 enum game_state state = GAME_STATE_LOOPING;
840 int level = r->level;
841 int lives = MAZEZAM_START_LIVES;
843 rb->lcd_clear_display();
845 while (state == GAME_STATE_LOOPING)
847 switch (level_loop(level,lives)) {
848 case LEVEL_STATE_COMPLETED:
849 level += 1;
850 if (!((level - r->level) % MAZEZAM_EXTRA_LIFE))
851 lives += 1;
852 break;
854 case LEVEL_STATE_QUIT:
855 state = GAME_STATE_QUIT;
856 break;
858 case LEVEL_STATE_FAILED:
859 lives -= 1;
860 break;
862 case LEVEL_STATE_PARSE_ERROR:
863 state = GAME_STATE_PARSE_ERROR;
864 break;
866 case LEVEL_STATE_USB_CONNECTED:
867 state = GAME_STATE_USB_CONNECTED;
868 break;
870 default:
871 break;
873 if (lives == 0)
874 state = GAME_STATE_OVER;
875 else if (level == MAZEZAM_NUM_LEVELS)
876 state = GAME_STATE_COMPLETED;
879 switch (state) {
880 case GAME_STATE_OVER:
881 #ifdef HAVE_REMOTE_LCD
882 /* Splash text seems to use the remote display by
883 * default. I suppose I better keep it tidy!
885 rb->lcd_remote_clear_display();
886 #endif
887 rb->splash(MAZEZAM_GAMEOVER_DELAY, MAZEZAM_GAMEOVER_TEXT);
888 break;
890 case GAME_STATE_COMPLETED:
891 switch (welldone_screen()) {
892 case TEXT_STATE_QUIT:
893 state = GAME_STATE_QUIT;
894 break;
896 case TEXT_STATE_USB_CONNECTED:
897 state = GAME_STATE_USB_CONNECTED;
898 break;
900 default:
901 state = GAME_STATE_OKAY;
902 break;
904 break;
906 default:
907 break;
910 /* This particular resume game logic is designed to make
911 * players prove they can solve a level more than once
913 if (level > r->level + 1)
914 r->level += 1;
916 return state;
919 /* Manage the instruction screen */
920 static enum text_state instruction_loop(void)
922 int button;
923 enum text_state state = TEXT_STATE_LOOPING;
924 int page = 0;
926 while (state == TEXT_STATE_LOOPING) {
927 display_text_page(help_page[page], 0);
928 button = rb->button_get(true);
930 switch (button) {
931 case MAZEZAM_LEFT:
932 page -= 1;
933 break;
935 case MAZEZAM_SELECT:
936 #if CONFIG_KEYPAD != ONDIO_PAD
937 case MAZEZAM_RIGHT:
938 #endif
939 page += 1;
940 break;
942 case MAZEZAM_QUIT:
943 state = TEXT_STATE_QUIT;
944 break;
946 default:
947 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
948 state = TEXT_STATE_USB_CONNECTED;
949 break;
953 if ((page < 0) || (page >= MAZEZAM_NUM_HELP_PAGES))
954 state = TEXT_STATE_OKAY;
957 return state;
960 /* Manage the text screen that offers the user the option of
961 * resuming or starting a new game
963 static enum text_state resume_game_loop (struct resume_data *r)
965 int button = BUTTON_NONE;
966 enum text_state state = TEXT_STATE_LOOPING;
967 short select = 0;
969 /* if the resume level is 0, don't bother asking */
970 if (r->level == 0) return TEXT_STATE_OKAY;
972 display_text_page(resume_page, select + 1);
974 while (state == TEXT_STATE_LOOPING) {
975 display_text_page(resume_page, select + 1);
977 button = rb->button_get(true);
979 switch (button) {
980 case MAZEZAM_QUIT:
981 state = TEXT_STATE_QUIT;
982 break;
984 case MAZEZAM_LEFT:
985 state = TEXT_STATE_BACK;
986 break;
988 case MAZEZAM_UP:
989 case MAZEZAM_DOWN:
990 select = 1 - select;
991 break;
993 case MAZEZAM_SELECT:
994 #if CONFIG_KEYPAD != ONDIO_PAD
995 case MAZEZAM_RIGHT:
996 #endif
997 if (select == 1) {
998 /* The player wants to play a new game. I could ask
999 * for confirmation here, but the only penalty is
1000 * playing through some already completed levels,
1001 * so I don't think it's necessary
1003 r->level = 0;
1005 state = TEXT_STATE_OKAY;
1006 break;
1008 default:
1009 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
1010 state = TEXT_STATE_USB_CONNECTED;
1011 break;
1015 return state;
1018 /* Load the resume data from the config file. The data is
1019 * stored in both r and old.
1021 static void resume_load_data (struct resume_data *r, struct resume_data *old)
1023 struct configdata config[] = {
1024 {TYPE_INT,0,MAZEZAM_NUM_LEVELS-1,&(r->level),
1025 MAZEZAM_CONFIG_LEVELS_NAME,NULL,NULL}
1028 if (configfile_load(MAZEZAM_CONFIG_FILENAME,config,MAZEZAM_CONFIG_NUM_ITEMS,
1029 MAZEZAM_CONFIG_VERSION) < 0)
1030 r->level = 0;
1031 /* an extra precaution */
1032 else if ((r->level < 0) || (MAZEZAM_NUM_LEVELS <= r->level))
1033 r->level = 0;
1035 old->level = r->level;
1038 /* Save the resume data in the config file, but only if necessary */
1039 static void resume_save_data (struct resume_data *r, struct resume_data *old)
1041 struct configdata config[] = {
1042 {TYPE_INT,0,MAZEZAM_NUM_LEVELS-1,&(r->level),
1043 MAZEZAM_CONFIG_LEVELS_NAME,NULL,NULL}
1046 /* To reduce disk usage, only write the file if the resume data has
1047 * changed.
1049 if (old->level != r->level)
1050 configfile_save(MAZEZAM_CONFIG_FILENAME,config,MAZEZAM_CONFIG_NUM_ITEMS,
1051 MAZEZAM_CONFIG_MINVERSION);
1054 /* The loop which manages the welcome screen and menu */
1055 static enum text_state welcome_loop(void)
1057 int button;
1058 short select = 0;
1059 enum text_state state = TEXT_STATE_LOOPING;
1060 struct resume_data r_data, old_data;
1062 /* Load data */
1063 resume_load_data(&r_data, &old_data);
1065 while (state == TEXT_STATE_LOOPING) {
1066 display_text_page(title_page, select + 1);
1067 button = rb->button_get(true);
1069 switch (button) {
1070 case MAZEZAM_QUIT:
1071 state = TEXT_STATE_QUIT;
1072 break;
1074 case MAZEZAM_UP:
1075 select = (select + (title_page.num_lines - 2)) %
1076 (title_page.num_lines - 1);
1077 break;
1079 case MAZEZAM_DOWN:
1080 select = (select + 1) % (title_page.num_lines - 1);
1081 break;
1083 case MAZEZAM_SELECT:
1084 #if CONFIG_KEYPAD != ONDIO_PAD
1085 case MAZEZAM_RIGHT:
1086 #endif
1087 if (select == 0) { /* play game */
1088 switch (resume_game_loop(&r_data)) {
1089 case TEXT_STATE_QUIT:
1090 state = TEXT_STATE_QUIT;
1091 break;
1093 case TEXT_STATE_USB_CONNECTED:
1094 state = TEXT_STATE_USB_CONNECTED;
1095 break;
1097 case TEXT_STATE_BACK:
1098 break;
1100 default: { /* Ouch! This nesting is too deep! */
1101 switch (game_loop(&r_data)) {
1102 case GAME_STATE_QUIT:
1103 state = TEXT_STATE_QUIT;
1104 break;
1106 case GAME_STATE_USB_CONNECTED:
1107 state = TEXT_STATE_USB_CONNECTED;
1108 break;
1110 case GAME_STATE_PARSE_ERROR:
1111 state = TEXT_STATE_PARSE_ERROR;
1112 break;
1114 default:
1115 break;
1117 break;
1121 else if (select == 1) { /* Instructions */
1122 switch (instruction_loop()) {
1123 case TEXT_STATE_QUIT:
1124 state = TEXT_STATE_QUIT;
1125 break;
1127 case TEXT_STATE_USB_CONNECTED:
1128 state = TEXT_STATE_USB_CONNECTED;
1129 break;
1131 default:
1132 break;
1135 else /* Quit */
1136 state = TEXT_STATE_QUIT;
1138 break;
1140 default:
1141 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
1142 state = TEXT_STATE_USB_CONNECTED;
1143 break;
1147 /* I'm not sure if it's appropriate to write to disk on USB events.
1148 * Currently, I do so.
1150 resume_save_data(&r_data, &old_data);
1152 return state;
1155 /* Plugin entry point */
1156 enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
1158 enum plugin_status state;
1160 /* Usual plugin stuff */
1161 (void)parameter;
1162 rb = api;
1164 /* Turn off backlight timeout */
1165 backlight_force_on(rb); /* backlight control in lib/helper.c */
1167 #ifdef HAVE_LCD_COLOR
1168 rb->lcd_set_background(MAZEZAM_BG_COLOR);
1169 rb->lcd_set_backdrop(NULL);
1170 #elif LCD_DEPTH > 1
1171 rb->lcd_set_background(MAZEZAM_BG_GRAY);
1172 #endif
1173 rb->lcd_setfont(FONT_SYSFIXED);
1175 /* initialise the config file module */
1176 configfile_init(rb);
1178 switch (welcome_loop()) {
1179 case TEXT_STATE_USB_CONNECTED:
1180 state = PLUGIN_USB_CONNECTED;
1181 break;
1183 case TEXT_STATE_PARSE_ERROR:
1184 state = PLUGIN_ERROR;
1185 break;
1187 default:
1188 state = PLUGIN_OK;
1189 break;
1192 /* Turn on backlight timeout (revert to settings) */
1193 backlight_use_settings(rb); /* backlight control in lib/helper.c */
1195 return state;