fix wrong score recording.
[kugel-rb.git] / apps / plugins / jewels.c
blob6c436cfb245138951ef51c372786dbb205ffe029
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
41 #define HK_SELECT "PLAY"
42 #define HK_CANCEL "OFF"
44 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
45 #define JEWELS_UP BUTTON_UP
46 #define JEWELS_DOWN BUTTON_DOWN
47 #define JEWELS_LEFT BUTTON_LEFT
48 #define JEWELS_RIGHT BUTTON_RIGHT
49 #define JEWELS_SELECT BUTTON_SELECT
50 #define JEWELS_CANCEL BUTTON_OFF
51 #define HK_SELECT "SELECT"
52 #define HK_CANCEL "OFF"
54 #elif CONFIG_KEYPAD == ONDIO_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_MENU
60 #define JEWELS_CANCEL BUTTON_OFF
61 #define HK_SELECT "MENU"
62 #define HK_CANCEL "OFF"
64 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
65 #define JEWELS_UP BUTTON_UP
66 #define JEWELS_DOWN BUTTON_DOWN
67 #define JEWELS_LEFT BUTTON_LEFT
68 #define JEWELS_RIGHT BUTTON_RIGHT
69 #define JEWELS_SELECT BUTTON_SELECT
70 #define JEWELS_CANCEL BUTTON_OFF
71 #define JEWELS_RC_CANCEL BUTTON_RC_STOP
72 #define HK_SELECT "SELECT"
73 #define HK_CANCEL "OFF"
75 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
76 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
77 #define JEWELS_SCROLLWHEEL
78 #define JEWELS_UP BUTTON_MENU
79 #define JEWELS_DOWN BUTTON_PLAY
80 #define JEWELS_LEFT BUTTON_LEFT
81 #define JEWELS_RIGHT BUTTON_RIGHT
82 #define JEWELS_PREV BUTTON_SCROLL_BACK
83 #define JEWELS_NEXT BUTTON_SCROLL_FWD
84 #define JEWELS_SELECT BUTTON_SELECT
85 #define JEWELS_CANCEL (BUTTON_SELECT | BUTTON_MENU)
86 #define HK_SELECT "SELECT"
87 #define HK_CANCEL "SEL + MENU"
89 #elif (CONFIG_KEYPAD == IPOD_3G_PAD)
90 #define JEWELS_LEFT BUTTON_LEFT
91 #define JEWELS_RIGHT BUTTON_RIGHT
92 #define JEWELS_UP BUTTON_SCROLL_BACK
93 #define JEWELS_DOWN BUTTON_SCROLL_FWD
94 #define JEWELS_SELECT BUTTON_SELECT
95 #define JEWELS_CANCEL BUTTON_MENU
96 #define HK_SELECT "SELECT"
97 #define HK_CANCEL "MENU"
99 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_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_PLAY
106 #define HK_SELECT "SELECT"
107 #define HK_CANCEL "PLAY"
109 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
110 #define JEWELS_UP BUTTON_UP
111 #define JEWELS_DOWN BUTTON_DOWN
112 #define JEWELS_LEFT BUTTON_LEFT
113 #define JEWELS_RIGHT BUTTON_RIGHT
114 #define JEWELS_SELECT BUTTON_SELECT
115 #define JEWELS_CANCEL BUTTON_POWER
116 #define HK_SELECT "SELECT"
117 #define HK_CANCEL "POWER"
119 #elif CONFIG_KEYPAD == GIGABEAT_PAD
120 #define JEWELS_UP BUTTON_UP
121 #define JEWELS_DOWN BUTTON_DOWN
122 #define JEWELS_LEFT BUTTON_LEFT
123 #define JEWELS_RIGHT BUTTON_RIGHT
124 #define JEWELS_SELECT BUTTON_SELECT
125 #define JEWELS_CANCEL BUTTON_POWER
126 #define HK_SELECT "SELECT"
127 #define HK_CANCEL "POWER"
129 #elif CONFIG_KEYPAD == SANSA_E200_PAD
130 #define JEWELS_SCROLLWHEEL
131 #define JEWELS_UP BUTTON_UP
132 #define JEWELS_DOWN BUTTON_DOWN
133 #define JEWELS_LEFT BUTTON_LEFT
134 #define JEWELS_RIGHT BUTTON_RIGHT
135 #define JEWELS_PREV BUTTON_SCROLL_BACK
136 #define JEWELS_NEXT BUTTON_SCROLL_FWD
137 #define JEWELS_SELECT BUTTON_SELECT
138 #define JEWELS_CANCEL BUTTON_POWER
139 #define HK_SELECT "SELECT"
140 #define HK_CANCEL "POWER"
142 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
143 #define JEWELS_SCROLLWHEEL
144 #define JEWELS_UP BUTTON_UP
145 #define JEWELS_DOWN BUTTON_DOWN
146 #define JEWELS_LEFT BUTTON_LEFT
147 #define JEWELS_RIGHT BUTTON_RIGHT
148 #define JEWELS_PREV BUTTON_SCROLL_BACK
149 #define JEWELS_NEXT BUTTON_SCROLL_FWD
150 #define JEWELS_SELECT BUTTON_SELECT
151 #define JEWELS_CANCEL (BUTTON_HOME|BUTTON_REPEAT)
152 #define HK_SELECT "SELECT"
153 #define HK_CANCEL "HOME"
155 #elif CONFIG_KEYPAD == SANSA_C200_PAD || \
156 CONFIG_KEYPAD == SANSA_CLIP_PAD || \
157 CONFIG_KEYPAD == SANSA_M200_PAD
158 #define JEWELS_UP BUTTON_UP
159 #define JEWELS_DOWN BUTTON_DOWN
160 #define JEWELS_LEFT BUTTON_LEFT
161 #define JEWELS_RIGHT BUTTON_RIGHT
162 #define JEWELS_SELECT BUTTON_SELECT
163 #define JEWELS_CANCEL BUTTON_POWER
164 #define HK_SELECT "SELECT"
165 #define HK_CANCEL "POWER"
167 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
168 #define JEWELS_UP BUTTON_SCROLL_UP
169 #define JEWELS_DOWN BUTTON_SCROLL_DOWN
170 #define JEWELS_LEFT BUTTON_LEFT
171 #define JEWELS_RIGHT BUTTON_RIGHT
172 #define JEWELS_SELECT BUTTON_PLAY
173 #define JEWELS_CANCEL BUTTON_POWER
174 #define HK_SELECT "PLAY"
175 #define HK_CANCEL "POWER"
177 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
178 #define JEWELS_UP BUTTON_UP
179 #define JEWELS_DOWN BUTTON_DOWN
180 #define JEWELS_LEFT BUTTON_LEFT
181 #define JEWELS_RIGHT BUTTON_RIGHT
182 #define JEWELS_SELECT BUTTON_SELECT
183 #define JEWELS_CANCEL BUTTON_BACK
184 #define HK_SELECT "SELECT"
185 #define HK_CANCEL "BACK"
187 #elif CONFIG_KEYPAD == MROBE100_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_POWER
194 #define HK_SELECT "SELECT"
195 #define HK_CANCEL "POWER"
197 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
198 #define JEWELS_UP BUTTON_RC_VOL_UP
199 #define JEWELS_DOWN BUTTON_RC_VOL_DOWN
200 #define JEWELS_LEFT BUTTON_RC_REW
201 #define JEWELS_RIGHT BUTTON_RC_FF
202 #define JEWELS_SELECT BUTTON_RC_PLAY
203 #define JEWELS_CANCEL BUTTON_RC_REC
204 #define HK_SELECT "PLAY"
205 #define HK_CANCEL "REC"
207 #define JEWELS_RC_CANCEL BUTTON_REC
209 #elif CONFIG_KEYPAD == COWOND2_PAD
210 #define JEWELS_CANCEL BUTTON_POWER
211 #define HK_CANCEL "POWER"
213 #elif CONFIG_KEYPAD == IAUDIO67_PAD
214 #define JEWELS_UP BUTTON_STOP
215 #define JEWELS_DOWN BUTTON_PLAY
216 #define JEWELS_LEFT BUTTON_LEFT
217 #define JEWELS_RIGHT BUTTON_RIGHT
218 #define JEWELS_SELECT BUTTON_MENU
219 #define JEWELS_CANCEL BUTTON_POWER
220 #define HK_SELECT "MENU"
221 #define HK_CANCEL "POWER"
223 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
224 #define JEWELS_UP BUTTON_UP
225 #define JEWELS_DOWN BUTTON_DOWN
226 #define JEWELS_LEFT BUTTON_LEFT
227 #define JEWELS_RIGHT BUTTON_RIGHT
228 #define JEWELS_SELECT BUTTON_SELECT
229 #define JEWELS_CANCEL BUTTON_BACK
230 #define HK_SELECT "MIDDLE"
231 #define HK_CANCEL "BACK"
233 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
234 #define JEWELS_UP BUTTON_UP
235 #define JEWELS_DOWN BUTTON_DOWN
236 #define JEWELS_LEFT BUTTON_LEFT
237 #define JEWELS_RIGHT BUTTON_RIGHT
238 #define JEWELS_SELECT BUTTON_SELECT
239 #define JEWELS_CANCEL BUTTON_POWER
240 #define HK_SELECT "SELECT"
241 #define HK_CANCEL "POWER"
243 #elif CONFIG_KEYPAD == ONDAVX747_PAD || \
244 CONFIG_KEYPAD == MROBE500_PAD
245 #define JEWELS_CANCEL BUTTON_POWER
246 #define HK_CANCEL "POWER"
248 #else
249 #error No keymap defined!
250 #endif
252 #ifdef HAVE_TOUCHSCREEN
253 #ifndef JEWELS_UP
254 #define JEWELS_UP BUTTON_TOPMIDDLE
255 #endif
256 #ifndef JEWELS_DOWN
257 #define JEWELS_DOWN BUTTON_BOTTOMMIDDLE
258 #endif
259 #ifndef JEWELS_LEFT
260 #define JEWELS_LEFT BUTTON_MIDLEFT
261 #endif
262 #ifndef JEWELS_RIGHT
263 #define JEWELS_RIGHT BUTTON_MIDRIGHT
264 #endif
265 #ifndef JEWELS_SELECT
266 #define JEWELS_SELECT BUTTON_CENTER
267 #define HK_SELECT "CENTER"
268 #endif
269 #ifndef JEWELS_CANCEL
270 #define JEWELS_CANCEL BUTTON_TOPLEFT
271 #define HK_CANCEL "TOPLEFT"
272 #endif
273 #endif
275 /* use 30x30 tiles (iPod Video, Gigabeat, Onda VX747) */
276 #if (LCD_HEIGHT == 240) && (LCD_WIDTH == 320) || \
277 ((LCD_HEIGHT == 320) && (LCD_WIDTH == 240)) || \
278 ((LCD_HEIGHT == 400) && (LCD_WIDTH == 240))
279 #define TILE_WIDTH 30
280 #define TILE_HEIGHT 30
281 #define YOFS 0
282 #define NUM_SCORES 10
284 /* use 22x22 tiles (H300, iPod Color) */
285 #elif ((LCD_HEIGHT == 176) && (LCD_WIDTH == 220)) || \
286 ((LCD_HEIGHT == 220) && (LCD_WIDTH == 176))
287 #define TILE_WIDTH 22
288 #define TILE_HEIGHT 22
289 #define YOFS 0
290 #define NUM_SCORES 5
292 /* use 16x16 tiles (iPod Nano) */
293 #elif (LCD_HEIGHT == 132) && (LCD_WIDTH == 176)
294 #define TILE_WIDTH 16
295 #define TILE_HEIGHT 16
296 #define YOFS 4
297 #define NUM_SCORES 5
299 /* use 16x16 tiles (H100, iAudio X5, iPod 3G, iPod 4G grayscale) */
300 #elif (LCD_HEIGHT == 128) && (LCD_WIDTH == 160)
301 #define TILE_WIDTH 16
302 #define TILE_HEIGHT 16
303 #define YOFS 0
304 #define NUM_SCORES 5
306 /* use 14x14 tiles (H10 5/6 GB) */
307 #elif (LCD_HEIGHT == 128) && (LCD_WIDTH == 128)
308 #define TILE_WIDTH 14
309 #define TILE_HEIGHT 14
310 #define YOFS 0
311 #define NUM_SCORES 5
313 /* use 13x13 tiles (iPod Mini) */
314 #elif (LCD_HEIGHT == 110) && (LCD_WIDTH == 138)
315 #define TILE_WIDTH 13
316 #define TILE_HEIGHT 13
317 #define YOFS 6
318 #define NUM_SCORES 5
320 /* use 12x12 tiles (iAudio M3) */
321 #elif (LCD_HEIGHT == 96) && (LCD_WIDTH == 128)
322 #define TILE_WIDTH 12
323 #define TILE_HEIGHT 12
324 #define YOFS 0
325 #define NUM_SCORES 5
327 /* use 10x10 tiles (Sansa c200) */
328 #elif (LCD_HEIGHT == 80) && (LCD_WIDTH == 132)
329 #define TILE_WIDTH 10
330 #define TILE_HEIGHT 10
331 #define YOFS 0
332 #define NUM_SCORES 5
334 /* use 10x8 tiles (iFP 700) */
335 #elif (LCD_HEIGHT == 64) && (LCD_WIDTH == 128)
336 #define TILE_WIDTH 10
337 #define TILE_HEIGHT 8
338 #define YOFS 0
339 #define NUM_SCORES 5
341 /* use 10x8 tiles (Recorder, Ondio) */
342 #elif (LCD_HEIGHT == 64) && (LCD_WIDTH == 112)
343 #define TILE_WIDTH 10
344 #define TILE_HEIGHT 8
345 #define YOFS 0
346 #define NUM_SCORES 5
348 #else
349 #error JEWELS: Unsupported LCD
350 #endif
352 /* swap directions */
353 #define SWAP_UP 0
354 #define SWAP_RIGHT 1
355 #define SWAP_DOWN 2
356 #define SWAP_LEFT 3
358 /* play board dimension */
359 #define BJ_HEIGHT 9
360 #define BJ_WIDTH 8
362 /* next level threshold */
363 #define LEVEL_PTS 100
365 /* animation frame rate */
366 #define MAX_FPS 20
368 /* text margin */
369 #define MARGIN 5
371 /* Game types */
372 enum game_type {
373 GAME_TYPE_NORMAL,
374 GAME_TYPE_PUZZLE
377 /* external bitmaps */
378 extern const fb_data jewels[];
380 /* tile background colors */
381 #ifdef HAVE_LCD_COLOR
382 static const unsigned jewels_bkgd[2] = {
383 LCD_RGBPACK(104, 63, 63),
384 LCD_RGBPACK(83, 44, 44)
386 #endif
388 /* the tile struct
389 * type is the jewel number 0-7
390 * falling if the jewel is falling
391 * delete marks the jewel for deletion
393 struct tile {
394 int type;
395 bool falling;
396 bool delete;
399 /* the game context struct
400 * score is the current level score
401 * segments is the number of cleared segments in the current run
402 * level is the current level
403 * tmp_type is the select type in the menu
404 * type is the game type (normal or puzzle)
405 * playboard is the game playing board (first row is hidden)
406 * num_jewels is the number of different jewels to use
408 struct game_context {
409 unsigned int score;
410 unsigned int segments;
411 unsigned int level;
412 unsigned int type;
413 unsigned int tmp_type;
414 struct tile playboard[BJ_HEIGHT][BJ_WIDTH];
415 unsigned int num_jewels;
418 #define MAX_NUM_JEWELS 7
420 #define MAX_PUZZLE_TILES 4
421 #define NUM_PUZZLE_LEVELS 10
423 struct puzzle_tile {
424 int x;
425 int y;
426 int tile_type;
429 struct puzzle_level {
430 unsigned int num_jewels;
431 unsigned int num_tiles;
432 struct puzzle_tile tiles[MAX_PUZZLE_TILES];
435 #define PUZZLE_TILE_UP 1
436 #define PUZZLE_TILE_DOWN 2
437 #define PUZZLE_TILE_LEFT 4
438 #define PUZZLE_TILE_RIGHT 8
440 struct puzzle_level puzzle_levels[NUM_PUZZLE_LEVELS] = {
441 { 5, 2, { {3, 3, PUZZLE_TILE_RIGHT},
442 {4, 2, PUZZLE_TILE_LEFT} } },
443 { 5, 2, { {3, 2, PUZZLE_TILE_DOWN},
444 {3, 4, PUZZLE_TILE_UP} } },
445 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
446 {3, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
447 {3, 6, PUZZLE_TILE_UP} } },
448 { 6, 3, { {3, 2, PUZZLE_TILE_RIGHT},
449 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
450 {5, 4, PUZZLE_TILE_LEFT} } },
451 { 6, 2, { {3, 4, PUZZLE_TILE_RIGHT},
452 {4, 2, PUZZLE_TILE_LEFT} } },
453 { 6, 2, { {3, 2, PUZZLE_TILE_DOWN},
454 {4, 4, PUZZLE_TILE_UP} } },
455 { 7, 4, { {3, 2, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
456 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
457 {3, 4, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
458 {4, 4, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
459 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
460 {4, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
461 {3, 6, PUZZLE_TILE_UP} } },
462 { 7, 3, { {2, 2, PUZZLE_TILE_RIGHT},
463 {4, 1, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
464 {5, 4, PUZZLE_TILE_LEFT} } },
465 { 7, 4, { {3, 0, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
466 {5, 0, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
467 {2, 7, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
468 {4, 7, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
471 #define SAVE_FILE PLUGIN_GAMES_DIR "/jewels.save"
473 #define HIGH_SCORE PLUGIN_GAMES_DIR "/jewels.score"
474 struct highscore highest[NUM_SCORES];
475 bool highest_updated = false;
478 /*****************************************************************************
479 * jewels_setcolors() set the foreground and background colors.
480 ******************************************************************************/
481 static inline void jewels_setcolors(void) {
482 #ifdef HAVE_LCD_COLOR
483 rb->lcd_set_background(LCD_RGBPACK(49, 26, 26));
484 rb->lcd_set_foreground(LCD_RGBPACK(210, 181, 181));
485 #endif
488 /*****************************************************************************
489 * jewels_loadgame() loads the saved game and returns load success.
490 ******************************************************************************/
491 static bool jewels_loadgame(struct game_context* bj)
493 int fd;
494 bool loaded = false;
496 /* open game file */
497 fd = rb->open(SAVE_FILE, O_RDONLY);
498 if(fd < 0) return loaded;
500 /* read in saved game */
501 while(true) {
502 if(rb->read(fd, &bj->tmp_type, sizeof(bj->tmp_type)) <= 0) break;
503 if(rb->read(fd, &bj->type, sizeof(bj->type)) <= 0) break;
504 if(rb->read(fd, &bj->score, sizeof(bj->score)) <= 0) break;
505 if(rb->read(fd, &bj->level, sizeof(bj->level)) <= 0) break;
506 if(rb->read(fd, &bj->segments, sizeof(bj->segments)) <= 0) break;
507 if(rb->read(fd, &bj->num_jewels, sizeof(bj->num_jewels)) <= 0) break;
508 if(rb->read(fd, bj->playboard, sizeof(bj->playboard)) <= 0) break;
509 loaded = true;
510 break;
513 rb->close(fd);
515 /* delete saved file */
516 rb->remove(SAVE_FILE);
517 return loaded;
520 /*****************************************************************************
521 * jewels_savegame() saves the current game state.
522 ******************************************************************************/
523 static void jewels_savegame(struct game_context* bj)
525 int fd;
526 /* write out the game state to the save file */
527 fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT);
528 rb->write(fd, &bj->tmp_type, sizeof(bj->tmp_type));
529 rb->write(fd, &bj->type, sizeof(bj->type));
530 rb->write(fd, &bj->score, sizeof(bj->score));
531 rb->write(fd, &bj->level, sizeof(bj->level));
532 rb->write(fd, &bj->segments, sizeof(bj->segments));
533 rb->write(fd, &bj->num_jewels, sizeof(bj->num_jewels));
534 rb->write(fd, bj->playboard, sizeof(bj->playboard));
535 rb->close(fd);
538 /*****************************************************************************
539 * jewels_drawboard() redraws the entire game board.
540 ******************************************************************************/
541 static void jewels_drawboard(struct game_context* bj) {
542 int i, j;
543 int w, h;
544 unsigned int tempscore;
545 unsigned int size;
546 char *title = "Level";
547 char str[10];
549 if (bj->type == GAME_TYPE_NORMAL) {
550 tempscore = (bj->score>LEVEL_PTS ? LEVEL_PTS : bj->score);
551 size = LEVEL_PTS;
552 } else {
553 tempscore = (bj->level>NUM_PUZZLE_LEVELS ? NUM_PUZZLE_LEVELS : bj->level);
554 size = NUM_PUZZLE_LEVELS;
557 /* clear screen */
558 rb->lcd_clear_display();
560 /* dispay playing board */
561 for(i=0; i<BJ_HEIGHT-1; i++){
562 for(j=0; j<BJ_WIDTH; j++){
563 #ifdef HAVE_LCD_COLOR
564 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
565 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
566 TILE_WIDTH, TILE_HEIGHT);
567 rb->lcd_bitmap_transparent_part(jewels,
568 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
569 TILE_WIDTH, j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
570 TILE_WIDTH, TILE_HEIGHT);
571 #else
572 rb->lcd_bitmap_part(jewels,
573 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
574 TILE_WIDTH, j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
575 TILE_WIDTH, TILE_HEIGHT);
576 #endif
580 #if LCD_WIDTH > LCD_HEIGHT /* horizontal layout */
582 /* draw separator lines */
583 jewels_setcolors();
584 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, LCD_HEIGHT-1);
586 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, 18);
587 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, LCD_HEIGHT-10);
589 /* draw progress bar */
590 #ifdef HAVE_LCD_COLOR
591 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
592 #endif
593 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
594 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
595 tempscore/size),
596 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
597 ((LCD_HEIGHT-10)-18)*tempscore/size);
598 #ifdef HAVE_LCD_COLOR
599 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
600 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
601 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
602 tempscore/size)+1,
603 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
604 ((LCD_HEIGHT-10)-18)*tempscore/size-1);
605 jewels_setcolors();
606 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
607 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*tempscore/size),
608 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
609 ((LCD_HEIGHT-10)-18)*tempscore/size+1);
610 #endif
612 /* print text */
613 rb->lcd_getstringsize(title, &w, &h);
614 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 1, title);
615 rb->snprintf(str, 4, "%d", bj->level);
616 rb->lcd_getstringsize(str, &w, &h);
617 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 10, str);
619 if (bj->type == GAME_TYPE_NORMAL) {
620 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
621 rb->lcd_getstringsize(str, &w, &h);
622 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2,
623 LCD_HEIGHT-8, str);
626 #elif LCD_WIDTH < LCD_HEIGHT /* vertical layout */
628 /* draw separator lines */
629 jewels_setcolors();
630 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
631 rb->lcd_hline(0, LCD_WIDTH-1, LCD_HEIGHT-14);
632 rb->lcd_vline(LCD_WIDTH/2, LCD_HEIGHT-14, LCD_HEIGHT-1);
634 /* draw progress bar */
635 #ifdef HAVE_LCD_COLOR
636 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
637 #endif
638 rb->lcd_fillrect(0, (8*TILE_HEIGHT+YOFS)
639 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
640 LCD_WIDTH*tempscore/size,
641 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
642 #ifdef HAVE_LCD_COLOR
643 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
644 rb->lcd_drawrect(1, (8*TILE_HEIGHT+YOFS)
645 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4+1,
646 LCD_WIDTH*tempscore/size-1,
647 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2-2);
648 jewels_setcolors();
649 rb->lcd_drawrect(0, (8*TILE_HEIGHT+YOFS)
650 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
651 LCD_WIDTH*tempscore/size+1,
652 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
653 #endif
655 /* print text */
656 rb->snprintf(str, 10, "%s %d", title, bj->level);
657 rb->lcd_putsxy(1, LCD_HEIGHT-10, str);
659 if (bj->type == GAME_TYPE_NORMAL) {
660 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
661 rb->lcd_getstringsize(str, &w, &h);
662 rb->lcd_putsxy((LCD_WIDTH-2)-w, LCD_HEIGHT-10, str);
666 #else /* square layout */
668 /* draw separator lines */
669 jewels_setcolors();
670 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
671 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, 8*TILE_HEIGHT+YOFS);
672 rb->lcd_vline(LCD_WIDTH/2, 8*TILE_HEIGHT+YOFS, LCD_HEIGHT-1);
674 /* draw progress bar */
675 #ifdef HAVE_LCD_COLOR
676 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
677 #endif
678 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
679 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)*tempscore/size,
680 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
681 (8*TILE_HEIGHT+YOFS)*tempscore/size);
682 #ifdef HAVE_LCD_COLOR
683 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
684 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
685 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
686 *tempscore/size+1,
687 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
688 (8*TILE_HEIGHT+YOFS)*tempscore/size-1);
689 jewels_setcolors();
690 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
691 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
692 *tempscore/size,
693 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
694 (8*TILE_HEIGHT+YOFS)*tempscore/size+1);
695 #endif
697 /* print text */
698 rb->snprintf(str, 10, "%s %d", title, bj->level);
699 rb->lcd_putsxy(1, LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
701 if (bj->type == GAME_TYPE_NORMAL) {
702 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
703 rb->lcd_getstringsize(str, &w, &h);
704 rb->lcd_putsxy((LCD_WIDTH-2)-w,
705 LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
708 #endif /* layout */
710 rb->lcd_update();
713 /*****************************************************************************
714 * jewels_putjewels() makes the jewels fall to fill empty spots and adds
715 * new random jewels at the empty spots at the top of each row.
716 ******************************************************************************/
717 static void jewels_putjewels(struct game_context* bj){
718 int i, j, k;
719 bool mark, done;
720 long lasttick, currenttick;
722 /* loop to make all the jewels fall */
723 while(true) {
724 /* mark falling jewels and add new jewels to hidden top row*/
725 mark = false;
726 done = true;
727 for(j=0; j<BJ_WIDTH; j++) {
728 if(bj->playboard[1][j].type == 0) {
729 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
731 for(i=BJ_HEIGHT-2; i>=0; i--) {
732 if(!mark && bj->playboard[i+1][j].type == 0) {
733 mark = true;
734 done = false;
736 if(mark) bj->playboard[i][j].falling = true;
738 /*if(bj->playboard[1][j].falling) {
739 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
740 bj->playboard[0][j].falling = true;
742 mark = false;
745 /* break if there are no falling jewels */
746 if(done) break;
748 /* animate falling jewels */
749 lasttick = *rb->current_tick;
751 for(k=1; k<=8; k++) {
752 for(i=BJ_HEIGHT-2; i>=0; i--) {
753 for(j=0; j<BJ_WIDTH; j++) {
754 if(bj->playboard[i][j].falling &&
755 bj->playboard[i][j].type != 0) {
756 /* clear old position */
757 #ifdef HAVE_LCD_COLOR
758 if(i == 0 && YOFS) {
759 rb->lcd_set_foreground(rb->lcd_get_background());
760 } else {
761 rb->lcd_set_foreground(jewels_bkgd[(i-1+j)%2]);
763 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
764 TILE_WIDTH, TILE_HEIGHT);
765 if(bj->playboard[i+1][j].type == 0) {
766 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
767 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
768 TILE_WIDTH, TILE_HEIGHT);
770 #else
771 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
772 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
773 TILE_WIDTH, TILE_HEIGHT);
774 if(bj->playboard[i+1][j].type == 0) {
775 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
776 TILE_WIDTH, TILE_HEIGHT);
778 rb->lcd_set_drawmode(DRMODE_SOLID);
779 #endif
781 /* draw new position */
782 #ifdef HAVE_LCD_COLOR
783 rb->lcd_bitmap_transparent_part(jewels, 0,
784 TILE_HEIGHT*(bj->playboard[i][j].type),
785 TILE_WIDTH, j*TILE_WIDTH,
786 (i-1)*TILE_HEIGHT+YOFS+
787 ((((TILE_HEIGHT<<10)*k)/8)>>10),
788 TILE_WIDTH, TILE_HEIGHT);
789 #else
790 rb->lcd_bitmap_part(jewels, 0,
791 TILE_HEIGHT*(bj->playboard[i][j].type),
792 TILE_WIDTH, j*TILE_WIDTH,
793 (i-1)*TILE_HEIGHT+YOFS+
794 ((((TILE_HEIGHT<<10)*k)/8)>>10),
795 TILE_WIDTH, TILE_HEIGHT);
796 #endif
801 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
802 jewels_setcolors();
804 /* framerate limiting */
805 currenttick = *rb->current_tick;
806 if(currenttick-lasttick < HZ/MAX_FPS) {
807 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
808 } else {
809 rb->yield();
811 lasttick = currenttick;
814 /* shift jewels down */
815 for(j=0; j<BJ_WIDTH; j++) {
816 for(i=BJ_HEIGHT-1; i>=1; i--) {
817 if(bj->playboard[i-1][j].falling) {
818 bj->playboard[i][j].type = bj->playboard[i-1][j].type;
823 /* clear out top row */
824 for(j=0; j<BJ_WIDTH; j++) {
825 bj->playboard[0][j].type = 0;
828 /* mark everything not falling */
829 for(i=0; i<BJ_HEIGHT; i++) {
830 for(j=0; j<BJ_WIDTH; j++) {
831 bj->playboard[i][j].falling = false;
837 /*****************************************************************************
838 * jewels_clearjewels() finds all the connected rows and columns and
839 * calculates and returns the points earned.
840 ******************************************************************************/
841 static unsigned int jewels_clearjewels(struct game_context* bj) {
842 int i, j;
843 int last, run;
844 unsigned int points = 0;
846 /* check for connected rows */
847 for(i=1; i<BJ_HEIGHT; i++) {
848 last = 0;
849 run = 1;
850 for(j=0; j<BJ_WIDTH; j++) {
851 if(bj->playboard[i][j].type == last &&
852 bj->playboard[i][j].type != 0 &&
853 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
854 run++;
856 if(run == 3) {
857 bj->segments++;
858 points += bj->segments;
859 bj->playboard[i][j].delete = true;
860 bj->playboard[i][j-1].delete = true;
861 bj->playboard[i][j-2].delete = true;
862 } else if(run > 3) {
863 points++;
864 bj->playboard[i][j].delete = true;
866 } else {
867 run = 1;
868 last = bj->playboard[i][j].type;
873 /* check for connected columns */
874 for(j=0; j<BJ_WIDTH; j++) {
875 last = 0;
876 run = 1;
877 for(i=1; i<BJ_HEIGHT; i++) {
878 if(bj->playboard[i][j].type != 0 &&
879 bj->playboard[i][j].type == last &&
880 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
881 run++;
883 if(run == 3) {
884 bj->segments++;
885 points += bj->segments;
886 bj->playboard[i][j].delete = true;
887 bj->playboard[i-1][j].delete = true;
888 bj->playboard[i-2][j].delete = true;
889 } else if(run > 3) {
890 points++;
891 bj->playboard[i][j].delete = true;
893 } else {
894 run = 1;
895 last = bj->playboard[i][j].type;
900 /* clear deleted jewels */
901 for(i=1; i<BJ_HEIGHT; i++) {
902 for(j=0; j<BJ_WIDTH; j++) {
903 if(bj->playboard[i][j].delete) {
904 bj->playboard[i][j].delete = false;
905 bj->playboard[i][j].type = 0;
910 return points;
913 /*****************************************************************************
914 * jewels_runboard() runs the board until it settles in a fixed state and
915 * returns points earned.
916 ******************************************************************************/
917 static unsigned int jewels_runboard(struct game_context* bj) {
918 unsigned int points = 0;
919 unsigned int ret;
921 bj->segments = 0;
923 while((ret = jewels_clearjewels(bj)) > 0) {
924 points += ret;
925 jewels_drawboard(bj);
926 jewels_putjewels(bj);
929 return points;
932 /*****************************************************************************
933 * jewels_swapjewels() swaps two jewels as long as it results in points and
934 * returns points earned.
935 ******************************************************************************/
936 static unsigned int jewels_swapjewels(struct game_context* bj,
937 int x, int y, int direc) {
938 int k;
939 int horzmod, vertmod;
940 int movelen = 0;
941 bool undo = false;
942 unsigned int points = 0;
943 long lasttick, currenttick;
945 /* check for invalid parameters */
946 if(x < 0 || x >= BJ_WIDTH || y < 0 || y >= BJ_HEIGHT-1 ||
947 direc < SWAP_UP || direc > SWAP_LEFT) {
948 return 0;
951 /* check for invalid directions */
952 if((x == 0 && direc == SWAP_LEFT) ||
953 (x == BJ_WIDTH-1 && direc == SWAP_RIGHT) ||
954 (y == 0 && direc == SWAP_UP) ||
955 (y == BJ_HEIGHT-2 && direc == SWAP_DOWN)) {
956 return 0;
959 /* set direction variables */
960 horzmod = 0;
961 vertmod = 0;
962 switch(direc) {
963 case SWAP_UP:
964 vertmod = -1;
965 movelen = TILE_HEIGHT;
966 break;
967 case SWAP_RIGHT:
968 horzmod = 1;
969 movelen = TILE_WIDTH;
970 break;
971 case SWAP_DOWN:
972 vertmod = 1;
973 movelen = TILE_HEIGHT;
974 break;
975 case SWAP_LEFT:
976 horzmod = -1;
977 movelen = TILE_WIDTH;
978 break;
981 while(true) {
982 lasttick = *rb->current_tick;
984 /* animate swapping jewels */
985 for(k=0; k<=8; k++) {
986 /* clear old position */
987 #ifdef HAVE_LCD_COLOR
988 rb->lcd_set_foreground(jewels_bkgd[(x+y)%2]);
989 rb->lcd_fillrect(x*TILE_WIDTH,
990 y*TILE_HEIGHT+YOFS,
991 TILE_WIDTH, TILE_HEIGHT);
992 rb->lcd_set_foreground(jewels_bkgd[(x+horzmod+y+vertmod)%2]);
993 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
994 (y+vertmod)*TILE_HEIGHT+YOFS,
995 TILE_WIDTH, TILE_HEIGHT);
996 #else
997 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
998 rb->lcd_fillrect(x*TILE_WIDTH,
999 y*TILE_HEIGHT+YOFS,
1000 TILE_WIDTH, TILE_HEIGHT);
1001 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
1002 (y+vertmod)*TILE_HEIGHT+YOFS,
1003 TILE_WIDTH, TILE_HEIGHT);
1004 rb->lcd_set_drawmode(DRMODE_SOLID);
1005 #endif
1006 /* draw new position */
1007 #ifdef HAVE_LCD_COLOR
1008 rb->lcd_bitmap_transparent_part(jewels,
1009 0, TILE_HEIGHT*(bj->playboard
1010 [y+1+vertmod][x+horzmod].type), TILE_WIDTH,
1011 (x+horzmod)*TILE_WIDTH-horzmod*
1012 ((((movelen<<10)*k)/8)>>10),
1013 (y+vertmod)*TILE_HEIGHT-vertmod*
1014 ((((movelen<<10)*k)/8)>>10)+YOFS,
1015 TILE_WIDTH, TILE_HEIGHT);
1016 rb->lcd_bitmap_transparent_part(jewels,
1017 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
1018 TILE_WIDTH, x*TILE_WIDTH+horzmod*
1019 ((((movelen<<10)*k)/8)>>10),
1020 y*TILE_HEIGHT+vertmod*
1021 ((((movelen<<10)*k)/8)>>10)+YOFS,
1022 TILE_WIDTH, TILE_HEIGHT);
1023 #else
1024 rb->lcd_bitmap_part(jewels,
1025 0, TILE_HEIGHT*(bj->playboard
1026 [y+1+vertmod][x+horzmod].type), TILE_WIDTH,
1027 (x+horzmod)*TILE_WIDTH-horzmod*
1028 ((((movelen<<10)*k)/8)>>10),
1029 (y+vertmod)*TILE_HEIGHT-vertmod*
1030 ((((movelen<<10)*k)/8)>>10)+YOFS,
1031 TILE_WIDTH, TILE_HEIGHT);
1032 rb->lcd_set_drawmode(DRMODE_FG);
1033 rb->lcd_bitmap_part(jewels,
1034 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
1035 TILE_WIDTH, x*TILE_WIDTH+horzmod*
1036 ((((movelen<<10)*k)/8)>>10),
1037 y*TILE_HEIGHT+vertmod*
1038 ((((movelen<<10)*k)/8)>>10)+YOFS,
1039 TILE_WIDTH, TILE_HEIGHT);
1040 rb->lcd_set_drawmode(DRMODE_SOLID);
1041 #endif
1043 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
1044 jewels_setcolors();
1046 /* framerate limiting */
1047 currenttick = *rb->current_tick;
1048 if(currenttick-lasttick < HZ/MAX_FPS) {
1049 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
1050 } else {
1051 rb->yield();
1053 lasttick = currenttick;
1056 /* swap jewels */
1057 int temp = bj->playboard[y+1][x].type;
1058 bj->playboard[y+1][x].type =
1059 bj->playboard[y+1+vertmod][x+horzmod].type;
1060 bj->playboard[y+1+vertmod][x+horzmod].type = temp;
1062 if(undo) break;
1064 points = jewels_runboard(bj);
1065 if(points == 0) {
1066 undo = true;
1067 } else {
1068 break;
1072 return points;
1075 /*****************************************************************************
1076 * jewels_movesavail() uses pattern matching to see if there are any
1077 * available move left.
1078 ******************************************************************************/
1079 static bool jewels_movesavail(struct game_context* bj) {
1080 int i, j;
1081 bool moves = false;
1082 int mytype;
1084 for(i=1; i<BJ_HEIGHT; i++) {
1085 for(j=0; j<BJ_WIDTH; j++) {
1086 mytype = bj->playboard[i][j].type;
1087 if(mytype == 0 || mytype > MAX_NUM_JEWELS) continue;
1089 /* check horizontal patterns */
1090 if(j <= BJ_WIDTH-3) {
1091 if(i > 1) {
1092 if(bj->playboard[i-1][j+1].type == mytype) {
1093 if(bj->playboard[i-1][j+2].type == mytype)
1094 {moves = true; break;}
1095 if(bj->playboard[i][j+2].type == mytype)
1096 {moves = true; break;}
1098 if(bj->playboard[i][j+1].type == mytype) {
1099 if(bj->playboard[i-1][j+2].type == mytype)
1100 {moves = true; break;}
1104 if(j <= BJ_WIDTH-4) {
1105 if(bj->playboard[i][j+3].type == mytype) {
1106 if(bj->playboard[i][j+1].type == mytype)
1107 {moves = true; break;}
1108 if(bj->playboard[i][j+2].type == mytype)
1109 {moves = true; break;}
1113 if(i < BJ_HEIGHT-1) {
1114 if(bj->playboard[i][j+1].type == mytype) {
1115 if(bj->playboard[i+1][j+2].type == mytype)
1116 {moves = true; break;}
1118 if(bj->playboard[i+1][j+1].type == mytype) {
1119 if(bj->playboard[i][j+2].type == mytype)
1120 {moves = true; break;}
1121 if(bj->playboard[i+1][j+2].type == mytype)
1122 {moves = true; break;}
1127 /* check vertical patterns */
1128 if(i <= BJ_HEIGHT-3) {
1129 if(j > 0) {
1130 if(bj->playboard[i+1][j-1].type == mytype) {
1131 if(bj->playboard[i+2][j-1].type == mytype)
1132 {moves = true; break;}
1133 if(bj->playboard[i+2][j].type == mytype)
1134 {moves = true; break;}
1136 if(bj->playboard[i+1][j].type == mytype) {
1137 if(bj->playboard[i+2][j-1].type == mytype)
1138 {moves = true; break;}
1142 if(i <= BJ_HEIGHT-4) {
1143 if(bj->playboard[i+3][j].type == mytype) {
1144 if(bj->playboard[i+1][j].type == mytype)
1145 {moves = true; break;}
1146 if(bj->playboard[i+2][j].type == mytype)
1147 {moves = true; break;}
1151 if(j < BJ_WIDTH-1) {
1152 if(bj->playboard[i+1][j].type == mytype) {
1153 if(bj->playboard[i+2][j+1].type == mytype)
1154 {moves = true; break;}
1156 if(bj->playboard[i+1][j+1].type == mytype) {
1157 if(bj->playboard[i+2][j].type == mytype)
1158 {moves = true; break;}
1159 if (bj->playboard[i+2][j+1].type == mytype)
1160 {moves = true; break;}
1165 if(moves) break;
1167 return moves;
1170 /*****************************************************************************
1171 * jewels_puzzle_is_finished() checks if the puzzle is finished.
1172 ******************************************************************************/
1173 static bool jewels_puzzle_is_finished(struct game_context* bj) {
1174 unsigned int i, j;
1175 for(i=0; i<BJ_HEIGHT; i++) {
1176 for(j=0; j<BJ_WIDTH; j++) {
1177 int mytype = bj->playboard[i][j].type;
1178 if(mytype>MAX_NUM_JEWELS) {
1179 mytype -= MAX_NUM_JEWELS;
1180 if(mytype&PUZZLE_TILE_UP) {
1181 if(i==0 || bj->playboard[i-1][j].type<=MAX_NUM_JEWELS ||
1182 !((bj->playboard[i-1][j].type-MAX_NUM_JEWELS)
1183 &PUZZLE_TILE_DOWN))
1184 return false;
1186 if(mytype&PUZZLE_TILE_DOWN) {
1187 if(i==BJ_HEIGHT-1 ||
1188 bj->playboard[i+1][j].type<=MAX_NUM_JEWELS ||
1189 !((bj->playboard[i+1][j].type-MAX_NUM_JEWELS)
1190 &PUZZLE_TILE_UP))
1191 return false;
1193 if(mytype&PUZZLE_TILE_LEFT) {
1194 if(j==0 || bj->playboard[i][j-1].type<=MAX_NUM_JEWELS ||
1195 !((bj->playboard[i][j-1].type-MAX_NUM_JEWELS)
1196 &PUZZLE_TILE_RIGHT))
1197 return false;
1199 if(mytype&PUZZLE_TILE_RIGHT) {
1200 if(j==BJ_WIDTH-1 ||
1201 bj->playboard[i][j+1].type<=MAX_NUM_JEWELS ||
1202 !((bj->playboard[i][j+1].type-MAX_NUM_JEWELS)
1203 &PUZZLE_TILE_LEFT))
1204 return false;
1209 return true;
1212 /*****************************************************************************
1213 * jewels_initlevel() initialises a level.
1214 ******************************************************************************/
1215 static unsigned int jewels_initlevel(struct game_context* bj) {
1216 unsigned int points = 0;
1218 switch(bj->type) {
1219 case GAME_TYPE_NORMAL:
1220 bj->num_jewels = MAX_NUM_JEWELS;
1221 break;
1223 case GAME_TYPE_PUZZLE:
1225 unsigned int i, j;
1226 struct puzzle_tile *tile;
1228 bj->num_jewels = puzzle_levels[bj->level-1].num_jewels;
1230 for(i=0; i<BJ_HEIGHT; i++) {
1231 for(j=0; j<BJ_WIDTH; j++) {
1232 bj->playboard[i][j].type = (rb->rand()%bj->num_jewels)+1;
1233 bj->playboard[i][j].falling = false;
1234 bj->playboard[i][j].delete = false;
1237 jewels_runboard(bj);
1238 tile = puzzle_levels[bj->level-1].tiles;
1239 for(i=0; i<puzzle_levels[bj->level-1].num_tiles; i++, tile++) {
1240 bj->playboard[tile->y+1][tile->x].type = MAX_NUM_JEWELS
1241 +tile->tile_type;
1244 break;
1247 jewels_drawboard(bj);
1249 /* run the play board */
1250 jewels_putjewels(bj);
1251 points += jewels_runboard(bj);
1252 return points;
1255 /*****************************************************************************
1256 * jewels_init() initializes jewels data structures.
1257 ******************************************************************************/
1258 static void jewels_init(struct game_context* bj) {
1259 /* seed the rand generator */
1260 rb->srand(*rb->current_tick);
1262 bj->type = bj->tmp_type;
1263 bj->level = 1;
1264 bj->score = 0;
1265 bj->segments = 0;
1267 jewels_setcolors();
1269 /* clear playing board */
1270 rb->memset(bj->playboard, 0, sizeof(bj->playboard));
1271 do {
1272 bj->score += jewels_initlevel(bj);
1273 } while(!jewels_movesavail(bj));
1276 /*****************************************************************************
1277 * jewels_nextlevel() advances the game to the next bj->level and returns
1278 * points earned.
1279 ******************************************************************************/
1280 static void jewels_nextlevel(struct game_context* bj) {
1281 int i, x, y;
1282 unsigned int points = 0;
1284 switch(bj->type) {
1285 case GAME_TYPE_NORMAL:
1286 /* roll over score, change and display level */
1287 while(bj->score >= LEVEL_PTS) {
1288 bj->score -= LEVEL_PTS;
1289 bj->level++;
1290 rb->splashf(HZ*2, "Level %d", bj->level);
1291 jewels_drawboard(bj);
1294 /* randomly clear some jewels */
1295 for(i=0; i<16; i++) {
1296 x = rb->rand()%8;
1297 y = rb->rand()%8;
1299 if(bj->playboard[y][x].type != 0) {
1300 points++;
1301 bj->playboard[y][x].type = 0;
1304 break;
1306 case GAME_TYPE_PUZZLE:
1307 bj->level++;
1308 rb->splashf(HZ*2, "Level %d", bj->level);
1309 break;
1312 points += jewels_initlevel(bj);
1313 bj->score += points;
1316 static void jewels_show_highscores(int position)
1318 int i, w, h;
1319 char str[30];
1321 #ifdef HAVE_LCD_COLOR
1322 rb->lcd_set_background(LCD_BLACK);
1323 rb->lcd_set_foreground(LCD_WHITE);
1324 #endif
1325 rb->button_clear_queue();
1326 rb->lcd_clear_display();
1328 rb->lcd_setfont(FONT_UI);
1329 rb->lcd_getstringsize("High Scores", &w, &h);
1330 /* check wether it fits on screen */
1331 if ((4*h + h*(NUM_SCORES-1) + MARGIN) > LCD_HEIGHT) {
1332 rb->lcd_setfont(FONT_SYSFIXED);
1333 rb->lcd_getstringsize("High Scores", &w, &h);
1335 rb->lcd_putsxy(LCD_WIDTH/2-w/2, MARGIN, "High Scores");
1336 rb->lcd_putsxy(LCD_WIDTH/4-w/4,2*h, "Score");
1337 rb->lcd_putsxy(LCD_WIDTH*3/4-w/4,2*h, "Level");
1339 for (i = 0; i<NUM_SCORES; i++)
1341 #ifdef HAVE_LCD_COLOR
1342 if (i == position) {
1343 rb->lcd_set_foreground(LCD_RGBPACK(245,0,0));
1345 #endif
1346 rb->snprintf (str, sizeof (str), "%d)", i+1);
1347 rb->lcd_putsxy (MARGIN,3*h + h*i, str);
1348 rb->snprintf (str, sizeof (str), "%d", highest[i].score);
1349 rb->lcd_putsxy (LCD_WIDTH/4-w/4,3*h + h*i, str);
1350 rb->snprintf (str, sizeof (str), "%d", highest[i].level);
1351 rb->lcd_putsxy (LCD_WIDTH*3/4-w/4,3*h + h*i, str);
1352 if(i == position) {
1353 #ifdef HAVE_LCD_COLOR
1354 rb->lcd_set_foreground(LCD_WHITE);
1355 #else
1356 rb->lcd_hline(MARGIN, LCD_WIDTH-MARGIN, 3*h + h*(i+1));
1357 #endif
1360 rb->lcd_update();
1361 rb->button_get(true);
1362 rb->lcd_setfont(FONT_SYSFIXED);
1365 static bool jewels_help(void)
1367 rb->lcd_setfont(FONT_UI);
1368 #define WORDS (sizeof help_text / sizeof (char*))
1369 static char *help_text[] = {
1370 "Jewels", "", "Aim", "",
1371 "Swap", "pairs", "of", "jewels", "to", "form", "connected",
1372 "segments", "of", "three", "or", "more", "of", "the", "same",
1373 "type.", "",
1374 "The", "goal", "of", "the", "game", "is", "to", "score", "as", "many",
1375 "points", "as", "possible", "before", "running", "out", "of",
1376 "available", "moves.", "", "",
1377 "Controls", "",
1378 "Directions",
1379 #ifdef JEWELS_SCROLLWHEEL
1380 "or", "scroll",
1381 #endif
1382 "to", "move", "",
1383 HK_SELECT, "to", "select", "",
1384 HK_CANCEL, "to", "go", "to", "menu"
1386 static struct style_text formation[]={
1387 { 0, TEXT_CENTER|TEXT_UNDERLINE },
1388 { 2, C_RED },
1389 { 42, C_RED },
1390 { -1, 0 }
1392 int button;
1394 #ifdef HAVE_LCD_COLOR
1395 rb->lcd_set_background(LCD_BLACK);
1396 rb->lcd_set_foreground(LCD_WHITE);
1397 #endif
1398 if (display_text(WORDS, help_text, formation, NULL))
1399 return true;
1400 do {
1401 button = rb->button_get(true);
1402 if (rb->default_event_handler (button) == SYS_USB_CONNECTED) {
1403 return true;
1405 } while( ( button == BUTTON_NONE )
1406 || ( button & (BUTTON_REL|BUTTON_REPEAT) ) );
1407 rb->lcd_setfont(FONT_SYSFIXED);
1409 return false;
1412 static bool _ingame;
1413 static int jewels_menu_cb(int action, const struct menu_item_ex *this_item)
1415 int i = ((intptr_t)this_item);
1416 if(action == ACTION_REQUEST_MENUITEM
1417 && !_ingame && (i==0 || i==6))
1418 return ACTION_EXIT_MENUITEM;
1419 return action;
1421 /*****************************************************************************
1422 * jewels_game_menu() shows the game menu.
1423 ******************************************************************************/
1424 static int jewels_game_menu(struct game_context* bj, bool ingame)
1426 rb->button_clear_queue();
1427 int choice = 0;
1429 _ingame = ingame;
1431 static struct opt_items mode[] = {
1432 { "Normal", -1 },
1433 { "Puzzle", -1 },
1436 MENUITEM_STRINGLIST (main_menu, "Jewels Menu", jewels_menu_cb,
1437 "Resume Game",
1438 "Start New Game",
1439 "Mode",
1440 "Help",
1441 "High Score",
1442 "Playback Control",
1443 "Quit without Saving",
1444 "Quit");
1446 while (1) {
1447 switch (rb->do_menu(&main_menu, &choice, NULL, false)) {
1448 case 0:
1449 jewels_setcolors();
1450 return 0;
1451 case 1:
1452 jewels_init(bj);
1453 return 0;
1454 case 2:
1455 rb->set_option("Mode", &bj->tmp_type, INT, mode, 2, NULL);
1456 break;
1457 case 3:
1458 if(jewels_help())
1459 return 1;
1460 break;
1461 case 4:
1462 jewels_show_highscores(NUM_SCORES);
1463 break;
1464 case 5:
1465 playback_control(NULL);
1466 break;
1467 case 6:
1468 return 1;
1469 case 7:
1470 if (ingame) {
1471 rb->splash(HZ*1, "Saving game ...");
1472 jewels_savegame(bj);
1474 return 1;
1475 case MENU_ATTACHED_USB:
1476 return 1;
1477 default:
1478 break;
1483 static int jewels_main(struct game_context* bj) {
1484 int button;
1485 int position;
1486 bool selected = false;
1487 bool no_movesavail;
1488 int x=0, y=0;
1490 bool loaded = jewels_loadgame(bj);
1491 if (jewels_game_menu(bj, loaded)!=0)
1492 return 0;
1494 while(true) {
1495 no_movesavail = false;
1497 /* refresh the board */
1498 jewels_drawboard(bj);
1500 /* display the cursor */
1501 if(selected) {
1502 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1503 rb->lcd_fillrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1504 TILE_WIDTH, TILE_HEIGHT);
1505 rb->lcd_set_drawmode(DRMODE_SOLID);
1506 } else {
1507 rb->lcd_drawrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1508 TILE_WIDTH, TILE_HEIGHT);
1510 rb->lcd_update_rect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1511 TILE_WIDTH, TILE_HEIGHT);
1513 /* handle game button presses */
1514 rb->yield();
1515 button = rb->button_get(true);
1516 switch(button){
1517 case JEWELS_LEFT: /* move cursor left */
1518 case (JEWELS_LEFT|BUTTON_REPEAT):
1519 if(selected) {
1520 bj->score += jewels_swapjewels(bj, x, y, SWAP_LEFT);
1521 selected = false;
1522 if (!jewels_movesavail(bj)) no_movesavail = true;
1523 } else {
1524 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1526 break;
1528 case JEWELS_RIGHT: /* move cursor right */
1529 case (JEWELS_RIGHT|BUTTON_REPEAT):
1530 if(selected) {
1531 bj->score += jewels_swapjewels(bj, x, y, SWAP_RIGHT);
1532 selected = false;
1533 if (!jewels_movesavail(bj)) no_movesavail = true;
1534 } else {
1535 x = (x+1)%BJ_WIDTH;
1537 break;
1539 case JEWELS_DOWN: /* move cursor down */
1540 case (JEWELS_DOWN|BUTTON_REPEAT):
1541 if(selected) {
1542 bj->score += jewels_swapjewels(bj, x, y, SWAP_DOWN);
1543 selected = false;
1544 if (!jewels_movesavail(bj)) no_movesavail = true;
1545 } else {
1546 y = (y+1)%(BJ_HEIGHT-1);
1548 break;
1550 case JEWELS_UP: /* move cursor up */
1551 case (JEWELS_UP|BUTTON_REPEAT):
1552 if(selected) {
1553 bj->score += jewels_swapjewels(bj, x, y, SWAP_UP);
1554 selected = false;
1555 if (!jewels_movesavail(bj)) no_movesavail = true;
1556 } else {
1557 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1559 break;
1561 #ifdef JEWELS_SCROLLWHEEL
1562 case JEWELS_PREV: /* scroll backwards */
1563 case (JEWELS_PREV|BUTTON_REPEAT):
1564 if(!selected) {
1565 if(x == 0) {
1566 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1568 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1570 break;
1572 case JEWELS_NEXT: /* scroll forwards */
1573 case (JEWELS_NEXT|BUTTON_REPEAT):
1574 if(!selected) {
1575 if(x == BJ_WIDTH-1) {
1576 y = (y+1)%(BJ_HEIGHT-1);
1578 x = (x+1)%BJ_WIDTH;
1580 break;
1581 #endif
1583 case JEWELS_SELECT: /* toggle selected */
1584 selected = !selected;
1585 break;
1587 #ifdef JEWELS_RC_CANCEL
1588 case JEWELS_RC_CANCEL:
1589 #endif
1590 case JEWELS_CANCEL: /* end game */
1591 if (jewels_game_menu(bj, true)!=0)
1592 return 0;
1593 break;
1595 default:
1596 if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
1597 return PLUGIN_USB_CONNECTED;
1598 break;
1601 switch(bj->type) {
1602 case GAME_TYPE_NORMAL:
1603 if(bj->score >= LEVEL_PTS)
1604 jewels_nextlevel(bj);
1605 break;
1606 case GAME_TYPE_PUZZLE:
1607 if (jewels_puzzle_is_finished(bj)) {
1608 if (bj->level < NUM_PUZZLE_LEVELS) {
1609 jewels_nextlevel(bj);
1610 } else {
1611 rb->splash(2*HZ, "Congratulations!");
1612 rb->splash(2*HZ, "You have finished the game!");
1613 if (jewels_game_menu(bj, false)!=0) {
1614 return 0;
1617 break;
1621 if (no_movesavail) {
1622 switch(bj->type) {
1623 case GAME_TYPE_NORMAL:
1624 rb->splash(HZ*2, "Game Over!");
1625 rb->lcd_clear_display();
1626 bj->score += (bj->level-1)*LEVEL_PTS;
1627 if (highscore_would_update(bj->score, highest,
1628 NUM_SCORES)) {
1629 position=highscore_update(bj->score,
1630 bj->level, "",
1631 highest,NUM_SCORES);
1632 highest_updated = true;
1633 if (position == 0) {
1634 rb->splash(HZ*2, "New High Score");
1636 jewels_show_highscores(position);
1638 break;
1639 case GAME_TYPE_PUZZLE:
1640 rb->splash(2*HZ, "Game Over");
1641 break;
1643 if (jewels_game_menu(bj, false)!=0) {
1644 return 0;
1650 /* this is the plugin entry point */
1651 enum plugin_status plugin_start(const void* parameter)
1653 (void)parameter;
1655 /* load high scores */
1656 highscore_load(HIGH_SCORE,highest,NUM_SCORES);
1657 highest_updated = false;
1659 rb->lcd_setfont(FONT_SYSFIXED);
1660 #if LCD_DEPTH > 1
1661 rb->lcd_set_backdrop(NULL);
1662 #endif
1664 struct game_context bj;
1665 bj.tmp_type = GAME_TYPE_NORMAL;
1666 jewels_main(&bj);
1667 if(highest_updated)
1668 highscore_save(HIGH_SCORE,highest,NUM_SCORES);
1669 rb->lcd_setfont(FONT_UI);
1671 return PLUGIN_OK;
1674 #endif /* HAVE_LCD_BITMAP */