1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
22 #include "configfile.h"
25 /* Include standard plugin macro */
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 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
145 #define MAZEZAM_UP BUTTON_UP
146 #define MAZEZAM_DOWN BUTTON_DOWN
147 #define MAZEZAM_LEFT BUTTON_LEFT
148 #define MAZEZAM_RIGHT BUTTON_RIGHT
149 #define MAZEZAM_SELECT BUTTON_SELECT
151 #define MAZEZAM_RETRY BUTTON_PLAY
152 #define MAZEZAM_RETRY_KEYNAME "[PLAY]"
153 #define MAZEZAM_QUIT BUTTON_BACK
154 #define MAZEZAM_QUIT_KEYNAME "[BACK]"
156 #elif (CONFIG_KEYPAD == MROBE100_PAD)
157 #define MAZEZAM_UP BUTTON_UP
158 #define MAZEZAM_DOWN BUTTON_DOWN
159 #define MAZEZAM_LEFT BUTTON_LEFT
160 #define MAZEZAM_RIGHT BUTTON_RIGHT
161 #define MAZEZAM_SELECT BUTTON_SELECT
163 #define MAZEZAM_RETRY BUTTON_DISPLAY
164 #define MAZEZAM_RETRY_KEYNAME "[DISPLAY]"
165 #define MAZEZAM_QUIT BUTTON_POWER
166 #define MAZEZAM_QUIT_KEYNAME "[POWER]"
168 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
169 #define MAZEZAM_UP BUTTON_RC_VOL_UP
170 #define MAZEZAM_DOWN BUTTON_RC_VOL_DOWN
171 #define MAZEZAM_LEFT BUTTON_RC_REW
172 #define MAZEZAM_RIGHT BUTTON_RC_FF
173 #define MAZEZAM_SELECT BUTTON_RC_PLAY
175 #define MAZEZAM_RETRY BUTTON_RC_MODE
176 #define MAZEZAM_RETRY_KEYNAME "[MODE]"
177 #define MAZEZAM_QUIT BUTTON_RC_REC
178 #define MAZEZAM_QUIT_KEYNAME "[REC]"
180 #elif (CONFIG_KEYPAD == COWOND2_PAD)
181 #define MAZEZAM_UP BUTTON_UP
182 #define MAZEZAM_DOWN BUTTON_DOWN
183 #define MAZEZAM_LEFT BUTTON_LEFT
184 #define MAZEZAM_RIGHT BUTTON_RIGHT
185 #define MAZEZAM_SELECT BUTTON_SELECT
187 #define MAZEZAM_RETRY BUTTON_SELECT
188 #define MAZEZAM_RETRY_KEYNAME "[PLAY]"
189 #define MAZEZAM_QUIT BUTTON_POWER
190 #define MAZEZAM_QUIT_KEYNAME "[POWER]"
193 #error No keymap defined!
196 /* The gap for the border around the heading in text pages. In fact, 2 is
197 * really the only acceptable value.
199 #define MAZEZAM_MENU_BORDER 2
200 #define MAZEZAM_EXTRA_LIFE 2 /* get an extra life every _ levels */
201 #define MAZEZAM_START_LIVES 3 /* how many lives at game start */
203 #ifdef HAVE_LCD_COLOR
204 #define MAZEZAM_HEADING_COLOR LCD_RGBPACK(255,255, 0) /* Yellow */
205 #define MAZEZAM_BORDER_COLOR LCD_RGBPACK( 0, 0,255) /* Blue */
206 #define MAZEZAM_TEXT_COLOR LCD_RGBPACK(255,255,255) /* White */
207 #define MAZEZAM_BG_COLOR LCD_RGBPACK( 0, 0, 0) /* Black */
208 #define MAZEZAM_WALL_COLOR LCD_RGBPACK(100,100,100) /* Dark gray */
209 #define MAZEZAM_PLAYER_COLOR LCD_RGBPACK(255,255,255) /* White */
210 #define MAZEZAM_GATE_COLOR LCD_RGBPACK(100,100,100) /* Dark gray */
212 /* the rows are coloured sequentially */
213 #define MAZEZAM_NUM_CHUNK_COLORS 8
214 static const unsigned chunk_colors
[MAZEZAM_NUM_CHUNK_COLORS
] = {
215 LCD_RGBPACK(255,192, 32), /* Orange */
216 LCD_RGBPACK(255, 0, 0), /* Red */
217 LCD_RGBPACK( 0,255, 0), /* Green */
218 LCD_RGBPACK( 0,255,255), /* Cyan */
219 LCD_RGBPACK(255,175,175), /* Pink */
220 LCD_RGBPACK(255,255, 0), /* Yellow */
221 LCD_RGBPACK( 0, 0,255), /* Blue */
222 LCD_RGBPACK(255, 0,255), /* Magenta */
227 #define MAZEZAM_HEADING_GRAY LCD_BLACK
228 #define MAZEZAM_BORDER_GRAY LCD_DARKGRAY
229 #define MAZEZAM_TEXT_GRAY LCD_BLACK
230 #define MAZEZAM_BG_GRAY LCD_WHITE
231 #define MAZEZAM_WALL_GRAY LCD_DARKGRAY
232 #define MAZEZAM_PLAYER_GRAY LCD_BLACK
233 #define MAZEZAM_GATE_GRAY LCD_BLACK
234 #define MAZEZAM_CHUNK_EDGE_GRAY LCD_BLACK
236 #define MAZEZAM_NUM_CHUNK_GRAYS 2
237 static const unsigned chunk_gray
[MAZEZAM_NUM_CHUNK_GRAYS
] = {
241 /* darker version of the above */
242 static const unsigned chunk_gray_shade
[MAZEZAM_NUM_CHUNK_GRAYS
] = {
248 #define MAZEZAM_GAMEOVER_TEXT "Game Over"
249 #define MAZEZAM_GAMEOVER_DELAY (3 * HZ) / 2
250 #define MAZEZAM_LEVEL_LIVES_TEXT "Level %d, Lives %d"
251 #define MAZEZAM_LEVEL_LIVES_DELAY HZ
252 #define MAZEZAM_WELLDONE_DELAY 4 * HZ
254 /* The maximum number of lines that a text page can display.
255 * This must be 4 or less if the Archos recorder is to be
258 #define MAZEZAM_TEXT_MAXLINES 4
260 /* A structure for holding text pages */
262 /* Ensure 1 < num_lines <= MAZEZAM_TEXT_MAXLINES */
264 char *line
[MAZEZAM_TEXT_MAXLINES
]; /* text of lines */
267 /* The text page for the welcome screen */
268 static const struct textpage title_page
= {
270 {"MazezaM", "play game", "instructions", "quit"}
273 /* The number of help screens */
274 #define MAZEZAM_NUM_HELP_PAGES 4
276 /* The instruction screens */
277 static const struct textpage help_page
[] = {
278 {4,{"Instructions","10 mazezams","bar your way","to freedom"}},
279 {4,{"Instructions","Push the rows","left and right","to escape"}},
280 {4,{"Instructions","Press " MAZEZAM_RETRY_KEYNAME
" to","retry a level",
282 {4,{"Instructions","Press " MAZEZAM_QUIT_KEYNAME
,"to quit","the game"}}
285 /* the text of the screen that asks for a quit confirmation */
286 static const struct textpage confirm_page
= {
288 {"Quit","Are you sure?","yes","no"}
291 /* the text of the screen at the end of the game */
292 static const struct textpage welldone_page
= {
294 {"Well Done","You have","escaped",""}
297 /* the text of the screen asking if the user wants to
298 * resume or start a new game.
300 static const struct textpage resume_page
= {
302 {"Checkpoint", "continue", "new game"}
305 /* maximum height of a level */
306 #define MAZEZAM_MAX_LINES 11
307 /* maximum number of chunks on a line */
308 #define MAZEZAM_MAX_CHUNKS 5
310 /* A structure for holding levels */
311 struct mazezam_level
{
312 short height
; /* the number of lines */
313 short width
; /* the width */
314 short entrance
; /* the line on which the entrance lies */
315 short exit
; /* the line on which the exit lies */
316 char *line
[MAZEZAM_MAX_LINES
]; /* the chunk data in string form */
319 /* The number of levels. Note that the instruction screens reference this
322 #define MAZEZAM_NUM_LEVELS 10
324 /* The levels. In theory, they could be stored in a file so this data
325 * structure should not be accessed outside parse_level()
327 * These levels are copyright (C) 2002 Malcolm Tyrrell. They're
328 * probably covered by the GPL as they constitute part of the source
329 * code of this plugin, but you may distibute them seperately with
330 * other Free Software if you want. You can download them from:
331 * http://webpages.dcu.ie/~tyrrelma/MazezaM.
333 static const struct mazezam_level level_data
[MAZEZAM_NUM_LEVELS
] = {
334 {2,7,0,0,{" $ $"," $ $$",NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,
336 {3,8,2,1,{" $ $$$"," $ $ $"," $ $ $",NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,
338 {4,14,1,3,{" $$$$$ $$ $$"," $$ $$ $$","$$ $ $$ $$$",
339 " $$$$$$$$ $",NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
}},
340 {6,7,4,2,{" $"," $$$$"," $$$ $$"," $ $ $"," $ $$","$ $$",
341 NULL
,NULL
,NULL
,NULL
,NULL
}},
342 {6,13,0,0,{" $$$$$","$ $$$$$ $$$"," $ $$$ $$$$",
343 "$ $ $$$$$$$"," $$$ $ $$","$ $ $ $$ $",NULL
,NULL
,
345 {11,5,10,0,{" $"," $ $$"," $$","$ $"," $ $"," $$$","$ $",
346 " $ $"," $ $","$ $$"," $"}},
347 {7,16,0,6,{" $$$$$$$"," $$$$ $$$$ $ $","$$ $$ $$$$$$ $ $",
348 "$ $ $"," $$$$$$$$$$$$$$"," $ $$ $ $$$",
349 " $ $$$ $$",NULL
,NULL
,NULL
,NULL
}},
350 {4,15,2,0,{" $$$$ $$$$ $$"," $ $$ $$ $ $$"," $ $$ $$$$ $$",
351 " $ $$ $$$$ $",NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
}},
352 {7,9,6,2,{" $ $$$$"," $ $ $$"," $ $$$$ $","$ $$ $"," $ $$$",
353 " $$$$$$"," $",NULL
,NULL
,NULL
,NULL
}},
354 {10,14,8,0,{" $"," $$$$$$$$$$ $"," $$$ $$",
355 " $ $$$$$$$$ $"," $$$ $$$ $$$"," $$$ $ $$$",
356 " $ $$$$$$$ $$"," $ $ $ $$$"," $$$$$$$$$$$$",
360 /* This is the data structure the game uses for managing levels */
362 /* the number of chunks on a line */
363 short l_num
[MAZEZAM_MAX_LINES
];
364 /* the width of a chunk */
365 short c_width
[MAZEZAM_MAX_LINES
][MAZEZAM_MAX_CHUNKS
];
366 /* the inset of a chunk */
367 short c_inset
[MAZEZAM_MAX_LINES
][MAZEZAM_MAX_CHUNKS
];
370 /* The state and exit code of the level loop */
373 LEVEL_STATE_COMPLETED
,
376 LEVEL_STATE_PARSE_ERROR
,
377 LEVEL_STATE_USB_CONNECTED
,
380 /* The state and exit code of the text screens. I use the
381 * same enum for all of them, even though there are some
388 TEXT_STATE_USB_CONNECTED
,
389 TEXT_STATE_PARSE_ERROR
,
393 /* The state and exit code of the game loop */
398 GAME_STATE_USB_CONNECTED
,
400 GAME_STATE_COMPLETED
,
401 GAME_STATE_PARSE_ERROR
,
404 /* The various constants needed for configuration files.
405 * See apps/plugins/lib/configfile.*
407 #define MAZEZAM_CONFIG_FILENAME "mazezam.data"
408 #define MAZEZAM_CONFIG_NUM_ITEMS 1
409 #define MAZEZAM_CONFIG_VERSION 0
410 #define MAZEZAM_CONFIG_MINVERSION 0
411 #define MAZEZAM_CONFIG_LEVELS_NAME "restart_level"
413 /* A structure containing the data that is written to
414 * the configuration file
417 int level
; /* level at which to restart the game */
420 /* Display a screen of text. line[0] is the heading.
421 * line[highlight] will be highlighted, unless highlight == 0
423 static void display_text_page(struct textpage text
, int highlight
)
425 int w
[text
.num_lines
], h
[text
.num_lines
];
426 int hsum
,i
,vgap
,vnext
;
428 rb
->lcd_clear_display();
430 /* find out how big the text is so we can determine the positioning */
432 for(i
= 0; i
< text
.num_lines
; i
++) {
433 rb
->lcd_getstringsize(text
.line
[i
], w
+i
, h
+i
);
437 vgap
= (LCD_HEIGHT
-hsum
)/(text
.num_lines
+1);
441 #ifdef HAVE_LCD_COLOR
442 rb
->lcd_set_foreground(MAZEZAM_BORDER_COLOR
);
444 rb
->lcd_set_foreground(MAZEZAM_BORDER_GRAY
);
446 rb
->lcd_drawrect((LCD_WIDTH
-w
[0])/2-MAZEZAM_MENU_BORDER
,
447 vgap
-MAZEZAM_MENU_BORDER
, w
[0] + 2*MAZEZAM_MENU_BORDER
,
448 h
[0] + 2*MAZEZAM_MENU_BORDER
);
449 rb
->lcd_drawrect((LCD_WIDTH
-w
[0])/2-MAZEZAM_MENU_BORDER
*2,
450 vgap
-MAZEZAM_MENU_BORDER
*2, w
[0] + 4*MAZEZAM_MENU_BORDER
,
451 h
[0] + 4*MAZEZAM_MENU_BORDER
);
452 rb
->lcd_drawline(0,vgap
+ h
[0]/2,(LCD_WIDTH
-w
[0])/2-MAZEZAM_MENU_BORDER
*2,
454 rb
->lcd_drawline((LCD_WIDTH
-w
[0])/2+w
[0]+MAZEZAM_MENU_BORDER
*2,
455 vgap
+ h
[0]/2,LCD_WIDTH
-1,vgap
+ h
[0]/2);
456 #ifdef HAVE_LCD_COLOR
457 rb
->lcd_set_foreground(MAZEZAM_HEADING_COLOR
);
459 rb
->lcd_set_foreground(MAZEZAM_HEADING_GRAY
);
461 rb
->lcd_putsxy((LCD_WIDTH
-w
[0])/2,vgap
,text
.line
[0]);
463 vnext
= vgap
*2 + h
[0];
465 /* The other lines */
467 #ifdef HAVE_LCD_COLOR
468 rb
->lcd_set_foreground(MAZEZAM_TEXT_COLOR
);
470 rb
->lcd_set_foreground(MAZEZAM_TEXT_GRAY
);
472 for (i
= 1; i
<text
.num_lines
; i
++) {
473 rb
->lcd_putsxy((LCD_WIDTH
-w
[i
])/2,vnext
,text
.line
[i
]);
475 /* add underlining if i is the highlighted line */
476 if (i
== highlight
) {
477 rb
->lcd_drawline((LCD_WIDTH
-w
[i
])/2, vnext
+ h
[i
] + 1,
478 (LCD_WIDTH
-w
[i
])/2 + w
[i
], vnext
+ h
[i
] + 1);
481 vnext
+= vgap
+ h
[i
];
488 /* Parse the level data from the level_data structure. This could be
489 * replaced by a file read. Returns true if the level parsed correctly.
491 static bool parse_level(short level
, struct chunk_data
*cd
,
492 short *width
, short *height
, short *entrance
, short *exit
)
497 *width
= level_data
[level
].width
;
498 *height
= level_data
[level
].height
;
499 *entrance
= level_data
[level
].entrance
;
500 *exit
= level_data
[level
].exit
;
502 /* for each line in the level */
503 for (i
= 0; i
<level_data
[level
].height
; i
++) {
504 if (level_data
[level
].line
[i
] == NULL
)
509 clast
= ' '; /* the character we last considered */
510 while ((c
= level_data
[level
].line
[i
][j
]) != '\0') {
514 if (cd
->l_num
[i
] > MAZEZAM_MAX_CHUNKS
)
516 cd
->c_inset
[i
][cd
->l_num
[i
] - 1] = j
;
517 cd
->c_width
[i
][cd
->l_num
[i
] - 1] = 1;
520 cd
->c_width
[i
][cd
->l_num
[i
] - 1] += 1;
531 static void draw_level(
532 struct chunk_data
*cd
, /* the data about the chunks */
533 short *shift
, /* an array of the horizontal offset of the lines */
538 short x
, /* player's x and y coords */
541 /* The number of pixels the side of a square should be */
542 short size
= (LCD_WIDTH
/(width
+2)) < (LCD_HEIGHT
/height
) ?
543 (LCD_WIDTH
/(width
+2)) : (LCD_HEIGHT
/height
);
544 /* The x and y position (in pixels) of the top left corner of the
547 short xOff
= (LCD_WIDTH
- (size
*width
))/2;
548 short yOff
= (LCD_HEIGHT
- (size
*height
))/2;
549 /* For drawing the player, taken from the sokoban plugin */
550 short max
= size
- 1;
551 short middle
= max
/ 2;
552 short ldelta
= (middle
+ 1) / 2;
554 short third
= size
/ 3;
555 short twothirds
= (2 * size
) / 3;
556 #ifndef HAVE_LCD_COLOR
557 /* We #def these out to supress a compiler warning */
564 rb
->lcd_clear_display();
566 #ifdef HAVE_LCD_COLOR
567 rb
->lcd_set_foreground(MAZEZAM_WALL_COLOR
);
569 rb
->lcd_set_foreground(MAZEZAM_WALL_GRAY
);
571 /* draw the upper wall */
572 rb
->lcd_fillrect(0,0,xOff
,yOff
+(size
*entrance
));
573 rb
->lcd_fillrect(xOff
,0,size
*width
,yOff
);
574 rb
->lcd_fillrect(xOff
+(size
*width
),0,LCD_WIDTH
-xOff
-(size
*width
),
577 /* draw the lower wall */
578 rb
->lcd_fillrect(0,yOff
+(size
*entrance
)+size
,xOff
,
579 LCD_HEIGHT
-yOff
-(size
*entrance
)-size
);
580 rb
->lcd_fillrect(xOff
,yOff
+(size
*height
),size
*width
,
581 LCD_HEIGHT
-yOff
-(size
*height
));
582 /* Note: the exit is made one pixel thinner than necessary as a visual
583 * clue that chunks cannot be pushed into it
585 rb
->lcd_fillrect(xOff
+(size
*width
),yOff
+(size
*exit
)+size
-1,
586 LCD_WIDTH
-xOff
+(size
*width
),
587 LCD_HEIGHT
-yOff
-(size
*exit
)-size
+1);
589 /* draw the chunks */
590 for (i
= 0; i
<height
; i
++) {
591 #ifdef HAVE_LCD_COLOR
592 /* adding width to i should have a fixed, but randomising effect on
593 * the choice of the colours of the top line of chunks
595 rb
->lcd_set_foreground(chunk_colors
[(i
+width
) %
596 MAZEZAM_NUM_CHUNK_COLORS
]);
598 for (j
= 0; j
<cd
->l_num
[i
]; j
++) {
599 #ifdef HAVE_LCD_COLOR
600 rb
->lcd_fillrect(xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
],
601 yOff
+size
*i
, cd
->c_width
[i
][j
]*size
,size
);
603 rb
->lcd_set_foreground(MAZEZAM_CHUNK_EDGE_GRAY
);
604 rb
->lcd_drawrect(xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
],
605 yOff
+size
*i
, cd
->c_width
[i
][j
]*size
,size
);
608 rb
->lcd_set_foreground(chunk_gray_shade
[(i
+width
) %
609 MAZEZAM_NUM_CHUNK_GRAYS
]);
610 rb
->lcd_drawline(xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
]+1,
612 xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
]+
613 cd
->c_width
[i
][j
]*size
-3,
615 rb
->lcd_drawline(xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
]+
616 cd
->c_width
[i
][j
]*size
-2,
618 xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
]+
619 cd
->c_width
[i
][j
]*size
-2,
623 rb
->lcd_set_foreground(chunk_gray
[(i
+width
) %
624 MAZEZAM_NUM_CHUNK_GRAYS
]);
625 for (k
= yOff
+size
*i
+2; k
< yOff
+size
*i
+size
-2; k
+= 2)
626 rb
->lcd_drawline(xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
]+2,k
,
627 xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
]+
628 cd
->c_width
[i
][j
]*size
-3,k
);
630 rb
->lcd_drawrect(xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
],
631 yOff
+size
*i
, cd
->c_width
[i
][j
]*size
,size
);
632 for (k
= xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
]+2;
633 k
< xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
]+
634 cd
->c_width
[i
][j
]*size
;
636 for (l
= yOff
+size
*i
+2; l
< yOff
+size
*i
+size
; l
+= 2 + (i
& 1))
637 rb
->lcd_drawpixel(k
, l
);
642 /* draw the player (mostly copied from the sokoban plugin) */
643 #ifdef HAVE_LCD_COLOR
644 rb
->lcd_set_foreground(MAZEZAM_PLAYER_COLOR
);
646 rb
->lcd_set_foreground(MAZEZAM_PLAYER_GRAY
);
648 rb
->lcd_drawline(xOff
+size
*x
, yOff
+size
*y
+middle
,
649 xOff
+size
*x
+max
, yOff
+size
*y
+middle
);
650 rb
->lcd_drawline(xOff
+size
*x
+middle
, yOff
+size
*y
,
651 xOff
+size
*x
+middle
, yOff
+size
*y
+max
-ldelta
);
652 rb
->lcd_drawline(xOff
+size
*x
+middle
, yOff
+size
*y
+max
-ldelta
,
653 xOff
+size
*x
+middle
-ldelta
, yOff
+size
*y
+max
);
654 rb
->lcd_drawline(xOff
+size
*x
+middle
, yOff
+size
*y
+max
-ldelta
,
655 xOff
+size
*x
+middle
+ldelta
, yOff
+size
*y
+max
);
657 /* draw the gate, if the player has moved into the level */
659 #ifdef HAVE_LCD_COLOR
660 rb
->lcd_set_foreground(MAZEZAM_GATE_COLOR
);
662 rb
->lcd_set_foreground(MAZEZAM_GATE_GRAY
);
664 rb
->lcd_drawline(xOff
-size
,yOff
+entrance
*size
+third
,
665 xOff
-1,yOff
+entrance
*size
+third
);
666 rb
->lcd_drawline(xOff
-size
,yOff
+entrance
*size
+twothirds
,
667 xOff
-1,yOff
+entrance
*size
+twothirds
);
668 rb
->lcd_drawline(xOff
-size
+third
,yOff
+entrance
*size
,
669 xOff
-size
+third
,yOff
+entrance
*size
+size
-1);
670 rb
->lcd_drawline(xOff
-size
+twothirds
,yOff
+entrance
*size
,
671 xOff
-size
+twothirds
,yOff
+entrance
*size
+size
-1);
675 /* Manage the congratulations screen */
676 static enum text_state
welldone_screen(void)
678 int button
= BUTTON_NONE
;
679 enum text_state state
= TEXT_STATE_LOOPING
;
681 display_text_page(welldone_page
, 0);
683 while (state
== TEXT_STATE_LOOPING
) {
684 button
= rb
->button_get(true);
688 state
= TEXT_STATE_QUIT
;
692 #if CONFIG_KEYPAD != ONDIO_PAD
695 state
= TEXT_STATE_OKAY
;
699 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
700 state
= TEXT_STATE_USB_CONNECTED
;
708 /* Manage the quit confimation screen */
709 static enum text_state
quitconfirm_loop(void)
711 int button
= BUTTON_NONE
;
712 enum text_state state
= TEXT_STATE_LOOPING
;
715 display_text_page(confirm_page
, select
+ 1);
717 /* Wait for a button release. This is useful when a repeated button
718 * press is used for quit.
720 while ((rb
->button_get(true) & BUTTON_REL
) != BUTTON_REL
);
722 while (state
== TEXT_STATE_LOOPING
) {
723 display_text_page(confirm_page
, select
+ 1);
725 button
= rb
->button_get(true);
729 state
= TEXT_STATE_QUIT
;
734 select
= (2 - select
) + 1;
738 #if CONFIG_KEYPAD != ONDIO_PAD
742 state
= TEXT_STATE_QUIT
;
744 state
= TEXT_STATE_OKAY
;
748 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
749 state
= TEXT_STATE_USB_CONNECTED
;
757 /* Manage the playing of a level */
758 static enum level_state
level_loop(short level
, short lives
)
760 struct chunk_data cd
;
761 short shift
[MAZEZAM_MAX_LINES
]; /* amount each line has been shifted */
769 enum level_state state
= LEVEL_STATE_LOOPING
;
770 bool blocked
; /* is there a chunk in the way of the player? */
772 if (!(parse_level(level
,&cd
,&width
,&height
,&entrance
,&exit
)))
773 return LEVEL_STATE_PARSE_ERROR
;
775 for (i
= 0; i
< height
; i
++)
781 draw_level(&cd
, shift
, width
, height
, entrance
, exit
, x
, y
);
783 #ifdef HAVE_REMOTE_LCD
784 /* Splash text seems to use the remote display by
785 * default. I suppose I better keep it tidy!
787 rb
->lcd_remote_clear_display();
789 rb
->splash(MAZEZAM_LEVEL_LIVES_DELAY
, MAZEZAM_LEVEL_LIVES_TEXT
,
792 /* ensure keys pressed during the splash screen are ignored */
793 rb
->button_clear_queue();
795 while (state
== LEVEL_STATE_LOOPING
) {
796 draw_level(&cd
, shift
, width
, height
, entrance
, exit
, x
, y
);
798 button
= rb
->button_get(true);
803 case MAZEZAM_UP
| BUTTON_REPEAT
:
804 if ((y
> 0) && (x
>= 0) && (x
< width
)) {
805 for (i
= 0; i
< cd
.l_num
[y
-1]; i
++)
807 ((x
>=shift
[y
-1]+cd
.c_inset
[y
-1][i
]) &&
808 (x
<shift
[y
-1]+cd
.c_inset
[y
-1][i
]+
809 cd
.c_width
[y
-1][i
]));
810 if (!blocked
) y
-= 1;
815 case MAZEZAM_DOWN
| BUTTON_REPEAT
:
816 if ((y
< height
-1) && (x
>= 0) && (x
< width
)) {
817 for (i
= 0; i
< cd
.l_num
[y
+1]; i
++)
819 ((x
>=shift
[y
+1]+cd
.c_inset
[y
+1][i
]) &&
820 (x
<shift
[y
+1]+cd
.c_inset
[y
+1][i
]+
821 cd
.c_width
[y
+1][i
]));
822 if (!blocked
) y
+= 1;
827 case MAZEZAM_LEFT
| BUTTON_REPEAT
:
829 for (i
= 0; i
< cd
.l_num
[y
]; i
++)
831 (x
== shift
[y
]+cd
.c_inset
[y
][i
]+
833 if (!blocked
) x
-= 1;
834 else if (shift
[y
] + cd
.c_inset
[y
][0] > 0) {
842 case MAZEZAM_RIGHT
| BUTTON_REPEAT
:
844 for (i
= 0; i
< cd
.l_num
[y
]; i
++)
845 blocked
= blocked
|| (x
+1 == shift
[y
]+cd
.c_inset
[y
][i
]);
846 if (!blocked
) x
+= 1;
847 else if (shift
[y
] + cd
.c_inset
[y
][cd
.l_num
[y
]-1] +
848 cd
.c_width
[y
][cd
.l_num
[y
]-1] < width
) {
853 else if (x
== width
) state
= LEVEL_STATE_COMPLETED
;
854 else if (y
== exit
) x
+= 1;
858 state
= LEVEL_STATE_FAILED
;
862 switch (quitconfirm_loop()) {
863 case TEXT_STATE_QUIT
:
864 state
= LEVEL_STATE_QUIT
;
867 case TEXT_STATE_USB_CONNECTED
:
868 state
= LEVEL_STATE_USB_CONNECTED
;
877 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
878 state
= LEVEL_STATE_USB_CONNECTED
;
886 /* The loop which manages a full game of MazezaM */
887 static enum game_state
game_loop(struct resume_data
*r
)
889 enum game_state state
= GAME_STATE_LOOPING
;
890 int level
= r
->level
;
891 int lives
= MAZEZAM_START_LIVES
;
893 rb
->lcd_clear_display();
895 while (state
== GAME_STATE_LOOPING
)
897 switch (level_loop(level
,lives
)) {
898 case LEVEL_STATE_COMPLETED
:
900 if (!((level
- r
->level
) % MAZEZAM_EXTRA_LIFE
))
904 case LEVEL_STATE_QUIT
:
905 state
= GAME_STATE_QUIT
;
908 case LEVEL_STATE_FAILED
:
912 case LEVEL_STATE_PARSE_ERROR
:
913 state
= GAME_STATE_PARSE_ERROR
;
916 case LEVEL_STATE_USB_CONNECTED
:
917 state
= GAME_STATE_USB_CONNECTED
;
924 state
= GAME_STATE_OVER
;
925 else if (level
== MAZEZAM_NUM_LEVELS
)
926 state
= GAME_STATE_COMPLETED
;
930 case GAME_STATE_OVER
:
931 #ifdef HAVE_REMOTE_LCD
932 /* Splash text seems to use the remote display by
933 * default. I suppose I better keep it tidy!
935 rb
->lcd_remote_clear_display();
937 rb
->splash(MAZEZAM_GAMEOVER_DELAY
, MAZEZAM_GAMEOVER_TEXT
);
940 case GAME_STATE_COMPLETED
:
941 switch (welldone_screen()) {
942 case TEXT_STATE_QUIT
:
943 state
= GAME_STATE_QUIT
;
946 case TEXT_STATE_USB_CONNECTED
:
947 state
= GAME_STATE_USB_CONNECTED
;
951 state
= GAME_STATE_OKAY
;
960 /* This particular resume game logic is designed to make
961 * players prove they can solve a level more than once
963 if (level
> r
->level
+ 1)
969 /* Manage the instruction screen */
970 static enum text_state
instruction_loop(void)
973 enum text_state state
= TEXT_STATE_LOOPING
;
976 while (state
== TEXT_STATE_LOOPING
) {
977 display_text_page(help_page
[page
], 0);
978 button
= rb
->button_get(true);
986 #if CONFIG_KEYPAD != ONDIO_PAD
993 state
= TEXT_STATE_QUIT
;
997 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
998 state
= TEXT_STATE_USB_CONNECTED
;
1003 if ((page
< 0) || (page
>= MAZEZAM_NUM_HELP_PAGES
))
1004 state
= TEXT_STATE_OKAY
;
1010 /* Manage the text screen that offers the user the option of
1011 * resuming or starting a new game
1013 static enum text_state
resume_game_loop (struct resume_data
*r
)
1015 int button
= BUTTON_NONE
;
1016 enum text_state state
= TEXT_STATE_LOOPING
;
1019 /* if the resume level is 0, don't bother asking */
1020 if (r
->level
== 0) return TEXT_STATE_OKAY
;
1022 display_text_page(resume_page
, select
+ 1);
1024 while (state
== TEXT_STATE_LOOPING
) {
1025 display_text_page(resume_page
, select
+ 1);
1027 button
= rb
->button_get(true);
1031 state
= TEXT_STATE_QUIT
;
1035 state
= TEXT_STATE_BACK
;
1040 select
= 1 - select
;
1043 case MAZEZAM_SELECT
:
1044 #if CONFIG_KEYPAD != ONDIO_PAD
1048 /* The player wants to play a new game. I could ask
1049 * for confirmation here, but the only penalty is
1050 * playing through some already completed levels,
1051 * so I don't think it's necessary
1055 state
= TEXT_STATE_OKAY
;
1059 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
1060 state
= TEXT_STATE_USB_CONNECTED
;
1068 /* Load the resume data from the config file. The data is
1069 * stored in both r and old.
1071 static void resume_load_data (struct resume_data
*r
, struct resume_data
*old
)
1073 struct configdata config
[] = {
1074 {TYPE_INT
,0,MAZEZAM_NUM_LEVELS
-1,&(r
->level
),
1075 MAZEZAM_CONFIG_LEVELS_NAME
,NULL
,NULL
}
1078 if (configfile_load(MAZEZAM_CONFIG_FILENAME
,config
,MAZEZAM_CONFIG_NUM_ITEMS
,
1079 MAZEZAM_CONFIG_VERSION
) < 0)
1081 /* an extra precaution */
1082 else if ((r
->level
< 0) || (MAZEZAM_NUM_LEVELS
<= r
->level
))
1085 old
->level
= r
->level
;
1088 /* Save the resume data in the config file, but only if necessary */
1089 static void resume_save_data (struct resume_data
*r
, struct resume_data
*old
)
1091 struct configdata config
[] = {
1092 {TYPE_INT
,0,MAZEZAM_NUM_LEVELS
-1,&(r
->level
),
1093 MAZEZAM_CONFIG_LEVELS_NAME
,NULL
,NULL
}
1096 /* To reduce disk usage, only write the file if the resume data has
1099 if (old
->level
!= r
->level
)
1100 configfile_save(MAZEZAM_CONFIG_FILENAME
,config
,MAZEZAM_CONFIG_NUM_ITEMS
,
1101 MAZEZAM_CONFIG_MINVERSION
);
1104 /* The loop which manages the welcome screen and menu */
1105 static enum text_state
welcome_loop(void)
1109 enum text_state state
= TEXT_STATE_LOOPING
;
1110 struct resume_data r_data
, old_data
;
1113 resume_load_data(&r_data
, &old_data
);
1115 while (state
== TEXT_STATE_LOOPING
) {
1116 display_text_page(title_page
, select
+ 1);
1117 button
= rb
->button_get(true);
1121 state
= TEXT_STATE_QUIT
;
1125 select
= (select
+ (title_page
.num_lines
- 2)) %
1126 (title_page
.num_lines
- 1);
1130 select
= (select
+ 1) % (title_page
.num_lines
- 1);
1133 case MAZEZAM_SELECT
:
1134 #if CONFIG_KEYPAD != ONDIO_PAD
1137 if (select
== 0) { /* play game */
1138 switch (resume_game_loop(&r_data
)) {
1139 case TEXT_STATE_QUIT
:
1140 state
= TEXT_STATE_QUIT
;
1143 case TEXT_STATE_USB_CONNECTED
:
1144 state
= TEXT_STATE_USB_CONNECTED
;
1147 case TEXT_STATE_BACK
:
1150 default: { /* Ouch! This nesting is too deep! */
1151 switch (game_loop(&r_data
)) {
1152 case GAME_STATE_QUIT
:
1153 state
= TEXT_STATE_QUIT
;
1156 case GAME_STATE_USB_CONNECTED
:
1157 state
= TEXT_STATE_USB_CONNECTED
;
1160 case GAME_STATE_PARSE_ERROR
:
1161 state
= TEXT_STATE_PARSE_ERROR
;
1171 else if (select
== 1) { /* Instructions */
1172 switch (instruction_loop()) {
1173 case TEXT_STATE_QUIT
:
1174 state
= TEXT_STATE_QUIT
;
1177 case TEXT_STATE_USB_CONNECTED
:
1178 state
= TEXT_STATE_USB_CONNECTED
;
1186 state
= TEXT_STATE_QUIT
;
1191 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
1192 state
= TEXT_STATE_USB_CONNECTED
;
1197 /* I'm not sure if it's appropriate to write to disk on USB events.
1198 * Currently, I do so.
1200 resume_save_data(&r_data
, &old_data
);
1205 /* Plugin entry point */
1206 enum plugin_status
plugin_start(struct plugin_api
* api
, void* parameter
)
1208 enum plugin_status state
;
1210 /* Usual plugin stuff */
1214 /* Turn off backlight timeout */
1215 backlight_force_on(rb
); /* backlight control in lib/helper.c */
1217 #ifdef HAVE_LCD_COLOR
1218 rb
->lcd_set_background(MAZEZAM_BG_COLOR
);
1219 rb
->lcd_set_backdrop(NULL
);
1221 rb
->lcd_set_background(MAZEZAM_BG_GRAY
);
1223 rb
->lcd_setfont(FONT_SYSFIXED
);
1225 /* initialise the config file module */
1226 configfile_init(rb
);
1228 switch (welcome_loop()) {
1229 case TEXT_STATE_USB_CONNECTED
:
1230 state
= PLUGIN_USB_CONNECTED
;
1233 case TEXT_STATE_PARSE_ERROR
:
1234 state
= PLUGIN_ERROR
;
1242 /* Turn on backlight timeout (revert to settings) */
1243 backlight_use_settings(rb
); /* backlight control in lib/helper.c */