Add 2008 to the copyright notice.
[Rockbox.git] / apps / plugins / jewels.c
blob5285cf912a206e8e657c961d52af43c20fdb6863
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_POWER
90 #elif CONFIG_KEYPAD == GIGABEAT_PAD
91 #define JEWELS_UP BUTTON_UP
92 #define JEWELS_DOWN BUTTON_DOWN
93 #define JEWELS_LEFT BUTTON_LEFT
94 #define JEWELS_RIGHT BUTTON_RIGHT
95 #define JEWELS_SELECT BUTTON_SELECT
96 #define JEWELS_CANCEL BUTTON_POWER
98 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
99 (CONFIG_KEYPAD == SANSA_C200_PAD)
100 #define JEWELS_UP BUTTON_UP
101 #define JEWELS_DOWN BUTTON_DOWN
102 #define JEWELS_LEFT BUTTON_LEFT
103 #define JEWELS_RIGHT BUTTON_RIGHT
104 #define JEWELS_SELECT BUTTON_SELECT
105 #define JEWELS_CANCEL BUTTON_POWER
107 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
108 #define JEWELS_UP BUTTON_SCROLL_UP
109 #define JEWELS_DOWN BUTTON_SCROLL_DOWN
110 #define JEWELS_LEFT BUTTON_LEFT
111 #define JEWELS_RIGHT BUTTON_RIGHT
112 #define JEWELS_SELECT BUTTON_PLAY
113 #define JEWELS_CANCEL BUTTON_POWER
115 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
116 #define JEWELS_UP BUTTON_UP
117 #define JEWELS_DOWN BUTTON_DOWN
118 #define JEWELS_LEFT BUTTON_LEFT
119 #define JEWELS_RIGHT BUTTON_RIGHT
120 #define JEWELS_SELECT BUTTON_SELECT
121 #define JEWELS_CANCEL BUTTON_BACK
123 #else
124 #error JEWELS: Unsupported keypad
125 #endif
127 /* use 30x30 tiles (iPod Video, Gigabeat) */
128 #if (LCD_HEIGHT == 240) && (LCD_WIDTH == 320) || \
129 ((LCD_HEIGHT == 320) && (LCD_WIDTH == 240))
130 #define TILE_WIDTH 30
131 #define TILE_HEIGHT 30
132 #define YOFS 0
133 #define NUM_SCORES 10
135 /* use 22x22 tiles (H300, iPod Color) */
136 #elif ((LCD_HEIGHT == 176) && (LCD_WIDTH == 220)) || \
137 ((LCD_HEIGHT == 220) && (LCD_WIDTH == 176))
138 #define TILE_WIDTH 22
139 #define TILE_HEIGHT 22
140 #define YOFS 0
141 #define NUM_SCORES 10
143 /* use 16x16 tiles (iPod Nano) */
144 #elif (LCD_HEIGHT == 132) && (LCD_WIDTH == 176)
145 #define TILE_WIDTH 16
146 #define TILE_HEIGHT 16
147 #define YOFS 4
148 #define NUM_SCORES 10
150 /* use 16x16 tiles (H100, iAudio X5, iPod 3G, iPod 4G grayscale) */
151 #elif (LCD_HEIGHT == 128) && (LCD_WIDTH == 160)
152 #define TILE_WIDTH 16
153 #define TILE_HEIGHT 16
154 #define YOFS 0
155 #define NUM_SCORES 10
157 /* use 14x14 tiles (H10 5/6 GB) */
158 #elif (LCD_HEIGHT == 128) && (LCD_WIDTH == 128)
159 #define TILE_WIDTH 14
160 #define TILE_HEIGHT 14
161 #define YOFS 0
162 #define NUM_SCORES 10
164 /* use 13x13 tiles (iPod Mini) */
165 #elif (LCD_HEIGHT == 110) && (LCD_WIDTH == 138)
166 #define TILE_WIDTH 13
167 #define TILE_HEIGHT 13
168 #define YOFS 6
169 #define NUM_SCORES 10
171 /* use 10x10 tiles (Sansa c200) */
172 #elif (LCD_HEIGHT == 80) && (LCD_WIDTH == 132)
173 #define TILE_WIDTH 10
174 #define TILE_HEIGHT 10
175 #define YOFS 0
176 #define NUM_SCORES 8
178 /* use 10x8 tiles (iFP 700) */
179 #elif (LCD_HEIGHT == 64) && (LCD_WIDTH == 128)
180 #define TILE_WIDTH 10
181 #define TILE_HEIGHT 8
182 #define YOFS 0
183 #define NUM_SCORES 8
185 /* use 10x8 tiles (Recorder, Ondio) */
186 #elif (LCD_HEIGHT == 64) && (LCD_WIDTH == 112)
187 #define TILE_WIDTH 10
188 #define TILE_HEIGHT 8
189 #define YOFS 0
190 #define NUM_SCORES 8
192 #else
193 #error JEWELS: Unsupported LCD
194 #endif
196 /* save files */
197 #define SCORE_FILE PLUGIN_GAMES_DIR "/jewels.score"
198 #define SAVE_FILE PLUGIN_GAMES_DIR "/jewels.save"
200 /* final game return status */
201 #define BJ_QUIT_FROM_GAME 4
202 #define BJ_END 3
203 #define BJ_USB 2
204 #define BJ_QUIT 1
205 #define BJ_LOSE 0
207 /* swap directions */
208 #define SWAP_UP 0
209 #define SWAP_RIGHT 1
210 #define SWAP_DOWN 2
211 #define SWAP_LEFT 3
213 /* play board dimension */
214 #define BJ_HEIGHT 9
215 #define BJ_WIDTH 8
217 /* next level threshold */
218 #define LEVEL_PTS 100
220 /* animation frame rate */
221 #define MAX_FPS 20
223 /* Game types */
224 enum game_type {
225 GAME_TYPE_NORMAL,
226 GAME_TYPE_PUZZLE
229 /* menu values */
230 #define FONT_HEIGHT 8
231 #define MAX_MITEMS 6
232 #define MENU_WIDTH 100
234 /* menu results */
235 enum menu_result {
236 MRES_NONE,
237 MRES_NEW,
238 MRES_PUZZLE,
239 MRES_SAVE,
240 MRES_RESUME,
241 MRES_SCORES,
242 MRES_HELP,
243 MRES_QUIT,
244 MRES_PLAYBACK,
245 MRES_EXIT
248 /* menu commands */
249 enum menu_cmd {
250 MCMD_NONE,
251 MCMD_NEXT,
252 MCMD_PREV,
253 MCMD_SELECT
256 /* menus */
257 struct jewels_menu {
258 char *title;
259 bool hasframe;
260 int selected;
261 int itemcnt;
262 struct jewels_menuitem {
263 char *text;
264 enum menu_result res;
265 } items[MAX_MITEMS];
266 } bjmenu[] = {
267 {"Jewels", false, 0, 6,
268 {{"New Game", MRES_NEW},
269 {"Puzzle", MRES_PUZZLE},
270 {"Resume Game", MRES_RESUME},
271 {"High Scores", MRES_SCORES},
272 {"Help", MRES_HELP},
273 {"Quit", MRES_QUIT}}},
274 {"Menu", true, 0, 5,
275 {{"Audio Playback", MRES_PLAYBACK },
276 {"Resume Game", MRES_RESUME},
277 {"Save Game", MRES_SAVE},
278 {"End Game", MRES_QUIT},
279 {"Exit Jewels", MRES_EXIT}}}
282 /* global rockbox api */
283 static struct plugin_api* rb;
285 /* external bitmaps */
286 extern const fb_data jewels[];
288 /* tile background colors */
289 #ifdef HAVE_LCD_COLOR
290 static const unsigned jewels_bkgd[2] = {
291 LCD_RGBPACK(104, 63, 63),
292 LCD_RGBPACK(83, 44, 44)
294 #endif
296 /* the tile struct
297 * type is the jewel number 0-7
298 * falling if the jewel is falling
299 * delete marks the jewel for deletion
301 struct tile {
302 int type;
303 bool falling;
304 bool delete;
307 /* the game context struct
308 * score is the current level score
309 * segments is the number of cleared segments in the current run
310 * level is the current level
311 * type is the game type (normal or puzzle)
312 * highscores is the list of high scores
313 * resume denotes whether to resume the currently loaded game
314 * dirty denotes whether the high scores are out of sync with the saved file
315 * playboard is the game playing board (first row is hidden)
316 * num_jewels is the number of different jewels to use
318 struct game_context {
319 unsigned int score;
320 unsigned int segments;
321 unsigned int level;
322 unsigned int type;
323 unsigned int highscores[NUM_SCORES];
324 bool resume;
325 bool dirty;
326 struct tile playboard[BJ_HEIGHT][BJ_WIDTH];
327 unsigned int num_jewels;
330 #define MAX_NUM_JEWELS 7
332 #define MAX_PUZZLE_TILES 4
333 #define NUM_PUZZLE_LEVELS 10
335 struct puzzle_tile {
336 int x;
337 int y;
338 int tile_type;
341 struct puzzle_level {
342 unsigned int num_jewels;
343 unsigned int num_tiles;
344 struct puzzle_tile tiles[MAX_PUZZLE_TILES];
347 #define PUZZLE_TILE_UP 1
348 #define PUZZLE_TILE_DOWN 2
349 #define PUZZLE_TILE_LEFT 4
350 #define PUZZLE_TILE_RIGHT 8
352 struct puzzle_level puzzle_levels[NUM_PUZZLE_LEVELS] = {
353 { 5, 2, { {3, 3, PUZZLE_TILE_RIGHT},
354 {4, 2, PUZZLE_TILE_LEFT} } },
355 { 5, 2, { {3, 2, PUZZLE_TILE_DOWN},
356 {3, 4, PUZZLE_TILE_UP} } },
357 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
358 {3, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
359 {3, 6, PUZZLE_TILE_UP} } },
360 { 6, 3, { {3, 2, PUZZLE_TILE_RIGHT},
361 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
362 {5, 4, PUZZLE_TILE_LEFT} } },
363 { 6, 2, { {3, 4, PUZZLE_TILE_RIGHT},
364 {4, 2, PUZZLE_TILE_LEFT} } },
365 { 6, 2, { {3, 2, PUZZLE_TILE_DOWN},
366 {4, 4, PUZZLE_TILE_UP} } },
367 { 7, 4, { {3, 2, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
368 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
369 {3, 4, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
370 {4, 4, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
371 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
372 {4, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
373 {3, 6, PUZZLE_TILE_UP} } },
374 { 7, 3, { {2, 2, PUZZLE_TILE_RIGHT},
375 {4, 1, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
376 {5, 4, PUZZLE_TILE_LEFT} } },
377 { 7, 4, { {3, 0, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
378 {5, 0, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
379 {2, 7, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
380 {4, 7, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
383 /*****************************************************************************
384 * jewels_init() initializes jewels data structures.
385 ******************************************************************************/
386 static void jewels_init(struct game_context* bj) {
387 /* seed the rand generator */
388 rb->srand(*rb->current_tick);
390 /* check for resumed game */
391 if(bj->resume) {
392 bj->resume = false;
393 return;
396 /* reset scoring */
397 bj->level = 1;
398 bj->score = 0;
399 bj->segments = 0;
401 /* clear playing board */
402 rb->memset(bj->playboard, 0, sizeof(bj->playboard));
405 /*****************************************************************************
406 * jewels_setcolors() set the foreground and background colors.
407 ******************************************************************************/
408 static inline void jewels_setcolors(void) {
409 #ifdef HAVE_LCD_COLOR
410 rb->lcd_set_background(LCD_RGBPACK(49, 26, 26));
411 rb->lcd_set_foreground(LCD_RGBPACK(210, 181, 181));
412 #endif
415 /*****************************************************************************
416 * jewels_drawboard() redraws the entire game board.
417 ******************************************************************************/
418 static void jewels_drawboard(struct game_context* bj) {
419 int i, j;
420 int w, h;
421 unsigned int tempscore;
422 char *title = "Level";
423 char str[10];
425 tempscore = (bj->score>LEVEL_PTS ? LEVEL_PTS : bj->score);
427 /* clear screen */
428 rb->lcd_clear_display();
430 /* dispay playing board */
431 for(i=0; i<BJ_HEIGHT-1; i++){
432 for(j=0; j<BJ_WIDTH; j++){
433 #ifdef HAVE_LCD_COLOR
434 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
435 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
436 TILE_WIDTH, TILE_HEIGHT);
437 rb->lcd_bitmap_transparent_part(jewels,
438 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
439 TILE_WIDTH, j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
440 TILE_WIDTH, TILE_HEIGHT);
441 #else
442 rb->lcd_bitmap_part(jewels,
443 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
444 TILE_WIDTH, j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
445 TILE_WIDTH, TILE_HEIGHT);
446 #endif
450 #if LCD_WIDTH > LCD_HEIGHT /* horizontal layout */
452 /* draw separator lines */
453 jewels_setcolors();
454 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, LCD_HEIGHT-1);
455 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, 18);
456 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, LCD_HEIGHT-10);
458 /* draw progress bar */
459 #ifdef HAVE_LCD_COLOR
460 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
461 #endif
462 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
463 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
464 tempscore/LEVEL_PTS),
465 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
466 ((LCD_HEIGHT-10)-18)*tempscore/LEVEL_PTS);
467 #ifdef HAVE_LCD_COLOR
468 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
469 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
470 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
471 tempscore/LEVEL_PTS)+1,
472 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
473 ((LCD_HEIGHT-10)-18)*tempscore/LEVEL_PTS-1);
474 jewels_setcolors();
475 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
476 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
477 tempscore/LEVEL_PTS),
478 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
479 ((LCD_HEIGHT-10)-18)*tempscore/LEVEL_PTS+1);
480 #endif
482 /* print text */
483 rb->lcd_getstringsize(title, &w, &h);
484 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 1, title);
486 rb->snprintf(str, 4, "%d", bj->level);
487 rb->lcd_getstringsize(str, &w, &h);
488 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 10, str);
490 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
491 rb->lcd_getstringsize(str, &w, &h);
492 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2,
493 LCD_HEIGHT-8, str);
495 #elif LCD_WIDTH < LCD_HEIGHT /* vertical layout */
497 /* draw separator lines */
498 jewels_setcolors();
499 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
500 rb->lcd_hline(0, LCD_WIDTH-1, LCD_HEIGHT-14);
501 rb->lcd_vline(LCD_WIDTH/2, LCD_HEIGHT-14, LCD_HEIGHT-1);
503 /* draw progress bar */
504 #ifdef HAVE_LCD_COLOR
505 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
506 #endif
507 rb->lcd_fillrect(0, (8*TILE_HEIGHT+YOFS)
508 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
509 LCD_WIDTH*tempscore/LEVEL_PTS,
510 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
511 #ifdef HAVE_LCD_COLOR
512 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
513 rb->lcd_drawrect(1, (8*TILE_HEIGHT+YOFS)
514 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4+1,
515 LCD_WIDTH*tempscore/LEVEL_PTS-1,
516 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2-2);
517 jewels_setcolors();
518 rb->lcd_drawrect(0, (8*TILE_HEIGHT+YOFS)
519 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
520 LCD_WIDTH*tempscore/LEVEL_PTS+1,
521 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
522 #endif
524 /* print text */
525 rb->snprintf(str, 10, "%s %d", title, bj->level);
526 rb->lcd_putsxy(1, LCD_HEIGHT-10, str);
528 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
529 rb->lcd_getstringsize(str, &w, &h);
530 rb->lcd_putsxy((LCD_WIDTH-2)-w, LCD_HEIGHT-10, str);
532 #else /* square layout */
534 /* draw separator lines */
535 jewels_setcolors();
536 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
537 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, 8*TILE_HEIGHT+YOFS);
538 rb->lcd_vline(LCD_WIDTH/2, 8*TILE_HEIGHT+YOFS, LCD_HEIGHT-1);
540 /* draw progress bar */
541 #ifdef HAVE_LCD_COLOR
542 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
543 #endif
544 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
545 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
546 *tempscore/LEVEL_PTS,
547 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
548 (8*TILE_HEIGHT+YOFS)*tempscore/LEVEL_PTS);
549 #ifdef HAVE_LCD_COLOR
550 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
551 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
552 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
553 *tempscore/LEVEL_PTS+1,
554 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
555 (8*TILE_HEIGHT+YOFS)*tempscore/LEVEL_PTS-1);
556 jewels_setcolors();
557 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
558 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
559 *tempscore/LEVEL_PTS,
560 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
561 (8*TILE_HEIGHT+YOFS)*tempscore/LEVEL_PTS+1);
562 #endif
564 /* print text */
565 rb->snprintf(str, 10, "%s %d", title, bj->level);
566 rb->lcd_putsxy(1, LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
568 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
569 rb->lcd_getstringsize(str, &w, &h);
570 rb->lcd_putsxy((LCD_WIDTH-2)-w,
571 LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
573 #endif /* layout */
575 rb->lcd_update();
578 /*****************************************************************************
579 * jewels_showmenu() displays the chosen menu after performing the chosen
580 * menu command.
581 ******************************************************************************/
582 static enum menu_result jewels_showmenu(struct jewels_menu* menu,
583 enum menu_cmd cmd) {
584 int i;
585 int w, h;
586 int firstline;
587 int adj;
589 /* handle menu command */
590 switch(cmd) {
591 case MCMD_NEXT:
592 menu->selected = (menu->selected+1)%menu->itemcnt;
593 break;
595 case MCMD_PREV:
596 menu->selected = (menu->selected-1+menu->itemcnt)%menu->itemcnt;
597 break;
599 case MCMD_SELECT:
600 return menu->items[menu->selected].res;
602 default:
603 break;
606 /* clear menu area */
607 firstline = (LCD_HEIGHT/FONT_HEIGHT-(menu->itemcnt+3))/2;
609 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
610 rb->lcd_fillrect((LCD_WIDTH-MENU_WIDTH)/2, firstline*FONT_HEIGHT,
611 MENU_WIDTH, (menu->itemcnt+3)*FONT_HEIGHT);
612 rb->lcd_set_drawmode(DRMODE_SOLID);
614 if(menu->hasframe) {
615 rb->lcd_drawrect((LCD_WIDTH-MENU_WIDTH)/2-1, firstline*FONT_HEIGHT-1,
616 MENU_WIDTH+2, (menu->itemcnt+3)*FONT_HEIGHT+2);
617 rb->lcd_hline((LCD_WIDTH-MENU_WIDTH)/2-1,
618 (LCD_WIDTH-MENU_WIDTH)/2-1+MENU_WIDTH+2,
619 (firstline+1)*FONT_HEIGHT);
622 /* draw menu items */
623 rb->lcd_getstringsize(menu->title, &w, &h);
624 rb->lcd_putsxy((LCD_WIDTH-w)/2, firstline*FONT_HEIGHT, menu->title);
626 for(i=0; i<menu->itemcnt; i++) {
627 if(i == menu->selected) {
628 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
630 rb->lcd_putsxy((LCD_WIDTH-MENU_WIDTH)/2, (firstline+i+2)*FONT_HEIGHT,
631 menu->items[i].text);
632 if(i == menu->selected) {
633 rb->lcd_set_drawmode(DRMODE_SOLID);
637 adj = (firstline == 0 ? 0 : 1);
638 rb->lcd_update_rect((LCD_WIDTH-MENU_WIDTH)/2-1, firstline*FONT_HEIGHT-adj,
639 MENU_WIDTH+2, (menu->itemcnt+3)*FONT_HEIGHT+2*adj);
640 return MRES_NONE;
643 /*****************************************************************************
644 * jewels_putjewels() makes the jewels fall to fill empty spots and adds
645 * new random jewels at the empty spots at the top of each row.
646 ******************************************************************************/
647 static void jewels_putjewels(struct game_context* bj){
648 int i, j, k;
649 bool mark, done;
650 long lasttick, currenttick;
652 /* loop to make all the jewels fall */
653 while(true) {
654 /* mark falling jewels and add new jewels to hidden top row*/
655 mark = false;
656 done = true;
657 for(j=0; j<BJ_WIDTH; j++) {
658 if(bj->playboard[1][j].type == 0) {
659 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
661 for(i=BJ_HEIGHT-2; i>=0; i--) {
662 if(!mark && bj->playboard[i+1][j].type == 0) {
663 mark = true;
664 done = false;
666 if(mark) bj->playboard[i][j].falling = true;
668 /*if(bj->playboard[1][j].falling) {
669 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
670 bj->playboard[0][j].falling = true;
672 mark = false;
675 /* break if there are no falling jewels */
676 if(done) break;
678 /* animate falling jewels */
679 lasttick = *rb->current_tick;
681 for(k=1; k<=8; k++) {
682 for(i=BJ_HEIGHT-2; i>=0; i--) {
683 for(j=0; j<BJ_WIDTH; j++) {
684 if(bj->playboard[i][j].falling &&
685 bj->playboard[i][j].type != 0) {
686 /* clear old position */
687 #ifdef HAVE_LCD_COLOR
688 if(i == 0 && YOFS) {
689 rb->lcd_set_foreground(rb->lcd_get_background());
690 } else {
691 rb->lcd_set_foreground(jewels_bkgd[(i-1+j)%2]);
693 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
694 TILE_WIDTH, TILE_HEIGHT);
695 if(bj->playboard[i+1][j].type == 0) {
696 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
697 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
698 TILE_WIDTH, TILE_HEIGHT);
700 #else
701 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
702 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
703 TILE_WIDTH, TILE_HEIGHT);
704 if(bj->playboard[i+1][j].type == 0) {
705 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
706 TILE_WIDTH, TILE_HEIGHT);
708 rb->lcd_set_drawmode(DRMODE_SOLID);
709 #endif
711 /* draw new position */
712 #ifdef HAVE_LCD_COLOR
713 rb->lcd_bitmap_transparent_part(jewels, 0,
714 TILE_HEIGHT*(bj->playboard[i][j].type),
715 TILE_WIDTH, j*TILE_WIDTH,
716 (i-1)*TILE_HEIGHT+YOFS+
717 ((((TILE_HEIGHT<<10)*k)/8)>>10),
718 TILE_WIDTH, TILE_HEIGHT);
719 #else
720 rb->lcd_bitmap_part(jewels, 0,
721 TILE_HEIGHT*(bj->playboard[i][j].type),
722 TILE_WIDTH, j*TILE_WIDTH,
723 (i-1)*TILE_HEIGHT+YOFS+
724 ((((TILE_HEIGHT<<10)*k)/8)>>10),
725 TILE_WIDTH, TILE_HEIGHT);
726 #endif
731 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
732 jewels_setcolors();
734 /* framerate limiting */
735 currenttick = *rb->current_tick;
736 if(currenttick-lasttick < HZ/MAX_FPS) {
737 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
738 } else {
739 rb->yield();
741 lasttick = currenttick;
744 /* shift jewels down */
745 for(j=0; j<BJ_WIDTH; j++) {
746 for(i=BJ_HEIGHT-1; i>=1; i--) {
747 if(bj->playboard[i-1][j].falling) {
748 bj->playboard[i][j].type = bj->playboard[i-1][j].type;
753 /* clear out top row */
754 for(j=0; j<BJ_WIDTH; j++) {
755 bj->playboard[0][j].type = 0;
758 /* mark everything not falling */
759 for(i=0; i<BJ_HEIGHT; i++) {
760 for(j=0; j<BJ_WIDTH; j++) {
761 bj->playboard[i][j].falling = false;
767 /*****************************************************************************
768 * jewels_clearjewels() finds all the connected rows and columns and
769 * calculates and returns the points earned.
770 ******************************************************************************/
771 static unsigned int jewels_clearjewels(struct game_context* bj) {
772 int i, j;
773 int last, run;
774 unsigned int points = 0;
776 /* check for connected rows */
777 for(i=1; i<BJ_HEIGHT; i++) {
778 last = 0;
779 run = 1;
780 for(j=0; j<BJ_WIDTH; j++) {
781 if(bj->playboard[i][j].type == last &&
782 bj->playboard[i][j].type != 0 &&
783 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
784 run++;
786 if(run == 3) {
787 bj->segments++;
788 points += bj->segments;
789 bj->playboard[i][j].delete = true;
790 bj->playboard[i][j-1].delete = true;
791 bj->playboard[i][j-2].delete = true;
792 } else if(run > 3) {
793 points++;
794 bj->playboard[i][j].delete = true;
796 } else {
797 run = 1;
798 last = bj->playboard[i][j].type;
803 /* check for connected columns */
804 for(j=0; j<BJ_WIDTH; j++) {
805 last = 0;
806 run = 1;
807 for(i=1; i<BJ_HEIGHT; i++) {
808 if(bj->playboard[i][j].type != 0 &&
809 bj->playboard[i][j].type == last &&
810 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
811 run++;
813 if(run == 3) {
814 bj->segments++;
815 points += bj->segments;
816 bj->playboard[i][j].delete = true;
817 bj->playboard[i-1][j].delete = true;
818 bj->playboard[i-2][j].delete = true;
819 } else if(run > 3) {
820 points++;
821 bj->playboard[i][j].delete = true;
823 } else {
824 run = 1;
825 last = bj->playboard[i][j].type;
830 /* clear deleted jewels */
831 for(i=1; i<BJ_HEIGHT; i++) {
832 for(j=0; j<BJ_WIDTH; j++) {
833 if(bj->playboard[i][j].delete) {
834 bj->playboard[i][j].delete = false;
835 bj->playboard[i][j].type = 0;
840 return points;
843 /*****************************************************************************
844 * jewels_runboard() runs the board until it settles in a fixed state and
845 * returns points earned.
846 ******************************************************************************/
847 static unsigned int jewels_runboard(struct game_context* bj) {
848 unsigned int points = 0;
849 unsigned int ret;
851 bj->segments = 0;
853 while((ret = jewels_clearjewels(bj)) > 0) {
854 points += ret;
855 jewels_drawboard(bj);
856 jewels_putjewels(bj);
859 return points;
862 /*****************************************************************************
863 * jewels_swapjewels() swaps two jewels as long as it results in points and
864 * returns points earned.
865 ******************************************************************************/
866 static unsigned int jewels_swapjewels(struct game_context* bj,
867 int x, int y, int direc) {
868 int k;
869 int horzmod, vertmod;
870 int movelen = 0;
871 bool undo = false;
872 unsigned int points = 0;
873 long lasttick, currenttick;
875 /* check for invalid parameters */
876 if(x < 0 || x >= BJ_WIDTH || y < 0 || y >= BJ_HEIGHT-1 ||
877 direc < SWAP_UP || direc > SWAP_LEFT) {
878 return 0;
881 /* check for invalid directions */
882 if((x == 0 && direc == SWAP_LEFT) ||
883 (x == BJ_WIDTH-1 && direc == SWAP_RIGHT) ||
884 (y == 0 && direc == SWAP_UP) ||
885 (y == BJ_HEIGHT-2 && direc == SWAP_DOWN)) {
886 return 0;
889 /* set direction variables */
890 horzmod = 0;
891 vertmod = 0;
892 switch(direc) {
893 case SWAP_UP:
894 vertmod = -1;
895 movelen = TILE_HEIGHT;
896 break;
897 case SWAP_RIGHT:
898 horzmod = 1;
899 movelen = TILE_WIDTH;
900 break;
901 case SWAP_DOWN:
902 vertmod = 1;
903 movelen = TILE_HEIGHT;
904 break;
905 case SWAP_LEFT:
906 horzmod = -1;
907 movelen = TILE_WIDTH;
908 break;
911 while(true) {
912 lasttick = *rb->current_tick;
914 /* animate swapping jewels */
915 for(k=0; k<=8; k++) {
916 /* clear old position */
917 #ifdef HAVE_LCD_COLOR
918 rb->lcd_set_foreground(jewels_bkgd[(x+y)%2]);
919 rb->lcd_fillrect(x*TILE_WIDTH,
920 y*TILE_HEIGHT+YOFS,
921 TILE_WIDTH, TILE_HEIGHT);
922 rb->lcd_set_foreground(jewels_bkgd[(x+horzmod+y+vertmod)%2]);
923 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
924 (y+vertmod)*TILE_HEIGHT+YOFS,
925 TILE_WIDTH, TILE_HEIGHT);
926 #else
927 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
928 rb->lcd_fillrect(x*TILE_WIDTH,
929 y*TILE_HEIGHT+YOFS,
930 TILE_WIDTH, TILE_HEIGHT);
931 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
932 (y+vertmod)*TILE_HEIGHT+YOFS,
933 TILE_WIDTH, TILE_HEIGHT);
934 rb->lcd_set_drawmode(DRMODE_SOLID);
935 #endif
936 /* draw new position */
937 #ifdef HAVE_LCD_COLOR
938 rb->lcd_bitmap_transparent_part(jewels,
939 0, TILE_HEIGHT*(bj->playboard
940 [y+1+vertmod][x+horzmod].type), TILE_WIDTH,
941 (x+horzmod)*TILE_WIDTH-horzmod*
942 ((((movelen<<10)*k)/8)>>10),
943 (y+vertmod)*TILE_HEIGHT-vertmod*
944 ((((movelen<<10)*k)/8)>>10)+YOFS,
945 TILE_WIDTH, TILE_HEIGHT);
946 rb->lcd_bitmap_transparent_part(jewels,
947 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
948 TILE_WIDTH, x*TILE_WIDTH+horzmod*
949 ((((movelen<<10)*k)/8)>>10),
950 y*TILE_HEIGHT+vertmod*
951 ((((movelen<<10)*k)/8)>>10)+YOFS,
952 TILE_WIDTH, TILE_HEIGHT);
953 #else
954 rb->lcd_bitmap_part(jewels,
955 0, TILE_HEIGHT*(bj->playboard
956 [y+1+vertmod][x+horzmod].type), TILE_WIDTH,
957 (x+horzmod)*TILE_WIDTH-horzmod*
958 ((((movelen<<10)*k)/8)>>10),
959 (y+vertmod)*TILE_HEIGHT-vertmod*
960 ((((movelen<<10)*k)/8)>>10)+YOFS,
961 TILE_WIDTH, TILE_HEIGHT);
962 rb->lcd_set_drawmode(DRMODE_FG);
963 rb->lcd_bitmap_part(jewels,
964 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
965 TILE_WIDTH, x*TILE_WIDTH+horzmod*
966 ((((movelen<<10)*k)/8)>>10),
967 y*TILE_HEIGHT+vertmod*
968 ((((movelen<<10)*k)/8)>>10)+YOFS,
969 TILE_WIDTH, TILE_HEIGHT);
970 rb->lcd_set_drawmode(DRMODE_SOLID);
971 #endif
973 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
974 jewels_setcolors();
976 /* framerate limiting */
977 currenttick = *rb->current_tick;
978 if(currenttick-lasttick < HZ/MAX_FPS) {
979 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
980 } else {
981 rb->yield();
983 lasttick = currenttick;
986 /* swap jewels */
987 int temp = bj->playboard[y+1][x].type;
988 bj->playboard[y+1][x].type =
989 bj->playboard[y+1+vertmod][x+horzmod].type;
990 bj->playboard[y+1+vertmod][x+horzmod].type = temp;
992 if(undo) break;
994 points = jewels_runboard(bj);
995 if(points == 0) {
996 undo = true;
997 } else {
998 break;
1002 return points;
1005 /*****************************************************************************
1006 * jewels_movesavail() uses pattern matching to see if there are any
1007 * available move left.
1008 ******************************************************************************/
1009 static bool jewels_movesavail(struct game_context* bj) {
1010 int i, j;
1011 bool moves = false;
1012 int mytype;
1014 for(i=1; i<BJ_HEIGHT; i++) {
1015 for(j=0; j<BJ_WIDTH; j++) {
1016 mytype = bj->playboard[i][j].type;
1017 if(mytype == 0 || mytype > MAX_NUM_JEWELS) continue;
1019 /* check horizontal patterns */
1020 if(j <= BJ_WIDTH-3) {
1021 if(i > 1) {
1022 if(bj->playboard[i-1][j+1].type == mytype) {
1023 if(bj->playboard[i-1][j+2].type == mytype)
1024 {moves = true; break;}
1025 if(bj->playboard[i][j+2].type == mytype)
1026 {moves = true; break;}
1028 if(bj->playboard[i][j+1].type == mytype) {
1029 if(bj->playboard[i-1][j+2].type == mytype)
1030 {moves = true; break;}
1034 if(j <= BJ_WIDTH-4) {
1035 if(bj->playboard[i][j+3].type == mytype) {
1036 if(bj->playboard[i][j+1].type == mytype)
1037 {moves = true; break;}
1038 if(bj->playboard[i][j+2].type == mytype)
1039 {moves = true; break;}
1043 if(i < BJ_HEIGHT-1) {
1044 if(bj->playboard[i][j+1].type == mytype) {
1045 if(bj->playboard[i+1][j+2].type == mytype)
1046 {moves = true; break;}
1048 if(bj->playboard[i+1][j+1].type == mytype) {
1049 if(bj->playboard[i][j+2].type == mytype)
1050 {moves = true; break;}
1051 if(bj->playboard[i+1][j+2].type == mytype)
1052 {moves = true; break;}
1057 /* check vertical patterns */
1058 if(i <= BJ_HEIGHT-3) {
1059 if(j > 0) {
1060 if(bj->playboard[i+1][j-1].type == mytype) {
1061 if(bj->playboard[i+2][j-1].type == mytype)
1062 {moves = true; break;}
1063 if(bj->playboard[i+2][j].type == mytype)
1064 {moves = true; break;}
1066 if(bj->playboard[i+1][j].type == mytype) {
1067 if(bj->playboard[i+2][j-1].type == mytype)
1068 {moves = true; break;}
1072 if(i <= BJ_HEIGHT-4) {
1073 if(bj->playboard[i+3][j].type == mytype) {
1074 if(bj->playboard[i+1][j].type == mytype)
1075 {moves = true; break;}
1076 if(bj->playboard[i+2][j].type == mytype)
1077 {moves = true; break;}
1081 if(j < BJ_WIDTH-1) {
1082 if(bj->playboard[i+1][j].type == mytype) {
1083 if(bj->playboard[i+2][j+1].type == mytype)
1084 {moves = true; break;}
1086 if(bj->playboard[i+1][j+1].type == mytype) {
1087 if(bj->playboard[i+2][j].type == mytype)
1088 {moves = true; break;}
1089 if (bj->playboard[i+2][j+1].type == mytype)
1090 {moves = true; break;}
1096 if(moves) break;
1099 return moves;
1102 /*****************************************************************************
1103 * jewels_puzzle_is_finished(bj) checks if the puzzle is finished.
1104 ******************************************************************************/
1105 static int jewels_puzzle_is_finished(struct game_context* bj) {
1106 unsigned int i, j;
1107 for(i=0; i<BJ_HEIGHT; i++) {
1108 for(j=0; j<BJ_WIDTH; j++) {
1109 int mytype = bj->playboard[i][j].type;
1110 if(mytype>MAX_NUM_JEWELS) {
1111 mytype -= MAX_NUM_JEWELS;
1112 if(mytype&PUZZLE_TILE_UP) {
1113 if(i==0 || bj->playboard[i-1][j].type<=MAX_NUM_JEWELS ||
1114 !((bj->playboard[i-1][j].type-MAX_NUM_JEWELS)
1115 &PUZZLE_TILE_DOWN))
1116 return 0;
1118 if(mytype&PUZZLE_TILE_DOWN) {
1119 if(i==BJ_HEIGHT-1 ||
1120 bj->playboard[i+1][j].type<=MAX_NUM_JEWELS ||
1121 !((bj->playboard[i+1][j].type-MAX_NUM_JEWELS)
1122 &PUZZLE_TILE_UP))
1123 return 0;
1125 if(mytype&PUZZLE_TILE_LEFT) {
1126 if(j==0 || bj->playboard[i][j-1].type<=MAX_NUM_JEWELS ||
1127 !((bj->playboard[i][j-1].type-MAX_NUM_JEWELS)
1128 &PUZZLE_TILE_RIGHT))
1129 return 0;
1131 if(mytype&PUZZLE_TILE_RIGHT) {
1132 if(j==BJ_WIDTH-1 ||
1133 bj->playboard[i][j+1].type<=MAX_NUM_JEWELS ||
1134 !((bj->playboard[i][j+1].type-MAX_NUM_JEWELS)
1135 &PUZZLE_TILE_LEFT))
1136 return 0;
1141 return 1;
1144 /*****************************************************************************
1145 * jewels_initlevel() initialises a level.
1146 ******************************************************************************/
1147 static unsigned int jewels_initlevel(struct game_context* bj) {
1148 unsigned int points = 0;
1150 switch(bj->type) {
1151 case GAME_TYPE_NORMAL:
1152 bj->num_jewels = MAX_NUM_JEWELS;
1153 break;
1155 case GAME_TYPE_PUZZLE:
1157 unsigned int i, j;
1158 struct puzzle_tile *tile;
1160 bj->num_jewels = puzzle_levels[bj->level-1].num_jewels;
1162 for(i=0; i<BJ_HEIGHT; i++) {
1163 for(j=0; j<BJ_WIDTH; j++) {
1164 bj->playboard[i][j].type = (rb->rand()%bj->num_jewels)+1;
1165 bj->playboard[i][j].falling = false;
1166 bj->playboard[i][j].delete = false;
1169 jewels_runboard(bj);
1170 tile = puzzle_levels[bj->level-1].tiles;
1171 for(i=0; i<puzzle_levels[bj->level-1].num_tiles; i++, tile++) {
1172 bj->playboard[tile->y+1][tile->x].type = MAX_NUM_JEWELS
1173 +tile->tile_type;
1176 break;
1179 jewels_drawboard(bj);
1181 /* run the play board */
1182 jewels_putjewels(bj);
1183 points += jewels_runboard(bj);
1184 return points;
1187 /*****************************************************************************
1188 * jewels_nextlevel() advances the game to the next level and returns
1189 * points earned.
1190 ******************************************************************************/
1191 static unsigned int jewels_nextlevel(struct game_context* bj) {
1192 int i, x, y;
1193 unsigned int points = 0;
1195 switch(bj->type) {
1196 case GAME_TYPE_NORMAL:
1197 /* roll over score, change and display level */
1198 while(bj->score >= LEVEL_PTS) {
1199 bj->score -= LEVEL_PTS;
1200 bj->level++;
1201 rb->splash(HZ*2, "Level %d", bj->level);
1202 jewels_drawboard(bj);
1205 /* randomly clear some jewels */
1206 for(i=0; i<16; i++) {
1207 x = rb->rand()%8;
1208 y = rb->rand()%8;
1210 if(bj->playboard[y][x].type != 0) {
1211 points++;
1212 bj->playboard[y][x].type = 0;
1215 break;
1217 case GAME_TYPE_PUZZLE:
1218 bj->level++;
1219 if(bj->level>NUM_PUZZLE_LEVELS) {
1220 rb->splash(HZ*2, "You win!");
1221 bj->level = 1;
1222 } else {
1223 rb->splash(HZ*2, "Level %d", bj->level);
1225 break;
1228 return jewels_initlevel(bj);
1231 /*****************************************************************************
1232 * jewels_recordscore() inserts a high score into the high scores list and
1233 * returns the high score position.
1234 ******************************************************************************/
1235 static int jewels_recordscore(struct game_context* bj) {
1236 int i;
1237 int position = 0;
1238 unsigned int current, temp;
1240 /* calculate total score */
1241 current = (bj->level-1)*LEVEL_PTS+bj->score;
1242 if(current <= 0) return 0;
1244 /* insert the current score into the high scores */
1245 for(i=0; i<NUM_SCORES; i++) {
1246 if(current >= bj->highscores[i]) {
1247 if(!position) {
1248 position = i+1;
1249 bj->dirty = true;
1251 temp = bj->highscores[i];
1252 bj->highscores[i] = current;
1253 current = temp;
1257 return position;
1260 /*****************************************************************************
1261 * jewels_loadscores() loads the high scores saved file.
1262 ******************************************************************************/
1263 static void jewels_loadscores(struct game_context* bj) {
1264 int fd;
1266 bj->dirty = false;
1268 /* clear high scores */
1269 rb->memset(bj->highscores, 0, sizeof(bj->highscores));
1271 /* open scores file */
1272 fd = rb->open(SCORE_FILE, O_RDONLY);
1273 if(fd < 0) return;
1275 /* read in high scores */
1276 if(rb->read(fd, bj->highscores, sizeof(bj->highscores)) <= 0) {
1277 /* scores are bad, reset */
1278 rb->memset(bj->highscores, 0, sizeof(bj->highscores));
1281 rb->close(fd);
1284 /*****************************************************************************
1285 * jewels_savescores() saves the high scores saved file.
1286 ******************************************************************************/
1287 static void jewels_savescores(struct game_context* bj) {
1288 int fd;
1290 /* write out the high scores to the save file */
1291 fd = rb->open(SCORE_FILE, O_WRONLY|O_CREAT);
1292 rb->write(fd, bj->highscores, sizeof(bj->highscores));
1293 rb->close(fd);
1294 bj->dirty = false;
1297 /*****************************************************************************
1298 * jewels_loadgame() loads the saved game and returns load success.
1299 ******************************************************************************/
1300 static bool jewels_loadgame(struct game_context* bj) {
1301 int fd;
1302 bool loaded = false;
1304 /* open game file */
1305 fd = rb->open(SAVE_FILE, O_RDONLY);
1306 if(fd < 0) return loaded;
1308 /* read in saved game */
1309 while(true) {
1310 if(rb->read(fd, &bj->score, sizeof(bj->score)) <= 0) break;
1311 if(rb->read(fd, &bj->level, sizeof(bj->level)) <= 0) break;
1312 if(rb->read(fd, &bj->type, sizeof(bj->type)) <= 0) break;
1313 if(rb->read(fd, bj->playboard, sizeof(bj->playboard)) <= 0) break;
1314 bj->resume = true;
1315 loaded = true;
1316 break;
1319 rb->close(fd);
1321 /* delete saved file */
1322 rb->remove(SAVE_FILE);
1323 return loaded;
1326 /*****************************************************************************
1327 * jewels_savegame() saves the current game state.
1328 ******************************************************************************/
1329 static void jewels_savegame(struct game_context* bj) {
1330 int fd;
1332 /* write out the game state to the save file */
1333 fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT);
1334 rb->write(fd, &bj->score, sizeof(bj->score));
1335 rb->write(fd, &bj->level, sizeof(bj->level));
1336 rb->write(fd, &bj->type, sizeof(bj->type));
1337 rb->write(fd, bj->playboard, sizeof(bj->playboard));
1338 rb->close(fd);
1340 bj->resume = true;
1343 /*****************************************************************************
1344 * jewels_callback() is the default event handler callback which is called
1345 * on usb connect and shutdown.
1346 ******************************************************************************/
1347 static void jewels_callback(void* param) {
1348 struct game_context* bj = (struct game_context*) param;
1349 if(bj->dirty) {
1350 rb->splash(HZ, "Saving high scores...");
1351 jewels_savescores(bj);
1355 /*****************************************************************************
1356 * jewels_main() is the main game subroutine, it returns the final game status.
1357 ******************************************************************************/
1358 static int jewels_main(struct game_context* bj) {
1359 int i, j;
1360 int w, h;
1361 int button;
1362 char str[18];
1363 bool startgame = false;
1364 bool inmenu = false;
1365 bool selected = false;
1366 enum menu_cmd cmd = MCMD_NONE;
1367 enum menu_result res;
1369 /* the cursor coordinates */
1370 int x=0, y=0;
1372 /* don't resume by default */
1373 bj->resume = false;
1375 /********************
1376 * menu *
1377 ********************/
1378 rb->lcd_clear_display();
1380 while(!startgame) {
1381 res = jewels_showmenu(&bjmenu[0], cmd);
1382 cmd = MCMD_NONE;
1384 rb->snprintf(str, 18, "High Score: %d", bj->highscores[0]);
1385 rb->lcd_getstringsize(str, &w, &h);
1386 rb->lcd_putsxy((LCD_WIDTH-w)/2, LCD_HEIGHT-8, str);
1387 rb->lcd_update();
1389 switch(res) {
1390 case MRES_NEW:
1391 startgame = true;
1392 bj->type = GAME_TYPE_NORMAL;
1393 continue;
1395 case MRES_PUZZLE:
1396 startgame = true;
1397 bj->type = GAME_TYPE_PUZZLE;
1398 continue;
1400 case MRES_RESUME:
1401 if(!jewels_loadgame(bj)) {
1402 rb->splash(HZ*2, "Nothing to resume");
1403 rb->lcd_clear_display();
1404 } else {
1405 startgame = true;
1407 continue;
1409 case MRES_SCORES:
1410 rb->lcd_clear_display();
1412 /* room for a title? */
1413 j = 0;
1414 if(LCD_HEIGHT-NUM_SCORES*8 >= 8) {
1415 rb->snprintf(str, 12, "%s", "High Scores");
1416 rb->lcd_getstringsize(str, &w, &h);
1417 rb->lcd_putsxy((LCD_WIDTH-w)/2, 0, str);
1418 j = 2;
1421 /* print high scores */
1422 for(i=0; i<NUM_SCORES; i++) {
1423 rb->snprintf(str, 11, "#%02d: %d", i+1, bj->highscores[i]);
1424 rb->lcd_puts(0, i+j, str);
1427 rb->lcd_update();
1428 while(true) {
1429 button = rb->button_get(true);
1430 if(button != BUTTON_NONE && !(button&BUTTON_REL)) break;
1432 rb->lcd_clear_display();
1433 continue;
1435 case MRES_HELP:
1436 /* welcome screen to display key bindings */
1437 rb->lcd_clear_display();
1438 rb->snprintf(str, 5, "%s", "Help");
1439 rb->lcd_getstringsize(str, &w, &h);
1440 rb->lcd_putsxy((LCD_WIDTH-w)/2, 0, str);
1441 #if CONFIG_KEYPAD == RECORDER_PAD
1442 rb->lcd_puts(0, 2, "Controls:");
1443 rb->lcd_puts(0, 3, "Directions = move");
1444 rb->lcd_puts(0, 4, "PLAY = select");
1445 rb->lcd_puts(0, 5, "Long PLAY = menu");
1446 rb->lcd_puts(0, 6, "OFF = cancel");
1447 #elif CONFIG_KEYPAD == ONDIO_PAD
1448 rb->lcd_puts(0, 2, "Controls:");
1449 rb->lcd_puts(0, 3, "Directions = move");
1450 rb->lcd_puts(0, 4, "MENU = select");
1451 rb->lcd_puts(0, 5, "Long MENU = menu");
1452 rb->lcd_puts(0, 6, "OFF = cancel");
1453 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
1454 rb->lcd_puts(0, 2, "Controls:");
1455 rb->lcd_puts(0, 3, "Directions = move");
1456 rb->lcd_puts(0, 4, "SELECT = select");
1457 rb->lcd_puts(0, 5, "Long SELECT = menu");
1458 rb->lcd_puts(0, 6, "PLAY = cancel");
1459 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
1460 rb->lcd_puts(0, 2, "Swap pairs of jewels to");
1461 rb->lcd_puts(0, 3, "form connected segments");
1462 rb->lcd_puts(0, 4, "of three or more of the");
1463 rb->lcd_puts(0, 5, "same type.");
1464 rb->lcd_puts(0, 7, "Controls:");
1465 rb->lcd_puts(0, 8, "Directions to move");
1466 rb->lcd_puts(0, 9, "SELECT to select");
1467 rb->lcd_puts(0, 10, "Long SELECT to show menu");
1468 rb->lcd_puts(0, 11, "OFF to cancel");
1469 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
1470 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
1471 rb->lcd_puts(0, 2, "Swap pairs of jewels to");
1472 rb->lcd_puts(0, 3, "form connected segments");
1473 rb->lcd_puts(0, 4, "of three or more of the");
1474 rb->lcd_puts(0, 5, "same type.");
1475 rb->lcd_puts(0, 7, "Controls:");
1476 rb->lcd_puts(0, 8, "Directions or scroll to move");
1477 rb->lcd_puts(0, 9, "SELECT to select");
1478 rb->lcd_puts(0, 10, "Long SELECT to show menu");
1479 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
1480 rb->lcd_puts(0, 2, "Swap pairs of jewels to");
1481 rb->lcd_puts(0, 3, "form connected segments");
1482 rb->lcd_puts(0, 4, "of three or more of the");
1483 rb->lcd_puts(0, 5, "same type.");
1484 rb->lcd_puts(0, 7, "Controls:");
1485 rb->lcd_puts(0, 8, "Directions to move");
1486 rb->lcd_puts(0, 9, "SELECT to select");
1487 rb->lcd_puts(0, 10, "Long SELECT to show menu");
1488 rb->lcd_puts(0, 11, "PLAY to cancel");
1489 #elif CONFIG_KEYPAD == GIGABEAT_PAD
1490 rb->lcd_puts(0, 2, "Swap pairs of jewels to");
1491 rb->lcd_puts(0, 3, "form connected segments");
1492 rb->lcd_puts(0, 4, "of three or more of the");
1493 rb->lcd_puts(0, 5, "same type.");
1494 rb->lcd_puts(0, 7, "Controls:");
1495 rb->lcd_puts(0, 8, "Directions to move");
1496 rb->lcd_puts(0, 9, "SELECT to select");
1497 rb->lcd_puts(0, 10, "Long SELECT to show menu");
1498 rb->lcd_puts(0, 11, "POWER to cancel");
1499 #elif CONFIG_KEYPAD == SANSA_E200_PAD \
1500 || CONFIG_KEYPAD == SANSA_C200_PAD
1501 rb->lcd_puts(0, 2, "Swap pairs of jewels to");
1502 rb->lcd_puts(0, 3, "form connected segments");
1503 rb->lcd_puts(0, 4, "of three or more of the");
1504 rb->lcd_puts(0, 5, "same type.");
1505 rb->lcd_puts(0, 7, "Controls:");
1506 rb->lcd_puts(0, 8, "Directions to move");
1507 rb->lcd_puts(0, 9, "SELECT to select");
1508 rb->lcd_puts(0, 10, "Long SELECT to show menu");
1509 rb->lcd_puts(0, 11, "POWER to cancel");
1510 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
1511 rb->lcd_puts(0, 2, "Swap pairs of jewels");
1512 rb->lcd_puts(0, 3, "to form connected");
1513 rb->lcd_puts(0, 4, "segments of three or ");
1514 rb->lcd_puts(0, 5, "more of the");
1515 rb->lcd_puts(0, 6, "same type.");
1516 rb->lcd_puts(0, 8, "Controls:");
1517 rb->lcd_puts(0, 9, "Directions or scroll to move");
1518 rb->lcd_puts(0, 10, "PLAY to select");
1519 rb->lcd_puts(0, 11, "Long PLAY for menu");
1520 rb->lcd_puts(0, 12, "POWER to cancel");
1521 #else
1522 #warning: missing help text.
1523 #endif
1524 rb->lcd_update();
1525 while(true) {
1526 button = rb->button_get(true);
1527 if(button != BUTTON_NONE && !(button&BUTTON_REL)) break;
1529 rb->lcd_clear_display();
1530 continue;
1532 case MRES_QUIT:
1533 return BJ_QUIT;
1535 default:
1536 break;
1539 /* handle menu button presses */
1540 button = rb->button_get(true);
1541 switch(button){
1542 #ifdef JEWELS_SCROLLWHEEL
1543 case JEWELS_PREV:
1544 case (JEWELS_PREV|BUTTON_REPEAT):
1545 #endif
1546 case JEWELS_UP:
1547 case (JEWELS_UP|BUTTON_REPEAT):
1548 cmd = MCMD_PREV;
1549 break;
1551 #ifdef JEWELS_SCROLLWHEEL
1552 case JEWELS_NEXT:
1553 case (JEWELS_NEXT|BUTTON_REPEAT):
1554 #endif
1555 case JEWELS_DOWN:
1556 case (JEWELS_DOWN|BUTTON_REPEAT):
1557 cmd = MCMD_NEXT;
1558 break;
1560 case JEWELS_SELECT:
1561 case JEWELS_RIGHT:
1562 cmd = MCMD_SELECT;
1563 break;
1565 #ifdef JEWELS_CANCEL
1566 #ifdef JEWELS_RC_CANCEL
1567 case JEWELS_RC_CANCEL:
1568 #endif
1569 case JEWELS_CANCEL:
1570 return BJ_QUIT;
1571 #endif
1573 default:
1574 if(rb->default_event_handler_ex(button, jewels_callback,
1575 (void*) bj) == SYS_USB_CONNECTED)
1576 return BJ_USB;
1577 break;
1581 /********************
1582 * init *
1583 ********************/
1584 jewels_init(bj);
1586 /********************
1587 * setup the board *
1588 ********************/
1589 bj->score += jewels_initlevel(bj);
1590 if (!jewels_movesavail(bj)) {
1591 switch(bj->type) {
1592 case GAME_TYPE_NORMAL:
1593 return BJ_LOSE;
1595 case GAME_TYPE_PUZZLE:
1596 do {
1597 rb->splash(2*HZ, "No more moves!");
1598 bj->score += jewels_initlevel(bj);
1599 } while(!jewels_movesavail(bj));
1600 break;
1604 /**********************
1605 * play *
1606 **********************/
1607 while(true) {
1608 int no_movesavail = false;
1610 if(!inmenu) {
1611 /* refresh the board */
1612 jewels_drawboard(bj);
1614 /* display the cursor */
1615 if(selected) {
1616 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1617 rb->lcd_fillrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1618 TILE_WIDTH, TILE_HEIGHT);
1619 rb->lcd_set_drawmode(DRMODE_SOLID);
1620 } else {
1621 rb->lcd_drawrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1622 TILE_WIDTH, TILE_HEIGHT);
1624 rb->lcd_update_rect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1625 TILE_WIDTH, TILE_HEIGHT);
1626 } else {
1627 res = jewels_showmenu(&bjmenu[1], cmd);
1628 cmd = MCMD_NONE;
1629 switch(res) {
1630 case MRES_RESUME:
1631 inmenu = false;
1632 selected = false;
1633 continue;
1635 case MRES_PLAYBACK:
1636 playback_control(rb);
1637 rb->lcd_setfont(FONT_SYSFIXED);
1638 inmenu = false;
1639 selected = false;
1640 break;
1642 case MRES_SAVE:
1643 rb->splash(HZ, "Saving game...");
1644 jewels_savegame(bj);
1645 return BJ_END;
1647 case MRES_QUIT:
1648 return BJ_END;
1650 case MRES_EXIT:
1651 return BJ_QUIT_FROM_GAME;
1653 default:
1654 break;
1658 /* handle game button presses */
1659 button = rb->button_get(true);
1660 switch(button){
1661 case JEWELS_LEFT: /* move cursor left */
1662 case (JEWELS_LEFT|BUTTON_REPEAT):
1663 if(!inmenu) {
1664 if(selected) {
1665 bj->score += jewels_swapjewels(bj, x, y, SWAP_LEFT);
1666 selected = false;
1667 if (!jewels_movesavail(bj)) no_movesavail = true;
1668 } else {
1669 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1672 break;
1674 case JEWELS_RIGHT: /* move cursor right */
1675 case (JEWELS_RIGHT|BUTTON_REPEAT):
1676 if(!inmenu) {
1677 if(selected) {
1678 bj->score += jewels_swapjewels(bj, x, y, SWAP_RIGHT);
1679 selected = false;
1680 if (!jewels_movesavail(bj)) no_movesavail = true;
1681 } else {
1682 x = (x+1)%BJ_WIDTH;
1684 } else {
1685 cmd = MCMD_SELECT;
1687 break;
1689 case JEWELS_DOWN: /* move cursor down */
1690 case (JEWELS_DOWN|BUTTON_REPEAT):
1691 if(!inmenu) {
1692 if(selected) {
1693 bj->score += jewels_swapjewels(bj, x, y, SWAP_DOWN);
1694 selected = false;
1695 if (!jewels_movesavail(bj)) no_movesavail = true;
1696 } else {
1697 y = (y+1)%(BJ_HEIGHT-1);
1699 } else {
1700 cmd = MCMD_NEXT;
1702 break;
1704 case JEWELS_UP: /* move cursor up */
1705 case (JEWELS_UP|BUTTON_REPEAT):
1706 if(!inmenu) {
1707 if(selected) {
1708 bj->score += jewels_swapjewels(bj, x, y, SWAP_UP);
1709 selected = false;
1710 if (!jewels_movesavail(bj)) no_movesavail = true;
1711 } else {
1712 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1714 } else {
1715 cmd = MCMD_PREV;
1717 break;
1719 #ifdef JEWELS_SCROLLWHEEL
1720 case JEWELS_PREV: /* scroll backwards */
1721 case (JEWELS_PREV|BUTTON_REPEAT):
1722 if(!inmenu) {
1723 if(!selected) {
1724 if(x == 0) {
1725 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1727 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1729 } else {
1730 cmd = MCMD_PREV;
1732 break;
1734 case JEWELS_NEXT: /* scroll forwards */
1735 case (JEWELS_NEXT|BUTTON_REPEAT):
1736 if(!inmenu) {
1737 if(!selected) {
1738 if(x == BJ_WIDTH-1) {
1739 y = (y+1)%(BJ_HEIGHT-1);
1741 x = (x+1)%BJ_WIDTH;
1743 } else {
1744 cmd = MCMD_NEXT;
1746 break;
1747 #endif
1749 case JEWELS_SELECT: /* toggle selected */
1750 if(!inmenu) {
1751 selected = !selected;
1752 } else {
1753 cmd = MCMD_SELECT;
1755 break;
1757 case (JEWELS_SELECT|BUTTON_REPEAT): /* show menu */
1758 if(!inmenu) inmenu = true;
1759 break;
1761 #ifdef JEWELS_CANCEL
1762 #ifdef JEWELS_RC_CANCEL
1763 case JEWELS_RC_CANCEL:
1764 #endif
1765 case JEWELS_CANCEL: /* end game */
1766 return BJ_END;
1767 break;
1768 #endif
1770 default:
1771 if(rb->default_event_handler_ex(button, jewels_callback,
1772 (void*) bj) == SYS_USB_CONNECTED)
1773 return BJ_USB;
1774 break;
1777 if (no_movesavail) {
1778 switch(bj->type) {
1779 case GAME_TYPE_NORMAL:
1780 return BJ_LOSE;
1782 case GAME_TYPE_PUZZLE:
1783 do {
1784 rb->splash(2*HZ, "No more moves!");
1785 bj->score += jewels_initlevel(bj);
1786 } while(!jewels_movesavail(bj));
1787 break;
1791 switch(bj->type) {
1792 case GAME_TYPE_NORMAL:
1793 if(bj->score >= LEVEL_PTS) bj->score = jewels_nextlevel(bj);
1794 break;
1796 case GAME_TYPE_PUZZLE:
1797 if(jewels_puzzle_is_finished(bj))
1798 bj->score += jewels_nextlevel(bj);
1799 break;
1804 /*****************************************************************************
1805 * plugin entry point.
1806 ******************************************************************************/
1807 enum plugin_status plugin_start(struct plugin_api* api, void* parameter) {
1808 struct game_context bj;
1809 bool exit = false;
1810 int position;
1811 char str[19];
1813 /* plugin init */
1814 (void)parameter;
1815 rb = api;
1816 /* end of plugin init */
1818 /* load high scores */
1819 jewels_loadscores(&bj);
1821 rb->lcd_setfont(FONT_SYSFIXED);
1822 #if LCD_DEPTH > 1
1823 rb->lcd_set_backdrop(NULL);
1824 #endif
1825 jewels_setcolors();
1827 while(!exit) {
1828 switch(jewels_main(&bj)){
1829 case BJ_LOSE:
1830 rb->splash(HZ*2, "No more moves!");
1831 /* fall through to BJ_END */
1833 case BJ_END:
1834 if(!bj.resume) {
1835 if((position = jewels_recordscore(&bj))) {
1836 rb->snprintf(str, 19, "New high score #%d!", position);
1837 rb->splash(HZ*2, str);
1840 break;
1842 case BJ_USB:
1843 rb->lcd_setfont(FONT_UI);
1844 return PLUGIN_USB_CONNECTED;
1846 case BJ_QUIT:
1847 if(bj.dirty) {
1848 rb->splash(HZ, "Saving high scores...");
1849 jewels_savescores(&bj);
1851 exit = true;
1852 break;
1854 case BJ_QUIT_FROM_GAME:
1855 if(!bj.resume) {
1856 if((position = jewels_recordscore(&bj))) {
1857 rb->snprintf(str, 19, "New high score #%d!", position);
1858 rb->splash(HZ*2, str);
1861 if(bj.dirty) {
1862 rb->splash(HZ, "Saving high scores...");
1863 jewels_savescores(&bj);
1865 exit = true;
1866 break;
1868 default:
1869 break;
1873 rb->lcd_setfont(FONT_UI);
1874 return PLUGIN_OK;
1877 #endif /* HAVE_LCD_BITMAP */