Call the oddly labelled button on the gigabeat remote "Menu" in the
[kugel-rb.git] / apps / plugins / jewels.c
blob8f799e973f76c0c73aa7c8404a3663cfaeebb733
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/display_text.h"
26 #include "lib/highscore.h"
27 #include "lib/playback_control.h"
28 #include "pluginbitmaps/jewels.h"
30 #ifdef HAVE_LCD_BITMAP
32 PLUGIN_HEADER
34 /* button definitions */
35 #if CONFIG_KEYPAD == RECORDER_PAD
36 #define JEWELS_UP BUTTON_UP
37 #define JEWELS_DOWN BUTTON_DOWN
38 #define JEWELS_LEFT BUTTON_LEFT
39 #define JEWELS_RIGHT BUTTON_RIGHT
40 #define JEWELS_SELECT BUTTON_PLAY
41 #define JEWELS_CANCEL BUTTON_OFF
42 #define HK_SELECT "PLAY"
43 #define HK_CANCEL "OFF"
45 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
46 #define JEWELS_UP BUTTON_UP
47 #define JEWELS_DOWN BUTTON_DOWN
48 #define JEWELS_LEFT BUTTON_LEFT
49 #define JEWELS_RIGHT BUTTON_RIGHT
50 #define JEWELS_SELECT BUTTON_SELECT
51 #define JEWELS_CANCEL BUTTON_OFF
52 #define HK_SELECT "SELECT"
53 #define HK_CANCEL "OFF"
55 #elif CONFIG_KEYPAD == ONDIO_PAD
56 #define JEWELS_UP BUTTON_UP
57 #define JEWELS_DOWN BUTTON_DOWN
58 #define JEWELS_LEFT BUTTON_LEFT
59 #define JEWELS_RIGHT BUTTON_RIGHT
60 #define JEWELS_SELECT BUTTON_MENU
61 #define JEWELS_CANCEL BUTTON_OFF
62 #define HK_SELECT "MENU"
63 #define HK_CANCEL "OFF"
65 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
66 #define JEWELS_UP BUTTON_UP
67 #define JEWELS_DOWN BUTTON_DOWN
68 #define JEWELS_LEFT BUTTON_LEFT
69 #define JEWELS_RIGHT BUTTON_RIGHT
70 #define JEWELS_SELECT BUTTON_SELECT
71 #define JEWELS_CANCEL BUTTON_OFF
72 #define JEWELS_RC_CANCEL BUTTON_RC_STOP
73 #define HK_SELECT "SELECT"
74 #define HK_CANCEL "OFF"
76 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
77 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
78 #define JEWELS_SCROLLWHEEL
79 #define JEWELS_UP BUTTON_MENU
80 #define JEWELS_DOWN BUTTON_PLAY
81 #define JEWELS_LEFT BUTTON_LEFT
82 #define JEWELS_RIGHT BUTTON_RIGHT
83 #define JEWELS_PREV BUTTON_SCROLL_BACK
84 #define JEWELS_NEXT BUTTON_SCROLL_FWD
85 #define JEWELS_SELECT BUTTON_SELECT
86 #define JEWELS_CANCEL (BUTTON_SELECT | BUTTON_MENU)
87 #define HK_SELECT "SELECT"
88 #define HK_CANCEL "SEL + MENU"
90 #elif (CONFIG_KEYPAD == IPOD_3G_PAD)
91 #define JEWELS_LEFT BUTTON_LEFT
92 #define JEWELS_RIGHT BUTTON_RIGHT
93 #define JEWELS_UP BUTTON_SCROLL_BACK
94 #define JEWELS_DOWN BUTTON_SCROLL_FWD
95 #define JEWELS_SELECT BUTTON_SELECT
96 #define JEWELS_CANCEL BUTTON_MENU
97 #define HK_SELECT "SELECT"
98 #define HK_CANCEL "MENU"
100 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
101 #define JEWELS_UP BUTTON_UP
102 #define JEWELS_DOWN BUTTON_DOWN
103 #define JEWELS_LEFT BUTTON_LEFT
104 #define JEWELS_RIGHT BUTTON_RIGHT
105 #define JEWELS_SELECT BUTTON_SELECT
106 #define JEWELS_CANCEL BUTTON_PLAY
107 #define HK_SELECT "SELECT"
108 #define HK_CANCEL "PLAY"
110 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
111 #define JEWELS_UP BUTTON_UP
112 #define JEWELS_DOWN BUTTON_DOWN
113 #define JEWELS_LEFT BUTTON_LEFT
114 #define JEWELS_RIGHT BUTTON_RIGHT
115 #define JEWELS_SELECT BUTTON_SELECT
116 #define JEWELS_CANCEL BUTTON_POWER
117 #define HK_SELECT "SELECT"
118 #define HK_CANCEL "POWER"
120 #elif CONFIG_KEYPAD == GIGABEAT_PAD
121 #define JEWELS_UP BUTTON_UP
122 #define JEWELS_DOWN BUTTON_DOWN
123 #define JEWELS_LEFT BUTTON_LEFT
124 #define JEWELS_RIGHT BUTTON_RIGHT
125 #define JEWELS_SELECT BUTTON_SELECT
126 #define JEWELS_CANCEL BUTTON_POWER
127 #define HK_SELECT "SELECT"
128 #define HK_CANCEL "POWER"
130 #elif CONFIG_KEYPAD == SANSA_E200_PAD
131 #define JEWELS_SCROLLWHEEL
132 #define JEWELS_UP BUTTON_UP
133 #define JEWELS_DOWN BUTTON_DOWN
134 #define JEWELS_LEFT BUTTON_LEFT
135 #define JEWELS_RIGHT BUTTON_RIGHT
136 #define JEWELS_PREV BUTTON_SCROLL_BACK
137 #define JEWELS_NEXT BUTTON_SCROLL_FWD
138 #define JEWELS_SELECT BUTTON_SELECT
139 #define JEWELS_CANCEL BUTTON_POWER
140 #define HK_SELECT "SELECT"
141 #define HK_CANCEL "POWER"
143 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
144 #define JEWELS_SCROLLWHEEL
145 #define JEWELS_UP BUTTON_UP
146 #define JEWELS_DOWN BUTTON_DOWN
147 #define JEWELS_LEFT BUTTON_LEFT
148 #define JEWELS_RIGHT BUTTON_RIGHT
149 #define JEWELS_PREV BUTTON_SCROLL_BACK
150 #define JEWELS_NEXT BUTTON_SCROLL_FWD
151 #define JEWELS_SELECT BUTTON_SELECT
152 #define JEWELS_CANCEL (BUTTON_HOME|BUTTON_REPEAT)
153 #define HK_SELECT "SELECT"
154 #define HK_CANCEL "HOME"
156 #elif CONFIG_KEYPAD == SANSA_C200_PAD || \
157 CONFIG_KEYPAD == SANSA_CLIP_PAD || \
158 CONFIG_KEYPAD == SANSA_M200_PAD
159 #define JEWELS_UP BUTTON_UP
160 #define JEWELS_DOWN BUTTON_DOWN
161 #define JEWELS_LEFT BUTTON_LEFT
162 #define JEWELS_RIGHT BUTTON_RIGHT
163 #define JEWELS_SELECT BUTTON_SELECT
164 #define JEWELS_CANCEL BUTTON_POWER
165 #define HK_SELECT "SELECT"
166 #define HK_CANCEL "POWER"
168 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
169 #define JEWELS_UP BUTTON_SCROLL_UP
170 #define JEWELS_DOWN BUTTON_SCROLL_DOWN
171 #define JEWELS_LEFT BUTTON_LEFT
172 #define JEWELS_RIGHT BUTTON_RIGHT
173 #define JEWELS_SELECT BUTTON_PLAY
174 #define JEWELS_CANCEL BUTTON_POWER
175 #define HK_SELECT "PLAY"
176 #define HK_CANCEL "POWER"
178 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
179 #define JEWELS_UP BUTTON_UP
180 #define JEWELS_DOWN BUTTON_DOWN
181 #define JEWELS_LEFT BUTTON_LEFT
182 #define JEWELS_RIGHT BUTTON_RIGHT
183 #define JEWELS_SELECT BUTTON_SELECT
184 #define JEWELS_CANCEL BUTTON_BACK
185 #define HK_SELECT "SELECT"
186 #define HK_CANCEL "BACK"
188 #elif CONFIG_KEYPAD == MROBE100_PAD
189 #define JEWELS_UP BUTTON_UP
190 #define JEWELS_DOWN BUTTON_DOWN
191 #define JEWELS_LEFT BUTTON_LEFT
192 #define JEWELS_RIGHT BUTTON_RIGHT
193 #define JEWELS_SELECT BUTTON_SELECT
194 #define JEWELS_CANCEL BUTTON_POWER
195 #define HK_SELECT "SELECT"
196 #define HK_CANCEL "POWER"
198 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
199 #define JEWELS_UP BUTTON_RC_VOL_UP
200 #define JEWELS_DOWN BUTTON_RC_VOL_DOWN
201 #define JEWELS_LEFT BUTTON_RC_REW
202 #define JEWELS_RIGHT BUTTON_RC_FF
203 #define JEWELS_SELECT BUTTON_RC_PLAY
204 #define JEWELS_CANCEL BUTTON_RC_REC
205 #define HK_SELECT "PLAY"
206 #define HK_CANCEL "REC"
208 #define JEWELS_RC_CANCEL BUTTON_REC
210 #elif CONFIG_KEYPAD == COWOND2_PAD
211 #define JEWELS_CANCEL BUTTON_POWER
212 #define HK_CANCEL "POWER"
214 #elif CONFIG_KEYPAD == IAUDIO67_PAD
215 #define JEWELS_UP BUTTON_STOP
216 #define JEWELS_DOWN BUTTON_PLAY
217 #define JEWELS_LEFT BUTTON_LEFT
218 #define JEWELS_RIGHT BUTTON_RIGHT
219 #define JEWELS_SELECT BUTTON_MENU
220 #define JEWELS_CANCEL BUTTON_POWER
221 #define HK_SELECT "MENU"
222 #define HK_CANCEL "POWER"
224 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
225 #define JEWELS_UP BUTTON_UP
226 #define JEWELS_DOWN BUTTON_DOWN
227 #define JEWELS_LEFT BUTTON_LEFT
228 #define JEWELS_RIGHT BUTTON_RIGHT
229 #define JEWELS_SELECT BUTTON_SELECT
230 #define JEWELS_CANCEL BUTTON_BACK
231 #define HK_SELECT "MIDDLE"
232 #define HK_CANCEL "BACK"
234 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
235 #define JEWELS_UP BUTTON_UP
236 #define JEWELS_DOWN BUTTON_DOWN
237 #define JEWELS_LEFT BUTTON_LEFT
238 #define JEWELS_RIGHT BUTTON_RIGHT
239 #define JEWELS_SELECT BUTTON_SELECT
240 #define JEWELS_CANCEL BUTTON_POWER
241 #define HK_SELECT "SELECT"
242 #define HK_CANCEL "POWER"
244 #elif CONFIG_KEYPAD == ONDAVX747_PAD || \
245 CONFIG_KEYPAD == MROBE500_PAD
246 #define JEWELS_CANCEL BUTTON_POWER
247 #define HK_CANCEL "POWER"
249 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
250 #define JEWELS_UP BUTTON_UP
251 #define JEWELS_DOWN BUTTON_DOWN
252 #define JEWELS_LEFT BUTTON_LEFT
253 #define JEWELS_RIGHT BUTTON_RIGHT
254 #define JEWELS_SELECT BUTTON_PLAY
255 #define JEWELS_CANCEL BUTTON_REW
256 #define HK_SELECT "PLAY"
257 #define HK_CANCEL "REWIND"
259 #else
260 #error No keymap defined!
261 #endif
263 #ifdef HAVE_TOUCHSCREEN
264 #ifndef JEWELS_UP
265 #define JEWELS_UP BUTTON_TOPMIDDLE
266 #endif
267 #ifndef JEWELS_DOWN
268 #define JEWELS_DOWN BUTTON_BOTTOMMIDDLE
269 #endif
270 #ifndef JEWELS_LEFT
271 #define JEWELS_LEFT BUTTON_MIDLEFT
272 #endif
273 #ifndef JEWELS_RIGHT
274 #define JEWELS_RIGHT BUTTON_MIDRIGHT
275 #endif
276 #ifndef JEWELS_SELECT
277 #define JEWELS_SELECT BUTTON_CENTER
278 #define HK_SELECT "CENTER"
279 #endif
280 #ifndef JEWELS_CANCEL
281 #define JEWELS_CANCEL BUTTON_TOPLEFT
282 #define HK_CANCEL "TOPLEFT"
283 #endif
284 #endif
286 #define TILE_WIDTH BMPWIDTH_jewels
287 #define TILE_HEIGHT (BMPHEIGHT_jewels/23)
289 #if LCD_HEIGHT < LCD_WIDTH
290 /* This calculation assumes integer division w/ LCD_HEIGHT/TILE_HEIGHT */
291 #define YOFS LCD_HEIGHT-((LCD_HEIGHT/TILE_HEIGHT)*TILE_HEIGHT)
292 #else
293 #define YOFS 0
294 #endif
296 #define NUM_SCORES 5
298 /* swap directions */
299 #define SWAP_UP 0
300 #define SWAP_RIGHT 1
301 #define SWAP_DOWN 2
302 #define SWAP_LEFT 3
304 /* play board dimension */
305 #define BJ_HEIGHT 9
306 #define BJ_WIDTH 8
308 /* next level threshold */
309 #define LEVEL_PTS 100
311 /* animation frame rate */
312 #define MAX_FPS 20
314 /* text margin */
315 #define MARGIN 5
317 /* Game types */
318 enum game_type {
319 GAME_TYPE_NORMAL,
320 GAME_TYPE_PUZZLE
323 /* external bitmaps */
324 extern const fb_data jewels[];
326 /* tile background colors */
327 #ifdef HAVE_LCD_COLOR
328 static const unsigned jewels_bkgd[2] = {
329 LCD_RGBPACK(104, 63, 63),
330 LCD_RGBPACK(83, 44, 44)
332 #endif
334 /* the tile struct
335 * type is the jewel number 0-7
336 * falling if the jewel is falling
337 * delete marks the jewel for deletion
339 struct tile {
340 int type;
341 bool falling;
342 bool delete;
345 /* the game context struct
346 * score is the current level score
347 * segments is the number of cleared segments in the current run
348 * level is the current level
349 * tmp_type is the select type in the menu
350 * type is the game type (normal or puzzle)
351 * playboard is the game playing board (first row is hidden)
352 * num_jewels is the number of different jewels to use
354 struct game_context {
355 unsigned int score;
356 unsigned int segments;
357 unsigned int level;
358 unsigned int type;
359 unsigned int tmp_type;
360 struct tile playboard[BJ_HEIGHT][BJ_WIDTH];
361 unsigned int num_jewels;
364 #define MAX_NUM_JEWELS 7
366 #define MAX_PUZZLE_TILES 4
367 #define NUM_PUZZLE_LEVELS 10
369 struct puzzle_tile {
370 int x;
371 int y;
372 int tile_type;
375 struct puzzle_level {
376 unsigned int num_jewels;
377 unsigned int num_tiles;
378 struct puzzle_tile tiles[MAX_PUZZLE_TILES];
381 #define PUZZLE_TILE_UP 1
382 #define PUZZLE_TILE_DOWN 2
383 #define PUZZLE_TILE_LEFT 4
384 #define PUZZLE_TILE_RIGHT 8
386 struct puzzle_level puzzle_levels[NUM_PUZZLE_LEVELS] = {
387 { 5, 2, { {3, 3, PUZZLE_TILE_RIGHT},
388 {4, 2, PUZZLE_TILE_LEFT} } },
389 { 5, 2, { {3, 2, PUZZLE_TILE_DOWN},
390 {3, 4, PUZZLE_TILE_UP} } },
391 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
392 {3, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
393 {3, 6, PUZZLE_TILE_UP} } },
394 { 6, 3, { {3, 2, PUZZLE_TILE_RIGHT},
395 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
396 {5, 4, PUZZLE_TILE_LEFT} } },
397 { 6, 2, { {3, 4, PUZZLE_TILE_RIGHT},
398 {4, 2, PUZZLE_TILE_LEFT} } },
399 { 6, 2, { {3, 2, PUZZLE_TILE_DOWN},
400 {4, 4, PUZZLE_TILE_UP} } },
401 { 7, 4, { {3, 2, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
402 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
403 {3, 4, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
404 {4, 4, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
405 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
406 {4, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
407 {3, 6, PUZZLE_TILE_UP} } },
408 { 7, 3, { {2, 2, PUZZLE_TILE_RIGHT},
409 {4, 1, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
410 {5, 4, PUZZLE_TILE_LEFT} } },
411 { 7, 4, { {3, 0, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
412 {5, 0, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
413 {2, 7, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
414 {4, 7, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
417 #define SAVE_FILE PLUGIN_GAMES_DIR "/jewels.save"
419 #define HIGH_SCORE PLUGIN_GAMES_DIR "/jewels.score"
420 struct highscore highest[NUM_SCORES];
423 /*****************************************************************************
424 * jewels_setcolors() set the foreground and background colors.
425 ******************************************************************************/
426 static inline void jewels_setcolors(void) {
427 #ifdef HAVE_LCD_COLOR
428 rb->lcd_set_background(LCD_RGBPACK(49, 26, 26));
429 rb->lcd_set_foreground(LCD_RGBPACK(210, 181, 181));
430 #endif
433 /*****************************************************************************
434 * jewels_loadgame() loads the saved game and returns load success.
435 ******************************************************************************/
436 static bool jewels_loadgame(struct game_context* bj)
438 int fd;
439 bool loaded = false;
441 /* open game file */
442 fd = rb->open(SAVE_FILE, O_RDONLY);
443 if(fd < 0) return loaded;
445 /* read in saved game */
446 while(true) {
447 if(rb->read(fd, &bj->tmp_type, sizeof(bj->tmp_type)) <= 0) break;
448 if(rb->read(fd, &bj->type, sizeof(bj->type)) <= 0) break;
449 if(rb->read(fd, &bj->score, sizeof(bj->score)) <= 0) break;
450 if(rb->read(fd, &bj->level, sizeof(bj->level)) <= 0) break;
451 if(rb->read(fd, &bj->segments, sizeof(bj->segments)) <= 0) break;
452 if(rb->read(fd, &bj->num_jewels, sizeof(bj->num_jewels)) <= 0) break;
453 if(rb->read(fd, bj->playboard, sizeof(bj->playboard)) <= 0) break;
454 loaded = true;
455 break;
458 rb->close(fd);
460 /* delete saved file */
461 rb->remove(SAVE_FILE);
462 return loaded;
465 /*****************************************************************************
466 * jewels_savegame() saves the current game state.
467 ******************************************************************************/
468 static void jewels_savegame(struct game_context* bj)
470 int fd;
471 /* write out the game state to the save file */
472 fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT);
473 rb->write(fd, &bj->tmp_type, sizeof(bj->tmp_type));
474 rb->write(fd, &bj->type, sizeof(bj->type));
475 rb->write(fd, &bj->score, sizeof(bj->score));
476 rb->write(fd, &bj->level, sizeof(bj->level));
477 rb->write(fd, &bj->segments, sizeof(bj->segments));
478 rb->write(fd, &bj->num_jewels, sizeof(bj->num_jewels));
479 rb->write(fd, bj->playboard, sizeof(bj->playboard));
480 rb->close(fd);
483 /*****************************************************************************
484 * jewels_drawboard() redraws the entire game board.
485 ******************************************************************************/
486 static void jewels_drawboard(struct game_context* bj) {
487 int i, j;
488 int w, h;
489 unsigned int tempscore;
490 unsigned int size;
491 char *title = "Level";
492 char str[10];
494 if (bj->type == GAME_TYPE_NORMAL) {
495 tempscore = (bj->score>LEVEL_PTS ? LEVEL_PTS : bj->score);
496 size = LEVEL_PTS;
497 } else {
498 tempscore = (bj->level>NUM_PUZZLE_LEVELS ? NUM_PUZZLE_LEVELS : bj->level);
499 size = NUM_PUZZLE_LEVELS;
502 /* clear screen */
503 rb->lcd_clear_display();
505 /* dispay playing board */
506 for(i=0; i<BJ_HEIGHT-1; i++){
507 for(j=0; j<BJ_WIDTH; j++){
508 #ifdef HAVE_LCD_COLOR
509 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
510 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
511 TILE_WIDTH, TILE_HEIGHT);
512 rb->lcd_bitmap_transparent_part(jewels,
513 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
514 TILE_WIDTH, j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
515 TILE_WIDTH, TILE_HEIGHT);
516 #else
517 rb->lcd_bitmap_part(jewels,
518 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
519 TILE_WIDTH, j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
520 TILE_WIDTH, TILE_HEIGHT);
521 #endif
525 #if LCD_WIDTH > LCD_HEIGHT /* horizontal layout */
527 /* draw separator lines */
528 jewels_setcolors();
529 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, LCD_HEIGHT-1);
531 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, 18);
532 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, LCD_HEIGHT-10);
534 /* draw progress bar */
535 #ifdef HAVE_LCD_COLOR
536 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
537 #endif
538 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
539 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
540 tempscore/size),
541 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
542 ((LCD_HEIGHT-10)-18)*tempscore/size);
543 #ifdef HAVE_LCD_COLOR
544 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
545 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
546 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
547 tempscore/size)+1,
548 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
549 ((LCD_HEIGHT-10)-18)*tempscore/size-1);
550 jewels_setcolors();
551 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
552 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*tempscore/size),
553 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
554 ((LCD_HEIGHT-10)-18)*tempscore/size+1);
555 #endif
557 /* print text */
558 rb->lcd_getstringsize(title, &w, &h);
559 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 1, title);
560 rb->snprintf(str, 4, "%d", bj->level);
561 rb->lcd_getstringsize(str, &w, &h);
562 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 10, str);
564 if (bj->type == GAME_TYPE_NORMAL) {
565 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
566 rb->lcd_getstringsize(str, &w, &h);
567 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2,
568 LCD_HEIGHT-8, str);
571 #elif LCD_WIDTH < LCD_HEIGHT /* vertical layout */
573 /* draw separator lines */
574 jewels_setcolors();
575 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
576 rb->lcd_hline(0, LCD_WIDTH-1, LCD_HEIGHT-14);
577 rb->lcd_vline(LCD_WIDTH/2, LCD_HEIGHT-14, LCD_HEIGHT-1);
579 /* draw progress bar */
580 #ifdef HAVE_LCD_COLOR
581 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
582 #endif
583 rb->lcd_fillrect(0, (8*TILE_HEIGHT+YOFS)
584 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
585 LCD_WIDTH*tempscore/size,
586 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
587 #ifdef HAVE_LCD_COLOR
588 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
589 rb->lcd_drawrect(1, (8*TILE_HEIGHT+YOFS)
590 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4+1,
591 LCD_WIDTH*tempscore/size-1,
592 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2-2);
593 jewels_setcolors();
594 rb->lcd_drawrect(0, (8*TILE_HEIGHT+YOFS)
595 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
596 LCD_WIDTH*tempscore/size+1,
597 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
598 #endif
600 /* print text */
601 rb->snprintf(str, 10, "%s %d", title, bj->level);
602 rb->lcd_putsxy(1, LCD_HEIGHT-10, str);
604 if (bj->type == GAME_TYPE_NORMAL) {
605 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
606 rb->lcd_getstringsize(str, &w, &h);
607 rb->lcd_putsxy((LCD_WIDTH-2)-w, LCD_HEIGHT-10, str);
611 #else /* square layout */
613 /* draw separator lines */
614 jewels_setcolors();
615 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
616 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, 8*TILE_HEIGHT+YOFS);
617 rb->lcd_vline(LCD_WIDTH/2, 8*TILE_HEIGHT+YOFS, LCD_HEIGHT-1);
619 /* draw progress bar */
620 #ifdef HAVE_LCD_COLOR
621 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
622 #endif
623 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
624 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)*tempscore/size,
625 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
626 (8*TILE_HEIGHT+YOFS)*tempscore/size);
627 #ifdef HAVE_LCD_COLOR
628 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
629 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
630 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
631 *tempscore/size+1,
632 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
633 (8*TILE_HEIGHT+YOFS)*tempscore/size-1);
634 jewels_setcolors();
635 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
636 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
637 *tempscore/size,
638 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
639 (8*TILE_HEIGHT+YOFS)*tempscore/size+1);
640 #endif
642 /* print text */
643 rb->snprintf(str, 10, "%s %d", title, bj->level);
644 rb->lcd_putsxy(1, LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
646 if (bj->type == GAME_TYPE_NORMAL) {
647 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
648 rb->lcd_getstringsize(str, &w, &h);
649 rb->lcd_putsxy((LCD_WIDTH-2)-w,
650 LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
653 #endif /* layout */
655 rb->lcd_update();
658 /*****************************************************************************
659 * jewels_putjewels() makes the jewels fall to fill empty spots and adds
660 * new random jewels at the empty spots at the top of each row.
661 ******************************************************************************/
662 static void jewels_putjewels(struct game_context* bj){
663 int i, j, k;
664 bool mark, done;
665 long lasttick, currenttick;
667 /* loop to make all the jewels fall */
668 while(true) {
669 /* mark falling jewels and add new jewels to hidden top row*/
670 mark = false;
671 done = true;
672 for(j=0; j<BJ_WIDTH; j++) {
673 if(bj->playboard[1][j].type == 0) {
674 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
676 for(i=BJ_HEIGHT-2; i>=0; i--) {
677 if(!mark && bj->playboard[i+1][j].type == 0) {
678 mark = true;
679 done = false;
681 if(mark) bj->playboard[i][j].falling = true;
683 /*if(bj->playboard[1][j].falling) {
684 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
685 bj->playboard[0][j].falling = true;
687 mark = false;
690 /* break if there are no falling jewels */
691 if(done) break;
693 /* animate falling jewels */
694 lasttick = *rb->current_tick;
696 for(k=1; k<=8; k++) {
697 for(i=BJ_HEIGHT-2; i>=0; i--) {
698 for(j=0; j<BJ_WIDTH; j++) {
699 if(bj->playboard[i][j].falling &&
700 bj->playboard[i][j].type != 0) {
701 /* clear old position */
702 #ifdef HAVE_LCD_COLOR
703 if(i == 0 && YOFS) {
704 rb->lcd_set_foreground(rb->lcd_get_background());
705 } else {
706 rb->lcd_set_foreground(jewels_bkgd[(i-1+j)%2]);
708 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
709 TILE_WIDTH, TILE_HEIGHT);
710 if(bj->playboard[i+1][j].type == 0) {
711 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
712 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
713 TILE_WIDTH, TILE_HEIGHT);
715 #else
716 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
717 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
718 TILE_WIDTH, TILE_HEIGHT);
719 if(bj->playboard[i+1][j].type == 0) {
720 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
721 TILE_WIDTH, TILE_HEIGHT);
723 rb->lcd_set_drawmode(DRMODE_SOLID);
724 #endif
726 /* draw new position */
727 #ifdef HAVE_LCD_COLOR
728 rb->lcd_bitmap_transparent_part(jewels, 0,
729 TILE_HEIGHT*(bj->playboard[i][j].type),
730 TILE_WIDTH, j*TILE_WIDTH,
731 (i-1)*TILE_HEIGHT+YOFS+
732 ((((TILE_HEIGHT<<10)*k)/8)>>10),
733 TILE_WIDTH, TILE_HEIGHT);
734 #else
735 rb->lcd_bitmap_part(jewels, 0,
736 TILE_HEIGHT*(bj->playboard[i][j].type),
737 TILE_WIDTH, j*TILE_WIDTH,
738 (i-1)*TILE_HEIGHT+YOFS+
739 ((((TILE_HEIGHT<<10)*k)/8)>>10),
740 TILE_WIDTH, TILE_HEIGHT);
741 #endif
746 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
747 jewels_setcolors();
749 /* framerate limiting */
750 currenttick = *rb->current_tick;
751 if(currenttick-lasttick < HZ/MAX_FPS) {
752 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
753 } else {
754 rb->yield();
756 lasttick = currenttick;
759 /* shift jewels down */
760 for(j=0; j<BJ_WIDTH; j++) {
761 for(i=BJ_HEIGHT-1; i>=1; i--) {
762 if(bj->playboard[i-1][j].falling) {
763 bj->playboard[i][j].type = bj->playboard[i-1][j].type;
768 /* clear out top row */
769 for(j=0; j<BJ_WIDTH; j++) {
770 bj->playboard[0][j].type = 0;
773 /* mark everything not falling */
774 for(i=0; i<BJ_HEIGHT; i++) {
775 for(j=0; j<BJ_WIDTH; j++) {
776 bj->playboard[i][j].falling = false;
782 /*****************************************************************************
783 * jewels_clearjewels() finds all the connected rows and columns and
784 * calculates and returns the points earned.
785 ******************************************************************************/
786 static unsigned int jewels_clearjewels(struct game_context* bj) {
787 int i, j;
788 int last, run;
789 unsigned int points = 0;
791 /* check for connected rows */
792 for(i=1; i<BJ_HEIGHT; i++) {
793 last = 0;
794 run = 1;
795 for(j=0; j<BJ_WIDTH; j++) {
796 if(bj->playboard[i][j].type == last &&
797 bj->playboard[i][j].type != 0 &&
798 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
799 run++;
801 if(run == 3) {
802 bj->segments++;
803 points += bj->segments;
804 bj->playboard[i][j].delete = true;
805 bj->playboard[i][j-1].delete = true;
806 bj->playboard[i][j-2].delete = true;
807 } else if(run > 3) {
808 points++;
809 bj->playboard[i][j].delete = true;
811 } else {
812 run = 1;
813 last = bj->playboard[i][j].type;
818 /* check for connected columns */
819 for(j=0; j<BJ_WIDTH; j++) {
820 last = 0;
821 run = 1;
822 for(i=1; i<BJ_HEIGHT; i++) {
823 if(bj->playboard[i][j].type != 0 &&
824 bj->playboard[i][j].type == last &&
825 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
826 run++;
828 if(run == 3) {
829 bj->segments++;
830 points += bj->segments;
831 bj->playboard[i][j].delete = true;
832 bj->playboard[i-1][j].delete = true;
833 bj->playboard[i-2][j].delete = true;
834 } else if(run > 3) {
835 points++;
836 bj->playboard[i][j].delete = true;
838 } else {
839 run = 1;
840 last = bj->playboard[i][j].type;
845 /* clear deleted jewels */
846 for(i=1; i<BJ_HEIGHT; i++) {
847 for(j=0; j<BJ_WIDTH; j++) {
848 if(bj->playboard[i][j].delete) {
849 bj->playboard[i][j].delete = false;
850 bj->playboard[i][j].type = 0;
855 return points;
858 /*****************************************************************************
859 * jewels_runboard() runs the board until it settles in a fixed state and
860 * returns points earned.
861 ******************************************************************************/
862 static unsigned int jewels_runboard(struct game_context* bj) {
863 unsigned int points = 0;
864 unsigned int ret;
866 bj->segments = 0;
868 while((ret = jewels_clearjewels(bj)) > 0) {
869 points += ret;
870 jewels_drawboard(bj);
871 jewels_putjewels(bj);
874 return points;
877 /*****************************************************************************
878 * jewels_swapjewels() swaps two jewels as long as it results in points and
879 * returns points earned.
880 ******************************************************************************/
881 static unsigned int jewels_swapjewels(struct game_context* bj,
882 int x, int y, int direc) {
883 int k;
884 int horzmod, vertmod;
885 int movelen = 0;
886 bool undo = false;
887 unsigned int points = 0;
888 long lasttick, currenttick;
890 /* check for invalid parameters */
891 if(x < 0 || x >= BJ_WIDTH || y < 0 || y >= BJ_HEIGHT-1 ||
892 direc < SWAP_UP || direc > SWAP_LEFT) {
893 return 0;
896 /* check for invalid directions */
897 if((x == 0 && direc == SWAP_LEFT) ||
898 (x == BJ_WIDTH-1 && direc == SWAP_RIGHT) ||
899 (y == 0 && direc == SWAP_UP) ||
900 (y == BJ_HEIGHT-2 && direc == SWAP_DOWN)) {
901 return 0;
904 /* set direction variables */
905 horzmod = 0;
906 vertmod = 0;
907 switch(direc) {
908 case SWAP_UP:
909 vertmod = -1;
910 movelen = TILE_HEIGHT;
911 break;
912 case SWAP_RIGHT:
913 horzmod = 1;
914 movelen = TILE_WIDTH;
915 break;
916 case SWAP_DOWN:
917 vertmod = 1;
918 movelen = TILE_HEIGHT;
919 break;
920 case SWAP_LEFT:
921 horzmod = -1;
922 movelen = TILE_WIDTH;
923 break;
926 while(true) {
927 lasttick = *rb->current_tick;
929 /* animate swapping jewels */
930 for(k=0; k<=8; k++) {
931 /* clear old position */
932 #ifdef HAVE_LCD_COLOR
933 rb->lcd_set_foreground(jewels_bkgd[(x+y)%2]);
934 rb->lcd_fillrect(x*TILE_WIDTH,
935 y*TILE_HEIGHT+YOFS,
936 TILE_WIDTH, TILE_HEIGHT);
937 rb->lcd_set_foreground(jewels_bkgd[(x+horzmod+y+vertmod)%2]);
938 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
939 (y+vertmod)*TILE_HEIGHT+YOFS,
940 TILE_WIDTH, TILE_HEIGHT);
941 #else
942 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
943 rb->lcd_fillrect(x*TILE_WIDTH,
944 y*TILE_HEIGHT+YOFS,
945 TILE_WIDTH, TILE_HEIGHT);
946 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
947 (y+vertmod)*TILE_HEIGHT+YOFS,
948 TILE_WIDTH, TILE_HEIGHT);
949 rb->lcd_set_drawmode(DRMODE_SOLID);
950 #endif
951 /* draw new position */
952 #ifdef HAVE_LCD_COLOR
953 rb->lcd_bitmap_transparent_part(jewels,
954 0, TILE_HEIGHT*(bj->playboard
955 [y+1+vertmod][x+horzmod].type), TILE_WIDTH,
956 (x+horzmod)*TILE_WIDTH-horzmod*
957 ((((movelen<<10)*k)/8)>>10),
958 (y+vertmod)*TILE_HEIGHT-vertmod*
959 ((((movelen<<10)*k)/8)>>10)+YOFS,
960 TILE_WIDTH, TILE_HEIGHT);
961 rb->lcd_bitmap_transparent_part(jewels,
962 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
963 TILE_WIDTH, x*TILE_WIDTH+horzmod*
964 ((((movelen<<10)*k)/8)>>10),
965 y*TILE_HEIGHT+vertmod*
966 ((((movelen<<10)*k)/8)>>10)+YOFS,
967 TILE_WIDTH, TILE_HEIGHT);
968 #else
969 rb->lcd_bitmap_part(jewels,
970 0, TILE_HEIGHT*(bj->playboard
971 [y+1+vertmod][x+horzmod].type), TILE_WIDTH,
972 (x+horzmod)*TILE_WIDTH-horzmod*
973 ((((movelen<<10)*k)/8)>>10),
974 (y+vertmod)*TILE_HEIGHT-vertmod*
975 ((((movelen<<10)*k)/8)>>10)+YOFS,
976 TILE_WIDTH, TILE_HEIGHT);
977 rb->lcd_set_drawmode(DRMODE_FG);
978 rb->lcd_bitmap_part(jewels,
979 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
980 TILE_WIDTH, x*TILE_WIDTH+horzmod*
981 ((((movelen<<10)*k)/8)>>10),
982 y*TILE_HEIGHT+vertmod*
983 ((((movelen<<10)*k)/8)>>10)+YOFS,
984 TILE_WIDTH, TILE_HEIGHT);
985 rb->lcd_set_drawmode(DRMODE_SOLID);
986 #endif
988 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
989 jewels_setcolors();
991 /* framerate limiting */
992 currenttick = *rb->current_tick;
993 if(currenttick-lasttick < HZ/MAX_FPS) {
994 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
995 } else {
996 rb->yield();
998 lasttick = currenttick;
1001 /* swap jewels */
1002 int temp = bj->playboard[y+1][x].type;
1003 bj->playboard[y+1][x].type =
1004 bj->playboard[y+1+vertmod][x+horzmod].type;
1005 bj->playboard[y+1+vertmod][x+horzmod].type = temp;
1007 if(undo) break;
1009 points = jewels_runboard(bj);
1010 if(points == 0) {
1011 undo = true;
1012 } else {
1013 break;
1017 return points;
1020 /*****************************************************************************
1021 * jewels_movesavail() uses pattern matching to see if there are any
1022 * available move left.
1023 ******************************************************************************/
1024 static bool jewels_movesavail(struct game_context* bj) {
1025 int i, j;
1026 bool moves = false;
1027 int mytype;
1029 for(i=1; i<BJ_HEIGHT; i++) {
1030 for(j=0; j<BJ_WIDTH; j++) {
1031 mytype = bj->playboard[i][j].type;
1032 if(mytype == 0 || mytype > MAX_NUM_JEWELS) continue;
1034 /* check horizontal patterns */
1035 if(j <= BJ_WIDTH-3) {
1036 if(i > 1) {
1037 if(bj->playboard[i-1][j+1].type == mytype) {
1038 if(bj->playboard[i-1][j+2].type == mytype)
1039 {moves = true; break;}
1040 if(bj->playboard[i][j+2].type == mytype)
1041 {moves = true; break;}
1043 if(bj->playboard[i][j+1].type == mytype) {
1044 if(bj->playboard[i-1][j+2].type == mytype)
1045 {moves = true; break;}
1049 if(j <= BJ_WIDTH-4) {
1050 if(bj->playboard[i][j+3].type == mytype) {
1051 if(bj->playboard[i][j+1].type == mytype)
1052 {moves = true; break;}
1053 if(bj->playboard[i][j+2].type == mytype)
1054 {moves = true; break;}
1058 if(i < BJ_HEIGHT-1) {
1059 if(bj->playboard[i][j+1].type == mytype) {
1060 if(bj->playboard[i+1][j+2].type == mytype)
1061 {moves = true; break;}
1063 if(bj->playboard[i+1][j+1].type == mytype) {
1064 if(bj->playboard[i][j+2].type == mytype)
1065 {moves = true; break;}
1066 if(bj->playboard[i+1][j+2].type == mytype)
1067 {moves = true; break;}
1072 /* check vertical patterns */
1073 if(i <= BJ_HEIGHT-3) {
1074 if(j > 0) {
1075 if(bj->playboard[i+1][j-1].type == mytype) {
1076 if(bj->playboard[i+2][j-1].type == mytype)
1077 {moves = true; break;}
1078 if(bj->playboard[i+2][j].type == mytype)
1079 {moves = true; break;}
1081 if(bj->playboard[i+1][j].type == mytype) {
1082 if(bj->playboard[i+2][j-1].type == mytype)
1083 {moves = true; break;}
1087 if(i <= BJ_HEIGHT-4) {
1088 if(bj->playboard[i+3][j].type == mytype) {
1089 if(bj->playboard[i+1][j].type == mytype)
1090 {moves = true; break;}
1091 if(bj->playboard[i+2][j].type == mytype)
1092 {moves = true; break;}
1096 if(j < BJ_WIDTH-1) {
1097 if(bj->playboard[i+1][j].type == mytype) {
1098 if(bj->playboard[i+2][j+1].type == mytype)
1099 {moves = true; break;}
1101 if(bj->playboard[i+1][j+1].type == mytype) {
1102 if(bj->playboard[i+2][j].type == mytype)
1103 {moves = true; break;}
1104 if (bj->playboard[i+2][j+1].type == mytype)
1105 {moves = true; break;}
1110 if(moves) break;
1112 return moves;
1115 /*****************************************************************************
1116 * jewels_puzzle_is_finished() checks if the puzzle is finished.
1117 ******************************************************************************/
1118 static bool jewels_puzzle_is_finished(struct game_context* bj) {
1119 unsigned int i, j;
1120 for(i=0; i<BJ_HEIGHT; i++) {
1121 for(j=0; j<BJ_WIDTH; j++) {
1122 int mytype = bj->playboard[i][j].type;
1123 if(mytype>MAX_NUM_JEWELS) {
1124 mytype -= MAX_NUM_JEWELS;
1125 if(mytype&PUZZLE_TILE_UP) {
1126 if(i==0 || bj->playboard[i-1][j].type<=MAX_NUM_JEWELS ||
1127 !((bj->playboard[i-1][j].type-MAX_NUM_JEWELS)
1128 &PUZZLE_TILE_DOWN))
1129 return false;
1131 if(mytype&PUZZLE_TILE_DOWN) {
1132 if(i==BJ_HEIGHT-1 ||
1133 bj->playboard[i+1][j].type<=MAX_NUM_JEWELS ||
1134 !((bj->playboard[i+1][j].type-MAX_NUM_JEWELS)
1135 &PUZZLE_TILE_UP))
1136 return false;
1138 if(mytype&PUZZLE_TILE_LEFT) {
1139 if(j==0 || bj->playboard[i][j-1].type<=MAX_NUM_JEWELS ||
1140 !((bj->playboard[i][j-1].type-MAX_NUM_JEWELS)
1141 &PUZZLE_TILE_RIGHT))
1142 return false;
1144 if(mytype&PUZZLE_TILE_RIGHT) {
1145 if(j==BJ_WIDTH-1 ||
1146 bj->playboard[i][j+1].type<=MAX_NUM_JEWELS ||
1147 !((bj->playboard[i][j+1].type-MAX_NUM_JEWELS)
1148 &PUZZLE_TILE_LEFT))
1149 return false;
1154 return true;
1157 /*****************************************************************************
1158 * jewels_initlevel() initialises a level.
1159 ******************************************************************************/
1160 static unsigned int jewels_initlevel(struct game_context* bj) {
1161 unsigned int points = 0;
1163 switch(bj->type) {
1164 case GAME_TYPE_NORMAL:
1165 bj->num_jewels = MAX_NUM_JEWELS;
1166 break;
1168 case GAME_TYPE_PUZZLE:
1170 unsigned int i, j;
1171 struct puzzle_tile *tile;
1173 bj->num_jewels = puzzle_levels[bj->level-1].num_jewels;
1175 for(i=0; i<BJ_HEIGHT; i++) {
1176 for(j=0; j<BJ_WIDTH; j++) {
1177 bj->playboard[i][j].type = (rb->rand()%bj->num_jewels)+1;
1178 bj->playboard[i][j].falling = false;
1179 bj->playboard[i][j].delete = false;
1182 jewels_runboard(bj);
1183 tile = puzzle_levels[bj->level-1].tiles;
1184 for(i=0; i<puzzle_levels[bj->level-1].num_tiles; i++, tile++) {
1185 bj->playboard[tile->y+1][tile->x].type = MAX_NUM_JEWELS
1186 +tile->tile_type;
1189 break;
1192 jewels_drawboard(bj);
1194 /* run the play board */
1195 jewels_putjewels(bj);
1196 points += jewels_runboard(bj);
1197 return points;
1200 /*****************************************************************************
1201 * jewels_init() initializes jewels data structures.
1202 ******************************************************************************/
1203 static void jewels_init(struct game_context* bj) {
1204 /* seed the rand generator */
1205 rb->srand(*rb->current_tick);
1207 bj->type = bj->tmp_type;
1208 bj->level = 1;
1209 bj->score = 0;
1210 bj->segments = 0;
1212 jewels_setcolors();
1214 /* clear playing board */
1215 rb->memset(bj->playboard, 0, sizeof(bj->playboard));
1216 do {
1217 bj->score += jewels_initlevel(bj);
1218 } while(!jewels_movesavail(bj));
1221 /*****************************************************************************
1222 * jewels_nextlevel() advances the game to the next bj->level and returns
1223 * points earned.
1224 ******************************************************************************/
1225 static void jewels_nextlevel(struct game_context* bj) {
1226 int i, x, y;
1227 unsigned int points = 0;
1229 switch(bj->type) {
1230 case GAME_TYPE_NORMAL:
1231 /* roll over score, change and display level */
1232 while(bj->score >= LEVEL_PTS) {
1233 bj->score -= LEVEL_PTS;
1234 bj->level++;
1235 rb->splashf(HZ*2, "Level %d", bj->level);
1236 jewels_drawboard(bj);
1239 /* randomly clear some jewels */
1240 for(i=0; i<16; i++) {
1241 x = rb->rand()%8;
1242 y = rb->rand()%8;
1244 if(bj->playboard[y][x].type != 0) {
1245 points++;
1246 bj->playboard[y][x].type = 0;
1249 break;
1251 case GAME_TYPE_PUZZLE:
1252 bj->level++;
1253 rb->splashf(HZ*2, "Level %d", bj->level);
1254 break;
1257 points += jewels_initlevel(bj);
1258 bj->score += points;
1261 static bool jewels_help(void)
1263 rb->lcd_setfont(FONT_UI);
1264 #define WORDS (sizeof help_text / sizeof (char*))
1265 static char *help_text[] = {
1266 "Jewels", "", "Aim", "",
1267 "Swap", "pairs", "of", "jewels", "to", "form", "connected",
1268 "segments", "of", "three", "or", "more", "of", "the", "same",
1269 "type.", "",
1270 "The", "goal", "of", "the", "game", "is", "to", "score", "as", "many",
1271 "points", "as", "possible", "before", "running", "out", "of",
1272 "available", "moves.", "", "",
1273 "Controls", "",
1274 "Directions",
1275 #ifdef JEWELS_SCROLLWHEEL
1276 "or", "scroll",
1277 #endif
1278 "to", "move", "",
1279 HK_SELECT, "to", "select", "",
1280 HK_CANCEL, "to", "go", "to", "menu"
1282 static struct style_text formation[]={
1283 { 0, TEXT_CENTER|TEXT_UNDERLINE },
1284 { 2, C_RED },
1285 { 42, C_RED },
1286 { -1, 0 }
1288 #ifdef HAVE_LCD_COLOR
1289 rb->lcd_set_background(LCD_BLACK);
1290 rb->lcd_set_foreground(LCD_WHITE);
1291 #endif
1292 int button;
1293 if (display_text(WORDS, help_text, formation, NULL))
1294 return true;
1295 do {
1296 button = rb->button_get(true);
1297 if (rb->default_event_handler (button) == SYS_USB_CONNECTED) {
1298 return true;
1300 } while( ( button == BUTTON_NONE )
1301 || ( button & (BUTTON_REL|BUTTON_REPEAT) ) );
1302 rb->lcd_setfont(FONT_SYSFIXED);
1304 return false;
1307 static bool _ingame;
1308 static int jewels_menu_cb(int action, const struct menu_item_ex *this_item)
1310 int i = ((intptr_t)this_item);
1311 if(action == ACTION_REQUEST_MENUITEM
1312 && !_ingame && (i==0 || i==6))
1313 return ACTION_EXIT_MENUITEM;
1314 return action;
1316 /*****************************************************************************
1317 * jewels_game_menu() shows the game menu.
1318 ******************************************************************************/
1319 static int jewels_game_menu(struct game_context* bj, bool ingame)
1321 rb->button_clear_queue();
1322 int choice = 0;
1324 _ingame = ingame;
1326 static struct opt_items mode[] = {
1327 { "Normal", -1 },
1328 { "Puzzle", -1 },
1331 MENUITEM_STRINGLIST (main_menu, "Jewels Menu", jewels_menu_cb,
1332 "Resume Game",
1333 "Start New Game",
1334 "Mode",
1335 "Help",
1336 "High Scores",
1337 "Playback Control",
1338 "Quit without Saving",
1339 "Quit");
1341 while (1) {
1342 switch (rb->do_menu(&main_menu, &choice, NULL, false)) {
1343 case 0:
1344 jewels_setcolors();
1345 return 0;
1346 case 1:
1347 jewels_init(bj);
1348 return 0;
1349 case 2:
1350 rb->set_option("Mode", &bj->tmp_type, INT, mode, 2, NULL);
1351 break;
1352 case 3:
1353 if(jewels_help())
1354 return 1;
1355 break;
1356 case 4:
1357 highscore_show(NUM_SCORES, highest, NUM_SCORES, true);
1358 break;
1359 case 5:
1360 playback_control(NULL);
1361 break;
1362 case 6:
1363 return 1;
1364 case 7:
1365 if (ingame) {
1366 rb->splash(HZ*1, "Saving game ...");
1367 jewels_savegame(bj);
1369 return 1;
1370 case MENU_ATTACHED_USB:
1371 return 1;
1372 default:
1373 break;
1378 static int jewels_main(struct game_context* bj) {
1379 int button;
1380 int position;
1381 bool selected = false;
1382 bool no_movesavail;
1383 int x=0, y=0;
1385 bool loaded = jewels_loadgame(bj);
1386 if (jewels_game_menu(bj, loaded)!=0)
1387 return 0;
1389 while(true) {
1390 no_movesavail = false;
1392 /* refresh the board */
1393 jewels_drawboard(bj);
1395 /* display the cursor */
1396 if(selected) {
1397 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1398 rb->lcd_fillrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1399 TILE_WIDTH, TILE_HEIGHT);
1400 rb->lcd_set_drawmode(DRMODE_SOLID);
1401 } else {
1402 rb->lcd_drawrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1403 TILE_WIDTH, TILE_HEIGHT);
1405 rb->lcd_update_rect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1406 TILE_WIDTH, TILE_HEIGHT);
1408 /* handle game button presses */
1409 rb->yield();
1410 button = rb->button_get(true);
1411 switch(button){
1412 case JEWELS_LEFT: /* move cursor left */
1413 case (JEWELS_LEFT|BUTTON_REPEAT):
1414 if(selected) {
1415 bj->score += jewels_swapjewels(bj, x, y, SWAP_LEFT);
1416 selected = false;
1417 if (!jewels_movesavail(bj)) no_movesavail = true;
1418 } else {
1419 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1421 break;
1423 case JEWELS_RIGHT: /* move cursor right */
1424 case (JEWELS_RIGHT|BUTTON_REPEAT):
1425 if(selected) {
1426 bj->score += jewels_swapjewels(bj, x, y, SWAP_RIGHT);
1427 selected = false;
1428 if (!jewels_movesavail(bj)) no_movesavail = true;
1429 } else {
1430 x = (x+1)%BJ_WIDTH;
1432 break;
1434 case JEWELS_DOWN: /* move cursor down */
1435 case (JEWELS_DOWN|BUTTON_REPEAT):
1436 if(selected) {
1437 bj->score += jewels_swapjewels(bj, x, y, SWAP_DOWN);
1438 selected = false;
1439 if (!jewels_movesavail(bj)) no_movesavail = true;
1440 } else {
1441 y = (y+1)%(BJ_HEIGHT-1);
1443 break;
1445 case JEWELS_UP: /* move cursor up */
1446 case (JEWELS_UP|BUTTON_REPEAT):
1447 if(selected) {
1448 bj->score += jewels_swapjewels(bj, x, y, SWAP_UP);
1449 selected = false;
1450 if (!jewels_movesavail(bj)) no_movesavail = true;
1451 } else {
1452 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1454 break;
1456 #ifdef JEWELS_SCROLLWHEEL
1457 case JEWELS_PREV: /* scroll backwards */
1458 case (JEWELS_PREV|BUTTON_REPEAT):
1459 if(!selected) {
1460 if(x == 0) {
1461 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1463 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1465 break;
1467 case JEWELS_NEXT: /* scroll forwards */
1468 case (JEWELS_NEXT|BUTTON_REPEAT):
1469 if(!selected) {
1470 if(x == BJ_WIDTH-1) {
1471 y = (y+1)%(BJ_HEIGHT-1);
1473 x = (x+1)%BJ_WIDTH;
1475 break;
1476 #endif
1478 case JEWELS_SELECT: /* toggle selected */
1479 selected = !selected;
1480 break;
1482 #ifdef JEWELS_RC_CANCEL
1483 case JEWELS_RC_CANCEL:
1484 #endif
1485 case JEWELS_CANCEL: /* end game */
1486 if (jewels_game_menu(bj, true)!=0)
1487 return 0;
1488 break;
1490 default:
1491 if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
1492 return PLUGIN_USB_CONNECTED;
1493 break;
1496 switch(bj->type) {
1497 case GAME_TYPE_NORMAL:
1498 if(bj->score >= LEVEL_PTS)
1499 jewels_nextlevel(bj);
1500 break;
1501 case GAME_TYPE_PUZZLE:
1502 if (jewels_puzzle_is_finished(bj)) {
1503 if (bj->level < NUM_PUZZLE_LEVELS) {
1504 jewels_nextlevel(bj);
1505 } else {
1506 rb->splash(2*HZ, "Congratulations!");
1507 rb->splash(2*HZ, "You have finished the game!");
1508 if (jewels_game_menu(bj, false)!=0) {
1509 return 0;
1512 break;
1516 if (no_movesavail) {
1517 switch(bj->type) {
1518 case GAME_TYPE_NORMAL:
1519 rb->splash(HZ*2, "Game Over!");
1520 rb->lcd_clear_display();
1521 bj->score += (bj->level-1)*LEVEL_PTS;
1522 position=highscore_update(bj->score, bj->level, "",
1523 highest, NUM_SCORES);
1524 if (position == 0)
1525 rb->splash(HZ*2, "New High Score");
1526 if (position != -1)
1527 highscore_show(position, highest, NUM_SCORES, true);
1528 break;
1529 case GAME_TYPE_PUZZLE:
1530 rb->splash(2*HZ, "Game Over");
1531 break;
1533 if (jewels_game_menu(bj, false)!=0) {
1534 return 0;
1540 /* this is the plugin entry point */
1541 enum plugin_status plugin_start(const void* parameter)
1543 (void)parameter;
1545 /* load high scores */
1546 highscore_load(HIGH_SCORE,highest,NUM_SCORES);
1548 rb->lcd_setfont(FONT_SYSFIXED);
1549 #if LCD_DEPTH > 1
1550 rb->lcd_set_backdrop(NULL);
1551 #endif
1553 struct game_context bj;
1554 bj.tmp_type = GAME_TYPE_NORMAL;
1555 jewels_main(&bj);
1556 highscore_save(HIGH_SCORE,highest,NUM_SCORES);
1557 rb->lcd_setfont(FONT_UI);
1559 return PLUGIN_OK;
1562 #endif /* HAVE_LCD_BITMAP */