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]"
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 */
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
] = {
191 /* darker version of the above */
192 static const unsigned chunk_gray_shade
[MAZEZAM_NUM_CHUNK_GRAYS
] = {
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
208 #define MAZEZAM_TEXT_MAXLINES 4
210 /* A structure for holding text pages */
212 /* Ensure 1 < num_lines <= MAZEZAM_TEXT_MAXLINES */
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",
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
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
,
286 {3,8,2,1,{" $ $$$"," $ $ $"," $ $ $",NULL
,NULL
,NULL
,NULL
,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
,
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 " $ $$$$$$$ $$"," $ $ $ $$$"," $$$$$$$$$$$$",
310 /* This is the data structure the game uses for managing levels */
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 */
323 LEVEL_STATE_COMPLETED
,
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
338 TEXT_STATE_USB_CONNECTED
,
339 TEXT_STATE_PARSE_ERROR
,
343 /* The state and exit code of the game loop */
348 GAME_STATE_USB_CONNECTED
,
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
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 */
382 for(i
= 0; i
< text
.num_lines
; i
++) {
383 rb
->lcd_getstringsize(text
.line
[i
], w
+i
, h
+i
);
387 vgap
= (LCD_HEIGHT
-hsum
)/(text
.num_lines
+1);
391 #ifdef HAVE_LCD_COLOR
392 rb
->lcd_set_foreground(MAZEZAM_BORDER_COLOR
);
394 rb
->lcd_set_foreground(MAZEZAM_BORDER_GRAY
);
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,
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
);
409 rb
->lcd_set_foreground(MAZEZAM_HEADING_GRAY
);
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
);
420 rb
->lcd_set_foreground(MAZEZAM_TEXT_GRAY
);
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
];
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
)
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
)
459 clast
= ' '; /* the character we last considered */
460 while ((c
= level_data
[level
].line
[i
][j
]) != '\0') {
464 if (cd
->l_num
[i
] > MAZEZAM_MAX_CHUNKS
)
466 cd
->c_inset
[i
][cd
->l_num
[i
] - 1] = j
;
467 cd
->c_width
[i
][cd
->l_num
[i
] - 1] = 1;
470 cd
->c_width
[i
][cd
->l_num
[i
] - 1] += 1;
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 */
488 short x
, /* player's x and y coords */
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
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;
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 */
514 rb
->lcd_clear_display();
516 #ifdef HAVE_LCD_COLOR
517 rb
->lcd_set_foreground(MAZEZAM_WALL_COLOR
);
519 rb
->lcd_set_foreground(MAZEZAM_WALL_GRAY
);
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
),
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
]);
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
);
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
);
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,
562 xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
]+
563 cd
->c_width
[i
][j
]*size
-3,
565 rb
->lcd_drawline(xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
]+
566 cd
->c_width
[i
][j
]*size
-2,
568 xOff
+size
*shift
[i
]+size
*cd
->c_inset
[i
][j
]+
569 cd
->c_width
[i
][j
]*size
-2,
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
);
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
;
586 for (l
= yOff
+size
*i
+2; l
< yOff
+size
*i
+size
; l
+= 2 + (i
& 1))
587 rb
->lcd_drawpixel(k
, l
);
592 /* draw the player (mostly copied from the sokoban plugin) */
593 #ifdef HAVE_LCD_COLOR
594 rb
->lcd_set_foreground(MAZEZAM_PLAYER_COLOR
);
596 rb
->lcd_set_foreground(MAZEZAM_PLAYER_GRAY
);
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 */
609 #ifdef HAVE_LCD_COLOR
610 rb
->lcd_set_foreground(MAZEZAM_GATE_COLOR
);
612 rb
->lcd_set_foreground(MAZEZAM_GATE_GRAY
);
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);
638 state
= TEXT_STATE_QUIT
;
642 #if CONFIG_KEYPAD != ONDIO_PAD
645 state
= TEXT_STATE_OKAY
;
649 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
650 state
= TEXT_STATE_USB_CONNECTED
;
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
;
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);
679 state
= TEXT_STATE_QUIT
;
684 select
= (2 - select
) + 1;
688 #if CONFIG_KEYPAD != ONDIO_PAD
692 state
= TEXT_STATE_QUIT
;
694 state
= TEXT_STATE_OKAY
;
698 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
699 state
= TEXT_STATE_USB_CONNECTED
;
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 */
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
++)
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();
739 rb
->splash(MAZEZAM_LEVEL_LIVES_DELAY
, MAZEZAM_LEVEL_LIVES_TEXT
,
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
);
748 button
= rb
->button_get(true);
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
++)
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;
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
++)
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;
777 case MAZEZAM_LEFT
| BUTTON_REPEAT
:
779 for (i
= 0; i
< cd
.l_num
[y
]; i
++)
781 (x
== shift
[y
]+cd
.c_inset
[y
][i
]+
783 if (!blocked
) x
-= 1;
784 else if (shift
[y
] + cd
.c_inset
[y
][0] > 0) {
792 case MAZEZAM_RIGHT
| BUTTON_REPEAT
:
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
) {
803 else if (x
== width
) state
= LEVEL_STATE_COMPLETED
;
804 else if (y
== exit
) x
+= 1;
808 state
= LEVEL_STATE_FAILED
;
812 switch (quitconfirm_loop()) {
813 case TEXT_STATE_QUIT
:
814 state
= LEVEL_STATE_QUIT
;
817 case TEXT_STATE_USB_CONNECTED
:
818 state
= LEVEL_STATE_USB_CONNECTED
;
827 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
828 state
= LEVEL_STATE_USB_CONNECTED
;
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
:
850 if (!((level
- r
->level
) % MAZEZAM_EXTRA_LIFE
))
854 case LEVEL_STATE_QUIT
:
855 state
= GAME_STATE_QUIT
;
858 case LEVEL_STATE_FAILED
:
862 case LEVEL_STATE_PARSE_ERROR
:
863 state
= GAME_STATE_PARSE_ERROR
;
866 case LEVEL_STATE_USB_CONNECTED
:
867 state
= GAME_STATE_USB_CONNECTED
;
874 state
= GAME_STATE_OVER
;
875 else if (level
== MAZEZAM_NUM_LEVELS
)
876 state
= GAME_STATE_COMPLETED
;
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();
887 rb
->splash(MAZEZAM_GAMEOVER_DELAY
, MAZEZAM_GAMEOVER_TEXT
);
890 case GAME_STATE_COMPLETED
:
891 switch (welldone_screen()) {
892 case TEXT_STATE_QUIT
:
893 state
= GAME_STATE_QUIT
;
896 case TEXT_STATE_USB_CONNECTED
:
897 state
= GAME_STATE_USB_CONNECTED
;
901 state
= GAME_STATE_OKAY
;
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)
919 /* Manage the instruction screen */
920 static enum text_state
instruction_loop(void)
923 enum text_state state
= TEXT_STATE_LOOPING
;
926 while (state
== TEXT_STATE_LOOPING
) {
927 display_text_page(help_page
[page
], 0);
928 button
= rb
->button_get(true);
936 #if CONFIG_KEYPAD != ONDIO_PAD
943 state
= TEXT_STATE_QUIT
;
947 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
948 state
= TEXT_STATE_USB_CONNECTED
;
953 if ((page
< 0) || (page
>= MAZEZAM_NUM_HELP_PAGES
))
954 state
= TEXT_STATE_OKAY
;
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
;
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);
981 state
= TEXT_STATE_QUIT
;
985 state
= TEXT_STATE_BACK
;
994 #if CONFIG_KEYPAD != ONDIO_PAD
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
1005 state
= TEXT_STATE_OKAY
;
1009 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
1010 state
= TEXT_STATE_USB_CONNECTED
;
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)
1031 /* an extra precaution */
1032 else if ((r
->level
< 0) || (MAZEZAM_NUM_LEVELS
<= r
->level
))
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
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)
1059 enum text_state state
= TEXT_STATE_LOOPING
;
1060 struct resume_data r_data
, old_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);
1071 state
= TEXT_STATE_QUIT
;
1075 select
= (select
+ (title_page
.num_lines
- 2)) %
1076 (title_page
.num_lines
- 1);
1080 select
= (select
+ 1) % (title_page
.num_lines
- 1);
1083 case MAZEZAM_SELECT
:
1084 #if CONFIG_KEYPAD != ONDIO_PAD
1087 if (select
== 0) { /* play game */
1088 switch (resume_game_loop(&r_data
)) {
1089 case TEXT_STATE_QUIT
:
1090 state
= TEXT_STATE_QUIT
;
1093 case TEXT_STATE_USB_CONNECTED
:
1094 state
= TEXT_STATE_USB_CONNECTED
;
1097 case TEXT_STATE_BACK
:
1100 default: { /* Ouch! This nesting is too deep! */
1101 switch (game_loop(&r_data
)) {
1102 case GAME_STATE_QUIT
:
1103 state
= TEXT_STATE_QUIT
;
1106 case GAME_STATE_USB_CONNECTED
:
1107 state
= TEXT_STATE_USB_CONNECTED
;
1110 case GAME_STATE_PARSE_ERROR
:
1111 state
= TEXT_STATE_PARSE_ERROR
;
1121 else if (select
== 1) { /* Instructions */
1122 switch (instruction_loop()) {
1123 case TEXT_STATE_QUIT
:
1124 state
= TEXT_STATE_QUIT
;
1127 case TEXT_STATE_USB_CONNECTED
:
1128 state
= TEXT_STATE_USB_CONNECTED
;
1136 state
= TEXT_STATE_QUIT
;
1141 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
1142 state
= TEXT_STATE_USB_CONNECTED
;
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
);
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 */
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
);
1171 rb
->lcd_set_background(MAZEZAM_BG_GRAY
);
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
;
1183 case TEXT_STATE_PARSE_ERROR
:
1184 state
= PLUGIN_ERROR
;
1192 /* Turn on backlight timeout (revert to settings) */
1193 backlight_use_settings(rb
); /* backlight control in lib/helper.c */