1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
24 #ifdef HAVE_LCD_BITMAP
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
90 #error JEWELS: Unsupported keypad
93 /* use 30x30 tiles (iPod Video) */
94 #if (LCD_HEIGHT == 240) && (LCD_WIDTH == 320)
96 #define TILE_HEIGHT 30
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
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
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
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
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
135 /* use 10x8 tiles (Recorder, Ondio) */
136 #elif (LCD_HEIGHT == 64) && (LCD_WIDTH == 112)
137 #define TILE_WIDTH 10
138 #define TILE_HEIGHT 8
143 #error JEWELS: Unsupported LCD
147 #define SCORE_FILE PLUGIN_DIR "/jewels.score"
148 #define SAVE_FILE PLUGIN_DIR "/jewels.save"
150 /* final game return status */
156 /* swap directions */
162 /* play board dimension */
166 /* next level threshold */
167 #define LEVEL_PTS 100
169 /* animation frame rate */
173 #define FONT_HEIGHT 8
175 #define MENU_WIDTH 100
202 struct jewels_menuitem
{
204 enum menu_result res
;
207 {"Jewels", false, 0, 5,
208 {{"New Game", MRES_NEW
},
209 {"Resume Game", MRES_RESUME
},
210 {"High Scores", MRES_SCORES
},
212 {"Quit", MRES_QUIT
}}},
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)
234 * type is the jewel number 0-7
235 * falling if the jewel is falling
236 * delete marks the jewel for deletion
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
{
255 unsigned int segments
;
257 unsigned short highscores
[NUM_SCORES
];
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 */
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));
295 /*****************************************************************************
296 * jewels_drawboard() redraws the entire game board.
297 ******************************************************************************/
298 static void jewels_drawboard(struct game_context
* bj
) {
301 unsigned int tempscore
;
302 char *title
= "Level";
305 tempscore
= (bj
->score
>LEVEL_PTS
? LEVEL_PTS
: bj
->score
);
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
);
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
);
335 /* draw progress bar */
336 #ifdef HAVE_LCD_COLOR
337 rb
->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
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);
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);
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,
375 /*****************************************************************************
376 * jewels_showmenu() displays the chosen menu after performing the chosen
378 ******************************************************************************/
379 static enum menu_result
jewels_showmenu(struct jewels_menu
* menu
,
386 /* handle menu command */
389 menu
->selected
= (menu
->selected
+1)%menu
->itemcnt
;
393 menu
->selected
= (menu
->selected
-1+menu
->itemcnt
)%menu
->itemcnt
;
397 return menu
->items
[menu
->selected
].res
;
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
);
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
);
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
){
447 long lasttick
, currenttick
;
449 /* loop to make all the jewels fall */
451 /* mark falling jewels and add new jewels to hidden top row*/
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) {
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;
472 /* break if there are no falling jewels */
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
486 rb
->lcd_set_foreground(rb
->lcd_get_background());
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
);
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
);
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
);
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
);
528 rb
->lcd_update_rect(0, 0, TILE_WIDTH
*8, LCD_HEIGHT
);
531 /* framerate limiting */
532 currenttick
= *rb
->current_tick
;
533 if(currenttick
-lasttick
< HZ
/MAX_FPS
) {
534 rb
->sleep((HZ
/MAX_FPS
)-(currenttick
-lasttick
));
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
) {
571 unsigned int points
= 0;
573 /* check for connected rows */
574 for(i
=1; i
<BJ_HEIGHT
; i
++) {
577 for(j
=0; j
<BJ_WIDTH
; j
++) {
578 if(bj
->playboard
[i
][j
].type
== last
&&
579 bj
->playboard
[i
][j
].type
!= 0) {
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;
590 bj
->playboard
[i
][j
].delete = true;
594 last
= bj
->playboard
[i
][j
].type
;
599 /* check for connected columns */
600 for(j
=0; j
<BJ_WIDTH
; j
++) {
603 for(i
=1; i
<BJ_HEIGHT
; i
++) {
604 if(bj
->playboard
[i
][j
].type
!= 0 &&
605 bj
->playboard
[i
][j
].type
== last
) {
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;
616 bj
->playboard
[i
][j
].delete = true;
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;
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;
648 while((ret
= jewels_clearjewels(bj
)) > 0) {
650 jewels_drawboard(bj
);
651 jewels_putjewels(bj
);
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
) {
664 int horzmod
, vertmod
;
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
) {
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
)) {
684 /* set direction variables */
690 movelen
= TILE_HEIGHT
;
694 movelen
= TILE_WIDTH
;
698 movelen
= TILE_HEIGHT
;
702 movelen
= TILE_WIDTH
;
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
,
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
);
722 rb
->lcd_set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
723 rb
->lcd_fillrect(x
*TILE_WIDTH
,
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
);
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
);
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
);
768 rb
->lcd_update_rect(0, 0, TILE_WIDTH
*8, LCD_HEIGHT
);
771 /* framerate limiting */
772 currenttick
= *rb
->current_tick
;
773 if(currenttick
-lasttick
< HZ
/MAX_FPS
) {
774 rb
->sleep((HZ
/MAX_FPS
)-(currenttick
-lasttick
));
778 lasttick
= currenttick
;
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
;
789 points
= jewels_runboard(bj
);
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
) {
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) {
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) {
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;}
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;}
896 /*****************************************************************************
897 * jewels_nextlevel() advances the game to the next level and returns
899 ******************************************************************************/
900 static unsigned int jewels_nextlevel(struct game_context
* bj
) {
902 unsigned int points
= 0;
904 /* roll over score, change and display level */
905 while(bj
->score
>= LEVEL_PTS
) {
906 bj
->score
-= LEVEL_PTS
;
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
++) {
917 if(bj
->playboard
[y
][x
].type
!= 0) {
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
);
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
) {
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
]) {
950 temp
= bj
->highscores
[i
];
951 bj
->highscores
[i
] = current
;
959 /*****************************************************************************
960 * jewels_loadscores() loads the high scores saved file.
961 ******************************************************************************/
962 static void jewels_loadscores(struct game_context
* bj
) {
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
);
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
));
983 /*****************************************************************************
984 * jewels_savescores() saves the high scores saved file.
985 ******************************************************************************/
986 static void jewels_savescores(struct game_context
* bj
) {
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
));
996 /*****************************************************************************
997 * jewels_loadgame() loads the saved game and returns load success.
998 ******************************************************************************/
999 static bool jewels_loadgame(struct game_context
* bj
) {
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 */
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;
1019 /* delete saved file */
1020 rb
->remove(SAVE_FILE
);
1024 /*****************************************************************************
1025 * jewels_savegame() saves the current game state.
1026 ******************************************************************************/
1027 static void jewels_savegame(struct game_context
* bj
) {
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
));
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
;
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
) {
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 */
1069 /* don't resume by default */
1072 /********************
1074 ********************/
1075 rb
->lcd_clear_display();
1078 res
= jewels_showmenu(&bjmenu
[0], cmd
);
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
);
1092 if(!jewels_loadgame(bj
)) {
1093 rb
->splash(HZ
*2, true, "Nothing to resume");
1094 rb
->lcd_clear_display();
1101 rb
->lcd_clear_display();
1103 /* room for a title? */
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
);
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
);
1120 button
= rb
->button_get(true);
1121 if(button
!= BUTTON_NONE
&& !(button
&BUTTON_REL
)) break;
1123 rb
->lcd_clear_display();
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");
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");
1157 button
= rb
->button_get(true);
1158 if(button
!= BUTTON_NONE
&& !(button
&BUTTON_REL
)) break;
1160 rb
->lcd_clear_display();
1170 /* handle menu button presses */
1171 button
= rb
->button_get(true);
1173 #ifdef JEWELS_SCROLLWHEEL
1177 case (JEWELS_UP
|BUTTON_REPEAT
):
1181 #ifdef JEWELS_SCROLLWHEEL
1185 case (JEWELS_DOWN
|BUTTON_REPEAT
):
1194 #ifdef JEWELS_CANCEL
1200 if(rb
->default_event_handler_ex(button
, jewels_callback
,
1201 (void*) bj
) == SYS_USB_CONNECTED
)
1207 /********************
1209 ********************/
1212 /********************
1214 ********************/
1215 jewels_drawboard(bj
);
1216 jewels_putjewels(bj
);
1217 bj
->score
+= jewels_runboard(bj
);
1218 if (!jewels_movesavail(bj
)) return BJ_LOSE
;
1220 /**********************
1222 **********************/
1225 /* refresh the board */
1226 jewels_drawboard(bj
);
1228 /* display the cursor */
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
);
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
);
1241 res
= jewels_showmenu(&bjmenu
[1], cmd
);
1250 rb
->splash(HZ
, true, "Saving game...");
1251 jewels_savegame(bj
);
1262 /* handle game button presses */
1263 button
= rb
->button_get(true);
1265 case JEWELS_LEFT
: /* move cursor left */
1266 case (JEWELS_LEFT
|BUTTON_REPEAT
):
1268 bj
->score
+= jewels_swapjewels(bj
, x
, y
, SWAP_LEFT
);
1270 if (!jewels_movesavail(bj
)) return BJ_LOSE
;
1272 x
= (x
+BJ_WIDTH
-1)%BJ_WIDTH
;
1276 case JEWELS_RIGHT
: /* move cursor right */
1277 case (JEWELS_RIGHT
|BUTTON_REPEAT
):
1280 bj
->score
+= jewels_swapjewels(bj
, x
, y
, SWAP_RIGHT
);
1282 if (!jewels_movesavail(bj
)) return BJ_LOSE
;
1291 case JEWELS_DOWN
: /* move cursor down */
1292 case (JEWELS_DOWN
|BUTTON_REPEAT
):
1295 bj
->score
+= jewels_swapjewels(bj
, x
, y
, SWAP_DOWN
);
1297 if (!jewels_movesavail(bj
)) return BJ_LOSE
;
1299 y
= (y
+1)%(BJ_HEIGHT
-1);
1306 case JEWELS_UP
: /* move cursor up */
1307 case (JEWELS_UP
|BUTTON_REPEAT
):
1310 bj
->score
+= jewels_swapjewels(bj
, x
, y
, SWAP_UP
);
1312 if (!jewels_movesavail(bj
)) return BJ_LOSE
;
1314 y
= (y
+(BJ_HEIGHT
-1)-1)%(BJ_HEIGHT
-1);
1321 #ifdef JEWELS_SCROLLWHEEL
1322 case JEWELS_PREV
: /* scroll backwards */
1323 case (JEWELS_PREV
|BUTTON_REPEAT
):
1327 y
= (y
+(BJ_HEIGHT
-1)-1)%(BJ_HEIGHT
-1);
1329 x
= (x
+BJ_WIDTH
-1)%BJ_WIDTH
;
1336 case JEWELS_NEXT
: /* scroll forwards */
1337 case (JEWELS_NEXT
|BUTTON_REPEAT
):
1340 if(x
== BJ_WIDTH
-1) {
1341 y
= (y
+1)%(BJ_HEIGHT
-1);
1351 case JEWELS_SELECT
: /* toggle selected */
1353 selected
= !selected
;
1359 case (JEWELS_SELECT
|BUTTON_REPEAT
): /* show menu */
1360 if(!inmenu
) inmenu
= true;
1363 #ifdef JEWELS_CANCEL
1364 case JEWELS_CANCEL
: /* toggle menu */
1371 if(rb
->default_event_handler_ex(button
, jewels_callback
,
1372 (void*) bj
) == SYS_USB_CONNECTED
)
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
;
1393 /* end of plugin init */
1395 /* load high scores */
1396 jewels_loadscores(&bj
);
1398 rb
->lcd_setfont(FONT_SYSFIXED
);
1402 switch(bejeweled(&bj
)){
1404 rb
->splash(HZ
*2, true, "No more moves!");
1405 /* fall through to BJ_END */
1409 if((position
= jewels_recordscore(&bj
))) {
1410 rb
->snprintf(str
, 19, "New high score #%d!", position
);
1411 rb
->splash(HZ
*2, true, str
);
1417 rb
->lcd_setfont(FONT_UI
);
1418 return PLUGIN_USB_CONNECTED
;
1422 rb
->splash(HZ
, true, "Saving high scores...");
1423 jewels_savescores(&bj
);
1433 rb
->lcd_setfont(FONT_UI
);
1437 #endif /* HAVE_LCD_BITMAP */