Hide symbols by default on 64 bit sim buildsto avoid clashing, fixes crashing on...
[kugel-rb.git] / apps / plugins / jewels.c
blobf46c3b23555ff1b8a1466cc80c4d4ea16262b9b2
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"
23 #include "playback_control.h"
25 #ifdef HAVE_LCD_BITMAP
27 PLUGIN_HEADER
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_PLAY
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 #else
116 #error JEWELS: Unsupported keypad
117 #endif
119 /* use 30x30 tiles (iPod Video, Gigabeat) */
120 #if (LCD_HEIGHT == 240) && (LCD_WIDTH == 320) || \
121 ((LCD_HEIGHT == 320) && (LCD_WIDTH == 240))
122 #define TILE_WIDTH 30
123 #define TILE_HEIGHT 30
124 #define YOFS 0
125 #define NUM_SCORES 10
127 /* use 22x22 tiles (H300, iPod Color) */
128 #elif ((LCD_HEIGHT == 176) && (LCD_WIDTH == 220)) || \
129 ((LCD_HEIGHT == 220) && (LCD_WIDTH == 176))
130 #define TILE_WIDTH 22
131 #define TILE_HEIGHT 22
132 #define YOFS 0
133 #define NUM_SCORES 10
135 /* use 16x16 tiles (iPod Nano) */
136 #elif (LCD_HEIGHT == 132) && (LCD_WIDTH == 176)
137 #define TILE_WIDTH 16
138 #define TILE_HEIGHT 16
139 #define YOFS 4
140 #define NUM_SCORES 10
142 /* use 16x16 tiles (H100, iAudio X5, iPod 3G, iPod 4G grayscale) */
143 #elif (LCD_HEIGHT == 128) && (LCD_WIDTH == 160)
144 #define TILE_WIDTH 16
145 #define TILE_HEIGHT 16
146 #define YOFS 0
147 #define NUM_SCORES 10
149 /* use 14x14 tiles (H10 5/6 GB) */
150 #elif (LCD_HEIGHT == 128) && (LCD_WIDTH == 128)
151 #define TILE_WIDTH 14
152 #define TILE_HEIGHT 14
153 #define YOFS 0
154 #define NUM_SCORES 10
156 /* use 13x13 tiles (iPod Mini) */
157 #elif (LCD_HEIGHT == 110) && (LCD_WIDTH == 138)
158 #define TILE_WIDTH 13
159 #define TILE_HEIGHT 13
160 #define YOFS 6
161 #define NUM_SCORES 10
163 /* use 10x10 tiles (Sansa c200) */
164 #elif (LCD_HEIGHT == 80) && (LCD_WIDTH == 132)
165 #define TILE_WIDTH 10
166 #define TILE_HEIGHT 10
167 #define YOFS 0
168 #define NUM_SCORES 8
170 /* use 10x8 tiles (iFP 700) */
171 #elif (LCD_HEIGHT == 64) && (LCD_WIDTH == 128)
172 #define TILE_WIDTH 10
173 #define TILE_HEIGHT 8
174 #define YOFS 0
175 #define NUM_SCORES 8
177 /* use 10x8 tiles (Recorder, Ondio) */
178 #elif (LCD_HEIGHT == 64) && (LCD_WIDTH == 112)
179 #define TILE_WIDTH 10
180 #define TILE_HEIGHT 8
181 #define YOFS 0
182 #define NUM_SCORES 8
184 #else
185 #error JEWELS: Unsupported LCD
186 #endif
188 /* save files */
189 #define SCORE_FILE PLUGIN_GAMES_DIR "/jewels.score"
190 #define SAVE_FILE PLUGIN_GAMES_DIR "/jewels.save"
192 /* final game return status */
193 #define BJ_QUIT_FROM_GAME 4
194 #define BJ_END 3
195 #define BJ_USB 2
196 #define BJ_QUIT 1
197 #define BJ_LOSE 0
199 /* swap directions */
200 #define SWAP_UP 0
201 #define SWAP_RIGHT 1
202 #define SWAP_DOWN 2
203 #define SWAP_LEFT 3
205 /* play board dimension */
206 #define BJ_HEIGHT 9
207 #define BJ_WIDTH 8
209 /* next level threshold */
210 #define LEVEL_PTS 100
212 /* animation frame rate */
213 #define MAX_FPS 20
215 /* Game types */
216 enum game_type {
217 GAME_TYPE_NORMAL,
218 GAME_TYPE_PUZZLE
221 /* menu values */
222 #define FONT_HEIGHT 8
223 #define MAX_MITEMS 6
224 #define MENU_WIDTH 100
226 /* menu results */
227 enum menu_result {
228 MRES_NONE,
229 MRES_NEW,
230 MRES_PUZZLE,
231 MRES_SAVE,
232 MRES_RESUME,
233 MRES_SCORES,
234 MRES_HELP,
235 MRES_QUIT,
236 MRES_PLAYBACK,
237 MRES_EXIT
240 /* menu commands */
241 enum menu_cmd {
242 MCMD_NONE,
243 MCMD_NEXT,
244 MCMD_PREV,
245 MCMD_SELECT
248 /* menus */
249 struct jewels_menu {
250 char *title;
251 bool hasframe;
252 int selected;
253 int itemcnt;
254 struct jewels_menuitem {
255 char *text;
256 enum menu_result res;
257 } items[MAX_MITEMS];
258 } bjmenu[] = {
259 {"Jewels", false, 0, 6,
260 {{"New Game", MRES_NEW},
261 {"Puzzle", MRES_PUZZLE},
262 {"Resume Game", MRES_RESUME},
263 {"High Scores", MRES_SCORES},
264 {"Help", MRES_HELP},
265 {"Quit", MRES_QUIT}}},
266 {"Menu", true, 0, 5,
267 {{"Audio Playback", MRES_PLAYBACK },
268 {"Resume Game", MRES_RESUME},
269 {"Save Game", MRES_SAVE},
270 {"End Game", MRES_QUIT},
271 {"Exit Jewels", MRES_EXIT}}}
274 /* global rockbox api */
275 static struct plugin_api* rb;
277 /* external bitmaps */
278 extern const fb_data jewels[];
280 /* tile background colors */
281 #ifdef HAVE_LCD_COLOR
282 static const unsigned jewels_bkgd[2] = {
283 LCD_RGBPACK(104, 63, 63),
284 LCD_RGBPACK(83, 44, 44)
286 #endif
288 /* the tile struct
289 * type is the jewel number 0-7
290 * falling if the jewel is falling
291 * delete marks the jewel for deletion
293 struct tile {
294 int type;
295 bool falling;
296 bool delete;
299 /* the game context struct
300 * score is the current level score
301 * segments is the number of cleared segments in the current run
302 * level is the current level
303 * type is the game type (normal or puzzle)
304 * highscores is the list of high scores
305 * resume denotes whether to resume the currently loaded game
306 * dirty denotes whether the high scores are out of sync with the saved file
307 * playboard is the game playing board (first row is hidden)
308 * num_jewels is the number of different jewels to use
310 struct game_context {
311 unsigned int score;
312 unsigned int segments;
313 unsigned int level;
314 unsigned int type;
315 unsigned int highscores[NUM_SCORES];
316 bool resume;
317 bool dirty;
318 struct tile playboard[BJ_HEIGHT][BJ_WIDTH];
319 unsigned int num_jewels;
322 #define MAX_NUM_JEWELS 7
324 #define MAX_PUZZLE_TILES 4
325 #define NUM_PUZZLE_LEVELS 10
327 struct puzzle_tile {
328 int x;
329 int y;
330 int tile_type;
333 struct puzzle_level {
334 unsigned int num_jewels;
335 unsigned int num_tiles;
336 struct puzzle_tile tiles[MAX_PUZZLE_TILES];
339 #define PUZZLE_TILE_UP 1
340 #define PUZZLE_TILE_DOWN 2
341 #define PUZZLE_TILE_LEFT 4
342 #define PUZZLE_TILE_RIGHT 8
344 struct puzzle_level puzzle_levels[NUM_PUZZLE_LEVELS] = {
345 { 5, 2, { {3, 3, PUZZLE_TILE_RIGHT},
346 {4, 2, PUZZLE_TILE_LEFT} } },
347 { 5, 2, { {3, 2, PUZZLE_TILE_DOWN},
348 {3, 4, PUZZLE_TILE_UP} } },
349 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
350 {3, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
351 {3, 6, PUZZLE_TILE_UP} } },
352 { 6, 3, { {3, 2, PUZZLE_TILE_RIGHT},
353 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
354 {5, 4, PUZZLE_TILE_LEFT} } },
355 { 6, 2, { {3, 4, PUZZLE_TILE_RIGHT},
356 {4, 2, PUZZLE_TILE_LEFT} } },
357 { 6, 2, { {3, 2, PUZZLE_TILE_DOWN},
358 {4, 4, PUZZLE_TILE_UP} } },
359 { 7, 4, { {3, 2, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
360 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
361 {3, 4, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
362 {4, 4, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
363 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
364 {4, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
365 {3, 6, PUZZLE_TILE_UP} } },
366 { 7, 3, { {2, 2, PUZZLE_TILE_RIGHT},
367 {4, 1, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
368 {5, 4, PUZZLE_TILE_LEFT} } },
369 { 7, 4, { {3, 0, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
370 {5, 0, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
371 {2, 7, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
372 {4, 7, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
375 /*****************************************************************************
376 * jewels_init() initializes jewels data structures.
377 ******************************************************************************/
378 static void jewels_init(struct game_context* bj) {
379 /* seed the rand generator */
380 rb->srand(*rb->current_tick);
382 /* check for resumed game */
383 if(bj->resume) {
384 bj->resume = false;
385 return;
388 /* reset scoring */
389 bj->level = 1;
390 bj->score = 0;
391 bj->segments = 0;
393 /* clear playing board */
394 rb->memset(bj->playboard, 0, sizeof(bj->playboard));
397 /*****************************************************************************
398 * jewels_setcolors() set the foreground and background colors.
399 ******************************************************************************/
400 static inline void jewels_setcolors(void) {
401 #ifdef HAVE_LCD_COLOR
402 rb->lcd_set_background(LCD_RGBPACK(49, 26, 26));
403 rb->lcd_set_foreground(LCD_RGBPACK(210, 181, 181));
404 #endif
407 /*****************************************************************************
408 * jewels_drawboard() redraws the entire game board.
409 ******************************************************************************/
410 static void jewels_drawboard(struct game_context* bj) {
411 int i, j;
412 int w, h;
413 unsigned int tempscore;
414 char *title = "Level";
415 char str[10];
417 tempscore = (bj->score>LEVEL_PTS ? LEVEL_PTS : bj->score);
419 /* clear screen */
420 rb->lcd_clear_display();
422 /* dispay playing board */
423 for(i=0; i<BJ_HEIGHT-1; i++){
424 for(j=0; j<BJ_WIDTH; j++){
425 #ifdef HAVE_LCD_COLOR
426 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
427 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
428 TILE_WIDTH, TILE_HEIGHT);
429 rb->lcd_bitmap_transparent_part(jewels,
430 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
431 TILE_WIDTH, j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
432 TILE_WIDTH, TILE_HEIGHT);
433 #else
434 rb->lcd_bitmap_part(jewels,
435 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
436 TILE_WIDTH, j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
437 TILE_WIDTH, TILE_HEIGHT);
438 #endif
442 #if LCD_WIDTH > LCD_HEIGHT /* horizontal layout */
444 /* draw separator lines */
445 jewels_setcolors();
446 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, LCD_HEIGHT-1);
447 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, 18);
448 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, LCD_HEIGHT-10);
450 /* draw progress bar */
451 #ifdef HAVE_LCD_COLOR
452 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
453 #endif
454 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
455 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
456 tempscore/LEVEL_PTS),
457 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
458 ((LCD_HEIGHT-10)-18)*tempscore/LEVEL_PTS);
459 #ifdef HAVE_LCD_COLOR
460 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
461 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
462 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
463 tempscore/LEVEL_PTS)+1,
464 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
465 ((LCD_HEIGHT-10)-18)*tempscore/LEVEL_PTS-1);
466 jewels_setcolors();
467 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
468 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
469 tempscore/LEVEL_PTS),
470 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
471 ((LCD_HEIGHT-10)-18)*tempscore/LEVEL_PTS+1);
472 #endif
474 /* print text */
475 rb->lcd_getstringsize(title, &w, &h);
476 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 1, title);
478 rb->snprintf(str, 4, "%d", bj->level);
479 rb->lcd_getstringsize(str, &w, &h);
480 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 10, str);
482 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
483 rb->lcd_getstringsize(str, &w, &h);
484 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2,
485 LCD_HEIGHT-8, str);
487 #elif LCD_WIDTH < LCD_HEIGHT /* vertical layout */
489 /* draw separator lines */
490 jewels_setcolors();
491 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
492 rb->lcd_hline(0, LCD_WIDTH-1, LCD_HEIGHT-14);
493 rb->lcd_vline(LCD_WIDTH/2, LCD_HEIGHT-14, LCD_HEIGHT-1);
495 /* draw progress bar */
496 #ifdef HAVE_LCD_COLOR
497 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
498 #endif
499 rb->lcd_fillrect(0, (8*TILE_HEIGHT+YOFS)
500 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
501 LCD_WIDTH*tempscore/LEVEL_PTS,
502 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
503 #ifdef HAVE_LCD_COLOR
504 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
505 rb->lcd_drawrect(1, (8*TILE_HEIGHT+YOFS)
506 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4+1,
507 LCD_WIDTH*tempscore/LEVEL_PTS-1,
508 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2-2);
509 jewels_setcolors();
510 rb->lcd_drawrect(0, (8*TILE_HEIGHT+YOFS)
511 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
512 LCD_WIDTH*tempscore/LEVEL_PTS+1,
513 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
514 #endif
516 /* print text */
517 rb->snprintf(str, 10, "%s %d", title, bj->level);
518 rb->lcd_putsxy(1, LCD_HEIGHT-10, str);
520 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
521 rb->lcd_getstringsize(str, &w, &h);
522 rb->lcd_putsxy((LCD_WIDTH-2)-w, LCD_HEIGHT-10, str);
524 #else /* square layout */
526 /* draw separator lines */
527 jewels_setcolors();
528 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
529 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, 8*TILE_HEIGHT+YOFS);
530 rb->lcd_vline(LCD_WIDTH/2, 8*TILE_HEIGHT+YOFS, LCD_HEIGHT-1);
532 /* draw progress bar */
533 #ifdef HAVE_LCD_COLOR
534 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
535 #endif
536 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
537 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
538 *tempscore/LEVEL_PTS,
539 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
540 (8*TILE_HEIGHT+YOFS)*tempscore/LEVEL_PTS);
541 #ifdef HAVE_LCD_COLOR
542 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
543 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
544 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
545 *tempscore/LEVEL_PTS+1,
546 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
547 (8*TILE_HEIGHT+YOFS)*tempscore/LEVEL_PTS-1);
548 jewels_setcolors();
549 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
550 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
551 *tempscore/LEVEL_PTS,
552 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
553 (8*TILE_HEIGHT+YOFS)*tempscore/LEVEL_PTS+1);
554 #endif
556 /* print text */
557 rb->snprintf(str, 10, "%s %d", title, bj->level);
558 rb->lcd_putsxy(1, LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
560 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
561 rb->lcd_getstringsize(str, &w, &h);
562 rb->lcd_putsxy((LCD_WIDTH-2)-w,
563 LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
565 #endif /* layout */
567 rb->lcd_update();
570 /*****************************************************************************
571 * jewels_showmenu() displays the chosen menu after performing the chosen
572 * menu command.
573 ******************************************************************************/
574 static enum menu_result jewels_showmenu(struct jewels_menu* menu,
575 enum menu_cmd cmd) {
576 int i;
577 int w, h;
578 int firstline;
579 int adj;
581 /* handle menu command */
582 switch(cmd) {
583 case MCMD_NEXT:
584 menu->selected = (menu->selected+1)%menu->itemcnt;
585 break;
587 case MCMD_PREV:
588 menu->selected = (menu->selected-1+menu->itemcnt)%menu->itemcnt;
589 break;
591 case MCMD_SELECT:
592 return menu->items[menu->selected].res;
594 default:
595 break;
598 /* clear menu area */
599 firstline = (LCD_HEIGHT/FONT_HEIGHT-(menu->itemcnt+3))/2;
601 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
602 rb->lcd_fillrect((LCD_WIDTH-MENU_WIDTH)/2, firstline*FONT_HEIGHT,
603 MENU_WIDTH, (menu->itemcnt+3)*FONT_HEIGHT);
604 rb->lcd_set_drawmode(DRMODE_SOLID);
606 if(menu->hasframe) {
607 rb->lcd_drawrect((LCD_WIDTH-MENU_WIDTH)/2-1, firstline*FONT_HEIGHT-1,
608 MENU_WIDTH+2, (menu->itemcnt+3)*FONT_HEIGHT+2);
609 rb->lcd_hline((LCD_WIDTH-MENU_WIDTH)/2-1,
610 (LCD_WIDTH-MENU_WIDTH)/2-1+MENU_WIDTH+2,
611 (firstline+1)*FONT_HEIGHT);
614 /* draw menu items */
615 rb->lcd_getstringsize(menu->title, &w, &h);
616 rb->lcd_putsxy((LCD_WIDTH-w)/2, firstline*FONT_HEIGHT, menu->title);
618 for(i=0; i<menu->itemcnt; i++) {
619 if(i == menu->selected) {
620 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
622 rb->lcd_putsxy((LCD_WIDTH-MENU_WIDTH)/2, (firstline+i+2)*FONT_HEIGHT,
623 menu->items[i].text);
624 if(i == menu->selected) {
625 rb->lcd_set_drawmode(DRMODE_SOLID);
629 adj = (firstline == 0 ? 0 : 1);
630 rb->lcd_update_rect((LCD_WIDTH-MENU_WIDTH)/2-1, firstline*FONT_HEIGHT-adj,
631 MENU_WIDTH+2, (menu->itemcnt+3)*FONT_HEIGHT+2*adj);
632 return MRES_NONE;
635 /*****************************************************************************
636 * jewels_putjewels() makes the jewels fall to fill empty spots and adds
637 * new random jewels at the empty spots at the top of each row.
638 ******************************************************************************/
639 static void jewels_putjewels(struct game_context* bj){
640 int i, j, k;
641 bool mark, done;
642 long lasttick, currenttick;
644 /* loop to make all the jewels fall */
645 while(true) {
646 /* mark falling jewels and add new jewels to hidden top row*/
647 mark = false;
648 done = true;
649 for(j=0; j<BJ_WIDTH; j++) {
650 if(bj->playboard[1][j].type == 0) {
651 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
653 for(i=BJ_HEIGHT-2; i>=0; i--) {
654 if(!mark && bj->playboard[i+1][j].type == 0) {
655 mark = true;
656 done = false;
658 if(mark) bj->playboard[i][j].falling = true;
660 /*if(bj->playboard[1][j].falling) {
661 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
662 bj->playboard[0][j].falling = true;
664 mark = false;
667 /* break if there are no falling jewels */
668 if(done) break;
670 /* animate falling jewels */
671 lasttick = *rb->current_tick;
673 for(k=1; k<=8; k++) {
674 for(i=BJ_HEIGHT-2; i>=0; i--) {
675 for(j=0; j<BJ_WIDTH; j++) {
676 if(bj->playboard[i][j].falling &&
677 bj->playboard[i][j].type != 0) {
678 /* clear old position */
679 #ifdef HAVE_LCD_COLOR
680 if(i == 0 && YOFS) {
681 rb->lcd_set_foreground(rb->lcd_get_background());
682 } else {
683 rb->lcd_set_foreground(jewels_bkgd[(i-1+j)%2]);
685 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
686 TILE_WIDTH, TILE_HEIGHT);
687 if(bj->playboard[i+1][j].type == 0) {
688 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
689 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
690 TILE_WIDTH, TILE_HEIGHT);
692 #else
693 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
694 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
695 TILE_WIDTH, TILE_HEIGHT);
696 if(bj->playboard[i+1][j].type == 0) {
697 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
698 TILE_WIDTH, TILE_HEIGHT);
700 rb->lcd_set_drawmode(DRMODE_SOLID);
701 #endif
703 /* draw new position */
704 #ifdef HAVE_LCD_COLOR
705 rb->lcd_bitmap_transparent_part(jewels, 0,
706 TILE_HEIGHT*(bj->playboard[i][j].type),
707 TILE_WIDTH, j*TILE_WIDTH,
708 (i-1)*TILE_HEIGHT+YOFS+
709 ((((TILE_HEIGHT<<10)*k)/8)>>10),
710 TILE_WIDTH, TILE_HEIGHT);
711 #else
712 rb->lcd_bitmap_part(jewels, 0,
713 TILE_HEIGHT*(bj->playboard[i][j].type),
714 TILE_WIDTH, j*TILE_WIDTH,
715 (i-1)*TILE_HEIGHT+YOFS+
716 ((((TILE_HEIGHT<<10)*k)/8)>>10),
717 TILE_WIDTH, TILE_HEIGHT);
718 #endif
723 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
724 jewels_setcolors();
726 /* framerate limiting */
727 currenttick = *rb->current_tick;
728 if(currenttick-lasttick < HZ/MAX_FPS) {
729 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
730 } else {
731 rb->yield();
733 lasttick = currenttick;
736 /* shift jewels down */
737 for(j=0; j<BJ_WIDTH; j++) {
738 for(i=BJ_HEIGHT-1; i>=1; i--) {
739 if(bj->playboard[i-1][j].falling) {
740 bj->playboard[i][j].type = bj->playboard[i-1][j].type;
745 /* clear out top row */
746 for(j=0; j<BJ_WIDTH; j++) {
747 bj->playboard[0][j].type = 0;
750 /* mark everything not falling */
751 for(i=0; i<BJ_HEIGHT; i++) {
752 for(j=0; j<BJ_WIDTH; j++) {
753 bj->playboard[i][j].falling = false;
759 /*****************************************************************************
760 * jewels_clearjewels() finds all the connected rows and columns and
761 * calculates and returns the points earned.
762 ******************************************************************************/
763 static unsigned int jewels_clearjewels(struct game_context* bj) {
764 int i, j;
765 int last, run;
766 unsigned int points = 0;
768 /* check for connected rows */
769 for(i=1; i<BJ_HEIGHT; i++) {
770 last = 0;
771 run = 1;
772 for(j=0; j<BJ_WIDTH; j++) {
773 if(bj->playboard[i][j].type == last &&
774 bj->playboard[i][j].type != 0 &&
775 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
776 run++;
778 if(run == 3) {
779 bj->segments++;
780 points += bj->segments;
781 bj->playboard[i][j].delete = true;
782 bj->playboard[i][j-1].delete = true;
783 bj->playboard[i][j-2].delete = true;
784 } else if(run > 3) {
785 points++;
786 bj->playboard[i][j].delete = true;
788 } else {
789 run = 1;
790 last = bj->playboard[i][j].type;
795 /* check for connected columns */
796 for(j=0; j<BJ_WIDTH; j++) {
797 last = 0;
798 run = 1;
799 for(i=1; i<BJ_HEIGHT; i++) {
800 if(bj->playboard[i][j].type != 0 &&
801 bj->playboard[i][j].type == last &&
802 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
803 run++;
805 if(run == 3) {
806 bj->segments++;
807 points += bj->segments;
808 bj->playboard[i][j].delete = true;
809 bj->playboard[i-1][j].delete = true;
810 bj->playboard[i-2][j].delete = true;
811 } else if(run > 3) {
812 points++;
813 bj->playboard[i][j].delete = true;
815 } else {
816 run = 1;
817 last = bj->playboard[i][j].type;
822 /* clear deleted jewels */
823 for(i=1; i<BJ_HEIGHT; i++) {
824 for(j=0; j<BJ_WIDTH; j++) {
825 if(bj->playboard[i][j].delete) {
826 bj->playboard[i][j].delete = false;
827 bj->playboard[i][j].type = 0;
832 return points;
835 /*****************************************************************************
836 * jewels_runboard() runs the board until it settles in a fixed state and
837 * returns points earned.
838 ******************************************************************************/
839 static unsigned int jewels_runboard(struct game_context* bj) {
840 unsigned int points = 0;
841 unsigned int ret;
843 bj->segments = 0;
845 while((ret = jewels_clearjewels(bj)) > 0) {
846 points += ret;
847 jewels_drawboard(bj);
848 jewels_putjewels(bj);
851 return points;
854 /*****************************************************************************
855 * jewels_swapjewels() swaps two jewels as long as it results in points and
856 * returns points earned.
857 ******************************************************************************/
858 static unsigned int jewels_swapjewels(struct game_context* bj,
859 int x, int y, int direc) {
860 int k;
861 int horzmod, vertmod;
862 int movelen = 0;
863 bool undo = false;
864 unsigned int points = 0;
865 long lasttick, currenttick;
867 /* check for invalid parameters */
868 if(x < 0 || x >= BJ_WIDTH || y < 0 || y >= BJ_HEIGHT-1 ||
869 direc < SWAP_UP || direc > SWAP_LEFT) {
870 return 0;
873 /* check for invalid directions */
874 if((x == 0 && direc == SWAP_LEFT) ||
875 (x == BJ_WIDTH-1 && direc == SWAP_RIGHT) ||
876 (y == 0 && direc == SWAP_UP) ||
877 (y == BJ_HEIGHT-2 && direc == SWAP_DOWN)) {
878 return 0;
881 /* set direction variables */
882 horzmod = 0;
883 vertmod = 0;
884 switch(direc) {
885 case SWAP_UP:
886 vertmod = -1;
887 movelen = TILE_HEIGHT;
888 break;
889 case SWAP_RIGHT:
890 horzmod = 1;
891 movelen = TILE_WIDTH;
892 break;
893 case SWAP_DOWN:
894 vertmod = 1;
895 movelen = TILE_HEIGHT;
896 break;
897 case SWAP_LEFT:
898 horzmod = -1;
899 movelen = TILE_WIDTH;
900 break;
903 while(true) {
904 lasttick = *rb->current_tick;
906 /* animate swapping jewels */
907 for(k=0; k<=8; k++) {
908 /* clear old position */
909 #ifdef HAVE_LCD_COLOR
910 rb->lcd_set_foreground(jewels_bkgd[(x+y)%2]);
911 rb->lcd_fillrect(x*TILE_WIDTH,
912 y*TILE_HEIGHT+YOFS,
913 TILE_WIDTH, TILE_HEIGHT);
914 rb->lcd_set_foreground(jewels_bkgd[(x+horzmod+y+vertmod)%2]);
915 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
916 (y+vertmod)*TILE_HEIGHT+YOFS,
917 TILE_WIDTH, TILE_HEIGHT);
918 #else
919 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
920 rb->lcd_fillrect(x*TILE_WIDTH,
921 y*TILE_HEIGHT+YOFS,
922 TILE_WIDTH, TILE_HEIGHT);
923 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
924 (y+vertmod)*TILE_HEIGHT+YOFS,
925 TILE_WIDTH, TILE_HEIGHT);
926 rb->lcd_set_drawmode(DRMODE_SOLID);
927 #endif
928 /* draw new position */
929 #ifdef HAVE_LCD_COLOR
930 rb->lcd_bitmap_transparent_part(jewels,
931 0, TILE_HEIGHT*(bj->playboard
932 [y+1+vertmod][x+horzmod].type), TILE_WIDTH,
933 (x+horzmod)*TILE_WIDTH-horzmod*
934 ((((movelen<<10)*k)/8)>>10),
935 (y+vertmod)*TILE_HEIGHT-vertmod*
936 ((((movelen<<10)*k)/8)>>10)+YOFS,
937 TILE_WIDTH, TILE_HEIGHT);
938 rb->lcd_bitmap_transparent_part(jewels,
939 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
940 TILE_WIDTH, x*TILE_WIDTH+horzmod*
941 ((((movelen<<10)*k)/8)>>10),
942 y*TILE_HEIGHT+vertmod*
943 ((((movelen<<10)*k)/8)>>10)+YOFS,
944 TILE_WIDTH, TILE_HEIGHT);
945 #else
946 rb->lcd_bitmap_part(jewels,
947 0, TILE_HEIGHT*(bj->playboard
948 [y+1+vertmod][x+horzmod].type), TILE_WIDTH,
949 (x+horzmod)*TILE_WIDTH-horzmod*
950 ((((movelen<<10)*k)/8)>>10),
951 (y+vertmod)*TILE_HEIGHT-vertmod*
952 ((((movelen<<10)*k)/8)>>10)+YOFS,
953 TILE_WIDTH, TILE_HEIGHT);
954 rb->lcd_set_drawmode(DRMODE_FG);
955 rb->lcd_bitmap_part(jewels,
956 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
957 TILE_WIDTH, x*TILE_WIDTH+horzmod*
958 ((((movelen<<10)*k)/8)>>10),
959 y*TILE_HEIGHT+vertmod*
960 ((((movelen<<10)*k)/8)>>10)+YOFS,
961 TILE_WIDTH, TILE_HEIGHT);
962 rb->lcd_set_drawmode(DRMODE_SOLID);
963 #endif
965 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
966 jewels_setcolors();
968 /* framerate limiting */
969 currenttick = *rb->current_tick;
970 if(currenttick-lasttick < HZ/MAX_FPS) {
971 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
972 } else {
973 rb->yield();
975 lasttick = currenttick;
978 /* swap jewels */
979 int temp = bj->playboard[y+1][x].type;
980 bj->playboard[y+1][x].type =
981 bj->playboard[y+1+vertmod][x+horzmod].type;
982 bj->playboard[y+1+vertmod][x+horzmod].type = temp;
984 if(undo) break;
986 points = jewels_runboard(bj);
987 if(points == 0) {
988 undo = true;
989 } else {
990 break;
994 return points;
997 /*****************************************************************************
998 * jewels_movesavail() uses pattern matching to see if there are any
999 * available move left.
1000 ******************************************************************************/
1001 static bool jewels_movesavail(struct game_context* bj) {
1002 int i, j;
1003 bool moves = false;
1004 int mytype;
1006 for(i=1; i<BJ_HEIGHT; i++) {
1007 for(j=0; j<BJ_WIDTH; j++) {
1008 mytype = bj->playboard[i][j].type;
1009 if(mytype == 0 || mytype > MAX_NUM_JEWELS) continue;
1011 /* check horizontal patterns */
1012 if(j <= BJ_WIDTH-3) {
1013 if(i > 1) {
1014 if(bj->playboard[i-1][j+1].type == mytype) {
1015 if(bj->playboard[i-1][j+2].type == mytype)
1016 {moves = true; break;}
1017 if(bj->playboard[i][j+2].type == mytype)
1018 {moves = true; break;}
1020 if(bj->playboard[i][j+1].type == mytype) {
1021 if(bj->playboard[i-1][j+2].type == mytype)
1022 {moves = true; break;}
1026 if(j <= BJ_WIDTH-4) {
1027 if(bj->playboard[i][j+3].type == mytype) {
1028 if(bj->playboard[i][j+1].type == mytype)
1029 {moves = true; break;}
1030 if(bj->playboard[i][j+2].type == mytype)
1031 {moves = true; break;}
1035 if(i < BJ_HEIGHT-1) {
1036 if(bj->playboard[i][j+1].type == mytype) {
1037 if(bj->playboard[i+1][j+2].type == mytype)
1038 {moves = true; break;}
1040 if(bj->playboard[i+1][j+1].type == mytype) {
1041 if(bj->playboard[i][j+2].type == mytype)
1042 {moves = true; break;}
1043 if(bj->playboard[i+1][j+2].type == mytype)
1044 {moves = true; break;}
1049 /* check vertical patterns */
1050 if(i <= BJ_HEIGHT-3) {
1051 if(j > 0) {
1052 if(bj->playboard[i+1][j-1].type == mytype) {
1053 if(bj->playboard[i+2][j-1].type == mytype)
1054 {moves = true; break;}
1055 if(bj->playboard[i+2][j].type == mytype)
1056 {moves = true; break;}
1058 if(bj->playboard[i+1][j].type == mytype) {
1059 if(bj->playboard[i+2][j-1].type == mytype)
1060 {moves = true; break;}
1064 if(i <= BJ_HEIGHT-4) {
1065 if(bj->playboard[i+3][j].type == mytype) {
1066 if(bj->playboard[i+1][j].type == mytype)
1067 {moves = true; break;}
1068 if(bj->playboard[i+2][j].type == mytype)
1069 {moves = true; break;}
1073 if(j < BJ_WIDTH-1) {
1074 if(bj->playboard[i+1][j].type == mytype) {
1075 if(bj->playboard[i+2][j+1].type == mytype)
1076 {moves = true; break;}
1078 if(bj->playboard[i+1][j+1].type == mytype) {
1079 if(bj->playboard[i+2][j].type == mytype)
1080 {moves = true; break;}
1081 if (bj->playboard[i+2][j+1].type == mytype)
1082 {moves = true; break;}
1088 if(moves) break;
1091 return moves;
1094 /*****************************************************************************
1095 * jewels_puzzle_is_finished(bj) checks if the puzzle is finished.
1096 ******************************************************************************/
1097 static int jewels_puzzle_is_finished(struct game_context* bj) {
1098 unsigned int i, j;
1099 for(i=0; i<BJ_HEIGHT; i++) {
1100 for(j=0; j<BJ_WIDTH; j++) {
1101 int mytype = bj->playboard[i][j].type;
1102 if(mytype>MAX_NUM_JEWELS) {
1103 mytype -= MAX_NUM_JEWELS;
1104 if(mytype&PUZZLE_TILE_UP) {
1105 if(i==0 || bj->playboard[i-1][j].type<=MAX_NUM_JEWELS ||
1106 !((bj->playboard[i-1][j].type-MAX_NUM_JEWELS)
1107 &PUZZLE_TILE_DOWN))
1108 return 0;
1110 if(mytype&PUZZLE_TILE_DOWN) {
1111 if(i==BJ_HEIGHT-1 ||
1112 bj->playboard[i+1][j].type<=MAX_NUM_JEWELS ||
1113 !((bj->playboard[i+1][j].type-MAX_NUM_JEWELS)
1114 &PUZZLE_TILE_UP))
1115 return 0;
1117 if(mytype&PUZZLE_TILE_LEFT) {
1118 if(j==0 || bj->playboard[i][j-1].type<=MAX_NUM_JEWELS ||
1119 !((bj->playboard[i][j-1].type-MAX_NUM_JEWELS)
1120 &PUZZLE_TILE_RIGHT))
1121 return 0;
1123 if(mytype&PUZZLE_TILE_RIGHT) {
1124 if(j==BJ_WIDTH-1 ||
1125 bj->playboard[i][j+1].type<=MAX_NUM_JEWELS ||
1126 !((bj->playboard[i][j+1].type-MAX_NUM_JEWELS)
1127 &PUZZLE_TILE_LEFT))
1128 return 0;
1133 return 1;
1136 /*****************************************************************************
1137 * jewels_initlevel() initialises a level.
1138 ******************************************************************************/
1139 static unsigned int jewels_initlevel(struct game_context* bj) {
1140 unsigned int points = 0;
1142 switch(bj->type) {
1143 case GAME_TYPE_NORMAL:
1144 bj->num_jewels = MAX_NUM_JEWELS;
1145 break;
1147 case GAME_TYPE_PUZZLE:
1149 unsigned int i, j;
1150 struct puzzle_tile *tile;
1152 bj->num_jewels = puzzle_levels[bj->level-1].num_jewels;
1154 for(i=0; i<BJ_HEIGHT; i++) {
1155 for(j=0; j<BJ_WIDTH; j++) {
1156 bj->playboard[i][j].type = (rb->rand()%bj->num_jewels)+1;
1157 bj->playboard[i][j].falling = false;
1158 bj->playboard[i][j].delete = false;
1161 jewels_runboard(bj);
1162 tile = puzzle_levels[bj->level-1].tiles;
1163 for(i=0; i<puzzle_levels[bj->level-1].num_tiles; i++, tile++) {
1164 bj->playboard[tile->y+1][tile->x].type = MAX_NUM_JEWELS
1165 +tile->tile_type;
1168 break;
1171 jewels_drawboard(bj);
1173 /* run the play board */
1174 jewels_putjewels(bj);
1175 points += jewels_runboard(bj);
1176 return points;
1179 /*****************************************************************************
1180 * jewels_nextlevel() advances the game to the next level and returns
1181 * points earned.
1182 ******************************************************************************/
1183 static unsigned int jewels_nextlevel(struct game_context* bj) {
1184 int i, x, y;
1185 unsigned int points = 0;
1187 switch(bj->type) {
1188 case GAME_TYPE_NORMAL:
1189 /* roll over score, change and display level */
1190 while(bj->score >= LEVEL_PTS) {
1191 bj->score -= LEVEL_PTS;
1192 bj->level++;
1193 rb->splash(HZ*2, "Level %d", bj->level);
1194 jewels_drawboard(bj);
1197 /* randomly clear some jewels */
1198 for(i=0; i<16; i++) {
1199 x = rb->rand()%8;
1200 y = rb->rand()%8;
1202 if(bj->playboard[y][x].type != 0) {
1203 points++;
1204 bj->playboard[y][x].type = 0;
1207 break;
1209 case GAME_TYPE_PUZZLE:
1210 bj->level++;
1211 if(bj->level>NUM_PUZZLE_LEVELS) {
1212 rb->splash(HZ*2, "You win!");
1213 bj->level = 1;
1214 } else {
1215 rb->splash(HZ*2, "Level %d", bj->level);
1217 break;
1220 return jewels_initlevel(bj);
1223 /*****************************************************************************
1224 * jewels_recordscore() inserts a high score into the high scores list and
1225 * returns the high score position.
1226 ******************************************************************************/
1227 static int jewels_recordscore(struct game_context* bj) {
1228 int i;
1229 int position = 0;
1230 unsigned int current, temp;
1232 /* calculate total score */
1233 current = (bj->level-1)*LEVEL_PTS+bj->score;
1234 if(current <= 0) return 0;
1236 /* insert the current score into the high scores */
1237 for(i=0; i<NUM_SCORES; i++) {
1238 if(current >= bj->highscores[i]) {
1239 if(!position) {
1240 position = i+1;
1241 bj->dirty = true;
1243 temp = bj->highscores[i];
1244 bj->highscores[i] = current;
1245 current = temp;
1249 return position;
1252 /*****************************************************************************
1253 * jewels_loadscores() loads the high scores saved file.
1254 ******************************************************************************/
1255 static void jewels_loadscores(struct game_context* bj) {
1256 int fd;
1258 bj->dirty = false;
1260 /* clear high scores */
1261 rb->memset(bj->highscores, 0, sizeof(bj->highscores));
1263 /* open scores file */
1264 fd = rb->open(SCORE_FILE, O_RDONLY);
1265 if(fd < 0) return;
1267 /* read in high scores */
1268 if(rb->read(fd, bj->highscores, sizeof(bj->highscores)) <= 0) {
1269 /* scores are bad, reset */
1270 rb->memset(bj->highscores, 0, sizeof(bj->highscores));
1273 rb->close(fd);
1276 /*****************************************************************************
1277 * jewels_savescores() saves the high scores saved file.
1278 ******************************************************************************/
1279 static void jewels_savescores(struct game_context* bj) {
1280 int fd;
1282 /* write out the high scores to the save file */
1283 fd = rb->open(SCORE_FILE, O_WRONLY|O_CREAT);
1284 rb->write(fd, bj->highscores, sizeof(bj->highscores));
1285 rb->close(fd);
1286 bj->dirty = false;
1289 /*****************************************************************************
1290 * jewels_loadgame() loads the saved game and returns load success.
1291 ******************************************************************************/
1292 static bool jewels_loadgame(struct game_context* bj) {
1293 int fd;
1294 bool loaded = false;
1296 /* open game file */
1297 fd = rb->open(SAVE_FILE, O_RDONLY);
1298 if(fd < 0) return loaded;
1300 /* read in saved game */
1301 while(true) {
1302 if(rb->read(fd, &bj->score, sizeof(bj->score)) <= 0) break;
1303 if(rb->read(fd, &bj->level, sizeof(bj->level)) <= 0) break;
1304 if(rb->read(fd, &bj->type, sizeof(bj->type)) <= 0) break;
1305 if(rb->read(fd, bj->playboard, sizeof(bj->playboard)) <= 0) break;
1306 bj->resume = true;
1307 loaded = true;
1308 break;
1311 rb->close(fd);
1313 /* delete saved file */
1314 rb->remove(SAVE_FILE);
1315 return loaded;
1318 /*****************************************************************************
1319 * jewels_savegame() saves the current game state.
1320 ******************************************************************************/
1321 static void jewels_savegame(struct game_context* bj) {
1322 int fd;
1324 /* write out the game state to the save file */
1325 fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT);
1326 rb->write(fd, &bj->score, sizeof(bj->score));
1327 rb->write(fd, &bj->level, sizeof(bj->level));
1328 rb->write(fd, &bj->type, sizeof(bj->type));
1329 rb->write(fd, bj->playboard, sizeof(bj->playboard));
1330 rb->close(fd);
1332 bj->resume = true;
1335 /*****************************************************************************
1336 * jewels_callback() is the default event handler callback which is called
1337 * on usb connect and shutdown.
1338 ******************************************************************************/
1339 static void jewels_callback(void* param) {
1340 struct game_context* bj = (struct game_context*) param;
1341 if(bj->dirty) {
1342 rb->splash(HZ, "Saving high scores...");
1343 jewels_savescores(bj);
1347 /*****************************************************************************
1348 * jewels_main() is the main game subroutine, it returns the final game status.
1349 ******************************************************************************/
1350 static int jewels_main(struct game_context* bj) {
1351 int i, j;
1352 int w, h;
1353 int button;
1354 char str[18];
1355 bool startgame = false;
1356 bool inmenu = false;
1357 bool selected = false;
1358 enum menu_cmd cmd = MCMD_NONE;
1359 enum menu_result res;
1361 /* the cursor coordinates */
1362 int x=0, y=0;
1364 /* don't resume by default */
1365 bj->resume = false;
1367 /********************
1368 * menu *
1369 ********************/
1370 rb->lcd_clear_display();
1372 while(!startgame) {
1373 res = jewels_showmenu(&bjmenu[0], cmd);
1374 cmd = MCMD_NONE;
1376 rb->snprintf(str, 18, "High Score: %d", bj->highscores[0]);
1377 rb->lcd_getstringsize(str, &w, &h);
1378 rb->lcd_putsxy((LCD_WIDTH-w)/2, LCD_HEIGHT-8, str);
1379 rb->lcd_update();
1381 switch(res) {
1382 case MRES_NEW:
1383 startgame = true;
1384 bj->type = GAME_TYPE_NORMAL;
1385 continue;
1387 case MRES_PUZZLE:
1388 startgame = true;
1389 bj->type = GAME_TYPE_PUZZLE;
1390 continue;
1392 case MRES_RESUME:
1393 if(!jewels_loadgame(bj)) {
1394 rb->splash(HZ*2, "Nothing to resume");
1395 rb->lcd_clear_display();
1396 } else {
1397 startgame = true;
1399 continue;
1401 case MRES_SCORES:
1402 rb->lcd_clear_display();
1404 /* room for a title? */
1405 j = 0;
1406 if(LCD_HEIGHT-NUM_SCORES*8 >= 8) {
1407 rb->snprintf(str, 12, "%s", "High Scores");
1408 rb->lcd_getstringsize(str, &w, &h);
1409 rb->lcd_putsxy((LCD_WIDTH-w)/2, 0, str);
1410 j = 2;
1413 /* print high scores */
1414 for(i=0; i<NUM_SCORES; i++) {
1415 rb->snprintf(str, 11, "#%02d: %d", i+1, bj->highscores[i]);
1416 rb->lcd_puts(0, i+j, str);
1419 rb->lcd_update();
1420 while(true) {
1421 button = rb->button_get(true);
1422 if(button != BUTTON_NONE && !(button&BUTTON_REL)) break;
1424 rb->lcd_clear_display();
1425 continue;
1427 case MRES_HELP:
1428 /* welcome screen to display key bindings */
1429 rb->lcd_clear_display();
1430 rb->snprintf(str, 5, "%s", "Help");
1431 rb->lcd_getstringsize(str, &w, &h);
1432 rb->lcd_putsxy((LCD_WIDTH-w)/2, 0, str);
1433 #if CONFIG_KEYPAD == RECORDER_PAD
1434 rb->lcd_puts(0, 2, "Controls:");
1435 rb->lcd_puts(0, 3, "Directions = move");
1436 rb->lcd_puts(0, 4, "PLAY = select");
1437 rb->lcd_puts(0, 5, "Long PLAY = menu");
1438 rb->lcd_puts(0, 6, "OFF = cancel");
1439 #elif CONFIG_KEYPAD == ONDIO_PAD
1440 rb->lcd_puts(0, 2, "Controls:");
1441 rb->lcd_puts(0, 3, "Directions = move");
1442 rb->lcd_puts(0, 4, "MENU = select");
1443 rb->lcd_puts(0, 5, "Long MENU = menu");
1444 rb->lcd_puts(0, 6, "OFF = cancel");
1445 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
1446 rb->lcd_puts(0, 2, "Controls:");
1447 rb->lcd_puts(0, 3, "Directions = move");
1448 rb->lcd_puts(0, 4, "SELECT = select");
1449 rb->lcd_puts(0, 5, "Long SELECT = menu");
1450 rb->lcd_puts(0, 6, "PLAY = cancel");
1451 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
1452 rb->lcd_puts(0, 2, "Swap pairs of jewels to");
1453 rb->lcd_puts(0, 3, "form connected segments");
1454 rb->lcd_puts(0, 4, "of three or more of the");
1455 rb->lcd_puts(0, 5, "same type.");
1456 rb->lcd_puts(0, 7, "Controls:");
1457 rb->lcd_puts(0, 8, "Directions to move");
1458 rb->lcd_puts(0, 9, "SELECT to select");
1459 rb->lcd_puts(0, 10, "Long SELECT to show menu");
1460 rb->lcd_puts(0, 11, "OFF to cancel");
1461 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
1462 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
1463 rb->lcd_puts(0, 2, "Swap pairs of jewels to");
1464 rb->lcd_puts(0, 3, "form connected segments");
1465 rb->lcd_puts(0, 4, "of three or more of the");
1466 rb->lcd_puts(0, 5, "same type.");
1467 rb->lcd_puts(0, 7, "Controls:");
1468 rb->lcd_puts(0, 8, "Directions or scroll to move");
1469 rb->lcd_puts(0, 9, "SELECT to select");
1470 rb->lcd_puts(0, 10, "Long SELECT to show menu");
1471 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
1472 rb->lcd_puts(0, 2, "Swap pairs of jewels to");
1473 rb->lcd_puts(0, 3, "form connected segments");
1474 rb->lcd_puts(0, 4, "of three or more of the");
1475 rb->lcd_puts(0, 5, "same type.");
1476 rb->lcd_puts(0, 7, "Controls:");
1477 rb->lcd_puts(0, 8, "Directions to move");
1478 rb->lcd_puts(0, 9, "SELECT to select");
1479 rb->lcd_puts(0, 10, "Long SELECT to show menu");
1480 rb->lcd_puts(0, 11, "PLAY to cancel");
1481 #elif CONFIG_KEYPAD == GIGABEAT_PAD
1482 rb->lcd_puts(0, 2, "Swap pairs of jewels to");
1483 rb->lcd_puts(0, 3, "form connected segments");
1484 rb->lcd_puts(0, 4, "of three or more of the");
1485 rb->lcd_puts(0, 5, "same type.");
1486 rb->lcd_puts(0, 7, "Controls:");
1487 rb->lcd_puts(0, 8, "Directions to move");
1488 rb->lcd_puts(0, 9, "SELECT to select");
1489 rb->lcd_puts(0, 10, "Long SELECT to show menu");
1490 rb->lcd_puts(0, 11, "POWER to cancel");
1491 #elif CONFIG_KEYPAD == SANSA_E200_PAD \
1492 || CONFIG_KEYPAD == SANSA_C200_PAD
1493 rb->lcd_puts(0, 2, "Swap pairs of jewels to");
1494 rb->lcd_puts(0, 3, "form connected segments");
1495 rb->lcd_puts(0, 4, "of three or more of the");
1496 rb->lcd_puts(0, 5, "same type.");
1497 rb->lcd_puts(0, 7, "Controls:");
1498 rb->lcd_puts(0, 8, "Directions to move");
1499 rb->lcd_puts(0, 9, "SELECT to select");
1500 rb->lcd_puts(0, 10, "Long SELECT to show menu");
1501 rb->lcd_puts(0, 11, "POWER to cancel");
1502 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
1503 rb->lcd_puts(0, 2, "Swap pairs of jewels");
1504 rb->lcd_puts(0, 3, "to form connected");
1505 rb->lcd_puts(0, 4, "segments of three or ");
1506 rb->lcd_puts(0, 5, "more of the");
1507 rb->lcd_puts(0, 6, "same type.");
1508 rb->lcd_puts(0, 8, "Controls:");
1509 rb->lcd_puts(0, 9, "Directions or scroll to move");
1510 rb->lcd_puts(0, 10, "PLAY to select");
1511 rb->lcd_puts(0, 11, "Long PLAY for menu");
1512 rb->lcd_puts(0, 12, "POWER to cancel");
1513 #else
1514 #warning: missing help text.
1515 #endif
1516 rb->lcd_update();
1517 while(true) {
1518 button = rb->button_get(true);
1519 if(button != BUTTON_NONE && !(button&BUTTON_REL)) break;
1521 rb->lcd_clear_display();
1522 continue;
1524 case MRES_QUIT:
1525 return BJ_QUIT;
1527 default:
1528 break;
1531 /* handle menu button presses */
1532 button = rb->button_get(true);
1533 switch(button){
1534 #ifdef JEWELS_SCROLLWHEEL
1535 case JEWELS_PREV:
1536 case (JEWELS_PREV|BUTTON_REPEAT):
1537 #endif
1538 case JEWELS_UP:
1539 case (JEWELS_UP|BUTTON_REPEAT):
1540 cmd = MCMD_PREV;
1541 break;
1543 #ifdef JEWELS_SCROLLWHEEL
1544 case JEWELS_NEXT:
1545 case (JEWELS_NEXT|BUTTON_REPEAT):
1546 #endif
1547 case JEWELS_DOWN:
1548 case (JEWELS_DOWN|BUTTON_REPEAT):
1549 cmd = MCMD_NEXT;
1550 break;
1552 case JEWELS_SELECT:
1553 case JEWELS_RIGHT:
1554 cmd = MCMD_SELECT;
1555 break;
1557 #ifdef JEWELS_CANCEL
1558 #ifdef JEWELS_RC_CANCEL
1559 case JEWELS_RC_CANCEL:
1560 #endif
1561 case JEWELS_CANCEL:
1562 return BJ_QUIT;
1563 #endif
1565 default:
1566 if(rb->default_event_handler_ex(button, jewels_callback,
1567 (void*) bj) == SYS_USB_CONNECTED)
1568 return BJ_USB;
1569 break;
1573 /********************
1574 * init *
1575 ********************/
1576 jewels_init(bj);
1578 /********************
1579 * setup the board *
1580 ********************/
1581 bj->score += jewels_initlevel(bj);
1582 if (!jewels_movesavail(bj)) {
1583 switch(bj->type) {
1584 case GAME_TYPE_NORMAL:
1585 return BJ_LOSE;
1587 case GAME_TYPE_PUZZLE:
1588 do {
1589 rb->splash(2*HZ, "No more moves!");
1590 bj->score += jewels_initlevel(bj);
1591 } while(!jewels_movesavail(bj));
1592 break;
1596 /**********************
1597 * play *
1598 **********************/
1599 while(true) {
1600 int no_movesavail = false;
1602 if(!inmenu) {
1603 /* refresh the board */
1604 jewels_drawboard(bj);
1606 /* display the cursor */
1607 if(selected) {
1608 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1609 rb->lcd_fillrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1610 TILE_WIDTH, TILE_HEIGHT);
1611 rb->lcd_set_drawmode(DRMODE_SOLID);
1612 } else {
1613 rb->lcd_drawrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1614 TILE_WIDTH, TILE_HEIGHT);
1616 rb->lcd_update_rect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1617 TILE_WIDTH, TILE_HEIGHT);
1618 } else {
1619 res = jewels_showmenu(&bjmenu[1], cmd);
1620 cmd = MCMD_NONE;
1621 switch(res) {
1622 case MRES_RESUME:
1623 inmenu = false;
1624 selected = false;
1625 continue;
1627 case MRES_PLAYBACK:
1628 playback_control(rb);
1629 rb->lcd_setfont(FONT_SYSFIXED);
1630 inmenu = false;
1631 selected = false;
1632 break;
1634 case MRES_SAVE:
1635 rb->splash(HZ, "Saving game...");
1636 jewels_savegame(bj);
1637 return BJ_END;
1639 case MRES_QUIT:
1640 return BJ_END;
1642 case MRES_EXIT:
1643 return BJ_QUIT_FROM_GAME;
1645 default:
1646 break;
1650 /* handle game button presses */
1651 button = rb->button_get(true);
1652 switch(button){
1653 case JEWELS_LEFT: /* move cursor left */
1654 case (JEWELS_LEFT|BUTTON_REPEAT):
1655 if(!inmenu) {
1656 if(selected) {
1657 bj->score += jewels_swapjewels(bj, x, y, SWAP_LEFT);
1658 selected = false;
1659 if (!jewels_movesavail(bj)) no_movesavail = true;
1660 } else {
1661 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1664 break;
1666 case JEWELS_RIGHT: /* move cursor right */
1667 case (JEWELS_RIGHT|BUTTON_REPEAT):
1668 if(!inmenu) {
1669 if(selected) {
1670 bj->score += jewels_swapjewels(bj, x, y, SWAP_RIGHT);
1671 selected = false;
1672 if (!jewels_movesavail(bj)) no_movesavail = true;
1673 } else {
1674 x = (x+1)%BJ_WIDTH;
1676 } else {
1677 cmd = MCMD_SELECT;
1679 break;
1681 case JEWELS_DOWN: /* move cursor down */
1682 case (JEWELS_DOWN|BUTTON_REPEAT):
1683 if(!inmenu) {
1684 if(selected) {
1685 bj->score += jewels_swapjewels(bj, x, y, SWAP_DOWN);
1686 selected = false;
1687 if (!jewels_movesavail(bj)) no_movesavail = true;
1688 } else {
1689 y = (y+1)%(BJ_HEIGHT-1);
1691 } else {
1692 cmd = MCMD_NEXT;
1694 break;
1696 case JEWELS_UP: /* move cursor up */
1697 case (JEWELS_UP|BUTTON_REPEAT):
1698 if(!inmenu) {
1699 if(selected) {
1700 bj->score += jewels_swapjewels(bj, x, y, SWAP_UP);
1701 selected = false;
1702 if (!jewels_movesavail(bj)) no_movesavail = true;
1703 } else {
1704 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1706 } else {
1707 cmd = MCMD_PREV;
1709 break;
1711 #ifdef JEWELS_SCROLLWHEEL
1712 case JEWELS_PREV: /* scroll backwards */
1713 case (JEWELS_PREV|BUTTON_REPEAT):
1714 if(!inmenu) {
1715 if(!selected) {
1716 if(x == 0) {
1717 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1719 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1721 } else {
1722 cmd = MCMD_PREV;
1724 break;
1726 case JEWELS_NEXT: /* scroll forwards */
1727 case (JEWELS_NEXT|BUTTON_REPEAT):
1728 if(!inmenu) {
1729 if(!selected) {
1730 if(x == BJ_WIDTH-1) {
1731 y = (y+1)%(BJ_HEIGHT-1);
1733 x = (x+1)%BJ_WIDTH;
1735 } else {
1736 cmd = MCMD_NEXT;
1738 break;
1739 #endif
1741 case JEWELS_SELECT: /* toggle selected */
1742 if(!inmenu) {
1743 selected = !selected;
1744 } else {
1745 cmd = MCMD_SELECT;
1747 break;
1749 case (JEWELS_SELECT|BUTTON_REPEAT): /* show menu */
1750 if(!inmenu) inmenu = true;
1751 break;
1753 #ifdef JEWELS_CANCEL
1754 #ifdef JEWELS_RC_CANCEL
1755 case JEWELS_RC_CANCEL:
1756 #endif
1757 case JEWELS_CANCEL: /* end game */
1758 return BJ_END;
1759 break;
1760 #endif
1762 default:
1763 if(rb->default_event_handler_ex(button, jewels_callback,
1764 (void*) bj) == SYS_USB_CONNECTED)
1765 return BJ_USB;
1766 break;
1769 if (no_movesavail) {
1770 switch(bj->type) {
1771 case GAME_TYPE_NORMAL:
1772 return BJ_LOSE;
1774 case GAME_TYPE_PUZZLE:
1775 do {
1776 rb->splash(2*HZ, "No more moves!");
1777 bj->score += jewels_initlevel(bj);
1778 } while(!jewels_movesavail(bj));
1779 break;
1783 switch(bj->type) {
1784 case GAME_TYPE_NORMAL:
1785 if(bj->score >= LEVEL_PTS) bj->score = jewels_nextlevel(bj);
1786 break;
1788 case GAME_TYPE_PUZZLE:
1789 if(jewels_puzzle_is_finished(bj))
1790 bj->score += jewels_nextlevel(bj);
1791 break;
1796 /*****************************************************************************
1797 * plugin entry point.
1798 ******************************************************************************/
1799 enum plugin_status plugin_start(struct plugin_api* api, void* parameter) {
1800 struct game_context bj;
1801 bool exit = false;
1802 int position;
1803 char str[19];
1805 /* plugin init */
1806 (void)parameter;
1807 rb = api;
1808 /* end of plugin init */
1810 /* load high scores */
1811 jewels_loadscores(&bj);
1813 rb->lcd_setfont(FONT_SYSFIXED);
1814 #if LCD_DEPTH > 1
1815 rb->lcd_set_backdrop(NULL);
1816 #endif
1817 jewels_setcolors();
1819 while(!exit) {
1820 switch(jewels_main(&bj)){
1821 case BJ_LOSE:
1822 rb->splash(HZ*2, "No more moves!");
1823 /* fall through to BJ_END */
1825 case BJ_END:
1826 if(!bj.resume) {
1827 if((position = jewels_recordscore(&bj))) {
1828 rb->snprintf(str, 19, "New high score #%d!", position);
1829 rb->splash(HZ*2, str);
1832 break;
1834 case BJ_USB:
1835 rb->lcd_setfont(FONT_UI);
1836 return PLUGIN_USB_CONNECTED;
1838 case BJ_QUIT:
1839 if(bj.dirty) {
1840 rb->splash(HZ, "Saving high scores...");
1841 jewels_savescores(&bj);
1843 exit = true;
1844 break;
1846 case BJ_QUIT_FROM_GAME:
1847 if(!bj.resume) {
1848 if((position = jewels_recordscore(&bj))) {
1849 rb->snprintf(str, 19, "New high score #%d!", position);
1850 rb->splash(HZ*2, str);
1853 if(bj.dirty) {
1854 rb->splash(HZ, "Saving high scores...");
1855 jewels_savescores(&bj);
1857 exit = true;
1858 break;
1860 default:
1861 break;
1865 rb->lcd_setfont(FONT_UI);
1866 return PLUGIN_OK;
1869 #endif /* HAVE_LCD_BITMAP */