1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
9 * Copyright (C) 2006, 2008 Malcolm Tyrrell
11 * MazezaM - a Rockbox version of my ZX Spectrum game from 2002
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
23 #include "configfile.h"
25 #include "pluginlib_actions.h"
26 #include "playback_control.h"
28 /* Include standard plugin macro */
31 static const struct plugin_api
* rb
;
33 /* The plugin actions of interest. */
34 const struct button_mapping
*plugin_contexts
[]
35 = {generic_directions
, generic_actions
};
37 MEM_FUNCTION_WRAPPERS(rb
);
39 /* Use the standard plugin buttons rather than a hard-to-maintain list of
40 * MazezaM specific buttons. */
41 #define MAZEZAM_UP PLA_UP
42 #define MAZEZAM_UP_REPEAT PLA_UP_REPEAT
43 #define MAZEZAM_DOWN PLA_DOWN
44 #define MAZEZAM_DOWN_REPEAT PLA_DOWN_REPEAT
45 #define MAZEZAM_LEFT PLA_LEFT
46 #define MAZEZAM_LEFT_REPEAT PLA_LEFT_REPEAT
47 #define MAZEZAM_RIGHT PLA_RIGHT
48 #define MAZEZAM_RIGHT_REPEAT PLA_RIGHT_REPEAT
49 #define MAZEZAM_MENU PLA_QUIT
51 /* All the text is here */
52 #define MAZEZAM_TEXT_GAME_OVER "Game Over"
53 #define MAZEZAM_TEXT_LIVES "Level %d, Lives %d"
54 #define MAZEZAM_TEXT_CHECKPOINT "Checkpoint reached"
55 #define MAZEZAM_TEXT_WELLDONE_TITLE "You have escaped!"
56 #define MAZEZAM_TEXT_WELLDONE_OPTION "Goodbye"
57 #define MAZEZAM_TEXT_MAZEZAM_MENU "MazezaM Menu"
58 #define MAZEZAM_TEXT_RETRY_LEVEL "Retry level"
59 #define MAZEZAM_TEXT_AUDIO_PLAYBACK "Audio playback"
60 #define MAZEZAM_TEXT_QUIT "Quit"
61 #define MAZEZAM_TEXT_BACK "Return"
62 #define MAZEZAM_TEXT_MAIN_MENU "MazezaM"
63 #define MAZEZAM_TEXT_CONTINUE "Play from checkpoint"
64 #define MAZEZAM_TEXT_PLAY_GAME "Play game"
65 #define MAZEZAM_TEXT_PLAY_NEW_GAME "Play new game"
67 #define MAZEZAM_START_LIVES 3 /* how many lives at game start */
68 #define MAZEZAM_FIRST_CHECKPOINT 3 /* The level at the first checkpoint */
69 #define MAZEZAM_CHECKPOINT_INTERVAL 4 /* A checkpoint every _ levels */
72 #define MAZEZAM_HEADING_COLOR LCD_RGBPACK(255,255, 0) /* Yellow */
73 #define MAZEZAM_BORDER_COLOR LCD_RGBPACK( 0, 0,255) /* Blue */
74 #define MAZEZAM_COLOR LCD_RGBPACK(255,255,255) /* White */
75 #define MAZEZAM_BG_COLOR LCD_RGBPACK( 0, 0, 0) /* Black */
76 #define MAZEZAM_WALL_COLOR LCD_RGBPACK(100,100,100) /* Dark gray */
77 #define MAZEZAM_PLAYER_COLOR LCD_RGBPACK(255,255,255) /* White */
78 #define MAZEZAM_GATE_COLOR LCD_RGBPACK(100,100,100) /* Dark gray */
80 /* the rows are coloured sequentially */
81 #define MAZEZAM_NUM_CHUNK_COLORS 8
82 static const unsigned chunk_colors
[MAZEZAM_NUM_CHUNK_COLORS
] = {
83 LCD_RGBPACK(255,192, 32), /* Orange */
84 LCD_RGBPACK(255, 0, 0), /* Red */
85 LCD_RGBPACK( 0,255, 0), /* Green */
86 LCD_RGBPACK( 0,255,255), /* Cyan */
87 LCD_RGBPACK(255,175,175), /* Pink */
88 LCD_RGBPACK(255,255, 0), /* Yellow */
89 LCD_RGBPACK( 0, 0,255), /* Blue */
90 LCD_RGBPACK(255, 0,255), /* Magenta */
95 #define MAZEZAM_HEADING_GRAY LCD_BLACK
96 #define MAZEZAM_BORDER_GRAY LCD_DARKGRAY
97 #define MAZEZAM_GRAY LCD_BLACK
98 #define MAZEZAM_BG_GRAY LCD_WHITE
99 #define MAZEZAM_WALL_GRAY LCD_DARKGRAY
100 #define MAZEZAM_PLAYER_GRAY LCD_BLACK
101 #define MAZEZAM_GATE_GRAY LCD_BLACK
102 #define MAZEZAM_CHUNK_EDGE_GRAY LCD_BLACK
104 #define MAZEZAM_NUM_CHUNK_GRAYS 2
105 static const unsigned chunk_gray
[MAZEZAM_NUM_CHUNK_GRAYS
] = {
109 /* darker version of the above */
110 static const unsigned chunk_gray_shade
[MAZEZAM_NUM_CHUNK_GRAYS
] = {
116 #define MAZEZAM_DELAY_CHECKPOINT HZ
117 #define MAZEZAM_DELAY_LIVES HZ
118 #define MAZEZAM_DELAY_GAME_OVER (3 * HZ) / 2
120 /* maximum height of a level */
121 #define MAZEZAM_MAX_LINES 11
122 /* maximum number of chunks on a line */
123 #define MAZEZAM_MAX_CHUNKS 5
125 /* A structure for storing level data in unparsed form */
126 struct mazezam_level
{
127 short height
; /* the number of lines */
128 short width
; /* the width */
129 short entrance
; /* the line on which the entrance lies */
130 short exit
; /* the line on which the exit lies */
131 char *line
[MAZEZAM_MAX_LINES
]; /* the chunk data in string form */
134 /* The number of levels. */
135 #define MAZEZAM_NUM_LEVELS 10
137 /* The levels. In theory, they could be stored in a file so this data
138 * structure should not be accessed outside parse_level()
140 * These levels are copyright (C) 2002 Malcolm Tyrrell. They're
141 * probably covered by the GPL as they constitute part of the source
142 * code of this plugin, but you may distibute them seperately with
143 * other Free Software if you want. You can download them from:
144 * http://webpages.dcu.ie/~tyrrelma/MazezaM.
146 static const struct mazezam_level level_data
[MAZEZAM_NUM_LEVELS
] = {
147 {2,7,0,0,{" $ $"," $ $$"}},
148 {3,8,2,1,{" $ $$$"," $ $ $"," $ $ $"}},
149 {4,14,1,3,{" $$$$$ $$ $$"," $$ $$ $$","$$ $ $$ $$$",
151 {6,7,4,2,{" $"," $$$$"," $$$ $$"," $ $ $"," $ $$","$ $$"}},
152 {6,13,0,0,{" $$$$$","$ $$$$$ $$$"," $ $$$ $$$$",
153 "$ $ $$$$$$$"," $$$ $ $$","$ $ $ $$ $"}},
154 {11,5,10,0,{" $"," $ $$"," $$","$ $"," $ $"," $$$","$ $",
155 " $ $"," $ $","$ $$"," $"}},
156 {7,16,0,6,{" $$$$$$$"," $$$$ $$$$ $ $","$$ $$ $$$$$$ $ $",
157 "$ $ $"," $$$$$$$$$$$$$$"," $ $$ $ $$$",
159 {4,15,2,0,{" $$$$ $$$$ $$"," $ $$ $$ $ $$"," $ $$ $$$$ $$",
161 {7,9,6,2,{" $ $$$$"," $ $ $$"," $ $$$$ $","$ $$ $"," $ $$$",
163 {10,14,8,0,{" $"," $$$$$$$$$$ $"," $$$ $$",
164 " $ $$$$$$$$ $"," $$$ $$$ $$$"," $$$ $ $$$",
165 " $ $$$$$$$ $$"," $ $ $ $$$"," $$$$$$$$$$$$",
169 /* This data structure which holds information about the rows */
171 /* the number of chunks on a line */
172 short l_num
[MAZEZAM_MAX_LINES
];
173 /* the width of a chunk */
174 short c_width
[MAZEZAM_MAX_LINES
][MAZEZAM_MAX_CHUNKS
];
175 /* the inset of a chunk */
176 short c_inset
[MAZEZAM_MAX_LINES
][MAZEZAM_MAX_CHUNKS
];
179 /* Parsed level data */
185 struct chunk_data cd
;
188 /* The state variable used to hold the state of the plugin */
190 STATE_QUIT
, /* The player wants to quit */
191 STATE_USB_CONNECTED
, /* A USB cable has been inserted */
192 STATE_PARSE_ERROR
, /* There's a parse error in the levels */
193 STATE_WELLDONE
, /* The player has finished the game */
195 STATE_IN_APPLICATION
,
197 STATE_MAIN_MENU
/* The player is at the main menu */
198 = STATE_IN_APPLICATION
,
199 STATE_GAME_OVER
, /* The player is out of lives */
203 STATE_COMPLETED
/* A level has been completed */
206 STATE_FAILED
, /* The player wants to retry the level */
207 STATE_GAME_MENU
, /* The player wan't to access the in-game menu */
212 /* The various constants needed for configuration files.
213 * See apps/plugins/lib/configfile.*
215 #define MAZEZAM_CONFIG_FILENAME "mazezam.data"
216 #define MAZEZAM_CONFIG_NUM_ITEMS 1
217 #define MAZEZAM_CONFIG_VERSION 0
218 #define MAZEZAM_CONFIG_MINVERSION 0
219 #define MAZEZAM_CONFIG_LEVELS_NAME "restart_level"
221 /* A structure containing the data that is written to
222 * the configuration file
225 int level
; /* level at which to restart the game */
229 /* Store the display settings so they are reintroduced during menus */
237 /*****************************************************************************
238 * Store the LCD settings
239 ******************************************************************************/
240 static void store_lcd_settings(void)
242 /* Store the old settings */
244 lcd_settings
.backdrop
= rb
->lcd_get_backdrop();
245 lcd_settings
.foreground
= rb
->lcd_get_foreground();
246 lcd_settings
.background
= rb
->lcd_get_background();
250 /*****************************************************************************
251 * Restore the LCD settings to their defaults
252 ******************************************************************************/
253 static void restore_lcd_settings(void) {
254 /* Turn on backlight timeout (revert to settings) */
255 backlight_use_settings(rb
); /* backlight control in lib/helper.c */
257 /* Restore the old settings */
259 rb
->lcd_set_foreground(lcd_settings
.foreground
);
260 rb
->lcd_set_background(lcd_settings
.background
);
261 rb
->lcd_set_backdrop(lcd_settings
.backdrop
);
265 /*****************************************************************************
266 * Adjust the LCD settings to suit MazezaM levels
267 ******************************************************************************/
268 static void plugin_lcd_settings(void) {
269 /* Turn off backlight timeout */
270 backlight_force_on(rb
); /* backlight control in lib/helper.c */
272 /* Set the new settings */
273 #ifdef HAVE_LCD_COLOR
274 rb
->lcd_set_background(MAZEZAM_BG_COLOR
);
275 rb
->lcd_set_backdrop(NULL
);
277 rb
->lcd_set_background(MAZEZAM_BG_GRAY
);
278 rb
->lcd_set_backdrop(NULL
);
282 /*****************************************************************************
283 * Parse the level data from the level_data structure. This could be
284 * replaced by a file read. Returns true if the level parsed correctly.
285 ******************************************************************************/
286 static bool parse_level(short level
, struct level_info
* li
)
291 li
->width
= level_data
[level
].width
;
292 li
->height
= level_data
[level
].height
;
293 li
->entrance
= level_data
[level
].entrance
;
294 li
->exit
= level_data
[level
].exit
;
296 /* for each line in the level */
297 for (i
= 0; i
<level_data
[level
].height
; i
++) {
298 if (level_data
[level
].line
[i
] == NULL
)
303 clast
= ' '; /* the character we last considered */
304 while ((c
= level_data
[level
].line
[i
][j
]) != '\0') {
307 li
->cd
.l_num
[i
] += 1;
308 if (li
->cd
.l_num
[i
] > MAZEZAM_MAX_CHUNKS
)
310 li
->cd
.c_inset
[i
][li
->cd
.l_num
[i
] - 1] = j
;
311 li
->cd
.c_width
[i
][li
->cd
.l_num
[i
] - 1] = 1;
314 li
->cd
.c_width
[i
][li
->cd
.l_num
[i
] - 1] += 1;
324 /*****************************************************************************
325 * Draw the walls of a level
326 ******************************************************************************/
327 static void draw_walls(
336 #ifdef HAVE_LCD_COLOR
337 rb
->lcd_set_foreground(MAZEZAM_WALL_COLOR
);
339 rb
->lcd_set_foreground(MAZEZAM_WALL_GRAY
);
341 /* draw the upper wall */
342 rb
->lcd_fillrect(0,0,xOff
,yOff
+(size
*entrance
));
343 rb
->lcd_fillrect(xOff
,0,size
*width
,yOff
);
344 rb
->lcd_fillrect(xOff
+(size
*width
),0,LCD_WIDTH
-xOff
-(size
*width
),
347 /* draw the lower wall */
348 rb
->lcd_fillrect(0,yOff
+(size
*entrance
)+size
,xOff
,
349 LCD_HEIGHT
-yOff
-(size
*entrance
)-size
);
350 rb
->lcd_fillrect(xOff
,yOff
+(size
*height
),size
*width
,
351 LCD_HEIGHT
-yOff
-(size
*height
));
352 /* Note: the exit is made one pixel thinner than necessary as a visual
353 * clue that chunks cannot be pushed into it
355 rb
->lcd_fillrect(xOff
+(size
*width
),yOff
+(size
*exit
)+size
-1,
356 LCD_WIDTH
-xOff
+(size
*width
),
357 LCD_HEIGHT
-yOff
-(size
*exit
)-size
+1);
360 /*****************************************************************************
362 ******************************************************************************/
363 static void draw_row(
368 short i
, /* the row number */
369 struct chunk_data
*cd
, /* the data about the chunks */
370 short *shift
/* an array of the horizontal offset of the lines */
373 /* The assignment below is just a hack to make supress a warning on
374 * non color targets */
376 #ifndef HAVE_LCD_COLOR
377 /* We #def these out to supress a compiler warning */
383 #ifdef HAVE_LCD_COLOR
384 /* adding width to i should have a fixed, but randomising effect on
385 * the choice of the colours of the top line of chunks
387 rb
->lcd_set_foreground(chunk_colors
[(i
+width
) %
388 MAZEZAM_NUM_CHUNK_COLORS
]);
390 for (j
= 0; j
<cd
->l_num
[i
]; j
++) {
391 #ifdef HAVE_LCD_COLOR
392 rb
->lcd_fillrect(xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
],
393 yOff
+size
*i
, cd
->c_width
[i
][j
]*size
,size
);
395 rb
->lcd_set_foreground(MAZEZAM_CHUNK_EDGE_GRAY
);
396 rb
->lcd_drawrect(xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
],
397 yOff
+size
*i
, cd
->c_width
[i
][j
]*size
,size
);
400 rb
->lcd_set_foreground(chunk_gray_shade
[(i
+width
) %
401 MAZEZAM_NUM_CHUNK_GRAYS
]);
402 rb
->lcd_hline(xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
]+1,
403 xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
]+
404 cd
->c_width
[i
][j
]*size
-3,
406 rb
->lcd_vline(xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
]+
407 cd
->c_width
[i
][j
]*size
-2,
412 rb
->lcd_set_foreground(chunk_gray
[(i
+width
) %
413 MAZEZAM_NUM_CHUNK_GRAYS
]);
414 for (k
= yOff
+size
*i
+2; k
< yOff
+size
*i
+size
-2; k
+= 2)
415 rb
->lcd_hline(xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
]+2,
416 xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
]+
417 cd
->c_width
[i
][j
]*size
-3,k
);
419 rb
->lcd_drawrect(xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
],
420 yOff
+size
*i
, cd
->c_width
[i
][j
]*size
,size
);
421 for (k
= xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
]+2;
422 k
< xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
]+
423 cd
->c_width
[i
][j
]*size
;
425 for (l
= yOff
+size
*i
+2; l
< yOff
+size
*i
+size
; l
+= 2 + (i
& 1))
426 rb
->lcd_drawpixel(k
, l
);
431 /*****************************************************************************
433 ******************************************************************************/
434 static void draw_player(
441 /* For drawing the player, taken from the sokoban plugin */
442 short max
= size
- 1;
443 short middle
= max
/ 2;
444 short ldelta
= (middle
+ 1) / 2;
446 /* draw the player (mostly copied from the sokoban plugin) */
447 #ifdef HAVE_LCD_COLOR
448 rb
->lcd_set_foreground(MAZEZAM_PLAYER_COLOR
);
450 rb
->lcd_set_foreground(MAZEZAM_PLAYER_GRAY
);
452 rb
->lcd_hline(xOff
+size
*x
, xOff
+size
*x
+max
, yOff
+size
*y
+middle
);
453 rb
->lcd_vline(xOff
+size
*x
+middle
, yOff
+size
*y
, yOff
+size
*y
+max
-ldelta
);
454 rb
->lcd_drawline(xOff
+size
*x
+middle
, yOff
+size
*y
+max
-ldelta
,
455 xOff
+size
*x
+middle
-ldelta
, yOff
+size
*y
+max
);
456 rb
->lcd_drawline(xOff
+size
*x
+middle
, yOff
+size
*y
+max
-ldelta
,
457 xOff
+size
*x
+middle
+ldelta
, yOff
+size
*y
+max
);
460 /*****************************************************************************
462 ******************************************************************************/
463 static void draw_gate(
469 short third
= size
/ 3;
470 short twothirds
= (2 * size
) / 3;
471 #ifdef HAVE_LCD_COLOR
472 rb
->lcd_set_foreground(MAZEZAM_GATE_COLOR
);
474 rb
->lcd_set_foreground(MAZEZAM_GATE_GRAY
);
476 rb
->lcd_hline(xOff
-size
,xOff
-1,yOff
+entrance
*size
+third
);
477 rb
->lcd_hline(xOff
-size
,xOff
-1,yOff
+entrance
*size
+twothirds
);
478 rb
->lcd_vline(xOff
-size
+third
,yOff
+entrance
*size
,
479 yOff
+entrance
*size
+size
-1);
480 rb
->lcd_vline(xOff
-size
+twothirds
,yOff
+entrance
*size
,
481 yOff
+entrance
*size
+size
-1);
484 /*****************************************************************************
486 ******************************************************************************/
487 static void draw_level(
488 struct level_info
* li
,
489 short *shift
, /* an array of the horizontal offset of the lines */
490 short x
, /* player's x and y coords */
493 /* First we calculate the draw info */
494 /* The number of pixels the side of a square should be */
495 short size
= (LCD_WIDTH
/(li
->width
+2)) < (LCD_HEIGHT
/li
->height
) ?
496 (LCD_WIDTH
/(li
->width
+2)) : (LCD_HEIGHT
/li
->height
);
497 /* The x and y position (in pixels) of the top left corner of the
500 short xOff
= (LCD_WIDTH
- (size
*li
->width
))/2;
501 short yOff
= (LCD_HEIGHT
- (size
*li
->height
))/2;
504 rb
->lcd_clear_display();
506 draw_walls(size
,xOff
,yOff
,li
->width
, li
->height
, li
->entrance
, li
->exit
);
508 /* draw the chunks */
509 for (i
= 0; i
<li
->height
; i
++) {
510 draw_row(size
,xOff
,yOff
,li
->width
,i
,&(li
->cd
),shift
);
513 draw_player(size
,xOff
,yOff
,x
,y
);
515 /* if the player has moved into the level, draw the gate */
517 draw_gate(size
,xOff
,yOff
,li
->entrance
);
520 /*****************************************************************************
521 * Manage the congratulations screen
522 ******************************************************************************/
523 static void welldone_screen(void)
525 int start_selection
= 0;
527 MENUITEM_STRINGLIST(menu
,MAZEZAM_TEXT_WELLDONE_TITLE
,NULL
,
528 MAZEZAM_TEXT_WELLDONE_OPTION
);
530 switch(rb
->do_menu(&menu
, &start_selection
, NULL
, true)){
531 case MENU_ATTACHED_USB
:
532 state
= STATE_USB_CONNECTED
;
537 /*****************************************************************************
538 * Manage the playing of a level
539 ******************************************************************************/
540 static void level_loop(struct level_info
* li
, short* shift
, short *x
, short *y
)
544 bool blocked
; /* is there a chunk in the way of the player? */
546 while (state
>= STATE_IN_LEVEL
) {
547 draw_level(li
, shift
, *x
, *y
);
549 button
= pluginlib_getaction(rb
, TIMEOUT_BLOCK
, plugin_contexts
, 2);
554 case MAZEZAM_UP_REPEAT
:
555 if ((*y
> 0) && (*x
>= 0) && (*x
< li
->width
)) {
556 for (i
= 0; i
< li
->cd
.l_num
[*y
-1]; i
++)
558 ((*x
>=shift
[*y
-1]+li
->cd
.c_inset
[*y
-1][i
]) &&
559 (*x
<shift
[*y
-1]+li
->cd
.c_inset
[*y
-1][i
]+
560 li
->cd
.c_width
[*y
-1][i
]));
561 if (!blocked
) *y
-= 1;
568 case MAZEZAM_DOWN_REPEAT
:
569 if ((*y
< li
->height
-1) && (*x
>= 0) && (*x
< li
->width
)) {
570 for (i
= 0; i
< li
->cd
.l_num
[*y
+1]; i
++)
572 ((*x
>=shift
[*y
+1]+li
->cd
.c_inset
[*y
+1][i
]) &&
573 (*x
<shift
[*y
+1]+li
->cd
.c_inset
[*y
+1][i
]+
574 li
->cd
.c_width
[*y
+1][i
]));
575 if (!blocked
) *y
+= 1;
580 case MAZEZAM_LEFT_REPEAT
:
582 for (i
= 0; i
< li
->cd
.l_num
[*y
]; i
++)
584 (*x
== shift
[*y
]+li
->cd
.c_inset
[*y
][i
]+
585 li
->cd
.c_width
[*y
][i
]);
586 if (!blocked
) *x
-= 1;
587 else if (shift
[*y
] + li
->cd
.c_inset
[*y
][0] > 0) {
595 case MAZEZAM_RIGHT_REPEAT
:
596 if (*x
< li
->width
-1) {
597 for (i
= 0; i
< li
->cd
.l_num
[*y
]; i
++)
599 (*x
+1 == shift
[*y
]+li
->cd
.c_inset
[*y
][i
]);
600 if (!blocked
) *x
+= 1;
602 + li
->cd
.c_inset
[*y
][li
->cd
.l_num
[*y
]-1]
603 + li
->cd
.c_width
[*y
][li
->cd
.l_num
[*y
]-1]
609 else if (*x
== li
->width
) state
= STATE_COMPLETED
;
610 else if (*y
== li
->exit
) *x
+= 1;
614 state
= STATE_GAME_MENU
;
618 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
619 state
= STATE_USB_CONNECTED
;
625 /*****************************************************************************
626 * Manage the in game menu
627 ******************************************************************************/
628 static void in_game_menu(void)
630 /* The initial option is retry level */
631 int start_selection
= 1;
633 MENUITEM_STRINGLIST(menu
,MAZEZAM_TEXT_MAZEZAM_MENU
, NULL
,
635 MAZEZAM_TEXT_RETRY_LEVEL
,
636 MAZEZAM_TEXT_AUDIO_PLAYBACK
,
639 /* Don't show the status bar */
640 switch(rb
->do_menu(&menu
, &start_selection
, NULL
, false)){
642 state
= STATE_FAILED
;
645 case 2: /* Audio playback */
646 playback_control(rb
, NULL
);
647 state
= STATE_IN_LEVEL
;
654 case MENU_ATTACHED_USB
:
655 state
= STATE_USB_CONNECTED
;
659 state
= STATE_IN_LEVEL
;
664 /*****************************************************************************
665 * Is the level a checkpoint
666 ******************************************************************************/
667 static bool at_checkpoint(int level
)
669 if (level
<= MAZEZAM_FIRST_CHECKPOINT
)
670 return level
== MAZEZAM_FIRST_CHECKPOINT
;
672 level
= level
- MAZEZAM_FIRST_CHECKPOINT
;
673 return level
% MAZEZAM_CHECKPOINT_INTERVAL
== 0;
677 /*****************************************************************************
678 * Set up and play a level
679 * new_level should be true if this is the first time we've encountered
681 ******************************************************************************/
682 static void play_level(short level
, short lives
, bool new_level
)
684 struct level_info li
;
685 short shift
[MAZEZAM_MAX_LINES
]; /* amount each line has been shifted */
689 state
= STATE_IN_LEVEL
;
691 if (!(parse_level(level
,&li
)))
692 state
= STATE_PARSE_ERROR
;
694 for (i
= 0; i
< li
.height
; i
++)
700 plugin_lcd_settings();
701 rb
->lcd_clear_display();
703 draw_level(&li
, shift
, x
, y
);
705 /* If we've just reached a checkpoint, then alert the player */
706 if (new_level
&& at_checkpoint(level
)) {
707 rb
->splash(MAZEZAM_DELAY_CHECKPOINT
, MAZEZAM_TEXT_CHECKPOINT
);
708 /* Clear the splash */
709 draw_level(&li
, shift
, x
, y
);
712 #ifdef HAVE_REMOTE_LCD
713 /* Splash text seems to use the remote display by
714 * default. I suppose I better keep it tidy!
716 rb
->lcd_remote_clear_display();
718 rb
->splash(MAZEZAM_DELAY_LIVES
, MAZEZAM_TEXT_LIVES
,
721 /* ensure keys pressed during the splash screen are ignored */
722 rb
->button_clear_queue();
724 /* this little loop just ensures we return to the game if the player
725 * doesn't perform an interesting action during the in game menu */
726 while (state
>= STATE_IN_LEVEL
) {
727 level_loop(&li
, shift
, &x
, &y
);
729 if (state
== STATE_GAME_MENU
) {
730 restore_lcd_settings();
732 plugin_lcd_settings();
735 restore_lcd_settings();
738 /*****************************************************************************
739 * Update the resume data based on the level reached
740 ******************************************************************************/
741 static void update_resume_data(struct resume_data
*r
, int level
)
743 if (at_checkpoint(level
))
747 /*****************************************************************************
748 * The loop which manages a full game of MazezaM.
749 ******************************************************************************/
750 static void game_loop(struct resume_data
*r
)
752 int level
= r
->level
;
753 int lives
= MAZEZAM_START_LIVES
;
754 /* We want to know when a player reaches a level for the first time,
755 * so we keep a second copy of the level. */
756 int old_level
= level
;
758 state
= STATE_IN_GAME
;
760 while (state
>= STATE_IN_GAME
)
762 play_level(level
, lives
, old_level
< level
);
766 case STATE_COMPLETED
:
768 if (level
== MAZEZAM_NUM_LEVELS
)
769 state
= STATE_WELLDONE
;
775 state
= STATE_GAME_OVER
;
782 update_resume_data(r
,level
);
786 case STATE_GAME_OVER
:
787 #ifdef HAVE_REMOTE_LCD
788 /* Splash text seems to use the remote display by
789 * default. I suppose I better keep it tidy!
791 rb
->lcd_remote_clear_display();
793 rb
->splash(MAZEZAM_DELAY_GAME_OVER
, MAZEZAM_TEXT_GAME_OVER
);
805 /*****************************************************************************
806 * Load the resume data from the config file. The data is
807 * stored in both r and old.
808 ******************************************************************************/
809 static void resume_load_data (struct resume_data
*r
, struct resume_data
*old
)
811 struct configdata config
[] = {
812 {TYPE_INT
,0,MAZEZAM_NUM_LEVELS
-1,&(r
->level
),
813 MAZEZAM_CONFIG_LEVELS_NAME
,NULL
,NULL
}
816 if (configfile_load(MAZEZAM_CONFIG_FILENAME
,config
,
817 MAZEZAM_CONFIG_NUM_ITEMS
, MAZEZAM_CONFIG_VERSION
) < 0)
819 /* an extra precaution */
820 else if ((r
->level
< 0) || (MAZEZAM_NUM_LEVELS
<= r
->level
))
823 old
->level
= r
->level
;
826 /*****************************************************************************
827 * Save the resume data in the config file, but only if necessary
828 ******************************************************************************/
829 static void resume_save_data (struct resume_data
*r
, struct resume_data
*old
)
831 struct configdata config
[] = {
832 {TYPE_INT
,0,MAZEZAM_NUM_LEVELS
-1,&(r
->level
),
833 MAZEZAM_CONFIG_LEVELS_NAME
,NULL
,NULL
}
836 /* To reduce disk usage, only write the file if the resume data has
839 if (old
->level
!= r
->level
)
840 configfile_save(MAZEZAM_CONFIG_FILENAME
,config
,
841 MAZEZAM_CONFIG_NUM_ITEMS
, MAZEZAM_CONFIG_MINVERSION
);
844 /*****************************************************************************
845 * Offer a main menu with no continue option
846 ******************************************************************************/
847 static int main_menu_without_continue(int* start_selection
)
849 MENUITEM_STRINGLIST(menu
,MAZEZAM_TEXT_MAIN_MENU
,NULL
,
850 MAZEZAM_TEXT_PLAY_GAME
,
852 return rb
->do_menu(&menu
, start_selection
, NULL
, false);
855 /*****************************************************************************
856 * Offer a main menu with a continue option
857 ******************************************************************************/
858 static int main_menu_with_continue(int* start_selection
)
860 MENUITEM_STRINGLIST(menu
,MAZEZAM_TEXT_MAIN_MENU
,NULL
,
861 MAZEZAM_TEXT_CONTINUE
,
862 MAZEZAM_TEXT_PLAY_NEW_GAME
,
864 return rb
->do_menu(&menu
, start_selection
, NULL
, false);
867 /*****************************************************************************
868 * Manages the main menu
869 ******************************************************************************/
870 static void main_menu(void)
872 /* The initial option is "play game" */
873 int start_selection
= 0;
875 struct resume_data r_data
, old_data
;
878 resume_load_data(&r_data
, &old_data
);
880 while (state
>= STATE_IN_APPLICATION
) {
881 if (r_data
.level
== 0)
882 choice
= main_menu_without_continue(&start_selection
);
884 choice
= main_menu_with_continue(&start_selection
);
887 case 0: /* Continue */
888 state
= STATE_IN_GAME
;
892 case 1: /* Quit or Play new game */
893 if (r_data
.level
== 0)
895 else { /* Play new game */
897 state
= STATE_IN_GAME
;
902 case MENU_ATTACHED_USB
:
903 state
= STATE_USB_CONNECTED
;
912 /* I'm not sure if it's appropriate to write to disk on USB events.
913 * Currently, I do so.
915 resume_save_data(&r_data
, &old_data
);
918 /*****************************************************************************
920 ******************************************************************************/
921 enum plugin_status
plugin_start(const struct plugin_api
* api
, const void* parameter
)
923 enum plugin_status plugin_state
;
925 /* Usual plugin stuff */
930 /* initialise the config file module */
933 store_lcd_settings();
935 state
= STATE_MAIN_MENU
;
939 case STATE_USB_CONNECTED
:
940 plugin_state
= PLUGIN_USB_CONNECTED
;
943 case STATE_PARSE_ERROR
:
944 plugin_state
= PLUGIN_ERROR
;
948 plugin_state
= PLUGIN_OK
;