Prepare new maemo release
[maemo-rb.git] / apps / plugins / jewels.c
blobaafb6e2bcf8db5af0eeb1a275200136fb85bfdf5
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_POWER
315 #define HK_SELECT "SELECT"
316 #define HK_CANCEL "POWER"
318 #elif (CONFIG_KEYPAD == HM60X_PAD) || \
319 (CONFIG_KEYPAD == HM801_PAD)
320 #define JEWELS_LEFT BUTTON_LEFT
321 #define JEWELS_RIGHT BUTTON_RIGHT
322 #define JEWELS_UP BUTTON_UP
323 #define JEWELS_DOWN BUTTON_DOWN
324 #define JEWELS_SELECT BUTTON_SELECT
325 #define JEWELS_CANCEL BUTTON_POWER
326 #define HK_SELECT "SELECT"
327 #define HK_CANCEL "POWER"
329 #else
330 #error No keymap defined!
331 #endif
333 #ifdef HAVE_TOUCHSCREEN
334 #ifndef JEWELS_UP
335 #define JEWELS_UP BUTTON_TOPMIDDLE
336 #endif
337 #ifndef JEWELS_DOWN
338 #define JEWELS_DOWN BUTTON_BOTTOMMIDDLE
339 #endif
340 #ifndef JEWELS_LEFT
341 #define JEWELS_LEFT BUTTON_MIDLEFT
342 #endif
343 #ifndef JEWELS_RIGHT
344 #define JEWELS_RIGHT BUTTON_MIDRIGHT
345 #endif
346 #ifndef JEWELS_SELECT
347 #define JEWELS_SELECT BUTTON_CENTER
348 #define HK_SELECT "CENTER"
349 #endif
350 #ifndef JEWELS_CANCEL
351 #define JEWELS_CANCEL BUTTON_TOPLEFT
352 #define HK_CANCEL "TOPLEFT"
353 #endif
354 #endif
356 #define TILE_WIDTH BMPWIDTH_jewels
357 #define TILE_HEIGHT (BMPHEIGHT_jewels/23)
359 #if LCD_HEIGHT < LCD_WIDTH
360 /* This calculation assumes integer division w/ LCD_HEIGHT/TILE_HEIGHT */
361 #define YOFS LCD_HEIGHT-((LCD_HEIGHT/TILE_HEIGHT)*TILE_HEIGHT)
362 #else
363 #define YOFS 0
364 #endif
366 #define NUM_SCORES 5
368 /* swap directions */
369 #define SWAP_UP 0
370 #define SWAP_RIGHT 1
371 #define SWAP_DOWN 2
372 #define SWAP_LEFT 3
374 /* play board dimension */
375 #define BJ_HEIGHT 9
376 #define BJ_WIDTH 8
378 /* next level threshold */
379 #define LEVEL_PTS 100
381 /* animation frame rate */
382 #define MAX_FPS 20
384 /* text margin */
385 #define MARGIN 5
387 /* Game types */
388 enum game_type {
389 GAME_TYPE_NORMAL,
390 GAME_TYPE_PUZZLE
393 /* external bitmaps */
394 extern const fb_data jewels[];
396 /* tile background colors */
397 #ifdef HAVE_LCD_COLOR
398 static const unsigned jewels_bkgd[2] = {
399 LCD_RGBPACK(104, 63, 63),
400 LCD_RGBPACK(83, 44, 44)
402 #endif
404 /* the tile struct
405 * type is the jewel number 0-7
406 * falling if the jewel is falling
407 * delete marks the jewel for deletion
409 struct tile {
410 int type;
411 bool falling;
412 bool delete;
415 /* the game context struct
416 * score is the current level score
417 * segments is the number of cleared segments in the current run
418 * level is the current level
419 * tmp_type is the select type in the menu
420 * type is the game type (normal or puzzle)
421 * playboard is the game playing board (first row is hidden)
422 * num_jewels is the number of different jewels to use
424 struct game_context {
425 unsigned int score;
426 unsigned int segments;
427 unsigned int level;
428 unsigned int type;
429 unsigned int tmp_type;
430 struct tile playboard[BJ_HEIGHT][BJ_WIDTH];
431 unsigned int num_jewels;
434 #define MAX_NUM_JEWELS 7
436 #define MAX_PUZZLE_TILES 4
437 #define NUM_PUZZLE_LEVELS 10
439 struct puzzle_tile {
440 int x;
441 int y;
442 int tile_type;
445 struct puzzle_level {
446 unsigned int num_jewels;
447 unsigned int num_tiles;
448 struct puzzle_tile tiles[MAX_PUZZLE_TILES];
451 #define PUZZLE_TILE_UP 1
452 #define PUZZLE_TILE_DOWN 2
453 #define PUZZLE_TILE_LEFT 4
454 #define PUZZLE_TILE_RIGHT 8
456 struct puzzle_level puzzle_levels[NUM_PUZZLE_LEVELS] = {
457 { 5, 2, { {3, 3, PUZZLE_TILE_RIGHT},
458 {4, 2, PUZZLE_TILE_LEFT} } },
459 { 5, 2, { {3, 2, PUZZLE_TILE_DOWN},
460 {3, 4, PUZZLE_TILE_UP} } },
461 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
462 {3, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
463 {3, 6, PUZZLE_TILE_UP} } },
464 { 6, 3, { {3, 2, PUZZLE_TILE_RIGHT},
465 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
466 {5, 4, PUZZLE_TILE_LEFT} } },
467 { 6, 2, { {3, 4, PUZZLE_TILE_RIGHT},
468 {4, 2, PUZZLE_TILE_LEFT} } },
469 { 6, 2, { {3, 2, PUZZLE_TILE_DOWN},
470 {4, 4, PUZZLE_TILE_UP} } },
471 { 7, 4, { {3, 2, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
472 {4, 3, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
473 {3, 4, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
474 {4, 4, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
475 { 6, 3, { {3, 2, PUZZLE_TILE_DOWN},
476 {4, 4, PUZZLE_TILE_UP|PUZZLE_TILE_DOWN},
477 {3, 6, PUZZLE_TILE_UP} } },
478 { 7, 3, { {2, 2, PUZZLE_TILE_RIGHT},
479 {4, 1, PUZZLE_TILE_LEFT|PUZZLE_TILE_RIGHT},
480 {5, 4, PUZZLE_TILE_LEFT} } },
481 { 7, 4, { {3, 0, PUZZLE_TILE_RIGHT|PUZZLE_TILE_DOWN},
482 {5, 0, PUZZLE_TILE_LEFT|PUZZLE_TILE_DOWN},
483 {2, 7, PUZZLE_TILE_RIGHT|PUZZLE_TILE_UP},
484 {4, 7, PUZZLE_TILE_LEFT|PUZZLE_TILE_UP} } },
487 #define SAVE_FILE PLUGIN_GAMES_DATA_DIR "/jewels.save"
488 #define SCORE_FILE PLUGIN_GAMES_DATA_DIR "/jewels.score"
489 struct highscore highscores[NUM_SCORES];
491 static bool resume_file = false;
493 /*****************************************************************************
494 * jewels_setcolors() set the foreground and background colors.
495 ******************************************************************************/
496 static inline void jewels_setcolors(void) {
497 #ifdef HAVE_LCD_COLOR
498 rb->lcd_set_background(LCD_RGBPACK(49, 26, 26));
499 rb->lcd_set_foreground(LCD_RGBPACK(210, 181, 181));
500 #endif
503 /*****************************************************************************
504 * jewels_loadgame() loads the saved game and returns load success.
505 ******************************************************************************/
506 static bool jewels_loadgame(struct game_context* bj)
508 int fd;
509 bool loaded = false;
511 /* open game file */
512 fd = rb->open(SAVE_FILE, O_RDONLY);
513 if(fd < 0) return loaded;
515 /* read in saved game */
516 while(true) {
517 if(rb->read(fd, &bj->tmp_type, sizeof(bj->tmp_type)) <= 0) break;
518 if(rb->read(fd, &bj->type, sizeof(bj->type)) <= 0) break;
519 if(rb->read(fd, &bj->score, sizeof(bj->score)) <= 0) break;
520 if(rb->read(fd, &bj->level, sizeof(bj->level)) <= 0) break;
521 if(rb->read(fd, &bj->segments, sizeof(bj->segments)) <= 0) break;
522 if(rb->read(fd, &bj->num_jewels, sizeof(bj->num_jewels)) <= 0) break;
523 if(rb->read(fd, bj->playboard, sizeof(bj->playboard)) <= 0) break;
524 loaded = true;
525 break;
528 rb->close(fd);
530 return loaded;
533 /*****************************************************************************
534 * jewels_savegame() saves the current game state.
535 ******************************************************************************/
536 static void jewels_savegame(struct game_context* bj)
538 int fd;
539 /* write out the game state to the save file */
540 fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT, 0666);
541 if(fd < 0) return;
543 rb->write(fd, &bj->tmp_type, sizeof(bj->tmp_type));
544 rb->write(fd, &bj->type, sizeof(bj->type));
545 rb->write(fd, &bj->score, sizeof(bj->score));
546 rb->write(fd, &bj->level, sizeof(bj->level));
547 rb->write(fd, &bj->segments, sizeof(bj->segments));
548 rb->write(fd, &bj->num_jewels, sizeof(bj->num_jewels));
549 rb->write(fd, bj->playboard, sizeof(bj->playboard));
550 rb->close(fd);
553 /*****************************************************************************
554 * jewels_drawboard() redraws the entire game board.
555 ******************************************************************************/
556 static void jewels_drawboard(struct game_context* bj) {
557 int i, j;
558 int w, h;
559 unsigned int tempscore;
560 unsigned int size;
561 char *title = "Level";
562 char str[10];
564 if (bj->type == GAME_TYPE_NORMAL) {
565 tempscore = (bj->score>LEVEL_PTS ? LEVEL_PTS : bj->score);
566 size = LEVEL_PTS;
567 } else {
568 tempscore = (bj->level>NUM_PUZZLE_LEVELS ? NUM_PUZZLE_LEVELS : bj->level);
569 size = NUM_PUZZLE_LEVELS;
572 /* clear screen */
573 rb->lcd_clear_display();
575 /* dispay playing board */
576 for(i=0; i<BJ_HEIGHT-1; i++){
577 for(j=0; j<BJ_WIDTH; j++){
578 #ifdef HAVE_LCD_COLOR
579 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
580 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
581 TILE_WIDTH, TILE_HEIGHT);
582 rb->lcd_bitmap_transparent_part(jewels,
583 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
584 STRIDE( SCREEN_MAIN,
585 BMPWIDTH_jewels, BMPHEIGHT_jewels),
586 j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
587 TILE_WIDTH, TILE_HEIGHT);
588 #else
589 rb->lcd_bitmap_part(jewels,
590 0, TILE_HEIGHT*(bj->playboard[i+1][j].type),
591 STRIDE( SCREEN_MAIN,
592 BMPWIDTH_jewels, BMPHEIGHT_jewels),
593 j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
594 TILE_WIDTH, TILE_HEIGHT);
595 #endif
599 #if LCD_WIDTH > LCD_HEIGHT /* horizontal layout */
601 /* draw separator lines */
602 jewels_setcolors();
603 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, LCD_HEIGHT-1);
605 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, 18);
606 rb->lcd_hline(BJ_WIDTH*TILE_WIDTH, LCD_WIDTH-1, LCD_HEIGHT-10);
608 /* draw progress bar */
609 #ifdef HAVE_LCD_COLOR
610 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
611 #endif
612 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
613 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
614 tempscore/size),
615 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
616 ((LCD_HEIGHT-10)-18)*tempscore/size);
617 #ifdef HAVE_LCD_COLOR
618 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
619 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
620 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*
621 tempscore/size)+1,
622 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
623 ((LCD_HEIGHT-10)-18)*tempscore/size-1);
624 jewels_setcolors();
625 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
626 (LCD_HEIGHT-10)-(((LCD_HEIGHT-10)-18)*tempscore/size),
627 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
628 ((LCD_HEIGHT-10)-18)*tempscore/size+1);
629 #endif
631 /* print text */
632 rb->lcd_getstringsize(title, &w, &h);
633 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 1, title);
634 rb->snprintf(str, 4, "%d", bj->level);
635 rb->lcd_getstringsize(str, &w, &h);
636 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2, 10, str);
638 if (bj->type == GAME_TYPE_NORMAL) {
639 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
640 rb->lcd_getstringsize(str, &w, &h);
641 rb->lcd_putsxy(LCD_WIDTH-(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-w/2,
642 LCD_HEIGHT-8, str);
645 #elif LCD_WIDTH < LCD_HEIGHT /* vertical layout */
647 /* draw separator lines */
648 jewels_setcolors();
649 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
650 rb->lcd_hline(0, LCD_WIDTH-1, LCD_HEIGHT-14);
651 rb->lcd_vline(LCD_WIDTH/2, LCD_HEIGHT-14, LCD_HEIGHT-1);
653 /* draw progress bar */
654 #ifdef HAVE_LCD_COLOR
655 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
656 #endif
657 rb->lcd_fillrect(0, (8*TILE_HEIGHT+YOFS)
658 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
659 LCD_WIDTH*tempscore/size,
660 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
661 #ifdef HAVE_LCD_COLOR
662 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
663 rb->lcd_drawrect(1, (8*TILE_HEIGHT+YOFS)
664 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4+1,
665 LCD_WIDTH*tempscore/size-1,
666 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2-2);
667 jewels_setcolors();
668 rb->lcd_drawrect(0, (8*TILE_HEIGHT+YOFS)
669 +(LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/4,
670 LCD_WIDTH*tempscore/size+1,
671 (LCD_HEIGHT-14-(8*TILE_HEIGHT+YOFS))/2);
672 #endif
674 /* print text */
675 rb->lcd_putsxyf(1, LCD_HEIGHT-10, "%s %d", title, bj->level);
677 if (bj->type == GAME_TYPE_NORMAL) {
678 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
679 rb->lcd_getstringsize(str, &w, &h);
680 rb->lcd_putsxy((LCD_WIDTH-2)-w, LCD_HEIGHT-10, str);
684 #else /* square layout */
686 /* draw separator lines */
687 jewels_setcolors();
688 rb->lcd_hline(0, LCD_WIDTH-1, 8*TILE_HEIGHT+YOFS);
689 rb->lcd_vline(BJ_WIDTH*TILE_WIDTH, 0, 8*TILE_HEIGHT+YOFS);
690 rb->lcd_vline(LCD_WIDTH/2, 8*TILE_HEIGHT+YOFS, LCD_HEIGHT-1);
692 /* draw progress bar */
693 #ifdef HAVE_LCD_COLOR
694 rb->lcd_set_foreground(LCD_RGBPACK(104, 63, 63));
695 #endif
696 rb->lcd_fillrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
697 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)*tempscore/size,
698 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
699 (8*TILE_HEIGHT+YOFS)*tempscore/size);
700 #ifdef HAVE_LCD_COLOR
701 rb->lcd_set_foreground(LCD_RGBPACK(83, 44, 44));
702 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4+1,
703 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
704 *tempscore/size+1,
705 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2-2,
706 (8*TILE_HEIGHT+YOFS)*tempscore/size-1);
707 jewels_setcolors();
708 rb->lcd_drawrect(BJ_WIDTH*TILE_WIDTH+(LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/4,
709 (8*TILE_HEIGHT+YOFS)-(8*TILE_HEIGHT+YOFS)
710 *tempscore/size,
711 (LCD_WIDTH-BJ_WIDTH*TILE_WIDTH)/2,
712 (8*TILE_HEIGHT+YOFS)*tempscore/size+1);
713 #endif
715 /* print text */
716 rb->lcd_putsxyf(1, LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3,"%s %d",
717 title, bj->level);
719 if (bj->type == GAME_TYPE_NORMAL) {
720 rb->snprintf(str, 6, "%d", (bj->level-1)*LEVEL_PTS+bj->score);
721 rb->lcd_getstringsize(str, &w, &h);
722 rb->lcd_putsxy((LCD_WIDTH-2)-w,
723 LCD_HEIGHT-(LCD_HEIGHT-(8*TILE_HEIGHT+YOFS))/2-3, str);
726 #endif /* layout */
728 rb->lcd_update();
731 /*****************************************************************************
732 * jewels_putjewels() makes the jewels fall to fill empty spots and adds
733 * new random jewels at the empty spots at the top of each row.
734 ******************************************************************************/
735 static void jewels_putjewels(struct game_context* bj){
736 int i, j, k;
737 bool mark, done;
738 long lasttick, currenttick;
740 /* loop to make all the jewels fall */
741 while(true) {
742 /* mark falling jewels and add new jewels to hidden top row*/
743 mark = false;
744 done = true;
745 for(j=0; j<BJ_WIDTH; j++) {
746 if(bj->playboard[1][j].type == 0) {
747 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
749 for(i=BJ_HEIGHT-2; i>=0; i--) {
750 if(!mark && bj->playboard[i+1][j].type == 0) {
751 mark = true;
752 done = false;
754 if(mark) bj->playboard[i][j].falling = true;
756 /*if(bj->playboard[1][j].falling) {
757 bj->playboard[0][j].type = rb->rand()%bj->num_jewels+1;
758 bj->playboard[0][j].falling = true;
760 mark = false;
763 /* break if there are no falling jewels */
764 if(done) break;
766 /* animate falling jewels */
767 lasttick = *rb->current_tick;
769 for(k=1; k<=8; k++) {
770 for(i=BJ_HEIGHT-2; i>=0; i--) {
771 for(j=0; j<BJ_WIDTH; j++) {
772 if(bj->playboard[i][j].falling &&
773 bj->playboard[i][j].type != 0) {
774 /* clear old position */
775 #ifdef HAVE_LCD_COLOR
776 if(i == 0 && YOFS) {
777 rb->lcd_set_foreground(rb->lcd_get_background());
778 } else {
779 rb->lcd_set_foreground(jewels_bkgd[(i-1+j)%2]);
781 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
782 TILE_WIDTH, TILE_HEIGHT);
783 if(bj->playboard[i+1][j].type == 0) {
784 rb->lcd_set_foreground(jewels_bkgd[(i+j)%2]);
785 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
786 TILE_WIDTH, TILE_HEIGHT);
788 #else
789 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
790 rb->lcd_fillrect(j*TILE_WIDTH, (i-1)*TILE_HEIGHT+YOFS,
791 TILE_WIDTH, TILE_HEIGHT);
792 if(bj->playboard[i+1][j].type == 0) {
793 rb->lcd_fillrect(j*TILE_WIDTH, i*TILE_HEIGHT+YOFS,
794 TILE_WIDTH, TILE_HEIGHT);
796 rb->lcd_set_drawmode(DRMODE_SOLID);
797 #endif
799 /* draw new position */
800 #ifdef HAVE_LCD_COLOR
801 rb->lcd_bitmap_transparent_part(jewels, 0,
802 TILE_HEIGHT*(bj->playboard[i][j].type),
803 STRIDE( SCREEN_MAIN,
804 BMPWIDTH_jewels,
805 BMPHEIGHT_jewels),
806 j*TILE_WIDTH,
807 (i-1)*TILE_HEIGHT+YOFS+
808 ((((TILE_HEIGHT<<10)*k)/8)>>10),
809 TILE_WIDTH, TILE_HEIGHT);
810 #else
811 rb->lcd_bitmap_part(jewels, 0,
812 TILE_HEIGHT*(bj->playboard[i][j].type),
813 STRIDE( SCREEN_MAIN,
814 BMPWIDTH_jewels,
815 BMPHEIGHT_jewels),
816 j*TILE_WIDTH,
817 (i-1)*TILE_HEIGHT+YOFS+
818 ((((TILE_HEIGHT<<10)*k)/8)>>10),
819 TILE_WIDTH, TILE_HEIGHT);
820 #endif
825 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
826 jewels_setcolors();
828 /* framerate limiting */
829 currenttick = *rb->current_tick;
830 if(currenttick-lasttick < HZ/MAX_FPS) {
831 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
832 } else {
833 rb->yield();
835 lasttick = currenttick;
838 /* shift jewels down */
839 for(j=0; j<BJ_WIDTH; j++) {
840 for(i=BJ_HEIGHT-1; i>=1; i--) {
841 if(bj->playboard[i-1][j].falling) {
842 bj->playboard[i][j].type = bj->playboard[i-1][j].type;
847 /* clear out top row */
848 for(j=0; j<BJ_WIDTH; j++) {
849 bj->playboard[0][j].type = 0;
852 /* mark everything not falling */
853 for(i=0; i<BJ_HEIGHT; i++) {
854 for(j=0; j<BJ_WIDTH; j++) {
855 bj->playboard[i][j].falling = false;
861 /*****************************************************************************
862 * jewels_clearjewels() finds all the connected rows and columns and
863 * calculates and returns the points earned.
864 ******************************************************************************/
865 static unsigned int jewels_clearjewels(struct game_context* bj) {
866 int i, j;
867 int last, run;
868 unsigned int points = 0;
870 /* check for connected rows */
871 for(i=1; i<BJ_HEIGHT; i++) {
872 last = 0;
873 run = 1;
874 for(j=0; j<BJ_WIDTH; j++) {
875 if(bj->playboard[i][j].type == last &&
876 bj->playboard[i][j].type != 0 &&
877 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
878 run++;
880 if(run == 3) {
881 bj->segments++;
882 points += bj->segments;
883 bj->playboard[i][j].delete = true;
884 bj->playboard[i][j-1].delete = true;
885 bj->playboard[i][j-2].delete = true;
886 } else if(run > 3) {
887 points++;
888 bj->playboard[i][j].delete = true;
890 } else {
891 run = 1;
892 last = bj->playboard[i][j].type;
897 /* check for connected columns */
898 for(j=0; j<BJ_WIDTH; j++) {
899 last = 0;
900 run = 1;
901 for(i=1; i<BJ_HEIGHT; i++) {
902 if(bj->playboard[i][j].type != 0 &&
903 bj->playboard[i][j].type == last &&
904 bj->playboard[i][j].type <= MAX_NUM_JEWELS) {
905 run++;
907 if(run == 3) {
908 bj->segments++;
909 points += bj->segments;
910 bj->playboard[i][j].delete = true;
911 bj->playboard[i-1][j].delete = true;
912 bj->playboard[i-2][j].delete = true;
913 } else if(run > 3) {
914 points++;
915 bj->playboard[i][j].delete = true;
917 } else {
918 run = 1;
919 last = bj->playboard[i][j].type;
924 /* clear deleted jewels */
925 for(i=1; i<BJ_HEIGHT; i++) {
926 for(j=0; j<BJ_WIDTH; j++) {
927 if(bj->playboard[i][j].delete) {
928 bj->playboard[i][j].delete = false;
929 bj->playboard[i][j].type = 0;
934 return points;
937 /*****************************************************************************
938 * jewels_runboard() runs the board until it settles in a fixed state and
939 * returns points earned.
940 ******************************************************************************/
941 static unsigned int jewels_runboard(struct game_context* bj) {
942 unsigned int points = 0;
943 unsigned int ret;
945 bj->segments = 0;
947 while((ret = jewels_clearjewels(bj)) > 0) {
948 points += ret;
949 jewels_drawboard(bj);
950 jewels_putjewels(bj);
953 return points;
956 /*****************************************************************************
957 * jewels_swapjewels() swaps two jewels as long as it results in points and
958 * returns points earned.
959 ******************************************************************************/
960 static unsigned int jewels_swapjewels(struct game_context* bj,
961 int x, int y, int direc) {
962 int k;
963 int horzmod, vertmod;
964 int movelen = 0;
965 bool undo = false;
966 unsigned int points = 0;
967 long lasttick, currenttick;
969 /* check for invalid parameters */
970 if(x < 0 || x >= BJ_WIDTH || y < 0 || y >= BJ_HEIGHT-1 ||
971 direc < SWAP_UP || direc > SWAP_LEFT) {
972 return 0;
975 /* check for invalid directions */
976 if((x == 0 && direc == SWAP_LEFT) ||
977 (x == BJ_WIDTH-1 && direc == SWAP_RIGHT) ||
978 (y == 0 && direc == SWAP_UP) ||
979 (y == BJ_HEIGHT-2 && direc == SWAP_DOWN)) {
980 return 0;
983 /* set direction variables */
984 horzmod = 0;
985 vertmod = 0;
986 switch(direc) {
987 case SWAP_UP:
988 vertmod = -1;
989 movelen = TILE_HEIGHT;
990 break;
991 case SWAP_RIGHT:
992 horzmod = 1;
993 movelen = TILE_WIDTH;
994 break;
995 case SWAP_DOWN:
996 vertmod = 1;
997 movelen = TILE_HEIGHT;
998 break;
999 case SWAP_LEFT:
1000 horzmod = -1;
1001 movelen = TILE_WIDTH;
1002 break;
1005 while(true) {
1006 lasttick = *rb->current_tick;
1008 /* animate swapping jewels */
1009 for(k=0; k<=8; k++) {
1010 /* clear old position */
1011 #ifdef HAVE_LCD_COLOR
1012 rb->lcd_set_foreground(jewels_bkgd[(x+y)%2]);
1013 rb->lcd_fillrect(x*TILE_WIDTH,
1014 y*TILE_HEIGHT+YOFS,
1015 TILE_WIDTH, TILE_HEIGHT);
1016 rb->lcd_set_foreground(jewels_bkgd[(x+horzmod+y+vertmod)%2]);
1017 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
1018 (y+vertmod)*TILE_HEIGHT+YOFS,
1019 TILE_WIDTH, TILE_HEIGHT);
1020 #else
1021 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1022 rb->lcd_fillrect(x*TILE_WIDTH,
1023 y*TILE_HEIGHT+YOFS,
1024 TILE_WIDTH, TILE_HEIGHT);
1025 rb->lcd_fillrect((x+horzmod)*TILE_WIDTH,
1026 (y+vertmod)*TILE_HEIGHT+YOFS,
1027 TILE_WIDTH, TILE_HEIGHT);
1028 rb->lcd_set_drawmode(DRMODE_SOLID);
1029 #endif
1030 /* draw new position */
1031 #ifdef HAVE_LCD_COLOR
1032 rb->lcd_bitmap_transparent_part(jewels,
1033 0, TILE_HEIGHT*(bj->playboard
1034 [y+1+vertmod][x+horzmod].type),
1035 STRIDE( SCREEN_MAIN,
1036 BMPWIDTH_jewels, BMPHEIGHT_jewels),
1037 (x+horzmod)*TILE_WIDTH-horzmod*
1038 ((((movelen<<10)*k)/8)>>10),
1039 (y+vertmod)*TILE_HEIGHT-vertmod*
1040 ((((movelen<<10)*k)/8)>>10)+YOFS,
1041 TILE_WIDTH, TILE_HEIGHT);
1042 rb->lcd_bitmap_transparent_part(jewels,
1043 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
1044 STRIDE( SCREEN_MAIN,
1045 BMPWIDTH_jewels, BMPHEIGHT_jewels),
1046 x*TILE_WIDTH+horzmod*
1047 ((((movelen<<10)*k)/8)>>10),
1048 y*TILE_HEIGHT+vertmod*
1049 ((((movelen<<10)*k)/8)>>10)+YOFS,
1050 TILE_WIDTH, TILE_HEIGHT);
1051 #else
1052 rb->lcd_bitmap_part(jewels,
1053 0, TILE_HEIGHT*(bj->playboard
1054 [y+1+vertmod][x+horzmod].type),
1055 STRIDE( SCREEN_MAIN,
1056 BMPWIDTH_jewels, BMPHEIGHT_jewels),
1057 (x+horzmod)*TILE_WIDTH-horzmod*
1058 ((((movelen<<10)*k)/8)>>10),
1059 (y+vertmod)*TILE_HEIGHT-vertmod*
1060 ((((movelen<<10)*k)/8)>>10)+YOFS,
1061 TILE_WIDTH, TILE_HEIGHT);
1062 rb->lcd_set_drawmode(DRMODE_FG);
1063 rb->lcd_bitmap_part(jewels,
1064 0, TILE_HEIGHT*(bj->playboard[y+1][x].type),
1065 STRIDE( SCREEN_MAIN,
1066 BMPWIDTH_jewels, BMPHEIGHT_jewels),
1067 x*TILE_WIDTH+horzmod*
1068 ((((movelen<<10)*k)/8)>>10),
1069 y*TILE_HEIGHT+vertmod*
1070 ((((movelen<<10)*k)/8)>>10)+YOFS,
1071 TILE_WIDTH, TILE_HEIGHT);
1072 rb->lcd_set_drawmode(DRMODE_SOLID);
1073 #endif
1075 rb->lcd_update_rect(0, 0, TILE_WIDTH*8, LCD_HEIGHT);
1076 jewels_setcolors();
1078 /* framerate limiting */
1079 currenttick = *rb->current_tick;
1080 if(currenttick-lasttick < HZ/MAX_FPS) {
1081 rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
1082 } else {
1083 rb->yield();
1085 lasttick = currenttick;
1088 /* swap jewels */
1089 int temp = bj->playboard[y+1][x].type;
1090 bj->playboard[y+1][x].type =
1091 bj->playboard[y+1+vertmod][x+horzmod].type;
1092 bj->playboard[y+1+vertmod][x+horzmod].type = temp;
1094 if(undo) break;
1096 points = jewels_runboard(bj);
1097 if(points == 0) {
1098 undo = true;
1099 } else {
1100 break;
1104 return points;
1107 /*****************************************************************************
1108 * jewels_movesavail() uses pattern matching to see if there are any
1109 * available move left.
1110 ******************************************************************************/
1111 static bool jewels_movesavail(struct game_context* bj) {
1112 int i, j;
1113 bool moves = false;
1114 int mytype;
1116 for(i=1; i<BJ_HEIGHT; i++) {
1117 for(j=0; j<BJ_WIDTH; j++) {
1118 mytype = bj->playboard[i][j].type;
1119 if(mytype == 0 || mytype > MAX_NUM_JEWELS) continue;
1121 /* check horizontal patterns */
1122 if(j <= BJ_WIDTH-3) {
1123 if(i > 1) {
1124 if(bj->playboard[i-1][j+1].type == mytype) {
1125 if(bj->playboard[i-1][j+2].type == mytype)
1126 {moves = true; break;}
1127 if(bj->playboard[i][j+2].type == mytype)
1128 {moves = true; break;}
1130 if(bj->playboard[i][j+1].type == mytype) {
1131 if(bj->playboard[i-1][j+2].type == mytype)
1132 {moves = true; break;}
1136 if(j <= BJ_WIDTH-4) {
1137 if(bj->playboard[i][j+3].type == mytype) {
1138 if(bj->playboard[i][j+1].type == mytype)
1139 {moves = true; break;}
1140 if(bj->playboard[i][j+2].type == mytype)
1141 {moves = true; break;}
1145 if(i < BJ_HEIGHT-1) {
1146 if(bj->playboard[i][j+1].type == mytype) {
1147 if(bj->playboard[i+1][j+2].type == mytype)
1148 {moves = true; break;}
1150 if(bj->playboard[i+1][j+1].type == mytype) {
1151 if(bj->playboard[i][j+2].type == mytype)
1152 {moves = true; break;}
1153 if(bj->playboard[i+1][j+2].type == mytype)
1154 {moves = true; break;}
1159 /* check vertical patterns */
1160 if(i <= BJ_HEIGHT-3) {
1161 if(j > 0) {
1162 if(bj->playboard[i+1][j-1].type == mytype) {
1163 if(bj->playboard[i+2][j-1].type == mytype)
1164 {moves = true; break;}
1165 if(bj->playboard[i+2][j].type == mytype)
1166 {moves = true; break;}
1168 if(bj->playboard[i+1][j].type == mytype) {
1169 if(bj->playboard[i+2][j-1].type == mytype)
1170 {moves = true; break;}
1174 if(i <= BJ_HEIGHT-4) {
1175 if(bj->playboard[i+3][j].type == mytype) {
1176 if(bj->playboard[i+1][j].type == mytype)
1177 {moves = true; break;}
1178 if(bj->playboard[i+2][j].type == mytype)
1179 {moves = true; break;}
1183 if(j < BJ_WIDTH-1) {
1184 if(bj->playboard[i+1][j].type == mytype) {
1185 if(bj->playboard[i+2][j+1].type == mytype)
1186 {moves = true; break;}
1188 if(bj->playboard[i+1][j+1].type == mytype) {
1189 if(bj->playboard[i+2][j].type == mytype)
1190 {moves = true; break;}
1191 if (bj->playboard[i+2][j+1].type == mytype)
1192 {moves = true; break;}
1197 if(moves) break;
1199 return moves;
1202 /*****************************************************************************
1203 * jewels_puzzle_is_finished() checks if the puzzle is finished.
1204 ******************************************************************************/
1205 static bool jewels_puzzle_is_finished(struct game_context* bj) {
1206 unsigned int i, j;
1207 for(i=0; i<BJ_HEIGHT; i++) {
1208 for(j=0; j<BJ_WIDTH; j++) {
1209 int mytype = bj->playboard[i][j].type;
1210 if(mytype>MAX_NUM_JEWELS) {
1211 mytype -= MAX_NUM_JEWELS;
1212 if(mytype&PUZZLE_TILE_UP) {
1213 if(i==0 || bj->playboard[i-1][j].type<=MAX_NUM_JEWELS ||
1214 !((bj->playboard[i-1][j].type-MAX_NUM_JEWELS)
1215 &PUZZLE_TILE_DOWN))
1216 return false;
1218 if(mytype&PUZZLE_TILE_DOWN) {
1219 if(i==BJ_HEIGHT-1 ||
1220 bj->playboard[i+1][j].type<=MAX_NUM_JEWELS ||
1221 !((bj->playboard[i+1][j].type-MAX_NUM_JEWELS)
1222 &PUZZLE_TILE_UP))
1223 return false;
1225 if(mytype&PUZZLE_TILE_LEFT) {
1226 if(j==0 || bj->playboard[i][j-1].type<=MAX_NUM_JEWELS ||
1227 !((bj->playboard[i][j-1].type-MAX_NUM_JEWELS)
1228 &PUZZLE_TILE_RIGHT))
1229 return false;
1231 if(mytype&PUZZLE_TILE_RIGHT) {
1232 if(j==BJ_WIDTH-1 ||
1233 bj->playboard[i][j+1].type<=MAX_NUM_JEWELS ||
1234 !((bj->playboard[i][j+1].type-MAX_NUM_JEWELS)
1235 &PUZZLE_TILE_LEFT))
1236 return false;
1241 return true;
1244 /*****************************************************************************
1245 * jewels_initlevel() initialises a level.
1246 ******************************************************************************/
1247 static unsigned int jewels_initlevel(struct game_context* bj) {
1248 unsigned int points = 0;
1250 switch(bj->type) {
1251 case GAME_TYPE_NORMAL:
1252 bj->num_jewels = MAX_NUM_JEWELS;
1253 break;
1255 case GAME_TYPE_PUZZLE:
1257 unsigned int i, j;
1258 struct puzzle_tile *tile;
1260 bj->num_jewels = puzzle_levels[bj->level-1].num_jewels;
1262 for(i=0; i<BJ_HEIGHT; i++) {
1263 for(j=0; j<BJ_WIDTH; j++) {
1264 bj->playboard[i][j].type = (rb->rand()%bj->num_jewels)+1;
1265 bj->playboard[i][j].falling = false;
1266 bj->playboard[i][j].delete = false;
1269 jewels_runboard(bj);
1270 tile = puzzle_levels[bj->level-1].tiles;
1271 for(i=0; i<puzzle_levels[bj->level-1].num_tiles; i++, tile++) {
1272 bj->playboard[tile->y+1][tile->x].type = MAX_NUM_JEWELS
1273 +tile->tile_type;
1276 break;
1279 jewels_drawboard(bj);
1281 /* run the play board */
1282 jewels_putjewels(bj);
1283 points += jewels_runboard(bj);
1284 return points;
1287 /*****************************************************************************
1288 * jewels_init() initializes jewels data structures.
1289 ******************************************************************************/
1290 static void jewels_init(struct game_context* bj) {
1291 /* seed the rand generator */
1292 rb->srand(*rb->current_tick);
1294 bj->type = bj->tmp_type;
1295 bj->level = 1;
1296 bj->score = 0;
1297 bj->segments = 0;
1299 jewels_setcolors();
1301 /* clear playing board */
1302 rb->memset(bj->playboard, 0, sizeof(bj->playboard));
1303 do {
1304 bj->score += jewels_initlevel(bj);
1305 } while(!jewels_movesavail(bj));
1308 /*****************************************************************************
1309 * jewels_nextlevel() advances the game to the next bj->level and returns
1310 * points earned.
1311 ******************************************************************************/
1312 static void jewels_nextlevel(struct game_context* bj) {
1313 int i, x, y;
1314 unsigned int points = 0;
1316 switch(bj->type) {
1317 case GAME_TYPE_NORMAL:
1318 /* roll over score, change and display level */
1319 while(bj->score >= LEVEL_PTS) {
1320 bj->score -= LEVEL_PTS;
1321 bj->level++;
1322 rb->splashf(HZ*2, "Level %d", bj->level);
1323 jewels_drawboard(bj);
1326 /* randomly clear some jewels */
1327 for(i=0; i<16; i++) {
1328 x = rb->rand()%8;
1329 y = rb->rand()%8;
1331 if(bj->playboard[y][x].type != 0) {
1332 points++;
1333 bj->playboard[y][x].type = 0;
1336 break;
1338 case GAME_TYPE_PUZZLE:
1339 bj->level++;
1340 rb->splashf(HZ*2, "Level %d", bj->level);
1341 break;
1344 points += jewels_initlevel(bj);
1345 bj->score += points;
1348 static bool jewels_help(void)
1350 static char *help_text[] = {
1351 "Jewels", "", "Aim", "",
1352 "Swap", "pairs", "of", "jewels", "to", "form", "connected",
1353 "segments", "of", "three", "or", "more", "of", "the", "same",
1354 "type.", "",
1355 "The", "goal", "of", "the", "game", "is", "to", "score", "as", "many",
1356 "points", "as", "possible", "before", "running", "out", "of",
1357 "available", "moves.", "", "",
1358 "Controls", "",
1359 "Directions",
1360 #ifdef JEWELS_SCROLLWHEEL
1361 "or", "scroll",
1362 #endif
1363 "to", "move", "",
1364 HK_SELECT, "to", "select", "",
1365 HK_CANCEL, "to", "go", "to", "menu"
1367 static struct style_text formation[]={
1368 { 0, TEXT_CENTER|TEXT_UNDERLINE },
1369 { 2, C_RED },
1370 { 42, C_RED },
1371 LAST_STYLE_ITEM
1374 rb->lcd_setfont(FONT_UI);
1375 if (display_text(ARRAYLEN(help_text), help_text, formation, NULL, true))
1376 return true;
1377 rb->lcd_setfont(FONT_SYSFIXED);
1379 return false;
1382 static bool _ingame;
1383 static int jewels_menu_cb(int action, const struct menu_item_ex *this_item)
1385 int i = ((intptr_t)this_item);
1386 if(action == ACTION_REQUEST_MENUITEM
1387 && !_ingame && (i==0 || i==6))
1388 return ACTION_EXIT_MENUITEM;
1389 return action;
1391 /*****************************************************************************
1392 * jewels_game_menu() shows the game menu.
1393 ******************************************************************************/
1394 static int jewels_game_menu(struct game_context* bj, bool ingame)
1396 rb->button_clear_queue();
1397 int choice = 0;
1399 _ingame = ingame;
1401 static struct opt_items mode[] = {
1402 { "Normal", -1 },
1403 { "Puzzle", -1 },
1406 MENUITEM_STRINGLIST (main_menu, "Jewels Menu", jewels_menu_cb,
1407 "Resume Game",
1408 "Start New Game",
1409 "Mode",
1410 "Help",
1411 "High Scores",
1412 "Playback Control",
1413 "Quit without Saving",
1414 "Quit");
1416 while (1) {
1417 switch (rb->do_menu(&main_menu, &choice, NULL, false)) {
1418 case 0:
1419 jewels_setcolors();
1420 if(resume_file)
1421 rb->remove(SAVE_FILE);
1422 return 0;
1423 case 1:
1424 jewels_init(bj);
1425 return 0;
1426 case 2:
1427 rb->set_option("Mode", &bj->tmp_type, INT, mode, 2, NULL);
1428 break;
1429 case 3:
1430 if(jewels_help())
1431 return 1;
1432 break;
1433 case 4:
1434 highscore_show(-1, highscores, NUM_SCORES, true);
1435 break;
1436 case 5:
1437 playback_control(NULL);
1438 break;
1439 case 6:
1440 return 1;
1441 case 7:
1442 if (ingame) {
1443 rb->splash(HZ*1, "Saving game ...");
1444 jewels_savegame(bj);
1446 return 1;
1447 case MENU_ATTACHED_USB:
1448 return 1;
1449 default:
1450 break;
1455 static int jewels_main(struct game_context* bj) {
1456 int button;
1457 int position;
1458 bool selected = false;
1459 int x=0, y=0;
1461 bool loaded = jewels_loadgame(bj);
1462 resume_file = loaded;
1463 if (jewels_game_menu(bj, loaded)!=0)
1464 return 0;
1466 resume_file = false;
1467 while(true) {
1468 /* refresh the board */
1469 jewels_drawboard(bj);
1471 /* display the cursor */
1472 if(selected) {
1473 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1474 rb->lcd_fillrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1475 TILE_WIDTH, TILE_HEIGHT);
1476 rb->lcd_set_drawmode(DRMODE_SOLID);
1477 } else {
1478 rb->lcd_drawrect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1479 TILE_WIDTH, TILE_HEIGHT);
1481 rb->lcd_update_rect(x*TILE_WIDTH, y*TILE_HEIGHT+YOFS,
1482 TILE_WIDTH, TILE_HEIGHT);
1484 /* handle game button presses */
1485 rb->yield();
1486 button = rb->button_get(true);
1487 switch(button){
1488 case JEWELS_LEFT: /* move cursor left */
1489 case (JEWELS_LEFT|BUTTON_REPEAT):
1490 if(selected) {
1491 bj->score += jewels_swapjewels(bj, x, y, SWAP_LEFT);
1492 selected = false;
1493 } else {
1494 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1496 break;
1498 case JEWELS_RIGHT: /* move cursor right */
1499 case (JEWELS_RIGHT|BUTTON_REPEAT):
1500 if(selected) {
1501 bj->score += jewels_swapjewels(bj, x, y, SWAP_RIGHT);
1502 selected = false;
1503 } else {
1504 x = (x+1)%BJ_WIDTH;
1506 break;
1508 case JEWELS_DOWN: /* move cursor down */
1509 case (JEWELS_DOWN|BUTTON_REPEAT):
1510 if(selected) {
1511 bj->score += jewels_swapjewels(bj, x, y, SWAP_DOWN);
1512 selected = false;
1513 } else {
1514 y = (y+1)%(BJ_HEIGHT-1);
1516 break;
1518 case JEWELS_UP: /* move cursor up */
1519 case (JEWELS_UP|BUTTON_REPEAT):
1520 if(selected) {
1521 bj->score += jewels_swapjewels(bj, x, y, SWAP_UP);
1522 selected = false;
1523 } else {
1524 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1526 break;
1528 #ifdef JEWELS_SCROLLWHEEL
1529 case JEWELS_PREV: /* scroll backwards */
1530 case (JEWELS_PREV|BUTTON_REPEAT):
1531 if(!selected) {
1532 if(x == 0) {
1533 y = (y+(BJ_HEIGHT-1)-1)%(BJ_HEIGHT-1);
1535 x = (x+BJ_WIDTH-1)%BJ_WIDTH;
1537 break;
1539 case JEWELS_NEXT: /* scroll forwards */
1540 case (JEWELS_NEXT|BUTTON_REPEAT):
1541 if(!selected) {
1542 if(x == BJ_WIDTH-1) {
1543 y = (y+1)%(BJ_HEIGHT-1);
1545 x = (x+1)%BJ_WIDTH;
1547 break;
1548 #endif
1550 case JEWELS_SELECT: /* toggle selected */
1551 selected = !selected;
1552 break;
1554 #ifdef JEWELS_RC_CANCEL
1555 case JEWELS_RC_CANCEL:
1556 #endif
1557 case JEWELS_CANCEL: /* end game */
1558 if (jewels_game_menu(bj, true)!=0)
1559 return 0;
1560 break;
1562 default:
1563 if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
1564 return PLUGIN_USB_CONNECTED;
1565 break;
1568 switch(bj->type) {
1569 case GAME_TYPE_NORMAL:
1570 if(bj->score >= LEVEL_PTS)
1571 jewels_nextlevel(bj);
1572 break;
1573 case GAME_TYPE_PUZZLE:
1574 if (jewels_puzzle_is_finished(bj)) {
1575 if (bj->level < NUM_PUZZLE_LEVELS) {
1576 jewels_nextlevel(bj);
1577 } else {
1578 rb->splash(2*HZ, "Congratulations!");
1579 rb->splash(2*HZ, "You have finished the game!");
1580 if (jewels_game_menu(bj, false)!=0) {
1581 return 0;
1584 break;
1588 if (!jewels_movesavail(bj)) {
1589 switch(bj->type) {
1590 case GAME_TYPE_NORMAL:
1591 rb->splash(HZ*2, "Game Over!");
1592 rb->lcd_clear_display();
1593 bj->score += (bj->level-1)*LEVEL_PTS;
1594 position=highscore_update(bj->score, bj->level, "",
1595 highscores, NUM_SCORES);
1596 if (position != -1)
1598 if (position == 0)
1599 rb->splash(HZ*2, "New High Score");
1600 highscore_show(position, highscores, NUM_SCORES, true);
1602 break;
1603 case GAME_TYPE_PUZZLE:
1604 rb->splash(2*HZ, "Game Over");
1605 break;
1607 if (jewels_game_menu(bj, false)!=0) {
1608 return 0;
1614 /* this is the plugin entry point */
1615 enum plugin_status plugin_start(const void* parameter)
1617 (void)parameter;
1619 /* load high scores */
1620 highscore_load(SCORE_FILE, highscores, NUM_SCORES);
1622 rb->lcd_setfont(FONT_SYSFIXED);
1623 #if LCD_DEPTH > 1
1624 rb->lcd_set_backdrop(NULL);
1625 #endif
1627 struct game_context bj;
1628 bj.tmp_type = GAME_TYPE_NORMAL;
1629 jewels_main(&bj);
1630 highscore_save(SCORE_FILE, highscores, NUM_SCORES);
1631 rb->lcd_setfont(FONT_UI);
1633 return PLUGIN_OK;