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 ****************************************************************************/
23 #include "playback_control.h"
25 #ifdef HAVE_LCD_BITMAP
29 /* button definitions */
30 #if CONFIG_KEYPAD == RECORDER_PAD
31 #define JEWELS_UP BUTTON_UP
32 #define JEWELS_DOWN BUTTON_DOWN
33 #define JEWELS_LEFT BUTTON_LEFT
34 #define JEWELS_RIGHT BUTTON_RIGHT
35 #define JEWELS_SELECT BUTTON_PLAY
36 #define JEWELS_CANCEL BUTTON_OFF
38 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
39 #define JEWELS_UP BUTTON_UP
40 #define JEWELS_DOWN BUTTON_DOWN
41 #define JEWELS_LEFT BUTTON_LEFT
42 #define JEWELS_RIGHT BUTTON_RIGHT
43 #define JEWELS_SELECT BUTTON_SELECT
44 #define JEWELS_CANCEL BUTTON_OFF
46 #elif CONFIG_KEYPAD == ONDIO_PAD
47 #define JEWELS_UP BUTTON_UP
48 #define JEWELS_DOWN BUTTON_DOWN
49 #define JEWELS_LEFT BUTTON_LEFT
50 #define JEWELS_RIGHT BUTTON_RIGHT
51 #define JEWELS_SELECT BUTTON_MENU
52 #define JEWELS_CANCEL BUTTON_OFF
54 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
55 #define JEWELS_UP BUTTON_UP
56 #define JEWELS_DOWN BUTTON_DOWN
57 #define JEWELS_LEFT BUTTON_LEFT
58 #define JEWELS_RIGHT BUTTON_RIGHT
59 #define JEWELS_SELECT BUTTON_SELECT
60 #define JEWELS_CANCEL BUTTON_OFF
61 #define JEWELS_RC_CANCEL BUTTON_RC_STOP
63 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
64 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
65 #define JEWELS_SCROLLWHEEL
66 #define JEWELS_UP BUTTON_MENU
67 #define JEWELS_DOWN BUTTON_PLAY
68 #define JEWELS_LEFT BUTTON_LEFT
69 #define JEWELS_RIGHT BUTTON_RIGHT
70 #define JEWELS_PREV BUTTON_SCROLL_BACK
71 #define JEWELS_NEXT BUTTON_SCROLL_FWD
72 #define JEWELS_SELECT BUTTON_SELECT
74 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
75 #define JEWELS_UP BUTTON_UP
76 #define JEWELS_DOWN BUTTON_DOWN
77 #define JEWELS_LEFT BUTTON_LEFT
78 #define JEWELS_RIGHT BUTTON_RIGHT
79 #define JEWELS_SELECT BUTTON_SELECT
80 #define JEWELS_CANCEL BUTTON_PLAY
82 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
83 #define JEWELS_UP BUTTON_UP
84 #define JEWELS_DOWN BUTTON_DOWN
85 #define JEWELS_LEFT BUTTON_LEFT
86 #define JEWELS_RIGHT BUTTON_RIGHT
87 #define JEWELS_SELECT BUTTON_SELECT
88 #define JEWELS_CANCEL BUTTON_POWER
90 #elif CONFIG_KEYPAD == GIGABEAT_PAD
91 #define JEWELS_UP BUTTON_UP
92 #define JEWELS_DOWN BUTTON_DOWN
93 #define JEWELS_LEFT BUTTON_LEFT
94 #define JEWELS_RIGHT BUTTON_RIGHT
95 #define JEWELS_SELECT BUTTON_SELECT
96 #define JEWELS_CANCEL BUTTON_POWER
98 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
99 (CONFIG_KEYPAD == SANSA_C200_PAD)
100 #define JEWELS_UP BUTTON_UP
101 #define JEWELS_DOWN BUTTON_DOWN
102 #define JEWELS_LEFT BUTTON_LEFT
103 #define JEWELS_RIGHT BUTTON_RIGHT
104 #define JEWELS_SELECT BUTTON_SELECT
105 #define JEWELS_CANCEL BUTTON_POWER
107 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
108 #define JEWELS_UP BUTTON_SCROLL_UP
109 #define JEWELS_DOWN BUTTON_SCROLL_DOWN
110 #define JEWELS_LEFT BUTTON_LEFT
111 #define JEWELS_RIGHT BUTTON_RIGHT
112 #define JEWELS_SELECT BUTTON_PLAY
113 #define JEWELS_CANCEL BUTTON_POWER
115 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
116 #define JEWELS_UP BUTTON_UP
117 #define JEWELS_DOWN BUTTON_DOWN
118 #define JEWELS_LEFT BUTTON_LEFT
119 #define JEWELS_RIGHT BUTTON_RIGHT
120 #define JEWELS_SELECT BUTTON_SELECT
121 #define JEWELS_CANCEL BUTTON_BACK
124 #error JEWELS: Unsupported keypad
127 /* use 30x30 tiles (iPod Video, Gigabeat) */
128 #if (LCD_HEIGHT == 240) && (LCD_WIDTH == 320) || \
129 ((LCD_HEIGHT == 320) && (LCD_WIDTH == 240))
130 #define TILE_WIDTH 30
131 #define TILE_HEIGHT 30
133 #define NUM_SCORES 10
135 /* use 22x22 tiles (H300, iPod Color) */
136 #elif ((LCD_HEIGHT == 176) && (LCD_WIDTH == 220)) || \
137 ((LCD_HEIGHT == 220) && (LCD_WIDTH == 176))
138 #define TILE_WIDTH 22
139 #define TILE_HEIGHT 22
141 #define NUM_SCORES 10
143 /* use 16x16 tiles (iPod Nano) */
144 #elif (LCD_HEIGHT == 132) && (LCD_WIDTH == 176)
145 #define TILE_WIDTH 16
146 #define TILE_HEIGHT 16
148 #define NUM_SCORES 10
150 /* use 16x16 tiles (H100, iAudio X5, iPod 3G, iPod 4G grayscale) */
151 #elif (LCD_HEIGHT == 128) && (LCD_WIDTH == 160)
152 #define TILE_WIDTH 16
153 #define TILE_HEIGHT 16
155 #define NUM_SCORES 10
157 /* use 14x14 tiles (H10 5/6 GB) */
158 #elif (LCD_HEIGHT == 128) && (LCD_WIDTH == 128)
159 #define TILE_WIDTH 14
160 #define TILE_HEIGHT 14
162 #define NUM_SCORES 10
164 /* use 13x13 tiles (iPod Mini) */
165 #elif (LCD_HEIGHT == 110) && (LCD_WIDTH == 138)
166 #define TILE_WIDTH 13
167 #define TILE_HEIGHT 13
169 #define NUM_SCORES 10
171 /* use 10x10 tiles (Sansa c200) */
172 #elif (LCD_HEIGHT == 80) && (LCD_WIDTH == 132)
173 #define TILE_WIDTH 10
174 #define TILE_HEIGHT 10
178 /* use 10x8 tiles (iFP 700) */
179 #elif (LCD_HEIGHT == 64) && (LCD_WIDTH == 128)
180 #define TILE_WIDTH 10
181 #define TILE_HEIGHT 8
185 /* use 10x8 tiles (Recorder, Ondio) */
186 #elif (LCD_HEIGHT == 64) && (LCD_WIDTH == 112)
187 #define TILE_WIDTH 10
188 #define TILE_HEIGHT 8
193 #error JEWELS: Unsupported LCD
197 #define SCORE_FILE PLUGIN_GAMES_DIR "/jewels.score"
198 #define SAVE_FILE PLUGIN_GAMES_DIR "/jewels.save"
200 /* final game return status */
201 #define BJ_QUIT_FROM_GAME 4
207 /* swap directions */
213 /* play board dimension */
217 /* next level threshold */
218 #define LEVEL_PTS 100
220 /* animation frame rate */
230 #define FONT_HEIGHT 8
232 #define MENU_WIDTH 100
262 struct jewels_menuitem
{
264 enum menu_result res
;
267 {"Jewels", false, 0, 6,
268 {{"New Game", MRES_NEW
},
269 {"Puzzle", MRES_PUZZLE
},
270 {"Resume Game", MRES_RESUME
},
271 {"High Scores", MRES_SCORES
},
273 {"Quit", MRES_QUIT
}}},
275 {{"Audio Playback", MRES_PLAYBACK
},
276 {"Resume Game", MRES_RESUME
},
277 {"Save Game", MRES_SAVE
},
278 {"End Game", MRES_QUIT
},
279 {"Exit Jewels", MRES_EXIT
}}}
282 /* global rockbox api */
283 static struct plugin_api
* rb
;
285 /* external bitmaps */
286 extern const fb_data jewels
[];
288 /* tile background colors */
289 #ifdef HAVE_LCD_COLOR
290 static const unsigned jewels_bkgd
[2] = {
291 LCD_RGBPACK(104, 63, 63),
292 LCD_RGBPACK(83, 44, 44)
297 * type is the jewel number 0-7
298 * falling if the jewel is falling
299 * delete marks the jewel for deletion
307 /* the game context struct
308 * score is the current level score
309 * segments is the number of cleared segments in the current run
310 * level is the current level
311 * type is the game type (normal or puzzle)
312 * highscores is the list of high scores
313 * resume denotes whether to resume the currently loaded game
314 * dirty denotes whether the high scores are out of sync with the saved file
315 * playboard is the game playing board (first row is hidden)
316 * num_jewels is the number of different jewels to use
318 struct game_context
{
320 unsigned int segments
;
323 unsigned int highscores
[NUM_SCORES
];
326 struct tile playboard
[BJ_HEIGHT
][BJ_WIDTH
];
327 unsigned int num_jewels
;
330 #define MAX_NUM_JEWELS 7
332 #define MAX_PUZZLE_TILES 4
333 #define NUM_PUZZLE_LEVELS 10
341 struct puzzle_level
{
342 unsigned int num_jewels
;
343 unsigned int num_tiles
;
344 struct puzzle_tile tiles
[MAX_PUZZLE_TILES
];
347 #define PUZZLE_TILE_UP 1
348 #define PUZZLE_TILE_DOWN 2
349 #define PUZZLE_TILE_LEFT 4
350 #define PUZZLE_TILE_RIGHT 8
352 struct puzzle_level puzzle_levels
[NUM_PUZZLE_LEVELS
] = {
353 { 5, 2, { {3, 3, PUZZLE_TILE_RIGHT
},
354 {4, 2, PUZZLE_TILE_LEFT
} } },
355 { 5, 2, { {3, 2, PUZZLE_TILE_DOWN
},
356 {3, 4, PUZZLE_TILE_UP
} } },
357 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN
},
358 {3, 4, PUZZLE_TILE_UP
|PUZZLE_TILE_DOWN
},
359 {3, 6, PUZZLE_TILE_UP
} } },
360 { 6, 3, { {3, 2, PUZZLE_TILE_RIGHT
},
361 {4, 3, PUZZLE_TILE_LEFT
|PUZZLE_TILE_RIGHT
},
362 {5, 4, PUZZLE_TILE_LEFT
} } },
363 { 6, 2, { {3, 4, PUZZLE_TILE_RIGHT
},
364 {4, 2, PUZZLE_TILE_LEFT
} } },
365 { 6, 2, { {3, 2, PUZZLE_TILE_DOWN
},
366 {4, 4, PUZZLE_TILE_UP
} } },
367 { 7, 4, { {3, 2, PUZZLE_TILE_RIGHT
|PUZZLE_TILE_DOWN
},
368 {4, 3, PUZZLE_TILE_LEFT
|PUZZLE_TILE_DOWN
},
369 {3, 4, PUZZLE_TILE_RIGHT
|PUZZLE_TILE_UP
},
370 {4, 4, PUZZLE_TILE_LEFT
|PUZZLE_TILE_UP
} } },
371 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN
},
372 {4, 4, PUZZLE_TILE_UP
|PUZZLE_TILE_DOWN
},
373 {3, 6, PUZZLE_TILE_UP
} } },
374 { 7, 3, { {2, 2, PUZZLE_TILE_RIGHT
},
375 {4, 1, PUZZLE_TILE_LEFT
|PUZZLE_TILE_RIGHT
},
376 {5, 4, PUZZLE_TILE_LEFT
} } },
377 { 7, 4, { {3, 0, PUZZLE_TILE_RIGHT
|PUZZLE_TILE_DOWN
},
378 {5, 0, PUZZLE_TILE_LEFT
|PUZZLE_TILE_DOWN
},
379 {2, 7, PUZZLE_TILE_RIGHT
|PUZZLE_TILE_UP
},
380 {4, 7, PUZZLE_TILE_LEFT
|PUZZLE_TILE_UP
} } },
383 /*****************************************************************************
384 * jewels_init() initializes jewels data structures.
385 ******************************************************************************/
386 static void jewels_init(struct game_context
* bj
) {
387 /* seed the rand generator */
388 rb
->srand(*rb
->current_tick
);
390 /* check for resumed game */
401 /* clear playing board */
402 rb
->memset(bj
->playboard
, 0, sizeof(bj
->playboard
));
405 /*****************************************************************************
406 * jewels_setcolors() set the foreground and background colors.
407 ******************************************************************************/
408 static inline void jewels_setcolors(void) {
409 #ifdef HAVE_LCD_COLOR
410 rb
->lcd_set_background(LCD_RGBPACK(49, 26, 26));
411 rb
->lcd_set_foreground(LCD_RGBPACK(210, 181, 181));
415 /*****************************************************************************
416 * jewels_drawboard() redraws the entire game board.
417 ******************************************************************************/
418 static void jewels_drawboard(struct game_context
* bj
) {
421 unsigned int tempscore
;
422 char *title
= "Level";
425 tempscore
= (bj
->score
>LEVEL_PTS
? LEVEL_PTS
: bj
->score
);
428 rb
->lcd_clear_display();
430 /* dispay playing board */
431 for(i
=0; i
<BJ_HEIGHT
-1; i
++){
432 for(j
=0; j
<BJ_WIDTH
; j
++){
433 #ifdef HAVE_LCD_COLOR
434 rb
->lcd_set_foreground(jewels_bkgd
[(i
+j
)%2]);
435 rb
->lcd_fillrect(j
*TILE_WIDTH
, i
*TILE_HEIGHT
+YOFS
,
436 TILE_WIDTH
, TILE_HEIGHT
);
437 rb
->lcd_bitmap_transparent_part(jewels
,
438 0, TILE_HEIGHT
*(bj
->playboard
[i
+1][j
].type
),
439 TILE_WIDTH
, j
*TILE_WIDTH
, i
*TILE_HEIGHT
+YOFS
,
440 TILE_WIDTH
, TILE_HEIGHT
);
442 rb
->lcd_bitmap_part(jewels
,
443 0, TILE_HEIGHT
*(bj
->playboard
[i
+1][j
].type
),
444 TILE_WIDTH
, j
*TILE_WIDTH
, i
*TILE_HEIGHT
+YOFS
,
445 TILE_WIDTH
, TILE_HEIGHT
);
450 #if LCD_WIDTH > LCD_HEIGHT /* horizontal layout */
452 /* draw separator lines */
454 rb
->lcd_vline(BJ_WIDTH
*TILE_WIDTH
, 0, LCD_HEIGHT
-1);
455 rb
->lcd_hline(BJ_WIDTH
*TILE_WIDTH
, LCD_WIDTH
-1, 18);
456 rb
->lcd_hline(BJ_WIDTH
*TILE_WIDTH
, LCD_WIDTH
-1, LCD_HEIGHT
-10);
458 /* draw progress bar */
459 #ifdef HAVE_LCD_COLOR
460 rb
->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
462 rb
->lcd_fillrect(BJ_WIDTH
*TILE_WIDTH
+(LCD_WIDTH
-BJ_WIDTH
*TILE_WIDTH
)/4,
463 (LCD_HEIGHT
-10)-(((LCD_HEIGHT
-10)-18)*
464 tempscore
/LEVEL_PTS
),
465 (LCD_WIDTH
-BJ_WIDTH
*TILE_WIDTH
)/2,
466 ((LCD_HEIGHT
-10)-18)*tempscore
/LEVEL_PTS
);
467 #ifdef HAVE_LCD_COLOR
468 rb
->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
469 rb
->lcd_drawrect(BJ_WIDTH
*TILE_WIDTH
+(LCD_WIDTH
-BJ_WIDTH
*TILE_WIDTH
)/4+1,
470 (LCD_HEIGHT
-10)-(((LCD_HEIGHT
-10)-18)*
471 tempscore
/LEVEL_PTS
)+1,
472 (LCD_WIDTH
-BJ_WIDTH
*TILE_WIDTH
)/2-2,
473 ((LCD_HEIGHT
-10)-18)*tempscore
/LEVEL_PTS
-1);
475 rb
->lcd_drawrect(BJ_WIDTH
*TILE_WIDTH
+(LCD_WIDTH
-BJ_WIDTH
*TILE_WIDTH
)/4,
476 (LCD_HEIGHT
-10)-(((LCD_HEIGHT
-10)-18)*
477 tempscore
/LEVEL_PTS
),
478 (LCD_WIDTH
-BJ_WIDTH
*TILE_WIDTH
)/2,
479 ((LCD_HEIGHT
-10)-18)*tempscore
/LEVEL_PTS
+1);
483 rb
->lcd_getstringsize(title
, &w
, &h
);
484 rb
->lcd_putsxy(LCD_WIDTH
-(LCD_WIDTH
-BJ_WIDTH
*TILE_WIDTH
)/2-w
/2, 1, title
);
486 rb
->snprintf(str
, 4, "%d", bj
->level
);
487 rb
->lcd_getstringsize(str
, &w
, &h
);
488 rb
->lcd_putsxy(LCD_WIDTH
-(LCD_WIDTH
-BJ_WIDTH
*TILE_WIDTH
)/2-w
/2, 10, str
);
490 rb
->snprintf(str
, 6, "%d", (bj
->level
-1)*LEVEL_PTS
+bj
->score
);
491 rb
->lcd_getstringsize(str
, &w
, &h
);
492 rb
->lcd_putsxy(LCD_WIDTH
-(LCD_WIDTH
-BJ_WIDTH
*TILE_WIDTH
)/2-w
/2,
495 #elif LCD_WIDTH < LCD_HEIGHT /* vertical layout */
497 /* draw separator lines */
499 rb
->lcd_hline(0, LCD_WIDTH
-1, 8*TILE_HEIGHT
+YOFS
);
500 rb
->lcd_hline(0, LCD_WIDTH
-1, LCD_HEIGHT
-14);
501 rb
->lcd_vline(LCD_WIDTH
/2, LCD_HEIGHT
-14, LCD_HEIGHT
-1);
503 /* draw progress bar */
504 #ifdef HAVE_LCD_COLOR
505 rb
->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
507 rb
->lcd_fillrect(0, (8*TILE_HEIGHT
+YOFS
)
508 +(LCD_HEIGHT
-14-(8*TILE_HEIGHT
+YOFS
))/4,
509 LCD_WIDTH
*tempscore
/LEVEL_PTS
,
510 (LCD_HEIGHT
-14-(8*TILE_HEIGHT
+YOFS
))/2);
511 #ifdef HAVE_LCD_COLOR
512 rb
->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
513 rb
->lcd_drawrect(1, (8*TILE_HEIGHT
+YOFS
)
514 +(LCD_HEIGHT
-14-(8*TILE_HEIGHT
+YOFS
))/4+1,
515 LCD_WIDTH
*tempscore
/LEVEL_PTS
-1,
516 (LCD_HEIGHT
-14-(8*TILE_HEIGHT
+YOFS
))/2-2);
518 rb
->lcd_drawrect(0, (8*TILE_HEIGHT
+YOFS
)
519 +(LCD_HEIGHT
-14-(8*TILE_HEIGHT
+YOFS
))/4,
520 LCD_WIDTH
*tempscore
/LEVEL_PTS
+1,
521 (LCD_HEIGHT
-14-(8*TILE_HEIGHT
+YOFS
))/2);
525 rb
->snprintf(str
, 10, "%s %d", title
, bj
->level
);
526 rb
->lcd_putsxy(1, LCD_HEIGHT
-10, str
);
528 rb
->snprintf(str
, 6, "%d", (bj
->level
-1)*LEVEL_PTS
+bj
->score
);
529 rb
->lcd_getstringsize(str
, &w
, &h
);
530 rb
->lcd_putsxy((LCD_WIDTH
-2)-w
, LCD_HEIGHT
-10, str
);
532 #else /* square layout */
534 /* draw separator lines */
536 rb
->lcd_hline(0, LCD_WIDTH
-1, 8*TILE_HEIGHT
+YOFS
);
537 rb
->lcd_vline(BJ_WIDTH
*TILE_WIDTH
, 0, 8*TILE_HEIGHT
+YOFS
);
538 rb
->lcd_vline(LCD_WIDTH
/2, 8*TILE_HEIGHT
+YOFS
, LCD_HEIGHT
-1);
540 /* draw progress bar */
541 #ifdef HAVE_LCD_COLOR
542 rb
->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
544 rb
->lcd_fillrect(BJ_WIDTH
*TILE_WIDTH
+(LCD_WIDTH
-BJ_WIDTH
*TILE_WIDTH
)/4,
545 (8*TILE_HEIGHT
+YOFS
)-(8*TILE_HEIGHT
+YOFS
)
546 *tempscore
/LEVEL_PTS
,
547 (LCD_WIDTH
-BJ_WIDTH
*TILE_WIDTH
)/2,
548 (8*TILE_HEIGHT
+YOFS
)*tempscore
/LEVEL_PTS
);
549 #ifdef HAVE_LCD_COLOR
550 rb
->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
551 rb
->lcd_drawrect(BJ_WIDTH
*TILE_WIDTH
+(LCD_WIDTH
-BJ_WIDTH
*TILE_WIDTH
)/4+1,
552 (8*TILE_HEIGHT
+YOFS
)-(8*TILE_HEIGHT
+YOFS
)
553 *tempscore
/LEVEL_PTS
+1,
554 (LCD_WIDTH
-BJ_WIDTH
*TILE_WIDTH
)/2-2,
555 (8*TILE_HEIGHT
+YOFS
)*tempscore
/LEVEL_PTS
-1);
557 rb
->lcd_drawrect(BJ_WIDTH
*TILE_WIDTH
+(LCD_WIDTH
-BJ_WIDTH
*TILE_WIDTH
)/4,
558 (8*TILE_HEIGHT
+YOFS
)-(8*TILE_HEIGHT
+YOFS
)
559 *tempscore
/LEVEL_PTS
,
560 (LCD_WIDTH
-BJ_WIDTH
*TILE_WIDTH
)/2,
561 (8*TILE_HEIGHT
+YOFS
)*tempscore
/LEVEL_PTS
+1);
565 rb
->snprintf(str
, 10, "%s %d", title
, bj
->level
);
566 rb
->lcd_putsxy(1, LCD_HEIGHT
-(LCD_HEIGHT
-(8*TILE_HEIGHT
+YOFS
))/2-3, str
);
568 rb
->snprintf(str
, 6, "%d", (bj
->level
-1)*LEVEL_PTS
+bj
->score
);
569 rb
->lcd_getstringsize(str
, &w
, &h
);
570 rb
->lcd_putsxy((LCD_WIDTH
-2)-w
,
571 LCD_HEIGHT
-(LCD_HEIGHT
-(8*TILE_HEIGHT
+YOFS
))/2-3, str
);
578 /*****************************************************************************
579 * jewels_showmenu() displays the chosen menu after performing the chosen
581 ******************************************************************************/
582 static enum menu_result
jewels_showmenu(struct jewels_menu
* menu
,
589 /* handle menu command */
592 menu
->selected
= (menu
->selected
+1)%menu
->itemcnt
;
596 menu
->selected
= (menu
->selected
-1+menu
->itemcnt
)%menu
->itemcnt
;
600 return menu
->items
[menu
->selected
].res
;
606 /* clear menu area */
607 firstline
= (LCD_HEIGHT
/FONT_HEIGHT
-(menu
->itemcnt
+3))/2;
609 rb
->lcd_set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
610 rb
->lcd_fillrect((LCD_WIDTH
-MENU_WIDTH
)/2, firstline
*FONT_HEIGHT
,
611 MENU_WIDTH
, (menu
->itemcnt
+3)*FONT_HEIGHT
);
612 rb
->lcd_set_drawmode(DRMODE_SOLID
);
615 rb
->lcd_drawrect((LCD_WIDTH
-MENU_WIDTH
)/2-1, firstline
*FONT_HEIGHT
-1,
616 MENU_WIDTH
+2, (menu
->itemcnt
+3)*FONT_HEIGHT
+2);
617 rb
->lcd_hline((LCD_WIDTH
-MENU_WIDTH
)/2-1,
618 (LCD_WIDTH
-MENU_WIDTH
)/2-1+MENU_WIDTH
+2,
619 (firstline
+1)*FONT_HEIGHT
);
622 /* draw menu items */
623 rb
->lcd_getstringsize(menu
->title
, &w
, &h
);
624 rb
->lcd_putsxy((LCD_WIDTH
-w
)/2, firstline
*FONT_HEIGHT
, menu
->title
);
626 for(i
=0; i
<menu
->itemcnt
; i
++) {
627 if(i
== menu
->selected
) {
628 rb
->lcd_set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
630 rb
->lcd_putsxy((LCD_WIDTH
-MENU_WIDTH
)/2, (firstline
+i
+2)*FONT_HEIGHT
,
631 menu
->items
[i
].text
);
632 if(i
== menu
->selected
) {
633 rb
->lcd_set_drawmode(DRMODE_SOLID
);
637 adj
= (firstline
== 0 ? 0 : 1);
638 rb
->lcd_update_rect((LCD_WIDTH
-MENU_WIDTH
)/2-1, firstline
*FONT_HEIGHT
-adj
,
639 MENU_WIDTH
+2, (menu
->itemcnt
+3)*FONT_HEIGHT
+2*adj
);
643 /*****************************************************************************
644 * jewels_putjewels() makes the jewels fall to fill empty spots and adds
645 * new random jewels at the empty spots at the top of each row.
646 ******************************************************************************/
647 static void jewels_putjewels(struct game_context
* bj
){
650 long lasttick
, currenttick
;
652 /* loop to make all the jewels fall */
654 /* mark falling jewels and add new jewels to hidden top row*/
657 for(j
=0; j
<BJ_WIDTH
; j
++) {
658 if(bj
->playboard
[1][j
].type
== 0) {
659 bj
->playboard
[0][j
].type
= rb
->rand()%bj
->num_jewels
+1;
661 for(i
=BJ_HEIGHT
-2; i
>=0; i
--) {
662 if(!mark
&& bj
->playboard
[i
+1][j
].type
== 0) {
666 if(mark
) bj
->playboard
[i
][j
].falling
= true;
668 /*if(bj->playboard[1][j].falling) {
669 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
670 bj->playboard[0][j].falling = true;
675 /* break if there are no falling jewels */
678 /* animate falling jewels */
679 lasttick
= *rb
->current_tick
;
681 for(k
=1; k
<=8; k
++) {
682 for(i
=BJ_HEIGHT
-2; i
>=0; i
--) {
683 for(j
=0; j
<BJ_WIDTH
; j
++) {
684 if(bj
->playboard
[i
][j
].falling
&&
685 bj
->playboard
[i
][j
].type
!= 0) {
686 /* clear old position */
687 #ifdef HAVE_LCD_COLOR
689 rb
->lcd_set_foreground(rb
->lcd_get_background());
691 rb
->lcd_set_foreground(jewels_bkgd
[(i
-1+j
)%2]);
693 rb
->lcd_fillrect(j
*TILE_WIDTH
, (i
-1)*TILE_HEIGHT
+YOFS
,
694 TILE_WIDTH
, TILE_HEIGHT
);
695 if(bj
->playboard
[i
+1][j
].type
== 0) {
696 rb
->lcd_set_foreground(jewels_bkgd
[(i
+j
)%2]);
697 rb
->lcd_fillrect(j
*TILE_WIDTH
, i
*TILE_HEIGHT
+YOFS
,
698 TILE_WIDTH
, TILE_HEIGHT
);
701 rb
->lcd_set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
702 rb
->lcd_fillrect(j
*TILE_WIDTH
, (i
-1)*TILE_HEIGHT
+YOFS
,
703 TILE_WIDTH
, TILE_HEIGHT
);
704 if(bj
->playboard
[i
+1][j
].type
== 0) {
705 rb
->lcd_fillrect(j
*TILE_WIDTH
, i
*TILE_HEIGHT
+YOFS
,
706 TILE_WIDTH
, TILE_HEIGHT
);
708 rb
->lcd_set_drawmode(DRMODE_SOLID
);
711 /* draw new position */
712 #ifdef HAVE_LCD_COLOR
713 rb
->lcd_bitmap_transparent_part(jewels
, 0,
714 TILE_HEIGHT
*(bj
->playboard
[i
][j
].type
),
715 TILE_WIDTH
, j
*TILE_WIDTH
,
716 (i
-1)*TILE_HEIGHT
+YOFS
+
717 ((((TILE_HEIGHT
<<10)*k
)/8)>>10),
718 TILE_WIDTH
, TILE_HEIGHT
);
720 rb
->lcd_bitmap_part(jewels
, 0,
721 TILE_HEIGHT
*(bj
->playboard
[i
][j
].type
),
722 TILE_WIDTH
, j
*TILE_WIDTH
,
723 (i
-1)*TILE_HEIGHT
+YOFS
+
724 ((((TILE_HEIGHT
<<10)*k
)/8)>>10),
725 TILE_WIDTH
, TILE_HEIGHT
);
731 rb
->lcd_update_rect(0, 0, TILE_WIDTH
*8, LCD_HEIGHT
);
734 /* framerate limiting */
735 currenttick
= *rb
->current_tick
;
736 if(currenttick
-lasttick
< HZ
/MAX_FPS
) {
737 rb
->sleep((HZ
/MAX_FPS
)-(currenttick
-lasttick
));
741 lasttick
= currenttick
;
744 /* shift jewels down */
745 for(j
=0; j
<BJ_WIDTH
; j
++) {
746 for(i
=BJ_HEIGHT
-1; i
>=1; i
--) {
747 if(bj
->playboard
[i
-1][j
].falling
) {
748 bj
->playboard
[i
][j
].type
= bj
->playboard
[i
-1][j
].type
;
753 /* clear out top row */
754 for(j
=0; j
<BJ_WIDTH
; j
++) {
755 bj
->playboard
[0][j
].type
= 0;
758 /* mark everything not falling */
759 for(i
=0; i
<BJ_HEIGHT
; i
++) {
760 for(j
=0; j
<BJ_WIDTH
; j
++) {
761 bj
->playboard
[i
][j
].falling
= false;
767 /*****************************************************************************
768 * jewels_clearjewels() finds all the connected rows and columns and
769 * calculates and returns the points earned.
770 ******************************************************************************/
771 static unsigned int jewels_clearjewels(struct game_context
* bj
) {
774 unsigned int points
= 0;
776 /* check for connected rows */
777 for(i
=1; i
<BJ_HEIGHT
; i
++) {
780 for(j
=0; j
<BJ_WIDTH
; j
++) {
781 if(bj
->playboard
[i
][j
].type
== last
&&
782 bj
->playboard
[i
][j
].type
!= 0 &&
783 bj
->playboard
[i
][j
].type
<= MAX_NUM_JEWELS
) {
788 points
+= bj
->segments
;
789 bj
->playboard
[i
][j
].delete = true;
790 bj
->playboard
[i
][j
-1].delete = true;
791 bj
->playboard
[i
][j
-2].delete = true;
794 bj
->playboard
[i
][j
].delete = true;
798 last
= bj
->playboard
[i
][j
].type
;
803 /* check for connected columns */
804 for(j
=0; j
<BJ_WIDTH
; j
++) {
807 for(i
=1; i
<BJ_HEIGHT
; i
++) {
808 if(bj
->playboard
[i
][j
].type
!= 0 &&
809 bj
->playboard
[i
][j
].type
== last
&&
810 bj
->playboard
[i
][j
].type
<= MAX_NUM_JEWELS
) {
815 points
+= bj
->segments
;
816 bj
->playboard
[i
][j
].delete = true;
817 bj
->playboard
[i
-1][j
].delete = true;
818 bj
->playboard
[i
-2][j
].delete = true;
821 bj
->playboard
[i
][j
].delete = true;
825 last
= bj
->playboard
[i
][j
].type
;
830 /* clear deleted jewels */
831 for(i
=1; i
<BJ_HEIGHT
; i
++) {
832 for(j
=0; j
<BJ_WIDTH
; j
++) {
833 if(bj
->playboard
[i
][j
].delete) {
834 bj
->playboard
[i
][j
].delete = false;
835 bj
->playboard
[i
][j
].type
= 0;
843 /*****************************************************************************
844 * jewels_runboard() runs the board until it settles in a fixed state and
845 * returns points earned.
846 ******************************************************************************/
847 static unsigned int jewels_runboard(struct game_context
* bj
) {
848 unsigned int points
= 0;
853 while((ret
= jewels_clearjewels(bj
)) > 0) {
855 jewels_drawboard(bj
);
856 jewels_putjewels(bj
);
862 /*****************************************************************************
863 * jewels_swapjewels() swaps two jewels as long as it results in points and
864 * returns points earned.
865 ******************************************************************************/
866 static unsigned int jewels_swapjewels(struct game_context
* bj
,
867 int x
, int y
, int direc
) {
869 int horzmod
, vertmod
;
872 unsigned int points
= 0;
873 long lasttick
, currenttick
;
875 /* check for invalid parameters */
876 if(x
< 0 || x
>= BJ_WIDTH
|| y
< 0 || y
>= BJ_HEIGHT
-1 ||
877 direc
< SWAP_UP
|| direc
> SWAP_LEFT
) {
881 /* check for invalid directions */
882 if((x
== 0 && direc
== SWAP_LEFT
) ||
883 (x
== BJ_WIDTH
-1 && direc
== SWAP_RIGHT
) ||
884 (y
== 0 && direc
== SWAP_UP
) ||
885 (y
== BJ_HEIGHT
-2 && direc
== SWAP_DOWN
)) {
889 /* set direction variables */
895 movelen
= TILE_HEIGHT
;
899 movelen
= TILE_WIDTH
;
903 movelen
= TILE_HEIGHT
;
907 movelen
= TILE_WIDTH
;
912 lasttick
= *rb
->current_tick
;
914 /* animate swapping jewels */
915 for(k
=0; k
<=8; k
++) {
916 /* clear old position */
917 #ifdef HAVE_LCD_COLOR
918 rb
->lcd_set_foreground(jewels_bkgd
[(x
+y
)%2]);
919 rb
->lcd_fillrect(x
*TILE_WIDTH
,
921 TILE_WIDTH
, TILE_HEIGHT
);
922 rb
->lcd_set_foreground(jewels_bkgd
[(x
+horzmod
+y
+vertmod
)%2]);
923 rb
->lcd_fillrect((x
+horzmod
)*TILE_WIDTH
,
924 (y
+vertmod
)*TILE_HEIGHT
+YOFS
,
925 TILE_WIDTH
, TILE_HEIGHT
);
927 rb
->lcd_set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
928 rb
->lcd_fillrect(x
*TILE_WIDTH
,
930 TILE_WIDTH
, TILE_HEIGHT
);
931 rb
->lcd_fillrect((x
+horzmod
)*TILE_WIDTH
,
932 (y
+vertmod
)*TILE_HEIGHT
+YOFS
,
933 TILE_WIDTH
, TILE_HEIGHT
);
934 rb
->lcd_set_drawmode(DRMODE_SOLID
);
936 /* draw new position */
937 #ifdef HAVE_LCD_COLOR
938 rb
->lcd_bitmap_transparent_part(jewels
,
939 0, TILE_HEIGHT
*(bj
->playboard
940 [y
+1+vertmod
][x
+horzmod
].type
), TILE_WIDTH
,
941 (x
+horzmod
)*TILE_WIDTH
-horzmod
*
942 ((((movelen
<<10)*k
)/8)>>10),
943 (y
+vertmod
)*TILE_HEIGHT
-vertmod
*
944 ((((movelen
<<10)*k
)/8)>>10)+YOFS
,
945 TILE_WIDTH
, TILE_HEIGHT
);
946 rb
->lcd_bitmap_transparent_part(jewels
,
947 0, TILE_HEIGHT
*(bj
->playboard
[y
+1][x
].type
),
948 TILE_WIDTH
, x
*TILE_WIDTH
+horzmod
*
949 ((((movelen
<<10)*k
)/8)>>10),
950 y
*TILE_HEIGHT
+vertmod
*
951 ((((movelen
<<10)*k
)/8)>>10)+YOFS
,
952 TILE_WIDTH
, TILE_HEIGHT
);
954 rb
->lcd_bitmap_part(jewels
,
955 0, TILE_HEIGHT
*(bj
->playboard
956 [y
+1+vertmod
][x
+horzmod
].type
), TILE_WIDTH
,
957 (x
+horzmod
)*TILE_WIDTH
-horzmod
*
958 ((((movelen
<<10)*k
)/8)>>10),
959 (y
+vertmod
)*TILE_HEIGHT
-vertmod
*
960 ((((movelen
<<10)*k
)/8)>>10)+YOFS
,
961 TILE_WIDTH
, TILE_HEIGHT
);
962 rb
->lcd_set_drawmode(DRMODE_FG
);
963 rb
->lcd_bitmap_part(jewels
,
964 0, TILE_HEIGHT
*(bj
->playboard
[y
+1][x
].type
),
965 TILE_WIDTH
, x
*TILE_WIDTH
+horzmod
*
966 ((((movelen
<<10)*k
)/8)>>10),
967 y
*TILE_HEIGHT
+vertmod
*
968 ((((movelen
<<10)*k
)/8)>>10)+YOFS
,
969 TILE_WIDTH
, TILE_HEIGHT
);
970 rb
->lcd_set_drawmode(DRMODE_SOLID
);
973 rb
->lcd_update_rect(0, 0, TILE_WIDTH
*8, LCD_HEIGHT
);
976 /* framerate limiting */
977 currenttick
= *rb
->current_tick
;
978 if(currenttick
-lasttick
< HZ
/MAX_FPS
) {
979 rb
->sleep((HZ
/MAX_FPS
)-(currenttick
-lasttick
));
983 lasttick
= currenttick
;
987 int temp
= bj
->playboard
[y
+1][x
].type
;
988 bj
->playboard
[y
+1][x
].type
=
989 bj
->playboard
[y
+1+vertmod
][x
+horzmod
].type
;
990 bj
->playboard
[y
+1+vertmod
][x
+horzmod
].type
= temp
;
994 points
= jewels_runboard(bj
);
1005 /*****************************************************************************
1006 * jewels_movesavail() uses pattern matching to see if there are any
1007 * available move left.
1008 ******************************************************************************/
1009 static bool jewels_movesavail(struct game_context
* bj
) {
1014 for(i
=1; i
<BJ_HEIGHT
; i
++) {
1015 for(j
=0; j
<BJ_WIDTH
; j
++) {
1016 mytype
= bj
->playboard
[i
][j
].type
;
1017 if(mytype
== 0 || mytype
> MAX_NUM_JEWELS
) continue;
1019 /* check horizontal patterns */
1020 if(j
<= BJ_WIDTH
-3) {
1022 if(bj
->playboard
[i
-1][j
+1].type
== mytype
) {
1023 if(bj
->playboard
[i
-1][j
+2].type
== mytype
)
1024 {moves
= true; break;}
1025 if(bj
->playboard
[i
][j
+2].type
== mytype
)
1026 {moves
= true; break;}
1028 if(bj
->playboard
[i
][j
+1].type
== mytype
) {
1029 if(bj
->playboard
[i
-1][j
+2].type
== mytype
)
1030 {moves
= true; break;}
1034 if(j
<= BJ_WIDTH
-4) {
1035 if(bj
->playboard
[i
][j
+3].type
== mytype
) {
1036 if(bj
->playboard
[i
][j
+1].type
== mytype
)
1037 {moves
= true; break;}
1038 if(bj
->playboard
[i
][j
+2].type
== mytype
)
1039 {moves
= true; break;}
1043 if(i
< BJ_HEIGHT
-1) {
1044 if(bj
->playboard
[i
][j
+1].type
== mytype
) {
1045 if(bj
->playboard
[i
+1][j
+2].type
== mytype
)
1046 {moves
= true; break;}
1048 if(bj
->playboard
[i
+1][j
+1].type
== mytype
) {
1049 if(bj
->playboard
[i
][j
+2].type
== mytype
)
1050 {moves
= true; break;}
1051 if(bj
->playboard
[i
+1][j
+2].type
== mytype
)
1052 {moves
= true; break;}
1057 /* check vertical patterns */
1058 if(i
<= BJ_HEIGHT
-3) {
1060 if(bj
->playboard
[i
+1][j
-1].type
== mytype
) {
1061 if(bj
->playboard
[i
+2][j
-1].type
== mytype
)
1062 {moves
= true; break;}
1063 if(bj
->playboard
[i
+2][j
].type
== mytype
)
1064 {moves
= true; break;}
1066 if(bj
->playboard
[i
+1][j
].type
== mytype
) {
1067 if(bj
->playboard
[i
+2][j
-1].type
== mytype
)
1068 {moves
= true; break;}
1072 if(i
<= BJ_HEIGHT
-4) {
1073 if(bj
->playboard
[i
+3][j
].type
== mytype
) {
1074 if(bj
->playboard
[i
+1][j
].type
== mytype
)
1075 {moves
= true; break;}
1076 if(bj
->playboard
[i
+2][j
].type
== mytype
)
1077 {moves
= true; break;}
1081 if(j
< BJ_WIDTH
-1) {
1082 if(bj
->playboard
[i
+1][j
].type
== mytype
) {
1083 if(bj
->playboard
[i
+2][j
+1].type
== mytype
)
1084 {moves
= true; break;}
1086 if(bj
->playboard
[i
+1][j
+1].type
== mytype
) {
1087 if(bj
->playboard
[i
+2][j
].type
== mytype
)
1088 {moves
= true; break;}
1089 if (bj
->playboard
[i
+2][j
+1].type
== mytype
)
1090 {moves
= true; break;}
1102 /*****************************************************************************
1103 * jewels_puzzle_is_finished(bj) checks if the puzzle is finished.
1104 ******************************************************************************/
1105 static int jewels_puzzle_is_finished(struct game_context
* bj
) {
1107 for(i
=0; i
<BJ_HEIGHT
; i
++) {
1108 for(j
=0; j
<BJ_WIDTH
; j
++) {
1109 int mytype
= bj
->playboard
[i
][j
].type
;
1110 if(mytype
>MAX_NUM_JEWELS
) {
1111 mytype
-= MAX_NUM_JEWELS
;
1112 if(mytype
&PUZZLE_TILE_UP
) {
1113 if(i
==0 || bj
->playboard
[i
-1][j
].type
<=MAX_NUM_JEWELS
||
1114 !((bj
->playboard
[i
-1][j
].type
-MAX_NUM_JEWELS
)
1118 if(mytype
&PUZZLE_TILE_DOWN
) {
1119 if(i
==BJ_HEIGHT
-1 ||
1120 bj
->playboard
[i
+1][j
].type
<=MAX_NUM_JEWELS
||
1121 !((bj
->playboard
[i
+1][j
].type
-MAX_NUM_JEWELS
)
1125 if(mytype
&PUZZLE_TILE_LEFT
) {
1126 if(j
==0 || bj
->playboard
[i
][j
-1].type
<=MAX_NUM_JEWELS
||
1127 !((bj
->playboard
[i
][j
-1].type
-MAX_NUM_JEWELS
)
1128 &PUZZLE_TILE_RIGHT
))
1131 if(mytype
&PUZZLE_TILE_RIGHT
) {
1133 bj
->playboard
[i
][j
+1].type
<=MAX_NUM_JEWELS
||
1134 !((bj
->playboard
[i
][j
+1].type
-MAX_NUM_JEWELS
)
1144 /*****************************************************************************
1145 * jewels_initlevel() initialises a level.
1146 ******************************************************************************/
1147 static unsigned int jewels_initlevel(struct game_context
* bj
) {
1148 unsigned int points
= 0;
1151 case GAME_TYPE_NORMAL
:
1152 bj
->num_jewels
= MAX_NUM_JEWELS
;
1155 case GAME_TYPE_PUZZLE
:
1158 struct puzzle_tile
*tile
;
1160 bj
->num_jewels
= puzzle_levels
[bj
->level
-1].num_jewels
;
1162 for(i
=0; i
<BJ_HEIGHT
; i
++) {
1163 for(j
=0; j
<BJ_WIDTH
; j
++) {
1164 bj
->playboard
[i
][j
].type
= (rb
->rand()%bj
->num_jewels
)+1;
1165 bj
->playboard
[i
][j
].falling
= false;
1166 bj
->playboard
[i
][j
].delete = false;
1169 jewels_runboard(bj
);
1170 tile
= puzzle_levels
[bj
->level
-1].tiles
;
1171 for(i
=0; i
<puzzle_levels
[bj
->level
-1].num_tiles
; i
++, tile
++) {
1172 bj
->playboard
[tile
->y
+1][tile
->x
].type
= MAX_NUM_JEWELS
1179 jewels_drawboard(bj
);
1181 /* run the play board */
1182 jewels_putjewels(bj
);
1183 points
+= jewels_runboard(bj
);
1187 /*****************************************************************************
1188 * jewels_nextlevel() advances the game to the next level and returns
1190 ******************************************************************************/
1191 static unsigned int jewels_nextlevel(struct game_context
* bj
) {
1193 unsigned int points
= 0;
1196 case GAME_TYPE_NORMAL
:
1197 /* roll over score, change and display level */
1198 while(bj
->score
>= LEVEL_PTS
) {
1199 bj
->score
-= LEVEL_PTS
;
1201 rb
->splash(HZ
*2, "Level %d", bj
->level
);
1202 jewels_drawboard(bj
);
1205 /* randomly clear some jewels */
1206 for(i
=0; i
<16; i
++) {
1210 if(bj
->playboard
[y
][x
].type
!= 0) {
1212 bj
->playboard
[y
][x
].type
= 0;
1217 case GAME_TYPE_PUZZLE
:
1219 if(bj
->level
>NUM_PUZZLE_LEVELS
) {
1220 rb
->splash(HZ
*2, "You win!");
1223 rb
->splash(HZ
*2, "Level %d", bj
->level
);
1228 return jewels_initlevel(bj
);
1231 /*****************************************************************************
1232 * jewels_recordscore() inserts a high score into the high scores list and
1233 * returns the high score position.
1234 ******************************************************************************/
1235 static int jewels_recordscore(struct game_context
* bj
) {
1238 unsigned int current
, temp
;
1240 /* calculate total score */
1241 current
= (bj
->level
-1)*LEVEL_PTS
+bj
->score
;
1242 if(current
<= 0) return 0;
1244 /* insert the current score into the high scores */
1245 for(i
=0; i
<NUM_SCORES
; i
++) {
1246 if(current
>= bj
->highscores
[i
]) {
1251 temp
= bj
->highscores
[i
];
1252 bj
->highscores
[i
] = current
;
1260 /*****************************************************************************
1261 * jewels_loadscores() loads the high scores saved file.
1262 ******************************************************************************/
1263 static void jewels_loadscores(struct game_context
* bj
) {
1268 /* clear high scores */
1269 rb
->memset(bj
->highscores
, 0, sizeof(bj
->highscores
));
1271 /* open scores file */
1272 fd
= rb
->open(SCORE_FILE
, O_RDONLY
);
1275 /* read in high scores */
1276 if(rb
->read(fd
, bj
->highscores
, sizeof(bj
->highscores
)) <= 0) {
1277 /* scores are bad, reset */
1278 rb
->memset(bj
->highscores
, 0, sizeof(bj
->highscores
));
1284 /*****************************************************************************
1285 * jewels_savescores() saves the high scores saved file.
1286 ******************************************************************************/
1287 static void jewels_savescores(struct game_context
* bj
) {
1290 /* write out the high scores to the save file */
1291 fd
= rb
->open(SCORE_FILE
, O_WRONLY
|O_CREAT
);
1292 rb
->write(fd
, bj
->highscores
, sizeof(bj
->highscores
));
1297 /*****************************************************************************
1298 * jewels_loadgame() loads the saved game and returns load success.
1299 ******************************************************************************/
1300 static bool jewels_loadgame(struct game_context
* bj
) {
1302 bool loaded
= false;
1304 /* open game file */
1305 fd
= rb
->open(SAVE_FILE
, O_RDONLY
);
1306 if(fd
< 0) return loaded
;
1308 /* read in saved game */
1310 if(rb
->read(fd
, &bj
->score
, sizeof(bj
->score
)) <= 0) break;
1311 if(rb
->read(fd
, &bj
->level
, sizeof(bj
->level
)) <= 0) break;
1312 if(rb
->read(fd
, &bj
->type
, sizeof(bj
->type
)) <= 0) break;
1313 if(rb
->read(fd
, bj
->playboard
, sizeof(bj
->playboard
)) <= 0) break;
1321 /* delete saved file */
1322 rb
->remove(SAVE_FILE
);
1326 /*****************************************************************************
1327 * jewels_savegame() saves the current game state.
1328 ******************************************************************************/
1329 static void jewels_savegame(struct game_context
* bj
) {
1332 /* write out the game state to the save file */
1333 fd
= rb
->open(SAVE_FILE
, O_WRONLY
|O_CREAT
);
1334 rb
->write(fd
, &bj
->score
, sizeof(bj
->score
));
1335 rb
->write(fd
, &bj
->level
, sizeof(bj
->level
));
1336 rb
->write(fd
, &bj
->type
, sizeof(bj
->type
));
1337 rb
->write(fd
, bj
->playboard
, sizeof(bj
->playboard
));
1343 /*****************************************************************************
1344 * jewels_callback() is the default event handler callback which is called
1345 * on usb connect and shutdown.
1346 ******************************************************************************/
1347 static void jewels_callback(void* param
) {
1348 struct game_context
* bj
= (struct game_context
*) param
;
1350 rb
->splash(HZ
, "Saving high scores...");
1351 jewels_savescores(bj
);
1355 /*****************************************************************************
1356 * jewels_main() is the main game subroutine, it returns the final game status.
1357 ******************************************************************************/
1358 static int jewels_main(struct game_context
* bj
) {
1363 bool startgame
= false;
1364 bool inmenu
= false;
1365 bool selected
= false;
1366 enum menu_cmd cmd
= MCMD_NONE
;
1367 enum menu_result res
;
1369 /* the cursor coordinates */
1372 /* don't resume by default */
1375 /********************
1377 ********************/
1378 rb
->lcd_clear_display();
1381 res
= jewels_showmenu(&bjmenu
[0], cmd
);
1384 rb
->snprintf(str
, 18, "High Score: %d", bj
->highscores
[0]);
1385 rb
->lcd_getstringsize(str
, &w
, &h
);
1386 rb
->lcd_putsxy((LCD_WIDTH
-w
)/2, LCD_HEIGHT
-8, str
);
1392 bj
->type
= GAME_TYPE_NORMAL
;
1397 bj
->type
= GAME_TYPE_PUZZLE
;
1401 if(!jewels_loadgame(bj
)) {
1402 rb
->splash(HZ
*2, "Nothing to resume");
1403 rb
->lcd_clear_display();
1410 rb
->lcd_clear_display();
1412 /* room for a title? */
1414 if(LCD_HEIGHT
-NUM_SCORES
*8 >= 8) {
1415 rb
->snprintf(str
, 12, "%s", "High Scores");
1416 rb
->lcd_getstringsize(str
, &w
, &h
);
1417 rb
->lcd_putsxy((LCD_WIDTH
-w
)/2, 0, str
);
1421 /* print high scores */
1422 for(i
=0; i
<NUM_SCORES
; i
++) {
1423 rb
->snprintf(str
, 11, "#%02d: %d", i
+1, bj
->highscores
[i
]);
1424 rb
->lcd_puts(0, i
+j
, str
);
1429 button
= rb
->button_get(true);
1430 if(button
!= BUTTON_NONE
&& !(button
&BUTTON_REL
)) break;
1432 rb
->lcd_clear_display();
1436 /* welcome screen to display key bindings */
1437 rb
->lcd_clear_display();
1438 rb
->snprintf(str
, 5, "%s", "Help");
1439 rb
->lcd_getstringsize(str
, &w
, &h
);
1440 rb
->lcd_putsxy((LCD_WIDTH
-w
)/2, 0, str
);
1441 #if CONFIG_KEYPAD == RECORDER_PAD
1442 rb
->lcd_puts(0, 2, "Controls:");
1443 rb
->lcd_puts(0, 3, "Directions = move");
1444 rb
->lcd_puts(0, 4, "PLAY = select");
1445 rb
->lcd_puts(0, 5, "Long PLAY = menu");
1446 rb
->lcd_puts(0, 6, "OFF = cancel");
1447 #elif CONFIG_KEYPAD == ONDIO_PAD
1448 rb
->lcd_puts(0, 2, "Controls:");
1449 rb
->lcd_puts(0, 3, "Directions = move");
1450 rb
->lcd_puts(0, 4, "MENU = select");
1451 rb
->lcd_puts(0, 5, "Long MENU = menu");
1452 rb
->lcd_puts(0, 6, "OFF = cancel");
1453 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
1454 rb
->lcd_puts(0, 2, "Controls:");
1455 rb
->lcd_puts(0, 3, "Directions = move");
1456 rb
->lcd_puts(0, 4, "SELECT = select");
1457 rb
->lcd_puts(0, 5, "Long SELECT = menu");
1458 rb
->lcd_puts(0, 6, "PLAY = cancel");
1459 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
1460 rb
->lcd_puts(0, 2, "Swap pairs of jewels to");
1461 rb
->lcd_puts(0, 3, "form connected segments");
1462 rb
->lcd_puts(0, 4, "of three or more of the");
1463 rb
->lcd_puts(0, 5, "same type.");
1464 rb
->lcd_puts(0, 7, "Controls:");
1465 rb
->lcd_puts(0, 8, "Directions to move");
1466 rb
->lcd_puts(0, 9, "SELECT to select");
1467 rb
->lcd_puts(0, 10, "Long SELECT to show menu");
1468 rb
->lcd_puts(0, 11, "OFF to cancel");
1469 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
1470 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
1471 rb
->lcd_puts(0, 2, "Swap pairs of jewels to");
1472 rb
->lcd_puts(0, 3, "form connected segments");
1473 rb
->lcd_puts(0, 4, "of three or more of the");
1474 rb
->lcd_puts(0, 5, "same type.");
1475 rb
->lcd_puts(0, 7, "Controls:");
1476 rb
->lcd_puts(0, 8, "Directions or scroll to move");
1477 rb
->lcd_puts(0, 9, "SELECT to select");
1478 rb
->lcd_puts(0, 10, "Long SELECT to show menu");
1479 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
1480 rb
->lcd_puts(0, 2, "Swap pairs of jewels to");
1481 rb
->lcd_puts(0, 3, "form connected segments");
1482 rb
->lcd_puts(0, 4, "of three or more of the");
1483 rb
->lcd_puts(0, 5, "same type.");
1484 rb
->lcd_puts(0, 7, "Controls:");
1485 rb
->lcd_puts(0, 8, "Directions to move");
1486 rb
->lcd_puts(0, 9, "SELECT to select");
1487 rb
->lcd_puts(0, 10, "Long SELECT to show menu");
1488 rb
->lcd_puts(0, 11, "PLAY to cancel");
1489 #elif CONFIG_KEYPAD == GIGABEAT_PAD
1490 rb
->lcd_puts(0, 2, "Swap pairs of jewels to");
1491 rb
->lcd_puts(0, 3, "form connected segments");
1492 rb
->lcd_puts(0, 4, "of three or more of the");
1493 rb
->lcd_puts(0, 5, "same type.");
1494 rb
->lcd_puts(0, 7, "Controls:");
1495 rb
->lcd_puts(0, 8, "Directions to move");
1496 rb
->lcd_puts(0, 9, "SELECT to select");
1497 rb
->lcd_puts(0, 10, "Long SELECT to show menu");
1498 rb
->lcd_puts(0, 11, "POWER to cancel");
1499 #elif CONFIG_KEYPAD == SANSA_E200_PAD \
1500 || CONFIG_KEYPAD == SANSA_C200_PAD
1501 rb
->lcd_puts(0, 2, "Swap pairs of jewels to");
1502 rb
->lcd_puts(0, 3, "form connected segments");
1503 rb
->lcd_puts(0, 4, "of three or more of the");
1504 rb
->lcd_puts(0, 5, "same type.");
1505 rb
->lcd_puts(0, 7, "Controls:");
1506 rb
->lcd_puts(0, 8, "Directions to move");
1507 rb
->lcd_puts(0, 9, "SELECT to select");
1508 rb
->lcd_puts(0, 10, "Long SELECT to show menu");
1509 rb
->lcd_puts(0, 11, "POWER to cancel");
1510 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
1511 rb
->lcd_puts(0, 2, "Swap pairs of jewels");
1512 rb
->lcd_puts(0, 3, "to form connected");
1513 rb
->lcd_puts(0, 4, "segments of three or ");
1514 rb
->lcd_puts(0, 5, "more of the");
1515 rb
->lcd_puts(0, 6, "same type.");
1516 rb
->lcd_puts(0, 8, "Controls:");
1517 rb
->lcd_puts(0, 9, "Directions or scroll to move");
1518 rb
->lcd_puts(0, 10, "PLAY to select");
1519 rb
->lcd_puts(0, 11, "Long PLAY for menu");
1520 rb
->lcd_puts(0, 12, "POWER to cancel");
1522 #warning: missing help text.
1526 button
= rb
->button_get(true);
1527 if(button
!= BUTTON_NONE
&& !(button
&BUTTON_REL
)) break;
1529 rb
->lcd_clear_display();
1539 /* handle menu button presses */
1540 button
= rb
->button_get(true);
1542 #ifdef JEWELS_SCROLLWHEEL
1544 case (JEWELS_PREV
|BUTTON_REPEAT
):
1547 case (JEWELS_UP
|BUTTON_REPEAT
):
1551 #ifdef JEWELS_SCROLLWHEEL
1553 case (JEWELS_NEXT
|BUTTON_REPEAT
):
1556 case (JEWELS_DOWN
|BUTTON_REPEAT
):
1565 #ifdef JEWELS_CANCEL
1566 #ifdef JEWELS_RC_CANCEL
1567 case JEWELS_RC_CANCEL
:
1574 if(rb
->default_event_handler_ex(button
, jewels_callback
,
1575 (void*) bj
) == SYS_USB_CONNECTED
)
1581 /********************
1583 ********************/
1586 /********************
1588 ********************/
1589 bj
->score
+= jewels_initlevel(bj
);
1590 if (!jewels_movesavail(bj
)) {
1592 case GAME_TYPE_NORMAL
:
1595 case GAME_TYPE_PUZZLE
:
1597 rb
->splash(2*HZ
, "No more moves!");
1598 bj
->score
+= jewels_initlevel(bj
);
1599 } while(!jewels_movesavail(bj
));
1604 /**********************
1606 **********************/
1608 int no_movesavail
= false;
1611 /* refresh the board */
1612 jewels_drawboard(bj
);
1614 /* display the cursor */
1616 rb
->lcd_set_drawmode(DRMODE_COMPLEMENT
);
1617 rb
->lcd_fillrect(x
*TILE_WIDTH
, y
*TILE_HEIGHT
+YOFS
,
1618 TILE_WIDTH
, TILE_HEIGHT
);
1619 rb
->lcd_set_drawmode(DRMODE_SOLID
);
1621 rb
->lcd_drawrect(x
*TILE_WIDTH
, y
*TILE_HEIGHT
+YOFS
,
1622 TILE_WIDTH
, TILE_HEIGHT
);
1624 rb
->lcd_update_rect(x
*TILE_WIDTH
, y
*TILE_HEIGHT
+YOFS
,
1625 TILE_WIDTH
, TILE_HEIGHT
);
1627 res
= jewels_showmenu(&bjmenu
[1], cmd
);
1636 playback_control(rb
);
1637 rb
->lcd_setfont(FONT_SYSFIXED
);
1643 rb
->splash(HZ
, "Saving game...");
1644 jewels_savegame(bj
);
1651 return BJ_QUIT_FROM_GAME
;
1658 /* handle game button presses */
1659 button
= rb
->button_get(true);
1661 case JEWELS_LEFT
: /* move cursor left */
1662 case (JEWELS_LEFT
|BUTTON_REPEAT
):
1665 bj
->score
+= jewels_swapjewels(bj
, x
, y
, SWAP_LEFT
);
1667 if (!jewels_movesavail(bj
)) no_movesavail
= true;
1669 x
= (x
+BJ_WIDTH
-1)%BJ_WIDTH
;
1674 case JEWELS_RIGHT
: /* move cursor right */
1675 case (JEWELS_RIGHT
|BUTTON_REPEAT
):
1678 bj
->score
+= jewels_swapjewels(bj
, x
, y
, SWAP_RIGHT
);
1680 if (!jewels_movesavail(bj
)) no_movesavail
= true;
1689 case JEWELS_DOWN
: /* move cursor down */
1690 case (JEWELS_DOWN
|BUTTON_REPEAT
):
1693 bj
->score
+= jewels_swapjewels(bj
, x
, y
, SWAP_DOWN
);
1695 if (!jewels_movesavail(bj
)) no_movesavail
= true;
1697 y
= (y
+1)%(BJ_HEIGHT
-1);
1704 case JEWELS_UP
: /* move cursor up */
1705 case (JEWELS_UP
|BUTTON_REPEAT
):
1708 bj
->score
+= jewels_swapjewels(bj
, x
, y
, SWAP_UP
);
1710 if (!jewels_movesavail(bj
)) no_movesavail
= true;
1712 y
= (y
+(BJ_HEIGHT
-1)-1)%(BJ_HEIGHT
-1);
1719 #ifdef JEWELS_SCROLLWHEEL
1720 case JEWELS_PREV
: /* scroll backwards */
1721 case (JEWELS_PREV
|BUTTON_REPEAT
):
1725 y
= (y
+(BJ_HEIGHT
-1)-1)%(BJ_HEIGHT
-1);
1727 x
= (x
+BJ_WIDTH
-1)%BJ_WIDTH
;
1734 case JEWELS_NEXT
: /* scroll forwards */
1735 case (JEWELS_NEXT
|BUTTON_REPEAT
):
1738 if(x
== BJ_WIDTH
-1) {
1739 y
= (y
+1)%(BJ_HEIGHT
-1);
1749 case JEWELS_SELECT
: /* toggle selected */
1751 selected
= !selected
;
1757 case (JEWELS_SELECT
|BUTTON_REPEAT
): /* show menu */
1758 if(!inmenu
) inmenu
= true;
1761 #ifdef JEWELS_CANCEL
1762 #ifdef JEWELS_RC_CANCEL
1763 case JEWELS_RC_CANCEL
:
1765 case JEWELS_CANCEL
: /* end game */
1771 if(rb
->default_event_handler_ex(button
, jewels_callback
,
1772 (void*) bj
) == SYS_USB_CONNECTED
)
1777 if (no_movesavail
) {
1779 case GAME_TYPE_NORMAL
:
1782 case GAME_TYPE_PUZZLE
:
1784 rb
->splash(2*HZ
, "No more moves!");
1785 bj
->score
+= jewels_initlevel(bj
);
1786 } while(!jewels_movesavail(bj
));
1792 case GAME_TYPE_NORMAL
:
1793 if(bj
->score
>= LEVEL_PTS
) bj
->score
= jewels_nextlevel(bj
);
1796 case GAME_TYPE_PUZZLE
:
1797 if(jewels_puzzle_is_finished(bj
))
1798 bj
->score
+= jewels_nextlevel(bj
);
1804 /*****************************************************************************
1805 * plugin entry point.
1806 ******************************************************************************/
1807 enum plugin_status
plugin_start(struct plugin_api
* api
, void* parameter
) {
1808 struct game_context bj
;
1816 /* end of plugin init */
1818 /* load high scores */
1819 jewels_loadscores(&bj
);
1821 rb
->lcd_setfont(FONT_SYSFIXED
);
1823 rb
->lcd_set_backdrop(NULL
);
1828 switch(jewels_main(&bj
)){
1830 rb
->splash(HZ
*2, "No more moves!");
1831 /* fall through to BJ_END */
1835 if((position
= jewels_recordscore(&bj
))) {
1836 rb
->snprintf(str
, 19, "New high score #%d!", position
);
1837 rb
->splash(HZ
*2, str
);
1843 rb
->lcd_setfont(FONT_UI
);
1844 return PLUGIN_USB_CONNECTED
;
1848 rb
->splash(HZ
, "Saving high scores...");
1849 jewels_savescores(&bj
);
1854 case BJ_QUIT_FROM_GAME
:
1856 if((position
= jewels_recordscore(&bj
))) {
1857 rb
->snprintf(str
, 19, "New high score #%d!", position
);
1858 rb
->splash(HZ
*2, str
);
1862 rb
->splash(HZ
, "Saving high scores...");
1863 jewels_savescores(&bj
);
1873 rb
->lcd_setfont(FONT_UI
);
1877 #endif /* HAVE_LCD_BITMAP */