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]"
181 #error No keymap defined!
184 /* The gap for the border around the heading in text pages. In fact, 2 is
185 * really the only acceptable value.
187 #define MAZEZAM_MENU_BORDER 2
188 #define MAZEZAM_EXTRA_LIFE 2 /* get an extra life every _ levels */
189 #define MAZEZAM_START_LIVES 3 /* how many lives at game start */
191 #ifdef HAVE_LCD_COLOR
192 #define MAZEZAM_HEADING_COLOR LCD_RGBPACK(255,255, 0) /* Yellow */
193 #define MAZEZAM_BORDER_COLOR LCD_RGBPACK( 0, 0,255) /* Blue */
194 #define MAZEZAM_TEXT_COLOR LCD_RGBPACK(255,255,255) /* White */
195 #define MAZEZAM_BG_COLOR LCD_RGBPACK( 0, 0, 0) /* Black */
196 #define MAZEZAM_WALL_COLOR LCD_RGBPACK(100,100,100) /* Dark gray */
197 #define MAZEZAM_PLAYER_COLOR LCD_RGBPACK(255,255,255) /* White */
198 #define MAZEZAM_GATE_COLOR LCD_RGBPACK(100,100,100) /* Dark gray */
200 /* the rows are coloured sequentially */
201 #define MAZEZAM_NUM_CHUNK_COLORS 8
202 static const unsigned chunk_colors
[MAZEZAM_NUM_CHUNK_COLORS
] = {
203 LCD_RGBPACK(255,192, 32), /* Orange */
204 LCD_RGBPACK(255, 0, 0), /* Red */
205 LCD_RGBPACK( 0,255, 0), /* Green */
206 LCD_RGBPACK( 0,255,255), /* Cyan */
207 LCD_RGBPACK(255,175,175), /* Pink */
208 LCD_RGBPACK(255,255, 0), /* Yellow */
209 LCD_RGBPACK( 0, 0,255), /* Blue */
210 LCD_RGBPACK(255, 0,255), /* Magenta */
215 #define MAZEZAM_HEADING_GRAY LCD_BLACK
216 #define MAZEZAM_BORDER_GRAY LCD_DARKGRAY
217 #define MAZEZAM_TEXT_GRAY LCD_BLACK
218 #define MAZEZAM_BG_GRAY LCD_WHITE
219 #define MAZEZAM_WALL_GRAY LCD_DARKGRAY
220 #define MAZEZAM_PLAYER_GRAY LCD_BLACK
221 #define MAZEZAM_GATE_GRAY LCD_BLACK
222 #define MAZEZAM_CHUNK_EDGE_GRAY LCD_BLACK
224 #define MAZEZAM_NUM_CHUNK_GRAYS 2
225 static const unsigned chunk_gray
[MAZEZAM_NUM_CHUNK_GRAYS
] = {
229 /* darker version of the above */
230 static const unsigned chunk_gray_shade
[MAZEZAM_NUM_CHUNK_GRAYS
] = {
236 #define MAZEZAM_GAMEOVER_TEXT "Game Over"
237 #define MAZEZAM_GAMEOVER_DELAY (3 * HZ) / 2
238 #define MAZEZAM_LEVEL_LIVES_TEXT "Level %d, Lives %d"
239 #define MAZEZAM_LEVEL_LIVES_DELAY HZ
240 #define MAZEZAM_WELLDONE_DELAY 4 * HZ
242 /* The maximum number of lines that a text page can display.
243 * This must be 4 or less if the Archos recorder is to be
246 #define MAZEZAM_TEXT_MAXLINES 4
248 /* A structure for holding text pages */
250 /* Ensure 1 < num_lines <= MAZEZAM_TEXT_MAXLINES */
252 char *line
[MAZEZAM_TEXT_MAXLINES
]; /* text of lines */
255 /* The text page for the welcome screen */
256 static const struct textpage title_page
= {
258 {"MazezaM", "play game", "instructions", "quit"}
261 /* The number of help screens */
262 #define MAZEZAM_NUM_HELP_PAGES 4
264 /* The instruction screens */
265 static const struct textpage help_page
[] = {
266 {4,{"Instructions","10 mazezams","bar your way","to freedom"}},
267 {4,{"Instructions","Push the rows","left and right","to escape"}},
268 {4,{"Instructions","Press " MAZEZAM_RETRY_KEYNAME
" to","retry a level",
270 {4,{"Instructions","Press " MAZEZAM_QUIT_KEYNAME
,"to quit","the game"}}
273 /* the text of the screen that asks for a quit confirmation */
274 static const struct textpage confirm_page
= {
276 {"Quit","Are you sure?","yes","no"}
279 /* the text of the screen at the end of the game */
280 static const struct textpage welldone_page
= {
282 {"Well Done","You have","escaped",""}
285 /* the text of the screen asking if the user wants to
286 * resume or start a new game.
288 static const struct textpage resume_page
= {
290 {"Checkpoint", "continue", "new game"}
293 /* maximum height of a level */
294 #define MAZEZAM_MAX_LINES 11
295 /* maximum number of chunks on a line */
296 #define MAZEZAM_MAX_CHUNKS 5
298 /* A structure for holding levels */
299 struct mazezam_level
{
300 short height
; /* the number of lines */
301 short width
; /* the width */
302 short entrance
; /* the line on which the entrance lies */
303 short exit
; /* the line on which the exit lies */
304 char *line
[MAZEZAM_MAX_LINES
]; /* the chunk data in string form */
307 /* The number of levels. Note that the instruction screens reference this
310 #define MAZEZAM_NUM_LEVELS 10
312 /* The levels. In theory, they could be stored in a file so this data
313 * structure should not be accessed outside parse_level()
315 * These levels are copyright (C) 2002 Malcolm Tyrrell. They're
316 * probably covered by the GPL as they constitute part of the source
317 * code of this plugin, but you may distibute them seperately with
318 * other Free Software if you want. You can download them from:
319 * http://webpages.dcu.ie/~tyrrelma/MazezaM.
321 static const struct mazezam_level level_data
[MAZEZAM_NUM_LEVELS
] = {
322 {2,7,0,0,{" $ $"," $ $$",NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,
324 {3,8,2,1,{" $ $$$"," $ $ $"," $ $ $",NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,
326 {4,14,1,3,{" $$$$$ $$ $$"," $$ $$ $$","$$ $ $$ $$$",
327 " $$$$$$$$ $",NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
}},
328 {6,7,4,2,{" $"," $$$$"," $$$ $$"," $ $ $"," $ $$","$ $$",
329 NULL
,NULL
,NULL
,NULL
,NULL
}},
330 {6,13,0,0,{" $$$$$","$ $$$$$ $$$"," $ $$$ $$$$",
331 "$ $ $$$$$$$"," $$$ $ $$","$ $ $ $$ $",NULL
,NULL
,
333 {11,5,10,0,{" $"," $ $$"," $$","$ $"," $ $"," $$$","$ $",
334 " $ $"," $ $","$ $$"," $"}},
335 {7,16,0,6,{" $$$$$$$"," $$$$ $$$$ $ $","$$ $$ $$$$$$ $ $",
336 "$ $ $"," $$$$$$$$$$$$$$"," $ $$ $ $$$",
337 " $ $$$ $$",NULL
,NULL
,NULL
,NULL
}},
338 {4,15,2,0,{" $$$$ $$$$ $$"," $ $$ $$ $ $$"," $ $$ $$$$ $$",
339 " $ $$ $$$$ $",NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
}},
340 {7,9,6,2,{" $ $$$$"," $ $ $$"," $ $$$$ $","$ $$ $"," $ $$$",
341 " $$$$$$"," $",NULL
,NULL
,NULL
,NULL
}},
342 {10,14,8,0,{" $"," $$$$$$$$$$ $"," $$$ $$",
343 " $ $$$$$$$$ $"," $$$ $$$ $$$"," $$$ $ $$$",
344 " $ $$$$$$$ $$"," $ $ $ $$$"," $$$$$$$$$$$$",
348 /* This is the data structure the game uses for managing levels */
350 /* the number of chunks on a line */
351 short l_num
[MAZEZAM_MAX_LINES
];
352 /* the width of a chunk */
353 short c_width
[MAZEZAM_MAX_LINES
][MAZEZAM_MAX_CHUNKS
];
354 /* the inset of a chunk */
355 short c_inset
[MAZEZAM_MAX_LINES
][MAZEZAM_MAX_CHUNKS
];
358 /* The state and exit code of the level loop */
361 LEVEL_STATE_COMPLETED
,
364 LEVEL_STATE_PARSE_ERROR
,
365 LEVEL_STATE_USB_CONNECTED
,
368 /* The state and exit code of the text screens. I use the
369 * same enum for all of them, even though there are some
376 TEXT_STATE_USB_CONNECTED
,
377 TEXT_STATE_PARSE_ERROR
,
381 /* The state and exit code of the game loop */
386 GAME_STATE_USB_CONNECTED
,
388 GAME_STATE_COMPLETED
,
389 GAME_STATE_PARSE_ERROR
,
392 /* The various constants needed for configuration files.
393 * See apps/plugins/lib/configfile.*
395 #define MAZEZAM_CONFIG_FILENAME "mazezam.data"
396 #define MAZEZAM_CONFIG_NUM_ITEMS 1
397 #define MAZEZAM_CONFIG_VERSION 0
398 #define MAZEZAM_CONFIG_MINVERSION 0
399 #define MAZEZAM_CONFIG_LEVELS_NAME "restart_level"
401 /* A structure containing the data that is written to
402 * the configuration file
405 int level
; /* level at which to restart the game */
408 /* Display a screen of text. line[0] is the heading.
409 * line[highlight] will be highlighted, unless highlight == 0
411 static void display_text_page(struct textpage text
, int highlight
)
413 int w
[text
.num_lines
], h
[text
.num_lines
];
414 int hsum
,i
,vgap
,vnext
;
416 rb
->lcd_clear_display();
418 /* find out how big the text is so we can determine the positioning */
420 for(i
= 0; i
< text
.num_lines
; i
++) {
421 rb
->lcd_getstringsize(text
.line
[i
], w
+i
, h
+i
);
425 vgap
= (LCD_HEIGHT
-hsum
)/(text
.num_lines
+1);
429 #ifdef HAVE_LCD_COLOR
430 rb
->lcd_set_foreground(MAZEZAM_BORDER_COLOR
);
432 rb
->lcd_set_foreground(MAZEZAM_BORDER_GRAY
);
434 rb
->lcd_drawrect((LCD_WIDTH
-w
[0])/2-MAZEZAM_MENU_BORDER
,
435 vgap
-MAZEZAM_MENU_BORDER
, w
[0] + 2*MAZEZAM_MENU_BORDER
,
436 h
[0] + 2*MAZEZAM_MENU_BORDER
);
437 rb
->lcd_drawrect((LCD_WIDTH
-w
[0])/2-MAZEZAM_MENU_BORDER
*2,
438 vgap
-MAZEZAM_MENU_BORDER
*2, w
[0] + 4*MAZEZAM_MENU_BORDER
,
439 h
[0] + 4*MAZEZAM_MENU_BORDER
);
440 rb
->lcd_drawline(0,vgap
+ h
[0]/2,(LCD_WIDTH
-w
[0])/2-MAZEZAM_MENU_BORDER
*2,
442 rb
->lcd_drawline((LCD_WIDTH
-w
[0])/2+w
[0]+MAZEZAM_MENU_BORDER
*2,
443 vgap
+ h
[0]/2,LCD_WIDTH
-1,vgap
+ h
[0]/2);
444 #ifdef HAVE_LCD_COLOR
445 rb
->lcd_set_foreground(MAZEZAM_HEADING_COLOR
);
447 rb
->lcd_set_foreground(MAZEZAM_HEADING_GRAY
);
449 rb
->lcd_putsxy((LCD_WIDTH
-w
[0])/2,vgap
,text
.line
[0]);
451 vnext
= vgap
*2 + h
[0];
453 /* The other lines */
455 #ifdef HAVE_LCD_COLOR
456 rb
->lcd_set_foreground(MAZEZAM_TEXT_COLOR
);
458 rb
->lcd_set_foreground(MAZEZAM_TEXT_GRAY
);
460 for (i
= 1; i
<text
.num_lines
; i
++) {
461 rb
->lcd_putsxy((LCD_WIDTH
-w
[i
])/2,vnext
,text
.line
[i
]);
463 /* add underlining if i is the highlighted line */
464 if (i
== highlight
) {
465 rb
->lcd_drawline((LCD_WIDTH
-w
[i
])/2, vnext
+ h
[i
] + 1,
466 (LCD_WIDTH
-w
[i
])/2 + w
[i
], vnext
+ h
[i
] + 1);
469 vnext
+= vgap
+ h
[i
];
476 /* Parse the level data from the level_data structure. This could be
477 * replaced by a file read. Returns true if the level parsed correctly.
479 static bool parse_level(short level
, struct chunk_data
*cd
,
480 short *width
, short *height
, short *entrance
, short *exit
)
485 *width
= level_data
[level
].width
;
486 *height
= level_data
[level
].height
;
487 *entrance
= level_data
[level
].entrance
;
488 *exit
= level_data
[level
].exit
;
490 /* for each line in the level */
491 for (i
= 0; i
<level_data
[level
].height
; i
++) {
492 if (level_data
[level
].line
[i
] == NULL
)
497 clast
= ' '; /* the character we last considered */
498 while ((c
= level_data
[level
].line
[i
][j
]) != '\0') {
502 if (cd
->l_num
[i
] > MAZEZAM_MAX_CHUNKS
)
504 cd
->c_inset
[i
][cd
->l_num
[i
] - 1] = j
;
505 cd
->c_width
[i
][cd
->l_num
[i
] - 1] = 1;
508 cd
->c_width
[i
][cd
->l_num
[i
] - 1] += 1;
519 static void draw_level(
520 struct chunk_data
*cd
, /* the data about the chunks */
521 short *shift
, /* an array of the horizontal offset of the lines */
526 short x
, /* player's x and y coords */
529 /* The number of pixels the side of a square should be */
530 short size
= (LCD_WIDTH
/(width
+2)) < (LCD_HEIGHT
/height
) ?
531 (LCD_WIDTH
/(width
+2)) : (LCD_HEIGHT
/height
);
532 /* The x and y position (in pixels) of the top left corner of the
535 short xOff
= (LCD_WIDTH
- (size
*width
))/2;
536 short yOff
= (LCD_HEIGHT
- (size
*height
))/2;
537 /* For drawing the player, taken from the sokoban plugin */
538 short max
= size
- 1;
539 short middle
= max
/ 2;
540 short ldelta
= (middle
+ 1) / 2;
542 short third
= size
/ 3;
543 short twothirds
= (2 * size
) / 3;
544 #ifndef HAVE_LCD_COLOR
545 /* We #def these out to supress a compiler warning */
552 rb
->lcd_clear_display();
554 #ifdef HAVE_LCD_COLOR
555 rb
->lcd_set_foreground(MAZEZAM_WALL_COLOR
);
557 rb
->lcd_set_foreground(MAZEZAM_WALL_GRAY
);
559 /* draw the upper wall */
560 rb
->lcd_fillrect(0,0,xOff
,yOff
+(size
*entrance
));
561 rb
->lcd_fillrect(xOff
,0,size
*width
,yOff
);
562 rb
->lcd_fillrect(xOff
+(size
*width
),0,LCD_WIDTH
-xOff
-(size
*width
),
565 /* draw the lower wall */
566 rb
->lcd_fillrect(0,yOff
+(size
*entrance
)+size
,xOff
,
567 LCD_HEIGHT
-yOff
-(size
*entrance
)-size
);
568 rb
->lcd_fillrect(xOff
,yOff
+(size
*height
),size
*width
,
569 LCD_HEIGHT
-yOff
-(size
*height
));
570 /* Note: the exit is made one pixel thinner than necessary as a visual
571 * clue that chunks cannot be pushed into it
573 rb
->lcd_fillrect(xOff
+(size
*width
),yOff
+(size
*exit
)+size
-1,
574 LCD_WIDTH
-xOff
+(size
*width
),
575 LCD_HEIGHT
-yOff
-(size
*exit
)-size
+1);
577 /* draw the chunks */
578 for (i
= 0; i
<height
; i
++) {
579 #ifdef HAVE_LCD_COLOR
580 /* adding width to i should have a fixed, but randomising effect on
581 * the choice of the colours of the top line of chunks
583 rb
->lcd_set_foreground(chunk_colors
[(i
+width
) %
584 MAZEZAM_NUM_CHUNK_COLORS
]);
586 for (j
= 0; j
<cd
->l_num
[i
]; j
++) {
587 #ifdef HAVE_LCD_COLOR
588 rb
->lcd_fillrect(xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
],
589 yOff
+size
*i
, cd
->c_width
[i
][j
]*size
,size
);
591 rb
->lcd_set_foreground(MAZEZAM_CHUNK_EDGE_GRAY
);
592 rb
->lcd_drawrect(xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
],
593 yOff
+size
*i
, cd
->c_width
[i
][j
]*size
,size
);
596 rb
->lcd_set_foreground(chunk_gray_shade
[(i
+width
) %
597 MAZEZAM_NUM_CHUNK_GRAYS
]);
598 rb
->lcd_drawline(xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
]+1,
600 xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
]+
601 cd
->c_width
[i
][j
]*size
-3,
603 rb
->lcd_drawline(xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
]+
604 cd
->c_width
[i
][j
]*size
-2,
606 xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
]+
607 cd
->c_width
[i
][j
]*size
-2,
611 rb
->lcd_set_foreground(chunk_gray
[(i
+width
) %
612 MAZEZAM_NUM_CHUNK_GRAYS
]);
613 for (k
= yOff
+size
*i
+2; k
< yOff
+size
*i
+size
-2; k
+= 2)
614 rb
->lcd_drawline(xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
]+2,k
,
615 xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
]+
616 cd
->c_width
[i
][j
]*size
-3,k
);
618 rb
->lcd_drawrect(xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
],
619 yOff
+size
*i
, cd
->c_width
[i
][j
]*size
,size
);
620 for (k
= xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
]+2;
621 k
< xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
]+
622 cd
->c_width
[i
][j
]*size
;
624 for (l
= yOff
+size
*i
+2; l
< yOff
+size
*i
+size
; l
+= 2 + (i
& 1))
625 rb
->lcd_drawpixel(k
, l
);
630 /* draw the player (mostly copied from the sokoban plugin) */
631 #ifdef HAVE_LCD_COLOR
632 rb
->lcd_set_foreground(MAZEZAM_PLAYER_COLOR
);
634 rb
->lcd_set_foreground(MAZEZAM_PLAYER_GRAY
);
636 rb
->lcd_drawline(xOff
+size
*x
, yOff
+size
*y
+middle
,
637 xOff
+size
*x
+max
, yOff
+size
*y
+middle
);
638 rb
->lcd_drawline(xOff
+size
*x
+middle
, yOff
+size
*y
,
639 xOff
+size
*x
+middle
, yOff
+size
*y
+max
-ldelta
);
640 rb
->lcd_drawline(xOff
+size
*x
+middle
, yOff
+size
*y
+max
-ldelta
,
641 xOff
+size
*x
+middle
-ldelta
, yOff
+size
*y
+max
);
642 rb
->lcd_drawline(xOff
+size
*x
+middle
, yOff
+size
*y
+max
-ldelta
,
643 xOff
+size
*x
+middle
+ldelta
, yOff
+size
*y
+max
);
645 /* draw the gate, if the player has moved into the level */
647 #ifdef HAVE_LCD_COLOR
648 rb
->lcd_set_foreground(MAZEZAM_GATE_COLOR
);
650 rb
->lcd_set_foreground(MAZEZAM_GATE_GRAY
);
652 rb
->lcd_drawline(xOff
-size
,yOff
+entrance
*size
+third
,
653 xOff
-1,yOff
+entrance
*size
+third
);
654 rb
->lcd_drawline(xOff
-size
,yOff
+entrance
*size
+twothirds
,
655 xOff
-1,yOff
+entrance
*size
+twothirds
);
656 rb
->lcd_drawline(xOff
-size
+third
,yOff
+entrance
*size
,
657 xOff
-size
+third
,yOff
+entrance
*size
+size
-1);
658 rb
->lcd_drawline(xOff
-size
+twothirds
,yOff
+entrance
*size
,
659 xOff
-size
+twothirds
,yOff
+entrance
*size
+size
-1);
663 /* Manage the congratulations screen */
664 static enum text_state
welldone_screen(void)
666 int button
= BUTTON_NONE
;
667 enum text_state state
= TEXT_STATE_LOOPING
;
669 display_text_page(welldone_page
, 0);
671 while (state
== TEXT_STATE_LOOPING
) {
672 button
= rb
->button_get(true);
676 state
= TEXT_STATE_QUIT
;
680 #if CONFIG_KEYPAD != ONDIO_PAD
683 state
= TEXT_STATE_OKAY
;
687 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
688 state
= TEXT_STATE_USB_CONNECTED
;
696 /* Manage the quit confimation screen */
697 static enum text_state
quitconfirm_loop(void)
699 int button
= BUTTON_NONE
;
700 enum text_state state
= TEXT_STATE_LOOPING
;
703 display_text_page(confirm_page
, select
+ 1);
705 /* Wait for a button release. This is useful when a repeated button
706 * press is used for quit.
708 while ((rb
->button_get(true) & BUTTON_REL
) != BUTTON_REL
);
710 while (state
== TEXT_STATE_LOOPING
) {
711 display_text_page(confirm_page
, select
+ 1);
713 button
= rb
->button_get(true);
717 state
= TEXT_STATE_QUIT
;
722 select
= (2 - select
) + 1;
726 #if CONFIG_KEYPAD != ONDIO_PAD
730 state
= TEXT_STATE_QUIT
;
732 state
= TEXT_STATE_OKAY
;
736 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
737 state
= TEXT_STATE_USB_CONNECTED
;
745 /* Manage the playing of a level */
746 static enum level_state
level_loop(short level
, short lives
)
748 struct chunk_data cd
;
749 short shift
[MAZEZAM_MAX_LINES
]; /* amount each line has been shifted */
757 enum level_state state
= LEVEL_STATE_LOOPING
;
758 bool blocked
; /* is there a chunk in the way of the player? */
760 if (!(parse_level(level
,&cd
,&width
,&height
,&entrance
,&exit
)))
761 return LEVEL_STATE_PARSE_ERROR
;
763 for (i
= 0; i
< height
; i
++)
769 draw_level(&cd
, shift
, width
, height
, entrance
, exit
, x
, y
);
771 #ifdef HAVE_REMOTE_LCD
772 /* Splash text seems to use the remote display by
773 * default. I suppose I better keep it tidy!
775 rb
->lcd_remote_clear_display();
777 rb
->splash(MAZEZAM_LEVEL_LIVES_DELAY
, MAZEZAM_LEVEL_LIVES_TEXT
,
780 /* ensure keys pressed during the splash screen are ignored */
781 rb
->button_clear_queue();
783 while (state
== LEVEL_STATE_LOOPING
) {
784 draw_level(&cd
, shift
, width
, height
, entrance
, exit
, x
, y
);
786 button
= rb
->button_get(true);
791 case MAZEZAM_UP
| BUTTON_REPEAT
:
792 if ((y
> 0) && (x
>= 0) && (x
< width
)) {
793 for (i
= 0; i
< cd
.l_num
[y
-1]; i
++)
795 ((x
>=shift
[y
-1]+cd
.c_inset
[y
-1][i
]) &&
796 (x
<shift
[y
-1]+cd
.c_inset
[y
-1][i
]+
797 cd
.c_width
[y
-1][i
]));
798 if (!blocked
) y
-= 1;
803 case MAZEZAM_DOWN
| BUTTON_REPEAT
:
804 if ((y
< height
-1) && (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_LEFT
| BUTTON_REPEAT
:
817 for (i
= 0; i
< cd
.l_num
[y
]; i
++)
819 (x
== shift
[y
]+cd
.c_inset
[y
][i
]+
821 if (!blocked
) x
-= 1;
822 else if (shift
[y
] + cd
.c_inset
[y
][0] > 0) {
830 case MAZEZAM_RIGHT
| BUTTON_REPEAT
:
832 for (i
= 0; i
< cd
.l_num
[y
]; i
++)
833 blocked
= blocked
|| (x
+1 == shift
[y
]+cd
.c_inset
[y
][i
]);
834 if (!blocked
) x
+= 1;
835 else if (shift
[y
] + cd
.c_inset
[y
][cd
.l_num
[y
]-1] +
836 cd
.c_width
[y
][cd
.l_num
[y
]-1] < width
) {
841 else if (x
== width
) state
= LEVEL_STATE_COMPLETED
;
842 else if (y
== exit
) x
+= 1;
846 state
= LEVEL_STATE_FAILED
;
850 switch (quitconfirm_loop()) {
851 case TEXT_STATE_QUIT
:
852 state
= LEVEL_STATE_QUIT
;
855 case TEXT_STATE_USB_CONNECTED
:
856 state
= LEVEL_STATE_USB_CONNECTED
;
865 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
866 state
= LEVEL_STATE_USB_CONNECTED
;
874 /* The loop which manages a full game of MazezaM */
875 static enum game_state
game_loop(struct resume_data
*r
)
877 enum game_state state
= GAME_STATE_LOOPING
;
878 int level
= r
->level
;
879 int lives
= MAZEZAM_START_LIVES
;
881 rb
->lcd_clear_display();
883 while (state
== GAME_STATE_LOOPING
)
885 switch (level_loop(level
,lives
)) {
886 case LEVEL_STATE_COMPLETED
:
888 if (!((level
- r
->level
) % MAZEZAM_EXTRA_LIFE
))
892 case LEVEL_STATE_QUIT
:
893 state
= GAME_STATE_QUIT
;
896 case LEVEL_STATE_FAILED
:
900 case LEVEL_STATE_PARSE_ERROR
:
901 state
= GAME_STATE_PARSE_ERROR
;
904 case LEVEL_STATE_USB_CONNECTED
:
905 state
= GAME_STATE_USB_CONNECTED
;
912 state
= GAME_STATE_OVER
;
913 else if (level
== MAZEZAM_NUM_LEVELS
)
914 state
= GAME_STATE_COMPLETED
;
918 case GAME_STATE_OVER
:
919 #ifdef HAVE_REMOTE_LCD
920 /* Splash text seems to use the remote display by
921 * default. I suppose I better keep it tidy!
923 rb
->lcd_remote_clear_display();
925 rb
->splash(MAZEZAM_GAMEOVER_DELAY
, MAZEZAM_GAMEOVER_TEXT
);
928 case GAME_STATE_COMPLETED
:
929 switch (welldone_screen()) {
930 case TEXT_STATE_QUIT
:
931 state
= GAME_STATE_QUIT
;
934 case TEXT_STATE_USB_CONNECTED
:
935 state
= GAME_STATE_USB_CONNECTED
;
939 state
= GAME_STATE_OKAY
;
948 /* This particular resume game logic is designed to make
949 * players prove they can solve a level more than once
951 if (level
> r
->level
+ 1)
957 /* Manage the instruction screen */
958 static enum text_state
instruction_loop(void)
961 enum text_state state
= TEXT_STATE_LOOPING
;
964 while (state
== TEXT_STATE_LOOPING
) {
965 display_text_page(help_page
[page
], 0);
966 button
= rb
->button_get(true);
974 #if CONFIG_KEYPAD != ONDIO_PAD
981 state
= TEXT_STATE_QUIT
;
985 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
986 state
= TEXT_STATE_USB_CONNECTED
;
991 if ((page
< 0) || (page
>= MAZEZAM_NUM_HELP_PAGES
))
992 state
= TEXT_STATE_OKAY
;
998 /* Manage the text screen that offers the user the option of
999 * resuming or starting a new game
1001 static enum text_state
resume_game_loop (struct resume_data
*r
)
1003 int button
= BUTTON_NONE
;
1004 enum text_state state
= TEXT_STATE_LOOPING
;
1007 /* if the resume level is 0, don't bother asking */
1008 if (r
->level
== 0) return TEXT_STATE_OKAY
;
1010 display_text_page(resume_page
, select
+ 1);
1012 while (state
== TEXT_STATE_LOOPING
) {
1013 display_text_page(resume_page
, select
+ 1);
1015 button
= rb
->button_get(true);
1019 state
= TEXT_STATE_QUIT
;
1023 state
= TEXT_STATE_BACK
;
1028 select
= 1 - select
;
1031 case MAZEZAM_SELECT
:
1032 #if CONFIG_KEYPAD != ONDIO_PAD
1036 /* The player wants to play a new game. I could ask
1037 * for confirmation here, but the only penalty is
1038 * playing through some already completed levels,
1039 * so I don't think it's necessary
1043 state
= TEXT_STATE_OKAY
;
1047 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
1048 state
= TEXT_STATE_USB_CONNECTED
;
1056 /* Load the resume data from the config file. The data is
1057 * stored in both r and old.
1059 static void resume_load_data (struct resume_data
*r
, struct resume_data
*old
)
1061 struct configdata config
[] = {
1062 {TYPE_INT
,0,MAZEZAM_NUM_LEVELS
-1,&(r
->level
),
1063 MAZEZAM_CONFIG_LEVELS_NAME
,NULL
,NULL
}
1066 if (configfile_load(MAZEZAM_CONFIG_FILENAME
,config
,MAZEZAM_CONFIG_NUM_ITEMS
,
1067 MAZEZAM_CONFIG_VERSION
) < 0)
1069 /* an extra precaution */
1070 else if ((r
->level
< 0) || (MAZEZAM_NUM_LEVELS
<= r
->level
))
1073 old
->level
= r
->level
;
1076 /* Save the resume data in the config file, but only if necessary */
1077 static void resume_save_data (struct resume_data
*r
, struct resume_data
*old
)
1079 struct configdata config
[] = {
1080 {TYPE_INT
,0,MAZEZAM_NUM_LEVELS
-1,&(r
->level
),
1081 MAZEZAM_CONFIG_LEVELS_NAME
,NULL
,NULL
}
1084 /* To reduce disk usage, only write the file if the resume data has
1087 if (old
->level
!= r
->level
)
1088 configfile_save(MAZEZAM_CONFIG_FILENAME
,config
,MAZEZAM_CONFIG_NUM_ITEMS
,
1089 MAZEZAM_CONFIG_MINVERSION
);
1092 /* The loop which manages the welcome screen and menu */
1093 static enum text_state
welcome_loop(void)
1097 enum text_state state
= TEXT_STATE_LOOPING
;
1098 struct resume_data r_data
, old_data
;
1101 resume_load_data(&r_data
, &old_data
);
1103 while (state
== TEXT_STATE_LOOPING
) {
1104 display_text_page(title_page
, select
+ 1);
1105 button
= rb
->button_get(true);
1109 state
= TEXT_STATE_QUIT
;
1113 select
= (select
+ (title_page
.num_lines
- 2)) %
1114 (title_page
.num_lines
- 1);
1118 select
= (select
+ 1) % (title_page
.num_lines
- 1);
1121 case MAZEZAM_SELECT
:
1122 #if CONFIG_KEYPAD != ONDIO_PAD
1125 if (select
== 0) { /* play game */
1126 switch (resume_game_loop(&r_data
)) {
1127 case TEXT_STATE_QUIT
:
1128 state
= TEXT_STATE_QUIT
;
1131 case TEXT_STATE_USB_CONNECTED
:
1132 state
= TEXT_STATE_USB_CONNECTED
;
1135 case TEXT_STATE_BACK
:
1138 default: { /* Ouch! This nesting is too deep! */
1139 switch (game_loop(&r_data
)) {
1140 case GAME_STATE_QUIT
:
1141 state
= TEXT_STATE_QUIT
;
1144 case GAME_STATE_USB_CONNECTED
:
1145 state
= TEXT_STATE_USB_CONNECTED
;
1148 case GAME_STATE_PARSE_ERROR
:
1149 state
= TEXT_STATE_PARSE_ERROR
;
1159 else if (select
== 1) { /* Instructions */
1160 switch (instruction_loop()) {
1161 case TEXT_STATE_QUIT
:
1162 state
= TEXT_STATE_QUIT
;
1165 case TEXT_STATE_USB_CONNECTED
:
1166 state
= TEXT_STATE_USB_CONNECTED
;
1174 state
= TEXT_STATE_QUIT
;
1179 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
1180 state
= TEXT_STATE_USB_CONNECTED
;
1185 /* I'm not sure if it's appropriate to write to disk on USB events.
1186 * Currently, I do so.
1188 resume_save_data(&r_data
, &old_data
);
1193 /* Plugin entry point */
1194 enum plugin_status
plugin_start(struct plugin_api
* api
, void* parameter
)
1196 enum plugin_status state
;
1198 /* Usual plugin stuff */
1202 /* Turn off backlight timeout */
1203 backlight_force_on(rb
); /* backlight control in lib/helper.c */
1205 #ifdef HAVE_LCD_COLOR
1206 rb
->lcd_set_background(MAZEZAM_BG_COLOR
);
1207 rb
->lcd_set_backdrop(NULL
);
1209 rb
->lcd_set_background(MAZEZAM_BG_GRAY
);
1211 rb
->lcd_setfont(FONT_SYSFIXED
);
1213 /* initialise the config file module */
1214 configfile_init(rb
);
1216 switch (welcome_loop()) {
1217 case TEXT_STATE_USB_CONNECTED
:
1218 state
= PLUGIN_USB_CONNECTED
;
1221 case TEXT_STATE_PARSE_ERROR
:
1222 state
= PLUGIN_ERROR
;
1230 /* Turn on backlight timeout (revert to settings) */
1231 backlight_use_settings(rb
); /* backlight control in lib/helper.c */