Fix warning about missing newline at the EOF
[maemo-rb.git] / apps / plugins / jewels.c
blob7d80a536ddeaa1fe3ee9515829c118d1aad2a7b0
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 (CONFIG_KEYPAD == SANSA_CONNECT_PAD)
128 #define JEWELS_SCROLLWHEEL
129 #define JEWELS_UP BUTTON_UP
130 #define JEWELS_DOWN BUTTON_DOWN
131 #define JEWELS_LEFT BUTTON_LEFT
132 #define JEWELS_RIGHT BUTTON_RIGHT
133 #define JEWELS_PREV BUTTON_SCROLL_BACK
134 #define JEWELS_NEXT BUTTON_SCROLL_FWD
135 #define JEWELS_SELECT BUTTON_SELECT
136 #define JEWELS_CANCEL BUTTON_POWER
137 #define HK_SELECT "SELECT"
138 #define HK_CANCEL "POWER"
140 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
141 #define JEWELS_SCROLLWHEEL
142 #define JEWELS_UP BUTTON_UP
143 #define JEWELS_DOWN BUTTON_DOWN
144 #define JEWELS_LEFT BUTTON_LEFT
145 #define JEWELS_RIGHT BUTTON_RIGHT
146 #define JEWELS_PREV BUTTON_SCROLL_BACK
147 #define JEWELS_NEXT BUTTON_SCROLL_FWD
148 #define JEWELS_SELECT BUTTON_SELECT
149 #define JEWELS_CANCEL (BUTTON_HOME|BUTTON_REPEAT)
150 #define HK_SELECT "SELECT"
151 #define HK_CANCEL "HOME"
153 #elif CONFIG_KEYPAD == SANSA_C200_PAD || \
154 CONFIG_KEYPAD == SANSA_CLIP_PAD || \
155 CONFIG_KEYPAD == SANSA_M200_PAD
156 #define JEWELS_UP BUTTON_UP
157 #define JEWELS_DOWN BUTTON_DOWN
158 #define JEWELS_LEFT BUTTON_LEFT
159 #define JEWELS_RIGHT BUTTON_RIGHT
160 #define JEWELS_SELECT BUTTON_SELECT
161 #define JEWELS_CANCEL BUTTON_POWER
162 #define HK_SELECT "SELECT"
163 #define HK_CANCEL "POWER"
165 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
166 #define JEWELS_UP BUTTON_SCROLL_UP
167 #define JEWELS_DOWN BUTTON_SCROLL_DOWN
168 #define JEWELS_LEFT BUTTON_LEFT
169 #define JEWELS_RIGHT BUTTON_RIGHT
170 #define JEWELS_SELECT BUTTON_PLAY
171 #define JEWELS_CANCEL BUTTON_POWER
172 #define HK_SELECT "PLAY"
173 #define HK_CANCEL "POWER"
175 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD || \
176 CONFIG_KEYPAD == SAMSUNG_YPR0_PAD
177 #define JEWELS_UP BUTTON_UP
178 #define JEWELS_DOWN BUTTON_DOWN
179 #define JEWELS_LEFT BUTTON_LEFT
180 #define JEWELS_RIGHT BUTTON_RIGHT
181 #define JEWELS_SELECT BUTTON_SELECT
182 #define JEWELS_CANCEL BUTTON_BACK
183 #define HK_SELECT "SELECT"
184 #define HK_CANCEL "BACK"
186 #elif CONFIG_KEYPAD == MROBE100_PAD
187 #define JEWELS_UP BUTTON_UP
188 #define JEWELS_DOWN BUTTON_DOWN
189 #define JEWELS_LEFT BUTTON_LEFT
190 #define JEWELS_RIGHT BUTTON_RIGHT
191 #define JEWELS_SELECT BUTTON_SELECT
192 #define JEWELS_CANCEL BUTTON_POWER
193 #define HK_SELECT "SELECT"
194 #define HK_CANCEL "POWER"
196 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
197 #define JEWELS_UP BUTTON_RC_VOL_UP
198 #define JEWELS_DOWN BUTTON_RC_VOL_DOWN
199 #define JEWELS_LEFT BUTTON_RC_REW
200 #define JEWELS_RIGHT BUTTON_RC_FF
201 #define JEWELS_SELECT BUTTON_RC_PLAY
202 #define JEWELS_CANCEL BUTTON_RC_REC
203 #define HK_SELECT "PLAY"
204 #define HK_CANCEL "REC"
206 #define JEWELS_RC_CANCEL BUTTON_REC
208 #elif CONFIG_KEYPAD == COWON_D2_PAD
209 #define JEWELS_CANCEL BUTTON_POWER
210 #define HK_CANCEL "POWER"
212 #elif CONFIG_KEYPAD == IAUDIO67_PAD
213 #define JEWELS_UP BUTTON_STOP
214 #define JEWELS_DOWN BUTTON_PLAY
215 #define JEWELS_LEFT BUTTON_LEFT
216 #define JEWELS_RIGHT BUTTON_RIGHT
217 #define JEWELS_SELECT BUTTON_MENU
218 #define JEWELS_CANCEL BUTTON_POWER
219 #define HK_SELECT "MENU"
220 #define HK_CANCEL "POWER"
222 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
223 #define JEWELS_UP BUTTON_UP
224 #define JEWELS_DOWN BUTTON_DOWN
225 #define JEWELS_LEFT BUTTON_LEFT
226 #define JEWELS_RIGHT BUTTON_RIGHT
227 #define JEWELS_SELECT BUTTON_SELECT
228 #define JEWELS_CANCEL BUTTON_BACK
229 #define HK_SELECT "MIDDLE"
230 #define HK_CANCEL "BACK"
232 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
233 #define JEWELS_UP BUTTON_UP
234 #define JEWELS_DOWN BUTTON_DOWN
235 #define JEWELS_LEFT BUTTON_LEFT
236 #define JEWELS_RIGHT BUTTON_RIGHT
237 #define JEWELS_SELECT BUTTON_SELECT
238 #define JEWELS_CANCEL BUTTON_POWER
239 #define HK_SELECT "SELECT"
240 #define HK_CANCEL "POWER"
242 #elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
243 #define JEWELS_UP BUTTON_UP
244 #define JEWELS_DOWN BUTTON_DOWN
245 #define JEWELS_LEFT BUTTON_LEFT
246 #define JEWELS_RIGHT BUTTON_RIGHT
247 #define JEWELS_SELECT BUTTON_PLAY
248 #define JEWELS_CANCEL BUTTON_POWER
249 #define HK_SELECT "PLAY"
250 #define HK_CANCEL "POWER"
252 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
253 #define JEWELS_UP BUTTON_UP
254 #define JEWELS_DOWN BUTTON_DOWN
255 #define JEWELS_LEFT BUTTON_PREV
256 #define JEWELS_RIGHT BUTTON_NEXT
257 #define JEWELS_SELECT BUTTON_PLAY
258 #define JEWELS_CANCEL BUTTON_POWER
259 #define HK_SELECT "PLAY"
260 #define HK_CANCEL "POWER"
262 #elif CONFIG_KEYPAD == ONDAVX747_PAD || \
263 CONFIG_KEYPAD == ONDAVX777_PAD || \
264 CONFIG_KEYPAD == MROBE500_PAD
265 #define JEWELS_CANCEL BUTTON_POWER
266 #define HK_CANCEL "POWER"
268 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
269 #define JEWELS_UP BUTTON_UP
270 #define JEWELS_DOWN BUTTON_DOWN
271 #define JEWELS_LEFT BUTTON_LEFT
272 #define JEWELS_RIGHT BUTTON_RIGHT
273 #define JEWELS_SELECT BUTTON_PLAY
274 #define JEWELS_CANCEL BUTTON_REW
275 #define HK_SELECT "PLAY"
276 #define HK_CANCEL "REWIND"
278 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
279 #define JEWELS_UP BUTTON_UP
280 #define JEWELS_DOWN BUTTON_DOWN
281 #define JEWELS_LEFT BUTTON_PREV
282 #define JEWELS_RIGHT BUTTON_NEXT
283 #define JEWELS_SELECT BUTTON_OK
284 #define JEWELS_CANCEL BUTTON_REC
285 #define HK_SELECT "OK"
286 #define HK_CANCEL "REC"
288 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
289 #define JEWELS_LEFT BUTTON_VOL_DOWN
290 #define JEWELS_RIGHT BUTTON_VOL_UP
291 #define JEWELS_UP BUTTON_REW
292 #define JEWELS_DOWN BUTTON_FF
293 #define JEWELS_SELECT BUTTON_FUNC
294 #define JEWELS_CANCEL BUTTON_REC
295 #define HK_SELECT "FUNC"
296 #define HK_CANCEL "REC"
298 #elif CONFIG_KEYPAD == MPIO_HD300_PAD
299 #define JEWELS_LEFT BUTTON_REW
300 #define JEWELS_RIGHT BUTTON_FF
301 #define JEWELS_UP BUTTON_UP
302 #define JEWELS_DOWN BUTTON_DOWN
303 #define JEWELS_SELECT BUTTON_ENTER
304 #define JEWELS_CANCEL BUTTON_MENU
305 #define HK_SELECT "ENTER"
306 #define HK_CANCEL "MENU"
308 #elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
309 #define JEWELS_LEFT BUTTON_LEFT
310 #define JEWELS_RIGHT BUTTON_RIGHT
311 #define JEWELS_UP BUTTON_UP
312 #define JEWELS_DOWN BUTTON_DOWN
313 #define JEWELS_SELECT BUTTON_SELECT
314 #define JEWELS_CANCEL BUTTON_BACK
315 #define HK_SELECT "SELECT"
316 #define HK_CANCEL "BACK"
318 #else
319 #error No keymap defined!
320 #endif
322 #ifdef HAVE_TOUCHSCREEN
323 #ifndef JEWELS_UP
324 #define JEWELS_UP BUTTON_TOPMIDDLE
325 #endif
326 #ifndef JEWELS_DOWN
327 #define JEWELS_DOWN BUTTON_BOTTOMMIDDLE
328 #endif
329 #ifndef JEWELS_LEFT
330 #define JEWELS_LEFT BUTTON_MIDLEFT
331 #endif
332 #ifndef JEWELS_RIGHT
333 #define JEWELS_RIGHT BUTTON_MIDRIGHT
334 #endif
335 #ifndef JEWELS_SELECT
336 #define JEWELS_SELECT BUTTON_CENTER
337 #define HK_SELECT "CENTER"
338 #endif
339 #ifndef JEWELS_CANCEL
340 #define JEWELS_CANCEL BUTTON_TOPLEFT
341 #define HK_CANCEL "TOPLEFT"
342 #endif
343 #endif
345 #define TILE_WIDTH BMPWIDTH_jewels
346 #define TILE_HEIGHT (BMPHEIGHT_jewels/23)
348 #if LCD_HEIGHT < LCD_WIDTH
349 /* This calculation assumes integer division w/ LCD_HEIGHT/TILE_HEIGHT */
350 #define YOFS LCD_HEIGHT-((LCD_HEIGHT/TILE_HEIGHT)*TILE_HEIGHT)
351 #else
352 #define YOFS 0
353 #endif
355 #define NUM_SCORES 5
357 /* swap directions */
358 #define SWAP_UP 0
359 #define SWAP_RIGHT 1
360 #define SWAP_DOWN 2
361 #define SWAP_LEFT 3
363 /* play board dimension */
364 #define BJ_HEIGHT 9
365 #define BJ_WIDTH 8
367 /* next level threshold */
368 #define LEVEL_PTS 100
370 /* animation frame rate */
371 #define MAX_FPS 20
373 /* text margin */
374 #define MARGIN 5
376 /* Game types */
377 enum game_type {
378 GAME_TYPE_NORMAL,
379 GAME_TYPE_PUZZLE
382 /* external bitmaps */
383 extern const fb_data jewels[];
385 /* tile background colors */
386 #ifdef HAVE_LCD_COLOR
387 static const unsigned jewels_bkgd[2] = {
388 LCD_RGBPACK(104, 63, 63),
389 LCD_RGBPACK(83, 44, 44)
391 #endif
393 /* the tile struct
394 * type is the jewel number 0-7
395 * falling if the jewel is falling
396 * delete marks the jewel for deletion
398 struct tile {
399 int type;
400 bool falling;
401 bool delete;
404 /* the game context struct
405 * score is the current level score
406 * segments is the number of cleared segments in the current run
407 * level is the current level
408 * tmp_type is the select type in the menu
409 * type is the game type (normal or puzzle)
410 * playboard is the game playing board (first row is hidden)
411 * num_jewels is the number of different jewels to use
413 struct game_context {
414 unsigned int score;
415 unsigned int segments;
416 unsigned int level;
417 unsigned int type;
418 unsigned int tmp_type;
419 struct tile playboard[BJ_HEIGHT][BJ_WIDTH];
420 unsigned int num_jewels;
423 #define MAX_NUM_JEWELS 7
425 #define MAX_PUZZLE_TILES 4
426 #define NUM_PUZZLE_LEVELS 10
428 struct puzzle_tile {
429 int x;
430 int y;
431 int tile_type;
434 struct puzzle_level {
435 unsigned int num_jewels;
436 unsigned int num_tiles;
437 struct puzzle_tile tiles[MAX_PUZZLE_TILES];
440 #define PUZZLE_TILE_UP 1
441 #define PUZZLE_TILE_DOWN 2
442 #define PUZZLE_TILE_LEFT 4
443 #define PUZZLE_TILE_RIGHT 8
445 struct puzzle_level puzzle_levels[NUM_PUZZLE_LEVELS] = {
446 { 5, 2, { {3, 3, PUZZLE_TILE_RIGHT},
447 {4, 2, PUZZLE_TILE_LEFT} } },
448 { 5, 2, { {3, 2, PUZZLE_TILE_DOWN},
449 {3, 4, PUZZLE_TILE_UP} } },
450 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
451 {3, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
452 {3, 6, PUZZLE_TILE_UP} } },
453 { 6, 3, { {3, 2, PUZZLE_TILE_RIGHT},
454 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
455 {5, 4, PUZZLE_TILE_LEFT} } },
456 { 6, 2, { {3, 4, PUZZLE_TILE_RIGHT},
457 {4, 2, PUZZLE_TILE_LEFT} } },
458 { 6, 2, { {3, 2, PUZZLE_TILE_DOWN},
459 {4, 4, PUZZLE_TILE_UP} } },
460 { 7, 4, { {3, 2, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
461 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
462 {3, 4, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
463 {4, 4, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
464 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
465 {4, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
466 {3, 6, PUZZLE_TILE_UP} } },
467 { 7, 3, { {2, 2, PUZZLE_TILE_RIGHT},
468 {4, 1, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
469 {5, 4, PUZZLE_TILE_LEFT} } },
470 { 7, 4, { {3, 0, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
471 {5, 0, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
472 {2, 7, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
473 {4, 7, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
476 #define SAVE_FILE PLUGIN_GAMES_DATA_DIR "/jewels.save"
477 #define SCORE_FILE PLUGIN_GAMES_DATA_DIR "/jewels.score"
478 struct highscore highscores[NUM_SCORES];
480 static bool resume_file = false;
482 /*****************************************************************************
483 * jewels_setcolors() set the foreground and background colors.
484 ******************************************************************************/
485 static inline void jewels_setcolors(void) {
486 #ifdef HAVE_LCD_COLOR
487 rb->lcd_set_background(LCD_RGBPACK(49, 26, 26));
488 rb->lcd_set_foreground(LCD_RGBPACK(210, 181, 181));
489 #endif
492 /*****************************************************************************
493 * jewels_loadgame() loads the saved game and returns load success.
494 ******************************************************************************/
495 static bool jewels_loadgame(struct game_context* bj)
497 int fd;
498 bool loaded = false;
500 /* open game file */
501 fd = rb->open(SAVE_FILE, O_RDONLY);
502 if(fd < 0) return loaded;
504 /* read in saved game */
505 while(true) {
506 if(rb->read(fd, &bj->tmp_type, sizeof(bj->tmp_type)) <= 0) break;
507 if(rb->read(fd, &bj->type, sizeof(bj->type)) <= 0) break;
508 if(rb->read(fd, &bj->score, sizeof(bj->score)) <= 0) break;
509 if(rb->read(fd, &bj->level, sizeof(bj->level)) <= 0) break;
510 if(rb->read(fd, &bj->segments, sizeof(bj->segments)) <= 0) break;
511 if(rb->read(fd, &bj->num_jewels, sizeof(bj->num_jewels)) <= 0) break;
512 if(rb->read(fd, bj->playboard, sizeof(bj->playboard)) <= 0) break;
513 loaded = true;
514 break;
517 rb->close(fd);
519 return loaded;
522 /*****************************************************************************
523 * jewels_savegame() saves the current game state.
524 ******************************************************************************/
525 static void jewels_savegame(struct game_context* bj)
527 int fd;
528 /* write out the game state to the save file */
529 fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT, 0666);
530 if(fd < 0) return;
532 rb->write(fd, &bj->tmp_type, sizeof(bj->tmp_type));
533 rb->write(fd, &bj->type, sizeof(bj->type));
534 rb->write(fd, &bj->score, sizeof(bj->score));
535 rb->write(fd, &bj->level, sizeof(bj->level));
536 rb->write(fd, &bj->segments, sizeof(bj->segments));
537 rb->write(fd, &bj->num_jewels, sizeof(bj->num_jewels));
538 rb->write(fd, bj->playboard, sizeof(bj->playboard));
539 rb->close(fd);
542 /*****************************************************************************
543 * jewels_drawboard() redraws the entire game board.
544 ******************************************************************************/
545 static void jewels_drawboard(struct game_context* bj) {
546 int i, j;
547 int w, h;
548 unsigned int tempscore;
549 unsigned int size;
550 char *title = "Level";
551 char str[10];
553 if (bj->type == GAME_TYPE_NORMAL) {
554 tempscore = (bj->score>LEVEL_PTS ? LEVEL_PTS : bj->score);
555 size = LEVEL_PTS;
556 } else {
557 tempscore = (bj->level>NUM_PUZZLE_LEVELS ? NUM_PUZZLE_LEVELS : bj->level);
558 size = NUM_PUZZLE_LEVELS;
561 /* clear screen */
562 rb->lcd_clear_display();
564 /* dispay playing board */
565 for(i=0; i<BJ_HEIGHT-1; i++){
566 for(j=0; j<BJ_WIDTH; j++){
567 #ifdef HAVE_LCD_COLOR
568 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
569 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
570 TILE_WIDTH, TILE_HEIGHT);
571 rb->lcd_bitmap_transparent_part(jewels,
572 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
573 STRIDE( SCREEN_MAIN,
574 BMPWIDTH_jewels, BMPHEIGHT_jewels),
575 j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
576 TILE_WIDTH, TILE_HEIGHT);
577 #else
578 rb->lcd_bitmap_part(jewels,
579 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
580 STRIDE( SCREEN_MAIN,
581 BMPWIDTH_jewels, BMPHEIGHT_jewels),
582 j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
583 TILE_WIDTH, TILE_HEIGHT);
584 #endif
588 #if LCD_WIDTH > LCD_HEIGHT /* horizontal layout */
590 /* draw separator lines */
591 jewels_setcolors();
592 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, LCD_HEIGHT-1);
594 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, 18);
595 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, LCD_HEIGHT-10);
597 /* draw progress bar */
598 #ifdef HAVE_LCD_COLOR
599 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
600 #endif
601 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
602 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
603 tempscore/size),
604 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
605 ((LCD_HEIGHT-10)-18)*tempscore/size);
606 #ifdef HAVE_LCD_COLOR
607 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
608 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
609 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
610 tempscore/size)+1,
611 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
612 ((LCD_HEIGHT-10)-18)*tempscore/size-1);
613 jewels_setcolors();
614 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
615 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*tempscore/size),
616 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
617 ((LCD_HEIGHT-10)-18)*tempscore/size+1);
618 #endif
620 /* print text */
621 rb->lcd_getstringsize(title, &w, &h);
622 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 1, title);
623 rb->snprintf(str, 4, "%d", bj->level);
624 rb->lcd_getstringsize(str, &w, &h);
625 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 10, str);
627 if (bj->type == GAME_TYPE_NORMAL) {
628 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
629 rb->lcd_getstringsize(str, &w, &h);
630 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2,
631 LCD_HEIGHT-8, str);
634 #elif LCD_WIDTH < LCD_HEIGHT /* vertical layout */
636 /* draw separator lines */
637 jewels_setcolors();
638 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
639 rb->lcd_hline(0, LCD_WIDTH-1, LCD_HEIGHT-14);
640 rb->lcd_vline(LCD_WIDTH/2, LCD_HEIGHT-14, LCD_HEIGHT-1);
642 /* draw progress bar */
643 #ifdef HAVE_LCD_COLOR
644 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
645 #endif
646 rb->lcd_fillrect(0, (8*TILE_HEIGHT+YOFS)
647 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
648 LCD_WIDTH*tempscore/size,
649 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
650 #ifdef HAVE_LCD_COLOR
651 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
652 rb->lcd_drawrect(1, (8*TILE_HEIGHT+YOFS)
653 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4+1,
654 LCD_WIDTH*tempscore/size-1,
655 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2-2);
656 jewels_setcolors();
657 rb->lcd_drawrect(0, (8*TILE_HEIGHT+YOFS)
658 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
659 LCD_WIDTH*tempscore/size+1,
660 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
661 #endif
663 /* print text */
664 rb->lcd_putsxyf(1, LCD_HEIGHT-10, "%s %d", title, bj->level);
666 if (bj->type == GAME_TYPE_NORMAL) {
667 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
668 rb->lcd_getstringsize(str, &w, &h);
669 rb->lcd_putsxy((LCD_WIDTH-2)-w, LCD_HEIGHT-10, str);
673 #else /* square layout */
675 /* draw separator lines */
676 jewels_setcolors();
677 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
678 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, 8*TILE_HEIGHT+YOFS);
679 rb->lcd_vline(LCD_WIDTH/2, 8*TILE_HEIGHT+YOFS, LCD_HEIGHT-1);
681 /* draw progress bar */
682 #ifdef HAVE_LCD_COLOR
683 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
684 #endif
685 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
686 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)*tempscore/size,
687 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
688 (8*TILE_HEIGHT+YOFS)*tempscore/size);
689 #ifdef HAVE_LCD_COLOR
690 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
691 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
692 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
693 *tempscore/size+1,
694 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
695 (8*TILE_HEIGHT+YOFS)*tempscore/size-1);
696 jewels_setcolors();
697 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
698 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
699 *tempscore/size,
700 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
701 (8*TILE_HEIGHT+YOFS)*tempscore/size+1);
702 #endif
704 /* print text */
705 rb->lcd_putsxyf(1, LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3,"%s %d",
706 title, bj->level);
708 if (bj->type == GAME_TYPE_NORMAL) {
709 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
710 rb->lcd_getstringsize(str, &w, &h);
711 rb->lcd_putsxy((LCD_WIDTH-2)-w,
712 LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
715 #endif /* layout */
717 rb->lcd_update();
720 /*****************************************************************************
721 * jewels_putjewels() makes the jewels fall to fill empty spots and adds
722 * new random jewels at the empty spots at the top of each row.
723 ******************************************************************************/
724 static void jewels_putjewels(struct game_context* bj){
725 int i, j, k;
726 bool mark, done;
727 long lasttick, currenttick;
729 /* loop to make all the jewels fall */
730 while(true) {
731 /* mark falling jewels and add new jewels to hidden top row*/
732 mark = false;
733 done = true;
734 for(j=0; j<BJ_WIDTH; j++) {
735 if(bj->playboard[1][j].type == 0) {
736 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
738 for(i=BJ_HEIGHT-2; i>=0; i--) {
739 if(!mark && bj->playboard[i+1][j].type == 0) {
740 mark = true;
741 done = false;
743 if(mark) bj->playboard[i][j].falling = true;
745 /*if(bj->playboard[1][j].falling) {
746 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
747 bj->playboard[0][j].falling = true;
749 mark = false;
752 /* break if there are no falling jewels */
753 if(done) break;
755 /* animate falling jewels */
756 lasttick = *rb->current_tick;
758 for(k=1; k<=8; k++) {
759 for(i=BJ_HEIGHT-2; i>=0; i--) {
760 for(j=0; j<BJ_WIDTH; j++) {
761 if(bj->playboard[i][j].falling &&
762 bj->playboard[i][j].type != 0) {
763 /* clear old position */
764 #ifdef HAVE_LCD_COLOR
765 if(i == 0 && YOFS) {
766 rb->lcd_set_foreground(rb->lcd_get_background());
767 } else {
768 rb->lcd_set_foreground(jewels_bkgd[(i-1+j)%2]);
770 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
771 TILE_WIDTH, TILE_HEIGHT);
772 if(bj->playboard[i+1][j].type == 0) {
773 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
774 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
775 TILE_WIDTH, TILE_HEIGHT);
777 #else
778 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
779 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
780 TILE_WIDTH, TILE_HEIGHT);
781 if(bj->playboard[i+1][j].type == 0) {
782 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
783 TILE_WIDTH, TILE_HEIGHT);
785 rb->lcd_set_drawmode(DRMODE_SOLID);
786 #endif
788 /* draw new position */
789 #ifdef HAVE_LCD_COLOR
790 rb->lcd_bitmap_transparent_part(jewels, 0,
791 TILE_HEIGHT*(bj->playboard[i][j].type),
792 STRIDE( SCREEN_MAIN,
793 BMPWIDTH_jewels,
794 BMPHEIGHT_jewels),
795 j*TILE_WIDTH,
796 (i-1)*TILE_HEIGHT+YOFS+
797 ((((TILE_HEIGHT<<10)*k)/8)>>10),
798 TILE_WIDTH, TILE_HEIGHT);
799 #else
800 rb->lcd_bitmap_part(jewels, 0,
801 TILE_HEIGHT*(bj->playboard[i][j].type),
802 STRIDE( SCREEN_MAIN,
803 BMPWIDTH_jewels,
804 BMPHEIGHT_jewels),
805 j*TILE_WIDTH,
806 (i-1)*TILE_HEIGHT+YOFS+
807 ((((TILE_HEIGHT<<10)*k)/8)>>10),
808 TILE_WIDTH, TILE_HEIGHT);
809 #endif
814 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
815 jewels_setcolors();
817 /* framerate limiting */
818 currenttick = *rb->current_tick;
819 if(currenttick-lasttick < HZ/MAX_FPS) {
820 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
821 } else {
822 rb->yield();
824 lasttick = currenttick;
827 /* shift jewels down */
828 for(j=0; j<BJ_WIDTH; j++) {
829 for(i=BJ_HEIGHT-1; i>=1; i--) {
830 if(bj->playboard[i-1][j].falling) {
831 bj->playboard[i][j].type = bj->playboard[i-1][j].type;
836 /* clear out top row */
837 for(j=0; j<BJ_WIDTH; j++) {
838 bj->playboard[0][j].type = 0;
841 /* mark everything not falling */
842 for(i=0; i<BJ_HEIGHT; i++) {
843 for(j=0; j<BJ_WIDTH; j++) {
844 bj->playboard[i][j].falling = false;
850 /*****************************************************************************
851 * jewels_clearjewels() finds all the connected rows and columns and
852 * calculates and returns the points earned.
853 ******************************************************************************/
854 static unsigned int jewels_clearjewels(struct game_context* bj) {
855 int i, j;
856 int last, run;
857 unsigned int points = 0;
859 /* check for connected rows */
860 for(i=1; i<BJ_HEIGHT; i++) {
861 last = 0;
862 run = 1;
863 for(j=0; j<BJ_WIDTH; j++) {
864 if(bj->playboard[i][j].type == last &&
865 bj->playboard[i][j].type != 0 &&
866 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
867 run++;
869 if(run == 3) {
870 bj->segments++;
871 points += bj->segments;
872 bj->playboard[i][j].delete = true;
873 bj->playboard[i][j-1].delete = true;
874 bj->playboard[i][j-2].delete = true;
875 } else if(run > 3) {
876 points++;
877 bj->playboard[i][j].delete = true;
879 } else {
880 run = 1;
881 last = bj->playboard[i][j].type;
886 /* check for connected columns */
887 for(j=0; j<BJ_WIDTH; j++) {
888 last = 0;
889 run = 1;
890 for(i=1; i<BJ_HEIGHT; i++) {
891 if(bj->playboard[i][j].type != 0 &&
892 bj->playboard[i][j].type == last &&
893 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
894 run++;
896 if(run == 3) {
897 bj->segments++;
898 points += bj->segments;
899 bj->playboard[i][j].delete = true;
900 bj->playboard[i-1][j].delete = true;
901 bj->playboard[i-2][j].delete = true;
902 } else if(run > 3) {
903 points++;
904 bj->playboard[i][j].delete = true;
906 } else {
907 run = 1;
908 last = bj->playboard[i][j].type;
913 /* clear deleted jewels */
914 for(i=1; i<BJ_HEIGHT; i++) {
915 for(j=0; j<BJ_WIDTH; j++) {
916 if(bj->playboard[i][j].delete) {
917 bj->playboard[i][j].delete = false;
918 bj->playboard[i][j].type = 0;
923 return points;
926 /*****************************************************************************
927 * jewels_runboard() runs the board until it settles in a fixed state and
928 * returns points earned.
929 ******************************************************************************/
930 static unsigned int jewels_runboard(struct game_context* bj) {
931 unsigned int points = 0;
932 unsigned int ret;
934 bj->segments = 0;
936 while((ret = jewels_clearjewels(bj)) > 0) {
937 points += ret;
938 jewels_drawboard(bj);
939 jewels_putjewels(bj);
942 return points;
945 /*****************************************************************************
946 * jewels_swapjewels() swaps two jewels as long as it results in points and
947 * returns points earned.
948 ******************************************************************************/
949 static unsigned int jewels_swapjewels(struct game_context* bj,
950 int x, int y, int direc) {
951 int k;
952 int horzmod, vertmod;
953 int movelen = 0;
954 bool undo = false;
955 unsigned int points = 0;
956 long lasttick, currenttick;
958 /* check for invalid parameters */
959 if(x < 0 || x >= BJ_WIDTH || y < 0 || y >= BJ_HEIGHT-1 ||
960 direc < SWAP_UP || direc > SWAP_LEFT) {
961 return 0;
964 /* check for invalid directions */
965 if((x == 0 && direc == SWAP_LEFT) ||
966 (x == BJ_WIDTH-1 && direc == SWAP_RIGHT) ||
967 (y == 0 && direc == SWAP_UP) ||
968 (y == BJ_HEIGHT-2 && direc == SWAP_DOWN)) {
969 return 0;
972 /* set direction variables */
973 horzmod = 0;
974 vertmod = 0;
975 switch(direc) {
976 case SWAP_UP:
977 vertmod = -1;
978 movelen = TILE_HEIGHT;
979 break;
980 case SWAP_RIGHT:
981 horzmod = 1;
982 movelen = TILE_WIDTH;
983 break;
984 case SWAP_DOWN:
985 vertmod = 1;
986 movelen = TILE_HEIGHT;
987 break;
988 case SWAP_LEFT:
989 horzmod = -1;
990 movelen = TILE_WIDTH;
991 break;
994 while(true) {
995 lasttick = *rb->current_tick;
997 /* animate swapping jewels */
998 for(k=0; k<=8; k++) {
999 /* clear old position */
1000 #ifdef HAVE_LCD_COLOR
1001 rb->lcd_set_foreground(jewels_bkgd[(x+y)%2]);
1002 rb->lcd_fillrect(x*TILE_WIDTH,
1003 y*TILE_HEIGHT+YOFS,
1004 TILE_WIDTH, TILE_HEIGHT);
1005 rb->lcd_set_foreground(jewels_bkgd[(x+horzmod+y+vertmod)%2]);
1006 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
1007 (y+vertmod)*TILE_HEIGHT+YOFS,
1008 TILE_WIDTH, TILE_HEIGHT);
1009 #else
1010 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1011 rb->lcd_fillrect(x*TILE_WIDTH,
1012 y*TILE_HEIGHT+YOFS,
1013 TILE_WIDTH, TILE_HEIGHT);
1014 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
1015 (y+vertmod)*TILE_HEIGHT+YOFS,
1016 TILE_WIDTH, TILE_HEIGHT);
1017 rb->lcd_set_drawmode(DRMODE_SOLID);
1018 #endif
1019 /* draw new position */
1020 #ifdef HAVE_LCD_COLOR
1021 rb->lcd_bitmap_transparent_part(jewels,
1022 0, TILE_HEIGHT*(bj->playboard
1023 [y+1+vertmod][x+horzmod].type),
1024 STRIDE( SCREEN_MAIN,
1025 BMPWIDTH_jewels, BMPHEIGHT_jewels),
1026 (x+horzmod)*TILE_WIDTH-horzmod*
1027 ((((movelen<<10)*k)/8)>>10),
1028 (y+vertmod)*TILE_HEIGHT-vertmod*
1029 ((((movelen<<10)*k)/8)>>10)+YOFS,
1030 TILE_WIDTH, TILE_HEIGHT);
1031 rb->lcd_bitmap_transparent_part(jewels,
1032 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
1033 STRIDE( SCREEN_MAIN,
1034 BMPWIDTH_jewels, BMPHEIGHT_jewels),
1035 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 #else
1041 rb->lcd_bitmap_part(jewels,
1042 0, TILE_HEIGHT*(bj->playboard
1043 [y+1+vertmod][x+horzmod].type),
1044 STRIDE( SCREEN_MAIN,
1045 BMPWIDTH_jewels, BMPHEIGHT_jewels),
1046 (x+horzmod)*TILE_WIDTH-horzmod*
1047 ((((movelen<<10)*k)/8)>>10),
1048 (y+vertmod)*TILE_HEIGHT-vertmod*
1049 ((((movelen<<10)*k)/8)>>10)+YOFS,
1050 TILE_WIDTH, TILE_HEIGHT);
1051 rb->lcd_set_drawmode(DRMODE_FG);
1052 rb->lcd_bitmap_part(jewels,
1053 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
1054 STRIDE( SCREEN_MAIN,
1055 BMPWIDTH_jewels, BMPHEIGHT_jewels),
1056 x*TILE_WIDTH+horzmod*
1057 ((((movelen<<10)*k)/8)>>10),
1058 y*TILE_HEIGHT+vertmod*
1059 ((((movelen<<10)*k)/8)>>10)+YOFS,
1060 TILE_WIDTH, TILE_HEIGHT);
1061 rb->lcd_set_drawmode(DRMODE_SOLID);
1062 #endif
1064 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
1065 jewels_setcolors();
1067 /* framerate limiting */
1068 currenttick = *rb->current_tick;
1069 if(currenttick-lasttick < HZ/MAX_FPS) {
1070 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
1071 } else {
1072 rb->yield();
1074 lasttick = currenttick;
1077 /* swap jewels */
1078 int temp = bj->playboard[y+1][x].type;
1079 bj->playboard[y+1][x].type =
1080 bj->playboard[y+1+vertmod][x+horzmod].type;
1081 bj->playboard[y+1+vertmod][x+horzmod].type = temp;
1083 if(undo) break;
1085 points = jewels_runboard(bj);
1086 if(points == 0) {
1087 undo = true;
1088 } else {
1089 break;
1093 return points;
1096 /*****************************************************************************
1097 * jewels_movesavail() uses pattern matching to see if there are any
1098 * available move left.
1099 ******************************************************************************/
1100 static bool jewels_movesavail(struct game_context* bj) {
1101 int i, j;
1102 bool moves = false;
1103 int mytype;
1105 for(i=1; i<BJ_HEIGHT; i++) {
1106 for(j=0; j<BJ_WIDTH; j++) {
1107 mytype = bj->playboard[i][j].type;
1108 if(mytype == 0 || mytype > MAX_NUM_JEWELS) continue;
1110 /* check horizontal patterns */
1111 if(j <= BJ_WIDTH-3) {
1112 if(i > 1) {
1113 if(bj->playboard[i-1][j+1].type == mytype) {
1114 if(bj->playboard[i-1][j+2].type == mytype)
1115 {moves = true; break;}
1116 if(bj->playboard[i][j+2].type == mytype)
1117 {moves = true; break;}
1119 if(bj->playboard[i][j+1].type == mytype) {
1120 if(bj->playboard[i-1][j+2].type == mytype)
1121 {moves = true; break;}
1125 if(j <= BJ_WIDTH-4) {
1126 if(bj->playboard[i][j+3].type == mytype) {
1127 if(bj->playboard[i][j+1].type == mytype)
1128 {moves = true; break;}
1129 if(bj->playboard[i][j+2].type == mytype)
1130 {moves = true; break;}
1134 if(i < BJ_HEIGHT-1) {
1135 if(bj->playboard[i][j+1].type == mytype) {
1136 if(bj->playboard[i+1][j+2].type == mytype)
1137 {moves = true; break;}
1139 if(bj->playboard[i+1][j+1].type == mytype) {
1140 if(bj->playboard[i][j+2].type == mytype)
1141 {moves = true; break;}
1142 if(bj->playboard[i+1][j+2].type == mytype)
1143 {moves = true; break;}
1148 /* check vertical patterns */
1149 if(i <= BJ_HEIGHT-3) {
1150 if(j > 0) {
1151 if(bj->playboard[i+1][j-1].type == mytype) {
1152 if(bj->playboard[i+2][j-1].type == mytype)
1153 {moves = true; break;}
1154 if(bj->playboard[i+2][j].type == mytype)
1155 {moves = true; break;}
1157 if(bj->playboard[i+1][j].type == mytype) {
1158 if(bj->playboard[i+2][j-1].type == mytype)
1159 {moves = true; break;}
1163 if(i <= BJ_HEIGHT-4) {
1164 if(bj->playboard[i+3][j].type == mytype) {
1165 if(bj->playboard[i+1][j].type == mytype)
1166 {moves = true; break;}
1167 if(bj->playboard[i+2][j].type == mytype)
1168 {moves = true; break;}
1172 if(j < BJ_WIDTH-1) {
1173 if(bj->playboard[i+1][j].type == mytype) {
1174 if(bj->playboard[i+2][j+1].type == mytype)
1175 {moves = true; break;}
1177 if(bj->playboard[i+1][j+1].type == mytype) {
1178 if(bj->playboard[i+2][j].type == mytype)
1179 {moves = true; break;}
1180 if (bj->playboard[i+2][j+1].type == mytype)
1181 {moves = true; break;}
1186 if(moves) break;
1188 return moves;
1191 /*****************************************************************************
1192 * jewels_puzzle_is_finished() checks if the puzzle is finished.
1193 ******************************************************************************/
1194 static bool jewels_puzzle_is_finished(struct game_context* bj) {
1195 unsigned int i, j;
1196 for(i=0; i<BJ_HEIGHT; i++) {
1197 for(j=0; j<BJ_WIDTH; j++) {
1198 int mytype = bj->playboard[i][j].type;
1199 if(mytype>MAX_NUM_JEWELS) {
1200 mytype -= MAX_NUM_JEWELS;
1201 if(mytype&PUZZLE_TILE_UP) {
1202 if(i==0 || bj->playboard[i-1][j].type<=MAX_NUM_JEWELS ||
1203 !((bj->playboard[i-1][j].type-MAX_NUM_JEWELS)
1204 &PUZZLE_TILE_DOWN))
1205 return false;
1207 if(mytype&PUZZLE_TILE_DOWN) {
1208 if(i==BJ_HEIGHT-1 ||
1209 bj->playboard[i+1][j].type<=MAX_NUM_JEWELS ||
1210 !((bj->playboard[i+1][j].type-MAX_NUM_JEWELS)
1211 &PUZZLE_TILE_UP))
1212 return false;
1214 if(mytype&PUZZLE_TILE_LEFT) {
1215 if(j==0 || bj->playboard[i][j-1].type<=MAX_NUM_JEWELS ||
1216 !((bj->playboard[i][j-1].type-MAX_NUM_JEWELS)
1217 &PUZZLE_TILE_RIGHT))
1218 return false;
1220 if(mytype&PUZZLE_TILE_RIGHT) {
1221 if(j==BJ_WIDTH-1 ||
1222 bj->playboard[i][j+1].type<=MAX_NUM_JEWELS ||
1223 !((bj->playboard[i][j+1].type-MAX_NUM_JEWELS)
1224 &PUZZLE_TILE_LEFT))
1225 return false;
1230 return true;
1233 /*****************************************************************************
1234 * jewels_initlevel() initialises a level.
1235 ******************************************************************************/
1236 static unsigned int jewels_initlevel(struct game_context* bj) {
1237 unsigned int points = 0;
1239 switch(bj->type) {
1240 case GAME_TYPE_NORMAL:
1241 bj->num_jewels = MAX_NUM_JEWELS;
1242 break;
1244 case GAME_TYPE_PUZZLE:
1246 unsigned int i, j;
1247 struct puzzle_tile *tile;
1249 bj->num_jewels = puzzle_levels[bj->level-1].num_jewels;
1251 for(i=0; i<BJ_HEIGHT; i++) {
1252 for(j=0; j<BJ_WIDTH; j++) {
1253 bj->playboard[i][j].type = (rb->rand()%bj->num_jewels)+1;
1254 bj->playboard[i][j].falling = false;
1255 bj->playboard[i][j].delete = false;
1258 jewels_runboard(bj);
1259 tile = puzzle_levels[bj->level-1].tiles;
1260 for(i=0; i<puzzle_levels[bj->level-1].num_tiles; i++, tile++) {
1261 bj->playboard[tile->y+1][tile->x].type = MAX_NUM_JEWELS
1262 +tile->tile_type;
1265 break;
1268 jewels_drawboard(bj);
1270 /* run the play board */
1271 jewels_putjewels(bj);
1272 points += jewels_runboard(bj);
1273 return points;
1276 /*****************************************************************************
1277 * jewels_init() initializes jewels data structures.
1278 ******************************************************************************/
1279 static void jewels_init(struct game_context* bj) {
1280 /* seed the rand generator */
1281 rb->srand(*rb->current_tick);
1283 bj->type = bj->tmp_type;
1284 bj->level = 1;
1285 bj->score = 0;
1286 bj->segments = 0;
1288 jewels_setcolors();
1290 /* clear playing board */
1291 rb->memset(bj->playboard, 0, sizeof(bj->playboard));
1292 do {
1293 bj->score += jewels_initlevel(bj);
1294 } while(!jewels_movesavail(bj));
1297 /*****************************************************************************
1298 * jewels_nextlevel() advances the game to the next bj->level and returns
1299 * points earned.
1300 ******************************************************************************/
1301 static void jewels_nextlevel(struct game_context* bj) {
1302 int i, x, y;
1303 unsigned int points = 0;
1305 switch(bj->type) {
1306 case GAME_TYPE_NORMAL:
1307 /* roll over score, change and display level */
1308 while(bj->score >= LEVEL_PTS) {
1309 bj->score -= LEVEL_PTS;
1310 bj->level++;
1311 rb->splashf(HZ*2, "Level %d", bj->level);
1312 jewels_drawboard(bj);
1315 /* randomly clear some jewels */
1316 for(i=0; i<16; i++) {
1317 x = rb->rand()%8;
1318 y = rb->rand()%8;
1320 if(bj->playboard[y][x].type != 0) {
1321 points++;
1322 bj->playboard[y][x].type = 0;
1325 break;
1327 case GAME_TYPE_PUZZLE:
1328 bj->level++;
1329 rb->splashf(HZ*2, "Level %d", bj->level);
1330 break;
1333 points += jewels_initlevel(bj);
1334 bj->score += points;
1337 static bool jewels_help(void)
1339 static char *help_text[] = {
1340 "Jewels", "", "Aim", "",
1341 "Swap", "pairs", "of", "jewels", "to", "form", "connected",
1342 "segments", "of", "three", "or", "more", "of", "the", "same",
1343 "type.", "",
1344 "The", "goal", "of", "the", "game", "is", "to", "score", "as", "many",
1345 "points", "as", "possible", "before", "running", "out", "of",
1346 "available", "moves.", "", "",
1347 "Controls", "",
1348 "Directions",
1349 #ifdef JEWELS_SCROLLWHEEL
1350 "or", "scroll",
1351 #endif
1352 "to", "move", "",
1353 HK_SELECT, "to", "select", "",
1354 HK_CANCEL, "to", "go", "to", "menu"
1356 static struct style_text formation[]={
1357 { 0, TEXT_CENTER|TEXT_UNDERLINE },
1358 { 2, C_RED },
1359 { 42, C_RED },
1360 LAST_STYLE_ITEM
1363 rb->lcd_setfont(FONT_UI);
1364 if (display_text(ARRAYLEN(help_text), help_text, formation, NULL, true))
1365 return true;
1366 rb->lcd_setfont(FONT_SYSFIXED);
1368 return false;
1371 static bool _ingame;
1372 static int jewels_menu_cb(int action, const struct menu_item_ex *this_item)
1374 int i = ((intptr_t)this_item);
1375 if(action == ACTION_REQUEST_MENUITEM
1376 && !_ingame && (i==0 || i==6))
1377 return ACTION_EXIT_MENUITEM;
1378 return action;
1380 /*****************************************************************************
1381 * jewels_game_menu() shows the game menu.
1382 ******************************************************************************/
1383 static int jewels_game_menu(struct game_context* bj, bool ingame)
1385 rb->button_clear_queue();
1386 int choice = 0;
1388 _ingame = ingame;
1390 static struct opt_items mode[] = {
1391 { "Normal", -1 },
1392 { "Puzzle", -1 },
1395 MENUITEM_STRINGLIST (main_menu, "Jewels Menu", jewels_menu_cb,
1396 "Resume Game",
1397 "Start New Game",
1398 "Mode",
1399 "Help",
1400 "High Scores",
1401 "Playback Control",
1402 "Quit without Saving",
1403 "Quit");
1405 while (1) {
1406 switch (rb->do_menu(&main_menu, &choice, NULL, false)) {
1407 case 0:
1408 jewels_setcolors();
1409 if(resume_file)
1410 rb->remove(SAVE_FILE);
1411 return 0;
1412 case 1:
1413 jewels_init(bj);
1414 return 0;
1415 case 2:
1416 rb->set_option("Mode", &bj->tmp_type, INT, mode, 2, NULL);
1417 break;
1418 case 3:
1419 if(jewels_help())
1420 return 1;
1421 break;
1422 case 4:
1423 highscore_show(-1, highscores, NUM_SCORES, true);
1424 break;
1425 case 5:
1426 playback_control(NULL);
1427 break;
1428 case 6:
1429 return 1;
1430 case 7:
1431 if (ingame) {
1432 rb->splash(HZ*1, "Saving game ...");
1433 jewels_savegame(bj);
1435 return 1;
1436 case MENU_ATTACHED_USB:
1437 return 1;
1438 default:
1439 break;
1444 static int jewels_main(struct game_context* bj) {
1445 int button;
1446 int position;
1447 bool selected = false;
1448 int x=0, y=0;
1450 bool loaded = jewels_loadgame(bj);
1451 resume_file = loaded;
1452 if (jewels_game_menu(bj, loaded)!=0)
1453 return 0;
1455 resume_file = false;
1456 while(true) {
1457 /* refresh the board */
1458 jewels_drawboard(bj);
1460 /* display the cursor */
1461 if(selected) {
1462 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1463 rb->lcd_fillrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1464 TILE_WIDTH, TILE_HEIGHT);
1465 rb->lcd_set_drawmode(DRMODE_SOLID);
1466 } else {
1467 rb->lcd_drawrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1468 TILE_WIDTH, TILE_HEIGHT);
1470 rb->lcd_update_rect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1471 TILE_WIDTH, TILE_HEIGHT);
1473 /* handle game button presses */
1474 rb->yield();
1475 button = rb->button_get(true);
1476 switch(button){
1477 case JEWELS_LEFT: /* move cursor left */
1478 case (JEWELS_LEFT|BUTTON_REPEAT):
1479 if(selected) {
1480 bj->score += jewels_swapjewels(bj, x, y, SWAP_LEFT);
1481 selected = false;
1482 } else {
1483 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1485 break;
1487 case JEWELS_RIGHT: /* move cursor right */
1488 case (JEWELS_RIGHT|BUTTON_REPEAT):
1489 if(selected) {
1490 bj->score += jewels_swapjewels(bj, x, y, SWAP_RIGHT);
1491 selected = false;
1492 } else {
1493 x = (x+1)%BJ_WIDTH;
1495 break;
1497 case JEWELS_DOWN: /* move cursor down */
1498 case (JEWELS_DOWN|BUTTON_REPEAT):
1499 if(selected) {
1500 bj->score += jewels_swapjewels(bj, x, y, SWAP_DOWN);
1501 selected = false;
1502 } else {
1503 y = (y+1)%(BJ_HEIGHT-1);
1505 break;
1507 case JEWELS_UP: /* move cursor up */
1508 case (JEWELS_UP|BUTTON_REPEAT):
1509 if(selected) {
1510 bj->score += jewels_swapjewels(bj, x, y, SWAP_UP);
1511 selected = false;
1512 } else {
1513 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1515 break;
1517 #ifdef JEWELS_SCROLLWHEEL
1518 case JEWELS_PREV: /* scroll backwards */
1519 case (JEWELS_PREV|BUTTON_REPEAT):
1520 if(!selected) {
1521 if(x == 0) {
1522 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1524 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1526 break;
1528 case JEWELS_NEXT: /* scroll forwards */
1529 case (JEWELS_NEXT|BUTTON_REPEAT):
1530 if(!selected) {
1531 if(x == BJ_WIDTH-1) {
1532 y = (y+1)%(BJ_HEIGHT-1);
1534 x = (x+1)%BJ_WIDTH;
1536 break;
1537 #endif
1539 case JEWELS_SELECT: /* toggle selected */
1540 selected = !selected;
1541 break;
1543 #ifdef JEWELS_RC_CANCEL
1544 case JEWELS_RC_CANCEL:
1545 #endif
1546 case JEWELS_CANCEL: /* end game */
1547 if (jewels_game_menu(bj, true)!=0)
1548 return 0;
1549 break;
1551 default:
1552 if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
1553 return PLUGIN_USB_CONNECTED;
1554 break;
1557 switch(bj->type) {
1558 case GAME_TYPE_NORMAL:
1559 if(bj->score >= LEVEL_PTS)
1560 jewels_nextlevel(bj);
1561 break;
1562 case GAME_TYPE_PUZZLE:
1563 if (jewels_puzzle_is_finished(bj)) {
1564 if (bj->level < NUM_PUZZLE_LEVELS) {
1565 jewels_nextlevel(bj);
1566 } else {
1567 rb->splash(2*HZ, "Congratulations!");
1568 rb->splash(2*HZ, "You have finished the game!");
1569 if (jewels_game_menu(bj, false)!=0) {
1570 return 0;
1573 break;
1577 if (!jewels_movesavail(bj)) {
1578 switch(bj->type) {
1579 case GAME_TYPE_NORMAL:
1580 rb->splash(HZ*2, "Game Over!");
1581 rb->lcd_clear_display();
1582 bj->score += (bj->level-1)*LEVEL_PTS;
1583 position=highscore_update(bj->score, bj->level, "",
1584 highscores, NUM_SCORES);
1585 if (position != -1)
1587 if (position == 0)
1588 rb->splash(HZ*2, "New High Score");
1589 highscore_show(position, highscores, NUM_SCORES, true);
1591 break;
1592 case GAME_TYPE_PUZZLE:
1593 rb->splash(2*HZ, "Game Over");
1594 break;
1596 if (jewels_game_menu(bj, false)!=0) {
1597 return 0;
1603 /* this is the plugin entry point */
1604 enum plugin_status plugin_start(const void* parameter)
1606 (void)parameter;
1608 /* load high scores */
1609 highscore_load(SCORE_FILE, highscores, NUM_SCORES);
1611 rb->lcd_setfont(FONT_SYSFIXED);
1612 #if LCD_DEPTH > 1
1613 rb->lcd_set_backdrop(NULL);
1614 #endif
1616 struct game_context bj;
1617 bj.tmp_type = GAME_TYPE_NORMAL;
1618 jewels_main(&bj);
1619 highscore_save(SCORE_FILE, highscores, NUM_SCORES);
1620 rb->lcd_setfont(FONT_UI);
1622 return PLUGIN_OK;