Updated our source code header to explicitly mention that we are GPL v2 or
[Rockbox.git] / apps / plugins / jewels.c
blob48735a2b446e6f5f7bdb3922bfd11db42ce12894
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 Adam Boot
12 * Color graphics from Gweled (http://sebdelestaing.free.fr/gweled/)
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ****************************************************************************/
24 #include "plugin.h"
25 #include "playback_control.h"
27 #ifdef HAVE_LCD_BITMAP
29 PLUGIN_HEADER
31 /* button definitions */
32 #if CONFIG_KEYPAD == RECORDER_PAD
33 #define JEWELS_UP BUTTON_UP
34 #define JEWELS_DOWN BUTTON_DOWN
35 #define JEWELS_LEFT BUTTON_LEFT
36 #define JEWELS_RIGHT BUTTON_RIGHT
37 #define JEWELS_SELECT BUTTON_PLAY
38 #define JEWELS_CANCEL BUTTON_OFF
40 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
41 #define JEWELS_UP BUTTON_UP
42 #define JEWELS_DOWN BUTTON_DOWN
43 #define JEWELS_LEFT BUTTON_LEFT
44 #define JEWELS_RIGHT BUTTON_RIGHT
45 #define JEWELS_SELECT BUTTON_SELECT
46 #define JEWELS_CANCEL BUTTON_OFF
48 #elif CONFIG_KEYPAD == ONDIO_PAD
49 #define JEWELS_UP BUTTON_UP
50 #define JEWELS_DOWN BUTTON_DOWN
51 #define JEWELS_LEFT BUTTON_LEFT
52 #define JEWELS_RIGHT BUTTON_RIGHT
53 #define JEWELS_SELECT BUTTON_MENU
54 #define JEWELS_CANCEL BUTTON_OFF
56 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
57 #define JEWELS_UP BUTTON_UP
58 #define JEWELS_DOWN BUTTON_DOWN
59 #define JEWELS_LEFT BUTTON_LEFT
60 #define JEWELS_RIGHT BUTTON_RIGHT
61 #define JEWELS_SELECT BUTTON_SELECT
62 #define JEWELS_CANCEL BUTTON_OFF
63 #define JEWELS_RC_CANCEL BUTTON_RC_STOP
65 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
66 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
67 #define JEWELS_SCROLLWHEEL
68 #define JEWELS_UP BUTTON_MENU
69 #define JEWELS_DOWN BUTTON_PLAY
70 #define JEWELS_LEFT BUTTON_LEFT
71 #define JEWELS_RIGHT BUTTON_RIGHT
72 #define JEWELS_PREV BUTTON_SCROLL_BACK
73 #define JEWELS_NEXT BUTTON_SCROLL_FWD
74 #define JEWELS_SELECT BUTTON_SELECT
76 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
77 #define JEWELS_UP BUTTON_UP
78 #define JEWELS_DOWN BUTTON_DOWN
79 #define JEWELS_LEFT BUTTON_LEFT
80 #define JEWELS_RIGHT BUTTON_RIGHT
81 #define JEWELS_SELECT BUTTON_SELECT
82 #define JEWELS_CANCEL BUTTON_PLAY
84 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
85 #define JEWELS_UP BUTTON_UP
86 #define JEWELS_DOWN BUTTON_DOWN
87 #define JEWELS_LEFT BUTTON_LEFT
88 #define JEWELS_RIGHT BUTTON_RIGHT
89 #define JEWELS_SELECT BUTTON_SELECT
90 #define JEWELS_CANCEL BUTTON_POWER
92 #elif CONFIG_KEYPAD == GIGABEAT_PAD
93 #define JEWELS_UP BUTTON_UP
94 #define JEWELS_DOWN BUTTON_DOWN
95 #define JEWELS_LEFT BUTTON_LEFT
96 #define JEWELS_RIGHT BUTTON_RIGHT
97 #define JEWELS_SELECT BUTTON_SELECT
98 #define JEWELS_CANCEL BUTTON_POWER
100 #elif CONFIG_KEYPAD == SANSA_E200_PAD
101 #define JEWELS_SCROLLWHEEL
102 #define JEWELS_UP BUTTON_UP
103 #define JEWELS_DOWN BUTTON_DOWN
104 #define JEWELS_LEFT BUTTON_LEFT
105 #define JEWELS_RIGHT BUTTON_RIGHT
106 #define JEWELS_PREV BUTTON_SCROLL_BACK
107 #define JEWELS_NEXT BUTTON_SCROLL_FWD
108 #define JEWELS_SELECT BUTTON_SELECT
109 #define JEWELS_CANCEL BUTTON_POWER
111 #elif CONFIG_KEYPAD == SANSA_C200_PAD
112 #define JEWELS_UP BUTTON_UP
113 #define JEWELS_DOWN BUTTON_DOWN
114 #define JEWELS_LEFT BUTTON_LEFT
115 #define JEWELS_RIGHT BUTTON_RIGHT
116 #define JEWELS_SELECT BUTTON_SELECT
117 #define JEWELS_CANCEL BUTTON_POWER
119 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
120 #define JEWELS_UP BUTTON_SCROLL_UP
121 #define JEWELS_DOWN BUTTON_SCROLL_DOWN
122 #define JEWELS_LEFT BUTTON_LEFT
123 #define JEWELS_RIGHT BUTTON_RIGHT
124 #define JEWELS_SELECT BUTTON_PLAY
125 #define JEWELS_CANCEL BUTTON_POWER
127 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
128 #define JEWELS_UP BUTTON_UP
129 #define JEWELS_DOWN BUTTON_DOWN
130 #define JEWELS_LEFT BUTTON_LEFT
131 #define JEWELS_RIGHT BUTTON_RIGHT
132 #define JEWELS_SELECT BUTTON_SELECT
133 #define JEWELS_CANCEL BUTTON_BACK
135 #elif CONFIG_KEYPAD == MROBE100_PAD
136 #define JEWELS_UP BUTTON_UP
137 #define JEWELS_DOWN BUTTON_DOWN
138 #define JEWELS_LEFT BUTTON_LEFT
139 #define JEWELS_RIGHT BUTTON_RIGHT
140 #define JEWELS_SELECT BUTTON_SELECT
141 #define JEWELS_CANCEL BUTTON_POWER
143 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
144 #define JEWELS_UP BUTTON_RC_VOL_UP
145 #define JEWELS_DOWN BUTTON_RC_VOL_DOWN
146 #define JEWELS_LEFT BUTTON_RC_REW
147 #define JEWELS_RIGHT BUTTON_RC_FF
148 #define JEWELS_SELECT BUTTON_RC_PLAY
149 #define JEWELS_CANCEL BUTTON_RC_REC
151 #define JEWELS_RC_CANCEL BUTTON_REC
153 #elif CONFIG_KEYPAD == COWOND2_PAD
154 #define JEWELS_CANCEL BUTTON_POWER
156 #else
157 #error No keymap defined!
158 #endif
160 #ifdef HAVE_TOUCHPAD
161 #ifndef JEWELS_UP
162 #define JEWELS_UP BUTTON_TOPMIDDLE
163 #endif
164 #ifndef JEWELS_DOWN
165 #define JEWELS_DOWN BUTTON_BOTTOMMIDDLE
166 #endif
167 #ifndef JEWELS_LEFT
168 #define JEWELS_LEFT BUTTON_MIDLEFT
169 #endif
170 #ifndef JEWELS_RIGHT
171 #define JEWELS_RIGHT BUTTON_MIDRIGHT
172 #endif
173 #ifndef JEWELS_SELECT
174 #define JEWELS_SELECT BUTTON_CENTER
175 #endif
176 #ifndef JEWELS_CANCEL
177 #define JEWELS_CANCEL BUTTON_TOPLEFT
178 #endif
179 #endif
181 /* use 30x30 tiles (iPod Video, Gigabeat) */
182 #if (LCD_HEIGHT == 240) && (LCD_WIDTH == 320) || \
183 ((LCD_HEIGHT == 320) && (LCD_WIDTH == 240))
184 #define TILE_WIDTH 30
185 #define TILE_HEIGHT 30
186 #define YOFS 0
187 #define NUM_SCORES 10
189 /* use 22x22 tiles (H300, iPod Color) */
190 #elif ((LCD_HEIGHT == 176) && (LCD_WIDTH == 220)) || \
191 ((LCD_HEIGHT == 220) && (LCD_WIDTH == 176))
192 #define TILE_WIDTH 22
193 #define TILE_HEIGHT 22
194 #define YOFS 0
195 #define NUM_SCORES 10
197 /* use 16x16 tiles (iPod Nano) */
198 #elif (LCD_HEIGHT == 132) && (LCD_WIDTH == 176)
199 #define TILE_WIDTH 16
200 #define TILE_HEIGHT 16
201 #define YOFS 4
202 #define NUM_SCORES 10
204 /* use 16x16 tiles (H100, iAudio X5, iPod 3G, iPod 4G grayscale) */
205 #elif (LCD_HEIGHT == 128) && (LCD_WIDTH == 160)
206 #define TILE_WIDTH 16
207 #define TILE_HEIGHT 16
208 #define YOFS 0
209 #define NUM_SCORES 10
211 /* use 14x14 tiles (H10 5/6 GB) */
212 #elif (LCD_HEIGHT == 128) && (LCD_WIDTH == 128)
213 #define TILE_WIDTH 14
214 #define TILE_HEIGHT 14
215 #define YOFS 0
216 #define NUM_SCORES 10
218 /* use 13x13 tiles (iPod Mini) */
219 #elif (LCD_HEIGHT == 110) && (LCD_WIDTH == 138)
220 #define TILE_WIDTH 13
221 #define TILE_HEIGHT 13
222 #define YOFS 6
223 #define NUM_SCORES 10
225 /* use 12x12 tiles (iAudio M3) */
226 #elif (LCD_HEIGHT == 96) && (LCD_WIDTH == 128)
227 #define TILE_WIDTH 12
228 #define TILE_HEIGHT 12
229 #define YOFS 0
230 #define NUM_SCORES 9
232 /* use 10x10 tiles (Sansa c200) */
233 #elif (LCD_HEIGHT == 80) && (LCD_WIDTH == 132)
234 #define TILE_WIDTH 10
235 #define TILE_HEIGHT 10
236 #define YOFS 0
237 #define NUM_SCORES 8
239 /* use 10x8 tiles (iFP 700) */
240 #elif (LCD_HEIGHT == 64) && (LCD_WIDTH == 128)
241 #define TILE_WIDTH 10
242 #define TILE_HEIGHT 8
243 #define YOFS 0
244 #define NUM_SCORES 8
246 /* use 10x8 tiles (Recorder, Ondio) */
247 #elif (LCD_HEIGHT == 64) && (LCD_WIDTH == 112)
248 #define TILE_WIDTH 10
249 #define TILE_HEIGHT 8
250 #define YOFS 0
251 #define NUM_SCORES 8
253 #else
254 #error JEWELS: Unsupported LCD
255 #endif
257 /* save files */
258 #define SCORE_FILE PLUGIN_GAMES_DIR "/jewels.score"
259 #define SAVE_FILE PLUGIN_GAMES_DIR "/jewels.save"
261 /* final game return status */
262 #define BJ_QUIT_FROM_GAME 4
263 #define BJ_END 3
264 #define BJ_USB 2
265 #define BJ_QUIT 1
266 #define BJ_LOSE 0
268 /* swap directions */
269 #define SWAP_UP 0
270 #define SWAP_RIGHT 1
271 #define SWAP_DOWN 2
272 #define SWAP_LEFT 3
274 /* play board dimension */
275 #define BJ_HEIGHT 9
276 #define BJ_WIDTH 8
278 /* next level threshold */
279 #define LEVEL_PTS 100
281 /* animation frame rate */
282 #define MAX_FPS 20
284 /* Game types */
285 enum game_type {
286 GAME_TYPE_NORMAL,
287 GAME_TYPE_PUZZLE
290 /* menu values */
291 #define FONT_HEIGHT 8
292 #define MAX_MITEMS 6
293 #define MENU_WIDTH 100
295 /* menu results */
296 enum menu_result {
297 MRES_NONE,
298 MRES_NEW,
299 MRES_PUZZLE,
300 MRES_SAVE,
301 MRES_RESUME,
302 MRES_SCORES,
303 MRES_HELP,
304 MRES_QUIT,
305 MRES_PLAYBACK,
306 MRES_EXIT
309 /* menu commands */
310 enum menu_cmd {
311 MCMD_NONE,
312 MCMD_NEXT,
313 MCMD_PREV,
314 MCMD_SELECT
317 /* menus */
318 struct jewels_menu {
319 char *title;
320 bool hasframe;
321 int selected;
322 int itemcnt;
323 struct jewels_menuitem {
324 char *text;
325 enum menu_result res;
326 } items[MAX_MITEMS];
327 } bjmenu[] = {
328 {"Jewels", false, 0, 6,
329 {{"New Game", MRES_NEW},
330 {"Puzzle", MRES_PUZZLE},
331 {"Resume Game", MRES_RESUME},
332 {"High Scores", MRES_SCORES},
333 {"Help", MRES_HELP},
334 {"Quit", MRES_QUIT}}},
335 {"Menu", true, 0, 5,
336 {{"Audio Playback", MRES_PLAYBACK },
337 {"Resume Game", MRES_RESUME},
338 {"Save Game", MRES_SAVE},
339 {"End Game", MRES_QUIT},
340 {"Exit Jewels", MRES_EXIT}}}
343 /* global rockbox api */
344 static const struct plugin_api* rb;
346 /* external bitmaps */
347 extern const fb_data jewels[];
349 /* tile background colors */
350 #ifdef HAVE_LCD_COLOR
351 static const unsigned jewels_bkgd[2] = {
352 LCD_RGBPACK(104, 63, 63),
353 LCD_RGBPACK(83, 44, 44)
355 #endif
357 /* the tile struct
358 * type is the jewel number 0-7
359 * falling if the jewel is falling
360 * delete marks the jewel for deletion
362 struct tile {
363 int type;
364 bool falling;
365 bool delete;
368 /* the game context struct
369 * score is the current level score
370 * segments is the number of cleared segments in the current run
371 * level is the current level
372 * type is the game type (normal or puzzle)
373 * highscores is the list of high scores
374 * resume denotes whether to resume the currently loaded game
375 * dirty denotes whether the high scores are out of sync with the saved file
376 * playboard is the game playing board (first row is hidden)
377 * num_jewels is the number of different jewels to use
379 struct game_context {
380 unsigned int score;
381 unsigned int segments;
382 unsigned int level;
383 unsigned int type;
384 unsigned int highscores[NUM_SCORES];
385 bool resume;
386 bool dirty;
387 struct tile playboard[BJ_HEIGHT][BJ_WIDTH];
388 unsigned int num_jewels;
391 #define MAX_NUM_JEWELS 7
393 #define MAX_PUZZLE_TILES 4
394 #define NUM_PUZZLE_LEVELS 10
396 struct puzzle_tile {
397 int x;
398 int y;
399 int tile_type;
402 struct puzzle_level {
403 unsigned int num_jewels;
404 unsigned int num_tiles;
405 struct puzzle_tile tiles[MAX_PUZZLE_TILES];
408 #define PUZZLE_TILE_UP 1
409 #define PUZZLE_TILE_DOWN 2
410 #define PUZZLE_TILE_LEFT 4
411 #define PUZZLE_TILE_RIGHT 8
413 struct puzzle_level puzzle_levels[NUM_PUZZLE_LEVELS] = {
414 { 5, 2, { {3, 3, PUZZLE_TILE_RIGHT},
415 {4, 2, PUZZLE_TILE_LEFT} } },
416 { 5, 2, { {3, 2, PUZZLE_TILE_DOWN},
417 {3, 4, PUZZLE_TILE_UP} } },
418 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
419 {3, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
420 {3, 6, PUZZLE_TILE_UP} } },
421 { 6, 3, { {3, 2, PUZZLE_TILE_RIGHT},
422 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
423 {5, 4, PUZZLE_TILE_LEFT} } },
424 { 6, 2, { {3, 4, PUZZLE_TILE_RIGHT},
425 {4, 2, PUZZLE_TILE_LEFT} } },
426 { 6, 2, { {3, 2, PUZZLE_TILE_DOWN},
427 {4, 4, PUZZLE_TILE_UP} } },
428 { 7, 4, { {3, 2, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
429 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
430 {3, 4, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
431 {4, 4, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
432 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
433 {4, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
434 {3, 6, PUZZLE_TILE_UP} } },
435 { 7, 3, { {2, 2, PUZZLE_TILE_RIGHT},
436 {4, 1, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
437 {5, 4, PUZZLE_TILE_LEFT} } },
438 { 7, 4, { {3, 0, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
439 {5, 0, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
440 {2, 7, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
441 {4, 7, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
444 /*****************************************************************************
445 * jewels_init() initializes jewels data structures.
446 ******************************************************************************/
447 static void jewels_init(struct game_context* bj) {
448 /* seed the rand generator */
449 rb->srand(*rb->current_tick);
451 /* check for resumed game */
452 if(bj->resume) {
453 bj->resume = false;
454 return;
457 /* reset scoring */
458 bj->level = 1;
459 bj->score = 0;
460 bj->segments = 0;
462 /* clear playing board */
463 rb->memset(bj->playboard, 0, sizeof(bj->playboard));
466 /*****************************************************************************
467 * jewels_setcolors() set the foreground and background colors.
468 ******************************************************************************/
469 static inline void jewels_setcolors(void) {
470 #ifdef HAVE_LCD_COLOR
471 rb->lcd_set_background(LCD_RGBPACK(49, 26, 26));
472 rb->lcd_set_foreground(LCD_RGBPACK(210, 181, 181));
473 #endif
476 /*****************************************************************************
477 * jewels_drawboard() redraws the entire game board.
478 ******************************************************************************/
479 static void jewels_drawboard(struct game_context* bj) {
480 int i, j;
481 int w, h;
482 unsigned int tempscore;
483 char *title = "Level";
484 char str[10];
486 tempscore = (bj->score>LEVEL_PTS ? LEVEL_PTS : bj->score);
488 /* clear screen */
489 rb->lcd_clear_display();
491 /* dispay playing board */
492 for(i=0; i<BJ_HEIGHT-1; i++){
493 for(j=0; j<BJ_WIDTH; j++){
494 #ifdef HAVE_LCD_COLOR
495 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
496 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
497 TILE_WIDTH, TILE_HEIGHT);
498 rb->lcd_bitmap_transparent_part(jewels,
499 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
500 TILE_WIDTH, j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
501 TILE_WIDTH, TILE_HEIGHT);
502 #else
503 rb->lcd_bitmap_part(jewels,
504 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
505 TILE_WIDTH, j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
506 TILE_WIDTH, TILE_HEIGHT);
507 #endif
511 #if LCD_WIDTH > LCD_HEIGHT /* horizontal layout */
513 /* draw separator lines */
514 jewels_setcolors();
515 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, LCD_HEIGHT-1);
516 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, 18);
517 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, LCD_HEIGHT-10);
519 /* draw progress bar */
520 #ifdef HAVE_LCD_COLOR
521 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
522 #endif
523 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
524 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
525 tempscore/LEVEL_PTS),
526 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
527 ((LCD_HEIGHT-10)-18)*tempscore/LEVEL_PTS);
528 #ifdef HAVE_LCD_COLOR
529 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
530 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
531 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
532 tempscore/LEVEL_PTS)+1,
533 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
534 ((LCD_HEIGHT-10)-18)*tempscore/LEVEL_PTS-1);
535 jewels_setcolors();
536 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
537 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
538 tempscore/LEVEL_PTS),
539 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
540 ((LCD_HEIGHT-10)-18)*tempscore/LEVEL_PTS+1);
541 #endif
543 /* print text */
544 rb->lcd_getstringsize(title, &w, &h);
545 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 1, title);
547 rb->snprintf(str, 4, "%d", bj->level);
548 rb->lcd_getstringsize(str, &w, &h);
549 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 10, str);
551 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
552 rb->lcd_getstringsize(str, &w, &h);
553 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2,
554 LCD_HEIGHT-8, str);
556 #elif LCD_WIDTH < LCD_HEIGHT /* vertical layout */
558 /* draw separator lines */
559 jewels_setcolors();
560 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
561 rb->lcd_hline(0, LCD_WIDTH-1, LCD_HEIGHT-14);
562 rb->lcd_vline(LCD_WIDTH/2, LCD_HEIGHT-14, LCD_HEIGHT-1);
564 /* draw progress bar */
565 #ifdef HAVE_LCD_COLOR
566 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
567 #endif
568 rb->lcd_fillrect(0, (8*TILE_HEIGHT+YOFS)
569 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
570 LCD_WIDTH*tempscore/LEVEL_PTS,
571 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
572 #ifdef HAVE_LCD_COLOR
573 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
574 rb->lcd_drawrect(1, (8*TILE_HEIGHT+YOFS)
575 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4+1,
576 LCD_WIDTH*tempscore/LEVEL_PTS-1,
577 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2-2);
578 jewels_setcolors();
579 rb->lcd_drawrect(0, (8*TILE_HEIGHT+YOFS)
580 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
581 LCD_WIDTH*tempscore/LEVEL_PTS+1,
582 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
583 #endif
585 /* print text */
586 rb->snprintf(str, 10, "%s %d", title, bj->level);
587 rb->lcd_putsxy(1, LCD_HEIGHT-10, str);
589 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
590 rb->lcd_getstringsize(str, &w, &h);
591 rb->lcd_putsxy((LCD_WIDTH-2)-w, LCD_HEIGHT-10, str);
593 #else /* square layout */
595 /* draw separator lines */
596 jewels_setcolors();
597 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
598 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, 8*TILE_HEIGHT+YOFS);
599 rb->lcd_vline(LCD_WIDTH/2, 8*TILE_HEIGHT+YOFS, LCD_HEIGHT-1);
601 /* draw progress bar */
602 #ifdef HAVE_LCD_COLOR
603 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
604 #endif
605 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
606 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
607 *tempscore/LEVEL_PTS,
608 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
609 (8*TILE_HEIGHT+YOFS)*tempscore/LEVEL_PTS);
610 #ifdef HAVE_LCD_COLOR
611 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
612 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
613 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
614 *tempscore/LEVEL_PTS+1,
615 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
616 (8*TILE_HEIGHT+YOFS)*tempscore/LEVEL_PTS-1);
617 jewels_setcolors();
618 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
619 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
620 *tempscore/LEVEL_PTS,
621 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
622 (8*TILE_HEIGHT+YOFS)*tempscore/LEVEL_PTS+1);
623 #endif
625 /* print text */
626 rb->snprintf(str, 10, "%s %d", title, bj->level);
627 rb->lcd_putsxy(1, LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
629 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
630 rb->lcd_getstringsize(str, &w, &h);
631 rb->lcd_putsxy((LCD_WIDTH-2)-w,
632 LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
634 #endif /* layout */
636 rb->lcd_update();
639 /*****************************************************************************
640 * jewels_showmenu() displays the chosen menu after performing the chosen
641 * menu command.
642 ******************************************************************************/
643 static enum menu_result jewels_showmenu(struct jewels_menu* menu,
644 enum menu_cmd cmd) {
645 int i;
646 int w, h;
647 int firstline;
648 int adj;
649 int extraline = LCD_HEIGHT <= ((menu->itemcnt+2)*FONT_HEIGHT) ? 0 : 1;
651 /* handle menu command */
652 switch(cmd) {
653 case MCMD_NEXT:
654 menu->selected = (menu->selected+1)%menu->itemcnt;
655 break;
657 case MCMD_PREV:
658 menu->selected = (menu->selected-1+menu->itemcnt)%menu->itemcnt;
659 break;
661 case MCMD_SELECT:
662 return menu->items[menu->selected].res;
664 default:
665 break;
668 /* clear menu area */
669 firstline = (LCD_HEIGHT/FONT_HEIGHT-(menu->itemcnt+3))/2;
671 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
672 rb->lcd_fillrect((LCD_WIDTH-MENU_WIDTH)/2, firstline*FONT_HEIGHT,
673 MENU_WIDTH, (menu->itemcnt+3)*FONT_HEIGHT);
674 rb->lcd_set_drawmode(DRMODE_SOLID);
676 if(menu->hasframe) {
677 rb->lcd_drawrect((LCD_WIDTH-MENU_WIDTH)/2-1, firstline*FONT_HEIGHT-1,
678 MENU_WIDTH+2, (menu->itemcnt+3)*FONT_HEIGHT+2);
679 rb->lcd_hline((LCD_WIDTH-MENU_WIDTH)/2-1,
680 (LCD_WIDTH-MENU_WIDTH)/2-1+MENU_WIDTH+2,
681 (firstline+1)*FONT_HEIGHT);
684 /* draw menu items */
685 rb->lcd_getstringsize(menu->title, &w, &h);
686 rb->lcd_putsxy((LCD_WIDTH-w)/2, firstline*FONT_HEIGHT, menu->title);
688 for(i=0; i<menu->itemcnt; i++) {
689 if(i == menu->selected) {
690 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
692 rb->lcd_putsxy((LCD_WIDTH-MENU_WIDTH)/2,
693 (firstline+i+1+extraline)*FONT_HEIGHT,
694 menu->items[i].text);
695 if(i == menu->selected) {
696 rb->lcd_set_drawmode(DRMODE_SOLID);
700 adj = (firstline == 0 ? 0 : 1);
701 rb->lcd_update_rect((LCD_WIDTH-MENU_WIDTH)/2-1, firstline*FONT_HEIGHT-adj,
702 MENU_WIDTH+2, (menu->itemcnt+3)*FONT_HEIGHT+2*adj);
703 return MRES_NONE;
706 /*****************************************************************************
707 * jewels_putjewels() makes the jewels fall to fill empty spots and adds
708 * new random jewels at the empty spots at the top of each row.
709 ******************************************************************************/
710 static void jewels_putjewels(struct game_context* bj){
711 int i, j, k;
712 bool mark, done;
713 long lasttick, currenttick;
715 /* loop to make all the jewels fall */
716 while(true) {
717 /* mark falling jewels and add new jewels to hidden top row*/
718 mark = false;
719 done = true;
720 for(j=0; j<BJ_WIDTH; j++) {
721 if(bj->playboard[1][j].type == 0) {
722 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
724 for(i=BJ_HEIGHT-2; i>=0; i--) {
725 if(!mark && bj->playboard[i+1][j].type == 0) {
726 mark = true;
727 done = false;
729 if(mark) bj->playboard[i][j].falling = true;
731 /*if(bj->playboard[1][j].falling) {
732 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
733 bj->playboard[0][j].falling = true;
735 mark = false;
738 /* break if there are no falling jewels */
739 if(done) break;
741 /* animate falling jewels */
742 lasttick = *rb->current_tick;
744 for(k=1; k<=8; k++) {
745 for(i=BJ_HEIGHT-2; i>=0; i--) {
746 for(j=0; j<BJ_WIDTH; j++) {
747 if(bj->playboard[i][j].falling &&
748 bj->playboard[i][j].type != 0) {
749 /* clear old position */
750 #ifdef HAVE_LCD_COLOR
751 if(i == 0 && YOFS) {
752 rb->lcd_set_foreground(rb->lcd_get_background());
753 } else {
754 rb->lcd_set_foreground(jewels_bkgd[(i-1+j)%2]);
756 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
757 TILE_WIDTH, TILE_HEIGHT);
758 if(bj->playboard[i+1][j].type == 0) {
759 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
760 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
761 TILE_WIDTH, TILE_HEIGHT);
763 #else
764 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
765 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
766 TILE_WIDTH, TILE_HEIGHT);
767 if(bj->playboard[i+1][j].type == 0) {
768 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
769 TILE_WIDTH, TILE_HEIGHT);
771 rb->lcd_set_drawmode(DRMODE_SOLID);
772 #endif
774 /* draw new position */
775 #ifdef HAVE_LCD_COLOR
776 rb->lcd_bitmap_transparent_part(jewels, 0,
777 TILE_HEIGHT*(bj->playboard[i][j].type),
778 TILE_WIDTH, j*TILE_WIDTH,
779 (i-1)*TILE_HEIGHT+YOFS+
780 ((((TILE_HEIGHT<<10)*k)/8)>>10),
781 TILE_WIDTH, TILE_HEIGHT);
782 #else
783 rb->lcd_bitmap_part(jewels, 0,
784 TILE_HEIGHT*(bj->playboard[i][j].type),
785 TILE_WIDTH, j*TILE_WIDTH,
786 (i-1)*TILE_HEIGHT+YOFS+
787 ((((TILE_HEIGHT<<10)*k)/8)>>10),
788 TILE_WIDTH, TILE_HEIGHT);
789 #endif
794 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
795 jewels_setcolors();
797 /* framerate limiting */
798 currenttick = *rb->current_tick;
799 if(currenttick-lasttick < HZ/MAX_FPS) {
800 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
801 } else {
802 rb->yield();
804 lasttick = currenttick;
807 /* shift jewels down */
808 for(j=0; j<BJ_WIDTH; j++) {
809 for(i=BJ_HEIGHT-1; i>=1; i--) {
810 if(bj->playboard[i-1][j].falling) {
811 bj->playboard[i][j].type = bj->playboard[i-1][j].type;
816 /* clear out top row */
817 for(j=0; j<BJ_WIDTH; j++) {
818 bj->playboard[0][j].type = 0;
821 /* mark everything not falling */
822 for(i=0; i<BJ_HEIGHT; i++) {
823 for(j=0; j<BJ_WIDTH; j++) {
824 bj->playboard[i][j].falling = false;
830 /*****************************************************************************
831 * jewels_clearjewels() finds all the connected rows and columns and
832 * calculates and returns the points earned.
833 ******************************************************************************/
834 static unsigned int jewels_clearjewels(struct game_context* bj) {
835 int i, j;
836 int last, run;
837 unsigned int points = 0;
839 /* check for connected rows */
840 for(i=1; i<BJ_HEIGHT; i++) {
841 last = 0;
842 run = 1;
843 for(j=0; j<BJ_WIDTH; j++) {
844 if(bj->playboard[i][j].type == last &&
845 bj->playboard[i][j].type != 0 &&
846 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
847 run++;
849 if(run == 3) {
850 bj->segments++;
851 points += bj->segments;
852 bj->playboard[i][j].delete = true;
853 bj->playboard[i][j-1].delete = true;
854 bj->playboard[i][j-2].delete = true;
855 } else if(run > 3) {
856 points++;
857 bj->playboard[i][j].delete = true;
859 } else {
860 run = 1;
861 last = bj->playboard[i][j].type;
866 /* check for connected columns */
867 for(j=0; j<BJ_WIDTH; j++) {
868 last = 0;
869 run = 1;
870 for(i=1; i<BJ_HEIGHT; i++) {
871 if(bj->playboard[i][j].type != 0 &&
872 bj->playboard[i][j].type == last &&
873 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
874 run++;
876 if(run == 3) {
877 bj->segments++;
878 points += bj->segments;
879 bj->playboard[i][j].delete = true;
880 bj->playboard[i-1][j].delete = true;
881 bj->playboard[i-2][j].delete = true;
882 } else if(run > 3) {
883 points++;
884 bj->playboard[i][j].delete = true;
886 } else {
887 run = 1;
888 last = bj->playboard[i][j].type;
893 /* clear deleted jewels */
894 for(i=1; i<BJ_HEIGHT; i++) {
895 for(j=0; j<BJ_WIDTH; j++) {
896 if(bj->playboard[i][j].delete) {
897 bj->playboard[i][j].delete = false;
898 bj->playboard[i][j].type = 0;
903 return points;
906 /*****************************************************************************
907 * jewels_runboard() runs the board until it settles in a fixed state and
908 * returns points earned.
909 ******************************************************************************/
910 static unsigned int jewels_runboard(struct game_context* bj) {
911 unsigned int points = 0;
912 unsigned int ret;
914 bj->segments = 0;
916 while((ret = jewels_clearjewels(bj)) > 0) {
917 points += ret;
918 jewels_drawboard(bj);
919 jewels_putjewels(bj);
922 return points;
925 /*****************************************************************************
926 * jewels_swapjewels() swaps two jewels as long as it results in points and
927 * returns points earned.
928 ******************************************************************************/
929 static unsigned int jewels_swapjewels(struct game_context* bj,
930 int x, int y, int direc) {
931 int k;
932 int horzmod, vertmod;
933 int movelen = 0;
934 bool undo = false;
935 unsigned int points = 0;
936 long lasttick, currenttick;
938 /* check for invalid parameters */
939 if(x < 0 || x >= BJ_WIDTH || y < 0 || y >= BJ_HEIGHT-1 ||
940 direc < SWAP_UP || direc > SWAP_LEFT) {
941 return 0;
944 /* check for invalid directions */
945 if((x == 0 && direc == SWAP_LEFT) ||
946 (x == BJ_WIDTH-1 && direc == SWAP_RIGHT) ||
947 (y == 0 && direc == SWAP_UP) ||
948 (y == BJ_HEIGHT-2 && direc == SWAP_DOWN)) {
949 return 0;
952 /* set direction variables */
953 horzmod = 0;
954 vertmod = 0;
955 switch(direc) {
956 case SWAP_UP:
957 vertmod = -1;
958 movelen = TILE_HEIGHT;
959 break;
960 case SWAP_RIGHT:
961 horzmod = 1;
962 movelen = TILE_WIDTH;
963 break;
964 case SWAP_DOWN:
965 vertmod = 1;
966 movelen = TILE_HEIGHT;
967 break;
968 case SWAP_LEFT:
969 horzmod = -1;
970 movelen = TILE_WIDTH;
971 break;
974 while(true) {
975 lasttick = *rb->current_tick;
977 /* animate swapping jewels */
978 for(k=0; k<=8; k++) {
979 /* clear old position */
980 #ifdef HAVE_LCD_COLOR
981 rb->lcd_set_foreground(jewels_bkgd[(x+y)%2]);
982 rb->lcd_fillrect(x*TILE_WIDTH,
983 y*TILE_HEIGHT+YOFS,
984 TILE_WIDTH, TILE_HEIGHT);
985 rb->lcd_set_foreground(jewels_bkgd[(x+horzmod+y+vertmod)%2]);
986 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
987 (y+vertmod)*TILE_HEIGHT+YOFS,
988 TILE_WIDTH, TILE_HEIGHT);
989 #else
990 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
991 rb->lcd_fillrect(x*TILE_WIDTH,
992 y*TILE_HEIGHT+YOFS,
993 TILE_WIDTH, TILE_HEIGHT);
994 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
995 (y+vertmod)*TILE_HEIGHT+YOFS,
996 TILE_WIDTH, TILE_HEIGHT);
997 rb->lcd_set_drawmode(DRMODE_SOLID);
998 #endif
999 /* draw new position */
1000 #ifdef HAVE_LCD_COLOR
1001 rb->lcd_bitmap_transparent_part(jewels,
1002 0, TILE_HEIGHT*(bj->playboard
1003 [y+1+vertmod][x+horzmod].type), TILE_WIDTH,
1004 (x+horzmod)*TILE_WIDTH-horzmod*
1005 ((((movelen<<10)*k)/8)>>10),
1006 (y+vertmod)*TILE_HEIGHT-vertmod*
1007 ((((movelen<<10)*k)/8)>>10)+YOFS,
1008 TILE_WIDTH, TILE_HEIGHT);
1009 rb->lcd_bitmap_transparent_part(jewels,
1010 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
1011 TILE_WIDTH, x*TILE_WIDTH+horzmod*
1012 ((((movelen<<10)*k)/8)>>10),
1013 y*TILE_HEIGHT+vertmod*
1014 ((((movelen<<10)*k)/8)>>10)+YOFS,
1015 TILE_WIDTH, TILE_HEIGHT);
1016 #else
1017 rb->lcd_bitmap_part(jewels,
1018 0, TILE_HEIGHT*(bj->playboard
1019 [y+1+vertmod][x+horzmod].type), TILE_WIDTH,
1020 (x+horzmod)*TILE_WIDTH-horzmod*
1021 ((((movelen<<10)*k)/8)>>10),
1022 (y+vertmod)*TILE_HEIGHT-vertmod*
1023 ((((movelen<<10)*k)/8)>>10)+YOFS,
1024 TILE_WIDTH, TILE_HEIGHT);
1025 rb->lcd_set_drawmode(DRMODE_FG);
1026 rb->lcd_bitmap_part(jewels,
1027 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
1028 TILE_WIDTH, x*TILE_WIDTH+horzmod*
1029 ((((movelen<<10)*k)/8)>>10),
1030 y*TILE_HEIGHT+vertmod*
1031 ((((movelen<<10)*k)/8)>>10)+YOFS,
1032 TILE_WIDTH, TILE_HEIGHT);
1033 rb->lcd_set_drawmode(DRMODE_SOLID);
1034 #endif
1036 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
1037 jewels_setcolors();
1039 /* framerate limiting */
1040 currenttick = *rb->current_tick;
1041 if(currenttick-lasttick < HZ/MAX_FPS) {
1042 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
1043 } else {
1044 rb->yield();
1046 lasttick = currenttick;
1049 /* swap jewels */
1050 int temp = bj->playboard[y+1][x].type;
1051 bj->playboard[y+1][x].type =
1052 bj->playboard[y+1+vertmod][x+horzmod].type;
1053 bj->playboard[y+1+vertmod][x+horzmod].type = temp;
1055 if(undo) break;
1057 points = jewels_runboard(bj);
1058 if(points == 0) {
1059 undo = true;
1060 } else {
1061 break;
1065 return points;
1068 /*****************************************************************************
1069 * jewels_movesavail() uses pattern matching to see if there are any
1070 * available move left.
1071 ******************************************************************************/
1072 static bool jewels_movesavail(struct game_context* bj) {
1073 int i, j;
1074 bool moves = false;
1075 int mytype;
1077 for(i=1; i<BJ_HEIGHT; i++) {
1078 for(j=0; j<BJ_WIDTH; j++) {
1079 mytype = bj->playboard[i][j].type;
1080 if(mytype == 0 || mytype > MAX_NUM_JEWELS) continue;
1082 /* check horizontal patterns */
1083 if(j <= BJ_WIDTH-3) {
1084 if(i > 1) {
1085 if(bj->playboard[i-1][j+1].type == mytype) {
1086 if(bj->playboard[i-1][j+2].type == mytype)
1087 {moves = true; break;}
1088 if(bj->playboard[i][j+2].type == mytype)
1089 {moves = true; break;}
1091 if(bj->playboard[i][j+1].type == mytype) {
1092 if(bj->playboard[i-1][j+2].type == mytype)
1093 {moves = true; break;}
1097 if(j <= BJ_WIDTH-4) {
1098 if(bj->playboard[i][j+3].type == mytype) {
1099 if(bj->playboard[i][j+1].type == mytype)
1100 {moves = true; break;}
1101 if(bj->playboard[i][j+2].type == mytype)
1102 {moves = true; break;}
1106 if(i < BJ_HEIGHT-1) {
1107 if(bj->playboard[i][j+1].type == mytype) {
1108 if(bj->playboard[i+1][j+2].type == mytype)
1109 {moves = true; break;}
1111 if(bj->playboard[i+1][j+1].type == mytype) {
1112 if(bj->playboard[i][j+2].type == mytype)
1113 {moves = true; break;}
1114 if(bj->playboard[i+1][j+2].type == mytype)
1115 {moves = true; break;}
1120 /* check vertical patterns */
1121 if(i <= BJ_HEIGHT-3) {
1122 if(j > 0) {
1123 if(bj->playboard[i+1][j-1].type == mytype) {
1124 if(bj->playboard[i+2][j-1].type == mytype)
1125 {moves = true; break;}
1126 if(bj->playboard[i+2][j].type == mytype)
1127 {moves = true; break;}
1129 if(bj->playboard[i+1][j].type == mytype) {
1130 if(bj->playboard[i+2][j-1].type == mytype)
1131 {moves = true; break;}
1135 if(i <= BJ_HEIGHT-4) {
1136 if(bj->playboard[i+3][j].type == mytype) {
1137 if(bj->playboard[i+1][j].type == mytype)
1138 {moves = true; break;}
1139 if(bj->playboard[i+2][j].type == mytype)
1140 {moves = true; break;}
1144 if(j < BJ_WIDTH-1) {
1145 if(bj->playboard[i+1][j].type == mytype) {
1146 if(bj->playboard[i+2][j+1].type == mytype)
1147 {moves = true; break;}
1149 if(bj->playboard[i+1][j+1].type == mytype) {
1150 if(bj->playboard[i+2][j].type == mytype)
1151 {moves = true; break;}
1152 if (bj->playboard[i+2][j+1].type == mytype)
1153 {moves = true; break;}
1159 if(moves) break;
1162 return moves;
1165 /*****************************************************************************
1166 * jewels_puzzle_is_finished(bj) checks if the puzzle is finished.
1167 ******************************************************************************/
1168 static int jewels_puzzle_is_finished(struct game_context* bj) {
1169 unsigned int i, j;
1170 for(i=0; i<BJ_HEIGHT; i++) {
1171 for(j=0; j<BJ_WIDTH; j++) {
1172 int mytype = bj->playboard[i][j].type;
1173 if(mytype>MAX_NUM_JEWELS) {
1174 mytype -= MAX_NUM_JEWELS;
1175 if(mytype&PUZZLE_TILE_UP) {
1176 if(i==0 || bj->playboard[i-1][j].type<=MAX_NUM_JEWELS ||
1177 !((bj->playboard[i-1][j].type-MAX_NUM_JEWELS)
1178 &PUZZLE_TILE_DOWN))
1179 return 0;
1181 if(mytype&PUZZLE_TILE_DOWN) {
1182 if(i==BJ_HEIGHT-1 ||
1183 bj->playboard[i+1][j].type<=MAX_NUM_JEWELS ||
1184 !((bj->playboard[i+1][j].type-MAX_NUM_JEWELS)
1185 &PUZZLE_TILE_UP))
1186 return 0;
1188 if(mytype&PUZZLE_TILE_LEFT) {
1189 if(j==0 || bj->playboard[i][j-1].type<=MAX_NUM_JEWELS ||
1190 !((bj->playboard[i][j-1].type-MAX_NUM_JEWELS)
1191 &PUZZLE_TILE_RIGHT))
1192 return 0;
1194 if(mytype&PUZZLE_TILE_RIGHT) {
1195 if(j==BJ_WIDTH-1 ||
1196 bj->playboard[i][j+1].type<=MAX_NUM_JEWELS ||
1197 !((bj->playboard[i][j+1].type-MAX_NUM_JEWELS)
1198 &PUZZLE_TILE_LEFT))
1199 return 0;
1204 return 1;
1207 /*****************************************************************************
1208 * jewels_initlevel() initialises a level.
1209 ******************************************************************************/
1210 static unsigned int jewels_initlevel(struct game_context* bj) {
1211 unsigned int points = 0;
1213 switch(bj->type) {
1214 case GAME_TYPE_NORMAL:
1215 bj->num_jewels = MAX_NUM_JEWELS;
1216 break;
1218 case GAME_TYPE_PUZZLE:
1220 unsigned int i, j;
1221 struct puzzle_tile *tile;
1223 bj->num_jewels = puzzle_levels[bj->level-1].num_jewels;
1225 for(i=0; i<BJ_HEIGHT; i++) {
1226 for(j=0; j<BJ_WIDTH; j++) {
1227 bj->playboard[i][j].type = (rb->rand()%bj->num_jewels)+1;
1228 bj->playboard[i][j].falling = false;
1229 bj->playboard[i][j].delete = false;
1232 jewels_runboard(bj);
1233 tile = puzzle_levels[bj->level-1].tiles;
1234 for(i=0; i<puzzle_levels[bj->level-1].num_tiles; i++, tile++) {
1235 bj->playboard[tile->y+1][tile->x].type = MAX_NUM_JEWELS
1236 +tile->tile_type;
1239 break;
1242 jewels_drawboard(bj);
1244 /* run the play board */
1245 jewels_putjewels(bj);
1246 points += jewels_runboard(bj);
1247 return points;
1250 /*****************************************************************************
1251 * jewels_nextlevel() advances the game to the next level and returns
1252 * points earned.
1253 ******************************************************************************/
1254 static void jewels_nextlevel(struct game_context* bj) {
1255 int i, x, y;
1256 unsigned int points = 0;
1258 switch(bj->type) {
1259 case GAME_TYPE_NORMAL:
1260 /* roll over score, change and display level */
1261 while(bj->score >= LEVEL_PTS) {
1262 bj->score -= LEVEL_PTS;
1263 bj->level++;
1264 rb->splash(HZ*2, "Level %d", bj->level);
1265 jewels_drawboard(bj);
1268 /* randomly clear some jewels */
1269 for(i=0; i<16; i++) {
1270 x = rb->rand()%8;
1271 y = rb->rand()%8;
1273 if(bj->playboard[y][x].type != 0) {
1274 points++;
1275 bj->playboard[y][x].type = 0;
1278 break;
1280 case GAME_TYPE_PUZZLE:
1281 bj->level++;
1282 if(bj->level>NUM_PUZZLE_LEVELS) {
1283 rb->splash(HZ*2, "You win!");
1284 bj->level = 1;
1285 } else {
1286 rb->splash(HZ*2, "Level %d", bj->level);
1288 break;
1291 points += jewels_initlevel(bj);
1292 bj->score += points;
1295 /*****************************************************************************
1296 * jewels_recordscore() inserts a high score into the high scores list and
1297 * returns the high score position.
1298 ******************************************************************************/
1299 static int jewels_recordscore(struct game_context* bj) {
1300 int i;
1301 int position = 0;
1302 unsigned int current, temp;
1304 /* calculate total score */
1305 current = (bj->level-1)*LEVEL_PTS+bj->score;
1306 if(current <= 0) return 0;
1308 /* insert the current score into the high scores */
1309 for(i=0; i<NUM_SCORES; i++) {
1310 if(current >= bj->highscores[i]) {
1311 if(!position) {
1312 position = i+1;
1313 bj->dirty = true;
1315 temp = bj->highscores[i];
1316 bj->highscores[i] = current;
1317 current = temp;
1321 return position;
1324 /*****************************************************************************
1325 * jewels_loadscores() loads the high scores saved file.
1326 ******************************************************************************/
1327 static void jewels_loadscores(struct game_context* bj) {
1328 int fd;
1330 bj->dirty = false;
1332 /* clear high scores */
1333 rb->memset(bj->highscores, 0, sizeof(bj->highscores));
1335 /* open scores file */
1336 fd = rb->open(SCORE_FILE, O_RDONLY);
1337 if(fd < 0) return;
1339 /* read in high scores */
1340 if(rb->read(fd, bj->highscores, sizeof(bj->highscores)) <= 0) {
1341 /* scores are bad, reset */
1342 rb->memset(bj->highscores, 0, sizeof(bj->highscores));
1345 rb->close(fd);
1348 /*****************************************************************************
1349 * jewels_savescores() saves the high scores saved file.
1350 ******************************************************************************/
1351 static void jewels_savescores(struct game_context* bj) {
1352 int fd;
1354 /* write out the high scores to the save file */
1355 fd = rb->open(SCORE_FILE, O_WRONLY|O_CREAT);
1356 rb->write(fd, bj->highscores, sizeof(bj->highscores));
1357 rb->close(fd);
1358 bj->dirty = false;
1361 /*****************************************************************************
1362 * jewels_loadgame() loads the saved game and returns load success.
1363 ******************************************************************************/
1364 static bool jewels_loadgame(struct game_context* bj) {
1365 int fd;
1366 bool loaded = false;
1368 /* open game file */
1369 fd = rb->open(SAVE_FILE, O_RDONLY);
1370 if(fd < 0) return loaded;
1372 /* read in saved game */
1373 while(true) {
1374 if(rb->read(fd, &bj->score, sizeof(bj->score)) <= 0) break;
1375 if(rb->read(fd, &bj->level, sizeof(bj->level)) <= 0) break;
1376 if(rb->read(fd, &bj->type, sizeof(bj->type)) <= 0) break;
1377 if(rb->read(fd, bj->playboard, sizeof(bj->playboard)) <= 0) break;
1378 bj->resume = true;
1379 loaded = true;
1380 break;
1383 rb->close(fd);
1385 /* delete saved file */
1386 rb->remove(SAVE_FILE);
1387 return loaded;
1390 /*****************************************************************************
1391 * jewels_savegame() saves the current game state.
1392 ******************************************************************************/
1393 static void jewels_savegame(struct game_context* bj) {
1394 int fd;
1396 /* write out the game state to the save file */
1397 fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT);
1398 rb->write(fd, &bj->score, sizeof(bj->score));
1399 rb->write(fd, &bj->level, sizeof(bj->level));
1400 rb->write(fd, &bj->type, sizeof(bj->type));
1401 rb->write(fd, bj->playboard, sizeof(bj->playboard));
1402 rb->close(fd);
1404 bj->resume = true;
1407 /*****************************************************************************
1408 * jewels_callback() is the default event handler callback which is called
1409 * on usb connect and shutdown.
1410 ******************************************************************************/
1411 static void jewels_callback(void* param) {
1412 struct game_context* bj = (struct game_context*) param;
1413 if(bj->dirty) {
1414 rb->splash(HZ, "Saving high scores...");
1415 jewels_savescores(bj);
1419 /*****************************************************************************
1420 * jewels_main() is the main game subroutine, it returns the final game status.
1421 ******************************************************************************/
1422 static int jewels_main(struct game_context* bj) {
1423 int i, j;
1424 int w, h;
1425 int button;
1426 char str[18];
1427 bool startgame = false;
1428 bool inmenu = false;
1429 bool selected = false;
1430 enum menu_cmd cmd = MCMD_NONE;
1431 enum menu_result res;
1433 /* the cursor coordinates */
1434 int x=0, y=0;
1436 /* don't resume by default */
1437 bj->resume = false;
1439 /********************
1440 * menu *
1441 ********************/
1442 rb->lcd_clear_display();
1444 while(!startgame) {
1445 res = jewels_showmenu(&bjmenu[0], cmd);
1446 cmd = MCMD_NONE;
1448 rb->snprintf(str, 18, "High Score: %d", bj->highscores[0]);
1449 rb->lcd_getstringsize(str, &w, &h);
1450 rb->lcd_putsxy((LCD_WIDTH-w)/2, LCD_HEIGHT-8, str);
1451 rb->lcd_update();
1453 switch(res) {
1454 case MRES_NEW:
1455 startgame = true;
1456 bj->type = GAME_TYPE_NORMAL;
1457 continue;
1459 case MRES_PUZZLE:
1460 startgame = true;
1461 bj->type = GAME_TYPE_PUZZLE;
1462 continue;
1464 case MRES_RESUME:
1465 if(!jewels_loadgame(bj)) {
1466 rb->splash(HZ*2, "Nothing to resume");
1467 rb->lcd_clear_display();
1468 } else {
1469 startgame = true;
1471 continue;
1473 case MRES_SCORES:
1474 rb->lcd_clear_display();
1476 /* room for a title? */
1477 j = 0;
1478 if(LCD_HEIGHT-NUM_SCORES*8 >= 8) {
1479 rb->snprintf(str, 12, "%s", "High Scores");
1480 rb->lcd_getstringsize(str, &w, &h);
1481 rb->lcd_putsxy((LCD_WIDTH-w)/2, 0, str);
1482 j = 2;
1485 /* print high scores */
1486 for(i=0; i<NUM_SCORES; i++) {
1487 rb->snprintf(str, 11, "#%02d: %d", i+1, bj->highscores[i]);
1488 rb->lcd_puts(0, i+j, str);
1491 rb->lcd_update();
1492 while(true) {
1493 button = rb->button_get(true);
1494 if(button != BUTTON_NONE && !(button&BUTTON_REL)) break;
1496 rb->lcd_clear_display();
1497 continue;
1499 case MRES_HELP:
1500 /* welcome screen to display key bindings */
1501 rb->lcd_clear_display();
1502 rb->snprintf(str, 5, "%s", "Help");
1503 rb->lcd_getstringsize(str, &w, &h);
1504 rb->lcd_putsxy((LCD_WIDTH-w)/2, 0, str);
1505 #if CONFIG_KEYPAD == RECORDER_PAD
1506 rb->lcd_puts(0, 2, "Controls:");
1507 rb->lcd_puts(0, 3, "Directions = move");
1508 rb->lcd_puts(0, 4, "PLAY = select");
1509 rb->lcd_puts(0, 5, "Long PLAY = menu");
1510 rb->lcd_puts(0, 6, "OFF = cancel");
1511 #elif CONFIG_KEYPAD == ONDIO_PAD
1512 rb->lcd_puts(0, 2, "Controls:");
1513 rb->lcd_puts(0, 3, "Directions = move");
1514 rb->lcd_puts(0, 4, "MENU = select");
1515 rb->lcd_puts(0, 5, "Long MENU = menu");
1516 rb->lcd_puts(0, 6, "OFF = cancel");
1517 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
1518 rb->lcd_puts(0, 2, "Controls:");
1519 rb->lcd_puts(0, 3, "Directions = move");
1520 rb->lcd_puts(0, 4, "SELECT = select");
1521 rb->lcd_puts(0, 5, "Long SELECT = menu");
1522 rb->lcd_puts(0, 6, "PLAY = cancel");
1523 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
1524 rb->lcd_puts(0, 2, "Swap pairs of jewels to");
1525 rb->lcd_puts(0, 3, "form connected segments");
1526 rb->lcd_puts(0, 4, "of three or more of the");
1527 rb->lcd_puts(0, 5, "same type.");
1528 rb->lcd_puts(0, 7, "Controls:");
1529 rb->lcd_puts(0, 8, "Directions to move");
1530 rb->lcd_puts(0, 9, "SELECT to select");
1531 rb->lcd_puts(0, 10, "Long SELECT to show menu");
1532 rb->lcd_puts(0, 11, "OFF to cancel");
1533 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
1534 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
1535 rb->lcd_puts(0, 2, "Swap pairs of jewels to");
1536 rb->lcd_puts(0, 3, "form connected segments");
1537 rb->lcd_puts(0, 4, "of three or more of the");
1538 rb->lcd_puts(0, 5, "same type.");
1539 rb->lcd_puts(0, 7, "Controls:");
1540 rb->lcd_puts(0, 8, "Directions or scroll to move");
1541 rb->lcd_puts(0, 9, "SELECT to select");
1542 rb->lcd_puts(0, 10, "Long SELECT to show menu");
1543 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
1544 rb->lcd_puts(0, 2, "Swap pairs of jewels to");
1545 rb->lcd_puts(0, 3, "form connected segments");
1546 rb->lcd_puts(0, 4, "of three or more of the");
1547 rb->lcd_puts(0, 5, "same type.");
1548 rb->lcd_puts(0, 7, "Controls:");
1549 rb->lcd_puts(0, 8, "Directions to move");
1550 rb->lcd_puts(0, 9, "SELECT to select");
1551 rb->lcd_puts(0, 10, "Long SELECT to show menu");
1552 rb->lcd_puts(0, 11, "PLAY to cancel");
1553 #elif CONFIG_KEYPAD == GIGABEAT_PAD \
1554 || CONFIG_KEYPAD == MROBE100_PAD
1555 rb->lcd_puts(0, 2, "Swap pairs of jewels to");
1556 rb->lcd_puts(0, 3, "form connected segments");
1557 rb->lcd_puts(0, 4, "of three or more of the");
1558 rb->lcd_puts(0, 5, "same type.");
1559 rb->lcd_puts(0, 7, "Controls:");
1560 rb->lcd_puts(0, 8, "Directions to move");
1561 rb->lcd_puts(0, 9, "SELECT to select");
1562 rb->lcd_puts(0, 10, "Long SELECT to show menu");
1563 rb->lcd_puts(0, 11, "POWER to cancel");
1564 #elif CONFIG_KEYPAD == SANSA_E200_PAD \
1565 || CONFIG_KEYPAD == SANSA_C200_PAD
1566 rb->lcd_puts(0, 2, "Swap pairs of jewels to");
1567 rb->lcd_puts(0, 3, "form connected segments");
1568 rb->lcd_puts(0, 4, "of three or more of the");
1569 rb->lcd_puts(0, 5, "same type.");
1570 rb->lcd_puts(0, 7, "Controls:");
1571 rb->lcd_puts(0, 8, "Directions to move");
1572 rb->lcd_puts(0, 9, "SELECT to select");
1573 rb->lcd_puts(0, 10, "Long SELECT to show menu");
1574 rb->lcd_puts(0, 11, "POWER to cancel");
1575 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
1576 rb->lcd_puts(0, 2, "Swap pairs of jewels");
1577 rb->lcd_puts(0, 3, "to form connected");
1578 rb->lcd_puts(0, 4, "segments of three or ");
1579 rb->lcd_puts(0, 5, "more of the");
1580 rb->lcd_puts(0, 6, "same type.");
1581 rb->lcd_puts(0, 8, "Controls:");
1582 rb->lcd_puts(0, 9, "Directions or scroll to move");
1583 rb->lcd_puts(0, 10, "PLAY to select");
1584 rb->lcd_puts(0, 11, "Long PLAY for menu");
1585 rb->lcd_puts(0, 12, "POWER to cancel");
1586 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
1587 rb->lcd_puts(0, 2, "Swap pairs of jewels");
1588 rb->lcd_puts(0, 3, "to form connected");
1589 rb->lcd_puts(0, 4, "segments of three or ");
1590 rb->lcd_puts(0, 5, "more of the");
1591 rb->lcd_puts(0, 6, "same type.");
1592 rb->lcd_puts(0, 8, "Controls:");
1593 rb->lcd_puts(0, 9, "Directions or scroll to move");
1594 rb->lcd_puts(0, 10, "PLAY to select");
1595 rb->lcd_puts(0, 11, "Long PLAY for menu");
1596 rb->lcd_puts(0, 12, "REC to cancel");
1597 #elif CONFIG_KEYPAD == COWOND2_PAD
1598 rb->lcd_puts(0, 11, "POWER to cancel");
1599 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
1600 rb->lcd_puts(0, 2, "Swap pairs of jewels to");
1601 rb->lcd_puts(0, 3, "form connected segments");
1602 rb->lcd_puts(0, 4, "of three or more of the");
1603 rb->lcd_puts(0, 5, "same type.");
1604 rb->lcd_puts(0, 7, "Controls:");
1605 rb->lcd_puts(0, 8, "Directions to move");
1606 rb->lcd_puts(0, 9, "SELECT to select");
1607 rb->lcd_puts(0, 10, "Long SELECT to show menu");
1608 rb->lcd_puts(0, 11, "BACK to cancel");
1609 #else
1610 #warning: missing help text.
1611 #endif
1613 #ifdef HAVE_TOUCHPAD
1614 rb->lcd_puts(0, 2, "Swap pairs of jewels to");
1615 rb->lcd_puts(0, 3, "form connected segments");
1616 rb->lcd_puts(0, 4, "of three or more of the");
1617 rb->lcd_puts(0, 5, "same type.");
1618 rb->lcd_puts(0, 7, "Controls:");
1619 rb->lcd_puts(0, 8, "Directions to move");
1620 rb->lcd_puts(0, 9, "CENTER to select");
1621 rb->lcd_puts(0, 10, "Long CENTER to show menu");
1622 #endif
1623 rb->lcd_update();
1624 while(true) {
1625 button = rb->button_get(true);
1626 if(button != BUTTON_NONE && !(button&BUTTON_REL)) break;
1628 rb->lcd_clear_display();
1629 continue;
1631 case MRES_QUIT:
1632 return BJ_QUIT;
1634 default:
1635 break;
1638 /* handle menu button presses */
1639 button = rb->button_get(true);
1640 switch(button){
1641 #ifdef JEWELS_SCROLLWHEEL
1642 case JEWELS_PREV:
1643 case (JEWELS_PREV|BUTTON_REPEAT):
1644 #endif
1645 case JEWELS_UP:
1646 case (JEWELS_UP|BUTTON_REPEAT):
1647 cmd = MCMD_PREV;
1648 break;
1650 #ifdef JEWELS_SCROLLWHEEL
1651 case JEWELS_NEXT:
1652 case (JEWELS_NEXT|BUTTON_REPEAT):
1653 #endif
1654 case JEWELS_DOWN:
1655 case (JEWELS_DOWN|BUTTON_REPEAT):
1656 cmd = MCMD_NEXT;
1657 break;
1659 case JEWELS_SELECT:
1660 case JEWELS_RIGHT:
1661 cmd = MCMD_SELECT;
1662 break;
1664 #ifdef JEWELS_CANCEL
1665 #ifdef JEWELS_RC_CANCEL
1666 case JEWELS_RC_CANCEL:
1667 #endif
1668 case JEWELS_CANCEL:
1669 return BJ_QUIT;
1670 #endif
1672 default:
1673 if(rb->default_event_handler_ex(button, jewels_callback,
1674 (void*) bj) == SYS_USB_CONNECTED)
1675 return BJ_USB;
1676 break;
1680 /********************
1681 * init *
1682 ********************/
1683 jewels_init(bj);
1685 /********************
1686 * setup the board *
1687 ********************/
1688 bj->score += jewels_initlevel(bj);
1689 if (!jewels_movesavail(bj)) {
1690 switch(bj->type) {
1691 case GAME_TYPE_NORMAL:
1692 return BJ_LOSE;
1694 case GAME_TYPE_PUZZLE:
1695 do {
1696 rb->splash(2*HZ, "No more moves!");
1697 bj->score += jewels_initlevel(bj);
1698 } while(!jewels_movesavail(bj));
1699 break;
1703 /**********************
1704 * play *
1705 **********************/
1706 while(true) {
1707 int no_movesavail = false;
1709 if(!inmenu) {
1710 /* refresh the board */
1711 jewels_drawboard(bj);
1713 /* display the cursor */
1714 if(selected) {
1715 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1716 rb->lcd_fillrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1717 TILE_WIDTH, TILE_HEIGHT);
1718 rb->lcd_set_drawmode(DRMODE_SOLID);
1719 } else {
1720 rb->lcd_drawrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1721 TILE_WIDTH, TILE_HEIGHT);
1723 rb->lcd_update_rect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1724 TILE_WIDTH, TILE_HEIGHT);
1725 } else {
1726 res = jewels_showmenu(&bjmenu[1], cmd);
1727 cmd = MCMD_NONE;
1728 switch(res) {
1729 case MRES_RESUME:
1730 inmenu = false;
1731 selected = false;
1732 continue;
1734 case MRES_PLAYBACK:
1735 playback_control(rb, NULL);
1736 rb->lcd_setfont(FONT_SYSFIXED);
1737 inmenu = false;
1738 selected = false;
1739 break;
1741 case MRES_SAVE:
1742 rb->splash(HZ, "Saving game...");
1743 jewels_savegame(bj);
1744 return BJ_END;
1746 case MRES_QUIT:
1747 return BJ_END;
1749 case MRES_EXIT:
1750 return BJ_QUIT_FROM_GAME;
1752 default:
1753 break;
1757 /* handle game button presses */
1758 button = rb->button_get(true);
1759 switch(button){
1760 case JEWELS_LEFT: /* move cursor left */
1761 case (JEWELS_LEFT|BUTTON_REPEAT):
1762 if(!inmenu) {
1763 if(selected) {
1764 bj->score += jewels_swapjewels(bj, x, y, SWAP_LEFT);
1765 selected = false;
1766 if (!jewels_movesavail(bj)) no_movesavail = true;
1767 } else {
1768 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1771 break;
1773 case JEWELS_RIGHT: /* move cursor right */
1774 case (JEWELS_RIGHT|BUTTON_REPEAT):
1775 if(!inmenu) {
1776 if(selected) {
1777 bj->score += jewels_swapjewels(bj, x, y, SWAP_RIGHT);
1778 selected = false;
1779 if (!jewels_movesavail(bj)) no_movesavail = true;
1780 } else {
1781 x = (x+1)%BJ_WIDTH;
1783 } else {
1784 cmd = MCMD_SELECT;
1786 break;
1788 case JEWELS_DOWN: /* move cursor down */
1789 case (JEWELS_DOWN|BUTTON_REPEAT):
1790 if(!inmenu) {
1791 if(selected) {
1792 bj->score += jewels_swapjewels(bj, x, y, SWAP_DOWN);
1793 selected = false;
1794 if (!jewels_movesavail(bj)) no_movesavail = true;
1795 } else {
1796 y = (y+1)%(BJ_HEIGHT-1);
1798 } else {
1799 cmd = MCMD_NEXT;
1801 break;
1803 case JEWELS_UP: /* move cursor up */
1804 case (JEWELS_UP|BUTTON_REPEAT):
1805 if(!inmenu) {
1806 if(selected) {
1807 bj->score += jewels_swapjewels(bj, x, y, SWAP_UP);
1808 selected = false;
1809 if (!jewels_movesavail(bj)) no_movesavail = true;
1810 } else {
1811 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1813 } else {
1814 cmd = MCMD_PREV;
1816 break;
1818 #ifdef JEWELS_SCROLLWHEEL
1819 case JEWELS_PREV: /* scroll backwards */
1820 case (JEWELS_PREV|BUTTON_REPEAT):
1821 if(!inmenu) {
1822 if(!selected) {
1823 if(x == 0) {
1824 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1826 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1828 } else {
1829 cmd = MCMD_PREV;
1831 break;
1833 case JEWELS_NEXT: /* scroll forwards */
1834 case (JEWELS_NEXT|BUTTON_REPEAT):
1835 if(!inmenu) {
1836 if(!selected) {
1837 if(x == BJ_WIDTH-1) {
1838 y = (y+1)%(BJ_HEIGHT-1);
1840 x = (x+1)%BJ_WIDTH;
1842 } else {
1843 cmd = MCMD_NEXT;
1845 break;
1846 #endif
1848 case JEWELS_SELECT: /* toggle selected */
1849 if(!inmenu) {
1850 selected = !selected;
1851 } else {
1852 cmd = MCMD_SELECT;
1854 break;
1856 case (JEWELS_SELECT|BUTTON_REPEAT): /* show menu */
1857 if(!inmenu) inmenu = true;
1858 break;
1860 #ifdef JEWELS_CANCEL
1861 #ifdef JEWELS_RC_CANCEL
1862 case JEWELS_RC_CANCEL:
1863 #endif
1864 case JEWELS_CANCEL: /* end game */
1865 return BJ_END;
1866 break;
1867 #endif
1869 default:
1870 if(rb->default_event_handler_ex(button, jewels_callback,
1871 (void*) bj) == SYS_USB_CONNECTED)
1872 return BJ_USB;
1873 break;
1876 if (no_movesavail) {
1877 switch(bj->type) {
1878 case GAME_TYPE_NORMAL:
1879 return BJ_LOSE;
1881 case GAME_TYPE_PUZZLE:
1882 do {
1883 rb->splash(2*HZ, "No more moves!");
1884 bj->score += jewels_initlevel(bj);
1885 } while(!jewels_movesavail(bj));
1886 break;
1890 switch(bj->type) {
1891 case GAME_TYPE_NORMAL:
1892 if(bj->score >= LEVEL_PTS)
1893 jewels_nextlevel(bj);
1894 break;
1896 case GAME_TYPE_PUZZLE:
1897 if(jewels_puzzle_is_finished(bj))
1898 jewels_nextlevel(bj);
1899 break;
1904 /*****************************************************************************
1905 * plugin entry point.
1906 ******************************************************************************/
1907 enum plugin_status plugin_start(const struct plugin_api* api, const void* parameter) {
1908 struct game_context bj;
1909 bool exit = false;
1910 int position;
1911 char str[19];
1913 /* plugin init */
1914 (void)parameter;
1915 rb = api;
1916 /* end of plugin init */
1918 /* load high scores */
1919 jewels_loadscores(&bj);
1921 rb->lcd_setfont(FONT_SYSFIXED);
1922 #if LCD_DEPTH > 1
1923 rb->lcd_set_backdrop(NULL);
1924 #endif
1925 jewels_setcolors();
1927 while(!exit) {
1928 switch(jewels_main(&bj)){
1929 case BJ_LOSE:
1930 rb->splash(HZ*2, "No more moves!");
1931 /* fall through to BJ_END */
1933 case BJ_END:
1934 if(!bj.resume) {
1935 if((position = jewels_recordscore(&bj))) {
1936 rb->snprintf(str, 19, "New high score #%d!", position);
1937 rb->splash(HZ*2, str);
1940 break;
1942 case BJ_USB:
1943 rb->lcd_setfont(FONT_UI);
1944 return PLUGIN_USB_CONNECTED;
1946 case BJ_QUIT:
1947 if(bj.dirty) {
1948 rb->splash(HZ, "Saving high scores...");
1949 jewels_savescores(&bj);
1951 exit = true;
1952 break;
1954 case BJ_QUIT_FROM_GAME:
1955 if(!bj.resume) {
1956 if((position = jewels_recordscore(&bj))) {
1957 rb->snprintf(str, 19, "New high score #%d!", position);
1958 rb->splash(HZ*2, str);
1961 if(bj.dirty) {
1962 rb->splash(HZ, "Saving high scores...");
1963 jewels_savescores(&bj);
1965 exit = true;
1966 break;
1968 default:
1969 break;
1973 rb->lcd_setfont(FONT_UI);
1974 return PLUGIN_OK;
1977 #endif /* HAVE_LCD_BITMAP */