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