Renamed Bejeweled to Jewels, for trademark reasons.
[kugel-rb.git] / apps / plugins / jewels.c
blob2fe86e88266ca0782232ff46c0e36feab3ba1d70
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 * All files in this archive are subject to the GNU General Public License.
15 * See the file COPYING in the source tree root for full license agreement.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include "plugin.h"
24 #ifdef HAVE_LCD_BITMAP
26 PLUGIN_HEADER
28 /* button definitions, every keypad must only have directions & select */
29 #if CONFIG_KEYPAD == RECORDER_PAD
30 #define JEWELS_UP BUTTON_UP
31 #define JEWELS_DOWN BUTTON_DOWN
32 #define JEWELS_LEFT BUTTON_LEFT
33 #define JEWELS_RIGHT BUTTON_RIGHT
34 #define JEWELS_SELECT BUTTON_PLAY
35 #define JEWELS_CANCEL BUTTON_OFF
37 #elif CONFIG_KEYPAD == ONDIO_PAD
38 #define JEWELS_UP BUTTON_UP
39 #define JEWELS_DOWN BUTTON_DOWN
40 #define JEWELS_LEFT BUTTON_LEFT
41 #define JEWELS_RIGHT BUTTON_RIGHT
42 #define JEWELS_SELECT BUTTON_MENU
43 #define JEWELS_CANCEL BUTTON_OFF
45 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
46 #define JEWELS_UP BUTTON_UP
47 #define JEWELS_DOWN BUTTON_DOWN
48 #define JEWELS_LEFT BUTTON_LEFT
49 #define JEWELS_RIGHT BUTTON_RIGHT
50 #define JEWELS_SELECT BUTTON_SELECT
51 #define JEWELS_CANCEL BUTTON_OFF
53 #elif (CONFIG_KEYPAD == IPOD_3G_PAD) || (CONFIG_KEYPAD == IPOD_4G_PAD)
54 #define JEWELS_SCROLLWHEEL
55 #define JEWELS_UP BUTTON_MENU
56 #define JEWELS_DOWN BUTTON_PLAY
57 #define JEWELS_LEFT BUTTON_LEFT
58 #define JEWELS_RIGHT BUTTON_RIGHT
59 #define JEWELS_PREV BUTTON_SCROLL_BACK
60 #define JEWELS_NEXT BUTTON_SCROLL_FWD
61 #define JEWELS_SELECT BUTTON_SELECT
63 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
64 #define JEWELS_UP BUTTON_UP
65 #define JEWELS_DOWN BUTTON_DOWN
66 #define JEWELS_LEFT BUTTON_LEFT
67 #define JEWELS_RIGHT BUTTON_RIGHT
68 #define JEWELS_SELECT BUTTON_SELECT
69 #define JEWELS_CANCEL BUTTON_PLAY
71 #elif CONFIG_KEYPAD == IAUDIO_X5_PAD
72 #define JEWELS_UP BUTTON_UP
73 #define JEWELS_DOWN BUTTON_DOWN
74 #define JEWELS_LEFT BUTTON_LEFT
75 #define JEWELS_RIGHT BUTTON_RIGHT
76 #define JEWELS_SELECT BUTTON_SELECT
77 #define JEWELS_CANCEL BUTTON_PLAY
79 #elif CONFIG_KEYPAD == GIGABEAT_PAD
80 #define JEWELS_UP BUTTON_UP
81 #define JEWELS_DOWN BUTTON_DOWN
82 #define JEWELS_LEFT BUTTON_LEFT
83 #define JEWELS_RIGHT BUTTON_RIGHT
84 #define JEWELS_QUIT BUTTON_A
85 #define JEWELS_START BUTTON_POWER
86 #define JEWELS_SELECT BUTTON_SELECT
87 #define JEWELS_RESUME BUTTON_MENU
89 #else
90 #error JEWELS: Unsupported keypad
91 #endif
93 /* use 30x30 tiles (iPod Video) */
94 #if (LCD_HEIGHT == 240) && (LCD_WIDTH == 320)
95 #define TILE_WIDTH 30
96 #define TILE_HEIGHT 30
97 #define YOFS 0
98 #define NUM_SCORES 10
100 /* use 22x22 tiles (H300, iPod Color) */
101 #elif ((LCD_HEIGHT == 176) && (LCD_WIDTH == 220)) || ((LCD_HEIGHT == 320) && (LCD_WIDTH == 240))
102 #define TILE_WIDTH 22
103 #define TILE_HEIGHT 22
104 #define YOFS 0
105 #define NUM_SCORES 10
107 /* use 16x16 tiles (iPod Nano) */
108 #elif (LCD_HEIGHT == 132) && (LCD_WIDTH == 176)
109 #define TILE_WIDTH 16
110 #define TILE_HEIGHT 16
111 #define YOFS 4
112 #define NUM_SCORES 10
114 /* use 16x16 tiles (H100, iAudio X5, iPod 3G, iPod 4G grayscale) */
115 #elif (LCD_HEIGHT == 128) && (LCD_WIDTH == 160)
116 #define TILE_WIDTH 16
117 #define TILE_HEIGHT 16
118 #define YOFS 0
119 #define NUM_SCORES 10
121 /* use 13x13 tiles (iPod Mini) */
122 #elif (LCD_HEIGHT == 110) && (LCD_WIDTH == 138)
123 #define TILE_WIDTH 13
124 #define TILE_HEIGHT 13
125 #define YOFS 6
126 #define NUM_SCORES 10
128 /* use 10x8 tiles (iFP 700) */
129 #elif (LCD_HEIGHT == 64) && (LCD_WIDTH == 128)
130 #define TILE_WIDTH 10
131 #define TILE_HEIGHT 8
132 #define YOFS 0
133 #define NUM_SCORES 8
135 /* use 10x8 tiles (Recorder, Ondio) */
136 #elif (LCD_HEIGHT == 64) && (LCD_WIDTH == 112)
137 #define TILE_WIDTH 10
138 #define TILE_HEIGHT 8
139 #define YOFS 0
140 #define NUM_SCORES 8
142 #else
143 #error JEWELS: Unsupported LCD
144 #endif
146 /* save files */
147 #define SCORE_FILE PLUGIN_DIR "/jewels.score"
148 #define SAVE_FILE PLUGIN_DIR "/jewels.save"
150 /* final game return status */
151 #define BJ_END 3
152 #define BJ_USB 2
153 #define BJ_QUIT 1
154 #define BJ_LOSE 0
156 /* swap directions */
157 #define SWAP_UP 0
158 #define SWAP_RIGHT 1
159 #define SWAP_DOWN 2
160 #define SWAP_LEFT 3
162 /* play board dimension */
163 #define BJ_HEIGHT 9
164 #define BJ_WIDTH 8
166 /* next level threshold */
167 #define LEVEL_PTS 100
169 /* animation frame rate */
170 #define MAX_FPS 20
172 /* menu values */
173 #define FONT_HEIGHT 8
174 #define MAX_MITEMS 5
175 #define MENU_WIDTH 100
177 /* menu results */
178 enum menu_result {
179 MRES_NONE,
180 MRES_NEW,
181 MRES_SAVE,
182 MRES_RESUME,
183 MRES_SCORES,
184 MRES_HELP,
185 MRES_QUIT
188 /* menu commands */
189 enum menu_cmd {
190 MCMD_NONE,
191 MCMD_NEXT,
192 MCMD_PREV,
193 MCMD_SELECT
196 /* menus */
197 struct jewels_menu {
198 char *title;
199 bool hasframe;
200 int selected;
201 int itemcnt;
202 struct jewels_menuitem {
203 char *text;
204 enum menu_result res;
205 } items[MAX_MITEMS];
206 } bjmenu[] = {
207 {"Jewels", false, 0, 5,
208 {{"New Game", MRES_NEW},
209 {"Resume Game", MRES_RESUME},
210 {"High Scores", MRES_SCORES},
211 {"Help", MRES_HELP},
212 {"Quit", MRES_QUIT}}},
213 {"Menu", true, 0, 3,
214 {{"Resume Game", MRES_RESUME},
215 {"Save Game", MRES_SAVE},
216 {"End Game", MRES_QUIT}}}
219 /* global rockbox api */
220 static struct plugin_api* rb;
222 /* external bitmaps */
223 extern const fb_data jewels[];
225 /* tile background colors */
226 #ifdef HAVE_LCD_COLOR
227 static const unsigned jewels_bkgd[2] = {
228 LCD_RGBPACK(104, 63, 63),
229 LCD_RGBPACK(83, 44, 44)
231 #endif
233 /* the tile struct
234 * type is the jewel number 0-7
235 * falling if the jewel is falling
236 * delete marks the jewel for deletion
238 struct tile {
239 int type;
240 bool falling;
241 bool delete;
244 /* the game context struct
245 * score is the current level score
246 * segments is the number of cleared segments in the current run
247 * level is the current level
248 * highscores is the list of high scores
249 * resume denotes whether to resume the currently loaded game
250 * dirty denotes whether the high scores are out of sync with the saved file
251 * playboard is the game playing board (first row is hidden)
253 struct game_context {
254 unsigned int score;
255 unsigned int segments;
256 unsigned int level;
257 unsigned short highscores[NUM_SCORES];
258 bool resume;
259 bool dirty;
260 struct tile playboard[BJ_HEIGHT][BJ_WIDTH];
263 /*****************************************************************************
264 * jewels_init() initializes jewels data structures.
265 ******************************************************************************/
266 static void jewels_init(struct game_context* bj) {
267 /* seed the rand generator */
268 rb->srand(*rb->current_tick);
270 /* check for resumed game */
271 if(bj->resume) {
272 bj->resume = false;
273 return;
276 /* reset scoring */
277 bj->level = 1;
278 bj->score = 0;
279 bj->segments = 0;
281 /* clear playing board */
282 rb->memset(bj->playboard, 0, sizeof(bj->playboard));
285 /*****************************************************************************
286 * jewels_setcolors() set the foreground and background colors.
287 ******************************************************************************/
288 static inline void jewels_setcolors(void) {
289 #ifdef HAVE_LCD_COLOR
290 rb->lcd_set_background(LCD_RGBPACK(49, 26, 26));
291 rb->lcd_set_foreground(LCD_RGBPACK(210, 181, 181));
292 #endif
295 /*****************************************************************************
296 * jewels_drawboard() redraws the entire game board.
297 ******************************************************************************/
298 static void jewels_drawboard(struct game_context* bj) {
299 int i, j;
300 int w, h;
301 unsigned int tempscore;
302 char *title = "Level";
303 char str[6];
305 tempscore = (bj->score>LEVEL_PTS ? LEVEL_PTS : bj->score);
307 /* clear screen */
308 rb->lcd_clear_display();
310 /* draw separator lines */
311 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, LCD_HEIGHT);
312 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH, 18);
313 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH, LCD_HEIGHT-10);
315 /* dispay playing board */
316 for(i=0; i<BJ_HEIGHT-1; i++){
317 for(j=0; j<BJ_WIDTH; j++){
318 #ifdef HAVE_LCD_COLOR
319 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
320 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
321 TILE_WIDTH, TILE_HEIGHT);
322 rb->lcd_bitmap_transparent_part(jewels,
323 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
324 TILE_WIDTH, j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
325 TILE_WIDTH, TILE_HEIGHT);
326 #else
327 rb->lcd_bitmap_part(jewels,
328 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
329 TILE_WIDTH, j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
330 TILE_WIDTH, TILE_HEIGHT);
331 #endif
335 /* draw progress bar */
336 #ifdef HAVE_LCD_COLOR
337 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
338 #endif
339 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
340 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
341 tempscore/LEVEL_PTS),
342 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
343 ((LCD_HEIGHT-10)-18)*tempscore/LEVEL_PTS);
344 #ifdef HAVE_LCD_COLOR
345 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
346 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
347 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
348 tempscore/LEVEL_PTS)+1,
349 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
350 ((LCD_HEIGHT-10)-18)*tempscore/LEVEL_PTS-1);
351 jewels_setcolors();
352 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
353 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
354 tempscore/LEVEL_PTS),
355 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
356 ((LCD_HEIGHT-10)-18)*tempscore/LEVEL_PTS+1);
357 #endif
359 /* print text */
360 rb->lcd_getstringsize(title, &w, &h);
361 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 1, title);
363 rb->snprintf(str, 4, "%d", bj->level);
364 rb->lcd_getstringsize(str, &w, &h);
365 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 10, str);
367 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
368 rb->lcd_getstringsize(str, &w, &h);
369 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2,
370 LCD_HEIGHT-8, str);
372 rb->lcd_update();
375 /*****************************************************************************
376 * jewels_showmenu() displays the chosen menu after performing the chosen
377 * menu command.
378 ******************************************************************************/
379 static enum menu_result jewels_showmenu(struct jewels_menu* menu,
380 enum menu_cmd cmd) {
381 int i;
382 int w, h;
383 int firstline;
384 int adj;
386 /* handle menu command */
387 switch(cmd) {
388 case MCMD_NEXT:
389 menu->selected = (menu->selected+1)%menu->itemcnt;
390 break;
392 case MCMD_PREV:
393 menu->selected = (menu->selected-1+menu->itemcnt)%menu->itemcnt;
394 break;
396 case MCMD_SELECT:
397 return menu->items[menu->selected].res;
399 default:
400 break;
403 /* clear menu area */
404 firstline = (LCD_HEIGHT/FONT_HEIGHT-(menu->itemcnt+3))/2;
406 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
407 rb->lcd_fillrect((LCD_WIDTH-MENU_WIDTH)/2, firstline*FONT_HEIGHT,
408 MENU_WIDTH, (menu->itemcnt+3)*FONT_HEIGHT);
409 rb->lcd_set_drawmode(DRMODE_SOLID);
411 if(menu->hasframe) {
412 rb->lcd_drawrect((LCD_WIDTH-MENU_WIDTH)/2-1, firstline*FONT_HEIGHT-1,
413 MENU_WIDTH+2, (menu->itemcnt+3)*FONT_HEIGHT+2);
414 rb->lcd_hline((LCD_WIDTH-MENU_WIDTH)/2-1,
415 (LCD_WIDTH-MENU_WIDTH)/2-1+MENU_WIDTH+2,
416 (firstline+1)*FONT_HEIGHT);
419 /* draw menu items */
420 rb->lcd_getstringsize(menu->title, &w, &h);
421 rb->lcd_putsxy((LCD_WIDTH-w)/2, firstline*FONT_HEIGHT, menu->title);
423 for(i=0; i<menu->itemcnt; i++) {
424 if(i == menu->selected) {
425 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
427 rb->lcd_putsxy((LCD_WIDTH-MENU_WIDTH)/2, (firstline+i+2)*FONT_HEIGHT,
428 menu->items[i].text);
429 if(i == menu->selected) {
430 rb->lcd_set_drawmode(DRMODE_SOLID);
434 adj = (firstline == 0 ? 0 : 1);
435 rb->lcd_update_rect((LCD_WIDTH-MENU_WIDTH)/2-1, firstline*FONT_HEIGHT-adj,
436 MENU_WIDTH+2, (menu->itemcnt+3)*FONT_HEIGHT+2*adj);
437 return MRES_NONE;
440 /*****************************************************************************
441 * jewels_putjewels() makes the jewels fall to fill empty spots and adds
442 * new random jewels at the empty spots at the top of each row.
443 ******************************************************************************/
444 static void jewels_putjewels(struct game_context* bj){
445 int i, j, k;
446 bool mark, done;
447 long lasttick, currenttick;
449 /* loop to make all the jewels fall */
450 while(true) {
451 /* mark falling jewels and add new jewels to hidden top row*/
452 mark = false;
453 done = true;
454 for(j=0; j<BJ_WIDTH; j++) {
455 if(bj->playboard[1][j].type == 0) {
456 bj->playboard[0][j].type = rb->rand()%7+1;
458 for(i=BJ_HEIGHT-2; i>=0; i--) {
459 if(!mark && bj->playboard[i+1][j].type == 0) {
460 mark = true;
461 done = false;
463 if(mark) bj->playboard[i][j].falling = true;
465 /*if(bj->playboard[1][j].falling) {
466 bj->playboard[0][j].type = rb->rand()%7+1;
467 bj->playboard[0][j].falling = true;
469 mark = false;
472 /* break if there are no falling jewels */
473 if(done) break;
475 /* animate falling jewels */
476 lasttick = *rb->current_tick;
478 for(k=1; k<=8; k++) {
479 for(i=BJ_HEIGHT-2; i>=0; i--) {
480 for(j=0; j<BJ_WIDTH; j++) {
481 if(bj->playboard[i][j].falling &&
482 bj->playboard[i][j].type != 0) {
483 /* clear old position */
484 #ifdef HAVE_LCD_COLOR
485 if(i == 0 && YOFS) {
486 rb->lcd_set_foreground(rb->lcd_get_background());
487 } else {
488 rb->lcd_set_foreground(jewels_bkgd[(i-1+j)%2]);
490 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
491 TILE_WIDTH, TILE_HEIGHT);
492 if(bj->playboard[i+1][j].type == 0) {
493 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
494 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
495 TILE_WIDTH, TILE_HEIGHT);
497 #else
498 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
499 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
500 TILE_WIDTH, TILE_HEIGHT);
501 if(bj->playboard[i+1][j].type == 0) {
502 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
503 TILE_WIDTH, TILE_HEIGHT);
505 rb->lcd_set_drawmode(DRMODE_SOLID);
506 #endif
508 /* draw new position */
509 #ifdef HAVE_LCD_COLOR
510 rb->lcd_bitmap_transparent_part(jewels, 0,
511 TILE_HEIGHT*(bj->playboard[i][j].type),
512 TILE_WIDTH, j*TILE_WIDTH,
513 (i-1)*TILE_HEIGHT+YOFS+
514 ((((TILE_HEIGHT<<10)*k)/8)>>10),
515 TILE_WIDTH, TILE_HEIGHT);
516 #else
517 rb->lcd_bitmap_part(jewels, 0,
518 TILE_HEIGHT*(bj->playboard[i][j].type),
519 TILE_WIDTH, j*TILE_WIDTH,
520 (i-1)*TILE_HEIGHT+YOFS+
521 ((((TILE_HEIGHT<<10)*k)/8)>>10),
522 TILE_WIDTH, TILE_HEIGHT);
523 #endif
528 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
529 jewels_setcolors();
531 /* framerate limiting */
532 currenttick = *rb->current_tick;
533 if(currenttick-lasttick < HZ/MAX_FPS) {
534 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
535 } else {
536 rb->yield();
538 lasttick = currenttick;
541 /* shift jewels down */
542 for(j=0; j<BJ_WIDTH; j++) {
543 for(i=BJ_HEIGHT-1; i>=1; i--) {
544 if(bj->playboard[i-1][j].falling) {
545 bj->playboard[i][j].type = bj->playboard[i-1][j].type;
550 /* clear out top row */
551 for(j=0; j<BJ_WIDTH; j++) {
552 bj->playboard[0][j].type = 0;
555 /* mark everything not falling */
556 for(i=0; i<BJ_HEIGHT; i++) {
557 for(j=0; j<BJ_WIDTH; j++) {
558 bj->playboard[i][j].falling = false;
564 /*****************************************************************************
565 * jewels_clearjewels() finds all the connected rows and columns and
566 * calculates and returns the points earned.
567 ******************************************************************************/
568 static unsigned int jewels_clearjewels(struct game_context* bj) {
569 int i, j;
570 int last, run;
571 unsigned int points = 0;
573 /* check for connected rows */
574 for(i=1; i<BJ_HEIGHT; i++) {
575 last = 0;
576 run = 1;
577 for(j=0; j<BJ_WIDTH; j++) {
578 if(bj->playboard[i][j].type == last &&
579 bj->playboard[i][j].type != 0) {
580 run++;
582 if(run == 3) {
583 bj->segments++;
584 points += bj->segments;
585 bj->playboard[i][j].delete = true;
586 bj->playboard[i][j-1].delete = true;
587 bj->playboard[i][j-2].delete = true;
588 } else if(run > 3) {
589 points++;
590 bj->playboard[i][j].delete = true;
592 } else {
593 run = 1;
594 last = bj->playboard[i][j].type;
599 /* check for connected columns */
600 for(j=0; j<BJ_WIDTH; j++) {
601 last = 0;
602 run = 1;
603 for(i=1; i<BJ_HEIGHT; i++) {
604 if(bj->playboard[i][j].type != 0 &&
605 bj->playboard[i][j].type == last) {
606 run++;
608 if(run == 3) {
609 bj->segments++;
610 points += bj->segments;
611 bj->playboard[i][j].delete = true;
612 bj->playboard[i-1][j].delete = true;
613 bj->playboard[i-2][j].delete = true;
614 } else if(run > 3) {
615 points++;
616 bj->playboard[i][j].delete = true;
618 } else {
619 run = 1;
620 last = bj->playboard[i][j].type;
625 /* clear deleted jewels */
626 for(i=1; i<BJ_HEIGHT; i++) {
627 for(j=0; j<BJ_WIDTH; j++) {
628 if(bj->playboard[i][j].delete) {
629 bj->playboard[i][j].delete = false;
630 bj->playboard[i][j].type = 0;
635 return points;
638 /*****************************************************************************
639 * jewels_runboard() runs the board until it settles in a fixed state and
640 * returns points earned.
641 ******************************************************************************/
642 static unsigned int jewels_runboard(struct game_context* bj) {
643 unsigned int points = 0;
644 unsigned int ret;
646 bj->segments = 0;
648 while((ret = jewels_clearjewels(bj)) > 0) {
649 points += ret;
650 jewels_drawboard(bj);
651 jewels_putjewels(bj);
654 return points;
657 /*****************************************************************************
658 * jewels_swapjewels() swaps two jewels as long as it results in points and
659 * returns points earned.
660 ******************************************************************************/
661 static unsigned int jewels_swapjewels(struct game_context* bj,
662 int x, int y, int direc) {
663 int k;
664 int horzmod, vertmod;
665 int movelen = 0;
666 bool undo = false;
667 unsigned int points = 0;
668 long lasttick, currenttick;
670 /* check for invalid parameters */
671 if(x < 0 || x >= BJ_WIDTH || y < 0 || y >= BJ_HEIGHT-1 ||
672 direc < SWAP_UP || direc > SWAP_LEFT) {
673 return 0;
676 /* check for invalid directions */
677 if((x == 0 && direc == SWAP_LEFT) ||
678 (x == BJ_WIDTH-1 && direc == SWAP_RIGHT) ||
679 (y == 0 && direc == SWAP_UP) ||
680 (y == BJ_HEIGHT-2 && direc == SWAP_DOWN)) {
681 return 0;
684 /* set direction variables */
685 horzmod = 0;
686 vertmod = 0;
687 switch(direc) {
688 case SWAP_UP:
689 vertmod = -1;
690 movelen = TILE_HEIGHT;
691 break;
692 case SWAP_RIGHT:
693 horzmod = 1;
694 movelen = TILE_WIDTH;
695 break;
696 case SWAP_DOWN:
697 vertmod = 1;
698 movelen = TILE_HEIGHT;
699 break;
700 case SWAP_LEFT:
701 horzmod = -1;
702 movelen = TILE_WIDTH;
703 break;
706 while(true) {
707 lasttick = *rb->current_tick;
709 /* animate swapping jewels */
710 for(k=0; k<=8; k++) {
711 /* clear old position */
712 #ifdef HAVE_LCD_COLOR
713 rb->lcd_set_foreground(jewels_bkgd[(x+y)%2]);
714 rb->lcd_fillrect(x*TILE_WIDTH,
715 y*TILE_HEIGHT+YOFS,
716 TILE_WIDTH, TILE_HEIGHT);
717 rb->lcd_set_foreground(jewels_bkgd[(x+horzmod+y+vertmod)%2]);
718 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
719 (y+vertmod)*TILE_HEIGHT+YOFS,
720 TILE_WIDTH, TILE_HEIGHT);
721 #else
722 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
723 rb->lcd_fillrect(x*TILE_WIDTH,
724 y*TILE_HEIGHT+YOFS,
725 TILE_WIDTH, TILE_HEIGHT);
726 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
727 (y+vertmod)*TILE_HEIGHT+YOFS,
728 TILE_WIDTH, TILE_HEIGHT);
729 rb->lcd_set_drawmode(DRMODE_SOLID);
730 #endif
731 /* draw new position */
732 #ifdef HAVE_LCD_COLOR
733 rb->lcd_bitmap_transparent_part(jewels,
734 0, TILE_HEIGHT*(bj->playboard
735 [y+1+vertmod][x+horzmod].type), TILE_WIDTH,
736 (x+horzmod)*TILE_WIDTH-horzmod*
737 ((((movelen<<10)*k)/8)>>10),
738 (y+vertmod)*TILE_HEIGHT-vertmod*
739 ((((movelen<<10)*k)/8)>>10)+YOFS,
740 TILE_WIDTH, TILE_HEIGHT);
741 rb->lcd_bitmap_transparent_part(jewels,
742 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
743 TILE_WIDTH, x*TILE_WIDTH+horzmod*
744 ((((movelen<<10)*k)/8)>>10),
745 y*TILE_HEIGHT+vertmod*
746 ((((movelen<<10)*k)/8)>>10)+YOFS,
747 TILE_WIDTH, TILE_HEIGHT);
748 #else
749 rb->lcd_bitmap_part(jewels,
750 0, TILE_HEIGHT*(bj->playboard
751 [y+1+vertmod][x+horzmod].type), TILE_WIDTH,
752 (x+horzmod)*TILE_WIDTH-horzmod*
753 ((((movelen<<10)*k)/8)>>10),
754 (y+vertmod)*TILE_HEIGHT-vertmod*
755 ((((movelen<<10)*k)/8)>>10)+YOFS,
756 TILE_WIDTH, TILE_HEIGHT);
757 rb->lcd_set_drawmode(DRMODE_FG);
758 rb->lcd_bitmap_part(jewels,
759 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
760 TILE_WIDTH, x*TILE_WIDTH+horzmod*
761 ((((movelen<<10)*k)/8)>>10),
762 y*TILE_HEIGHT+vertmod*
763 ((((movelen<<10)*k)/8)>>10)+YOFS,
764 TILE_WIDTH, TILE_HEIGHT);
765 rb->lcd_set_drawmode(DRMODE_SOLID);
766 #endif
768 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
769 jewels_setcolors();
771 /* framerate limiting */
772 currenttick = *rb->current_tick;
773 if(currenttick-lasttick < HZ/MAX_FPS) {
774 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
775 } else {
776 rb->yield();
778 lasttick = currenttick;
781 /* swap jewels */
782 int temp = bj->playboard[y+1][x].type;
783 bj->playboard[y+1][x].type =
784 bj->playboard[y+1+vertmod][x+horzmod].type;
785 bj->playboard[y+1+vertmod][x+horzmod].type = temp;
787 if(undo) break;
789 points = jewels_runboard(bj);
790 if(points == 0) {
791 undo = true;
792 } else {
793 break;
797 return points;
800 /*****************************************************************************
801 * jewels_movesavail() uses pattern matching to see if there are any
802 * available move left.
803 ******************************************************************************/
804 static bool jewels_movesavail(struct game_context* bj) {
805 int i, j;
806 bool moves = false;
807 int mytype;
809 for(i=1; i<BJ_HEIGHT; i++) {
810 for(j=0; j<BJ_WIDTH; j++) {
811 mytype = bj->playboard[i][j].type;
813 /* check horizontal patterns */
814 if(j <= BJ_WIDTH-3) {
815 if(i > 1) {
816 if(bj->playboard[i-1][j+1].type == mytype) {
817 if(bj->playboard[i-1][j+2].type == mytype)
818 {moves = true; break;}
819 if(bj->playboard[i][j+2].type == mytype)
820 {moves = true; break;}
822 if(bj->playboard[i][j+1].type == mytype) {
823 if(bj->playboard[i-1][j+2].type == mytype)
824 {moves = true; break;}
828 if(j <= BJ_WIDTH-4) {
829 if(bj->playboard[i][j+3].type == mytype) {
830 if(bj->playboard[i][j+1].type == mytype)
831 {moves = true; break;}
832 if(bj->playboard[i][j+2].type == mytype)
833 {moves = true; break;}
837 if(i < BJ_HEIGHT-1) {
838 if(bj->playboard[i][j+1].type == mytype) {
839 if(bj->playboard[i+1][j+2].type == mytype)
840 {moves = true; break;}
842 if(bj->playboard[i+1][j+1].type == mytype) {
843 if(bj->playboard[i][j+2].type == mytype)
844 {moves = true; break;}
845 if(bj->playboard[i+1][j+2].type == mytype)
846 {moves = true; break;}
851 /* check vertical patterns */
852 if(i <= BJ_HEIGHT-3) {
853 if(j > 0) {
854 if(bj->playboard[i+1][j-1].type == mytype) {
855 if(bj->playboard[i+2][j-1].type == mytype)
856 {moves = true; break;}
857 if(bj->playboard[i+2][j].type == mytype)
858 {moves = true; break;}
860 if(bj->playboard[i+1][j].type == mytype) {
861 if(bj->playboard[i+2][j-1].type == mytype)
862 {moves = true; break;}
866 if(i <= BJ_HEIGHT-4) {
867 if(bj->playboard[i+3][j].type == mytype) {
868 if(bj->playboard[i+1][j].type == mytype)
869 {moves = true; break;}
870 if(bj->playboard[i+2][j].type == mytype)
871 {moves = true; break;}
875 if(j < BJ_WIDTH-1) {
876 if(bj->playboard[i+1][j].type == mytype) {
877 if(bj->playboard[i+2][j+1].type == mytype)
878 {moves = true; break;}
880 if(bj->playboard[i+1][j+1].type == mytype) {
881 if(bj->playboard[i+2][j].type == mytype)
882 {moves = true; break;}
883 if (bj->playboard[i+2][j+1].type == mytype)
884 {moves = true; break;}
890 if(moves) break;
893 return moves;
896 /*****************************************************************************
897 * jewels_nextlevel() advances the game to the next level and returns
898 * points earned.
899 ******************************************************************************/
900 static unsigned int jewels_nextlevel(struct game_context* bj) {
901 int i, x, y;
902 unsigned int points = 0;
904 /* roll over score, change and display level */
905 while(bj->score >= LEVEL_PTS) {
906 bj->score -= LEVEL_PTS;
907 bj->level++;
908 rb->splash(HZ*2, true, "Level %d", bj->level);
909 jewels_drawboard(bj);
912 /* randomly clear some jewels */
913 for(i=0; i<16; i++) {
914 x = rb->rand()%8;
915 y = rb->rand()%8;
917 if(bj->playboard[y][x].type != 0) {
918 points++;
919 bj->playboard[y][x].type = 0;
922 jewels_drawboard(bj);
924 /* run the play board */
925 jewels_putjewels(bj);
926 points += jewels_runboard(bj);
927 return points;
930 /*****************************************************************************
931 * bejeweld_recordscore() inserts a high score into the high scores list and
932 * returns the high score position.
933 ******************************************************************************/
934 static int jewels_recordscore(struct game_context* bj) {
935 int i;
936 int position = 0;
937 unsigned short current, temp;
939 /* calculate total score */
940 current = (bj->level-1)*LEVEL_PTS+bj->score;
941 if(current <= 0) return 0;
943 /* insert the current score into the high scores */
944 for(i=0; i<NUM_SCORES; i++) {
945 if(current >= bj->highscores[i]) {
946 if(!position) {
947 position = i+1;
948 bj->dirty = true;
950 temp = bj->highscores[i];
951 bj->highscores[i] = current;
952 current = temp;
956 return position;
959 /*****************************************************************************
960 * jewels_loadscores() loads the high scores saved file.
961 ******************************************************************************/
962 static void jewels_loadscores(struct game_context* bj) {
963 int fd;
965 bj->dirty = false;
967 /* clear high scores */
968 rb->memset(bj->highscores, 0, sizeof(bj->highscores));
970 /* open scores file */
971 fd = rb->open(SCORE_FILE, O_RDONLY);
972 if(fd < 0) return;
974 /* read in high scores */
975 if(rb->read(fd, bj->highscores, sizeof(bj->highscores)) <= 0) {
976 /* scores are bad, reset */
977 rb->memset(bj->highscores, 0, sizeof(bj->highscores));
980 rb->close(fd);
983 /*****************************************************************************
984 * jewels_savescores() saves the high scores saved file.
985 ******************************************************************************/
986 static void jewels_savescores(struct game_context* bj) {
987 int fd;
989 /* write out the high scores to the save file */
990 fd = rb->open(SCORE_FILE, O_WRONLY|O_CREAT);
991 rb->write(fd, bj->highscores, sizeof(bj->highscores));
992 rb->close(fd);
993 bj->dirty = false;
996 /*****************************************************************************
997 * jewels_loadgame() loads the saved game and returns load success.
998 ******************************************************************************/
999 static bool jewels_loadgame(struct game_context* bj) {
1000 int fd;
1001 bool loaded = false;
1003 /* open game file */
1004 fd = rb->open(SAVE_FILE, O_RDONLY);
1005 if(fd < 0) return loaded;
1007 /* read in saved game */
1008 while(true) {
1009 if(rb->read(fd, &bj->score, sizeof(bj->score)) <= 0) break;
1010 if(rb->read(fd, &bj->level, sizeof(bj->level)) <= 0) break;
1011 if(rb->read(fd, bj->playboard, sizeof(bj->playboard)) <= 0) break;
1012 bj->resume = true;
1013 loaded = true;
1014 break;
1017 rb->close(fd);
1019 /* delete saved file */
1020 rb->remove(SAVE_FILE);
1021 return loaded;
1024 /*****************************************************************************
1025 * jewels_savegame() saves the current game state.
1026 ******************************************************************************/
1027 static void jewels_savegame(struct game_context* bj) {
1028 int fd;
1030 /* write out the game state to the save file */
1031 fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT);
1032 rb->write(fd, &bj->score, sizeof(bj->score));
1033 rb->write(fd, &bj->level, sizeof(bj->level));
1034 rb->write(fd, bj->playboard, sizeof(bj->playboard));
1035 rb->close(fd);
1037 bj->resume = true;
1040 /*****************************************************************************
1041 * jewels_callback() is the default event handler callback which is called
1042 * on usb connect and shutdown.
1043 ******************************************************************************/
1044 static void jewels_callback(void* param) {
1045 struct game_context* bj = (struct game_context*) param;
1046 if(bj->dirty) {
1047 rb->splash(HZ, true, "Saving high scores...");
1048 jewels_savescores(bj);
1052 /*****************************************************************************
1053 * bejeweled() is the main game subroutine, it returns the final game status.
1054 ******************************************************************************/
1055 static int bejeweled(struct game_context* bj) {
1056 int i, j;
1057 int w, h;
1058 int button;
1059 char str[18];
1060 bool startgame = false;
1061 bool inmenu = false;
1062 bool selected = false;
1063 enum menu_cmd cmd = MCMD_NONE;
1064 enum menu_result res;
1066 /* the cursor coordinates */
1067 int x=0, y=0;
1069 /* don't resume by default */
1070 bj->resume = false;
1072 /********************
1073 * menu *
1074 ********************/
1075 rb->lcd_clear_display();
1077 while(!startgame) {
1078 res = jewels_showmenu(&bjmenu[0], cmd);
1079 cmd = MCMD_NONE;
1081 rb->snprintf(str, 18, "High Score: %d", bj->highscores[0]);
1082 rb->lcd_getstringsize(str, &w, &h);
1083 rb->lcd_putsxy((LCD_WIDTH-w)/2, LCD_HEIGHT-8, str);
1084 rb->lcd_update();
1086 switch(res) {
1087 case MRES_NEW:
1088 startgame = true;
1089 continue;
1091 case MRES_RESUME:
1092 if(!jewels_loadgame(bj)) {
1093 rb->splash(HZ*2, true, "Nothing to resume");
1094 rb->lcd_clear_display();
1095 } else {
1096 startgame = true;
1098 continue;
1100 case MRES_SCORES:
1101 rb->lcd_clear_display();
1103 /* room for a title? */
1104 j = 0;
1105 if(LCD_HEIGHT-NUM_SCORES*8 >= 8) {
1106 rb->snprintf(str, 12, "%s", "High Scores");
1107 rb->lcd_getstringsize(str, &w, &h);
1108 rb->lcd_putsxy((LCD_WIDTH-w)/2, 0, str);
1109 j = 2;
1112 /* print high scores */
1113 for(i=0; i<NUM_SCORES; i++) {
1114 rb->snprintf(str, 11, "#%02d: %d", i+1, bj->highscores[i]);
1115 rb->lcd_puts(0, i+j, str);
1118 rb->lcd_update();
1119 while(true) {
1120 button = rb->button_get(true);
1121 if(button != BUTTON_NONE && !(button&BUTTON_REL)) break;
1123 rb->lcd_clear_display();
1124 continue;
1126 case MRES_HELP:
1127 /* welcome screen to display key bindings */
1128 rb->lcd_clear_display();
1129 rb->snprintf(str, 5, "%s", "Help");
1130 rb->lcd_getstringsize(str, &w, &h);
1131 rb->lcd_putsxy((LCD_WIDTH-w)/2, 0, str);
1132 #if (LCD_HEIGHT <= 64)
1133 rb->lcd_puts(0, 2, "Controls:");
1134 rb->lcd_puts(0, 3, "Directions = move");
1135 rb->lcd_puts(0, 4, "SELECT = select");
1136 rb->lcd_puts(0, 5, "Long SELECT = menu");
1137 #elif (LCD_HEIGHT <= 132)
1138 rb->lcd_puts(0, 2, "Swap pairs of jewels to");
1139 rb->lcd_puts(0, 3, "form connected segments");
1140 rb->lcd_puts(0, 4, "of three or more of the");
1141 rb->lcd_puts(0, 5, "same type.");
1142 rb->lcd_puts(0, 7, "Controls:");
1143 rb->lcd_puts(0, 8, "Directions to move");
1144 rb->lcd_puts(0, 9, "SELECT to select");
1145 rb->lcd_puts(0, 10, "Long SELECT to show menu");
1146 #else
1147 rb->lcd_puts(0, 2, "Swap pairs of jewels to form");
1148 rb->lcd_puts(0, 3, "connected segments of three");
1149 rb->lcd_puts(0, 4, "or more of the same type.");
1150 rb->lcd_puts(0, 6, "Controls:");
1151 rb->lcd_puts(0, 7, "Directions to move cursor");
1152 rb->lcd_puts(0, 8, "SELECT to select");
1153 rb->lcd_puts(0, 9, "Long SELECT to show menu");
1154 #endif
1155 rb->lcd_update();
1156 while(true) {
1157 button = rb->button_get(true);
1158 if(button != BUTTON_NONE && !(button&BUTTON_REL)) break;
1160 rb->lcd_clear_display();
1161 continue;
1163 case MRES_QUIT:
1164 return BJ_QUIT;
1166 default:
1167 break;
1170 /* handle menu button presses */
1171 button = rb->button_get(true);
1172 switch(button){
1173 #ifdef JEWELS_SCROLLWHEEL
1174 case JEWELS_PREV:
1175 #endif
1176 case JEWELS_UP:
1177 case (JEWELS_UP|BUTTON_REPEAT):
1178 cmd = MCMD_PREV;
1179 break;
1181 #ifdef JEWELS_SCROLLWHEEL
1182 case JEWELS_NEXT:
1183 #endif
1184 case JEWELS_DOWN:
1185 case (JEWELS_DOWN|BUTTON_REPEAT):
1186 cmd = MCMD_NEXT;
1187 break;
1189 case JEWELS_SELECT:
1190 case JEWELS_RIGHT:
1191 cmd = MCMD_SELECT;
1192 break;
1194 #ifdef JEWELS_CANCEL
1195 case JEWELS_CANCEL:
1196 return BJ_QUIT;
1197 #endif
1199 default:
1200 if(rb->default_event_handler_ex(button, jewels_callback,
1201 (void*) bj) == SYS_USB_CONNECTED)
1202 return BJ_USB;
1203 break;
1207 /********************
1208 * init *
1209 ********************/
1210 jewels_init(bj);
1212 /********************
1213 * setup the board *
1214 ********************/
1215 jewels_drawboard(bj);
1216 jewels_putjewels(bj);
1217 bj->score += jewels_runboard(bj);
1218 if (!jewels_movesavail(bj)) return BJ_LOSE;
1220 /**********************
1221 * play *
1222 **********************/
1223 while(true) {
1224 if(!inmenu) {
1225 /* refresh the board */
1226 jewels_drawboard(bj);
1228 /* display the cursor */
1229 if(selected) {
1230 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1231 rb->lcd_fillrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1232 TILE_WIDTH, TILE_HEIGHT);
1233 rb->lcd_set_drawmode(DRMODE_SOLID);
1234 } else {
1235 rb->lcd_drawrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1236 TILE_WIDTH, TILE_HEIGHT);
1238 rb->lcd_update_rect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1239 TILE_WIDTH, TILE_HEIGHT);
1240 } else {
1241 res = jewels_showmenu(&bjmenu[1], cmd);
1242 cmd = MCMD_NONE;
1243 switch(res) {
1244 case MRES_RESUME:
1245 inmenu = false;
1246 selected = false;
1247 continue;
1249 case MRES_SAVE:
1250 rb->splash(HZ, true, "Saving game...");
1251 jewels_savegame(bj);
1252 return BJ_END;
1254 case MRES_QUIT:
1255 return BJ_END;
1257 default:
1258 break;
1262 /* handle game button presses */
1263 button = rb->button_get(true);
1264 switch(button){
1265 case JEWELS_LEFT: /* move cursor left */
1266 case (JEWELS_LEFT|BUTTON_REPEAT):
1267 if(selected) {
1268 bj->score += jewels_swapjewels(bj, x, y, SWAP_LEFT);
1269 selected = false;
1270 if (!jewels_movesavail(bj)) return BJ_LOSE;
1271 } else {
1272 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1274 break;
1276 case JEWELS_RIGHT: /* move cursor right */
1277 case (JEWELS_RIGHT|BUTTON_REPEAT):
1278 if(!inmenu) {
1279 if(selected) {
1280 bj->score += jewels_swapjewels(bj, x, y, SWAP_RIGHT);
1281 selected = false;
1282 if (!jewels_movesavail(bj)) return BJ_LOSE;
1283 } else {
1284 x = (x+1)%BJ_WIDTH;
1286 } else {
1287 cmd = MCMD_SELECT;
1289 break;
1291 case JEWELS_DOWN: /* move cursor down */
1292 case (JEWELS_DOWN|BUTTON_REPEAT):
1293 if(!inmenu) {
1294 if(selected) {
1295 bj->score += jewels_swapjewels(bj, x, y, SWAP_DOWN);
1296 selected = false;
1297 if (!jewels_movesavail(bj)) return BJ_LOSE;
1298 } else {
1299 y = (y+1)%(BJ_HEIGHT-1);
1301 } else {
1302 cmd = MCMD_NEXT;
1304 break;
1306 case JEWELS_UP: /* move cursor up */
1307 case (JEWELS_UP|BUTTON_REPEAT):
1308 if(!inmenu) {
1309 if(selected) {
1310 bj->score += jewels_swapjewels(bj, x, y, SWAP_UP);
1311 selected = false;
1312 if (!jewels_movesavail(bj)) return BJ_LOSE;
1313 } else {
1314 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1316 } else {
1317 cmd = MCMD_PREV;
1319 break;
1321 #ifdef JEWELS_SCROLLWHEEL
1322 case JEWELS_PREV: /* scroll backwards */
1323 case (JEWELS_PREV|BUTTON_REPEAT):
1324 if(!inmenu) {
1325 if(!selected) {
1326 if(x == 0) {
1327 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1329 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1331 } else {
1332 cmd = MCMD_PREV;
1334 break;
1336 case JEWELS_NEXT: /* scroll forwards */
1337 case (JEWELS_NEXT|BUTTON_REPEAT):
1338 if(!inmenu) {
1339 if(!selected) {
1340 if(x == BJ_WIDTH-1) {
1341 y = (y+1)%(BJ_HEIGHT-1);
1343 x = (x+1)%BJ_WIDTH;
1345 } else {
1346 cmd = MCMD_NEXT;
1348 break;
1349 #endif
1351 case JEWELS_SELECT: /* toggle selected */
1352 if(!inmenu) {
1353 selected = !selected;
1354 } else {
1355 cmd = MCMD_SELECT;
1357 break;
1359 case (JEWELS_SELECT|BUTTON_REPEAT): /* show menu */
1360 if(!inmenu) inmenu = true;
1361 break;
1363 #ifdef JEWELS_CANCEL
1364 case JEWELS_CANCEL: /* toggle menu */
1365 inmenu = !inmenu;
1366 selected = false;
1367 break;
1368 #endif
1370 default:
1371 if(rb->default_event_handler_ex(button, jewels_callback,
1372 (void*) bj) == SYS_USB_CONNECTED)
1373 return BJ_USB;
1374 break;
1377 if(bj->score >= LEVEL_PTS) bj->score = jewels_nextlevel(bj);
1381 /*****************************************************************************
1382 * plugin entry point.
1383 ******************************************************************************/
1384 enum plugin_status plugin_start(struct plugin_api* api, void* parameter) {
1385 struct game_context bj;
1386 bool exit = false;
1387 int position;
1388 char str[19];
1390 /* plugin init */
1391 (void)parameter;
1392 rb = api;
1393 /* end of plugin init */
1395 /* load high scores */
1396 jewels_loadscores(&bj);
1398 rb->lcd_setfont(FONT_SYSFIXED);
1399 jewels_setcolors();
1401 while(!exit) {
1402 switch(bejeweled(&bj)){
1403 case BJ_LOSE:
1404 rb->splash(HZ*2, true, "No more moves!");
1405 /* fall through to BJ_END */
1407 case BJ_END:
1408 if(!bj.resume) {
1409 if((position = jewels_recordscore(&bj))) {
1410 rb->snprintf(str, 19, "New high score #%d!", position);
1411 rb->splash(HZ*2, true, str);
1414 break;
1416 case BJ_USB:
1417 rb->lcd_setfont(FONT_UI);
1418 return PLUGIN_USB_CONNECTED;
1420 case BJ_QUIT:
1421 if(bj.dirty) {
1422 rb->splash(HZ, true, "Saving high scores...");
1423 jewels_savescores(&bj);
1425 exit = true;
1426 break;
1428 default:
1429 break;
1433 rb->lcd_setfont(FONT_UI);
1434 return PLUGIN_OK;
1437 #endif /* HAVE_LCD_BITMAP */