New makefile solution: A single invocation of 'make' to build the entire tree. Fully...
[kugel-rb.git] / apps / plugins / jewels.c
blob239827c12356663ba583e53f2ca65ea8576b249a
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 Adam Boot
12 * Color graphics from Gweled (http://sebdelestaing.free.fr/gweled/)
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ****************************************************************************/
24 #include "plugin.h"
25 #include "lib/playback_control.h"
27 #ifdef HAVE_LCD_BITMAP
29 PLUGIN_HEADER
31 /* button definitions */
32 #if CONFIG_KEYPAD == RECORDER_PAD
33 #define JEWELS_UP BUTTON_UP
34 #define JEWELS_DOWN BUTTON_DOWN
35 #define JEWELS_LEFT BUTTON_LEFT
36 #define JEWELS_RIGHT BUTTON_RIGHT
37 #define JEWELS_SELECT BUTTON_PLAY
38 #define JEWELS_CANCEL BUTTON_OFF
40 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
41 #define JEWELS_UP BUTTON_UP
42 #define JEWELS_DOWN BUTTON_DOWN
43 #define JEWELS_LEFT BUTTON_LEFT
44 #define JEWELS_RIGHT BUTTON_RIGHT
45 #define JEWELS_SELECT BUTTON_SELECT
46 #define JEWELS_CANCEL BUTTON_OFF
48 #elif CONFIG_KEYPAD == ONDIO_PAD
49 #define JEWELS_UP BUTTON_UP
50 #define JEWELS_DOWN BUTTON_DOWN
51 #define JEWELS_LEFT BUTTON_LEFT
52 #define JEWELS_RIGHT BUTTON_RIGHT
53 #define JEWELS_SELECT BUTTON_MENU
54 #define JEWELS_CANCEL BUTTON_OFF
56 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
57 #define JEWELS_UP BUTTON_UP
58 #define JEWELS_DOWN BUTTON_DOWN
59 #define JEWELS_LEFT BUTTON_LEFT
60 #define JEWELS_RIGHT BUTTON_RIGHT
61 #define JEWELS_SELECT BUTTON_SELECT
62 #define JEWELS_CANCEL BUTTON_OFF
63 #define JEWELS_RC_CANCEL BUTTON_RC_STOP
65 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
66 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
67 #define JEWELS_SCROLLWHEEL
68 #define JEWELS_UP BUTTON_MENU
69 #define JEWELS_DOWN BUTTON_PLAY
70 #define JEWELS_LEFT BUTTON_LEFT
71 #define JEWELS_RIGHT BUTTON_RIGHT
72 #define JEWELS_PREV BUTTON_SCROLL_BACK
73 #define JEWELS_NEXT BUTTON_SCROLL_FWD
74 #define JEWELS_SELECT BUTTON_SELECT
76 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
77 #define JEWELS_UP BUTTON_UP
78 #define JEWELS_DOWN BUTTON_DOWN
79 #define JEWELS_LEFT BUTTON_LEFT
80 #define JEWELS_RIGHT BUTTON_RIGHT
81 #define JEWELS_SELECT BUTTON_SELECT
82 #define JEWELS_CANCEL BUTTON_PLAY
84 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
85 #define JEWELS_UP BUTTON_UP
86 #define JEWELS_DOWN BUTTON_DOWN
87 #define JEWELS_LEFT BUTTON_LEFT
88 #define JEWELS_RIGHT BUTTON_RIGHT
89 #define JEWELS_SELECT BUTTON_SELECT
90 #define JEWELS_CANCEL BUTTON_POWER
92 #elif CONFIG_KEYPAD == GIGABEAT_PAD
93 #define JEWELS_UP BUTTON_UP
94 #define JEWELS_DOWN BUTTON_DOWN
95 #define JEWELS_LEFT BUTTON_LEFT
96 #define JEWELS_RIGHT BUTTON_RIGHT
97 #define JEWELS_SELECT BUTTON_SELECT
98 #define JEWELS_CANCEL BUTTON_POWER
100 #elif CONFIG_KEYPAD == SANSA_E200_PAD
101 #define JEWELS_SCROLLWHEEL
102 #define JEWELS_UP BUTTON_UP
103 #define JEWELS_DOWN BUTTON_DOWN
104 #define JEWELS_LEFT BUTTON_LEFT
105 #define JEWELS_RIGHT BUTTON_RIGHT
106 #define JEWELS_PREV BUTTON_SCROLL_BACK
107 #define JEWELS_NEXT BUTTON_SCROLL_FWD
108 #define JEWELS_SELECT BUTTON_SELECT
109 #define JEWELS_CANCEL BUTTON_POWER
111 #elif CONFIG_KEYPAD == SANSA_C200_PAD
112 #define JEWELS_UP BUTTON_UP
113 #define JEWELS_DOWN BUTTON_DOWN
114 #define JEWELS_LEFT BUTTON_LEFT
115 #define JEWELS_RIGHT BUTTON_RIGHT
116 #define JEWELS_SELECT BUTTON_SELECT
117 #define JEWELS_CANCEL BUTTON_POWER
119 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
120 #define JEWELS_UP BUTTON_SCROLL_UP
121 #define JEWELS_DOWN BUTTON_SCROLL_DOWN
122 #define JEWELS_LEFT BUTTON_LEFT
123 #define JEWELS_RIGHT BUTTON_RIGHT
124 #define JEWELS_SELECT BUTTON_PLAY
125 #define JEWELS_CANCEL BUTTON_POWER
127 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
128 #define JEWELS_UP BUTTON_UP
129 #define JEWELS_DOWN BUTTON_DOWN
130 #define JEWELS_LEFT BUTTON_LEFT
131 #define JEWELS_RIGHT BUTTON_RIGHT
132 #define JEWELS_SELECT BUTTON_SELECT
133 #define JEWELS_CANCEL BUTTON_BACK
135 #elif CONFIG_KEYPAD == MROBE100_PAD
136 #define JEWELS_UP BUTTON_UP
137 #define JEWELS_DOWN BUTTON_DOWN
138 #define JEWELS_LEFT BUTTON_LEFT
139 #define JEWELS_RIGHT BUTTON_RIGHT
140 #define JEWELS_SELECT BUTTON_SELECT
141 #define JEWELS_CANCEL BUTTON_POWER
143 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
144 #define JEWELS_UP BUTTON_RC_VOL_UP
145 #define JEWELS_DOWN BUTTON_RC_VOL_DOWN
146 #define JEWELS_LEFT BUTTON_RC_REW
147 #define JEWELS_RIGHT BUTTON_RC_FF
148 #define JEWELS_SELECT BUTTON_RC_PLAY
149 #define JEWELS_CANCEL BUTTON_RC_REC
151 #define JEWELS_RC_CANCEL BUTTON_REC
153 #elif CONFIG_KEYPAD == COWOND2_PAD
154 #define JEWELS_CANCEL BUTTON_POWER
156 #elif CONFIG_KEYPAD == IAUDIO67_PAD
157 #define JEWELS_UP BUTTON_STOP
158 #define JEWELS_DOWN BUTTON_PLAY
159 #define JEWELS_LEFT BUTTON_LEFT
160 #define JEWELS_RIGHT BUTTON_RIGHT
161 #define JEWELS_SELECT BUTTON_MENU
162 #define JEWELS_CANCEL BUTTON_POWER
164 #else
165 #error No keymap defined!
166 #endif
168 #ifdef HAVE_TOUCHSCREEN
169 #ifndef JEWELS_UP
170 #define JEWELS_UP BUTTON_TOPMIDDLE
171 #endif
172 #ifndef JEWELS_DOWN
173 #define JEWELS_DOWN BUTTON_BOTTOMMIDDLE
174 #endif
175 #ifndef JEWELS_LEFT
176 #define JEWELS_LEFT BUTTON_MIDLEFT
177 #endif
178 #ifndef JEWELS_RIGHT
179 #define JEWELS_RIGHT BUTTON_MIDRIGHT
180 #endif
181 #ifndef JEWELS_SELECT
182 #define JEWELS_SELECT BUTTON_CENTER
183 #endif
184 #ifndef JEWELS_CANCEL
185 #define JEWELS_CANCEL BUTTON_TOPLEFT
186 #endif
187 #endif
189 /* use 30x30 tiles (iPod Video, Gigabeat) */
190 #if (LCD_HEIGHT == 240) && (LCD_WIDTH == 320) || \
191 ((LCD_HEIGHT == 320) && (LCD_WIDTH == 240))
192 #define TILE_WIDTH 30
193 #define TILE_HEIGHT 30
194 #define YOFS 0
195 #define NUM_SCORES 10
197 /* use 22x22 tiles (H300, iPod Color) */
198 #elif ((LCD_HEIGHT == 176) && (LCD_WIDTH == 220)) || \
199 ((LCD_HEIGHT == 220) && (LCD_WIDTH == 176))
200 #define TILE_WIDTH 22
201 #define TILE_HEIGHT 22
202 #define YOFS 0
203 #define NUM_SCORES 10
205 /* use 16x16 tiles (iPod Nano) */
206 #elif (LCD_HEIGHT == 132) && (LCD_WIDTH == 176)
207 #define TILE_WIDTH 16
208 #define TILE_HEIGHT 16
209 #define YOFS 4
210 #define NUM_SCORES 10
212 /* use 16x16 tiles (H100, iAudio X5, iPod 3G, iPod 4G grayscale) */
213 #elif (LCD_HEIGHT == 128) && (LCD_WIDTH == 160)
214 #define TILE_WIDTH 16
215 #define TILE_HEIGHT 16
216 #define YOFS 0
217 #define NUM_SCORES 10
219 /* use 14x14 tiles (H10 5/6 GB) */
220 #elif (LCD_HEIGHT == 128) && (LCD_WIDTH == 128)
221 #define TILE_WIDTH 14
222 #define TILE_HEIGHT 14
223 #define YOFS 0
224 #define NUM_SCORES 10
226 /* use 13x13 tiles (iPod Mini) */
227 #elif (LCD_HEIGHT == 110) && (LCD_WIDTH == 138)
228 #define TILE_WIDTH 13
229 #define TILE_HEIGHT 13
230 #define YOFS 6
231 #define NUM_SCORES 10
233 /* use 12x12 tiles (iAudio M3) */
234 #elif (LCD_HEIGHT == 96) && (LCD_WIDTH == 128)
235 #define TILE_WIDTH 12
236 #define TILE_HEIGHT 12
237 #define YOFS 0
238 #define NUM_SCORES 9
240 /* use 10x10 tiles (Sansa c200) */
241 #elif (LCD_HEIGHT == 80) && (LCD_WIDTH == 132)
242 #define TILE_WIDTH 10
243 #define TILE_HEIGHT 10
244 #define YOFS 0
245 #define NUM_SCORES 8
247 /* use 10x8 tiles (iFP 700) */
248 #elif (LCD_HEIGHT == 64) && (LCD_WIDTH == 128)
249 #define TILE_WIDTH 10
250 #define TILE_HEIGHT 8
251 #define YOFS 0
252 #define NUM_SCORES 8
254 /* use 10x8 tiles (Recorder, Ondio) */
255 #elif (LCD_HEIGHT == 64) && (LCD_WIDTH == 112)
256 #define TILE_WIDTH 10
257 #define TILE_HEIGHT 8
258 #define YOFS 0
259 #define NUM_SCORES 8
261 #else
262 #error JEWELS: Unsupported LCD
263 #endif
265 /* save files */
266 #define SCORE_FILE PLUGIN_GAMES_DIR "/jewels.score"
267 #define SAVE_FILE PLUGIN_GAMES_DIR "/jewels.save"
269 /* final game return status */
270 #define BJ_QUIT_FROM_GAME 4
271 #define BJ_END 3
272 #define BJ_USB 2
273 #define BJ_QUIT 1
274 #define BJ_LOSE 0
276 /* swap directions */
277 #define SWAP_UP 0
278 #define SWAP_RIGHT 1
279 #define SWAP_DOWN 2
280 #define SWAP_LEFT 3
282 /* play board dimension */
283 #define BJ_HEIGHT 9
284 #define BJ_WIDTH 8
286 /* next level threshold */
287 #define LEVEL_PTS 100
289 /* animation frame rate */
290 #define MAX_FPS 20
292 /* Game types */
293 enum game_type {
294 GAME_TYPE_NORMAL,
295 GAME_TYPE_PUZZLE
298 /* menu values */
299 #define FONT_HEIGHT 8
300 #define MAX_MITEMS 6
301 #define MENU_WIDTH 100
303 /* menu results */
304 enum menu_result {
305 MRES_NONE,
306 MRES_NEW,
307 MRES_PUZZLE,
308 MRES_SAVE,
309 MRES_RESUME,
310 MRES_SCORES,
311 MRES_HELP,
312 MRES_QUIT,
313 MRES_PLAYBACK,
314 MRES_EXIT
317 /* menu commands */
318 enum menu_cmd {
319 MCMD_NONE,
320 MCMD_NEXT,
321 MCMD_PREV,
322 MCMD_SELECT
325 /* menus */
326 struct jewels_menu {
327 char *title;
328 bool hasframe;
329 int selected;
330 int itemcnt;
331 struct jewels_menuitem {
332 char *text;
333 enum menu_result res;
334 } items[MAX_MITEMS];
335 } bjmenu[] = {
336 {"Jewels", false, 0, 6,
337 {{"New Game", MRES_NEW},
338 {"Puzzle", MRES_PUZZLE},
339 {"Resume Game", MRES_RESUME},
340 {"High Scores", MRES_SCORES},
341 {"Help", MRES_HELP},
342 {"Quit", MRES_QUIT}}},
343 {"Menu", true, 0, 5,
344 {{"Audio Playback", MRES_PLAYBACK },
345 {"Resume Game", MRES_RESUME},
346 {"Save Game", MRES_SAVE},
347 {"End Game", MRES_QUIT},
348 {"Exit Jewels", MRES_EXIT}}}
351 /* global rockbox api */
352 static const struct plugin_api* rb;
354 /* external bitmaps */
355 extern const fb_data jewels[];
357 /* tile background colors */
358 #ifdef HAVE_LCD_COLOR
359 static const unsigned jewels_bkgd[2] = {
360 LCD_RGBPACK(104, 63, 63),
361 LCD_RGBPACK(83, 44, 44)
363 #endif
365 /* the tile struct
366 * type is the jewel number 0-7
367 * falling if the jewel is falling
368 * delete marks the jewel for deletion
370 struct tile {
371 int type;
372 bool falling;
373 bool delete;
376 /* the game context struct
377 * score is the current level score
378 * segments is the number of cleared segments in the current run
379 * level is the current level
380 * type is the game type (normal or puzzle)
381 * highscores is the list of high scores
382 * resume denotes whether to resume the currently loaded game
383 * dirty denotes whether the high scores are out of sync with the saved file
384 * playboard is the game playing board (first row is hidden)
385 * num_jewels is the number of different jewels to use
387 struct game_context {
388 unsigned int score;
389 unsigned int segments;
390 unsigned int level;
391 unsigned int type;
392 unsigned int highscores[NUM_SCORES];
393 bool resume;
394 bool dirty;
395 struct tile playboard[BJ_HEIGHT][BJ_WIDTH];
396 unsigned int num_jewels;
399 #define MAX_NUM_JEWELS 7
401 #define MAX_PUZZLE_TILES 4
402 #define NUM_PUZZLE_LEVELS 10
404 struct puzzle_tile {
405 int x;
406 int y;
407 int tile_type;
410 struct puzzle_level {
411 unsigned int num_jewels;
412 unsigned int num_tiles;
413 struct puzzle_tile tiles[MAX_PUZZLE_TILES];
416 #define PUZZLE_TILE_UP 1
417 #define PUZZLE_TILE_DOWN 2
418 #define PUZZLE_TILE_LEFT 4
419 #define PUZZLE_TILE_RIGHT 8
421 struct puzzle_level puzzle_levels[NUM_PUZZLE_LEVELS] = {
422 { 5, 2, { {3, 3, PUZZLE_TILE_RIGHT},
423 {4, 2, PUZZLE_TILE_LEFT} } },
424 { 5, 2, { {3, 2, PUZZLE_TILE_DOWN},
425 {3, 4, PUZZLE_TILE_UP} } },
426 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
427 {3, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
428 {3, 6, PUZZLE_TILE_UP} } },
429 { 6, 3, { {3, 2, PUZZLE_TILE_RIGHT},
430 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
431 {5, 4, PUZZLE_TILE_LEFT} } },
432 { 6, 2, { {3, 4, PUZZLE_TILE_RIGHT},
433 {4, 2, PUZZLE_TILE_LEFT} } },
434 { 6, 2, { {3, 2, PUZZLE_TILE_DOWN},
435 {4, 4, PUZZLE_TILE_UP} } },
436 { 7, 4, { {3, 2, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
437 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
438 {3, 4, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
439 {4, 4, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
440 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
441 {4, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
442 {3, 6, PUZZLE_TILE_UP} } },
443 { 7, 3, { {2, 2, PUZZLE_TILE_RIGHT},
444 {4, 1, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
445 {5, 4, PUZZLE_TILE_LEFT} } },
446 { 7, 4, { {3, 0, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
447 {5, 0, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
448 {2, 7, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
449 {4, 7, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
452 /*****************************************************************************
453 * jewels_init() initializes jewels data structures.
454 ******************************************************************************/
455 static void jewels_init(struct game_context* bj) {
456 /* seed the rand generator */
457 rb->srand(*rb->current_tick);
459 /* check for resumed game */
460 if(bj->resume) {
461 bj->resume = false;
462 return;
465 /* reset scoring */
466 bj->level = 1;
467 bj->score = 0;
468 bj->segments = 0;
470 /* clear playing board */
471 rb->memset(bj->playboard, 0, sizeof(bj->playboard));
474 /*****************************************************************************
475 * jewels_setcolors() set the foreground and background colors.
476 ******************************************************************************/
477 static inline void jewels_setcolors(void) {
478 #ifdef HAVE_LCD_COLOR
479 rb->lcd_set_background(LCD_RGBPACK(49, 26, 26));
480 rb->lcd_set_foreground(LCD_RGBPACK(210, 181, 181));
481 #endif
484 /*****************************************************************************
485 * jewels_drawboard() redraws the entire game board.
486 ******************************************************************************/
487 static void jewels_drawboard(struct game_context* bj) {
488 int i, j;
489 int w, h;
490 unsigned int tempscore;
491 char *title = "Level";
492 char str[10];
494 tempscore = (bj->score>LEVEL_PTS ? LEVEL_PTS : bj->score);
496 /* clear screen */
497 rb->lcd_clear_display();
499 /* dispay playing board */
500 for(i=0; i<BJ_HEIGHT-1; i++){
501 for(j=0; j<BJ_WIDTH; j++){
502 #ifdef HAVE_LCD_COLOR
503 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
504 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
505 TILE_WIDTH, TILE_HEIGHT);
506 rb->lcd_bitmap_transparent_part(jewels,
507 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
508 TILE_WIDTH, j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
509 TILE_WIDTH, TILE_HEIGHT);
510 #else
511 rb->lcd_bitmap_part(jewels,
512 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
513 TILE_WIDTH, j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
514 TILE_WIDTH, TILE_HEIGHT);
515 #endif
519 #if LCD_WIDTH > LCD_HEIGHT /* horizontal layout */
521 /* draw separator lines */
522 jewels_setcolors();
523 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, LCD_HEIGHT-1);
524 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, 18);
525 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, LCD_HEIGHT-10);
527 /* draw progress bar */
528 #ifdef HAVE_LCD_COLOR
529 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
530 #endif
531 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
532 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
533 tempscore/LEVEL_PTS),
534 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
535 ((LCD_HEIGHT-10)-18)*tempscore/LEVEL_PTS);
536 #ifdef HAVE_LCD_COLOR
537 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
538 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
539 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
540 tempscore/LEVEL_PTS)+1,
541 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
542 ((LCD_HEIGHT-10)-18)*tempscore/LEVEL_PTS-1);
543 jewels_setcolors();
544 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
545 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
546 tempscore/LEVEL_PTS),
547 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
548 ((LCD_HEIGHT-10)-18)*tempscore/LEVEL_PTS+1);
549 #endif
551 /* print text */
552 rb->lcd_getstringsize(title, &w, &h);
553 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 1, title);
555 rb->snprintf(str, 4, "%d", bj->level);
556 rb->lcd_getstringsize(str, &w, &h);
557 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 10, str);
559 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
560 rb->lcd_getstringsize(str, &w, &h);
561 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2,
562 LCD_HEIGHT-8, str);
564 #elif LCD_WIDTH < LCD_HEIGHT /* vertical layout */
566 /* draw separator lines */
567 jewels_setcolors();
568 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
569 rb->lcd_hline(0, LCD_WIDTH-1, LCD_HEIGHT-14);
570 rb->lcd_vline(LCD_WIDTH/2, LCD_HEIGHT-14, LCD_HEIGHT-1);
572 /* draw progress bar */
573 #ifdef HAVE_LCD_COLOR
574 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
575 #endif
576 rb->lcd_fillrect(0, (8*TILE_HEIGHT+YOFS)
577 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
578 LCD_WIDTH*tempscore/LEVEL_PTS,
579 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
580 #ifdef HAVE_LCD_COLOR
581 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
582 rb->lcd_drawrect(1, (8*TILE_HEIGHT+YOFS)
583 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4+1,
584 LCD_WIDTH*tempscore/LEVEL_PTS-1,
585 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2-2);
586 jewels_setcolors();
587 rb->lcd_drawrect(0, (8*TILE_HEIGHT+YOFS)
588 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
589 LCD_WIDTH*tempscore/LEVEL_PTS+1,
590 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
591 #endif
593 /* print text */
594 rb->snprintf(str, 10, "%s %d", title, bj->level);
595 rb->lcd_putsxy(1, LCD_HEIGHT-10, str);
597 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
598 rb->lcd_getstringsize(str, &w, &h);
599 rb->lcd_putsxy((LCD_WIDTH-2)-w, LCD_HEIGHT-10, str);
601 #else /* square layout */
603 /* draw separator lines */
604 jewels_setcolors();
605 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
606 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, 8*TILE_HEIGHT+YOFS);
607 rb->lcd_vline(LCD_WIDTH/2, 8*TILE_HEIGHT+YOFS, LCD_HEIGHT-1);
609 /* draw progress bar */
610 #ifdef HAVE_LCD_COLOR
611 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
612 #endif
613 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
614 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
615 *tempscore/LEVEL_PTS,
616 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
617 (8*TILE_HEIGHT+YOFS)*tempscore/LEVEL_PTS);
618 #ifdef HAVE_LCD_COLOR
619 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
620 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
621 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
622 *tempscore/LEVEL_PTS+1,
623 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
624 (8*TILE_HEIGHT+YOFS)*tempscore/LEVEL_PTS-1);
625 jewels_setcolors();
626 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
627 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
628 *tempscore/LEVEL_PTS,
629 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
630 (8*TILE_HEIGHT+YOFS)*tempscore/LEVEL_PTS+1);
631 #endif
633 /* print text */
634 rb->snprintf(str, 10, "%s %d", title, bj->level);
635 rb->lcd_putsxy(1, LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
637 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
638 rb->lcd_getstringsize(str, &w, &h);
639 rb->lcd_putsxy((LCD_WIDTH-2)-w,
640 LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
642 #endif /* layout */
644 rb->lcd_update();
647 /*****************************************************************************
648 * jewels_showmenu() displays the chosen menu after performing the chosen
649 * menu command.
650 ******************************************************************************/
651 static enum menu_result jewels_showmenu(struct jewels_menu* menu,
652 enum menu_cmd cmd) {
653 int i;
654 int w, h;
655 int firstline;
656 int adj;
657 int extraline = LCD_HEIGHT <= ((menu->itemcnt+2)*FONT_HEIGHT) ? 0 : 1;
659 /* handle menu command */
660 switch(cmd) {
661 case MCMD_NEXT:
662 menu->selected = (menu->selected+1)%menu->itemcnt;
663 break;
665 case MCMD_PREV:
666 menu->selected = (menu->selected-1+menu->itemcnt)%menu->itemcnt;
667 break;
669 case MCMD_SELECT:
670 return menu->items[menu->selected].res;
672 default:
673 break;
676 /* clear menu area */
677 firstline = (LCD_HEIGHT/FONT_HEIGHT-(menu->itemcnt+3))/2;
679 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
680 rb->lcd_fillrect((LCD_WIDTH-MENU_WIDTH)/2, firstline*FONT_HEIGHT,
681 MENU_WIDTH, (menu->itemcnt+3)*FONT_HEIGHT);
682 rb->lcd_set_drawmode(DRMODE_SOLID);
684 if(menu->hasframe) {
685 rb->lcd_drawrect((LCD_WIDTH-MENU_WIDTH)/2-1, firstline*FONT_HEIGHT-1,
686 MENU_WIDTH+2, (menu->itemcnt+3)*FONT_HEIGHT+2);
687 rb->lcd_hline((LCD_WIDTH-MENU_WIDTH)/2-1,
688 (LCD_WIDTH-MENU_WIDTH)/2-1+MENU_WIDTH+2,
689 (firstline+1)*FONT_HEIGHT);
692 /* draw menu items */
693 rb->lcd_getstringsize(menu->title, &w, &h);
694 rb->lcd_putsxy((LCD_WIDTH-w)/2, firstline*FONT_HEIGHT, menu->title);
696 for(i=0; i<menu->itemcnt; i++) {
697 if(i == menu->selected) {
698 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
700 rb->lcd_putsxy((LCD_WIDTH-MENU_WIDTH)/2,
701 (firstline+i+1+extraline)*FONT_HEIGHT,
702 menu->items[i].text);
703 if(i == menu->selected) {
704 rb->lcd_set_drawmode(DRMODE_SOLID);
708 adj = (firstline == 0 ? 0 : 1);
709 rb->lcd_update_rect((LCD_WIDTH-MENU_WIDTH)/2-1, firstline*FONT_HEIGHT-adj,
710 MENU_WIDTH+2, (menu->itemcnt+3)*FONT_HEIGHT+2*adj);
711 return MRES_NONE;
714 /*****************************************************************************
715 * jewels_putjewels() makes the jewels fall to fill empty spots and adds
716 * new random jewels at the empty spots at the top of each row.
717 ******************************************************************************/
718 static void jewels_putjewels(struct game_context* bj){
719 int i, j, k;
720 bool mark, done;
721 long lasttick, currenttick;
723 /* loop to make all the jewels fall */
724 while(true) {
725 /* mark falling jewels and add new jewels to hidden top row*/
726 mark = false;
727 done = true;
728 for(j=0; j<BJ_WIDTH; j++) {
729 if(bj->playboard[1][j].type == 0) {
730 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
732 for(i=BJ_HEIGHT-2; i>=0; i--) {
733 if(!mark && bj->playboard[i+1][j].type == 0) {
734 mark = true;
735 done = false;
737 if(mark) bj->playboard[i][j].falling = true;
739 /*if(bj->playboard[1][j].falling) {
740 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
741 bj->playboard[0][j].falling = true;
743 mark = false;
746 /* break if there are no falling jewels */
747 if(done) break;
749 /* animate falling jewels */
750 lasttick = *rb->current_tick;
752 for(k=1; k<=8; k++) {
753 for(i=BJ_HEIGHT-2; i>=0; i--) {
754 for(j=0; j<BJ_WIDTH; j++) {
755 if(bj->playboard[i][j].falling &&
756 bj->playboard[i][j].type != 0) {
757 /* clear old position */
758 #ifdef HAVE_LCD_COLOR
759 if(i == 0 && YOFS) {
760 rb->lcd_set_foreground(rb->lcd_get_background());
761 } else {
762 rb->lcd_set_foreground(jewels_bkgd[(i-1+j)%2]);
764 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
765 TILE_WIDTH, TILE_HEIGHT);
766 if(bj->playboard[i+1][j].type == 0) {
767 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
768 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
769 TILE_WIDTH, TILE_HEIGHT);
771 #else
772 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
773 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
774 TILE_WIDTH, TILE_HEIGHT);
775 if(bj->playboard[i+1][j].type == 0) {
776 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
777 TILE_WIDTH, TILE_HEIGHT);
779 rb->lcd_set_drawmode(DRMODE_SOLID);
780 #endif
782 /* draw new position */
783 #ifdef HAVE_LCD_COLOR
784 rb->lcd_bitmap_transparent_part(jewels, 0,
785 TILE_HEIGHT*(bj->playboard[i][j].type),
786 TILE_WIDTH, j*TILE_WIDTH,
787 (i-1)*TILE_HEIGHT+YOFS+
788 ((((TILE_HEIGHT<<10)*k)/8)>>10),
789 TILE_WIDTH, TILE_HEIGHT);
790 #else
791 rb->lcd_bitmap_part(jewels, 0,
792 TILE_HEIGHT*(bj->playboard[i][j].type),
793 TILE_WIDTH, j*TILE_WIDTH,
794 (i-1)*TILE_HEIGHT+YOFS+
795 ((((TILE_HEIGHT<<10)*k)/8)>>10),
796 TILE_WIDTH, TILE_HEIGHT);
797 #endif
802 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
803 jewels_setcolors();
805 /* framerate limiting */
806 currenttick = *rb->current_tick;
807 if(currenttick-lasttick < HZ/MAX_FPS) {
808 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
809 } else {
810 rb->yield();
812 lasttick = currenttick;
815 /* shift jewels down */
816 for(j=0; j<BJ_WIDTH; j++) {
817 for(i=BJ_HEIGHT-1; i>=1; i--) {
818 if(bj->playboard[i-1][j].falling) {
819 bj->playboard[i][j].type = bj->playboard[i-1][j].type;
824 /* clear out top row */
825 for(j=0; j<BJ_WIDTH; j++) {
826 bj->playboard[0][j].type = 0;
829 /* mark everything not falling */
830 for(i=0; i<BJ_HEIGHT; i++) {
831 for(j=0; j<BJ_WIDTH; j++) {
832 bj->playboard[i][j].falling = false;
838 /*****************************************************************************
839 * jewels_clearjewels() finds all the connected rows and columns and
840 * calculates and returns the points earned.
841 ******************************************************************************/
842 static unsigned int jewels_clearjewels(struct game_context* bj) {
843 int i, j;
844 int last, run;
845 unsigned int points = 0;
847 /* check for connected rows */
848 for(i=1; i<BJ_HEIGHT; i++) {
849 last = 0;
850 run = 1;
851 for(j=0; j<BJ_WIDTH; j++) {
852 if(bj->playboard[i][j].type == last &&
853 bj->playboard[i][j].type != 0 &&
854 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
855 run++;
857 if(run == 3) {
858 bj->segments++;
859 points += bj->segments;
860 bj->playboard[i][j].delete = true;
861 bj->playboard[i][j-1].delete = true;
862 bj->playboard[i][j-2].delete = true;
863 } else if(run > 3) {
864 points++;
865 bj->playboard[i][j].delete = true;
867 } else {
868 run = 1;
869 last = bj->playboard[i][j].type;
874 /* check for connected columns */
875 for(j=0; j<BJ_WIDTH; j++) {
876 last = 0;
877 run = 1;
878 for(i=1; i<BJ_HEIGHT; i++) {
879 if(bj->playboard[i][j].type != 0 &&
880 bj->playboard[i][j].type == last &&
881 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
882 run++;
884 if(run == 3) {
885 bj->segments++;
886 points += bj->segments;
887 bj->playboard[i][j].delete = true;
888 bj->playboard[i-1][j].delete = true;
889 bj->playboard[i-2][j].delete = true;
890 } else if(run > 3) {
891 points++;
892 bj->playboard[i][j].delete = true;
894 } else {
895 run = 1;
896 last = bj->playboard[i][j].type;
901 /* clear deleted jewels */
902 for(i=1; i<BJ_HEIGHT; i++) {
903 for(j=0; j<BJ_WIDTH; j++) {
904 if(bj->playboard[i][j].delete) {
905 bj->playboard[i][j].delete = false;
906 bj->playboard[i][j].type = 0;
911 return points;
914 /*****************************************************************************
915 * jewels_runboard() runs the board until it settles in a fixed state and
916 * returns points earned.
917 ******************************************************************************/
918 static unsigned int jewels_runboard(struct game_context* bj) {
919 unsigned int points = 0;
920 unsigned int ret;
922 bj->segments = 0;
924 while((ret = jewels_clearjewels(bj)) > 0) {
925 points += ret;
926 jewels_drawboard(bj);
927 jewels_putjewels(bj);
930 return points;
933 /*****************************************************************************
934 * jewels_swapjewels() swaps two jewels as long as it results in points and
935 * returns points earned.
936 ******************************************************************************/
937 static unsigned int jewels_swapjewels(struct game_context* bj,
938 int x, int y, int direc) {
939 int k;
940 int horzmod, vertmod;
941 int movelen = 0;
942 bool undo = false;
943 unsigned int points = 0;
944 long lasttick, currenttick;
946 /* check for invalid parameters */
947 if(x < 0 || x >= BJ_WIDTH || y < 0 || y >= BJ_HEIGHT-1 ||
948 direc < SWAP_UP || direc > SWAP_LEFT) {
949 return 0;
952 /* check for invalid directions */
953 if((x == 0 && direc == SWAP_LEFT) ||
954 (x == BJ_WIDTH-1 && direc == SWAP_RIGHT) ||
955 (y == 0 && direc == SWAP_UP) ||
956 (y == BJ_HEIGHT-2 && direc == SWAP_DOWN)) {
957 return 0;
960 /* set direction variables */
961 horzmod = 0;
962 vertmod = 0;
963 switch(direc) {
964 case SWAP_UP:
965 vertmod = -1;
966 movelen = TILE_HEIGHT;
967 break;
968 case SWAP_RIGHT:
969 horzmod = 1;
970 movelen = TILE_WIDTH;
971 break;
972 case SWAP_DOWN:
973 vertmod = 1;
974 movelen = TILE_HEIGHT;
975 break;
976 case SWAP_LEFT:
977 horzmod = -1;
978 movelen = TILE_WIDTH;
979 break;
982 while(true) {
983 lasttick = *rb->current_tick;
985 /* animate swapping jewels */
986 for(k=0; k<=8; k++) {
987 /* clear old position */
988 #ifdef HAVE_LCD_COLOR
989 rb->lcd_set_foreground(jewels_bkgd[(x+y)%2]);
990 rb->lcd_fillrect(x*TILE_WIDTH,
991 y*TILE_HEIGHT+YOFS,
992 TILE_WIDTH, TILE_HEIGHT);
993 rb->lcd_set_foreground(jewels_bkgd[(x+horzmod+y+vertmod)%2]);
994 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
995 (y+vertmod)*TILE_HEIGHT+YOFS,
996 TILE_WIDTH, TILE_HEIGHT);
997 #else
998 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
999 rb->lcd_fillrect(x*TILE_WIDTH,
1000 y*TILE_HEIGHT+YOFS,
1001 TILE_WIDTH, TILE_HEIGHT);
1002 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
1003 (y+vertmod)*TILE_HEIGHT+YOFS,
1004 TILE_WIDTH, TILE_HEIGHT);
1005 rb->lcd_set_drawmode(DRMODE_SOLID);
1006 #endif
1007 /* draw new position */
1008 #ifdef HAVE_LCD_COLOR
1009 rb->lcd_bitmap_transparent_part(jewels,
1010 0, TILE_HEIGHT*(bj->playboard
1011 [y+1+vertmod][x+horzmod].type), TILE_WIDTH,
1012 (x+horzmod)*TILE_WIDTH-horzmod*
1013 ((((movelen<<10)*k)/8)>>10),
1014 (y+vertmod)*TILE_HEIGHT-vertmod*
1015 ((((movelen<<10)*k)/8)>>10)+YOFS,
1016 TILE_WIDTH, TILE_HEIGHT);
1017 rb->lcd_bitmap_transparent_part(jewels,
1018 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
1019 TILE_WIDTH, x*TILE_WIDTH+horzmod*
1020 ((((movelen<<10)*k)/8)>>10),
1021 y*TILE_HEIGHT+vertmod*
1022 ((((movelen<<10)*k)/8)>>10)+YOFS,
1023 TILE_WIDTH, TILE_HEIGHT);
1024 #else
1025 rb->lcd_bitmap_part(jewels,
1026 0, TILE_HEIGHT*(bj->playboard
1027 [y+1+vertmod][x+horzmod].type), TILE_WIDTH,
1028 (x+horzmod)*TILE_WIDTH-horzmod*
1029 ((((movelen<<10)*k)/8)>>10),
1030 (y+vertmod)*TILE_HEIGHT-vertmod*
1031 ((((movelen<<10)*k)/8)>>10)+YOFS,
1032 TILE_WIDTH, TILE_HEIGHT);
1033 rb->lcd_set_drawmode(DRMODE_FG);
1034 rb->lcd_bitmap_part(jewels,
1035 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
1036 TILE_WIDTH, x*TILE_WIDTH+horzmod*
1037 ((((movelen<<10)*k)/8)>>10),
1038 y*TILE_HEIGHT+vertmod*
1039 ((((movelen<<10)*k)/8)>>10)+YOFS,
1040 TILE_WIDTH, TILE_HEIGHT);
1041 rb->lcd_set_drawmode(DRMODE_SOLID);
1042 #endif
1044 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
1045 jewels_setcolors();
1047 /* framerate limiting */
1048 currenttick = *rb->current_tick;
1049 if(currenttick-lasttick < HZ/MAX_FPS) {
1050 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
1051 } else {
1052 rb->yield();
1054 lasttick = currenttick;
1057 /* swap jewels */
1058 int temp = bj->playboard[y+1][x].type;
1059 bj->playboard[y+1][x].type =
1060 bj->playboard[y+1+vertmod][x+horzmod].type;
1061 bj->playboard[y+1+vertmod][x+horzmod].type = temp;
1063 if(undo) break;
1065 points = jewels_runboard(bj);
1066 if(points == 0) {
1067 undo = true;
1068 } else {
1069 break;
1073 return points;
1076 /*****************************************************************************
1077 * jewels_movesavail() uses pattern matching to see if there are any
1078 * available move left.
1079 ******************************************************************************/
1080 static bool jewels_movesavail(struct game_context* bj) {
1081 int i, j;
1082 bool moves = false;
1083 int mytype;
1085 for(i=1; i<BJ_HEIGHT; i++) {
1086 for(j=0; j<BJ_WIDTH; j++) {
1087 mytype = bj->playboard[i][j].type;
1088 if(mytype == 0 || mytype > MAX_NUM_JEWELS) continue;
1090 /* check horizontal patterns */
1091 if(j <= BJ_WIDTH-3) {
1092 if(i > 1) {
1093 if(bj->playboard[i-1][j+1].type == mytype) {
1094 if(bj->playboard[i-1][j+2].type == mytype)
1095 {moves = true; break;}
1096 if(bj->playboard[i][j+2].type == mytype)
1097 {moves = true; break;}
1099 if(bj->playboard[i][j+1].type == mytype) {
1100 if(bj->playboard[i-1][j+2].type == mytype)
1101 {moves = true; break;}
1105 if(j <= BJ_WIDTH-4) {
1106 if(bj->playboard[i][j+3].type == mytype) {
1107 if(bj->playboard[i][j+1].type == mytype)
1108 {moves = true; break;}
1109 if(bj->playboard[i][j+2].type == mytype)
1110 {moves = true; break;}
1114 if(i < BJ_HEIGHT-1) {
1115 if(bj->playboard[i][j+1].type == mytype) {
1116 if(bj->playboard[i+1][j+2].type == mytype)
1117 {moves = true; break;}
1119 if(bj->playboard[i+1][j+1].type == mytype) {
1120 if(bj->playboard[i][j+2].type == mytype)
1121 {moves = true; break;}
1122 if(bj->playboard[i+1][j+2].type == mytype)
1123 {moves = true; break;}
1128 /* check vertical patterns */
1129 if(i <= BJ_HEIGHT-3) {
1130 if(j > 0) {
1131 if(bj->playboard[i+1][j-1].type == mytype) {
1132 if(bj->playboard[i+2][j-1].type == mytype)
1133 {moves = true; break;}
1134 if(bj->playboard[i+2][j].type == mytype)
1135 {moves = true; break;}
1137 if(bj->playboard[i+1][j].type == mytype) {
1138 if(bj->playboard[i+2][j-1].type == mytype)
1139 {moves = true; break;}
1143 if(i <= BJ_HEIGHT-4) {
1144 if(bj->playboard[i+3][j].type == mytype) {
1145 if(bj->playboard[i+1][j].type == mytype)
1146 {moves = true; break;}
1147 if(bj->playboard[i+2][j].type == mytype)
1148 {moves = true; break;}
1152 if(j < BJ_WIDTH-1) {
1153 if(bj->playboard[i+1][j].type == mytype) {
1154 if(bj->playboard[i+2][j+1].type == mytype)
1155 {moves = true; break;}
1157 if(bj->playboard[i+1][j+1].type == mytype) {
1158 if(bj->playboard[i+2][j].type == mytype)
1159 {moves = true; break;}
1160 if (bj->playboard[i+2][j+1].type == mytype)
1161 {moves = true; break;}
1167 if(moves) break;
1170 return moves;
1173 /*****************************************************************************
1174 * jewels_puzzle_is_finished(bj) checks if the puzzle is finished.
1175 ******************************************************************************/
1176 static int jewels_puzzle_is_finished(struct game_context* bj) {
1177 unsigned int i, j;
1178 for(i=0; i<BJ_HEIGHT; i++) {
1179 for(j=0; j<BJ_WIDTH; j++) {
1180 int mytype = bj->playboard[i][j].type;
1181 if(mytype>MAX_NUM_JEWELS) {
1182 mytype -= MAX_NUM_JEWELS;
1183 if(mytype&PUZZLE_TILE_UP) {
1184 if(i==0 || bj->playboard[i-1][j].type<=MAX_NUM_JEWELS ||
1185 !((bj->playboard[i-1][j].type-MAX_NUM_JEWELS)
1186 &PUZZLE_TILE_DOWN))
1187 return 0;
1189 if(mytype&PUZZLE_TILE_DOWN) {
1190 if(i==BJ_HEIGHT-1 ||
1191 bj->playboard[i+1][j].type<=MAX_NUM_JEWELS ||
1192 !((bj->playboard[i+1][j].type-MAX_NUM_JEWELS)
1193 &PUZZLE_TILE_UP))
1194 return 0;
1196 if(mytype&PUZZLE_TILE_LEFT) {
1197 if(j==0 || bj->playboard[i][j-1].type<=MAX_NUM_JEWELS ||
1198 !((bj->playboard[i][j-1].type-MAX_NUM_JEWELS)
1199 &PUZZLE_TILE_RIGHT))
1200 return 0;
1202 if(mytype&PUZZLE_TILE_RIGHT) {
1203 if(j==BJ_WIDTH-1 ||
1204 bj->playboard[i][j+1].type<=MAX_NUM_JEWELS ||
1205 !((bj->playboard[i][j+1].type-MAX_NUM_JEWELS)
1206 &PUZZLE_TILE_LEFT))
1207 return 0;
1212 return 1;
1215 /*****************************************************************************
1216 * jewels_initlevel() initialises a level.
1217 ******************************************************************************/
1218 static unsigned int jewels_initlevel(struct game_context* bj) {
1219 unsigned int points = 0;
1221 switch(bj->type) {
1222 case GAME_TYPE_NORMAL:
1223 bj->num_jewels = MAX_NUM_JEWELS;
1224 break;
1226 case GAME_TYPE_PUZZLE:
1228 unsigned int i, j;
1229 struct puzzle_tile *tile;
1231 bj->num_jewels = puzzle_levels[bj->level-1].num_jewels;
1233 for(i=0; i<BJ_HEIGHT; i++) {
1234 for(j=0; j<BJ_WIDTH; j++) {
1235 bj->playboard[i][j].type = (rb->rand()%bj->num_jewels)+1;
1236 bj->playboard[i][j].falling = false;
1237 bj->playboard[i][j].delete = false;
1240 jewels_runboard(bj);
1241 tile = puzzle_levels[bj->level-1].tiles;
1242 for(i=0; i<puzzle_levels[bj->level-1].num_tiles; i++, tile++) {
1243 bj->playboard[tile->y+1][tile->x].type = MAX_NUM_JEWELS
1244 +tile->tile_type;
1247 break;
1250 jewels_drawboard(bj);
1252 /* run the play board */
1253 jewels_putjewels(bj);
1254 points += jewels_runboard(bj);
1255 return points;
1258 /*****************************************************************************
1259 * jewels_nextlevel() advances the game to the next level and returns
1260 * points earned.
1261 ******************************************************************************/
1262 static void jewels_nextlevel(struct game_context* bj) {
1263 int i, x, y;
1264 unsigned int points = 0;
1266 switch(bj->type) {
1267 case GAME_TYPE_NORMAL:
1268 /* roll over score, change and display level */
1269 while(bj->score >= LEVEL_PTS) {
1270 bj->score -= LEVEL_PTS;
1271 bj->level++;
1272 rb->splashf(HZ*2, "Level %d", bj->level);
1273 jewels_drawboard(bj);
1276 /* randomly clear some jewels */
1277 for(i=0; i<16; i++) {
1278 x = rb->rand()%8;
1279 y = rb->rand()%8;
1281 if(bj->playboard[y][x].type != 0) {
1282 points++;
1283 bj->playboard[y][x].type = 0;
1286 break;
1288 case GAME_TYPE_PUZZLE:
1289 bj->level++;
1290 if(bj->level>NUM_PUZZLE_LEVELS) {
1291 rb->splash(HZ*2, "You win!");
1292 bj->level = 1;
1293 } else {
1294 rb->splashf(HZ*2, "Level %d", bj->level);
1296 break;
1299 points += jewels_initlevel(bj);
1300 bj->score += points;
1303 /*****************************************************************************
1304 * jewels_recordscore() inserts a high score into the high scores list and
1305 * returns the high score position.
1306 ******************************************************************************/
1307 static int jewels_recordscore(struct game_context* bj) {
1308 int i;
1309 int position = 0;
1310 unsigned int current, temp;
1312 /* calculate total score */
1313 current = (bj->level-1)*LEVEL_PTS+bj->score;
1314 if(current <= 0) return 0;
1316 /* insert the current score into the high scores */
1317 for(i=0; i<NUM_SCORES; i++) {
1318 if(current >= bj->highscores[i]) {
1319 if(!position) {
1320 position = i+1;
1321 bj->dirty = true;
1323 temp = bj->highscores[i];
1324 bj->highscores[i] = current;
1325 current = temp;
1329 return position;
1332 /*****************************************************************************
1333 * jewels_loadscores() loads the high scores saved file.
1334 ******************************************************************************/
1335 static void jewels_loadscores(struct game_context* bj) {
1336 int fd;
1338 bj->dirty = false;
1340 /* clear high scores */
1341 rb->memset(bj->highscores, 0, sizeof(bj->highscores));
1343 /* open scores file */
1344 fd = rb->open(SCORE_FILE, O_RDONLY);
1345 if(fd < 0) return;
1347 /* read in high scores */
1348 if(rb->read(fd, bj->highscores, sizeof(bj->highscores)) <= 0) {
1349 /* scores are bad, reset */
1350 rb->memset(bj->highscores, 0, sizeof(bj->highscores));
1353 rb->close(fd);
1356 /*****************************************************************************
1357 * jewels_savescores() saves the high scores saved file.
1358 ******************************************************************************/
1359 static void jewels_savescores(struct game_context* bj) {
1360 int fd;
1362 /* write out the high scores to the save file */
1363 fd = rb->open(SCORE_FILE, O_WRONLY|O_CREAT);
1364 rb->write(fd, bj->highscores, sizeof(bj->highscores));
1365 rb->close(fd);
1366 bj->dirty = false;
1369 /*****************************************************************************
1370 * jewels_loadgame() loads the saved game and returns load success.
1371 ******************************************************************************/
1372 static bool jewels_loadgame(struct game_context* bj) {
1373 int fd;
1374 bool loaded = false;
1376 /* open game file */
1377 fd = rb->open(SAVE_FILE, O_RDONLY);
1378 if(fd < 0) return loaded;
1380 /* read in saved game */
1381 while(true) {
1382 if(rb->read(fd, &bj->score, sizeof(bj->score)) <= 0) break;
1383 if(rb->read(fd, &bj->level, sizeof(bj->level)) <= 0) break;
1384 if(rb->read(fd, &bj->type, sizeof(bj->type)) <= 0) break;
1385 if(rb->read(fd, bj->playboard, sizeof(bj->playboard)) <= 0) break;
1386 bj->resume = true;
1387 loaded = true;
1388 break;
1391 rb->close(fd);
1393 /* delete saved file */
1394 rb->remove(SAVE_FILE);
1395 return loaded;
1398 /*****************************************************************************
1399 * jewels_savegame() saves the current game state.
1400 ******************************************************************************/
1401 static void jewels_savegame(struct game_context* bj) {
1402 int fd;
1404 /* write out the game state to the save file */
1405 fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT);
1406 rb->write(fd, &bj->score, sizeof(bj->score));
1407 rb->write(fd, &bj->level, sizeof(bj->level));
1408 rb->write(fd, &bj->type, sizeof(bj->type));
1409 rb->write(fd, bj->playboard, sizeof(bj->playboard));
1410 rb->close(fd);
1412 bj->resume = true;
1415 /*****************************************************************************
1416 * jewels_callback() is the default event handler callback which is called
1417 * on usb connect and shutdown.
1418 ******************************************************************************/
1419 static void jewels_callback(void* param) {
1420 struct game_context* bj = (struct game_context*) param;
1421 if(bj->dirty) {
1422 rb->splash(HZ, "Saving high scores...");
1423 jewels_savescores(bj);
1427 /*****************************************************************************
1428 * jewels_main() is the main game subroutine, it returns the final game status.
1429 ******************************************************************************/
1430 static int jewels_main(struct game_context* bj) {
1431 int i, j;
1432 int w, h;
1433 int button;
1434 char str[18];
1435 bool startgame = false;
1436 bool inmenu = false;
1437 bool selected = false;
1438 enum menu_cmd cmd = MCMD_NONE;
1439 enum menu_result res;
1441 /* the cursor coordinates */
1442 int x=0, y=0;
1444 /* don't resume by default */
1445 bj->resume = false;
1447 /********************
1448 * menu *
1449 ********************/
1450 rb->lcd_clear_display();
1452 while(!startgame) {
1453 res = jewels_showmenu(&bjmenu[0], cmd);
1454 cmd = MCMD_NONE;
1456 rb->snprintf(str, 18, "High Score: %d", bj->highscores[0]);
1457 rb->lcd_getstringsize(str, &w, &h);
1458 rb->lcd_putsxy((LCD_WIDTH-w)/2, LCD_HEIGHT-8, str);
1459 rb->lcd_update();
1461 switch(res) {
1462 case MRES_NEW:
1463 startgame = true;
1464 bj->type = GAME_TYPE_NORMAL;
1465 continue;
1467 case MRES_PUZZLE:
1468 startgame = true;
1469 bj->type = GAME_TYPE_PUZZLE;
1470 continue;
1472 case MRES_RESUME:
1473 if(!jewels_loadgame(bj)) {
1474 rb->splash(HZ*2, "Nothing to resume");
1475 rb->lcd_clear_display();
1476 } else {
1477 startgame = true;
1479 continue;
1481 case MRES_SCORES:
1482 rb->lcd_clear_display();
1484 /* room for a title? */
1485 j = 0;
1486 if(LCD_HEIGHT-NUM_SCORES*8 >= 8) {
1487 rb->snprintf(str, 12, "%s", "High Scores");
1488 rb->lcd_getstringsize(str, &w, &h);
1489 rb->lcd_putsxy((LCD_WIDTH-w)/2, 0, str);
1490 j = 2;
1493 /* print high scores */
1494 for(i=0; i<NUM_SCORES; i++) {
1495 rb->snprintf(str, 11, "#%02d: %d", i+1, bj->highscores[i]);
1496 rb->lcd_puts(0, i+j, str);
1499 rb->lcd_update();
1500 while(true) {
1501 button = rb->button_get(true);
1502 if(button != BUTTON_NONE && !(button&BUTTON_REL)) break;
1504 rb->lcd_clear_display();
1505 continue;
1507 case MRES_HELP:
1508 /* welcome screen to display key bindings */
1509 rb->lcd_clear_display();
1510 rb->snprintf(str, 5, "%s", "Help");
1511 rb->lcd_getstringsize(str, &w, &h);
1512 rb->lcd_putsxy((LCD_WIDTH-w)/2, 0, str);
1513 #if CONFIG_KEYPAD == RECORDER_PAD
1514 rb->lcd_puts(0, 2, "Controls:");
1515 rb->lcd_puts(0, 3, "Directions = move");
1516 rb->lcd_puts(0, 4, "PLAY = select");
1517 rb->lcd_puts(0, 5, "Long PLAY = menu");
1518 rb->lcd_puts(0, 6, "OFF = cancel");
1519 #elif CONFIG_KEYPAD == ONDIO_PAD
1520 rb->lcd_puts(0, 2, "Controls:");
1521 rb->lcd_puts(0, 3, "Directions = move");
1522 rb->lcd_puts(0, 4, "MENU = select");
1523 rb->lcd_puts(0, 5, "Long MENU = menu");
1524 rb->lcd_puts(0, 6, "OFF = cancel");
1525 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
1526 rb->lcd_puts(0, 2, "Controls:");
1527 rb->lcd_puts(0, 3, "Directions = move");
1528 rb->lcd_puts(0, 4, "SELECT = select");
1529 rb->lcd_puts(0, 5, "Long SELECT = menu");
1530 rb->lcd_puts(0, 6, "PLAY = cancel");
1531 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
1532 rb->lcd_puts(0, 2, "Swap pairs of jewels to");
1533 rb->lcd_puts(0, 3, "form connected segments");
1534 rb->lcd_puts(0, 4, "of three or more of the");
1535 rb->lcd_puts(0, 5, "same type.");
1536 rb->lcd_puts(0, 7, "Controls:");
1537 rb->lcd_puts(0, 8, "Directions to move");
1538 rb->lcd_puts(0, 9, "SELECT to select");
1539 rb->lcd_puts(0, 10, "Long SELECT to show menu");
1540 rb->lcd_puts(0, 11, "OFF to cancel");
1541 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
1542 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
1543 rb->lcd_puts(0, 2, "Swap pairs of jewels to");
1544 rb->lcd_puts(0, 3, "form connected segments");
1545 rb->lcd_puts(0, 4, "of three or more of the");
1546 rb->lcd_puts(0, 5, "same type.");
1547 rb->lcd_puts(0, 7, "Controls:");
1548 rb->lcd_puts(0, 8, "Directions or scroll to move");
1549 rb->lcd_puts(0, 9, "SELECT to select");
1550 rb->lcd_puts(0, 10, "Long SELECT to show menu");
1551 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
1552 rb->lcd_puts(0, 2, "Swap pairs of jewels to");
1553 rb->lcd_puts(0, 3, "form connected segments");
1554 rb->lcd_puts(0, 4, "of three or more of the");
1555 rb->lcd_puts(0, 5, "same type.");
1556 rb->lcd_puts(0, 7, "Controls:");
1557 rb->lcd_puts(0, 8, "Directions to move");
1558 rb->lcd_puts(0, 9, "SELECT to select");
1559 rb->lcd_puts(0, 10, "Long SELECT to show menu");
1560 rb->lcd_puts(0, 11, "PLAY to cancel");
1561 #elif CONFIG_KEYPAD == GIGABEAT_PAD \
1562 || CONFIG_KEYPAD == MROBE100_PAD
1563 rb->lcd_puts(0, 2, "Swap pairs of jewels to");
1564 rb->lcd_puts(0, 3, "form connected segments");
1565 rb->lcd_puts(0, 4, "of three or more of the");
1566 rb->lcd_puts(0, 5, "same type.");
1567 rb->lcd_puts(0, 7, "Controls:");
1568 rb->lcd_puts(0, 8, "Directions to move");
1569 rb->lcd_puts(0, 9, "SELECT to select");
1570 rb->lcd_puts(0, 10, "Long SELECT to show menu");
1571 rb->lcd_puts(0, 11, "POWER to cancel");
1572 #elif CONFIG_KEYPAD == SANSA_E200_PAD \
1573 || CONFIG_KEYPAD == SANSA_C200_PAD
1574 rb->lcd_puts(0, 2, "Swap pairs of jewels to");
1575 rb->lcd_puts(0, 3, "form connected segments");
1576 rb->lcd_puts(0, 4, "of three or more of the");
1577 rb->lcd_puts(0, 5, "same type.");
1578 rb->lcd_puts(0, 7, "Controls:");
1579 rb->lcd_puts(0, 8, "Directions to move");
1580 rb->lcd_puts(0, 9, "SELECT to select");
1581 rb->lcd_puts(0, 10, "Long SELECT to show menu");
1582 rb->lcd_puts(0, 11, "POWER to cancel");
1583 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
1584 rb->lcd_puts(0, 2, "Swap pairs of jewels");
1585 rb->lcd_puts(0, 3, "to form connected");
1586 rb->lcd_puts(0, 4, "segments of three or ");
1587 rb->lcd_puts(0, 5, "more of the");
1588 rb->lcd_puts(0, 6, "same type.");
1589 rb->lcd_puts(0, 8, "Controls:");
1590 rb->lcd_puts(0, 9, "Directions or scroll to move");
1591 rb->lcd_puts(0, 10, "PLAY to select");
1592 rb->lcd_puts(0, 11, "Long PLAY for menu");
1593 rb->lcd_puts(0, 12, "POWER to cancel");
1594 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
1595 rb->lcd_puts(0, 2, "Swap pairs of jewels");
1596 rb->lcd_puts(0, 3, "to form connected");
1597 rb->lcd_puts(0, 4, "segments of three or ");
1598 rb->lcd_puts(0, 5, "more of the");
1599 rb->lcd_puts(0, 6, "same type.");
1600 rb->lcd_puts(0, 8, "Controls:");
1601 rb->lcd_puts(0, 9, "Directions or scroll to move");
1602 rb->lcd_puts(0, 10, "PLAY to select");
1603 rb->lcd_puts(0, 11, "Long PLAY for menu");
1604 rb->lcd_puts(0, 12, "REC to cancel");
1605 #elif CONFIG_KEYPAD == COWOND2_PAD
1606 rb->lcd_puts(0, 11, "POWER to cancel");
1607 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
1608 rb->lcd_puts(0, 2, "Swap pairs of jewels to");
1609 rb->lcd_puts(0, 3, "form connected segments");
1610 rb->lcd_puts(0, 4, "of three or more of the");
1611 rb->lcd_puts(0, 5, "same type.");
1612 rb->lcd_puts(0, 7, "Controls:");
1613 rb->lcd_puts(0, 8, "Directions to move");
1614 rb->lcd_puts(0, 9, "SELECT to select");
1615 rb->lcd_puts(0, 10, "Long SELECT to show menu");
1616 rb->lcd_puts(0, 11, "BACK to cancel");
1617 #else
1618 #warning: missing help text.
1619 #endif
1621 #ifdef HAVE_TOUCHSCREEN
1622 rb->lcd_puts(0, 2, "Swap pairs of jewels to");
1623 rb->lcd_puts(0, 3, "form connected segments");
1624 rb->lcd_puts(0, 4, "of three or more of the");
1625 rb->lcd_puts(0, 5, "same type.");
1626 rb->lcd_puts(0, 7, "Controls:");
1627 rb->lcd_puts(0, 8, "Directions to move");
1628 rb->lcd_puts(0, 9, "CENTER to select");
1629 rb->lcd_puts(0, 10, "Long CENTER to show menu");
1630 #endif
1631 rb->lcd_update();
1632 while(true) {
1633 button = rb->button_get(true);
1634 if(button != BUTTON_NONE && !(button&BUTTON_REL)) break;
1636 rb->lcd_clear_display();
1637 continue;
1639 case MRES_QUIT:
1640 return BJ_QUIT;
1642 default:
1643 break;
1646 /* handle menu button presses */
1647 button = rb->button_get(true);
1648 switch(button){
1649 #ifdef JEWELS_SCROLLWHEEL
1650 case JEWELS_PREV:
1651 case (JEWELS_PREV|BUTTON_REPEAT):
1652 #endif
1653 case JEWELS_UP:
1654 case (JEWELS_UP|BUTTON_REPEAT):
1655 cmd = MCMD_PREV;
1656 break;
1658 #ifdef JEWELS_SCROLLWHEEL
1659 case JEWELS_NEXT:
1660 case (JEWELS_NEXT|BUTTON_REPEAT):
1661 #endif
1662 case JEWELS_DOWN:
1663 case (JEWELS_DOWN|BUTTON_REPEAT):
1664 cmd = MCMD_NEXT;
1665 break;
1667 case JEWELS_SELECT:
1668 case JEWELS_RIGHT:
1669 cmd = MCMD_SELECT;
1670 break;
1672 #ifdef JEWELS_CANCEL
1673 #ifdef JEWELS_RC_CANCEL
1674 case JEWELS_RC_CANCEL:
1675 #endif
1676 case JEWELS_CANCEL:
1677 return BJ_QUIT;
1678 #endif
1680 default:
1681 if(rb->default_event_handler_ex(button, jewels_callback,
1682 (void*) bj) == SYS_USB_CONNECTED)
1683 return BJ_USB;
1684 break;
1688 /********************
1689 * init *
1690 ********************/
1691 jewels_init(bj);
1693 /********************
1694 * setup the board *
1695 ********************/
1696 bj->score += jewels_initlevel(bj);
1697 if (!jewels_movesavail(bj)) {
1698 switch(bj->type) {
1699 case GAME_TYPE_NORMAL:
1700 return BJ_LOSE;
1702 case GAME_TYPE_PUZZLE:
1703 do {
1704 rb->splash(2*HZ, "No more moves!");
1705 bj->score += jewels_initlevel(bj);
1706 } while(!jewels_movesavail(bj));
1707 break;
1711 /**********************
1712 * play *
1713 **********************/
1714 while(true) {
1715 int no_movesavail = false;
1717 if(!inmenu) {
1718 /* refresh the board */
1719 jewels_drawboard(bj);
1721 /* display the cursor */
1722 if(selected) {
1723 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1724 rb->lcd_fillrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1725 TILE_WIDTH, TILE_HEIGHT);
1726 rb->lcd_set_drawmode(DRMODE_SOLID);
1727 } else {
1728 rb->lcd_drawrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1729 TILE_WIDTH, TILE_HEIGHT);
1731 rb->lcd_update_rect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1732 TILE_WIDTH, TILE_HEIGHT);
1733 } else {
1734 res = jewels_showmenu(&bjmenu[1], cmd);
1735 cmd = MCMD_NONE;
1736 switch(res) {
1737 case MRES_RESUME:
1738 inmenu = false;
1739 selected = false;
1740 continue;
1742 case MRES_PLAYBACK:
1743 playback_control(rb, NULL);
1744 rb->lcd_setfont(FONT_SYSFIXED);
1745 inmenu = false;
1746 selected = false;
1747 break;
1749 case MRES_SAVE:
1750 rb->splash(HZ, "Saving game...");
1751 jewels_savegame(bj);
1752 return BJ_END;
1754 case MRES_QUIT:
1755 return BJ_END;
1757 case MRES_EXIT:
1758 return BJ_QUIT_FROM_GAME;
1760 default:
1761 break;
1765 /* handle game button presses */
1766 button = rb->button_get(true);
1767 switch(button){
1768 case JEWELS_LEFT: /* move cursor left */
1769 case (JEWELS_LEFT|BUTTON_REPEAT):
1770 if(!inmenu) {
1771 if(selected) {
1772 bj->score += jewels_swapjewels(bj, x, y, SWAP_LEFT);
1773 selected = false;
1774 if (!jewels_movesavail(bj)) no_movesavail = true;
1775 } else {
1776 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1779 break;
1781 case JEWELS_RIGHT: /* move cursor right */
1782 case (JEWELS_RIGHT|BUTTON_REPEAT):
1783 if(!inmenu) {
1784 if(selected) {
1785 bj->score += jewels_swapjewels(bj, x, y, SWAP_RIGHT);
1786 selected = false;
1787 if (!jewels_movesavail(bj)) no_movesavail = true;
1788 } else {
1789 x = (x+1)%BJ_WIDTH;
1791 } else {
1792 cmd = MCMD_SELECT;
1794 break;
1796 case JEWELS_DOWN: /* move cursor down */
1797 case (JEWELS_DOWN|BUTTON_REPEAT):
1798 if(!inmenu) {
1799 if(selected) {
1800 bj->score += jewels_swapjewels(bj, x, y, SWAP_DOWN);
1801 selected = false;
1802 if (!jewels_movesavail(bj)) no_movesavail = true;
1803 } else {
1804 y = (y+1)%(BJ_HEIGHT-1);
1806 } else {
1807 cmd = MCMD_NEXT;
1809 break;
1811 case JEWELS_UP: /* move cursor up */
1812 case (JEWELS_UP|BUTTON_REPEAT):
1813 if(!inmenu) {
1814 if(selected) {
1815 bj->score += jewels_swapjewels(bj, x, y, SWAP_UP);
1816 selected = false;
1817 if (!jewels_movesavail(bj)) no_movesavail = true;
1818 } else {
1819 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1821 } else {
1822 cmd = MCMD_PREV;
1824 break;
1826 #ifdef JEWELS_SCROLLWHEEL
1827 case JEWELS_PREV: /* scroll backwards */
1828 case (JEWELS_PREV|BUTTON_REPEAT):
1829 if(!inmenu) {
1830 if(!selected) {
1831 if(x == 0) {
1832 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1834 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1836 } else {
1837 cmd = MCMD_PREV;
1839 break;
1841 case JEWELS_NEXT: /* scroll forwards */
1842 case (JEWELS_NEXT|BUTTON_REPEAT):
1843 if(!inmenu) {
1844 if(!selected) {
1845 if(x == BJ_WIDTH-1) {
1846 y = (y+1)%(BJ_HEIGHT-1);
1848 x = (x+1)%BJ_WIDTH;
1850 } else {
1851 cmd = MCMD_NEXT;
1853 break;
1854 #endif
1856 case JEWELS_SELECT: /* toggle selected */
1857 if(!inmenu) {
1858 selected = !selected;
1859 } else {
1860 cmd = MCMD_SELECT;
1862 break;
1864 case (JEWELS_SELECT|BUTTON_REPEAT): /* show menu */
1865 if(!inmenu) inmenu = true;
1866 break;
1868 #ifdef JEWELS_CANCEL
1869 #ifdef JEWELS_RC_CANCEL
1870 case JEWELS_RC_CANCEL:
1871 #endif
1872 case JEWELS_CANCEL: /* end game */
1873 return BJ_END;
1874 break;
1875 #endif
1877 default:
1878 if(rb->default_event_handler_ex(button, jewels_callback,
1879 (void*) bj) == SYS_USB_CONNECTED)
1880 return BJ_USB;
1881 break;
1884 if (no_movesavail) {
1885 switch(bj->type) {
1886 case GAME_TYPE_NORMAL:
1887 return BJ_LOSE;
1889 case GAME_TYPE_PUZZLE:
1890 do {
1891 rb->splash(2*HZ, "No more moves!");
1892 bj->score += jewels_initlevel(bj);
1893 } while(!jewels_movesavail(bj));
1894 break;
1898 switch(bj->type) {
1899 case GAME_TYPE_NORMAL:
1900 if(bj->score >= LEVEL_PTS)
1901 jewels_nextlevel(bj);
1902 break;
1904 case GAME_TYPE_PUZZLE:
1905 if(jewels_puzzle_is_finished(bj))
1906 jewels_nextlevel(bj);
1907 break;
1912 /*****************************************************************************
1913 * plugin entry point.
1914 ******************************************************************************/
1915 enum plugin_status plugin_start(const struct plugin_api* api, const void* parameter) {
1916 struct game_context bj;
1917 bool exit = false;
1918 int position;
1919 char str[19];
1921 /* plugin init */
1922 (void)parameter;
1923 rb = api;
1924 /* end of plugin init */
1926 /* load high scores */
1927 jewels_loadscores(&bj);
1929 rb->lcd_setfont(FONT_SYSFIXED);
1930 #if LCD_DEPTH > 1
1931 rb->lcd_set_backdrop(NULL);
1932 #endif
1933 jewels_setcolors();
1935 while(!exit) {
1936 switch(jewels_main(&bj)){
1937 case BJ_LOSE:
1938 rb->splash(HZ*2, "No more moves!");
1939 /* fall through to BJ_END */
1941 case BJ_END:
1942 if(!bj.resume) {
1943 if((position = jewels_recordscore(&bj))) {
1944 rb->snprintf(str, 19, "New high score #%d!", position);
1945 rb->splash(HZ*2, str);
1948 break;
1950 case BJ_USB:
1951 rb->lcd_setfont(FONT_UI);
1952 return PLUGIN_USB_CONNECTED;
1954 case BJ_QUIT:
1955 if(bj.dirty) {
1956 rb->splash(HZ, "Saving high scores...");
1957 jewels_savescores(&bj);
1959 exit = true;
1960 break;
1962 case BJ_QUIT_FROM_GAME:
1963 if(!bj.resume) {
1964 if((position = jewels_recordscore(&bj))) {
1965 rb->snprintf(str, 19, "New high score #%d!", position);
1966 rb->splash(HZ*2, str);
1969 if(bj.dirty) {
1970 rb->splash(HZ, "Saving high scores...");
1971 jewels_savescores(&bj);
1973 exit = true;
1974 break;
1976 default:
1977 break;
1981 rb->lcd_setfont(FONT_UI);
1982 return PLUGIN_OK;
1985 #endif /* HAVE_LCD_BITMAP */