add a way to quit without saving game.
[kugel-rb.git] / apps / plugins / jewels.c
blob08051c8e9e9c5cf50246c34545fc52c7ecb3dd9e
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"
29 #ifdef HAVE_LCD_BITMAP
31 PLUGIN_HEADER
33 /* button definitions */
34 #if CONFIG_KEYPAD == RECORDER_PAD
35 #define JEWELS_UP BUTTON_UP
36 #define JEWELS_DOWN BUTTON_DOWN
37 #define JEWELS_LEFT BUTTON_LEFT
38 #define JEWELS_RIGHT BUTTON_RIGHT
39 #define JEWELS_SELECT BUTTON_PLAY
40 #define JEWELS_CANCEL BUTTON_OFF
42 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
43 #define JEWELS_UP BUTTON_UP
44 #define JEWELS_DOWN BUTTON_DOWN
45 #define JEWELS_LEFT BUTTON_LEFT
46 #define JEWELS_RIGHT BUTTON_RIGHT
47 #define JEWELS_SELECT BUTTON_SELECT
48 #define JEWELS_CANCEL BUTTON_OFF
50 #elif CONFIG_KEYPAD == ONDIO_PAD
51 #define JEWELS_UP BUTTON_UP
52 #define JEWELS_DOWN BUTTON_DOWN
53 #define JEWELS_LEFT BUTTON_LEFT
54 #define JEWELS_RIGHT BUTTON_RIGHT
55 #define JEWELS_SELECT BUTTON_MENU
56 #define JEWELS_CANCEL BUTTON_OFF
58 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
59 #define JEWELS_UP BUTTON_UP
60 #define JEWELS_DOWN BUTTON_DOWN
61 #define JEWELS_LEFT BUTTON_LEFT
62 #define JEWELS_RIGHT BUTTON_RIGHT
63 #define JEWELS_SELECT BUTTON_SELECT
64 #define JEWELS_CANCEL BUTTON_OFF
65 #define JEWELS_RC_CANCEL BUTTON_RC_STOP
67 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
68 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
69 #define JEWELS_SCROLLWHEEL
70 #define JEWELS_UP BUTTON_MENU
71 #define JEWELS_DOWN BUTTON_PLAY
72 #define JEWELS_LEFT BUTTON_LEFT
73 #define JEWELS_RIGHT BUTTON_RIGHT
74 #define JEWELS_PREV BUTTON_SCROLL_BACK
75 #define JEWELS_NEXT BUTTON_SCROLL_FWD
76 #define JEWELS_SELECT BUTTON_SELECT
77 #define JEWELS_CANCEL (BUTTON_SELECT | BUTTON_MENU)
79 #elif (CONFIG_KEYPAD == IPOD_3G_PAD)
80 #define JEWELS_LEFT BUTTON_LEFT
81 #define JEWELS_RIGHT BUTTON_RIGHT
82 #define JEWELS_UP BUTTON_SCROLL_BACK
83 #define JEWELS_DOWN BUTTON_SCROLL_FWD
84 #define JEWELS_SELECT BUTTON_SELECT
85 #define JEWELS_CANCEL BUTTON_MENU
87 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
88 #define JEWELS_UP BUTTON_UP
89 #define JEWELS_DOWN BUTTON_DOWN
90 #define JEWELS_LEFT BUTTON_LEFT
91 #define JEWELS_RIGHT BUTTON_RIGHT
92 #define JEWELS_SELECT BUTTON_SELECT
93 #define JEWELS_CANCEL BUTTON_PLAY
95 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
96 #define JEWELS_UP BUTTON_UP
97 #define JEWELS_DOWN BUTTON_DOWN
98 #define JEWELS_LEFT BUTTON_LEFT
99 #define JEWELS_RIGHT BUTTON_RIGHT
100 #define JEWELS_SELECT BUTTON_SELECT
101 #define JEWELS_CANCEL BUTTON_POWER
103 #elif CONFIG_KEYPAD == GIGABEAT_PAD
104 #define JEWELS_UP BUTTON_UP
105 #define JEWELS_DOWN BUTTON_DOWN
106 #define JEWELS_LEFT BUTTON_LEFT
107 #define JEWELS_RIGHT BUTTON_RIGHT
108 #define JEWELS_SELECT BUTTON_SELECT
109 #define JEWELS_CANCEL BUTTON_POWER
111 #elif CONFIG_KEYPAD == SANSA_E200_PAD
112 #define JEWELS_SCROLLWHEEL
113 #define JEWELS_UP BUTTON_UP
114 #define JEWELS_DOWN BUTTON_DOWN
115 #define JEWELS_LEFT BUTTON_LEFT
116 #define JEWELS_RIGHT BUTTON_RIGHT
117 #define JEWELS_PREV BUTTON_SCROLL_BACK
118 #define JEWELS_NEXT BUTTON_SCROLL_FWD
119 #define JEWELS_SELECT BUTTON_SELECT
120 #define JEWELS_CANCEL BUTTON_POWER
122 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
123 #define JEWELS_SCROLLWHEEL
124 #define JEWELS_UP BUTTON_UP
125 #define JEWELS_DOWN BUTTON_DOWN
126 #define JEWELS_LEFT BUTTON_LEFT
127 #define JEWELS_RIGHT BUTTON_RIGHT
128 #define JEWELS_PREV BUTTON_SCROLL_BACK
129 #define JEWELS_NEXT BUTTON_SCROLL_FWD
130 #define JEWELS_SELECT BUTTON_SELECT
131 #define JEWELS_CANCEL (BUTTON_HOME|BUTTON_REPEAT)
133 #elif CONFIG_KEYPAD == SANSA_C200_PAD || \
134 CONFIG_KEYPAD == SANSA_CLIP_PAD || \
135 CONFIG_KEYPAD == SANSA_M200_PAD
136 #define JEWELS_UP BUTTON_UP
137 #define JEWELS_DOWN BUTTON_DOWN
138 #define JEWELS_LEFT BUTTON_LEFT
139 #define JEWELS_RIGHT BUTTON_RIGHT
140 #define JEWELS_SELECT BUTTON_SELECT
141 #define JEWELS_CANCEL BUTTON_POWER
143 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
144 #define JEWELS_UP BUTTON_SCROLL_UP
145 #define JEWELS_DOWN BUTTON_SCROLL_DOWN
146 #define JEWELS_LEFT BUTTON_LEFT
147 #define JEWELS_RIGHT BUTTON_RIGHT
148 #define JEWELS_SELECT BUTTON_PLAY
149 #define JEWELS_CANCEL BUTTON_POWER
151 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
152 #define JEWELS_UP BUTTON_UP
153 #define JEWELS_DOWN BUTTON_DOWN
154 #define JEWELS_LEFT BUTTON_LEFT
155 #define JEWELS_RIGHT BUTTON_RIGHT
156 #define JEWELS_SELECT BUTTON_SELECT
157 #define JEWELS_CANCEL BUTTON_BACK
159 #elif CONFIG_KEYPAD == MROBE100_PAD
160 #define JEWELS_UP BUTTON_UP
161 #define JEWELS_DOWN BUTTON_DOWN
162 #define JEWELS_LEFT BUTTON_LEFT
163 #define JEWELS_RIGHT BUTTON_RIGHT
164 #define JEWELS_SELECT BUTTON_SELECT
165 #define JEWELS_CANCEL BUTTON_POWER
167 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
168 #define JEWELS_UP BUTTON_RC_VOL_UP
169 #define JEWELS_DOWN BUTTON_RC_VOL_DOWN
170 #define JEWELS_LEFT BUTTON_RC_REW
171 #define JEWELS_RIGHT BUTTON_RC_FF
172 #define JEWELS_SELECT BUTTON_RC_PLAY
173 #define JEWELS_CANCEL BUTTON_RC_REC
174 #define JEWELS_RC_CANCEL BUTTON_REC
176 #elif CONFIG_KEYPAD == COWOND2_PAD
177 #define JEWELS_CANCEL BUTTON_POWER
179 #elif CONFIG_KEYPAD == IAUDIO67_PAD
180 #define JEWELS_UP BUTTON_STOP
181 #define JEWELS_DOWN BUTTON_PLAY
182 #define JEWELS_LEFT BUTTON_LEFT
183 #define JEWELS_RIGHT BUTTON_RIGHT
184 #define JEWELS_SELECT BUTTON_MENU
185 #define JEWELS_CANCEL BUTTON_POWER
187 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
188 #define JEWELS_UP BUTTON_UP
189 #define JEWELS_DOWN BUTTON_DOWN
190 #define JEWELS_LEFT BUTTON_LEFT
191 #define JEWELS_RIGHT BUTTON_RIGHT
192 #define JEWELS_SELECT BUTTON_SELECT
193 #define JEWELS_CANCEL BUTTON_BACK
195 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
196 #define JEWELS_UP BUTTON_UP
197 #define JEWELS_DOWN BUTTON_DOWN
198 #define JEWELS_LEFT BUTTON_LEFT
199 #define JEWELS_RIGHT BUTTON_RIGHT
200 #define JEWELS_SELECT BUTTON_SELECT
201 #define JEWELS_CANCEL BUTTON_POWER
203 #elif CONFIG_KEYPAD == ONDAVX747_PAD || \
204 CONFIG_KEYPAD == MROBE500_PAD
205 #define JEWELS_CANCEL BUTTON_POWER
207 #else
208 #error No keymap defined!
209 #endif
211 #ifdef HAVE_TOUCHSCREEN
212 #ifndef JEWELS_UP
213 #define JEWELS_UP BUTTON_TOPMIDDLE
214 #endif
215 #ifndef JEWELS_DOWN
216 #define JEWELS_DOWN BUTTON_BOTTOMMIDDLE
217 #endif
218 #ifndef JEWELS_LEFT
219 #define JEWELS_LEFT BUTTON_MIDLEFT
220 #endif
221 #ifndef JEWELS_RIGHT
222 #define JEWELS_RIGHT BUTTON_MIDRIGHT
223 #endif
224 #ifndef JEWELS_SELECT
225 #define JEWELS_SELECT BUTTON_CENTER
226 #endif
227 #ifndef JEWELS_CANCEL
228 #define JEWELS_CANCEL BUTTON_TOPLEFT
229 #endif
230 #endif
232 /* use 30x30 tiles (iPod Video, Gigabeat, Onda VX747) */
233 #if (LCD_HEIGHT == 240) && (LCD_WIDTH == 320) || \
234 ((LCD_HEIGHT == 320) && (LCD_WIDTH == 240)) || \
235 ((LCD_HEIGHT == 400) && (LCD_WIDTH == 240))
236 #define TILE_WIDTH 30
237 #define TILE_HEIGHT 30
238 #define YOFS 0
239 #define NUM_SCORES 10
241 /* use 22x22 tiles (H300, iPod Color) */
242 #elif ((LCD_HEIGHT == 176) && (LCD_WIDTH == 220)) || \
243 ((LCD_HEIGHT == 220) && (LCD_WIDTH == 176))
244 #define TILE_WIDTH 22
245 #define TILE_HEIGHT 22
246 #define YOFS 0
247 #define NUM_SCORES 5
249 /* use 16x16 tiles (iPod Nano) */
250 #elif (LCD_HEIGHT == 132) && (LCD_WIDTH == 176)
251 #define TILE_WIDTH 16
252 #define TILE_HEIGHT 16
253 #define YOFS 4
254 #define NUM_SCORES 5
256 /* use 16x16 tiles (H100, iAudio X5, iPod 3G, iPod 4G grayscale) */
257 #elif (LCD_HEIGHT == 128) && (LCD_WIDTH == 160)
258 #define TILE_WIDTH 16
259 #define TILE_HEIGHT 16
260 #define YOFS 0
261 #define NUM_SCORES 5
263 /* use 14x14 tiles (H10 5/6 GB) */
264 #elif (LCD_HEIGHT == 128) && (LCD_WIDTH == 128)
265 #define TILE_WIDTH 14
266 #define TILE_HEIGHT 14
267 #define YOFS 0
268 #define NUM_SCORES 5
270 /* use 13x13 tiles (iPod Mini) */
271 #elif (LCD_HEIGHT == 110) && (LCD_WIDTH == 138)
272 #define TILE_WIDTH 13
273 #define TILE_HEIGHT 13
274 #define YOFS 6
275 #define NUM_SCORES 5
277 /* use 12x12 tiles (iAudio M3) */
278 #elif (LCD_HEIGHT == 96) && (LCD_WIDTH == 128)
279 #define TILE_WIDTH 12
280 #define TILE_HEIGHT 12
281 #define YOFS 0
282 #define NUM_SCORES 5
284 /* use 10x10 tiles (Sansa c200) */
285 #elif (LCD_HEIGHT == 80) && (LCD_WIDTH == 132)
286 #define TILE_WIDTH 10
287 #define TILE_HEIGHT 10
288 #define YOFS 0
289 #define NUM_SCORES 5
291 /* use 10x8 tiles (iFP 700) */
292 #elif (LCD_HEIGHT == 64) && (LCD_WIDTH == 128)
293 #define TILE_WIDTH 10
294 #define TILE_HEIGHT 8
295 #define YOFS 0
296 #define NUM_SCORES 5
298 /* use 10x8 tiles (Recorder, Ondio) */
299 #elif (LCD_HEIGHT == 64) && (LCD_WIDTH == 112)
300 #define TILE_WIDTH 10
301 #define TILE_HEIGHT 8
302 #define YOFS 0
303 #define NUM_SCORES 5
305 #else
306 #error JEWELS: Unsupported LCD
307 #endif
309 /* swap directions */
310 #define SWAP_UP 0
311 #define SWAP_RIGHT 1
312 #define SWAP_DOWN 2
313 #define SWAP_LEFT 3
315 /* play board dimension */
316 #define BJ_HEIGHT 9
317 #define BJ_WIDTH 8
319 /* next level threshold */
320 #define LEVEL_PTS 100
322 /* animation frame rate */
323 #define MAX_FPS 20
325 /* text margin */
326 #define MARGIN 5
328 /* Game types */
329 enum game_type {
330 GAME_TYPE_NORMAL,
331 GAME_TYPE_PUZZLE
334 /* external bitmaps */
335 extern const fb_data jewels[];
337 /* tile background colors */
338 #ifdef HAVE_LCD_COLOR
339 static const unsigned jewels_bkgd[2] = {
340 LCD_RGBPACK(104, 63, 63),
341 LCD_RGBPACK(83, 44, 44)
343 #endif
345 /* the tile struct
346 * type is the jewel number 0-7
347 * falling if the jewel is falling
348 * delete marks the jewel for deletion
350 struct tile {
351 int type;
352 bool falling;
353 bool delete;
356 /* the game context struct
357 * score is the current level score
358 * segments is the number of cleared segments in the current run
359 * level is the current level
360 * tmp_type is the select type in the menu
361 * type is the game type (normal or puzzle)
362 * playboard is the game playing board (first row is hidden)
363 * num_jewels is the number of different jewels to use
365 struct game_context {
366 unsigned int score;
367 unsigned int segments;
368 unsigned int level;
369 unsigned int type;
370 unsigned int tmp_type;
371 struct tile playboard[BJ_HEIGHT][BJ_WIDTH];
372 unsigned int num_jewels;
375 #define MAX_NUM_JEWELS 7
377 #define MAX_PUZZLE_TILES 4
378 #define NUM_PUZZLE_LEVELS 10
380 struct puzzle_tile {
381 int x;
382 int y;
383 int tile_type;
386 struct puzzle_level {
387 unsigned int num_jewels;
388 unsigned int num_tiles;
389 struct puzzle_tile tiles[MAX_PUZZLE_TILES];
392 #define PUZZLE_TILE_UP 1
393 #define PUZZLE_TILE_DOWN 2
394 #define PUZZLE_TILE_LEFT 4
395 #define PUZZLE_TILE_RIGHT 8
397 struct puzzle_level puzzle_levels[NUM_PUZZLE_LEVELS] = {
398 { 5, 2, { {3, 3, PUZZLE_TILE_RIGHT},
399 {4, 2, PUZZLE_TILE_LEFT} } },
400 { 5, 2, { {3, 2, PUZZLE_TILE_DOWN},
401 {3, 4, PUZZLE_TILE_UP} } },
402 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
403 {3, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
404 {3, 6, PUZZLE_TILE_UP} } },
405 { 6, 3, { {3, 2, PUZZLE_TILE_RIGHT},
406 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
407 {5, 4, PUZZLE_TILE_LEFT} } },
408 { 6, 2, { {3, 4, PUZZLE_TILE_RIGHT},
409 {4, 2, PUZZLE_TILE_LEFT} } },
410 { 6, 2, { {3, 2, PUZZLE_TILE_DOWN},
411 {4, 4, PUZZLE_TILE_UP} } },
412 { 7, 4, { {3, 2, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
413 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
414 {3, 4, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
415 {4, 4, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
416 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
417 {4, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
418 {3, 6, PUZZLE_TILE_UP} } },
419 { 7, 3, { {2, 2, PUZZLE_TILE_RIGHT},
420 {4, 1, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
421 {5, 4, PUZZLE_TILE_LEFT} } },
422 { 7, 4, { {3, 0, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
423 {5, 0, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
424 {2, 7, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
425 {4, 7, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
428 #define SAVE_FILE PLUGIN_GAMES_DIR "/jewels.save"
430 #define HIGH_SCORE PLUGIN_GAMES_DIR "/jewels.score"
431 struct highscore highest[NUM_SCORES];
432 bool highest_updated = false;
435 /*****************************************************************************
436 * jewels_setcolors() set the foreground and background colors.
437 ******************************************************************************/
438 static inline void jewels_setcolors(void) {
439 #ifdef HAVE_LCD_COLOR
440 rb->lcd_set_background(LCD_RGBPACK(49, 26, 26));
441 rb->lcd_set_foreground(LCD_RGBPACK(210, 181, 181));
442 #endif
445 /*****************************************************************************
446 * jewels_loadgame() loads the saved game and returns load success.
447 ******************************************************************************/
448 static bool jewels_loadgame(struct game_context* bj)
450 int fd;
451 bool loaded = false;
453 /* open game file */
454 fd = rb->open(SAVE_FILE, O_RDONLY);
455 if(fd < 0) return loaded;
457 /* read in saved game */
458 while(true) {
459 if(rb->read(fd, &bj->tmp_type, sizeof(bj->tmp_type)) <= 0) break;
460 if(rb->read(fd, &bj->type, sizeof(bj->type)) <= 0) break;
461 if(rb->read(fd, &bj->score, sizeof(bj->score)) <= 0) break;
462 if(rb->read(fd, &bj->level, sizeof(bj->level)) <= 0) break;
463 if(rb->read(fd, &bj->segments, sizeof(bj->segments)) <= 0) break;
464 if(rb->read(fd, &bj->num_jewels, sizeof(bj->num_jewels)) <= 0) break;
465 if(rb->read(fd, bj->playboard, sizeof(bj->playboard)) <= 0) break;
466 loaded = true;
467 break;
470 rb->close(fd);
472 /* delete saved file */
473 rb->remove(SAVE_FILE);
474 return loaded;
477 /*****************************************************************************
478 * jewels_savegame() saves the current game state.
479 ******************************************************************************/
480 static void jewels_savegame(struct game_context* bj)
482 int fd;
483 /* write out the game state to the save file */
484 fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT);
485 rb->write(fd, &bj->tmp_type, sizeof(bj->tmp_type));
486 rb->write(fd, &bj->type, sizeof(bj->type));
487 rb->write(fd, &bj->score, sizeof(bj->score));
488 rb->write(fd, &bj->level, sizeof(bj->level));
489 rb->write(fd, &bj->segments, sizeof(bj->segments));
490 rb->write(fd, &bj->num_jewels, sizeof(bj->num_jewels));
491 rb->write(fd, bj->playboard, sizeof(bj->playboard));
492 rb->close(fd);
495 /*****************************************************************************
496 * jewels_drawboard() redraws the entire game board.
497 ******************************************************************************/
498 static void jewels_drawboard(struct game_context* bj) {
499 int i, j;
500 int w, h;
501 unsigned int tempscore;
502 char *title = "Level";
503 char str[10];
505 tempscore = (bj->score>LEVEL_PTS ? LEVEL_PTS : bj->score);
507 /* clear screen */
508 rb->lcd_clear_display();
510 /* dispay playing board */
511 for(i=0; i<BJ_HEIGHT-1; i++){
512 for(j=0; j<BJ_WIDTH; j++){
513 #ifdef HAVE_LCD_COLOR
514 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
515 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
516 TILE_WIDTH, TILE_HEIGHT);
517 rb->lcd_bitmap_transparent_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 #else
522 rb->lcd_bitmap_part(jewels,
523 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
524 TILE_WIDTH, j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
525 TILE_WIDTH, TILE_HEIGHT);
526 #endif
530 #if LCD_WIDTH > LCD_HEIGHT /* horizontal layout */
532 /* draw separator lines */
533 jewels_setcolors();
534 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, LCD_HEIGHT-1);
535 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, 18);
536 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, LCD_HEIGHT-10);
538 /* draw progress bar */
539 #ifdef HAVE_LCD_COLOR
540 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
541 #endif
542 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
543 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
544 tempscore/LEVEL_PTS),
545 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
546 ((LCD_HEIGHT-10)-18)*tempscore/LEVEL_PTS);
547 #ifdef HAVE_LCD_COLOR
548 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
549 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
550 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
551 tempscore/LEVEL_PTS)+1,
552 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
553 ((LCD_HEIGHT-10)-18)*tempscore/LEVEL_PTS-1);
554 jewels_setcolors();
555 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
556 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
557 tempscore/LEVEL_PTS),
558 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
559 ((LCD_HEIGHT-10)-18)*tempscore/LEVEL_PTS+1);
560 #endif
562 /* print text */
563 rb->lcd_getstringsize(title, &w, &h);
564 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 1, title);
566 rb->snprintf(str, 4, "%d", bj->level);
567 rb->lcd_getstringsize(str, &w, &h);
568 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 10, str);
570 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
571 rb->lcd_getstringsize(str, &w, &h);
572 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2,
573 LCD_HEIGHT-8, str);
575 #elif LCD_WIDTH < LCD_HEIGHT /* vertical layout */
577 /* draw separator lines */
578 jewels_setcolors();
579 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
580 rb->lcd_hline(0, LCD_WIDTH-1, LCD_HEIGHT-14);
581 rb->lcd_vline(LCD_WIDTH/2, LCD_HEIGHT-14, LCD_HEIGHT-1);
583 /* draw progress bar */
584 #ifdef HAVE_LCD_COLOR
585 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
586 #endif
587 rb->lcd_fillrect(0, (8*TILE_HEIGHT+YOFS)
588 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
589 LCD_WIDTH*tempscore/LEVEL_PTS,
590 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
591 #ifdef HAVE_LCD_COLOR
592 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
593 rb->lcd_drawrect(1, (8*TILE_HEIGHT+YOFS)
594 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4+1,
595 LCD_WIDTH*tempscore/LEVEL_PTS-1,
596 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2-2);
597 jewels_setcolors();
598 rb->lcd_drawrect(0, (8*TILE_HEIGHT+YOFS)
599 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
600 LCD_WIDTH*tempscore/LEVEL_PTS+1,
601 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
602 #endif
604 /* print text */
605 rb->snprintf(str, 10, "%s %d", title, bj->level);
606 rb->lcd_putsxy(1, LCD_HEIGHT-10, str);
608 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
609 rb->lcd_getstringsize(str, &w, &h);
610 rb->lcd_putsxy((LCD_WIDTH-2)-w, LCD_HEIGHT-10, str);
612 #else /* square layout */
614 /* draw separator lines */
615 jewels_setcolors();
616 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
617 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, 8*TILE_HEIGHT+YOFS);
618 rb->lcd_vline(LCD_WIDTH/2, 8*TILE_HEIGHT+YOFS, LCD_HEIGHT-1);
620 /* draw progress bar */
621 #ifdef HAVE_LCD_COLOR
622 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
623 #endif
624 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
625 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
626 *tempscore/LEVEL_PTS,
627 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
628 (8*TILE_HEIGHT+YOFS)*tempscore/LEVEL_PTS);
629 #ifdef HAVE_LCD_COLOR
630 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
631 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
632 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
633 *tempscore/LEVEL_PTS+1,
634 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
635 (8*TILE_HEIGHT+YOFS)*tempscore/LEVEL_PTS-1);
636 jewels_setcolors();
637 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
638 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
639 *tempscore/LEVEL_PTS,
640 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
641 (8*TILE_HEIGHT+YOFS)*tempscore/LEVEL_PTS+1);
642 #endif
644 /* print text */
645 rb->snprintf(str, 10, "%s %d", title, bj->level);
646 rb->lcd_putsxy(1, LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
648 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
649 rb->lcd_getstringsize(str, &w, &h);
650 rb->lcd_putsxy((LCD_WIDTH-2)-w,
651 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 void jewels_show_highscores(int position)
1263 int i, w, h;
1264 char str[30];
1266 #ifdef HAVE_LCD_COLOR
1267 rb->lcd_set_background(LCD_BLACK);
1268 rb->lcd_set_foreground(LCD_WHITE);
1269 #endif
1270 rb->button_clear_queue();
1271 rb->lcd_clear_display();
1273 rb->lcd_setfont(FONT_UI);
1274 rb->lcd_getstringsize("High Scores", &w, &h);
1275 /* check wether it fits on screen */
1276 if ((4*h + h*(NUM_SCORES-1) + MARGIN) > LCD_HEIGHT) {
1277 rb->lcd_setfont(FONT_SYSFIXED);
1278 rb->lcd_getstringsize("High Scores", &w, &h);
1280 rb->lcd_putsxy(LCD_WIDTH/2-w/2, MARGIN, "High Scores");
1281 rb->lcd_putsxy(LCD_WIDTH/4-w/4,2*h, "Score");
1282 rb->lcd_putsxy(LCD_WIDTH*3/4-w/4,2*h, "Level");
1284 for (i = 0; i<NUM_SCORES; i++)
1286 #ifdef HAVE_LCD_COLOR
1287 if (i == position) {
1288 rb->lcd_set_foreground(LCD_RGBPACK(245,0,0));
1290 #endif
1291 rb->snprintf (str, sizeof (str), "%d)", i+1);
1292 rb->lcd_putsxy (MARGIN,3*h + h*i, str);
1293 rb->snprintf (str, sizeof (str), "%d", highest[i].score);
1294 rb->lcd_putsxy (LCD_WIDTH/4-w/4,3*h + h*i, str);
1295 rb->snprintf (str, sizeof (str), "%d", highest[i].level);
1296 rb->lcd_putsxy (LCD_WIDTH*3/4-w/4,3*h + h*i, str);
1297 if(i == position) {
1298 #ifdef HAVE_LCD_COLOR
1299 rb->lcd_set_foreground(LCD_WHITE);
1300 #else
1301 rb->lcd_hline(MARGIN, LCD_WIDTH-MARGIN, 3*h + h*(i+1));
1302 #endif
1305 rb->lcd_update();
1306 rb->button_get(true);
1307 rb->lcd_setfont(FONT_SYSFIXED);
1310 static int jewels_help(void)
1312 rb->lcd_setfont(FONT_UI);
1313 #define WORDS (sizeof help_text / sizeof (char*))
1314 static char *help_text[] = {
1315 "Jewels", "", "Aim", "", "Swap", "pairs", "of", "jewels", "to",
1316 "form", "connected", "segments", "of", "three", "or", "more", "of",
1317 "the", "same", "type.", "", "The", "goal", "of", "the", "game",
1318 "is", "to", "score", "as", "many", "points", "as", "possible",
1319 "before", "running", "out", "of", "available", "moves."
1321 static struct style_text formation[]={
1322 { 0, TEXT_CENTER|TEXT_UNDERLINE },
1323 { 2, C_RED }
1326 #ifdef HAVE_LCD_COLOR
1327 rb->lcd_set_background(LCD_BLACK);
1328 rb->lcd_set_foreground(LCD_WHITE);
1329 #endif
1330 if (display_text(WORDS, help_text, formation, NULL)==PLUGIN_USB_CONNECTED)
1331 return PLUGIN_USB_CONNECTED;
1332 int button;
1333 do {
1334 button = rb->button_get(true);
1335 if (button == SYS_USB_CONNECTED) {
1336 return PLUGIN_USB_CONNECTED;
1338 } while( ( button == BUTTON_NONE )
1339 || ( button & (BUTTON_REL|BUTTON_REPEAT) ) );
1340 rb->lcd_setfont(FONT_SYSFIXED);
1342 return 0;
1345 static bool _ingame;
1346 static int jewels_menu_cb(int action, const struct menu_item_ex *this_item)
1348 int i = ((intptr_t)this_item);
1349 if(action == ACTION_REQUEST_MENUITEM
1350 && !_ingame && (i==0 || i==6))
1351 return ACTION_EXIT_MENUITEM;
1352 return action;
1354 /*****************************************************************************
1355 * jewels_game_menu() shows the game menu.
1356 ******************************************************************************/
1357 static int jewels_game_menu(struct game_context* bj, bool ingame)
1359 rb->button_clear_queue();
1360 int choice = 0;
1362 _ingame = ingame;
1364 static struct opt_items mode[] = {
1365 { "Normal", -1 },
1366 { "Puzzle", -1 },
1369 MENUITEM_STRINGLIST (main_menu, "Jewels Menu", jewels_menu_cb,
1370 "Resume Game",
1371 "Start New Game",
1372 "Mode",
1373 "Help",
1374 "High Score",
1375 "Playback Control",
1376 "Save and Quit",
1377 "Quit");
1379 while (1) {
1380 switch (rb->do_menu(&main_menu, &choice, NULL, false)) {
1381 case 0:
1382 jewels_setcolors();
1383 return 0;
1384 case 1:
1385 jewels_init(bj);
1386 return 0;
1387 case 2:
1388 rb->set_option("Mode", &bj->tmp_type, INT, mode, 2, NULL);
1389 break;
1390 case 3:
1391 jewels_help();
1392 break;
1393 case 4:
1394 jewels_show_highscores(NUM_SCORES);
1395 break;
1396 case 5:
1397 playback_control(NULL);
1398 break;
1399 case 6:
1400 if (ingame) {
1401 rb->splash(HZ*1, "Saving game ...");
1402 jewels_savegame(bj);
1404 return 1;
1405 case 7:
1406 return 1;
1407 case MENU_ATTACHED_USB:
1408 return 1;
1409 default:
1410 break;
1415 static int jewels_main(struct game_context* bj) {
1416 int button;
1417 int position;
1418 bool selected = false;
1419 bool no_movesavail;
1420 int x=0, y=0;
1422 bool loaded = jewels_loadgame(bj);
1423 if (jewels_game_menu(bj, loaded)!=0)
1424 return 0;
1426 while(true) {
1427 no_movesavail = false;
1429 /* refresh the board */
1430 jewels_drawboard(bj);
1432 /* display the cursor */
1433 if(selected) {
1434 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1435 rb->lcd_fillrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1436 TILE_WIDTH, TILE_HEIGHT);
1437 rb->lcd_set_drawmode(DRMODE_SOLID);
1438 } else {
1439 rb->lcd_drawrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1440 TILE_WIDTH, TILE_HEIGHT);
1442 rb->lcd_update_rect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1443 TILE_WIDTH, TILE_HEIGHT);
1445 /* handle game button presses */
1446 rb->yield();
1447 button = rb->button_get(true);
1448 switch(button){
1449 case JEWELS_LEFT: /* move cursor left */
1450 case (JEWELS_LEFT|BUTTON_REPEAT):
1451 if(selected) {
1452 bj->score += jewels_swapjewels(bj, x, y, SWAP_LEFT);
1453 selected = false;
1454 if (!jewels_movesavail(bj)) no_movesavail = true;
1455 } else {
1456 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1458 break;
1460 case JEWELS_RIGHT: /* move cursor right */
1461 case (JEWELS_RIGHT|BUTTON_REPEAT):
1462 if(selected) {
1463 bj->score += jewels_swapjewels(bj, x, y, SWAP_RIGHT);
1464 selected = false;
1465 if (!jewels_movesavail(bj)) no_movesavail = true;
1466 } else {
1467 x = (x+1)%BJ_WIDTH;
1469 break;
1471 case JEWELS_DOWN: /* move cursor down */
1472 case (JEWELS_DOWN|BUTTON_REPEAT):
1473 if(selected) {
1474 bj->score += jewels_swapjewels(bj, x, y, SWAP_DOWN);
1475 selected = false;
1476 if (!jewels_movesavail(bj)) no_movesavail = true;
1477 } else {
1478 y = (y+1)%(BJ_HEIGHT-1);
1480 break;
1482 case JEWELS_UP: /* move cursor up */
1483 case (JEWELS_UP|BUTTON_REPEAT):
1484 if(selected) {
1485 bj->score += jewels_swapjewels(bj, x, y, SWAP_UP);
1486 selected = false;
1487 if (!jewels_movesavail(bj)) no_movesavail = true;
1488 } else {
1489 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1491 break;
1493 #ifdef JEWELS_SCROLLWHEEL
1494 case JEWELS_PREV: /* scroll backwards */
1495 case (JEWELS_PREV|BUTTON_REPEAT):
1496 if(!selected) {
1497 if(x == 0) {
1498 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1500 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1502 break;
1504 case JEWELS_NEXT: /* scroll forwards */
1505 case (JEWELS_NEXT|BUTTON_REPEAT):
1506 if(!selected) {
1507 if(x == BJ_WIDTH-1) {
1508 y = (y+1)%(BJ_HEIGHT-1);
1510 x = (x+1)%BJ_WIDTH;
1512 break;
1513 #endif
1515 case JEWELS_SELECT: /* toggle selected */
1516 selected = !selected;
1517 break;
1519 #ifdef JEWELS_RC_CANCEL
1520 case JEWELS_RC_CANCEL:
1521 #endif
1522 case JEWELS_CANCEL: /* end game */
1523 if (jewels_game_menu(bj, true)!=0)
1524 return 0;
1525 break;
1527 default:
1528 if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
1529 return PLUGIN_USB_CONNECTED;
1530 break;
1533 switch(bj->type) {
1534 case GAME_TYPE_NORMAL:
1535 if(bj->score >= LEVEL_PTS)
1536 jewels_nextlevel(bj);
1537 break;
1538 case GAME_TYPE_PUZZLE:
1539 if (jewels_puzzle_is_finished(bj)) {
1540 if (bj->level < NUM_PUZZLE_LEVELS) {
1541 jewels_nextlevel(bj);
1542 } else {
1543 rb->splash(2*HZ, "Congratulations!");
1544 rb->splash(2*HZ, "You have finished the game!");
1545 if (jewels_game_menu(bj, false)!=0) {
1546 return 0;
1549 break;
1553 if (no_movesavail) {
1554 switch(bj->type) {
1555 case GAME_TYPE_NORMAL:
1556 rb->splash(HZ*2, "Game Over!");
1557 rb->lcd_clear_display();
1558 if (highscore_would_update(bj->score, highest,
1559 NUM_SCORES)) {
1560 position=highscore_update(bj->score,
1561 bj->level, "",
1562 highest,NUM_SCORES);
1563 highest_updated = true;
1564 if (position == 0) {
1565 rb->splash(HZ*2, "New High Score");
1567 jewels_show_highscores(position);
1569 break;
1570 case GAME_TYPE_PUZZLE:
1571 rb->splash(2*HZ, "Game Over");
1572 break;
1574 if (jewels_game_menu(bj, false)!=0) {
1575 return 0;
1581 /* this is the plugin entry point */
1582 enum plugin_status plugin_start(const void* parameter)
1584 (void)parameter;
1586 /* load high scores */
1587 highscore_load(HIGH_SCORE,highest,NUM_SCORES);
1588 highest_updated = false;
1590 rb->lcd_setfont(FONT_SYSFIXED);
1591 #if LCD_DEPTH > 1
1592 rb->lcd_set_backdrop(NULL);
1593 #endif
1595 struct game_context bj;
1596 bj.tmp_type = GAME_TYPE_NORMAL;
1597 jewels_main(&bj);
1598 if(highest_updated)
1599 highscore_save(HIGH_SCORE,highest,NUM_SCORES);
1600 rb->lcd_setfont(FONT_UI);
1602 return PLUGIN_OK;
1605 #endif /* HAVE_LCD_BITMAP */