Gigabeat S: Do simple direct keypad scanning rather than triggering a separate scan...
[kugel-rb.git] / apps / plugins / jewels.c
blob0df7c35d84d6f87b141610f669829acac19402e0
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 Adam Boot
12 * Color graphics from Gweled (http://sebdelestaing.free.fr/gweled/)
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ****************************************************************************/
24 #include "plugin.h"
25 #include "lib/display_text.h"
26 #include "lib/highscore.h"
27 #include "lib/playback_control.h"
28 #include "pluginbitmaps/jewels.h"
30 /* button definitions */
31 #if CONFIG_KEYPAD == RECORDER_PAD
32 #define JEWELS_UP BUTTON_UP
33 #define JEWELS_DOWN BUTTON_DOWN
34 #define JEWELS_LEFT BUTTON_LEFT
35 #define JEWELS_RIGHT BUTTON_RIGHT
36 #define JEWELS_SELECT BUTTON_PLAY
37 #define JEWELS_CANCEL BUTTON_OFF
38 #define HK_SELECT "PLAY"
39 #define HK_CANCEL "OFF"
41 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
42 #define JEWELS_UP BUTTON_UP
43 #define JEWELS_DOWN BUTTON_DOWN
44 #define JEWELS_LEFT BUTTON_LEFT
45 #define JEWELS_RIGHT BUTTON_RIGHT
46 #define JEWELS_SELECT BUTTON_SELECT
47 #define JEWELS_CANCEL BUTTON_OFF
48 #define HK_SELECT "SELECT"
49 #define HK_CANCEL "OFF"
51 #elif CONFIG_KEYPAD == ONDIO_PAD
52 #define JEWELS_UP BUTTON_UP
53 #define JEWELS_DOWN BUTTON_DOWN
54 #define JEWELS_LEFT BUTTON_LEFT
55 #define JEWELS_RIGHT BUTTON_RIGHT
56 #define JEWELS_SELECT BUTTON_MENU
57 #define JEWELS_CANCEL BUTTON_OFF
58 #define HK_SELECT "MENU"
59 #define HK_CANCEL "OFF"
61 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
62 #define JEWELS_UP BUTTON_UP
63 #define JEWELS_DOWN BUTTON_DOWN
64 #define JEWELS_LEFT BUTTON_LEFT
65 #define JEWELS_RIGHT BUTTON_RIGHT
66 #define JEWELS_SELECT BUTTON_SELECT
67 #define JEWELS_CANCEL BUTTON_OFF
68 #define JEWELS_RC_CANCEL BUTTON_RC_STOP
69 #define HK_SELECT "SELECT"
70 #define HK_CANCEL "OFF"
72 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
73 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
74 #define JEWELS_SCROLLWHEEL
75 #define JEWELS_UP BUTTON_MENU
76 #define JEWELS_DOWN BUTTON_PLAY
77 #define JEWELS_LEFT BUTTON_LEFT
78 #define JEWELS_RIGHT BUTTON_RIGHT
79 #define JEWELS_PREV BUTTON_SCROLL_BACK
80 #define JEWELS_NEXT BUTTON_SCROLL_FWD
81 #define JEWELS_SELECT BUTTON_SELECT
82 #define JEWELS_CANCEL (BUTTON_SELECT | BUTTON_MENU)
83 #define HK_SELECT "SELECT"
84 #define HK_CANCEL "SEL + MENU"
86 #elif (CONFIG_KEYPAD == IPOD_3G_PAD)
87 #define JEWELS_LEFT BUTTON_LEFT
88 #define JEWELS_RIGHT BUTTON_RIGHT
89 #define JEWELS_UP BUTTON_SCROLL_BACK
90 #define JEWELS_DOWN BUTTON_SCROLL_FWD
91 #define JEWELS_SELECT BUTTON_SELECT
92 #define JEWELS_CANCEL BUTTON_MENU
93 #define HK_SELECT "SELECT"
94 #define HK_CANCEL "MENU"
96 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
97 #define JEWELS_UP BUTTON_UP
98 #define JEWELS_DOWN BUTTON_DOWN
99 #define JEWELS_LEFT BUTTON_LEFT
100 #define JEWELS_RIGHT BUTTON_RIGHT
101 #define JEWELS_SELECT BUTTON_SELECT
102 #define JEWELS_CANCEL BUTTON_PLAY
103 #define HK_SELECT "SELECT"
104 #define HK_CANCEL "PLAY"
106 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
107 #define JEWELS_UP BUTTON_UP
108 #define JEWELS_DOWN BUTTON_DOWN
109 #define JEWELS_LEFT BUTTON_LEFT
110 #define JEWELS_RIGHT BUTTON_RIGHT
111 #define JEWELS_SELECT BUTTON_SELECT
112 #define JEWELS_CANCEL BUTTON_POWER
113 #define HK_SELECT "SELECT"
114 #define HK_CANCEL "POWER"
116 #elif CONFIG_KEYPAD == GIGABEAT_PAD
117 #define JEWELS_UP BUTTON_UP
118 #define JEWELS_DOWN BUTTON_DOWN
119 #define JEWELS_LEFT BUTTON_LEFT
120 #define JEWELS_RIGHT BUTTON_RIGHT
121 #define JEWELS_SELECT BUTTON_SELECT
122 #define JEWELS_CANCEL BUTTON_POWER
123 #define HK_SELECT "SELECT"
124 #define HK_CANCEL "POWER"
126 #elif CONFIG_KEYPAD == SANSA_E200_PAD
127 #define JEWELS_SCROLLWHEEL
128 #define JEWELS_UP BUTTON_UP
129 #define JEWELS_DOWN BUTTON_DOWN
130 #define JEWELS_LEFT BUTTON_LEFT
131 #define JEWELS_RIGHT BUTTON_RIGHT
132 #define JEWELS_PREV BUTTON_SCROLL_BACK
133 #define JEWELS_NEXT BUTTON_SCROLL_FWD
134 #define JEWELS_SELECT BUTTON_SELECT
135 #define JEWELS_CANCEL BUTTON_POWER
136 #define HK_SELECT "SELECT"
137 #define HK_CANCEL "POWER"
139 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
140 #define JEWELS_SCROLLWHEEL
141 #define JEWELS_UP BUTTON_UP
142 #define JEWELS_DOWN BUTTON_DOWN
143 #define JEWELS_LEFT BUTTON_LEFT
144 #define JEWELS_RIGHT BUTTON_RIGHT
145 #define JEWELS_PREV BUTTON_SCROLL_BACK
146 #define JEWELS_NEXT BUTTON_SCROLL_FWD
147 #define JEWELS_SELECT BUTTON_SELECT
148 #define JEWELS_CANCEL (BUTTON_HOME|BUTTON_REPEAT)
149 #define HK_SELECT "SELECT"
150 #define HK_CANCEL "HOME"
152 #elif CONFIG_KEYPAD == SANSA_C200_PAD || \
153 CONFIG_KEYPAD == SANSA_CLIP_PAD || \
154 CONFIG_KEYPAD == SANSA_M200_PAD
155 #define JEWELS_UP BUTTON_UP
156 #define JEWELS_DOWN BUTTON_DOWN
157 #define JEWELS_LEFT BUTTON_LEFT
158 #define JEWELS_RIGHT BUTTON_RIGHT
159 #define JEWELS_SELECT BUTTON_SELECT
160 #define JEWELS_CANCEL BUTTON_POWER
161 #define HK_SELECT "SELECT"
162 #define HK_CANCEL "POWER"
164 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
165 #define JEWELS_UP BUTTON_SCROLL_UP
166 #define JEWELS_DOWN BUTTON_SCROLL_DOWN
167 #define JEWELS_LEFT BUTTON_LEFT
168 #define JEWELS_RIGHT BUTTON_RIGHT
169 #define JEWELS_SELECT BUTTON_PLAY
170 #define JEWELS_CANCEL BUTTON_POWER
171 #define HK_SELECT "PLAY"
172 #define HK_CANCEL "POWER"
174 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
175 #define JEWELS_UP BUTTON_UP
176 #define JEWELS_DOWN BUTTON_DOWN
177 #define JEWELS_LEFT BUTTON_LEFT
178 #define JEWELS_RIGHT BUTTON_RIGHT
179 #define JEWELS_SELECT BUTTON_SELECT
180 #define JEWELS_CANCEL BUTTON_BACK
181 #define HK_SELECT "SELECT"
182 #define HK_CANCEL "BACK"
184 #elif CONFIG_KEYPAD == MROBE100_PAD
185 #define JEWELS_UP BUTTON_UP
186 #define JEWELS_DOWN BUTTON_DOWN
187 #define JEWELS_LEFT BUTTON_LEFT
188 #define JEWELS_RIGHT BUTTON_RIGHT
189 #define JEWELS_SELECT BUTTON_SELECT
190 #define JEWELS_CANCEL BUTTON_POWER
191 #define HK_SELECT "SELECT"
192 #define HK_CANCEL "POWER"
194 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
195 #define JEWELS_UP BUTTON_RC_VOL_UP
196 #define JEWELS_DOWN BUTTON_RC_VOL_DOWN
197 #define JEWELS_LEFT BUTTON_RC_REW
198 #define JEWELS_RIGHT BUTTON_RC_FF
199 #define JEWELS_SELECT BUTTON_RC_PLAY
200 #define JEWELS_CANCEL BUTTON_RC_REC
201 #define HK_SELECT "PLAY"
202 #define HK_CANCEL "REC"
204 #define JEWELS_RC_CANCEL BUTTON_REC
206 #elif CONFIG_KEYPAD == COWON_D2_PAD
207 #define JEWELS_CANCEL BUTTON_POWER
208 #define HK_CANCEL "POWER"
210 #elif CONFIG_KEYPAD == IAUDIO67_PAD
211 #define JEWELS_UP BUTTON_STOP
212 #define JEWELS_DOWN BUTTON_PLAY
213 #define JEWELS_LEFT BUTTON_LEFT
214 #define JEWELS_RIGHT BUTTON_RIGHT
215 #define JEWELS_SELECT BUTTON_MENU
216 #define JEWELS_CANCEL BUTTON_POWER
217 #define HK_SELECT "MENU"
218 #define HK_CANCEL "POWER"
220 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
221 #define JEWELS_UP BUTTON_UP
222 #define JEWELS_DOWN BUTTON_DOWN
223 #define JEWELS_LEFT BUTTON_LEFT
224 #define JEWELS_RIGHT BUTTON_RIGHT
225 #define JEWELS_SELECT BUTTON_SELECT
226 #define JEWELS_CANCEL BUTTON_BACK
227 #define HK_SELECT "MIDDLE"
228 #define HK_CANCEL "BACK"
230 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
231 #define JEWELS_UP BUTTON_UP
232 #define JEWELS_DOWN BUTTON_DOWN
233 #define JEWELS_LEFT BUTTON_LEFT
234 #define JEWELS_RIGHT BUTTON_RIGHT
235 #define JEWELS_SELECT BUTTON_SELECT
236 #define JEWELS_CANCEL BUTTON_POWER
237 #define HK_SELECT "SELECT"
238 #define HK_CANCEL "POWER"
240 #elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
241 #define JEWELS_UP BUTTON_UP
242 #define JEWELS_DOWN BUTTON_DOWN
243 #define JEWELS_LEFT BUTTON_LEFT
244 #define JEWELS_RIGHT BUTTON_RIGHT
245 #define JEWELS_SELECT BUTTON_PLAY
246 #define JEWELS_CANCEL BUTTON_POWER
247 #define HK_SELECT "PLAY"
248 #define HK_CANCEL "POWER"
250 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
251 #define JEWELS_UP BUTTON_UP
252 #define JEWELS_DOWN BUTTON_DOWN
253 #define JEWELS_LEFT BUTTON_PREV
254 #define JEWELS_RIGHT BUTTON_NEXT
255 #define JEWELS_SELECT BUTTON_PLAY
256 #define JEWELS_CANCEL BUTTON_POWER
257 #define HK_SELECT "PLAY"
258 #define HK_CANCEL "POWER"
260 #elif CONFIG_KEYPAD == ONDAVX747_PAD || \
261 CONFIG_KEYPAD == ONDAVX777_PAD || \
262 CONFIG_KEYPAD == MROBE500_PAD
263 #define JEWELS_CANCEL BUTTON_POWER
264 #define HK_CANCEL "POWER"
266 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
267 #define JEWELS_UP BUTTON_UP
268 #define JEWELS_DOWN BUTTON_DOWN
269 #define JEWELS_LEFT BUTTON_LEFT
270 #define JEWELS_RIGHT BUTTON_RIGHT
271 #define JEWELS_SELECT BUTTON_PLAY
272 #define JEWELS_CANCEL BUTTON_REW
273 #define HK_SELECT "PLAY"
274 #define HK_CANCEL "REWIND"
276 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
277 #define JEWELS_UP BUTTON_UP
278 #define JEWELS_DOWN BUTTON_DOWN
279 #define JEWELS_LEFT BUTTON_PREV
280 #define JEWELS_RIGHT BUTTON_NEXT
281 #define JEWELS_SELECT BUTTON_OK
282 #define JEWELS_CANCEL BUTTON_REC
283 #define HK_SELECT "OK"
284 #define HK_CANCEL "REC"
286 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
287 #define JEWELS_LEFT BUTTON_VOL_DOWN
288 #define JEWELS_RIGHT BUTTON_VOL_UP
289 #define JEWELS_UP BUTTON_REW
290 #define JEWELS_DOWN BUTTON_FF
291 #define JEWELS_SELECT BUTTON_FUNC
292 #define JEWELS_CANCEL BUTTON_REC
293 #define HK_SELECT "FUNC"
294 #define HK_CANCEL "REC"
296 #elif CONFIG_KEYPAD == MPIO_HD300_PAD
297 #define JEWELS_LEFT BUTTON_REW
298 #define JEWELS_RIGHT BUTTON_FF
299 #define JEWELS_UP BUTTON_UP
300 #define JEWELS_DOWN BUTTON_DOWN
301 #define JEWELS_SELECT BUTTON_ENTER
302 #define JEWELS_CANCEL BUTTON_MENU
303 #define HK_SELECT "ENTER"
304 #define HK_CANCEL "MENU"
306 #else
307 #error No keymap defined!
308 #endif
310 #ifdef HAVE_TOUCHSCREEN
311 #ifndef JEWELS_UP
312 #define JEWELS_UP BUTTON_TOPMIDDLE
313 #endif
314 #ifndef JEWELS_DOWN
315 #define JEWELS_DOWN BUTTON_BOTTOMMIDDLE
316 #endif
317 #ifndef JEWELS_LEFT
318 #define JEWELS_LEFT BUTTON_MIDLEFT
319 #endif
320 #ifndef JEWELS_RIGHT
321 #define JEWELS_RIGHT BUTTON_MIDRIGHT
322 #endif
323 #ifndef JEWELS_SELECT
324 #define JEWELS_SELECT BUTTON_CENTER
325 #define HK_SELECT "CENTER"
326 #endif
327 #ifndef JEWELS_CANCEL
328 #define JEWELS_CANCEL BUTTON_TOPLEFT
329 #define HK_CANCEL "TOPLEFT"
330 #endif
331 #endif
333 #define TILE_WIDTH BMPWIDTH_jewels
334 #define TILE_HEIGHT (BMPHEIGHT_jewels/23)
336 #if LCD_HEIGHT < LCD_WIDTH
337 /* This calculation assumes integer division w/ LCD_HEIGHT/TILE_HEIGHT */
338 #define YOFS LCD_HEIGHT-((LCD_HEIGHT/TILE_HEIGHT)*TILE_HEIGHT)
339 #else
340 #define YOFS 0
341 #endif
343 #define NUM_SCORES 5
345 /* swap directions */
346 #define SWAP_UP 0
347 #define SWAP_RIGHT 1
348 #define SWAP_DOWN 2
349 #define SWAP_LEFT 3
351 /* play board dimension */
352 #define BJ_HEIGHT 9
353 #define BJ_WIDTH 8
355 /* next level threshold */
356 #define LEVEL_PTS 100
358 /* animation frame rate */
359 #define MAX_FPS 20
361 /* text margin */
362 #define MARGIN 5
364 /* Game types */
365 enum game_type {
366 GAME_TYPE_NORMAL,
367 GAME_TYPE_PUZZLE
370 /* external bitmaps */
371 extern const fb_data jewels[];
373 /* tile background colors */
374 #ifdef HAVE_LCD_COLOR
375 static const unsigned jewels_bkgd[2] = {
376 LCD_RGBPACK(104, 63, 63),
377 LCD_RGBPACK(83, 44, 44)
379 #endif
381 /* the tile struct
382 * type is the jewel number 0-7
383 * falling if the jewel is falling
384 * delete marks the jewel for deletion
386 struct tile {
387 int type;
388 bool falling;
389 bool delete;
392 /* the game context struct
393 * score is the current level score
394 * segments is the number of cleared segments in the current run
395 * level is the current level
396 * tmp_type is the select type in the menu
397 * type is the game type (normal or puzzle)
398 * playboard is the game playing board (first row is hidden)
399 * num_jewels is the number of different jewels to use
401 struct game_context {
402 unsigned int score;
403 unsigned int segments;
404 unsigned int level;
405 unsigned int type;
406 unsigned int tmp_type;
407 struct tile playboard[BJ_HEIGHT][BJ_WIDTH];
408 unsigned int num_jewels;
411 #define MAX_NUM_JEWELS 7
413 #define MAX_PUZZLE_TILES 4
414 #define NUM_PUZZLE_LEVELS 10
416 struct puzzle_tile {
417 int x;
418 int y;
419 int tile_type;
422 struct puzzle_level {
423 unsigned int num_jewels;
424 unsigned int num_tiles;
425 struct puzzle_tile tiles[MAX_PUZZLE_TILES];
428 #define PUZZLE_TILE_UP 1
429 #define PUZZLE_TILE_DOWN 2
430 #define PUZZLE_TILE_LEFT 4
431 #define PUZZLE_TILE_RIGHT 8
433 struct puzzle_level puzzle_levels[NUM_PUZZLE_LEVELS] = {
434 { 5, 2, { {3, 3, PUZZLE_TILE_RIGHT},
435 {4, 2, PUZZLE_TILE_LEFT} } },
436 { 5, 2, { {3, 2, PUZZLE_TILE_DOWN},
437 {3, 4, PUZZLE_TILE_UP} } },
438 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
439 {3, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
440 {3, 6, PUZZLE_TILE_UP} } },
441 { 6, 3, { {3, 2, PUZZLE_TILE_RIGHT},
442 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
443 {5, 4, PUZZLE_TILE_LEFT} } },
444 { 6, 2, { {3, 4, PUZZLE_TILE_RIGHT},
445 {4, 2, PUZZLE_TILE_LEFT} } },
446 { 6, 2, { {3, 2, PUZZLE_TILE_DOWN},
447 {4, 4, PUZZLE_TILE_UP} } },
448 { 7, 4, { {3, 2, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
449 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
450 {3, 4, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
451 {4, 4, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
452 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
453 {4, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
454 {3, 6, PUZZLE_TILE_UP} } },
455 { 7, 3, { {2, 2, PUZZLE_TILE_RIGHT},
456 {4, 1, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
457 {5, 4, PUZZLE_TILE_LEFT} } },
458 { 7, 4, { {3, 0, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
459 {5, 0, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
460 {2, 7, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
461 {4, 7, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
464 #define SAVE_FILE PLUGIN_GAMES_DIR "/jewels.save"
465 #define SCORE_FILE PLUGIN_GAMES_DIR "/jewels.score"
466 struct highscore highscores[NUM_SCORES];
468 static bool resume_file = false;
470 /*****************************************************************************
471 * jewels_setcolors() set the foreground and background colors.
472 ******************************************************************************/
473 static inline void jewels_setcolors(void) {
474 #ifdef HAVE_LCD_COLOR
475 rb->lcd_set_background(LCD_RGBPACK(49, 26, 26));
476 rb->lcd_set_foreground(LCD_RGBPACK(210, 181, 181));
477 #endif
480 /*****************************************************************************
481 * jewels_loadgame() loads the saved game and returns load success.
482 ******************************************************************************/
483 static bool jewels_loadgame(struct game_context* bj)
485 int fd;
486 bool loaded = false;
488 /* open game file */
489 fd = rb->open(SAVE_FILE, O_RDONLY);
490 if(fd < 0) return loaded;
492 /* read in saved game */
493 while(true) {
494 if(rb->read(fd, &bj->tmp_type, sizeof(bj->tmp_type)) <= 0) break;
495 if(rb->read(fd, &bj->type, sizeof(bj->type)) <= 0) break;
496 if(rb->read(fd, &bj->score, sizeof(bj->score)) <= 0) break;
497 if(rb->read(fd, &bj->level, sizeof(bj->level)) <= 0) break;
498 if(rb->read(fd, &bj->segments, sizeof(bj->segments)) <= 0) break;
499 if(rb->read(fd, &bj->num_jewels, sizeof(bj->num_jewels)) <= 0) break;
500 if(rb->read(fd, bj->playboard, sizeof(bj->playboard)) <= 0) break;
501 loaded = true;
502 break;
505 rb->close(fd);
507 return loaded;
510 /*****************************************************************************
511 * jewels_savegame() saves the current game state.
512 ******************************************************************************/
513 static void jewels_savegame(struct game_context* bj)
515 int fd;
516 /* write out the game state to the save file */
517 fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT, 0666);
518 if(fd < 0) return;
520 rb->write(fd, &bj->tmp_type, sizeof(bj->tmp_type));
521 rb->write(fd, &bj->type, sizeof(bj->type));
522 rb->write(fd, &bj->score, sizeof(bj->score));
523 rb->write(fd, &bj->level, sizeof(bj->level));
524 rb->write(fd, &bj->segments, sizeof(bj->segments));
525 rb->write(fd, &bj->num_jewels, sizeof(bj->num_jewels));
526 rb->write(fd, bj->playboard, sizeof(bj->playboard));
527 rb->close(fd);
530 /*****************************************************************************
531 * jewels_drawboard() redraws the entire game board.
532 ******************************************************************************/
533 static void jewels_drawboard(struct game_context* bj) {
534 int i, j;
535 int w, h;
536 unsigned int tempscore;
537 unsigned int size;
538 char *title = "Level";
539 char str[10];
541 if (bj->type == GAME_TYPE_NORMAL) {
542 tempscore = (bj->score>LEVEL_PTS ? LEVEL_PTS : bj->score);
543 size = LEVEL_PTS;
544 } else {
545 tempscore = (bj->level>NUM_PUZZLE_LEVELS ? NUM_PUZZLE_LEVELS : bj->level);
546 size = NUM_PUZZLE_LEVELS;
549 /* clear screen */
550 rb->lcd_clear_display();
552 /* dispay playing board */
553 for(i=0; i<BJ_HEIGHT-1; i++){
554 for(j=0; j<BJ_WIDTH; j++){
555 #ifdef HAVE_LCD_COLOR
556 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
557 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
558 TILE_WIDTH, TILE_HEIGHT);
559 rb->lcd_bitmap_transparent_part(jewels,
560 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
561 STRIDE( SCREEN_MAIN,
562 BMPWIDTH_jewels, BMPHEIGHT_jewels),
563 j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
564 TILE_WIDTH, TILE_HEIGHT);
565 #else
566 rb->lcd_bitmap_part(jewels,
567 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
568 STRIDE( SCREEN_MAIN,
569 BMPWIDTH_jewels, BMPHEIGHT_jewels),
570 j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
571 TILE_WIDTH, TILE_HEIGHT);
572 #endif
576 #if LCD_WIDTH > LCD_HEIGHT /* horizontal layout */
578 /* draw separator lines */
579 jewels_setcolors();
580 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, LCD_HEIGHT-1);
582 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, 18);
583 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, LCD_HEIGHT-10);
585 /* draw progress bar */
586 #ifdef HAVE_LCD_COLOR
587 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
588 #endif
589 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
590 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
591 tempscore/size),
592 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
593 ((LCD_HEIGHT-10)-18)*tempscore/size);
594 #ifdef HAVE_LCD_COLOR
595 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
596 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
597 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
598 tempscore/size)+1,
599 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
600 ((LCD_HEIGHT-10)-18)*tempscore/size-1);
601 jewels_setcolors();
602 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
603 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*tempscore/size),
604 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
605 ((LCD_HEIGHT-10)-18)*tempscore/size+1);
606 #endif
608 /* print text */
609 rb->lcd_getstringsize(title, &w, &h);
610 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 1, title);
611 rb->snprintf(str, 4, "%d", bj->level);
612 rb->lcd_getstringsize(str, &w, &h);
613 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 10, str);
615 if (bj->type == GAME_TYPE_NORMAL) {
616 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
617 rb->lcd_getstringsize(str, &w, &h);
618 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2,
619 LCD_HEIGHT-8, str);
622 #elif LCD_WIDTH < LCD_HEIGHT /* vertical layout */
624 /* draw separator lines */
625 jewels_setcolors();
626 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
627 rb->lcd_hline(0, LCD_WIDTH-1, LCD_HEIGHT-14);
628 rb->lcd_vline(LCD_WIDTH/2, LCD_HEIGHT-14, LCD_HEIGHT-1);
630 /* draw progress bar */
631 #ifdef HAVE_LCD_COLOR
632 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
633 #endif
634 rb->lcd_fillrect(0, (8*TILE_HEIGHT+YOFS)
635 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
636 LCD_WIDTH*tempscore/size,
637 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
638 #ifdef HAVE_LCD_COLOR
639 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
640 rb->lcd_drawrect(1, (8*TILE_HEIGHT+YOFS)
641 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4+1,
642 LCD_WIDTH*tempscore/size-1,
643 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2-2);
644 jewels_setcolors();
645 rb->lcd_drawrect(0, (8*TILE_HEIGHT+YOFS)
646 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
647 LCD_WIDTH*tempscore/size+1,
648 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
649 #endif
651 /* print text */
652 rb->lcd_putsxyf(1, LCD_HEIGHT-10, "%s %d", title, bj->level);
654 if (bj->type == GAME_TYPE_NORMAL) {
655 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
656 rb->lcd_getstringsize(str, &w, &h);
657 rb->lcd_putsxy((LCD_WIDTH-2)-w, LCD_HEIGHT-10, str);
661 #else /* square layout */
663 /* draw separator lines */
664 jewels_setcolors();
665 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
666 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, 8*TILE_HEIGHT+YOFS);
667 rb->lcd_vline(LCD_WIDTH/2, 8*TILE_HEIGHT+YOFS, LCD_HEIGHT-1);
669 /* draw progress bar */
670 #ifdef HAVE_LCD_COLOR
671 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
672 #endif
673 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
674 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)*tempscore/size,
675 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
676 (8*TILE_HEIGHT+YOFS)*tempscore/size);
677 #ifdef HAVE_LCD_COLOR
678 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
679 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
680 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
681 *tempscore/size+1,
682 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
683 (8*TILE_HEIGHT+YOFS)*tempscore/size-1);
684 jewels_setcolors();
685 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
686 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
687 *tempscore/size,
688 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
689 (8*TILE_HEIGHT+YOFS)*tempscore/size+1);
690 #endif
692 /* print text */
693 rb->lcd_putsxyf(1, LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3,"%s %d",
694 title, bj->level);
696 if (bj->type == GAME_TYPE_NORMAL) {
697 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
698 rb->lcd_getstringsize(str, &w, &h);
699 rb->lcd_putsxy((LCD_WIDTH-2)-w,
700 LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
703 #endif /* layout */
705 rb->lcd_update();
708 /*****************************************************************************
709 * jewels_putjewels() makes the jewels fall to fill empty spots and adds
710 * new random jewels at the empty spots at the top of each row.
711 ******************************************************************************/
712 static void jewels_putjewels(struct game_context* bj){
713 int i, j, k;
714 bool mark, done;
715 long lasttick, currenttick;
717 /* loop to make all the jewels fall */
718 while(true) {
719 /* mark falling jewels and add new jewels to hidden top row*/
720 mark = false;
721 done = true;
722 for(j=0; j<BJ_WIDTH; j++) {
723 if(bj->playboard[1][j].type == 0) {
724 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
726 for(i=BJ_HEIGHT-2; i>=0; i--) {
727 if(!mark && bj->playboard[i+1][j].type == 0) {
728 mark = true;
729 done = false;
731 if(mark) bj->playboard[i][j].falling = true;
733 /*if(bj->playboard[1][j].falling) {
734 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
735 bj->playboard[0][j].falling = true;
737 mark = false;
740 /* break if there are no falling jewels */
741 if(done) break;
743 /* animate falling jewels */
744 lasttick = *rb->current_tick;
746 for(k=1; k<=8; k++) {
747 for(i=BJ_HEIGHT-2; i>=0; i--) {
748 for(j=0; j<BJ_WIDTH; j++) {
749 if(bj->playboard[i][j].falling &&
750 bj->playboard[i][j].type != 0) {
751 /* clear old position */
752 #ifdef HAVE_LCD_COLOR
753 if(i == 0 && YOFS) {
754 rb->lcd_set_foreground(rb->lcd_get_background());
755 } else {
756 rb->lcd_set_foreground(jewels_bkgd[(i-1+j)%2]);
758 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
759 TILE_WIDTH, TILE_HEIGHT);
760 if(bj->playboard[i+1][j].type == 0) {
761 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
762 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
763 TILE_WIDTH, TILE_HEIGHT);
765 #else
766 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
767 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
768 TILE_WIDTH, TILE_HEIGHT);
769 if(bj->playboard[i+1][j].type == 0) {
770 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
771 TILE_WIDTH, TILE_HEIGHT);
773 rb->lcd_set_drawmode(DRMODE_SOLID);
774 #endif
776 /* draw new position */
777 #ifdef HAVE_LCD_COLOR
778 rb->lcd_bitmap_transparent_part(jewels, 0,
779 TILE_HEIGHT*(bj->playboard[i][j].type),
780 STRIDE( SCREEN_MAIN,
781 BMPWIDTH_jewels,
782 BMPHEIGHT_jewels),
783 j*TILE_WIDTH,
784 (i-1)*TILE_HEIGHT+YOFS+
785 ((((TILE_HEIGHT<<10)*k)/8)>>10),
786 TILE_WIDTH, TILE_HEIGHT);
787 #else
788 rb->lcd_bitmap_part(jewels, 0,
789 TILE_HEIGHT*(bj->playboard[i][j].type),
790 STRIDE( SCREEN_MAIN,
791 BMPWIDTH_jewels,
792 BMPHEIGHT_jewels),
793 j*TILE_WIDTH,
794 (i-1)*TILE_HEIGHT+YOFS+
795 ((((TILE_HEIGHT<<10)*k)/8)>>10),
796 TILE_WIDTH, TILE_HEIGHT);
797 #endif
802 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
803 jewels_setcolors();
805 /* framerate limiting */
806 currenttick = *rb->current_tick;
807 if(currenttick-lasttick < HZ/MAX_FPS) {
808 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
809 } else {
810 rb->yield();
812 lasttick = currenttick;
815 /* shift jewels down */
816 for(j=0; j<BJ_WIDTH; j++) {
817 for(i=BJ_HEIGHT-1; i>=1; i--) {
818 if(bj->playboard[i-1][j].falling) {
819 bj->playboard[i][j].type = bj->playboard[i-1][j].type;
824 /* clear out top row */
825 for(j=0; j<BJ_WIDTH; j++) {
826 bj->playboard[0][j].type = 0;
829 /* mark everything not falling */
830 for(i=0; i<BJ_HEIGHT; i++) {
831 for(j=0; j<BJ_WIDTH; j++) {
832 bj->playboard[i][j].falling = false;
838 /*****************************************************************************
839 * jewels_clearjewels() finds all the connected rows and columns and
840 * calculates and returns the points earned.
841 ******************************************************************************/
842 static unsigned int jewels_clearjewels(struct game_context* bj) {
843 int i, j;
844 int last, run;
845 unsigned int points = 0;
847 /* check for connected rows */
848 for(i=1; i<BJ_HEIGHT; i++) {
849 last = 0;
850 run = 1;
851 for(j=0; j<BJ_WIDTH; j++) {
852 if(bj->playboard[i][j].type == last &&
853 bj->playboard[i][j].type != 0 &&
854 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
855 run++;
857 if(run == 3) {
858 bj->segments++;
859 points += bj->segments;
860 bj->playboard[i][j].delete = true;
861 bj->playboard[i][j-1].delete = true;
862 bj->playboard[i][j-2].delete = true;
863 } else if(run > 3) {
864 points++;
865 bj->playboard[i][j].delete = true;
867 } else {
868 run = 1;
869 last = bj->playboard[i][j].type;
874 /* check for connected columns */
875 for(j=0; j<BJ_WIDTH; j++) {
876 last = 0;
877 run = 1;
878 for(i=1; i<BJ_HEIGHT; i++) {
879 if(bj->playboard[i][j].type != 0 &&
880 bj->playboard[i][j].type == last &&
881 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
882 run++;
884 if(run == 3) {
885 bj->segments++;
886 points += bj->segments;
887 bj->playboard[i][j].delete = true;
888 bj->playboard[i-1][j].delete = true;
889 bj->playboard[i-2][j].delete = true;
890 } else if(run > 3) {
891 points++;
892 bj->playboard[i][j].delete = true;
894 } else {
895 run = 1;
896 last = bj->playboard[i][j].type;
901 /* clear deleted jewels */
902 for(i=1; i<BJ_HEIGHT; i++) {
903 for(j=0; j<BJ_WIDTH; j++) {
904 if(bj->playboard[i][j].delete) {
905 bj->playboard[i][j].delete = false;
906 bj->playboard[i][j].type = 0;
911 return points;
914 /*****************************************************************************
915 * jewels_runboard() runs the board until it settles in a fixed state and
916 * returns points earned.
917 ******************************************************************************/
918 static unsigned int jewels_runboard(struct game_context* bj) {
919 unsigned int points = 0;
920 unsigned int ret;
922 bj->segments = 0;
924 while((ret = jewels_clearjewels(bj)) > 0) {
925 points += ret;
926 jewels_drawboard(bj);
927 jewels_putjewels(bj);
930 return points;
933 /*****************************************************************************
934 * jewels_swapjewels() swaps two jewels as long as it results in points and
935 * returns points earned.
936 ******************************************************************************/
937 static unsigned int jewels_swapjewels(struct game_context* bj,
938 int x, int y, int direc) {
939 int k;
940 int horzmod, vertmod;
941 int movelen = 0;
942 bool undo = false;
943 unsigned int points = 0;
944 long lasttick, currenttick;
946 /* check for invalid parameters */
947 if(x < 0 || x >= BJ_WIDTH || y < 0 || y >= BJ_HEIGHT-1 ||
948 direc < SWAP_UP || direc > SWAP_LEFT) {
949 return 0;
952 /* check for invalid directions */
953 if((x == 0 && direc == SWAP_LEFT) ||
954 (x == BJ_WIDTH-1 && direc == SWAP_RIGHT) ||
955 (y == 0 && direc == SWAP_UP) ||
956 (y == BJ_HEIGHT-2 && direc == SWAP_DOWN)) {
957 return 0;
960 /* set direction variables */
961 horzmod = 0;
962 vertmod = 0;
963 switch(direc) {
964 case SWAP_UP:
965 vertmod = -1;
966 movelen = TILE_HEIGHT;
967 break;
968 case SWAP_RIGHT:
969 horzmod = 1;
970 movelen = TILE_WIDTH;
971 break;
972 case SWAP_DOWN:
973 vertmod = 1;
974 movelen = TILE_HEIGHT;
975 break;
976 case SWAP_LEFT:
977 horzmod = -1;
978 movelen = TILE_WIDTH;
979 break;
982 while(true) {
983 lasttick = *rb->current_tick;
985 /* animate swapping jewels */
986 for(k=0; k<=8; k++) {
987 /* clear old position */
988 #ifdef HAVE_LCD_COLOR
989 rb->lcd_set_foreground(jewels_bkgd[(x+y)%2]);
990 rb->lcd_fillrect(x*TILE_WIDTH,
991 y*TILE_HEIGHT+YOFS,
992 TILE_WIDTH, TILE_HEIGHT);
993 rb->lcd_set_foreground(jewels_bkgd[(x+horzmod+y+vertmod)%2]);
994 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
995 (y+vertmod)*TILE_HEIGHT+YOFS,
996 TILE_WIDTH, TILE_HEIGHT);
997 #else
998 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
999 rb->lcd_fillrect(x*TILE_WIDTH,
1000 y*TILE_HEIGHT+YOFS,
1001 TILE_WIDTH, TILE_HEIGHT);
1002 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
1003 (y+vertmod)*TILE_HEIGHT+YOFS,
1004 TILE_WIDTH, TILE_HEIGHT);
1005 rb->lcd_set_drawmode(DRMODE_SOLID);
1006 #endif
1007 /* draw new position */
1008 #ifdef HAVE_LCD_COLOR
1009 rb->lcd_bitmap_transparent_part(jewels,
1010 0, TILE_HEIGHT*(bj->playboard
1011 [y+1+vertmod][x+horzmod].type),
1012 STRIDE( SCREEN_MAIN,
1013 BMPWIDTH_jewels, BMPHEIGHT_jewels),
1014 (x+horzmod)*TILE_WIDTH-horzmod*
1015 ((((movelen<<10)*k)/8)>>10),
1016 (y+vertmod)*TILE_HEIGHT-vertmod*
1017 ((((movelen<<10)*k)/8)>>10)+YOFS,
1018 TILE_WIDTH, TILE_HEIGHT);
1019 rb->lcd_bitmap_transparent_part(jewels,
1020 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
1021 STRIDE( SCREEN_MAIN,
1022 BMPWIDTH_jewels, BMPHEIGHT_jewels),
1023 x*TILE_WIDTH+horzmod*
1024 ((((movelen<<10)*k)/8)>>10),
1025 y*TILE_HEIGHT+vertmod*
1026 ((((movelen<<10)*k)/8)>>10)+YOFS,
1027 TILE_WIDTH, TILE_HEIGHT);
1028 #else
1029 rb->lcd_bitmap_part(jewels,
1030 0, TILE_HEIGHT*(bj->playboard
1031 [y+1+vertmod][x+horzmod].type),
1032 STRIDE( SCREEN_MAIN,
1033 BMPWIDTH_jewels, BMPHEIGHT_jewels),
1034 (x+horzmod)*TILE_WIDTH-horzmod*
1035 ((((movelen<<10)*k)/8)>>10),
1036 (y+vertmod)*TILE_HEIGHT-vertmod*
1037 ((((movelen<<10)*k)/8)>>10)+YOFS,
1038 TILE_WIDTH, TILE_HEIGHT);
1039 rb->lcd_set_drawmode(DRMODE_FG);
1040 rb->lcd_bitmap_part(jewels,
1041 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
1042 STRIDE( SCREEN_MAIN,
1043 BMPWIDTH_jewels, BMPHEIGHT_jewels),
1044 x*TILE_WIDTH+horzmod*
1045 ((((movelen<<10)*k)/8)>>10),
1046 y*TILE_HEIGHT+vertmod*
1047 ((((movelen<<10)*k)/8)>>10)+YOFS,
1048 TILE_WIDTH, TILE_HEIGHT);
1049 rb->lcd_set_drawmode(DRMODE_SOLID);
1050 #endif
1052 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
1053 jewels_setcolors();
1055 /* framerate limiting */
1056 currenttick = *rb->current_tick;
1057 if(currenttick-lasttick < HZ/MAX_FPS) {
1058 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
1059 } else {
1060 rb->yield();
1062 lasttick = currenttick;
1065 /* swap jewels */
1066 int temp = bj->playboard[y+1][x].type;
1067 bj->playboard[y+1][x].type =
1068 bj->playboard[y+1+vertmod][x+horzmod].type;
1069 bj->playboard[y+1+vertmod][x+horzmod].type = temp;
1071 if(undo) break;
1073 points = jewels_runboard(bj);
1074 if(points == 0) {
1075 undo = true;
1076 } else {
1077 break;
1081 return points;
1084 /*****************************************************************************
1085 * jewels_movesavail() uses pattern matching to see if there are any
1086 * available move left.
1087 ******************************************************************************/
1088 static bool jewels_movesavail(struct game_context* bj) {
1089 int i, j;
1090 bool moves = false;
1091 int mytype;
1093 for(i=1; i<BJ_HEIGHT; i++) {
1094 for(j=0; j<BJ_WIDTH; j++) {
1095 mytype = bj->playboard[i][j].type;
1096 if(mytype == 0 || mytype > MAX_NUM_JEWELS) continue;
1098 /* check horizontal patterns */
1099 if(j <= BJ_WIDTH-3) {
1100 if(i > 1) {
1101 if(bj->playboard[i-1][j+1].type == mytype) {
1102 if(bj->playboard[i-1][j+2].type == mytype)
1103 {moves = true; break;}
1104 if(bj->playboard[i][j+2].type == mytype)
1105 {moves = true; break;}
1107 if(bj->playboard[i][j+1].type == mytype) {
1108 if(bj->playboard[i-1][j+2].type == mytype)
1109 {moves = true; break;}
1113 if(j <= BJ_WIDTH-4) {
1114 if(bj->playboard[i][j+3].type == mytype) {
1115 if(bj->playboard[i][j+1].type == mytype)
1116 {moves = true; break;}
1117 if(bj->playboard[i][j+2].type == mytype)
1118 {moves = true; break;}
1122 if(i < BJ_HEIGHT-1) {
1123 if(bj->playboard[i][j+1].type == mytype) {
1124 if(bj->playboard[i+1][j+2].type == mytype)
1125 {moves = true; break;}
1127 if(bj->playboard[i+1][j+1].type == mytype) {
1128 if(bj->playboard[i][j+2].type == mytype)
1129 {moves = true; break;}
1130 if(bj->playboard[i+1][j+2].type == mytype)
1131 {moves = true; break;}
1136 /* check vertical patterns */
1137 if(i <= BJ_HEIGHT-3) {
1138 if(j > 0) {
1139 if(bj->playboard[i+1][j-1].type == mytype) {
1140 if(bj->playboard[i+2][j-1].type == mytype)
1141 {moves = true; break;}
1142 if(bj->playboard[i+2][j].type == mytype)
1143 {moves = true; break;}
1145 if(bj->playboard[i+1][j].type == mytype) {
1146 if(bj->playboard[i+2][j-1].type == mytype)
1147 {moves = true; break;}
1151 if(i <= BJ_HEIGHT-4) {
1152 if(bj->playboard[i+3][j].type == mytype) {
1153 if(bj->playboard[i+1][j].type == mytype)
1154 {moves = true; break;}
1155 if(bj->playboard[i+2][j].type == mytype)
1156 {moves = true; break;}
1160 if(j < BJ_WIDTH-1) {
1161 if(bj->playboard[i+1][j].type == mytype) {
1162 if(bj->playboard[i+2][j+1].type == mytype)
1163 {moves = true; break;}
1165 if(bj->playboard[i+1][j+1].type == mytype) {
1166 if(bj->playboard[i+2][j].type == mytype)
1167 {moves = true; break;}
1168 if (bj->playboard[i+2][j+1].type == mytype)
1169 {moves = true; break;}
1174 if(moves) break;
1176 return moves;
1179 /*****************************************************************************
1180 * jewels_puzzle_is_finished() checks if the puzzle is finished.
1181 ******************************************************************************/
1182 static bool jewels_puzzle_is_finished(struct game_context* bj) {
1183 unsigned int i, j;
1184 for(i=0; i<BJ_HEIGHT; i++) {
1185 for(j=0; j<BJ_WIDTH; j++) {
1186 int mytype = bj->playboard[i][j].type;
1187 if(mytype>MAX_NUM_JEWELS) {
1188 mytype -= MAX_NUM_JEWELS;
1189 if(mytype&PUZZLE_TILE_UP) {
1190 if(i==0 || bj->playboard[i-1][j].type<=MAX_NUM_JEWELS ||
1191 !((bj->playboard[i-1][j].type-MAX_NUM_JEWELS)
1192 &PUZZLE_TILE_DOWN))
1193 return false;
1195 if(mytype&PUZZLE_TILE_DOWN) {
1196 if(i==BJ_HEIGHT-1 ||
1197 bj->playboard[i+1][j].type<=MAX_NUM_JEWELS ||
1198 !((bj->playboard[i+1][j].type-MAX_NUM_JEWELS)
1199 &PUZZLE_TILE_UP))
1200 return false;
1202 if(mytype&PUZZLE_TILE_LEFT) {
1203 if(j==0 || bj->playboard[i][j-1].type<=MAX_NUM_JEWELS ||
1204 !((bj->playboard[i][j-1].type-MAX_NUM_JEWELS)
1205 &PUZZLE_TILE_RIGHT))
1206 return false;
1208 if(mytype&PUZZLE_TILE_RIGHT) {
1209 if(j==BJ_WIDTH-1 ||
1210 bj->playboard[i][j+1].type<=MAX_NUM_JEWELS ||
1211 !((bj->playboard[i][j+1].type-MAX_NUM_JEWELS)
1212 &PUZZLE_TILE_LEFT))
1213 return false;
1218 return true;
1221 /*****************************************************************************
1222 * jewels_initlevel() initialises a level.
1223 ******************************************************************************/
1224 static unsigned int jewels_initlevel(struct game_context* bj) {
1225 unsigned int points = 0;
1227 switch(bj->type) {
1228 case GAME_TYPE_NORMAL:
1229 bj->num_jewels = MAX_NUM_JEWELS;
1230 break;
1232 case GAME_TYPE_PUZZLE:
1234 unsigned int i, j;
1235 struct puzzle_tile *tile;
1237 bj->num_jewels = puzzle_levels[bj->level-1].num_jewels;
1239 for(i=0; i<BJ_HEIGHT; i++) {
1240 for(j=0; j<BJ_WIDTH; j++) {
1241 bj->playboard[i][j].type = (rb->rand()%bj->num_jewels)+1;
1242 bj->playboard[i][j].falling = false;
1243 bj->playboard[i][j].delete = false;
1246 jewels_runboard(bj);
1247 tile = puzzle_levels[bj->level-1].tiles;
1248 for(i=0; i<puzzle_levels[bj->level-1].num_tiles; i++, tile++) {
1249 bj->playboard[tile->y+1][tile->x].type = MAX_NUM_JEWELS
1250 +tile->tile_type;
1253 break;
1256 jewels_drawboard(bj);
1258 /* run the play board */
1259 jewels_putjewels(bj);
1260 points += jewels_runboard(bj);
1261 return points;
1264 /*****************************************************************************
1265 * jewels_init() initializes jewels data structures.
1266 ******************************************************************************/
1267 static void jewels_init(struct game_context* bj) {
1268 /* seed the rand generator */
1269 rb->srand(*rb->current_tick);
1271 bj->type = bj->tmp_type;
1272 bj->level = 1;
1273 bj->score = 0;
1274 bj->segments = 0;
1276 jewels_setcolors();
1278 /* clear playing board */
1279 rb->memset(bj->playboard, 0, sizeof(bj->playboard));
1280 do {
1281 bj->score += jewels_initlevel(bj);
1282 } while(!jewels_movesavail(bj));
1285 /*****************************************************************************
1286 * jewels_nextlevel() advances the game to the next bj->level and returns
1287 * points earned.
1288 ******************************************************************************/
1289 static void jewels_nextlevel(struct game_context* bj) {
1290 int i, x, y;
1291 unsigned int points = 0;
1293 switch(bj->type) {
1294 case GAME_TYPE_NORMAL:
1295 /* roll over score, change and display level */
1296 while(bj->score >= LEVEL_PTS) {
1297 bj->score -= LEVEL_PTS;
1298 bj->level++;
1299 rb->splashf(HZ*2, "Level %d", bj->level);
1300 jewels_drawboard(bj);
1303 /* randomly clear some jewels */
1304 for(i=0; i<16; i++) {
1305 x = rb->rand()%8;
1306 y = rb->rand()%8;
1308 if(bj->playboard[y][x].type != 0) {
1309 points++;
1310 bj->playboard[y][x].type = 0;
1313 break;
1315 case GAME_TYPE_PUZZLE:
1316 bj->level++;
1317 rb->splashf(HZ*2, "Level %d", bj->level);
1318 break;
1321 points += jewels_initlevel(bj);
1322 bj->score += points;
1325 static bool jewels_help(void)
1327 static char *help_text[] = {
1328 "Jewels", "", "Aim", "",
1329 "Swap", "pairs", "of", "jewels", "to", "form", "connected",
1330 "segments", "of", "three", "or", "more", "of", "the", "same",
1331 "type.", "",
1332 "The", "goal", "of", "the", "game", "is", "to", "score", "as", "many",
1333 "points", "as", "possible", "before", "running", "out", "of",
1334 "available", "moves.", "", "",
1335 "Controls", "",
1336 "Directions",
1337 #ifdef JEWELS_SCROLLWHEEL
1338 "or", "scroll",
1339 #endif
1340 "to", "move", "",
1341 HK_SELECT, "to", "select", "",
1342 HK_CANCEL, "to", "go", "to", "menu"
1344 static struct style_text formation[]={
1345 { 0, TEXT_CENTER|TEXT_UNDERLINE },
1346 { 2, C_RED },
1347 { 42, C_RED },
1348 LAST_STYLE_ITEM
1351 rb->lcd_setfont(FONT_UI);
1352 if (display_text(ARRAYLEN(help_text), help_text, formation, NULL, true))
1353 return true;
1354 rb->lcd_setfont(FONT_SYSFIXED);
1356 return false;
1359 static bool _ingame;
1360 static int jewels_menu_cb(int action, const struct menu_item_ex *this_item)
1362 int i = ((intptr_t)this_item);
1363 if(action == ACTION_REQUEST_MENUITEM
1364 && !_ingame && (i==0 || i==6))
1365 return ACTION_EXIT_MENUITEM;
1366 return action;
1368 /*****************************************************************************
1369 * jewels_game_menu() shows the game menu.
1370 ******************************************************************************/
1371 static int jewels_game_menu(struct game_context* bj, bool ingame)
1373 rb->button_clear_queue();
1374 int choice = 0;
1376 _ingame = ingame;
1378 static struct opt_items mode[] = {
1379 { "Normal", -1 },
1380 { "Puzzle", -1 },
1383 MENUITEM_STRINGLIST (main_menu, "Jewels Menu", jewels_menu_cb,
1384 "Resume Game",
1385 "Start New Game",
1386 "Mode",
1387 "Help",
1388 "High Scores",
1389 "Playback Control",
1390 "Quit without Saving",
1391 "Quit");
1393 while (1) {
1394 switch (rb->do_menu(&main_menu, &choice, NULL, false)) {
1395 case 0:
1396 jewels_setcolors();
1397 if(resume_file)
1398 rb->remove(SAVE_FILE);
1399 return 0;
1400 case 1:
1401 jewels_init(bj);
1402 return 0;
1403 case 2:
1404 rb->set_option("Mode", &bj->tmp_type, INT, mode, 2, NULL);
1405 break;
1406 case 3:
1407 if(jewels_help())
1408 return 1;
1409 break;
1410 case 4:
1411 highscore_show(-1, highscores, NUM_SCORES, true);
1412 break;
1413 case 5:
1414 playback_control(NULL);
1415 break;
1416 case 6:
1417 return 1;
1418 case 7:
1419 if (ingame) {
1420 rb->splash(HZ*1, "Saving game ...");
1421 jewels_savegame(bj);
1423 return 1;
1424 case MENU_ATTACHED_USB:
1425 return 1;
1426 default:
1427 break;
1432 static int jewels_main(struct game_context* bj) {
1433 int button;
1434 int position;
1435 bool selected = false;
1436 bool no_movesavail;
1437 int x=0, y=0;
1439 bool loaded = jewels_loadgame(bj);
1440 resume_file = loaded;
1441 if (jewels_game_menu(bj, loaded)!=0)
1442 return 0;
1444 resume_file = false;
1445 while(true) {
1446 no_movesavail = false;
1448 /* refresh the board */
1449 jewels_drawboard(bj);
1451 /* display the cursor */
1452 if(selected) {
1453 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1454 rb->lcd_fillrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1455 TILE_WIDTH, TILE_HEIGHT);
1456 rb->lcd_set_drawmode(DRMODE_SOLID);
1457 } else {
1458 rb->lcd_drawrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1459 TILE_WIDTH, TILE_HEIGHT);
1461 rb->lcd_update_rect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1462 TILE_WIDTH, TILE_HEIGHT);
1464 /* handle game button presses */
1465 rb->yield();
1466 button = rb->button_get(true);
1467 switch(button){
1468 case JEWELS_LEFT: /* move cursor left */
1469 case (JEWELS_LEFT|BUTTON_REPEAT):
1470 if(selected) {
1471 bj->score += jewels_swapjewels(bj, x, y, SWAP_LEFT);
1472 selected = false;
1473 if (!jewels_movesavail(bj)) no_movesavail = true;
1474 } else {
1475 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1477 break;
1479 case JEWELS_RIGHT: /* move cursor right */
1480 case (JEWELS_RIGHT|BUTTON_REPEAT):
1481 if(selected) {
1482 bj->score += jewels_swapjewels(bj, x, y, SWAP_RIGHT);
1483 selected = false;
1484 if (!jewels_movesavail(bj)) no_movesavail = true;
1485 } else {
1486 x = (x+1)%BJ_WIDTH;
1488 break;
1490 case JEWELS_DOWN: /* move cursor down */
1491 case (JEWELS_DOWN|BUTTON_REPEAT):
1492 if(selected) {
1493 bj->score += jewels_swapjewels(bj, x, y, SWAP_DOWN);
1494 selected = false;
1495 if (!jewels_movesavail(bj)) no_movesavail = true;
1496 } else {
1497 y = (y+1)%(BJ_HEIGHT-1);
1499 break;
1501 case JEWELS_UP: /* move cursor up */
1502 case (JEWELS_UP|BUTTON_REPEAT):
1503 if(selected) {
1504 bj->score += jewels_swapjewels(bj, x, y, SWAP_UP);
1505 selected = false;
1506 if (!jewels_movesavail(bj)) no_movesavail = true;
1507 } else {
1508 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1510 break;
1512 #ifdef JEWELS_SCROLLWHEEL
1513 case JEWELS_PREV: /* scroll backwards */
1514 case (JEWELS_PREV|BUTTON_REPEAT):
1515 if(!selected) {
1516 if(x == 0) {
1517 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1519 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1521 break;
1523 case JEWELS_NEXT: /* scroll forwards */
1524 case (JEWELS_NEXT|BUTTON_REPEAT):
1525 if(!selected) {
1526 if(x == BJ_WIDTH-1) {
1527 y = (y+1)%(BJ_HEIGHT-1);
1529 x = (x+1)%BJ_WIDTH;
1531 break;
1532 #endif
1534 case JEWELS_SELECT: /* toggle selected */
1535 selected = !selected;
1536 break;
1538 #ifdef JEWELS_RC_CANCEL
1539 case JEWELS_RC_CANCEL:
1540 #endif
1541 case JEWELS_CANCEL: /* end game */
1542 if (jewels_game_menu(bj, true)!=0)
1543 return 0;
1544 break;
1546 default:
1547 if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
1548 return PLUGIN_USB_CONNECTED;
1549 break;
1552 switch(bj->type) {
1553 case GAME_TYPE_NORMAL:
1554 if(bj->score >= LEVEL_PTS)
1555 jewels_nextlevel(bj);
1556 break;
1557 case GAME_TYPE_PUZZLE:
1558 if (jewels_puzzle_is_finished(bj)) {
1559 if (bj->level < NUM_PUZZLE_LEVELS) {
1560 jewels_nextlevel(bj);
1561 } else {
1562 rb->splash(2*HZ, "Congratulations!");
1563 rb->splash(2*HZ, "You have finished the game!");
1564 if (jewels_game_menu(bj, false)!=0) {
1565 return 0;
1568 break;
1572 if (no_movesavail) {
1573 switch(bj->type) {
1574 case GAME_TYPE_NORMAL:
1575 rb->splash(HZ*2, "Game Over!");
1576 rb->lcd_clear_display();
1577 bj->score += (bj->level-1)*LEVEL_PTS;
1578 position=highscore_update(bj->score, bj->level, "",
1579 highscores, NUM_SCORES);
1580 if (position != -1)
1582 if (position == 0)
1583 rb->splash(HZ*2, "New High Score");
1584 highscore_show(position, highscores, NUM_SCORES, true);
1586 break;
1587 case GAME_TYPE_PUZZLE:
1588 rb->splash(2*HZ, "Game Over");
1589 break;
1591 if (jewels_game_menu(bj, false)!=0) {
1592 return 0;
1598 /* this is the plugin entry point */
1599 enum plugin_status plugin_start(const void* parameter)
1601 (void)parameter;
1603 /* load high scores */
1604 highscore_load(SCORE_FILE, highscores, NUM_SCORES);
1606 rb->lcd_setfont(FONT_SYSFIXED);
1607 #if LCD_DEPTH > 1
1608 rb->lcd_set_backdrop(NULL);
1609 #endif
1611 struct game_context bj;
1612 bj.tmp_type = GAME_TYPE_NORMAL;
1613 jewels_main(&bj);
1614 highscore_save(SCORE_FILE, highscores, NUM_SCORES);
1615 rb->lcd_setfont(FONT_UI);
1617 return PLUGIN_OK;